Skip to content

canon extract caesar

CAESAR WEB CANON — verbatim extraction from products/caesar/screens/dashboard.html

Section titled “CAESAR WEB CANON — verbatim extraction from products/caesar/screens/dashboard.html”

Source: /Users/Ridwan/Pictures/Claude/Rechaj 2.0/rechaj-design-system/products/caesar/screens/dashboard.html (6,260 lines: CSS lines 8–707, static markup 709–1161, JS 1162–6258). All CSS read in full; markup snippets are copied verbatim from the file (SVG path data occasionally elided with … for length — never elide when building real screens, copy the icon from the file).

File header comment (the self-declared intent, verbatim):

/* ═══════════════════════════════════════
CAESAR — Dashboard. Zoho Books FEATURES & FLOWS, redesigned in the Rechaj
design system: collapsible/expandable sidebar (.sb-*), our topbar, KPI
stat cards, charts, line tabs, circular tinted icons. Brand green #01E46F,
General Sans, shadow-only cards (no strokes), 4px grid. Prefix: cz-*.
Dropped per finance walkthrough: Items, Time Tracking, Tax Returns,
Configure Features list, Projects widget, Account Watchlist, Getting Started.
═══════════════════════════════════════ */

Font load: <link href="https://api.fontshare.com/v2/css?f[]=general-sans@400,500,600,700&display=swap" rel="stylesheet"> — General Sans, weights 400/500/600/700 only.


The full :root block, verbatim:

:root {
--brand:#01E46F; --brand-dim:rgba(1,228,111,.10); --brand-dim-h:rgba(1,228,111,.16); --g-dark:#05853E;
--blue:#0090FF; --blue-dim:rgba(0,144,255,.12);
--amber:#FFC53D; --amber-text:#9A5B00; --amber-dim:rgba(255,197,61,.18);
--red:#E32627; --red-dim:rgba(227,38,39,.10);
--purple:#7C3AED; --purple-dim:rgba(124,58,237,.12);
--ink:#0F1D13;
--bg:#F5F6FA; --surface:#fff; --hover:#F0F1F4;
--grey1:#FAFAFA; --grey2:#F4F4F5;
--border:#E8E8EC; --border-soft:#EFEFF2;
--sh-card:2px 3px 16px 0 rgba(193,197,210,.18);
--r-sm:6px; --r-md:8px; --r-lg:12px; --r-xl:16px; --r-f:999px;
--side-w:248px; --side-w-collapsed:68px; --topbar-h:60px;
--input-bg:#fff;
}

(note: --text-1:#111; --text-2:#555; --text-3:#888; sit on the --ink line group in the file — full line order above is verbatim; the text tokens appear between --ink and --bg:)

--ink:#0F1D13;
--bg:#F5F6FA; --surface:#fff; --hover:#F0F1F4;
--grey1:#FAFAFA; --grey2:#F4F4F5;
--border:#E8E8EC; --border-soft:#EFEFF2;
--text-1:#111; --text-2:#555; --text-3:#888;

Dark-mode token overrides (verbatim — every component adapts automatically because it uses var(--*)):

body.dark {
color-scheme:dark;
--ink:#EEF1F5;
--bg:#0F1116; --surface:#181B21; --hover:#262B34;
--grey1:#1F232B; --grey2:#262B34;
--border:#2D323C; --border-soft:#252932;
--text-1:#EAECF0; --text-2:#A6AEBA; --text-3:#7E8591;
--sh-card:0 2px 14px 0 rgba(0,0,0,.45);
--g-dark:#35D982; --brand-dim:rgba(1,228,111,.15); --brand-dim-h:rgba(1,228,111,.24);
--blue:#3BA5FF; --blue-dim:rgba(0,144,255,.20);
--amber:#FFD166; --amber-text:#F0B94A; --amber-dim:rgba(255,197,61,.16);
--red:#FF5B5D; --red-dim:rgba(255,91,93,.16);
--purple:#A78BFA; --purple-dim:rgba(167,139,250,.18);
--input-bg:#20242C;
}
body.dark input, body.dark select, body.dark textarea { background:var(--input-bg); color:var(--text-1); border-color:var(--border); }
body.dark .dlg { background:var(--surface); }
body.dark .dlg-overlay { background:rgba(0,0,0,.6); }
body.dark .sb-tooltip, body.dark .chart-tip, body.dark .inc-bar .inc-tip { background:#2C313B; color:#fff; }
body.dark .alert-toast--default { background:rgba(0,144,255,.16); } body.dark .alert-toast--default .alert-toast-badge, body.dark .alert-toast--default .alert-toast-text { color:#6FB8FF; }
body.dark .alert-toast--gray { background:rgba(255,255,255,.10); } body.dark .alert-toast--gray .alert-toast-badge, body.dark .alert-toast--gray .alert-toast-text { color:#C4CBD6; }
body.dark .alert-toast--error { background:rgba(255,91,93,.16); } body.dark .alert-toast--error .alert-toast-badge, body.dark .alert-toast--error .alert-toast-text { color:#FF8E90; }
body.dark .alert-toast--warning { background:rgba(255,197,61,.16); } body.dark .alert-toast--warning .alert-toast-badge, body.dark .alert-toast--warning .alert-toast-text { color:#F0C24A; }
body.dark .alert-toast--success { background:rgba(1,228,111,.15); } body.dark .alert-toast--success .alert-toast-badge, body.dark .alert-toast--success .alert-toast-text { color:#4ADE80; }
/* theme-toggle icon swap (moon in light, sun in dark) */
body:not(.dark) #theme-toggle .ico-sun { display:none; }
body.dark #theme-toggle .ico-moon { display:none; }

Base reset + typography (verbatim):

*,*::before,*::after { box-sizing:border-box; margin:0; padding:0; }
html { font-size:14px; -webkit-font-smoothing:antialiased; overflow-y:scroll; scrollbar-gutter:stable; }
body { font-family:'General Sans',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;
font-size:14px; color:var(--text-1); background:var(--bg); line-height:1.5; }
a { text-decoration:none; color:inherit; }
b,strong { font-weight:600; }
button { cursor:pointer; border:none; background:none; font-family:inherit; }

Key facts: base font-size 14px; b/strong are remapped to 600 (bold text is never 700 by default); html forces a scrollbar with stable gutter so screen swaps never shift layout.


<body> ← scrolls (the ONLY vertical scroller for content)
├── <aside class="sidebar"> ← position:fixed, top/left/bottom 0, width 248px (68px collapsed)
│ ├── .sb-logo ← 60px tall (=topbar-h), brand SVG + "Caesar" wordmark
│ ├── <nav class="sb-body"> ← flex:1, its own overflow-y:auto (5px scrollbar)
│ │ .sb-item / .sb-item.has-sub + .sb-sub > .sb-subitem…
│ └── .sb-foot ← Settings, pinned bottom, border-top
├── <div class="main"> ← margin-left:248px (68px collapsed), min-height:100vh, flex column
│ ├── <header class="topbar"> ← position:sticky top:0, 60px, z-40
│ └── <div class="page app-screen show" id="scr-dashboard"> … </div>
│ <div class="page app-screen" id="scr-banking"> … </div>
│ <div class="page app-screen" id="scr-…"> (≈50 empty screen shells, filled by JS) </div>
├── .qc-overlay#qc ← Quick-Create mega menu (fixed, z-200)
└── <div id="modal-root"> ← .dlg dialogs injected here (z-400)
#toast-container ← created lazily by toast() (fixed top-center, z-9999)
  • The page body scrolls; .topbar is sticky within .main; .sidebar is fixed and has its own internal scroll on .sb-body.
  • Every “page” is a .page.app-screen div; navigation swaps screens by toggling .show (display:none ↔ block) and calls window.scrollTo(0,0) — a single-file SPA, no URL routing.
  • Modal open sets document.body.style.overflow='hidden'; the .dlg-overlay itself is overflow-y:auto so tall dialogs scroll inside the overlay.
  • Wide tables scroll horizontally inside .tscroll { overflow-x:auto; } — never the page.
/* ── SIDEBAR ── */
.sidebar { position:fixed; top:0; left:0; bottom:0; width:var(--side-w); z-index:50;
background:var(--surface); border-right:1px solid var(--border); display:flex; flex-direction:column;
transition:width .18s ease; }
.sb-logo { display:flex; align-items:center; gap:10px; height:var(--topbar-h); padding:0 20px; border-bottom:1px solid var(--border-soft); flex-shrink:0; }
.sb-logo-name { font-size:18px; font-weight:700; letter-spacing:-.02em; color:var(--ink); white-space:nowrap; }
.sb-body { flex:1; overflow-y:auto; overflow-x:hidden; padding:10px 12px; }
.sb-body::-webkit-scrollbar { width:5px; } .sb-body::-webkit-scrollbar-thumb { background:var(--border); border-radius:4px; }
/* ── MAIN ── */
.main { margin-left:var(--side-w); transition:margin-left .18s ease; min-height:100vh; display:flex; flex-direction:column; }
body.sb-collapsed .main { margin-left:var(--side-w-collapsed); }
.topbar { position:sticky; top:0; z-index:40; height:var(--topbar-h); background:var(--surface);
border-bottom:1px solid var(--border); display:flex; align-items:center; gap:16px; padding:0 24px; }
/* ── PAGE ── */
.page { max-width:1320px; width:100%; margin:0 auto; padding:24px 32px 48px; }
/* ── App screens ── */
.app-screen { display:none; } .app-screen.show { display:block; }

Key dimensions: sidebar 248 / 68 collapsed; topbar 60; page column max-width 1320px, padding 24px 32px 48px, centered. Z-index ladder: topbar 40 < sidebar 50 < gsearch panel 60 < dd-menu 120 < quick-create 200 < dlg-overlay 400 < toasts/tooltips 9999.


Canonical selectors: .sidebar, .sb-logo, .sb-logo-name, .sb-body, .sb-item (+.active, .has-sub, .open), .sb-label, .sb-chev, .sb-sub (+.show), .sb-subitem (+.active), .sb-foot, .sb-badge, body.sb-collapsed, .sb-tooltip.

.sb-item { display:flex; align-items:center; gap:12px; height:38px; padding:0 12px; border-radius:var(--r-md);
font-size:13.5px; color:var(--text-2); cursor:pointer; user-select:none; transition:background .12s,color .12s; white-space:nowrap; position:relative; }
.sb-item:hover { background:var(--hover); color:var(--text-1); }
.sb-item.active { background:var(--brand-dim); color:var(--g-dark); font-weight:500; }
.sb-item svg.lead { flex-shrink:0; }
.sb-label { flex:1; }
.sb-chev { transition:transform .18s; color:var(--text-3); }
.sb-item.open .sb-chev { transform:rotate(90deg); }
.sb-sub { display:none; margin:2px 0 4px; }
.sb-sub.show { display:block; }
.sb-subitem { display:flex; align-items:center; height:34px; padding:0 12px 0 46px; border-radius:var(--r-md);
font-size:13px; color:var(--text-2); cursor:pointer; white-space:nowrap; }
.sb-subitem:hover { background:var(--hover); color:var(--text-1); }
.sb-subitem.active { color:var(--g-dark); font-weight:500; }
.sb-subitem .sb-badge { margin-left:auto; }
.sb-foot { border-top:1px solid var(--border-soft); padding:10px 12px; flex-shrink:0; }
.sb-badge { font-size:11px; font-weight:600; color:var(--amber-text); background:var(--amber-dim); border-radius:var(--r-f); padding:2px 8px; }
.sb-item .sb-badge { margin-left:auto; }
/* collapsed rail */
body.sb-collapsed .sidebar { width:var(--side-w-collapsed); }
body.sb-collapsed .sb-logo-name,
body.sb-collapsed .sb-label,
body.sb-collapsed .sb-chev,
body.sb-collapsed .sb-item .sb-badge { display:none; }
body.sb-collapsed .sb-logo { justify-content:center; padding:0; }
body.sb-collapsed .sb-item { justify-content:center; padding:0; }
body.sb-collapsed .sb-sub { display:none !important; }
/* collapsed hover tooltip — Atlas .sb-tooltip (fixed pill, escapes sidebar overflow) */
.sb-tooltip { position:fixed; display:none; background:var(--ink); color:#fff; padding:6px 10px; border-radius:6px;
font-size:12.5px; font-weight:500; white-space:nowrap; transform:translateY(-50%); z-index:9999; pointer-events:none;
box-shadow:0 4px 12px rgba(0,0,0,.15); }
/* collapse/expand toggle icon swaps with state */
body.sb-collapsed .ico-expanded, body:not(.sb-collapsed) .ico-collapsed { display:none; }

Markup (verbatim, one plain item, one expandable group, and the pinned foot):

<aside class="sidebar">
<div class="sb-logo">
<svg width="28" height="28" viewBox="0 0 57 57" fill="none" …aria-label="Rechaj" style="flex-shrink:0">
<path d="M39.6964 8.40009…" fill="#0F1D0F"/>
<path d="M17.597 48.0272…" fill="#01E46F"/>
</svg>
<span class="sb-logo-name">Caesar</span>
</div>
<nav class="sb-body">
<div class="sb-item active" data-tip="Dashboard" onclick="navTo('dashboard',this)"><svg class="lead" width="18" height="18" …></svg><span class="sb-label">Dashboard</span></div>
<div class="sb-item has-sub" data-tip="Sales" onclick="toggleSub(this)"><svg class="lead" width="18" height="18" …></svg><span class="sb-label">Sales</span><svg class="sb-chev" width="14" height="14" …><polyline points="9 18 15 12 9 6"/></svg></div>
<div class="sb-sub">
<div class="sb-subitem" data-screen onclick="openCustomers()">Customers</div><div class="sb-subitem" data-screen onclick="openEstimates()">Estimates</div>…
</div>
</nav>
<div class="sb-foot">
<div class="sb-item" data-tip="Settings" onclick="openSettings('organization')">…<span class="sb-label">Settings</span></div>
</div>
</aside>

Usage notes: nav icons are 18×18 stroke 1.9 lucide-style inline SVGs with class lead; group chevron is 14×14 rotating 90° when .open. Sub-menus are an accordion — only one open at a time (toggleSub). Active top item = brand-dim bg + g-dark text at weight 500; active subitem is text-color only (no bg). data-tip drives both the collapsed-rail hover tooltip and active-state lookup. Sub-items without a built screen keep no data-screen attribute and fall through to a “… is not in this prototype” toast — never a dead click. The sidebar auto-collapses when navigating two levels deep (detail/form pages), stays expanded on list pages; expanding again re-opens the active section’s submenu.

Selectors: .topbar, .crumb-toggle, .crumbs > .crumb-item (+.crumb-current, .link) + .crumb-sep, .tb-search-wrap > .tb-search + .gsearch-panel, .tb-spacer, .ds-btn-primary, .icon-action (+.dot), .user-pill, .avatar, .user-name, .user-role.

.crumb-toggle { width:34px; height:34px; border-radius:var(--r-md); display:flex; align-items:center; justify-content:center; color:var(--text-2); }
.crumb-toggle:hover { background:var(--hover); color:var(--text-1); }
.crumb { font-size:15px; font-weight:600; color:var(--text-1); }
.crumbs { display:flex; align-items:center; gap:8px; font-size:15px; min-width:0; }
.crumb-item { font-weight:500; color:var(--text-3); white-space:nowrap; }
.crumb-item.crumb-current { font-weight:600; color:var(--text-1); }
.crumb-item.link { color:var(--text-3); font-weight:500; cursor:pointer; }
.crumb-item.link:hover { color:var(--g-dark); }
.crumb-sep { color:#C9CED6; flex-shrink:0; }
.tb-search-wrap { flex:1; max-width:420px; position:relative; }
.tb-search { height:38px; display:flex; align-items:center; gap:8px; padding:0 12px; background:var(--bg); border-radius:var(--r-md); color:var(--text-3); font-size:13px; }
.tb-search input { flex:1; border:none; background:none; outline:none; font-family:inherit; font-size:13px; color:var(--text-1); }
.gsearch-panel { display:none; position:absolute; top:46px; left:0; right:0; background:var(--surface); border-radius:var(--r-md); box-shadow:var(--sh-pop,0 8px 28px rgba(15,23,42,.16)); padding:6px; max-height:420px; overflow-y:auto; z-index:60; }
.gsearch-panel.open { display:block; }
.gs-group-t { font-size:11px; font-weight:600; color:var(--text-3); letter-spacing:.02em; padding:8px 10px 4px; }
.gs-item { display:flex; align-items:center; gap:10px; padding:8px 10px; border-radius:var(--r-sm,8px); cursor:pointer; }
.gs-item:hover, .gs-item.gs-cur { background:var(--grey1); }
.gs-ic { width:28px; height:28px; border-radius:50%; flex-shrink:0; display:flex; align-items:center; justify-content:center; background:var(--grey1); color:var(--text-2); }
.gs-item:hover .gs-ic, .gs-item.gs-cur .gs-ic { background:var(--surface); }
.gs-main { flex:1; min-width:0; }
.gs-t { font-size:13px; color:var(--text-1); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.gs-t b { font-weight:600; }
.gs-m { font-size:11.5px; color:var(--text-3); margin-top:1px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.gs-amt { font-size:12.5px; font-weight:600; color:var(--text-2); flex-shrink:0; }
.gs-empty { padding:18px 12px; text-align:center; font-size:12.5px; color:var(--text-3); }
.tb-spacer { flex:1; }
.ds-btn-primary { display:inline-flex; align-items:center; justify-content:center; width:38px; height:38px; border-radius:var(--r-md);
background:var(--brand); color:#111; }
.ds-btn-primary:hover { background:#00c45b; }
.icon-action { width:38px; height:38px; border-radius:var(--r-md); border:1px solid var(--border); background:var(--surface);
display:flex; align-items:center; justify-content:center; color:var(--text-2); cursor:pointer; position:relative; }
.icon-action:hover { background:var(--hover); color:var(--text-1); }
.icon-action .dot { position:absolute; top:7px; right:8px; width:7px; height:7px; border-radius:50%; background:var(--red); border:1.5px solid #fff; }
.user-pill { display:flex; align-items:center; gap:8px; height:42px; padding:4px 12px 4px 4px; border-radius:var(--r-f); background:var(--bg); cursor:pointer; }
.avatar { width:32px; height:32px; border-radius:50%; background:var(--brand-dim); color:var(--g-dark); display:flex; align-items:center; justify-content:center; font-size:12px; font-weight:600; flex-shrink:0; }
.user-name { font-size:13px; font-weight:600; color:var(--text-1); line-height:1.1; }
.user-role { font-size:11px; color:var(--text-3); }

Markup (verbatim, condensed SVGs):

<header class="topbar">
<div class="crumb-toggle" onclick="toggleSidebar()" title="Toggle sidebar">
<svg class="ico-expanded" width="20" height="20" …></svg>
<svg class="ico-collapsed" width="20" height="20" …></svg>
</div>
<nav class="crumbs" id="crumbs"><span class="crumb-item crumb-current">Dashboard</span></nav>
<div class="tb-search-wrap">
<div class="tb-search">
<svg width="16" height="16" …search icon…></svg>
<input id="gsearch" autocomplete="off" placeholder="Search invoices, bills, contacts, accounts ( / )" oninput="globalSearch(this.value)" onfocus="globalSearch(this.value)" onkeydown="gsearchKey(event)">
</div>
<div class="gsearch-panel" id="gsearch-panel"></div>
</div>
<div class="tb-spacer"></div>
<button class="ds-btn-primary" title="Create new" onclick="toggleQC(event)"><svg width="18" height="18" …plus…></svg></button>
<button class="icon-action" id="theme-toggle" title="Toggle dark mode" onclick="toggleTheme()">…moon/sun svgs…</button>
<div class="dd">
<div class="icon-action dd-trigger"><svg …bell…></svg><span class="dot" id="notif-dot"></span></div>
<div class="dd-menu" style="min-width:312px">
<div class="dd-head"><span class="nm">Notifications</span><span class="link" onclick="notifMarkAll(event)">Mark all read</span></div>
<div class="dd-divider"></div>
<div class="dd-note" onclick="closeAllDD();openBills()"><span class="nt-dot"></span><div><div class="nt-t">Bill <b>BILL-0312</b> from Telpo awaits approval</div><div class="nt-m">10 minutes ago</div></div></div>
…
</div>
</div>
<div class="dd">
<div class="user-pill dd-trigger">
<div class="avatar">FO</div>
<div><div class="user-name">Francis Ogunlaja</div><div class="user-role">Finance Lead</div></div>
<svg class="dd-cv" width="15" height="15" …chevron…></svg>
</div>
<div class="dd-menu" style="min-width:236px">
<div class="dd-head" style="display:block"><div class="nm">Francis Ogunlaja</div><div class="em">francis.ogunlaja@mocx.co</div></div>
<div class="dd-divider"></div>
<div class="dd-opt" onclick="openSettings('profile')">…My profile</div>
<div class="dd-opt" onclick="openSettings('organization')">…Settings</div>
<div class="dd-divider"></div>
<div class="dd-opt danger" onclick="location.href='login.html'">…Sign out</div>
</div>
</div>
</header>

Usage notes: topbar order is fixed — sidebar toggle → breadcrumbs → global search (max 420px) → spacer → green square + button (quick-create) → theme toggle → bell w/ red dot → user pill. Breadcrumb trail is rendered by renderCrumbs(name, extra) from CRUMB_TRAILS; earlier crumbs are clickable .crumb-item.link, last is .crumb-current. / focuses the global search from anywhere; results are grouped (Contacts / Transactions / Accounts) with keyboard ↑↓/Enter support.

Selectors: .pg-head, .pg-title, .pg-sub, .pg-actions, .period.

.pg-head { display:flex; align-items:flex-end; justify-content:space-between; gap:20px; margin-bottom:24px; }
.pg-title { font-size:23px; font-weight:700; letter-spacing:-.02em; color:var(--ink); }
.pg-sub { font-size:13px; color:var(--text-3); margin-top:4px; }
.period { display:inline-flex; align-items:center; gap:8px; height:38px; padding:0 16px; border-radius:var(--r-md);
background:var(--surface); box-shadow:var(--sh-card); font-size:13px; font-weight:500; cursor:pointer; }
.period svg { color:var(--text-3); }
.pg-actions { display:flex; gap:10px; flex-wrap:wrap; justify-content:flex-end; }
.form-actions { display:flex; gap:10px; flex-wrap:wrap; }

Markup (verbatim, dashboard variant with period dropdown; list variant uses .pg-actions with buttons):

<div class="pg-head">
<div>
<div class="pg-title">Hello, Francis</div>
<div class="pg-sub">Financial position for MocX Group · as of 18 Jun 2026</div>
</div>
<div class="dd" data-dd="period">
<div class="period dd-trigger"><span class="dd-label">This Fiscal Year</span> <svg class="dd-cv" width="14" height="14" …chevron…></svg></div>
</div>
</div>
<div class="pg-head">
<div><div class="pg-title">Banking</div><div class="pg-sub">Accounts, balances and reconciliation · MocX Group</div></div>
<div class="pg-actions">
<button class="btn-sec" onclick="importStmtModal()">…Import Statement</button>
<button class="btn-pri" onclick="addAccountModal()">…Add Bank / Credit Card</button>
</div>
</div>

Usage notes: title 23/700 (display exception), sub 13/text-3 joined with the DS middot · (never em dashes). Detail pages reuse the head as .cust-detail-head with a 21px title, inline status badge, and .txn-actions button row. On -form screens the .form-actions row is moved by showScreen() into the .pg-head as pg-actions (actions sit top-right of a form page).

3.4 Tabs & segmented controls — every variant

Section titled “3.4 Tabs & segmented controls — every variant”

(a) .seg — pill segmented control (view switcher inside a card; e.g. Account Summary ↔ Transactions, Users ↔ Roles, detail-page Overview/Comments/Transactions/Statement tabs):

.seg { display:flex; gap:2px; background:var(--bg); border-radius:var(--r-md); padding:4px; }
.seg button { font-size:12.5px; font-weight:500; color:var(--text-2); padding:4px 12px; border-radius:6px; }
.seg button.on { background:var(--surface); color:var(--g-dark); box-shadow:var(--sh-card); }

Markup (from txnScreenHTML / customerDetailHTML — note the detail-page usage overrides the track to --hover):

<div class="seg" id="txn-viewseg"><button onclick="bankView('gtbank','summary')">Account Summary</button><button class="on" onclick="bankView('gtbank','txns')">Transactions</button></div>
<div class="seg" id="cust-tabs" style="width:fit-content;margin-bottom:16px;background:var(--hover)">
<button class="on" data-ct="Overview" onclick="custDetailTab('Overview')">Overview</button>…
</div>

(b) .txn-tab — line tabs with count pills (status filter over a table: All / Uncategorized n / Categorized):

.txn-tabs { display:flex; gap:2px; }
.txn-tab { font-size:13px; font-weight:500; color:var(--text-2); height:38px; padding:0 14px; cursor:pointer; display:inline-flex; align-items:center; gap:8px; position:relative; white-space:nowrap; transition:color .13s; }
.txn-tab::after { content:''; position:absolute; left:0; right:0; bottom:-1px; height:2px; background:var(--brand); border-radius:2px 2px 0 0; opacity:0; transition:opacity .13s; }
.txn-tab:hover { color:var(--text-1); }
.txn-tab.on { color:var(--g-dark); font-weight:600; }
.txn-tab.on::after { opacity:1; }
.txn-tab .ct { font-size:11px; font-weight:600; background:var(--grey2); color:var(--text-3); border-radius:var(--r-f); padding:1px 7px; }
.txn-tab.on .ct { background:var(--brand-dim); color:var(--g-dark); }
<div class="txn-tabs" id="txn-tabs"><div class="txn-tab on" data-st="all" onclick="bankTab('gtbank','all')">All</div><div class="txn-tab" data-st="uncat" onclick="bankTab('gtbank','uncat')">Uncategorized <span class="ct">4</span></div><div class="txn-tab" data-st="cat" onclick="bankTab('gtbank','cat')">Categorized</div></div>

(c) .form-tab — underlined form-section tabs (multi-section forms, e.g. customer form Other Details / Address / Contact Persons / Remarks):

.form-tabs { display:flex; gap:2px; border-bottom:1px solid var(--border-soft); margin:8px 0 20px; flex-wrap:wrap; }
.form-tab { font-size:13px; font-weight:500; color:var(--text-2); padding:8px 16px; cursor:pointer; border-bottom:2px solid transparent; margin-bottom:-1px; }
.form-tab:hover { color:var(--text-1); }
.form-tab.on { color:var(--g-dark); border-bottom-color:var(--brand); font-weight:600; }
.form-sec { display:none; } .form-sec.on { display:block; }

(d) .sn-item — settings vertical nav (sticky left rail inside the Settings page):

.settings-nav { display:flex; flex-direction:column; gap:2px; position:sticky; top:88px; }
.sn-item { display:flex; align-items:center; gap:10px; padding:9px 12px; border-radius:var(--r-md); font-size:13.5px; font-weight:500; color:var(--text-2); cursor:pointer; transition:background .12s,color .12s; }
.sn-item svg { color:var(--text-3); flex-shrink:0; }
.sn-item:hover { background:var(--hover); color:var(--text-1); }
.sn-item.on { background:var(--brand-dim); color:var(--g-dark); font-weight:600; }
.sn-item.on svg { color:var(--g-dark); }

(e) .rpt-cat — category rail (Reports Center / Documents left nav, scroll-to or filter):

.rpt-cat { font-size:13px; color:var(--text-2); padding:8px 12px; border-radius:var(--r-md); cursor:pointer; }
.rpt-cat:hover { background:var(--hover); color:var(--text-1); } .rpt-cat.on { background:var(--brand-dim); color:var(--g-dark); font-weight:600; }

When to use which: .seg = switching views of the same data (2–4 options, inside a card head or detail page); .txn-tabs = status filters above a table (with live count pills); .form-tabs = sections within a form card; .sn-item/.rpt-cat = page-level left rail on settings/reports/documents. The active state is always brand-dim bg + --g-dark text (or brand underline for line tabs) — never a solid green fill.

Selectors: .tbl-card, .tbl-card-head, .tscroll, .tbl, th.sortable + .sort-ic, .acct/.acct-ic/.acct-nm/.acct-ty, .amt, .muted, .row-arrow, .tbl-foot, .txn-search, .bulk-bar, .row-menu-btn, .st-dot, tr.inactive, .cust-nm-cell, .txn-link-row, .doc-link.

.tbl { width:100%; border-collapse:separate; border-spacing:0; font-size:13.5px; }
.tbl th { height:48px; padding:0 16px; font-size:12px; font-weight:500; text-transform:uppercase; letter-spacing:.06em; color:var(--text-2); text-align:left; white-space:nowrap; background:var(--grey2); }
.tbl th:first-child { border-radius:8px 0 0 8px; } .tbl th:last-child { border-radius:0 8px 8px 0; }
.tbl th.r, .tbl td.r { text-align:right; }
.tbl tbody tr { transition:background .1s; } .tbl tbody tr:hover td { background:var(--hover); }
.tbl tbody tr:has([onclick*="open"]) { cursor:pointer; }
.tbl td { padding:12px 16px; font-size:13.5px; color:var(--text-1); border-bottom:1px solid var(--border-soft); vertical-align:middle; }
.tbl tbody tr:last-child td { border-bottom:none; }
.acct { display:flex; align-items:center; gap:12px; }
.acct-ic { width:38px; height:38px; border-radius:50%; display:flex; align-items:center; justify-content:center; flex-shrink:0; }
.acct-nm { font-size:14px; font-weight:600; color:var(--text-1); } .acct-ty { font-size:11.5px; color:var(--text-3); }
.amt { font-weight:600; color:var(--ink); } .muted { color:var(--text-3); }
.row-arrow { color:var(--text-3); } .tbl tbody tr:hover .row-arrow { color:var(--g-dark); }
.tbl-foot { display:flex; align-items:center; justify-content:space-between; padding:16px; border-top:1.5px solid var(--border); }
.tbl-foot .lbl { font-size:12.5px; color:var(--text-3); } .tbl-foot .val { font-size:16px; font-weight:700; color:var(--ink); }
.tbl-card { background:var(--surface); border-radius:var(--r-xl); padding:16px; }
.tbl-card-head { display:flex; align-items:center; justify-content:space-between; margin-bottom:16px; }
.tscroll { overflow-x:auto; }
.st-dot { width:7px; height:7px; border-radius:50%; background:#30A46C; display:inline-block; margin-right:8px; vertical-align:middle; }
.tbl tbody tr.inactive { opacity:.5; }
.tbl tbody tr.inactive .st-dot { background:#C9CED6; }
.row-menu-btn { width:30px; height:30px; border-radius:50%; display:inline-flex; align-items:center; justify-content:center; color:var(--text-3); cursor:pointer; }
.row-menu-btn:hover { background:rgba(15,29,19,.09); color:var(--text-1); }
.dd.open .row-menu-btn { background:rgba(15,29,19,.09); color:var(--text-1); }
.txn-search { display:inline-flex; align-items:center; gap:8px; height:34px; padding:0 12px; background:var(--bg); border-radius:var(--r-md); color:var(--text-3); }
.txn-search input { border:none; background:none; outline:none; font:inherit; font-size:13px; color:var(--text-1); width:160px; }
.txn-search input::placeholder { color:var(--text-3); }
.tbl thead th.sortable { cursor:pointer; user-select:none; white-space:nowrap; }
.tbl thead th.sortable:hover { color:var(--text-1); }
.sort-ic { display:inline-flex; vertical-align:middle; margin-left:4px; color:var(--text-3); }
.sort-ic.on { color:var(--g-dark); }
.bulk-bar { display:none; align-items:center; gap:16px; padding:10px 16px; background:var(--brand-dim); border-radius:var(--r-md); margin-bottom:12px; font-size:13px; font-weight:600; color:var(--g-dark); }
.bulk-bar.show { display:flex; }
.bulk-bar .ba { font-weight:500; color:var(--text-2); cursor:pointer; }
.bulk-bar .ba:hover { color:var(--g-dark); }
.bulk-bar .ba.danger { color:var(--red); } .bulk-bar .ba.danger:hover { color:var(--red); }
.bulk-bar .x { margin-left:auto; cursor:pointer; color:var(--text-3); display:inline-flex; }
.chk { width:16px; height:16px; accent-color:var(--brand); cursor:pointer; vertical-align:middle; }
.cust-nm-cell { display:flex; align-items:center; gap:12px; cursor:pointer; }
.txn-link-row { cursor:pointer; transition:background .12s; }
.txn-link-row:hover { background:var(--grey1); }
.doc-link { color:var(--g-dark); font-weight:600; cursor:pointer; }
.doc-link:hover { text-decoration:underline; }

Canonical list-card composition (verbatim skeleton from customersListHTML()):

<div class="tbl-card">
<div class="tbl-card-head">
<div class="dd" data-dd="custview"><div class="card-title dd-trigger" style="display:inline-flex;align-items:center;gap:6px;cursor:pointer">All Customers <svg…chev…></div><div class="dd-menu" style="min-width:210px">…view options…</div></div>
<div style="display:flex;align-items:center;gap:12px">
<div class="txn-search"><svg…search…><input id="cust-search" placeholder="Search customers" oninput="custSearch(this.value)"></div>
<div class="dd" data-dd="custmore"><div class="gear-btn dd-trigger" title="List options">…hamburger…</div><div class="dd-menu" style="min-width:200px;right:0">
<div class="dd-flt">Sort by</div>…sort options…
<div class="dd-divider"></div>
<div class="dd-opt" onclick="importModalAuto()"><span>Import Customers</span></div>
<div class="dd-opt" onclick="exportCSVAuto()"><span>Export Customers</span></div>
<div class="dd-opt" onclick="custRefresh()"><span>Refresh List</span></div>
</div></div>
</div>
</div>
<div class="bulk-bar" id="cust-bulk"><span id="cust-bulk-n">0 selected</span><span class="ba" onclick="custBulk('inactive')">Mark as inactive</span><span class="ba" onclick="custBulk('merge')">Merge</span><span class="ba" onclick="custBulk('email')">Send email</span><span class="ba danger" onclick="custBulk('delete')">Delete</span><span class="x" onclick="custClearSel()">…x…</span></div>
<div class="tscroll"><table class="tbl"><thead><tr>
<th style="width:34px"><input type="checkbox" class="chk" onclick="custToggleAll(this)"></th>
<th class="sortable" onclick="custSort('name')">Name <span id="sic-name"></span></th>
…
<th class="r" style="width:40px"></th>
</tr></thead><tbody id="cust-rows"></tbody></table></div>
</div>

Row markup (verbatim from custRenderRows() — checkbox, avatar+dot+name cell, kebab menu in last cell):

<tr>
<td><input type="checkbox" class="chk cust-cb" value="c1" onclick="custRowToggle(event)"></td>
<td><div class="cust-nm-cell" onclick="openCustomer('c1')"><span class="cust-avatar" style="width:30px;height:30px;font-size:11px">AO</span><span class="st-dot" style="background:#30A46C"></span><span style="font-weight:600;color:var(--text-1)">Acme Ltd</span></div></td>
<td>Acme Nigeria</td><td>ops@acme.ng</td><td><span class="muted">—</span></td>
<td class="r amt">₦12,400,000</td><td class="r amt"><span class="muted">—</span></td>
<td class="r"><div class="dd" style="display:inline-block"><div class="row-menu-btn dd-trigger">…kebab…</div><div class="dd-menu" style="min-width:200px">…Edit / View details / ─ / Mark as inactive…</div></div></td>
</tr>

Usage notes: header row is a grey2 strip with pill-rounded outer corners (8px), uppercase 12/500 labels. Money cells: class="r amt" (right-aligned, 600, ink); absent values render <span class="muted">—</span>. Whole-row click opens the detail via a global delegated click handler that finds the first open* (non-Form) onclick inside the row and clicks it (rows inside #scr-banking/#scr-bank-txns wire their own handlers). Selecting checkboxes reveals the .bulk-bar (brand-dim strip with actions, danger action in red). Table footer .tbl-foot carries an aggregate (e.g. “Group cash position” + 16/700 value). Sort = th.sortable + injected .sort-ic span (green when active, direction arrow).

.btn-pri { display:inline-flex; align-items:center; gap:8px; height:38px; padding:0 16px; border-radius:var(--r-md); background:var(--brand); color:#111; font-size:13.5px; font-weight:600; }
.btn-pri:hover { background:#00c45b; }
.btn-sec { display:inline-flex; align-items:center; gap:8px; height:38px; padding:0 16px; border-radius:var(--r-md); background:transparent; border:1px solid var(--border); color:var(--text-1); font-size:13.5px; font-weight:500; }
.btn-sec:hover { background:var(--hover); border-color:var(--text-3); }
.ds-btn-primary { display:inline-flex; align-items:center; justify-content:center; width:38px; height:38px; border-radius:var(--r-md);
background:var(--brand); color:#111; }
.ds-btn-primary:hover { background:#00c45b; }
.icon-action { width:38px; height:38px; border-radius:var(--r-md); border:1px solid var(--border); background:var(--surface);
display:flex; align-items:center; justify-content:center; color:var(--text-2); cursor:pointer; position:relative; }
.gear-btn { width:38px; height:38px; border-radius:var(--r-md); display:inline-flex; align-items:center; justify-content:center; color:var(--text-2); background:transparent; border:1px solid var(--border); cursor:pointer; }
.gear-btn:hover { color:var(--g-dark); background:var(--hover); border-color:var(--text-3); }
.row-menu-btn { width:30px; height:30px; border-radius:50%; display:inline-flex; align-items:center; justify-content:center; color:var(--text-3); cursor:pointer; }
.card-link { display:inline-flex; align-items:center; gap:4px; font-size:12.5px; font-weight:600; color:var(--g-dark);
padding:6px 12px; border-radius:var(--r-f); background:var(--brand-dim); cursor:pointer; transition:background .12s; }
.card-link:hover { background:var(--brand-dim-h); }
.card-link svg { transition:transform .14s; }
.card-link:hover svg { transform:translateX(2px); }
.link-add { display:inline-flex; align-items:center; gap:4px; font-size:12.5px; font-weight:600; color:var(--g-dark); cursor:pointer; margin-top:4px; }
.ext-link { color:var(--g-dark); text-decoration:none; font-weight:500; }
.ext-link:hover { text-decoration:underline; }

Markup examples (verbatim):

<button class="btn-pri" onclick="addAccountModal()"><svg width="16" height="16" …plus…></svg> Add Bank / Credit Card</button>
<button class="btn-sec" onclick="importStmtModal()"><svg width="16" height="16" …upload…></svg> Import Statement</button>
<div class="card-link" onclick="navTo('banking')">Go to Banking <svg width="13" height="13" …arrow-right…></svg></div>
<span class="link-add" onclick="invAddLine()">…plus… Add another line</span>

Variant inventory & exact specs:

  • Primary .btn-pri: 38px tall, radius 8, brand green bg + dark #111 text (never white-on-green), 13.5/600, hover #00c45b. Icons 15–16px.
  • Secondary .btn-sec: 38px, radius 8, transparent bg + 1px --border outline, 13.5/500, hover fills --hover.
  • Destructive: there is no dedicated .btn-danger class — confirm dialogs restyle the primary inline: <button class="btn-pri" style="background:var(--red);color:#fff" …> (from confirmModal(…, danger=true)). Row/menu deletes use .dd-opt.danger (red text menu option).
  • Icon primary .ds-btn-primary: 38×38 square radius-8 brand button (topbar quick-create “+”).
  • Icon neutral .icon-action (38×38, bordered, white bg — topbar) and .gear-btn (38×38, bordered, transparent — page-level “more”/gear triggers).
  • Row kebab .row-menu-btn: 30×30 circular ghost.
  • Tertiary/inline links: .card-link (brand-dim pill, arrow slides on hover), .link-add (green + plus), .ext-link, .doc-link (green 600, underline on hover), .unc-link, .sum-viewmore, .go-txn, .acc-add.
  • Ghost dashed tile .bank-add (add-account slot): transparent, 1.5px dashed var(--border), hover turns brand.
.bank-add { display:flex; flex-direction:column; align-items:center; justify-content:center; gap:8px; border-radius:var(--r-lg);
background:transparent; box-shadow:none; border:1.5px dashed var(--border); color:var(--text-3); font-size:13px; font-weight:500; cursor:pointer; min-height:128px; }
.bank-add:hover { color:var(--g-dark); border-color:var(--brand); }

(a) .badge — the standard badge (12/500, 6px radius, no uppercase, letter-spacing −0.01em; 20px tall):

.badge { display:inline-flex; align-items:center; gap:4px; height:20px; padding:0 8px; border-radius:var(--r-sm); font-size:12px; font-weight:500; letter-spacing:-.01em; }
.b-red { background:var(--red-dim); color:var(--red); } .b-amber { background:var(--amber-dim); color:var(--amber-text); }
.b-green { background:var(--brand-dim); color:var(--g-dark); } .b-blue { background:var(--blue-dim); color:var(--blue); }
.b-grey { background:var(--grey2); color:var(--text-3); }
.up { color:var(--g-dark); font-weight:600; } .down { color:var(--red); font-weight:600; }
<span class="badge b-amber">₦304.5M overdue</span>
<span class="badge b-green">Reconciled</span>
<td class="r"><span class="badge b-blue">2</span></td>

Document-status → color map (verbatim JS, use for every doc lifecycle):

var ST_BADGE={Paid:'b-green',Approved:'b-green',Accepted:'b-green',Invoiced:'b-green',Active:'b-green',Published:'b-green',Reimbursed:'b-blue',Open:'b-blue',Sent:'b-blue','Client Viewed':'b-blue','Pending Approval':'b-amber',Pending:'b-amber','Partially Paid':'b-amber',Expired:'b-grey',Billable:'b-amber',Unbilled:'b-grey','Non-Billable':'b-grey',Draft:'b-grey',Void:'b-grey','Written Off':'b-grey',Stopped:'b-grey',Closed:'b-grey',Overdue:'b-red',Declined:'b-red'};
function stBadge(s){ return s?'<span class="badge '+(ST_BADGE[s]||'b-grey')+'">'+s+'</span>':'<span class="muted">—</span>'; }

(b) .st-pill — compact transaction-state pill (11/600, used in banking txn tables):

.st-pill { font-size:11px; font-weight:600; border-radius:var(--r-sm); padding:2px 8px; letter-spacing:-.01em; }
.st-cat { background:var(--brand-dim); color:var(--g-dark); } .st-unc { background:var(--amber-dim); color:var(--amber-text); }
.st-man { background:var(--blue-dim); color:var(--blue); } .st-mat { background:rgba(48,164,108,.14); color:var(--g-dark); }
<span class="st-pill st-cat">Categorized</span>

(c) .feed-tag — fully-round pill on uncategorized feed rows: tag-match (green “Match found”) / tag-recog (blue “Recognized”):

.feed-tag { font-size:11px; font-weight:600; border-radius:var(--r-f); padding:2px 8px; white-space:nowrap; }
.tag-match { background:rgba(48,164,108,.14); color:var(--g-dark); } .tag-recog { background:var(--blue-dim); color:var(--blue); }

(d) .st-dot — 7px status dot before names (green #30A46C active / #C9CED6 inactive), CSS above in §3.5. (e) .sb-badge — sidebar count pill (amber, fully round). (f) .dep/.wd (green/red 600 amounts), .up/.down deltas, .acc-cnt (count pill in accordion titles: font-size:11px; font-weight:600; color:var(--text-3); background:var(--grey2); border-radius:20px; padding:1px 8px;).

(a) Dashboard KPI .kpi:

.kpi-row { display:grid; grid-template-columns:repeat(4,1fr); gap:16px; margin-bottom:16px; }
.kpi { background:var(--surface); border-radius:var(--r-lg); padding:16px 20px; display:flex; flex-direction:column; }
.kpi-top { display:flex; align-items:flex-start; justify-content:space-between; margin-bottom:8px; }
.kpi-lbl { font-size:12.5px; font-weight:500; color:var(--text-3); }
.kpi-ic { width:38px; height:38px; border-radius:50%; display:flex; align-items:center; justify-content:center; flex-shrink:0; }
.kpi-val { font-size:25px; font-weight:700; letter-spacing:-.02em; color:var(--ink); white-space:nowrap; }
.kpi-bar { height:7px; border-radius:var(--r-f); background:#EDEFF2; display:flex; overflow:hidden; margin-top:12px; }
.kpi-bar .seg-cur { background:var(--blue); cursor:pointer; } .kpi-bar .seg-od { background:var(--amber); cursor:pointer; }
.kpi-foot { margin-top:auto; padding-top:12px; font-size:12px; color:var(--text-3); display:flex; align-items:center; gap:8px; }

Tonal icon tints (shared across ALL stat/acct icons):

.ic-green { background:var(--brand-dim); color:var(--g-dark); }
.ic-amber { background:var(--amber-dim); color:var(--amber-text); }
.ic-blue { background:var(--blue-dim); color:var(--blue); }
.ic-ink { background:rgba(15,29,19,.07); color:var(--ink); }
.ic-grey { background:var(--grey2); color:var(--text-2); }
.ic-purple { background:var(--purple-dim); color:var(--purple); }

Markup (verbatim):

<div class="kpi">
<div class="kpi-top"><div class="kpi-lbl">Total Receivables</div><div class="kpi-ic ic-green"><svg width="19" height="19" …></svg></div></div>
<div class="kpi-val">₦372.58M</div>
<div class="kpi-bar"><div class="seg-cur" style="width:18%" data-tip-l="Current" data-tip-v="₦68.04M"></div><div class="seg-od" style="width:82%" data-tip-l="Overdue" data-tip-v="₦304.54M"></div></div>
<div class="kpi-foot"><span class="badge b-amber">₦304.5M overdue</span> Current ₦68.04M</div>
</div>

(b) Section stat tiles — .bk-stat (page-level, with shadow), .bk-s (grey tile inside a white overview card), .txn-s (account-detail summary):

.bk-stats { display:grid; grid-template-columns:repeat(4,1fr); gap:16px; margin-bottom:16px; }
.bk-stat { background:var(--surface); border-radius:var(--r-lg); box-shadow:var(--sh-card); padding:16px 20px; }
.bk-stat .l { font-size:12px; color:var(--text-3); display:flex; align-items:center; gap:8px; }
.bk-stat .l .i { width:30px; height:30px; border-radius:50%; display:flex; align-items:center; justify-content:center; }
.bk-stat .v { font-size:23px; font-weight:700; letter-spacing:-.02em; color:var(--ink); margin-top:10px; }
.bk-stat .s { font-size:11.5px; color:var(--text-3); margin-top:4px; }
.bk-statrow { display:grid; grid-template-columns:repeat(5,1fr); gap:16px; padding:16px 20px; }
.bk-s { background:var(--grey1); border-radius:var(--r-lg); padding:16px 16px; display:flex; align-items:center; gap:12px; min-width:0; }
.bk-s .i { width:38px; height:38px; border-radius:50%; display:flex; align-items:center; justify-content:center; flex-shrink:0; }
.bk-s .meta { min-width:0; }
.bk-s .l { font-size:12px; color:var(--text-3); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.bk-s .v { font-size:20px; font-weight:700; letter-spacing:-.02em; color:var(--ink); margin-top:4px; white-space:nowrap; }
.bk-s.neg .v { color:var(--red); }
.bk-s-unc { background:var(--amber-dim); }
.bk-s-unc .l { white-space:normal; line-height:1.25; }
.unc-n { min-width:44px; height:40px; padding:0 12px; border-radius:var(--r-md); background:rgba(255,197,61,.32); color:var(--amber-text); font-size:18px; font-weight:700; letter-spacing:-.02em; display:flex; align-items:center; justify-content:center; flex-shrink:0; }
.unc-link { display:inline-flex; align-items:center; gap:4px; font-size:12px; font-weight:600; color:var(--g-dark); margin-top:4px; cursor:pointer; }
.txn-summary { display:grid; grid-template-columns:repeat(4,1fr); gap:16px; margin-bottom:16px; }
.txn-s { background:var(--surface); border-radius:var(--r-lg); padding:16px 16px; }
.txn-s .l { font-size:12px; color:var(--text-3); } .txn-s .v { font-size:19px; font-weight:700; letter-spacing:-.02em; color:var(--ink); margin-top:4px; }
.txn-s .v.neg { color:var(--red); }

Usage notes: value sizes step down with hierarchy — 25 (dashboard KPI) → 23 (bk-stat) → 20 (bk-s) → 19 (txn-s/cf-v) → 18 (sum-stat). All values are 700 with −0.02em tracking (the sanctioned 700 usage). Icon is always a circular tonal chip (tinted bg + colored glyph, never solid). Negative amounts get .neg → red; negative sign uses U+2212 −, not hyphen.

.card { background:var(--surface); border-radius:var(--r-lg); padding:20px 24px; }
.card-h { display:flex; align-items:center; justify-content:space-between; margin-bottom:16px; }
.card-title { font-size:15px; font-weight:600; color:var(--text-1); }
.mini-period { display:inline-flex; align-items:center; gap:6px; font-size:12.5px; color:var(--text-2); padding:4px 10px; border-radius:var(--r-md); background:var(--bg); cursor:pointer; }
.bank-card { background:var(--surface); border-radius:var(--r-lg); padding:16px 20px; }
.bank-top { display:flex; align-items:center; gap:12px; margin-bottom:16px; }
.bank-ic { width:40px; height:40px; border-radius:50%; display:flex; align-items:center; justify-content:center; flex-shrink:0; }
.bank-nm { font-size:14px; font-weight:600; color:var(--text-1); } .bank-ty { font-size:11.5px; color:var(--text-3); }
.bank-bal { font-size:22px; font-weight:700; letter-spacing:-.02em; color:var(--ink); }
.bank-foot { display:flex; align-items:center; justify-content:space-between; margin-top:12px; font-size:12px; color:var(--text-3); }
.sum-card { background:var(--surface); border-radius:var(--r-lg); padding:16px 20px; margin-bottom:16px; }
.sum-card-t { font-size:13px; font-weight:600; color:var(--ink); margin-bottom:12px; display:flex; align-items:center; justify-content:space-between; }
.txn-card { background:var(--surface); border-radius:var(--r-lg); padding:16px 20px; }
.txn-card.flat { background:none; padding:0; }
.cust-form-card { background:var(--surface); border-radius:var(--r-lg); padding:24px 24px; max-width:900px; }
.est-doc { background:var(--surface); border-radius:var(--r-lg); padding:24px 28px; }
.set-panel { background:var(--surface); border-radius:var(--r-lg); overflow:hidden; }
.bk-overview { background:var(--surface); border-radius:var(--r-lg); margin-bottom:16px; }

Card header pattern (verbatim):

<div class="card" style="margin-bottom:16px">
<div class="card-h"><div class="card-title">Cash Flow</div><div class="dd" data-dd="period"><div class="mini-period dd-trigger"><span class="dd-label">This Fiscal Year</span> <svg class="dd-cv" width="13" height="13" …chevron…></svg></div></div></div>
…body…
</div>

Borders: NO card in this file has a border. Radius is --r-lg (12px) for content cards, --r-xl (16px) for .tbl-card and the quick-create panel. Cards are white --surface sitting on the grey --bg page and most carry no shadow at all (separation by contrast); shadow var(--sh-card) appears only on .bk-stat, .acct-card, .period and the active .seg button.on. Internal separations are border-top/bottom:1px solid var(--border-soft) on rows — never on the card box. The only dashed outline is the ghost .bank-add add-tile and .drop-zone (intentional affordances). Nested cards inside a white container flip to --grey1 (#txn-body .sum-card { background:var(--grey1); } — comment in file: “house each section as a grey1 card inside the white container (stat tiles above stay white to pop)”). Where a white card would sit on white, the wrapper goes transparent (.txn-card.flat).

3.10 Overlays: modal / mega-menu (no drawers, no bottom sheets on web)

Section titled “3.10 Overlays: modal / mega-menu (no drawers, no bottom sheets on web)”

(a) .dlg centered web modal — THE confirmation/task surface on web:

.dlg-overlay { position:fixed; inset:0; background:rgba(15,29,19,.34); z-index:400; display:flex; align-items:flex-start; justify-content:center; padding:46px 20px; overflow-y:auto; opacity:0; pointer-events:none; transition:opacity .15s; }
.dlg-overlay.show { opacity:1; pointer-events:auto; }
.dlg { width:100%; max-width:540px; background:var(--surface); border-radius:var(--r-lg); box-shadow:0 24px 64px rgba(0,0,0,.24); overflow:hidden; transform:translateY(10px); transition:transform .15s; margin:auto 0; }
.dlg-overlay.show .dlg { transform:none; }
.dlg.dlg-md { max-width:600px; } .dlg.dlg-lg { max-width:760px; }
.dlg.lg { max-width:780px; }
.dlg-head { display:flex; align-items:flex-start; justify-content:space-between; padding:16px 24px; border-bottom:1px solid var(--border-soft); }
.dlg-title { font-size:16px; font-weight:600; color:var(--ink); }
.dlg-sub { font-size:12.5px; color:var(--text-3); margin-top:4px; }
.dlg-close { width:32px; height:32px; border-radius:50%; display:flex; align-items:center; justify-content:center; color:var(--text-2); cursor:pointer; flex-shrink:0; }
.dlg-close:hover { background:var(--grey2); color:var(--text-1); }
.dlg-body { padding:20px 24px; max-height:62vh; overflow-y:auto; }
.dlg-foot { display:flex; align-items:center; justify-content:flex-end; gap:10px; padding:16px 24px; border-top:1px solid var(--border-soft); background:var(--grey1); }
.dlg-foot.between { justify-content:space-between; }

Composition (assembled by JS — the canonical shape):

<div id="modal-root">
<div class="dlg-overlay" onclick="if(event.target===this)closeModal()">
<div class="dlg dlg-md">
<div class="dlg-head"><div><div class="dlg-title">Record Payment</div><div class="dlg-sub">for INV-2231 · Acme Ltd</div></div><div class="dlg-close" onclick="closeModal()">…x…</div></div>
<div class="dlg-body">…fields…</div>
<div class="dlg-foot"><button class="btn-sec" onclick="closeModal()">Cancel</button><button class="btn-pri" onclick="applyPayment('inv1')">Record Payment</button></div>
</div>
</div>
</div>

Footer button placement: right-aligned; Cancel (.btn-sec) first, then the primary (.btn-pri). Footer sits on a --grey1 strip with a soft top border. .dlg-foot.between splits (e.g. rules modal: “+ New rule” link left, “Done” right). Destructive confirm = confirmModal(title, body, confirmLabel, onConfirm, danger) which renders the primary with inline background:var(--red);color:#fff. Sizes: default 540, dlg-md 600, dlg-lg 760, lg 780. Overlay click (backdrop only) and Escape both close. Used for: confirms, record payment/refund, add/edit account, import stepper, transaction rules, reconcile, compose email, invite user/role/tax/location, new tax.

(b) .qc-overlay Quick-Create mega menu (topbar “+” — an anchored panel, not a modal):

.qc-overlay { position:fixed; inset:0; z-index:200; display:none; }
.qc-overlay.show { display:block; }
.qc-backdrop { position:absolute; inset:0; background:rgba(15,29,19,.10); }
.qc-panel { position:absolute; top:66px; right:24px; width:748px; background:var(--surface); border-radius:16px;
box-shadow:0 20px 56px rgba(15,29,19,.18), 0 6px 16px rgba(0,0,0,.06); padding:20px 24px 24px;
transform-origin:top right; animation:qcIn .16s cubic-bezier(.2,.8,.2,1); }
@keyframes qcIn { from { opacity:0; transform:scale(.97) translateY(-6px); } to { opacity:1; transform:none; } }
.qc-head { display:flex; align-items:center; justify-content:space-between; padding-bottom:16px; border-bottom:1px solid var(--border-soft); }
.qc-title { font-size:15px; font-weight:600; color:var(--ink); }
.qc-close { width:30px; height:30px; border-radius:50%; background:var(--grey2); color:var(--text-2); display:flex; align-items:center; justify-content:center; cursor:pointer; }
.qc-close:hover { background:var(--hover); color:var(--text-1); }
.qc-grid { display:grid; grid-template-columns:repeat(4,1fr); gap:16px; margin-top:16px; }
.qc-col-h { display:flex; align-items:center; gap:8px; font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:.06em; color:var(--text-3); padding:0 8px 6px; }
.qc-col-h .dot { width:7px; height:7px; border-radius:50%; }
.qc-item { display:flex; align-items:center; gap:10px; padding:8px 8px; border-radius:var(--r-md); font-size:13px; color:var(--text-1); cursor:pointer; }
.qc-item:hover { background:var(--grey1); }
.qc-item:hover .qc-ic { background:var(--brand-dim); color:var(--g-dark); }
.qc-ic { width:30px; height:30px; border-radius:50%; background:var(--grey2); color:var(--text-2); display:flex; align-items:center; justify-content:center; flex-shrink:0; transition:background .12s,color .12s; }
<div class="qc-overlay" id="qc">
<div class="qc-backdrop" onclick="closeQC()"></div>
<div class="qc-panel">
<div class="qc-head"><div class="qc-title">Create new</div><div class="qc-close" onclick="closeQC()">…x…</div></div>
<div class="qc-grid">
<div>
<div class="qc-col-h"><span class="dot" style="background:var(--ink)"></span>General</div>
<div class="qc-item" onclick="closeQC();openJournalForm()"><span class="qc-ic">…svg…</span>Journal Entry</div>
…
</div>
<div><div class="qc-col-h"><span class="dot" style="background:var(--brand)"></span>Sales</div>…</div>
<div><div class="qc-col-h"><span class="dot" style="background:var(--blue)"></span>Purchases</div>…</div>
<div><div class="qc-col-h"><span class="dot" style="background:var(--amber)"></span>Banking</div>…</div>
</div>
</div>
</div>

Column headers are color-dot-coded per module (ink=General, brand=Sales, blue=Purchases, amber=Banking). There is no drawer and no bottom sheet anywhere in the file — detail views are full screen swaps, tasks are .dlg modals.

CSS (verbatim — canonical .alert-toast pill, top-center):

#toast-container { position:fixed; top:24px; left:50%; transform:translateX(-50%); display:flex; flex-direction:column; align-items:center; gap:10px; z-index:9999; pointer-events:none; }
.toast-live { display:inline-flex; align-items:center; gap:10px; border-radius:16px; padding:6px 16px 6px 6px; pointer-events:all; opacity:0; transform:translateY(-16px); transition:opacity .25s ease, transform .25s ease; box-shadow:0 4px 24px rgba(0,0,0,.13); max-width:90vw; }
.toast-live.toast-in { opacity:1; transform:translateY(0); }
.toast-live.toast-out { opacity:0; transform:translateY(-10px); }
.toast-live .alert-toast-badge { display:flex; align-items:center; justify-content:center; background:rgba(255,255,255,.85); border-radius:12px; padding:4px 10px; font-size:13px; font-weight:500; line-height:20px; white-space:nowrap; flex-shrink:0; }
.toast-live .alert-toast-text { font-size:13px; font-weight:500; line-height:20px; }
.toast-live .toast-close { background:none; border:none; cursor:pointer; padding:0 2px; opacity:.55; display:flex; align-items:center; color:currentColor; margin-left:4px; flex-shrink:0; }
.toast-live .toast-close:hover { opacity:1; }
.alert-toast--default { background:#e6f4fe; } .alert-toast--default .alert-toast-badge, .alert-toast--default .alert-toast-text { color:#0d74ce; }
.alert-toast--gray { background:#f0f0f0; } .alert-toast--gray .alert-toast-badge, .alert-toast--gray .alert-toast-text { color:#646464; }
.alert-toast--error { background:#ffebe8; } .alert-toast--error .alert-toast-badge, .alert-toast--error .alert-toast-text { color:#d8121c; }
.alert-toast--warning { background:var(--input-bg)7c2; } .alert-toast--warning .alert-toast-badge, .alert-toast--warning .alert-toast-text { color:#9e6c00; }
.alert-toast--success { background:#e6f6eb; } .alert-toast--success .alert-toast-badge, .alert-toast--success .alert-toast-text { color:#05853e; }

⚠ In-file bug: .alert-toast--warning { background:var(--input-bg)7c2; } is a corrupted value — the canonical DS value is a pale yellow (#fff7c2). Do not copy the bug.

JS helper (verbatim signature + auto-type inference + badge words):

var TOAST_BADGE = { success:'Done', default:'Info', gray:'Note', warning:'Heads up', error:'Error' };
function toast(msg, type){
if(!type){
if(/prototype|copied|not in this/i.test(msg)) type='default';
else if(/must be|cannot|unable|failed|error|invalid/i.test(msg)) type='warning';
else if(/deleted|excluded|reconcile later|marked inactive/i.test(msg)) type='gray';
else type='success';
}
… el.className = 'toast-live alert-toast alert-toast--' + type;
… '<div class="alert-toast-badge">' + (TOAST_BADGE[type]||'Info') + '</div><span class="alert-toast-text">' + msg + '</span>'
+ '<button class="toast-close" aria-label="Dismiss" onclick="this.closest(\'.toast-live\').remove()">…12px x…</button>';
… auto-dismiss after 3600ms (toast-in → toast-out, remove at +300ms)
}

Structure: badge word chip (white 85% pill) + message + close ×. Top-center, stacked with 10px gap. Never a custom black bottom pill.

.fld { margin-bottom:16px; } .fld:last-child { margin-bottom:0; }
.fld > label { display:block; font-size:12px; font-weight:500; color:var(--text-2); margin-bottom:6px; }
.fld .req { color:var(--red); margin-left:1px; }
.fld input[type=text],.fld input[type=number],.fld input[type=date],.fld select,.fld textarea,
.dlg input[type=text],.dlg input[type=number],.dlg input[type=date] { width:100%; height:42px; padding:0 12px; border:1px solid var(--border); border-radius:var(--r-md); font:inherit; font-size:13.5px; color:var(--text-1); background:var(--input-bg); outline:none; transition:border-color .12s, box-shadow .12s; }
.fld textarea { height:auto; min-height:66px; }
.dlg input:focus { border-color:var(--brand); box-shadow:0 0 0 3px var(--brand-dim); }
/* native selects in dialogs: custom chevron with room from the right edge */
.fld select, .dlg select { width:100%; height:42px; border:1px solid var(--border); border-radius:var(--r-md); font:inherit; font-size:13.5px; color:var(--text-1); outline:none; transition:border-color .12s, box-shadow .12s; appearance:none; -webkit-appearance:none; -moz-appearance:none; padding:0 36px 0 12px; background-color:var(--input-bg); background-repeat:no-repeat; background-position:right 12px center; background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%23888888' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'/></svg>"); }
.fld select:focus, .dlg select:focus { border-color:var(--brand); box-shadow:0 0 0 3px var(--brand-dim); }
.fld textarea { height:auto; min-height:62px; padding:8px 12px; resize:vertical; }
.fld input:focus,.fld select:focus,.fld textarea:focus { border-color:var(--brand); box-shadow:0 0 0 3px var(--brand-dim); }
.fld-row { display:grid; grid-template-columns:1fr 1fr; gap:16px; }
.fld-hint { font-size:11.5px; color:var(--text-3); margin-top:4px; }
.radio-row { display:flex; gap:10px; flex-wrap:wrap; }
.radio-pill { font-size:13px; font-weight:500; color:var(--text-2); padding:8px 16px; border:1px solid var(--border); border-radius:var(--r-md); cursor:pointer; }
.radio-pill.on { border-color:var(--brand); background:var(--brand-dim); color:var(--g-dark); font-weight:600; }
.dlg-section-t { font-size:12px; font-weight:600; color:var(--text-3); text-transform:uppercase; letter-spacing:.04em; margin:16px 0 10px; }
.dlg-section-t:first-child { margin-top:0; }
.chk { width:17px; height:17px; accent-color:var(--brand); cursor:pointer; }
.fld-3 { display:grid; grid-template-columns:140px 1fr 1fr; gap:16px; }

Settings-row controls (40px variant) + settings scaffolding:

.set-wrap { max-width:1080px; margin:0 auto; }
.settings-layout { display:grid; grid-template-columns:208px 1fr; gap:20px; align-items:start; }
.set-sec { padding:24px 28px; }
.set-sec-h { font-size:15px; font-weight:600; color:var(--ink); }
.set-sec-s { font-size:13px; color:var(--text-3); margin-top:4px; margin-bottom:16px; line-height:1.6; }
.set-row { display:flex; align-items:center; justify-content:space-between; gap:24px; padding:14px 0; border-top:1px solid var(--border-soft); }
.set-row:first-of-type { border-top:none; }
.set-row-info { flex:1; min-width:0; }
.set-row-label { font-size:13.5px; font-weight:500; color:var(--text-1); }
.set-row-sub { font-size:12px; color:var(--text-3); margin-top:2px; }
.set-ctl { width:320px; flex-shrink:0; display:flex; justify-content:flex-end; }
.set-ctl input, .set-ctl select { width:100%; height:40px; padding:0 12px; border:1px solid var(--border); border-radius:var(--r-md); font:inherit; font-size:13.5px; color:var(--text-1); background:var(--input-bg); outline:none; transition:border-color .12s, box-shadow .12s; }
.set-ctl input:focus, .set-ctl select:focus { border-color:var(--brand); box-shadow:0 0 0 3px var(--brand-dim); }
.set-foot { padding:16px 28px; border-top:1px solid var(--border-soft); background:var(--grey1); display:flex; justify-content:flex-end; gap:10px; }
.set-avatar { width:64px; height:64px; border-radius:50%; background:var(--brand-dim); color:var(--g-dark); display:flex; align-items:center; justify-content:center; font-size:22px; font-weight:700; flex-shrink:0; }

Line-item table inputs (40px), import stepper, drop-zone, file control:

.li-table { width:100%; border-collapse:collapse; margin-bottom:12px; }
.li-table th { font-size:11px; text-transform:uppercase; letter-spacing:.04em; color:var(--text-3); text-align:left; padding:0 8px 8px; font-weight:600; }
.li-table th.r, .li-table td.r { text-align:right; }
.li-table td { padding:4px 8px; vertical-align:middle; }
.li-table input { width:100%; height:40px; border:1px solid var(--border); border-radius:var(--r-md); padding:0 12px; font:inherit; font-size:13.5px; color:var(--text-1); background:var(--input-bg); outline:none; transition:border-color .12s, box-shadow .12s; }
.li-table input:focus { border-color:var(--brand); box-shadow:0 0 0 3px var(--brand-dim); }
.li-table input.r { text-align:right; }
.li-table select { …40px, same chevron data-URI, padding:0 32px 0 12px… }
.li-table input[type="checkbox"], .li-table input.chk { width:17px; height:17px; min-width:0; padding:0; accent-color:var(--brand); cursor:pointer; }
/* line-item "Item Details": item name + description side by side, not stacked */
.li-table input[class$="-name"] { display:inline-block; width:56%; vertical-align:top; }
.li-table input[class$="-desc"] { display:inline-block; width:calc(44% - 6px); margin-left:6px !important; margin-top:0 !important; vertical-align:top; font-size:13px !important; color:var(--text-1) !important; }
.li-amt { font-weight:600; color:var(--ink); white-space:nowrap; }
.li-x { color:var(--text-3); cursor:pointer; display:inline-flex; } .li-x:hover { color:var(--red); }
.est-form-totals { max-width:330px; margin-left:auto; margin-top:6px; }
.est-form-totals .row { display:flex; justify-content:space-between; align-items:center; gap:12px; padding:8px 0; font-size:13px; color:var(--text-2); }
.est-form-totals .row input { width:130px; height:36px; border:1px solid var(--border); border-radius:var(--r-md); padding:0 10px; text-align:right; font:inherit; font-size:13px; outline:none; }
.est-form-totals .row.grand { border-top:1px solid var(--border-soft); margin-top:4px; padding-top:12px; font-size:15px; font-weight:700; color:var(--ink); }
.stepper { display:flex; align-items:center; gap:8px; margin-bottom:16px; }
.step { display:flex; align-items:center; gap:8px; font-size:12.5px; font-weight:500; color:var(--text-3); }
.step .n { width:22px; height:22px; border-radius:50%; background:var(--grey2); color:var(--text-3); display:flex; align-items:center; justify-content:center; font-size:11px; font-weight:600; }
.step.on { color:var(--g-dark); } .step.on .n { background:var(--brand); color:var(--ink); }
.step.done .n { background:var(--brand-dim); color:var(--g-dark); }
.step-line { flex:1; height:1px; background:var(--border); min-width:18px; }
.drop-zone { border:1.5px dashed var(--border); border-radius:var(--r-lg); padding:32px; text-align:center; color:var(--text-3); cursor:pointer; }
.drop-zone:hover { border-color:var(--brand); color:var(--g-dark); background:var(--brand-dim); }
.file-drop { display:inline-flex; align-items:center; gap:8px; font-size:13px; font-weight:500; color:var(--g-dark); background:var(--grey1); border-radius:var(--r-md); padding:8px 16px; cursor:pointer; }
.file-drop:hover { background:var(--hover); }

Markup (verbatim field + row from a modal):

<div class="fld-row">
<div class="fld"><label>Amount Received</label><input id="rp-amt" type="text" value="372580000"></div>
<div class="fld"><label>Payment Date</label><input id="rp-date" type="text" value="19 Jun 2026"></div>
</div>
<div class="fld"><label>Deposit To</label><select id="rp-acct"><option>GTBank · Operations</option><option>Sterling · Collections</option><option>Petty Cash</option></select></div>
<div class="fld"><label>Tax Name <span class="req">*</span></label><input id="nt-name" type="text" placeholder="e.g. VAT"></div>

Usage notes: input heights — 42px in dialogs/forms, 40px in settings rows and line-item tables, 36px in the totals column, 34px in inline search. Focus ring is always border-color:var(--brand) + box-shadow:0 0 0 3px var(--brand-dim). Required marker = red * in .req. Selects are appearance:none with a grey (#888) chevron data-URI. Validation: on failed save the field gets style.borderColor='var(--red)' + focus + a warning toast — no inline error text pattern in this file.

.dd { position:relative; }
.dd-menu { position:absolute; top:calc(100% + 8px); right:0; min-width:208px; background:var(--surface);
border-radius:12px; box-shadow:0 14px 40px rgba(15,29,19,.16), 0 4px 10px rgba(0,0,0,.05); padding:6px; z-index:120;
display:none; animation:ddIn .14s ease; }
.dd.open > .dd-menu { display:block; }
@keyframes ddIn { from { opacity:0; transform:translateY(-5px); } to { opacity:1; transform:none; } }
.dd-cv { transition:transform .16s; }
.dd.open .dd-cv { transform:rotate(180deg); }
.dd-opt { display:flex; align-items:center; gap:10px; padding:8px 10px; border-radius:var(--r-md); font-size:13px; color:var(--text-1); cursor:pointer; white-space:nowrap; }
.dd-opt:hover { background:var(--grey1); }
.dd-opt.sel { color:var(--g-dark); font-weight:500; }
.dd-opt .ck { margin-left:auto; color:var(--g-dark); }
.dd-opt svg.oi { color:var(--text-3); flex-shrink:0; }
.dd-opt.danger { color:var(--red); } .dd-opt.danger svg.oi { color:var(--red); }
.dd-divider { height:1px; background:var(--border-soft); margin:6px 4px; }
.dd-flt { font-size:10.5px; font-weight:600; letter-spacing:.04em; text-transform:uppercase; color:var(--text-3); padding:8px 12px 4px; }
.dd-head { padding:8px 10px 4px; display:flex; align-items:center; justify-content:space-between; }
.dd-head .nm { font-size:13px; font-weight:600; color:var(--text-1); }
.dd-head .em { font-size:11.5px; color:var(--text-3); margin-top:1px; }
.dd-head .link { font-size:11.5px; font-weight:500; color:var(--g-dark); cursor:pointer; }
.dd-note { display:flex; gap:10px; padding:8px 10px; border-radius:var(--r-md); cursor:pointer; }
.dd-note:hover { background:var(--grey1); }
.dd-note .nt-dot { width:7px; height:7px; border-radius:50%; background:var(--brand); margin-top:4px; flex-shrink:0; }
.dd-note.read .nt-dot { background:var(--border); }
.dd-note .nt-t { font-size:12.5px; color:var(--text-1); }
.dd-note .nt-m { font-size:11.5px; color:var(--text-3); margin-top:1px; }

Wiring (verbatim — one global delegated handler, no per-dd JS):

// Generic dropdown open/close + selection
document.addEventListener('click', function(e){
var trig = e.target.closest('.dd-trigger');
if (trig){
var dd = trig.closest('.dd'); var wasOpen = dd.classList.contains('open');
closeAllDD(); closeQC();
if (!wasOpen) dd.classList.add('open');
e.stopPropagation(); return;
}
var opt = e.target.closest('.dd-opt');
if (opt){
var dd = opt.closest('.dd');
if (opt.hasAttribute('data-val')){
var lbl = dd.querySelector('.dd-label'); if (lbl) lbl.textContent = opt.getAttribute('data-val');
dd.querySelectorAll('.dd-opt').forEach(function(o){ o.classList.remove('sel'); var c=o.querySelector('.ck'); if(c) c.remove(); });
opt.classList.add('sel'); opt.insertAdjacentHTML('beforeend', CK);
if (dd.getAttribute('data-dd') === 'accounts') filterCfAccount(opt.getAttribute('data-val'));
}
dd.classList.remove('open'); return;
}
if (!e.target.closest('.dd-menu')) closeAllDD();
});

Pattern: any trigger is <element class="… dd-trigger"> inside <div class="dd">, followed by .dd-menu. Selected option = .sel + appended CK checkmark SVG; the trigger’s .dd-label text is swapped. Trigger chevron uses class dd-cv (rotates 180° open). data-dd="period|range|accounts|…" menus are auto-built from DD_OPTS. Variants: selection menu (.sel+.ck), action menu (.dd-opt + svg.oi icon + .danger), grouped menu (.dd-flt uppercase group labels), notifications (.dd-head + .dd-note), account switcher (.sw-ic circular chip + .sw-nm + .sw-cur), and the mega add-transaction menu (.dd-menu containing .tt-grid):

.tt-grid { display:grid; grid-template-columns:1fr 1fr; gap:16px; }
.tt-col-h { font-size:12px; font-weight:600; color:var(--text-3); text-transform:uppercase; letter-spacing:.04em; margin-bottom:8px; display:flex; align-items:center; gap:8px; }
.tt-item { display:flex; align-items:center; gap:8px; font-size:13.5px; color:var(--text-1); padding:8px 10px; border-radius:var(--r-md); cursor:pointer; }
.tt-item:hover { background:var(--brand-dim); color:var(--g-dark); }
.tt-item .d { width:7px; height:7px; border-radius:50%; flex-shrink:0; }
.txn-empty { text-align:center; padding:48px 20px; color:var(--text-3); }
.txn-empty .ic { width:54px; height:54px; border-radius:50%; background:var(--grey2); color:var(--text-3); display:flex; align-items:center; justify-content:center; margin:0 auto 16px; }
.txn-empty .t { font-size:14px; font-weight:600; color:var(--text-1); margin-bottom:4px; }
.txn-empty .m { font-size:13px; max-width:420px; margin:0 auto 16px; line-height:1.5; }
.gs-empty { padding:18px 12px; text-align:center; font-size:12.5px; color:var(--text-3); }
.sum-empty { font-size:12.5px; color:var(--text-3); padding:8px 0; }

Markup (verbatim from emptyTxns() and a filtered-list empty):

<div class="txn-empty"><div class="ic">…bank svg 24…</div><div class="t">No transactions yet</div><div class="m">There are no transactions recorded for this account. Begin by adding new transactions or simply import your latest account statement.</div></div>
<tr><td colspan="8"><div class="txn-empty" style="padding:32px"><div class="t">No customers</div><div class="m">No customers match this view.</div></div></td></tr>

Notes: full empty state (icon + title + message) only when a container is truly empty; filtered-empty inside a table drops the icon and shrinks padding to 32px. Search-empty echoes the query: No matches … match "q".

All icon containers are circular (border-radius:50%) and tonal — tinted background (*-dim token) + colored glyph, never a solid fill, never a rounded square. Established sizes in this file:

container size context
.txn-acct .i 46px account detail header
.bank-ic 40px dashboard bank card
.kpi-ic, .acct-ic, .bk-s .i 38px KPI / table rows / stat tiles
.avatar, .cmt .av, .dlg-close 32px user/comment avatars, dialog close
.bk-stat .l .i, .qc-ic, .qc-close, .row-menu-btn 30px inline stat, quick-create, kebab
.gs-ic 28px global-search result
.sw-ic 26px account-switcher menu
.step .n 22px stepper number
.txn-empty .ic 54px empty state
.set-avatar 64px settings profile
.cust-avatar (42px, .lg 56px) avatars initials chips, brand-dim/g-dark

Tint classes: ic-green / ic-amber / ic-blue / ic-ink / ic-grey / ic-purple (§3.8). Avatars = brand-dim bg + --g-dark initials at 600. Glyphs are inline lucide-style SVGs, stroke-width 1.8–1.9 (2.2–2.4 for small chevrons/arrows), generated via svg(path, size) with default stroke-width 1.85. Small square swatches (chart legends: .legend i 10px, .exp-row .sw 10px, .chart-tip .tr i 8px) use 2–3px radius — swatches, not icon containers. One rectangular exception: .unc-n (uncategorized count, 40px tall, --r-md) and .sum-side-tile .n — these are number tiles, not icons.

.cf-wrap { display:grid; grid-template-columns:1fr 286px; gap:28px; align-items:center; }
.cf-chart { width:100%; height:236px; }
.ax { font-size:10px; fill:#b9bdc4; font-family:inherit; }
.cf-sum > div { padding:12px 0; }
.cf-sum > div + div { border-top:1px solid var(--border-soft); }
.cf-k { font-size:12px; color:var(--text-3); margin-bottom:4px; }
.cf-v { font-size:19px; font-weight:700; letter-spacing:-.02em; color:var(--ink); display:flex; align-items:center; justify-content:space-between; }
.cf-v .op { font-size:16px; color:var(--text-3); font-weight:500; }
.cf-in { color:var(--g-dark); } .cf-out { color:var(--red); }
.bars { display:flex; align-items:flex-end; gap:16px; height:188px; padding-top:10px; }
.bar-grp { flex:1; display:flex; align-items:flex-end; justify-content:center; gap:4px; height:100%; }
.bar { width:13px; border-radius:4px 4px 0 0; }
.bar.inc { background:var(--brand); } .bar.exp { background:#D7DBE0; }
.bars-x { display:flex; gap:16px; margin:8px 0 16px; }
.bars-x span { flex:1; text-align:center; font-size:11px; color:var(--text-3); }
.legend { display:flex; gap:16px; }
.legend span { display:inline-flex; align-items:center; gap:8px; font-size:12px; color:var(--text-2); }
.legend i { width:10px; height:10px; border-radius:3px; }
.chart-tip { position:fixed; transform:translate(-50%,-100%); background:var(--ink); color:#fff; padding:8px 12px; border-radius:8px; font-size:12px; pointer-events:none; opacity:0; transition:opacity .12s; z-index:9999; white-space:nowrap; box-shadow:0 6px 16px rgba(0,0,0,.18); text-align:center; }
.chart-tip.show { opacity:1; }
.chart-tip .l { display:block; font-size:10.5px; color:#9fb0a4; margin-bottom:4px; }
.chart-tip .v { font-weight:700; }
.chart-tip .rows { display:block; text-align:left; }
.chart-tip .tr { display:flex; align-items:center; gap:8px; font-size:11.5px; padding:2px 0; white-space:nowrap; }
.chart-tip .tr i { width:8px; height:8px; border-radius:2px; flex-shrink:0; }
.chart-tip .tr .tk { color:#c2cdc6; }
.chart-tip .tr b { margin-left:auto; padding-left:16px; color:#fff; font-weight:600; }

Charts are hand-authored inline SVG (line/area with preserveAspectRatio="none", gradient fill #01E46F .20→0, gridlines #EDEFF2, axis text .ax 10px #b9bdc4), hoverable via invisible hit targets carrying data-tip-l/data-tip-v (single value) or data-tip-rows="Name|#color|value;…" (multi-series). A single global .chart-tip (dark --ink pill) follows the cursor for multi-row tips or anchors above the target for single tips. Donut = stacked <circle stroke-dasharray> at r=15.9155 in a 42-viewBox, stroke 5.5, with .exp-row legend list. The multi-account Banking chart rebases each series to index 100 at period start (per Caesar chart conventions; absolute balances live in the tooltip), with clickable .legend span[data-acct] filtering series opacity (1 vs .14).

Other minor atoms present: .kv key/value rows, .sum-acc accordions (chevron rotates 180°, .sum-acc-b collapses), .cmt comment threads, .est-banner status banner (dot + text, colored by status), .est-doc printable document card, .est-hist-item history feed (9px brand dot + text + timestamp), .stepper (Configure → Map fields → Preview), .rec-bal reconcile balance trio, .rule-item/.crit-row rules builder, .feed-row uncategorized bank feed with expandable .feed-panel, .bulk-bar, .inc-chart mini bar chart with .inc-tip hover, .rpt-grid/.rpt-nav/.rpt-link/.rpt-net report scaffolding, .settings-layout.


Every page lives in <div class="page app-screen" id="scr-…">; the shell (sidebar/topbar) never re-renders. Screen ids: list scr-invoices, detail scr-invoice, form scr-invoice-form.

Top-to-bottom: .pg-head (greeting “Hello, Francis” + sub + .period fiscal-year dropdown) → .kpi-row (4 KPI cards) → “Bank and Credit Cards” section (.card-h header w/ .card-link “Go to Banking” + .row.row-3 of .bank-cards) → Cash Flow .card (.cf-wrap: SVG line chart + .cf-sum right column: opening / incoming(+) / outgoing(−) / closing(=)) → .row.row-2 with Income & Expense .card (grouped bars + .bars-x + .legend) and Top Expenses .card (donut + .exp-list). Grids: .row { display:grid; gap:16px; margin-bottom:16px; } .row-2 {1fr 1fr} .row-3 {1fr 1fr 1fr}. Every card head carries its own .mini-period dropdown. Bank cards click through to the account’s Summary view.

.pg-head w/ .pg-actions (Import Statement .btn-sec, Add Bank / Credit Card .btn-pri) → .bk-overview white card: .bk-ov-head (account-switch dd + range dd) / .bk-statrow (5 grey .bk-s tiles, last one amber .bk-s-unc w/ count + “Categorize now”) / collapsible .bk-cf-head+.bk-cf-body (multi-series indexed chart, Hide/Show chart toggle) → .tbl-card Accounts table (view dropdown in head; rows injected with st-dots + kebab menus; whole row → openAccount(id,'summary')) → .tbl-foot group cash position.

4.3 Account transactions (#scr-bank-txns) — detail, 2 levels deep (sidebar auto-collapses)

Section titled “4.3 Account transactions (#scr-bank-txns) — detail, 2 levels deep (sidebar auto-collapses)”

.pg-head variant: .txn-acct identity block that is itself an account-switcher dropdown (46px tonal icon + 19px name + chevron + meta) + .txn-actions (Add Transaction .btn-pri w/ mega dd, Import Statement .btn-sec, gear dd: import / rules / reconcile / edit) → .txn-summary (4 .txn-s tiles) → .txn-card containing .txn-bar (left: .seg Account Summary | Transactions; right .txn-tools: .txn-search + .txn-tabs All / Uncategorized n / Categorized) → #txn-body (sortable .tbl, or .feed list for uncategorized with checkboxes, expandable categorize/match panels and bulk actions, or the Summary view: .sum-grid of grey1 .sum-cards + .sum-side-tiles + accordions). Tools hide in Summary view.

4.4 List pages (Customers, Invoices, Bills, … — all identical skeleton)

Section titled “4.4 List pages (Customers, Invoices, Bills, … — all identical skeleton)”

.pg-head (title + sub, right: single .btn-pri “New X”) → .tbl-card: .tbl-card-head (left: view-filter dropdown styled as the card title with chevron; right: .txn-search + .gear-btn hamburger dd w/ Sort by group, Import/Export/Refresh) → .bulk-bar (hidden until checkboxes selected) → .tscroll > table.tbl (checkbox col 34px, sortable headers, status via stBadge(), right-aligned .amt money, kebab dd last col 40px). Row click (delegated) opens detail; checkbox/menu/input clicks are excluded. Empty → in-table .txn-empty.

4.5 Record detail (Invoice/Bill/Estimate/… #scr-invoice)

Section titled “4.5 Record detail (Invoice/Bill/Estimate/… #scr-invoice)”

.cust-detail-head (21px doc number + stBadge inline + .pg-sub meta; right .txn-actions: Comments & History .btn-sec, Edit, Send, PDF/Print, Journal, status-dependent primary e.g. Record Payment, kebab dd w/ Mark as Sent / Clone / Credit Note / Write Off / Void / ─ / Delete) → .est-banner status banner (colored dot + sentence) → .est-doc printable document (org block, .est-doc-title “INVOICE”, .est-meta, .est-billto, table.tbl line items, .est-tot-rows totals with .grand rows, Notes / Attachments / Terms blocks) → comments & history section (toggleable). Contact detail (Customer/Vendor) instead uses: avatar .cust-avatar.lg + name + active pill + .txn-summary 3-tile strip + .seg pill tabs (Overview / Comments / Transactions / Statement) over #cust-det-body; Overview = .sum-grid of .sum-cards (kv rows, addresses, income mini-chart).

.pg-head (title “New Invoice” / “Edit Invoice” + sub) — showScreen() relocates the trailing .form-actions row into the head as pg-actions → .cust-form-card (white, max-width 900 (1040 for invoice), fields in .fld-row 2-col grids, .dlg-section-t uppercase section labels, .li-table line-item editor with “+ Add another line” .link-add, .est-form-totals right-aligned totals, notes/terms textareas, .file-drop attach) → actions: primary first (“Save and Send” .btn-pri), then secondaries (“Save as Draft”, “Make Recurring”, “Cancel”). Cancel returns to detail (edit) or list (new). Validation = red border + warning toast.

.set-wrap (max 1080) → .pg-head → .settings-layout (208px sticky .settings-nav of .sn-items | .set-panel white card) → .set-sec (15px heading + 13px sub) with stacked .set-rows (label+sub left, 320px .set-ctl right) → .set-foot grey strip, right-aligned Save .btn-pri. Team section nests a .seg (Users/Roles) + .tbl. Radio-card group (journal approvals) = full-width labels w/ brand-dim bg when selected.

4.8 Reports (#scr-reports → #scr-report) & Documents (#scr-documents)

Section titled “4.8 Reports (#scr-reports → #scr-report) & Documents (#scr-documents)”

Reports Center: .pg-head (+ Custom Report .btn-sec) → .rpt-grid (220px .rpt-nav category rail, scroll-to-section; right: .sum-card per category with .rpt-link rows). Report viewer: .cust-detail-head (name + range dd + Export/Print) → .est-doc with centered report header + .tbl.rpt-tbl + .rpt-net grand-total strip (brand-dim, red .neg variant); sidebar collapses; account/customer names linkify to their details. Documents: same .rpt-grid scaffold with folder rail + files table.

  1. Row click = open detail. Detail = full-screen swap (screen div + crumbs + window.scrollTo(0,0)), never a modal/drawer. Back = clickable breadcrumb.
  2. Modals (.dlg) are for tasks and confirmations only (record payment, import, rules, reconcile, invite, confirm delete/void). Confirmations on web are centered .dlg — never bottom sheets.
  3. Default selections: banking rows open on the Summary view; account screen defaults to Transactions/All when opened from elsewhere; settings defaults to profile (topbar) or organization (sidebar); first .seg option is preselected.
  4. Search lives per-list (.txn-search inside .tbl-card-head) + one global search in the topbar (/ shortcut, grouped results, ↑↓/Enter).
  5. Destructive actions confirm via confirmModal(…, danger) (red primary) for void/write-off; simple deletes act immediately + toast (prototype behavior).
  6. Every control works — unbuilt destinations toast “… is not in this prototype” (type default), never a dead click.
  7. Sidebar depth rule: list page = expanded + section open + subitem active; detail/form (2 levels) = body.sb-collapsed (forward-only; expanding is user-controlled).
  8. Escape closes: search panel, quick-create, dropdowns, modal. Backdrop click closes modal & quick-create.
  9. Mutations always: update the data array → toast(…) → re-render the affected screen (openX(id) again) and keep counts in sync (syncAcctBadges()).

Navigation & shell:

  • navTo(name, el) — show #scr-<name>, set sidebar active, close submenus, renderCrumbs(name), scroll top.
  • showScreen(id) — bare screen swap + form-action relocation + closeAllDD(); closeQC(); window.scrollTo(0,0).
  • renderCrumbs(name, extra) — builds topbar trail from CRUMB_TRAILS[name], appends optional extra leaf.
  • crumbNav(to) — crumb click router → openX() functions.
  • topActive(tip) / navActive(menuTip, label) + salesActive(label), purchasesActive(label), accountantActive(label) — sidebar state; navActive collapses sidebar when not on a list screen.
  • toggleSub(el) — sidebar accordion; toggleSidebar() — collapse/expand rail; toggleTheme() — dark mode (+localStorage caesar-theme).
  • toggleQC(e) / closeQC() — quick-create; closeAllDD() — close all dropdowns.
  • activeScreen() → current .app-screen.show id.

Per-entity screen contract (repeated for customers, estimates, invoices, paymentsR/M, recInv, creditNotes, vendors, bills, expenses, recBills, recExpenses, vendorCredits, COA, journals, recJournals, bulk, curradj):

  • openXs() (list) / openX(id) (detail) / openXForm(id?) (create/edit form) — e.g. openInvoices(), openInvoice(id), openInvoiceForm(id).
  • xsListHTML() / xDetailHTML(rec) / xFormHTML(rec); xRenderRows(), xFiltered(), xSearch(v), xSort(col), xFilter(val, el), xRefresh(), xBulk(type), xToggleAll(cb), xRowToggle(e), xUpdateBulk(), xClearSel(), xAction(type, id), saveX(mode).
  • Banking: openAccount(id, view) (view: 'summary'|'txns'), bankView(id, which), bankTab(id, status) (all|uncat|cat), bankSearch(v), bankSort(col), txnScreenHTML(a,id), txnBodyHTML(a,id), summaryHTML(a,id), feedListHTML(a,id), addTxnForm(id, type, dir), saveTxn(), bankCategorize(idx), bankMatch(idx), bankExclude(idx).

Overlays & feedback:

  • toast(msg, type) — type ∈ success|default|gray|warning|error, auto-inferred from msg when omitted (§3.11).
  • openModal(html, cls) / closeModal() — cls ∈ ''|'dlg-md'|'dlg-lg'|'lg'; dlgHead(title, sub) builds the header; confirmModal(title, body, confirmLabel, onConfirm, danger).
  • Task modals: recordPaymentModal(id), cnRefundModal(id), composeEmailModal(to, subject, onSend), importStmtModal(id) (+ impGo(n), impDone() stepper), rulesModal(id), reconcileModal(id), addAccountModal(), editAccountModal(id), newTaxModal(sel, recalc), inviteUserModal(email), newRoleModal(id), locationModal(id), editTaxModal(i), mergeModal(kind), portalModal(kind), glModal(title, sub, lines).

Tabs & switches: custDetailTab(tab) / vendDetailTab(tab) (pill-seg detail tabs), bankView/bankTab (above), setTeamTab(t), settingsNav(sec), rptScrollCat(id, el), sumToggle(el) (accordions), toggleCf(btn) (chart collapse), pillPick(el) (radio pills).

Search: globalSearch(raw), gsearchKey(e), gsearchGo(i), gsearchClose(); / hotkey focuses.

Utilities: svg(path, sz=16) (stroke 1.85 icon factory), usd(n) → '₦'+toLocaleString, money(n, cur), curSym(cur), abbrevMoney(n), parseAmt/parseMoney/parseDate, isoToDisp/dispToIso/dateDef/dateOpt (dates shown as 18 Jun 2026, APP_TODAY='2026-06-18'), fv(id) (field value), initials(nm), stBadge(s), docLink(num) / docOpen(num) / linkifyDocs(s) (auto-link INV|BILL|PAY|PMT|EXP|EST|CN|VC-\d+), downloadCSV(filename, headers, rows), exportTableCSV(scope, filename), exportCSVAuto(), emailAuto(), cloneAuto(), printDoc(), syncAcctBadges(), filterCfAccount(name).

Data globals: BANK (keyed accounts w/ txns, feed, rules), CUSTOMERS, VENDORS, INVOICES, BILLS, ESTIMATES, CREDITNOTES, VENDORCREDITS, PAYMENTS, PAYMENTSMADE, EXPENSES, JOURNALS, COA, TAXES, USERS, ROLES, LOCATIONS, DOCUMENTS; UI state in CUR_* / *_FILTER / *_SORT / *_SEARCH globals. A boot IIFE multiplies seeded amounts ≥100 by 1000 so figures read at company scale.


  • Class prefixes (all lowercase-hyphen): shell sb- (sidebar), tb- (topbar), crumb-; page pg-; cards card/tbl-card/sum-/bank-/kpi-; banking bk-, txn-, cf-, feed-, rec-, unc-; sales cust-, est-, inv (ids only), cn/vc/pmt (JS); dropdowns dd-; quick-create qc-; dialogs dlg-; forms fld, li-, tt-, crit-, cp-; settings set-, sn-; reports rpt-; global search gs-; toasts alert-toast--*/toast-*. (The header comment claims prefix cz-* but no cz- class exists — the real convention is the semantic prefixes above.)
  • Screen ids: scr-<entity> list, scr-<entity-singular> detail, scr-<entity>-form form (the [id$="-form"] CSS selector relies on this).
  • State classes: .show (screens/subs/overlays/bulk-bar), .open (dd/sb-item/accordion), .on (tabs/seg/pills/settings nav), .active (sidebar), .sel (dd options), .inactive (rows), .neg (negative values), .read (notifications), .danger (red options/actions).
  • Personas / org: Francis Ogunlaja — Finance Lead (avatar FO, francis.ogunlaja@mocx.co); Deborah Egbele — Accountant; Phebe Idowu — AP Clerk; Aisha Bello — salesperson. Org: MocX Group, Lagos/Abuja/PHC locations, FIRS tax agencies. Base currency NGN (₦), multi-currency USD/GBP/EUR….
  • ID formats: documents INV-2231, BILL-0312, EST-…, CN-…, VC-…, PAY-…, PMT-…, EXP-218, JV-001, TRF-0098, WIRE-0461; account codes 1001–2001; masked numbers 0124 •••• 4821 (monospace .mono); record ids in JS are slugs (gtbank, c1, inv1, loc-hq, role-…).
  • Copy conventions: middot · joins meta fragments (“Guaranty Trust · NGN”, “10 minutes ago · GTBank Operations”); minus is − (U+2212); “uncategorised/uncategorized” both appear (UI labels prefer -ized in tabs, -ised in notifications); dates DD Mon YYYY.

Where 700 appears (sanctioned — display numerals & splash-grade display text): .kpi-val 25px, .bk-stat .v 23, .pg-title 23, .bank-bal 22, .set-avatar 22 (initials), .bk-s .v 20, .est-doc-title 20, .cf-v/.txn-s .v 19, .unc-n/.sum-stat .n 18, .sb-logo-name 18 (wordmark), .rec-b .v 17, .tbl-foot .val 16, .est-tot-rows .tr.grand / .est-form-totals .row.grand / .rpt-net 15, .chart-tip .v 12. Everything else — names, buttons, section titles, card titles, badges, tab labels, b/strong — is 600 or lighter (600 remap of b,strong enforces this).

Borderline/violations noticed within the file itself (do NOT propagate):

  1. .alert-toast--warning { background:var(--input-bg)7c2; } — corrupted color value (should be the pale-yellow #fff7c2 canonical toast tint).
  2. Report tables inject inline font-weight:700 on text labels (“Total”, “Assets”, “Net Profit” row label inside .rpt-net) — 700 on words, not numerals; canon says those should be 600.
  3. .est-doc-title (“INVOICE”/“ESTIMATE” wordmark) and .sb-logo-name are 700 display text — accepted as display-grade, but they are not numerals.
  4. .gsearch-panel uses var(--sh-pop, fallback) — --sh-pop is never defined in :root (fallback always used).
  5. .chk is defined twice (17px then 16px — the later 16px wins for the bare class; .li-table re-forces 17px).
  6. .fld textarea defined twice (min-height 66 then 62 — 62 wins).
  7. Duplicated toggleAccountActive definition (first stub is overwritten later) — harmless but present.

Spacing rhythm (4px grid):

  • Page: padding:24px 32px 48px, column max 1320, .pg-head margin-bottom 24.
  • Grids/cards: 16px is the universal gap (.kpi-row, .row, .fld-row, stat rows, margin-bottom:16px between stacked cards); card padding 20×24 (.card), 16×20 (.kpi, .bank-card, .sum-card, .txn-card), 16 all-round (.tbl-card), 24×24/24×28 (.cust-form-card, .est-doc, .set-sec).
  • Controls: heights 38 (buttons, sb-item, tb-search, tabs, period), 42 (dialog/form inputs, user-pill), 40 (settings/line-item inputs), 34 (subitems, inline search, crumb-toggle), 30 (kebab, qc icons); dropdown menu padding 6, option padding 8×10; dialog paddings 16×24 (head/foot) & 20×24 (body); table cells 12×16, header 48 tall.
  • Micro-gaps: 4/6/8/10/12 used deliberately (icon-text 8–12, label-input 6, badge gap 4).
  • Off-grid values that exist in the file: 13.5/12.5/11.5px type sizes (half-pixel type scale — intentional), 7px dots & progress-bar height, 9px .sn-item vertical padding, 46px .txn-acct .i, top:46px search panel, top:66px qc panel, 22px stepper dots, 1.5px dashed/tbl-foot borders. These are the established exceptions; do not invent new ones.

Radius scale: 6 (--r-sm: badges), 8 (--r-md: buttons/inputs/nav items), 12 (--r-lg: cards/dialogs/dd-menus), 16 (--r-xl: tbl-card/qc-panel/toast pill), 999 (--r-f: pills/avatars/bars). Icon containers 50%.

Color discipline: brand #01E46F is used only for fills of primary buttons, active-tab underline, chart primary series, progress, stepper-active, and selection accents; text on brand is always #111; green text is always --g-dark (#05853E), never --brand. Tinted (*-dim) backgrounds always pair with their dark text partner (--amber-text for amber, base hue otherwise).