/* =====================================================================
   MOP Finances v4 — "Navy Ledger" design system (14 sep 2026)
   ---------------------------------------------------------------------
   One corporate navy on white paper and cool gray; money stays emerald /
   crimson / neutral. The token roles are law for every part file:
     - --accent      the navy FILL: buttons, active rail/tab, FAB, chosen
                     segment, undo pill. Ink laid on it is --btn-ink, which
                     is WHITE in both themes (it used to be dark).
     - --accent-ink  accent-COLORED TEXT (8.96:1 on white). Never paint
                     text with the raw --accent.
     - --accent-deep data marks & focus rings on light surfaces (chart
                     lines, dots, meters, sortable underline, outlines).
     - --accent-soft washes: active tab pill, open accordion head, .tag.
     - --band-*      the navy band family. On a navy surface (page band,
                     modal header strip, PIP head, .cardviz strip, rail
                     logo tile) NEVER use the page tokens: marks/focus =
                     --band-accent, the 28x3 underline = --band-underline,
                     money = --band-pos / --band-neg, amber = --band-warn,
                     secondary text = --band-ink2, hairlines = --band-line,
                     hover/track = --band-fill.
     - --crit        badges, delete button, PIP fail head, crit strips. Its
                     ink is ALWAYS --on-crit (white in light, near-black in
                     dark — the dark --crit is a light salmon). --on-good
                     is the ink on --pos-bright (ticks, PIP done head).
     - --pos-ink     green pill text (6.62:1 on the pill tint); --pos stays
                     the money green as text.
     - --ink3        legal on --surface/--bg/--surface2/--surface3 (>=4.59:1),
                     11px minimum, except caps labels (tab-bar label, rail
                     group, th.paycell, .up-when) at 10-10.5px, weight >=600,
                     where they still measure >=4.5:1.
     - --line        alias of --hairline (.acct-detail/.acct-activity read it).
     - --wrap-x      the ONLY source of .wrap side padding (28px, 16px at
                     <=1000px); the page band's negative margins read it.
     - --font        Inter: body and EVERY number (tnum, cv11).
     - --font-display Poppins: titles only, NEVER on a number.
   Series pair --s1 (Business blue) / --s2 (Personal orange) is the
   CVD-validated pair and nothing here is allowed to move it.
   ===================================================================== */

/* ============================ tokens ============================ */
:root {
  color-scheme: light;

  --bg: #F4F6F9;
  --surface: #FFFFFF;
  --surface2: #F1F4F8;
  --surface3: #E6EBF2;
  --border: #DDE3EC;
  --hairline: #EBEFF5;
  --line: var(--hairline);        /* NEW: .acct-detail/.acct-activity already read it (undefined until now) */
  --ink: #111827;
  --ink2: #3F4A5C;
  --ink3: #5C687B;

  --accent: #1B4B7C;              /* the navy FILL: buttons, active nav/tab, FAB, chosen segment, undo pill */
  --accent2: #16406B;             /* hover of a fill */
  --accent-deep: #2A72B3;         /* data marks & focus rings on light surfaces (5.05:1 on white) */
  --accent-ink: #1B4B7C;          /* accent-COLORED TEXT (8.96:1) */
  --accent-soft: #E8F0F8;         /* washes, active tab pill, open accordion header, .tag */
  --btn-ink: #FFFFFF;             /* ink laid on an accent fill — WAS dark */

  --pos: #047857;                 /* money green as text */
  --pos-ink: #065F46;             /* NEW: green pill text (6.62:1 on the pill tint) */
  --pos-bright: #10B981;          /* green fills: ticks, bars, progress "landed" */
  --pos-soft: #DCF5E9;
  --neg: #B3261E;                 /* money red as text (6.54:1) */
  --neg-bright: #E0534B;          /* red bars/tints — never carries white text */
  --neg-soft: #FCE9E7;
  --warn: #B45309;
  --warn-bright: #F59E0B;
  --warn-soft: #FDF0D8;
  --serious: #C2410C;
  --warn-ink: #92400E;
  --serious-ink: #9A3412;
  --good: #047857;
  --crit: #8F1D17;                /* badges, delete button, PIP fail head, crit header strips */
  --on-crit: #FFFFFF;             /* NEW: ink on --crit */
  --on-good: #052E1C;             /* ink on --pos-bright (5.85:1) — WAS white (2.5:1) */

  --s1: #2E6BE6;                  /* series 1 — Business (CVD pair, unchanged) */
  --s2: #EB6834;                  /* series 2 — Personal (CVD pair, unchanged) */
  --track: #E6EBF2;
  --tint: 16%;
  --tint-soft: 8%;
  --rav1: #4338ca; --rav2: #115e59; --rav3: #7e22ce; --rav5: #92400e;

  --dark1: #12274A;               /* always-dark surfaces (kept for future use; .cardviz no longer uses them) */
  --dark2: #0B1930;
  --dark-line: rgba(255, 255, 255, 0.12);

  /* NEW — the navy band family */
  --band: #1B4B7C;                /* page band, modal header strip, PIP head, .cardviz header strip, .sb-logo tile */
  --band2: #143A62;               /* gradient end of a band */
  --band-ink: #FFFFFF;
  --band-ink2: rgba(255, 255, 255, 0.76);   /* secondary text on a band (5.95:1 composite) */
  --band-line: rgba(255, 255, 255, 0.14);   /* hairlines/tracks inside a band */
  --band-fill: rgba(255, 255, 255, 0.10);   /* hover/segctl track inside a band */
  --band-pos: #7EE2B8;            /* money green ON navy (5.74:1) */
  --band-neg: #FFB4B4;            /* money red ON navy (5.31:1) — never #FF8FA6 (4.15) */
  --band-warn: #FCD34D;           /* amber ON navy (6.21:1) */
  --band-accent: #9CCBF4;         /* focus rings, links, marks ON navy (5.23:1) */
  --band-underline: #8FC3F0;      /* the 28×3 underline ON navy (4.79:1) — never the sky --accent-deep (≈2.1:1) */
  --underline: #2A72B3;           /* the 28×3 underline on light surfaces */

  /* NEW — dark footer strip */
  --foot-bg: #111827;
  --foot-ink: #C7CFDB;            /* 11.30:1 */
  --foot-ink2: #8B97AB;           /* 6.01:1 */

  --radius: 14px;                 /* was 18 — the template's ~10–14px card */
  --radius-sm: 10px;
  --radius-xs: 8px;
  --shadow-card: 0 1px 2px rgba(17, 24, 39, 0.05), 0 8px 24px rgba(27, 75, 124, 0.08);
  --shadow-lift: 0 4px 10px rgba(27, 75, 124, 0.10), 0 20px 44px rgba(27, 75, 124, 0.14);
  --shadow-pop: 0 18px 48px rgba(17, 24, 39, 0.18);
  --shadow-dark: 0 12px 32px rgba(11, 25, 48, 0.30);

  --sb-w: 248px;
  --tb-h: 60px;                   /* 56px at ≤760px — set in the phone shell block, not here */
  --wrap-x: 28px;                 /* NEW: .wrap side gutter; the band's negative margins read it (16px ≤1000px) */
  --font: "Inter", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
  --font-display: "Poppins", "Inter", ui-sans-serif, system-ui, sans-serif;   /* NEW: titles only, NEVER on numbers */
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --bg: #0B1220;
    --surface: #121B2E;
    --surface2: #192437;
    --surface3: #212E45;
    --border: #263650;
    --hairline: #1C2740;
    --line: var(--hairline);
    --ink: #E8EEF7;
    --ink2: #AAB6C9;
    --ink3: #8797AE;
    --accent: #2A69AC;            /* white on it 5.65:1; 3.04:1 boundary vs surface */
    --accent2: #2F72B8;
    --accent-deep: #6FB1EA;
    --accent-ink: #8FC4F0;
    --accent-soft: #17304D;
    --btn-ink: #FFFFFF;
    --pos: #34D399;
    --pos-ink: #34D399;
    --pos-bright: #34D399;
    --pos-soft: #0E3A2A;
    --neg: #F1918A;
    --neg-bright: #F1918A;
    --neg-soft: #3B1614;
    --warn: #FBBF24;
    --warn-bright: #FBBF24;
    --warn-soft: #3A2A08;
    --serious: #FB923C;
    --warn-ink: #FCD34D;
    --serious-ink: #FDBA74;
    --good: #34D399;
    --crit: #F17E76;              /* light salmon: readable as text (6.54:1) AND as a fill with --on-crit */
    --on-crit: #2A0B08;           /* 6.95:1 on --crit */
    --on-good: #04231A;
    --s1: #5C8BEF;
    --s2: #DE6A2E;
    --track: #223047;
    --tint: 12%;
    --tint-soft: 7%;
    --rav1: #a5b4fc; --rav2: #5eead4; --rav3: #d8b4fe; --rav5: #fcd34d;
    --dark1: #142B4E;
    --dark2: #0B1930;
    --dark-line: rgba(255, 255, 255, 0.10);
    --band: #15304F;              /* one step LIGHTER than --bg so the band still reads as a band */
    --band2: #10243C;
    --band-ink: #FFFFFF;
    --band-ink2: rgba(255, 255, 255, 0.76);
    --band-line: rgba(255, 255, 255, 0.12);
    --band-fill: rgba(255, 255, 255, 0.08);
    --band-pos: #7EE2B8;
    --band-neg: #FFB4B4;
    --band-warn: #FCD34D;
    --band-accent: #9CCBF4;
    --band-underline: #8FC3F0;
    --underline: #6FB1EA;
    --foot-bg: #070C16;
    --foot-ink: #AAB6C9;
    --foot-ink2: #8797AE;
    --shadow-card: 0 1px 0 rgba(255, 255, 255, 0.03), 0 12px 30px rgba(0, 0, 0, 0.40);
    --shadow-lift: 0 2px 0 rgba(255, 255, 255, 0.04), 0 20px 44px rgba(0, 0, 0, 0.50);
    --shadow-pop: 0 20px 52px rgba(0, 0, 0, 0.55);
    --shadow-dark: 0 12px 32px rgba(0, 0, 0, 0.45);
  }
}
:root[data-theme="dark"] {
  color-scheme: dark;
  --bg: #0B1220;
  --surface: #121B2E;
  --surface2: #192437;
  --surface3: #212E45;
  --border: #263650;
  --hairline: #1C2740;
  --line: var(--hairline);
  --ink: #E8EEF7;
  --ink2: #AAB6C9;
  --ink3: #8797AE;
  --accent: #2A69AC;
  --accent2: #2F72B8;
  --accent-deep: #6FB1EA;
  --accent-ink: #8FC4F0;
  --accent-soft: #17304D;
  --btn-ink: #FFFFFF;
  --pos: #34D399;
  --pos-ink: #34D399;
  --pos-bright: #34D399;
  --pos-soft: #0E3A2A;
  --neg: #F1918A;
  --neg-bright: #F1918A;
  --neg-soft: #3B1614;
  --warn: #FBBF24;
  --warn-bright: #FBBF24;
  --warn-soft: #3A2A08;
  --serious: #FB923C;
  --warn-ink: #FCD34D;
  --serious-ink: #FDBA74;
  --good: #34D399;
  --crit: #F17E76;
  --on-crit: #2A0B08;
  --on-good: #04231A;
  --s1: #5C8BEF;
  --s2: #DE6A2E;
  --track: #223047;
  --tint: 12%;
  --tint-soft: 7%;
  --rav1: #a5b4fc; --rav2: #5eead4; --rav3: #d8b4fe; --rav5: #fcd34d;
  --dark1: #142B4E;
  --dark2: #0B1930;
  --dark-line: rgba(255, 255, 255, 0.10);
  --band: #15304F;
  --band2: #10243C;
  --band-ink: #FFFFFF;
  --band-ink2: rgba(255, 255, 255, 0.76);
  --band-line: rgba(255, 255, 255, 0.12);
  --band-fill: rgba(255, 255, 255, 0.08);
  --band-pos: #7EE2B8;
  --band-neg: #FFB4B4;
  --band-warn: #FCD34D;
  --band-accent: #9CCBF4;
  --band-underline: #8FC3F0;
  --underline: #6FB1EA;
  --foot-bg: #070C16;
  --foot-ink: #AAB6C9;
  --foot-ink2: #8797AE;
  --shadow-card: 0 1px 0 rgba(255, 255, 255, 0.03), 0 12px 30px rgba(0, 0, 0, 0.40);
  --shadow-lift: 0 2px 0 rgba(255, 255, 255, 0.04), 0 20px 44px rgba(0, 0, 0, 0.50);
  --shadow-pop: 0 20px 52px rgba(0, 0, 0, 0.55);
  --shadow-dark: 0 12px 32px rgba(0, 0, 0, 0.45);
}
/* ============================ base ============================ */
* { box-sizing: border-box; margin: 0; padding: 0; }
html, body { height: 100%; }
body {
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font);
  font-size: 14px;
  line-height: 1.45;
  font-feature-settings: "tnum" 1, "cv11" 1;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
button { font: inherit; color: inherit; background: none; border: none; cursor: pointer; }
input, select, textarea { font: inherit; color: var(--ink); }
::placeholder { color: var(--ink3); }
:focus-visible { outline: 2px solid var(--accent-deep); outline-offset: 2px; border-radius: 6px; }
h1, h2, h3 { font-weight: 700; letter-spacing: -0.015em; }

/* Display face (Poppins) — titles, labels and buttons ONLY. Set here once so
   no part file has to name a font; a part may only override it back to
   var(--font) on a numeric element. */
.tb-title, .card h3, .h3row h3, .sect-title, .modal h2, .pop-h, .login-card .logo,
.eyebrow, th, .tab-item span, .steps li span, .btn,
.cellpop-label, .sr-head, .modal h3, .acct-group-title, .pg-head-row, .notecard .h3row > b,
.sb-group, tr.day-head td { font-family: var(--font-display); }

/* Numbers never wear Poppins (no tabular figures): Inter + tnum on every
   numeric class, beating the display rule above and any inherited face. */
.amt, td.num, .t-value, .hero-value, .cyc-num, .cv-amt, .pmoney, .bal-amt, .up-amt,
.ob-amt, .day-sum, .trio .v, .hbar-val, .rowfoot b, .stat-chip b, .tb-stat-pct, .pg-head-row b,
.pg-head-row em, .pg-head em, .card h3 .hint, .h3row h3 .hint, .eyebrow .hint, .cnt {
  font-family: var(--font);
  font-variant-numeric: tabular-nums;
}

/* Money sign coloring — green up, red down, ink for plain magnitudes. */
.amt { font-variant-numeric: tabular-nums; }
.amt.pos { color: var(--pos); }
.amt.neg { color: var(--neg); }
.amt.info { color: var(--accent-ink); } /* a credit-in-favor balance — money coming back, not a gain or a loss */
/* ============================ app shell ============================
   Navy Ledger (v4): a white rail with a navy logo tile, one navy fill on
   the active page, a --surface2 "Manage" block for the settings items;
   a white blurred topbar whose only pill is the "+ Add" CTA. */
#app { min-height: 100%; }

/* ---- left rail ---- */
/* The rail scrolls as ONE column (10 sep 2026). It used to give the page
   list its own scrollbar while the settings block below it kept its full
   height, so on a 720px-tall screen the seventeen pages were squeezed into
   306px: everything from Loans down — Delivery notes included — sat behind
   a thin inner scrollbar that reads as the end of the list. Let the whole
   rail scroll instead and nothing is hidden; on a tall screen the spacer
   still pushes the settings block to the bottom, exactly as before. */
.sb {
  position: fixed; z-index: 40; left: 0; top: 0; bottom: 0; width: var(--sb-w);
  background: var(--surface);
  border-right: 1px solid var(--hairline);
  display: flex; flex-direction: column;
  padding: 14px 12px 12px;
  overflow-y: auto; overscroll-behavior: contain; scrollbar-width: thin;
  transition: transform 0.22s cubic-bezier(0.22, 1, 0.36, 1);
}
/* the navy logo tile */
.sb-logo {
  display: flex; align-items: center; gap: 11px; flex: none;
  font-size: 18px; font-weight: 800; letter-spacing: -0.02em;
  padding: 12px 12px; margin: 0 0 16px; border-radius: 12px;
  background: var(--band); color: var(--band-ink);
}
.mark {
  width: 34px; height: 34px; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
}
.mark img { width: 100%; height: 100%; object-fit: contain; display: block; filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.18)); }
/* the mark sits on a white rounded square inside the navy tile */
.sb-logo .mark { width: 32px; height: 32px; border-radius: 9px; background: #fff; padding: 4px; }
.sb-logo .mark img { filter: none; }
.sb-logo .wm { line-height: 1; }
.sb-logo .wm i {
  display: block; font-style: normal; font-size: 10.5px; font-weight: 600;
  color: var(--band-ink2); letter-spacing: 0.09em; text-transform: uppercase; margin-top: 3px;
}

.sb-nav { display: flex; flex-direction: column; gap: 3px; flex: none; }
.sb-item {
  display: flex; align-items: center; gap: 11px; width: 100%;
  min-height: 36px; padding: 0 12px; border-radius: 10px;
  font-size: 13.5px; font-weight: 600; color: var(--ink2);
  text-align: left; transition: background-color 0.15s, color 0.15s;
}
.sb-item svg { width: 18px; height: 18px; flex: none; opacity: 0.85; }
.sb-item:hover { background: var(--surface2); color: var(--ink); }
.sb-item.on {
  background: var(--accent); color: var(--btn-ink);
  box-shadow: 0 4px 12px color-mix(in srgb, var(--accent) 28%, transparent);
}
.sb-item.on svg { opacity: 1; }
.sb-item:focus-visible { outline: 2px solid var(--accent-deep); outline-offset: 2px; }
.sb-item.on:focus-visible { outline-color: var(--band-accent); outline-offset: -3px; } /* inside the navy fill: on the white rail this ring measured 1.71:1 */
.sb-item .badge {
  margin-left: auto; min-width: 20px; height: 19px; padding: 0 6px; border-radius: 999px;
  background: var(--crit); color: var(--on-crit); font-size: 11px; font-weight: 700;
  display: inline-flex; align-items: center; justify-content: center;
  font-variant-numeric: tabular-nums;
}
/* inverted on the navy item: white pill, navy count */
.sb-item.on .badge { background: #fff; color: var(--accent); }
.sb-spacer { flex: 1 0 14px; min-height: 14px; }

/* group labels between the pages (Money · Trade · …) — emitted by
   shell.js rail() only while the rail is in its built-in order; they are
   plain text, never a drag or click target */
.sb-group {
  margin: 6px 12px 2px;
  font-size: 10.5px; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--ink3); line-height: 1.2;
  pointer-events: none; -webkit-user-select: none; user-select: none;
}
.sb-group:first-child { margin-top: 0; }

/* Drag a rail item to move it (10 sep 2026 — navorder.js). touch-action
   stays pan-y so a finger still scrolls the rail; the press-and-hold that
   starts a drag blocks the scroll from JS instead. */
.sb-nav .sb-item { touch-action: pan-y; -webkit-user-select: none; user-select: none; }
.sb-nav.dnd-on { cursor: grabbing; }
.sb-nav.dnd-on .sb-item { transition: none; }
.sb-item.dragging {
  background: var(--surface2); color: var(--ink);
  box-shadow: var(--shadow-pop); cursor: grabbing;
}
.sb-item.dragging.on { background: var(--accent); color: var(--btn-ink); }
body.dnd-busy { cursor: grabbing; -webkit-user-select: none; user-select: none; }

/* the bottom block — Manage · Settings · Log out — never reads as pages.
   Pinned (18 sep 2026): it is sticky inside the rail's own scroll, so the
   page list slides under it instead of pushing it below the fold (with
   eighteen pages the rail is ~1100px tall; Settings and Log out used to sit
   ~400px under a 900px laptop screen). The fade above it only shows while
   a page entry is actually underneath — on a tall screen it lies over the
   white spacer and is invisible. The six set-up panels open from the
   Manage popover (shell.js MANAGE_MENU), not as rows of their own. */
.sb-foot {
  position: sticky; bottom: 0; z-index: 2;
  background: var(--surface2); border-radius: 12px;
  padding: 6px; margin: 0 -4px -4px;
  display: flex; flex-direction: column; gap: 2px; flex: none;
}
.sb-foot::after {
  content: ""; position: absolute; left: -8px; right: -8px; bottom: 100%; height: 22px;
  background: linear-gradient(to bottom, color-mix(in srgb, var(--surface) 0%, transparent), var(--surface));
  pointer-events: none;
}
/* sticky stops at the rail's bottom padding: without this strip the entries
   scrolling underneath showed through the 12px gap below the block */
.sb-foot::before {
  content: ""; position: absolute; left: -8px; right: -8px; top: 100%; height: 16px;
  background: var(--surface); pointer-events: none;
}
.sb-foot .sb-item { min-height: 34px; font-size: 13px; }
.sb-foot #rail-logout { color: var(--neg); }
.sb-foot #rail-logout:hover { background: var(--neg-soft); }
.sb-manage { position: relative; }
.sb-caret { margin-left: auto; display: inline-flex; color: var(--ink3); transition: transform 0.15s; }
.sb-caret svg { width: 15px; height: 15px; }
#rail-manage.open { background: var(--surface); color: var(--ink); box-shadow: 0 0 0 1px var(--border); }
#rail-manage.open .sb-caret { transform: rotate(180deg); }
@media (prefers-reduced-motion: reduce) { .sb-caret { transition: none; } }
/* the Manage menu opens upward, over the page list, inside the rail's width
   (a flyout past the rail's edge would be clipped by its scroll) */
.pop.managepop {
  top: auto; bottom: calc(100% + 6px); left: 0; right: 0;
  min-width: 0; max-width: none;
  max-height: calc(100vh - 150px); overflow-y: auto;
}
.pop.managepop .pop-item > svg {
  width: 28px; height: 28px; padding: 6px; box-sizing: border-box;
  border: 1.5px solid var(--accent-ink); border-radius: 50%; color: var(--accent-ink); flex: none;
  transition: background-color 0.12s, color 0.12s;
}
.pop.managepop .pop-item:hover > svg { background: var(--accent); border-color: var(--accent); color: var(--btn-ink); }
.avatar {
  width: 34px; height: 34px; border-radius: 50%; flex: none;
  background: var(--accent); border: 0;
  color: var(--btn-ink); font-weight: 800; font-size: 13px;
  display: inline-flex; align-items: center; justify-content: center;
}

/* ---- scrim (mobile drawer) ---- */
.scrim {
  position: fixed; inset: 0; z-index: 35; background: rgba(8, 14, 26, 0.5);
  -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px);
  opacity: 0; pointer-events: none; transition: opacity 0.2s;
}

/* ---- main column ---- */
.main { margin-left: var(--sb-w); min-height: 100vh; display: flex; flex-direction: column; }

.tb {
  position: sticky; top: 0; z-index: 25;
  min-height: var(--tb-h);
  display: flex; align-items: center; gap: 12px;
  padding: 8px var(--wrap-x);
  background: color-mix(in srgb, var(--surface) 92%, transparent);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--hairline);
}
/* .tb-ico sets display below at equal specificity — the burger needs the compound selector to stay hidden beside the permanent rail */
.tb-ico.tb-burger { display: none; }
.tb-head { min-width: 0; flex: 0 1 auto; }
.tb-title { font-size: 19px; font-weight: 700; letter-spacing: -0.01em; line-height: 1.15; }
.tb-sub {
  display: block; font-size: 12px; font-weight: 500; color: var(--ink3); margin-top: 2px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.tb-tools { margin-left: auto; display: flex; align-items: center; gap: 8px; flex: none; }

.tb-ico {
  position: relative; width: 40px; height: 40px; border-radius: 10px; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--ink2); background: var(--surface); border: 1px solid var(--border);
  transition: background-color 0.15s, color 0.15s, border-color 0.15s;
}
.tb-ico:hover { background: var(--surface2); color: var(--ink); border-color: var(--surface3); }
.tb-ico:focus-visible { outline: 2px solid var(--accent-deep); outline-offset: 2px; }
.tb-ico svg { width: 18px; height: 18px; }
.tb-ico.on { color: var(--accent-ink); border-color: color-mix(in srgb, var(--accent) 45%, var(--border)); background: var(--accent-soft); }
.tb-ico .dot {
  position: absolute; top: -5px; right: -5px; min-width: 19px; height: 19px; padding: 0 5px;
  border-radius: 999px; background: var(--crit); color: var(--on-crit);
  font-size: 11px; font-weight: 700; line-height: 15px;
  border: 2px solid var(--surface); font-variant-numeric: tabular-nums;
}
.tb-ico .dot.warn { background: var(--warn-bright); color: #241703; }

/* the ONE pill in the app: the "+ Add" CTA (buttons keep radius 10 elsewhere) */
.tb .btn { border-radius: 999px; padding: 0 16px 0 12px; }

/* ---- global search ---- */
.gsearch { position: relative; width: 260px; flex: 0 1 260px; }
.gsearch .gs-in {
  width: 100%; height: 40px; padding: 0 34px 0 38px;
  border-radius: 10px; border: 1px solid var(--border);
  background: var(--surface2); font-size: 13px; color: var(--ink);
}
.gsearch .gs-in:focus {
  outline: none; border-color: var(--accent-deep);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent-deep) 22%, transparent);
}
.gsearch .gs-ico { position: absolute; left: 12px; top: 11px; width: 17px; height: 17px; color: var(--ink3); pointer-events: none; }
.gsearch .gs-x {
  position: absolute; right: 6px; top: 8px; width: 24px; height: 24px; border-radius: 8px;
  color: var(--ink3); display: none; align-items: center; justify-content: center;
}
.gsearch .gs-x svg { width: 13px; height: 13px; }
.gsearch.has .gs-x { display: inline-flex; }
.gsearch .gs-x:hover { background: var(--surface2); color: var(--ink); }

/* ---- popovers (alerts, user menu, search results) — Navy Ledger: 12px radius,
   a Poppins eyebrow with the 22×3 underline, 38px items (44px on touch) ---- */
.pop {
  position: absolute; z-index: 60; top: calc(100% + 8px); right: 0;
  min-width: 268px; max-width: min(380px, calc(100vw - 24px));
  background: var(--surface); border: 1px solid var(--border);
  border-radius: 12px; box-shadow: var(--shadow-pop);
  padding: 8px;
}
.pop.left { right: auto; left: 0; }
@media (prefers-reduced-motion: no-preference) {
  .pop { animation: pop-in 0.14s ease-out; }
  @keyframes pop-in { from { opacity: 0; transform: translateY(-5px); } }
}
.pop-h {
  position: relative;
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 8px 10px 14px; font-size: 11px; font-weight: 600;
  letter-spacing: 0.10em; text-transform: uppercase; color: var(--accent-ink);
}
/* the "a block starts here" bar; absolute so it is not a third flex item */
.pop-h::after {
  content: ""; position: absolute; left: 10px; bottom: 5px;
  width: 22px; height: 3px; border-radius: 2px; background: var(--underline);
}
/* the count is a number: back to Inter (Poppins has no tabular figures) */
.pop-h b { font-family: var(--font); color: var(--ink2); font-variant-numeric: tabular-nums; letter-spacing: 0; text-transform: none; font-size: 12px; font-weight: 700; }
.pop-item {
  display: flex; align-items: center; gap: 10px; width: 100%;
  min-height: 38px; padding: 6px 10px; border-radius: 10px; text-align: left;
  font-size: 13px; color: var(--ink2);
}
.pop-item:hover { background: var(--surface2); color: var(--ink); }
.pop-item:focus-visible { outline: 2px solid var(--accent-deep); outline-offset: -2px; border-radius: 10px; }
.pop-item svg { width: 16px; height: 16px; flex: none; color: var(--ink3); }
.pop-item:hover svg { color: var(--ink2); }
.pop-item .rt { margin-left: auto; font-size: 11.5px; color: var(--ink3); text-transform: capitalize; }
.pop-item.danger { color: var(--crit); }
.pop-item.danger svg { color: var(--crit); }
.pop-item.danger:hover { background: color-mix(in srgb, var(--crit) 10%, transparent); color: var(--crit); }
.pop-empty { padding: 16px 12px 18px; font-size: 13px; color: var(--ink3); text-align: center; }
.pop-scroll { max-height: min(60vh, 430px); overflow-y: auto; }
@media (pointer: coarse) {
  .pop-item { min-height: 44px; padding: 8px 10px; }
  .al-row, .sr-row { min-height: 44px; }
}

/* ---- click-to-edit table cells ---- */
.cellbtn {
  display: inline-flex; align-items: center; gap: 6px; max-width: 100%;
  font: inherit; color: inherit; background: transparent; border: 1px solid transparent;
  border-radius: 8px; padding: 4px 7px; margin: -4px -7px; cursor: pointer; text-align: left;
  transition: background-color 0.12s, border-color 0.12s;
}
.cellbtn:hover, .cellbtn:focus-visible { background: var(--surface2); border-color: var(--border); outline: none; }
.cellbtn:active { background: var(--surface3); }
.cellbtn .namecell, .cellbtn .chip, .cellbtn .typechip { pointer-events: none; }
.cellpop {
  position: fixed; z-index: 50; width: 240px; max-width: calc(100vw - 16px);
  background: var(--surface); border: 1px solid var(--border); border-radius: 12px;
  box-shadow: var(--shadow-pop); padding: 12px;
}
@media (prefers-reduced-motion: no-preference) { .cellpop { animation: pop-in 0.14s ease-out; } }
.cellpop-label {
  font-size: 11px; font-weight: 600; color: var(--accent-ink); text-transform: uppercase;
  letter-spacing: 0.08em; margin-bottom: 8px;
}
.cellpop-legacy { font-size: 12px; color: var(--ink3); margin: -3px 0 9px; }
.cellpop input {
  width: 100%; background: var(--surface2); border: 1px solid var(--border);
  border-radius: 10px; padding: 0 11px; font-size: 13.5px; height: 38px;
}
.cellpop input:focus {
  outline: none; border-color: var(--accent-deep); background: var(--surface);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent-deep) 20%, transparent);
}
.cellpop-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 10px; }
.cellpop-opts { display: flex; flex-wrap: wrap; gap: 6px; }
.cellpop-opt {
  padding: 7px 12px; border-radius: 9px; font-size: 12.5px; font-weight: 600;
  background: var(--surface2); color: var(--ink2); border: 1px solid transparent; text-align: left;
}
.cellpop-opt:hover { background: var(--surface3); color: var(--ink); }
.cellpop-opt.on {
  background: color-mix(in srgb, var(--accent) 22%, var(--surface)); color: var(--accent-ink);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent-deep) 40%, transparent);
}
.cellpop-opt:focus-visible { outline: 2px solid var(--accent-deep); outline-offset: 1px; }
@media (pointer: coarse) { .cellbtn { padding: 7px 8px; margin: -7px -8px; } .cellpop-opt { min-height: 38px; } }

/* alert rows */
.al-row { display: flex; align-items: center; gap: 10px; width: 100%; padding: 8px 10px; border-radius: 10px; text-align: left; }
.al-row:hover { background: var(--surface2); }
.al-row:focus-visible { outline: 2px solid var(--accent-deep); outline-offset: -2px; }
.al-day {
  flex: none; width: 40px; text-align: center; font-size: 10.5px; font-weight: 700;
  border-radius: 9px; padding: 5px 2px; line-height: 1.15;
  background: var(--surface2); color: var(--ink2); font-variant-numeric: tabular-nums;
  text-transform: uppercase; letter-spacing: 0.05em;
}
.al-day b { display: block; font-size: 13.5px; color: var(--ink); }
.al-row.crit .al-day { background: color-mix(in srgb, var(--crit) var(--tint), transparent); color: var(--crit); }
.al-row.crit .al-day b { color: var(--crit); }
.al-row.warn .al-day { background: color-mix(in srgb, var(--warn-bright) var(--tint), transparent); color: var(--warn-ink); }
.al-row.warn .al-day b { color: var(--warn-ink); }
.al-txt { flex: 1; min-width: 0; }
.al-txt b { display: block; font-size: 13px; font-weight: 600; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.al-txt span { font-size: 11.5px; color: var(--ink3); }
.al-amt { flex: none; font-size: 13px; font-weight: 700; font-variant-numeric: tabular-nums; }

/* search results */
.sr-head { font-size: 11px; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase; color: var(--accent-ink); padding: 9px 10px 4px; }
.sr-row { display: flex; align-items: center; gap: 10px; width: 100%; padding: 8px 10px; border-radius: 10px; text-align: left; }
.sr-row:hover, .sr-row.cur { background: var(--surface2); }
.sr-row.cur { box-shadow: inset 3px 0 0 var(--accent); }
.sr-row:focus-visible { outline: 2px solid var(--accent-deep); outline-offset: -2px; }
.sr-row .sr-t { flex: 1; min-width: 0; font-size: 13px; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sr-row .sr-t i { font-style: normal; color: var(--ink3); font-size: 11.5px; }
.sr-row .sr-a { flex: none; font-size: 12.5px; font-variant-numeric: tabular-nums; color: var(--ink2); font-weight: 600; }
.sr-foot { padding: 9px 11px 5px; font-size: 11.5px; color: var(--ink3); border-top: 1px solid var(--hairline); margin-top: 6px; }
.sr-foot kbd {
  font-family: inherit; font-size: 11px; font-weight: 700; padding: 1px 5px;
  border: 1px solid var(--border); border-radius: 5px; background: var(--surface2); color: var(--ink2);
}
/* ---- content column ----
   --wrap-x (tokens) is the ONLY source of the side gutter at every width:
   the page band's negative margins read the same token, so the two can
   never drift and the band bleeds exactly to the edge with no overflow. */
.wrap { flex: 1; width: 100%; max-width: none; padding: 6px var(--wrap-x) 40px; }
/* :where keeps this at single-class specificity so a child's own max-width still wins */
.wrap > :where(:not(.pageband)) { max-width: 1284px; }
/* slim dark strip. The footer is a sibling of .wrap inside .main, so zero
   side margins already make it full-bleed; the gutter comes back as padding. */
footer.foot {
  margin: 34px 0 0; padding: 14px var(--wrap-x);
  background: var(--foot-bg); color: var(--foot-ink2);
  border: 0; border-top: 3px solid var(--underline);
  font-size: 12.5px; display: flex; justify-content: space-between;
  flex-wrap: wrap; gap: 6px; max-width: none;
}
footer.foot > span:first-child { color: var(--foot-ink); font-weight: 600; }
.banner {
  margin: 0 0 16px; padding: 11px 14px; border: 1px solid var(--border);
  border-left: 3px solid var(--warn-bright); border-radius: 10px;
  background: var(--surface); color: var(--ink2); font-size: 13px;
}
.banner.info { border-left-color: var(--accent); }
.loading-dim { opacity: 0.5; pointer-events: none; transition: opacity 0.2s; }

/* ---- responsive shell ---- */
@media (min-width: 1081px) { .only-narrow { display: none !important; } }
@media (max-width: 1080px) { #gsearch { display: none; } }
@media (max-width: 1000px) {
  :root { --wrap-x: 16px; }
  .sb { width: 280px; transform: translateX(-100%); box-shadow: var(--shadow-pop); }
  #app.nav-open .sb { transform: none; }
  #app.nav-open .scrim { opacity: 1; pointer-events: auto; }
  .main { margin-left: 0; }
  .tb-ico.tb-burger { display: inline-flex; }
  .tb { padding: 8px var(--wrap-x); }
  .wrap { padding: 6px var(--wrap-x) 32px; }
  footer.foot { margin: 28px 0 0; }
}
@media (max-width: 620px) {
  .tb-title { font-size: 17px; }
  .tb-sub { font-size: 11.5px; }
  .tb-tools { gap: 6px; }
  .tb-ico { width: 38px; height: 38px; }
}
/* ============================ cards ============================ */
.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 18px;
  box-shadow: var(--shadow-card);
}
@media (max-width: 640px) { .card { padding: 14px; border-radius: 12px; } }

/* Section header pattern: display title + the 28x3 navy underline that says
   "a block starts here" (the same bar sits under day-group heads, eyebrows
   and section titles). */
.card h3, .h3row h3 {
  position: relative; padding-bottom: 8px; margin-bottom: 12px;
  font-size: 15px; font-weight: 600; letter-spacing: -0.01em;
}
.card h3::after, .h3row h3::after {
  content: ""; position: absolute; left: 0; bottom: 0;
  width: 28px; height: 3px; border-radius: 2px; background: var(--underline);
}
/* Secondary text anywhere. A standalone .hint (under a balance, in a card or
   a confirm dialog, in a table cell) never had a rule of its own and rendered
   as body copy — 14px in full ink, reading like content ("It is a rate per
   thousand…" on Taxes). Headings, eyebrows and the modal strip keep their
   own sizes. */
.hint { font-size: 12.5px; line-height: 1.45; color: var(--ink3); }
.card h3 .hint, .h3row h3 .hint { font-weight: 500; color: var(--ink3); font-size: 12.5px; letter-spacing: 0; }
.h3row { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 12px; flex-wrap: wrap; }
.h3row h3 { margin-bottom: 0; }
.h3row .tools { display: inline-flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.h3row input[type="search"], .h3row .tools input[type="text"], .h3row .tools select {
  height: 34px; padding: 0 12px; border: 1px solid var(--border); border-radius: 10px;
  background: var(--surface); color: var(--ink); font-size: 12.5px; min-width: 190px;
}
.h3row input[type="search"]:focus, .h3row .tools input[type="text"]:focus, .h3row .tools select:focus { outline: none; border-color: var(--accent-deep); }
.h3row .tools select { min-width: 0; padding-right: 28px; }

/* Section eyebrow: underline bar + overline label */
.eyebrow {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  font-size: 11px; font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--ink3); margin-bottom: 12px;
}
.eyebrow::before { content: ""; width: 24px; height: 3px; border-radius: 2px; background: var(--underline); flex: none; }
.eyebrow .hint { font-weight: 500; letter-spacing: 0.02em; text-transform: none; color: var(--ink3); font-size: inherit; line-height: inherit; }

/* Section head (.sect) — left-aligned page section title with a 40x3 bar,
   used where a page has three or more distinct groups. Markup:
   components.js sectionHead(). */
.sect { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; margin: 26px 0 10px; }
.sect-title {
  position: relative; padding-bottom: 8px; margin: 0;
  font-size: 17px; font-weight: 700; letter-spacing: -0.01em; line-height: 1.2;
}
.sect-title::after {
  content: ""; position: absolute; left: 0; bottom: 0;
  width: 40px; height: 3px; border-radius: 2px; background: var(--underline);
}
.sect-hint { font-size: 12.5px; color: var(--ink3); }

/* =====================================================================
   Page band (.pageband) — the full-bleed navy band every page opens with.
   It IS the statement card: eyebrow, headline figure, caption. Faint
   line-art, a curved bottom edge drawn in the page background, and the
   next sibling (the .tiles row) floats over that curve. Markup:
   components.js pageBand(); every view wraps ONLY its .card.hero in it.
   The band's negative margins equal .wrap's side padding (--wrap-x), so
   it can never overflow the page.
   ===================================================================== */
.pageband {
  position: relative; isolation: isolate;
  margin: 0 calc(-1 * var(--wrap-x, 28px));
  padding: 22px var(--wrap-x, 28px) 58px;
  background: linear-gradient(135deg, var(--band), var(--band2));
  color: var(--band-ink);
}
.wrap > .pageband:first-child { margin-top: -6px; }
.pb-in { position: relative; z-index: 1; max-width: 1284px; }
/* line-art: a faint grid, faded in from the top-left */
.pageband::before {
  content: ""; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background-image:
    repeating-linear-gradient(90deg, var(--band-line) 0 1px, transparent 1px 64px),
    repeating-linear-gradient(0deg, var(--band-line) 0 1px, transparent 1px 64px);
  opacity: 0.35;
  -webkit-mask-image: linear-gradient(115deg, transparent 55%, #000 100%);
  mask-image: linear-gradient(115deg, transparent 55%, #000 100%);
}
/* the curve: the page background bites into the band's bottom edge */
.pageband::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; z-index: 0; height: 26px;
  background: var(--bg); border-radius: 50% 50% 0 0 / 100% 100% 0 0; pointer-events: none;
}
/* whatever follows the band overlaps its curve (the tiles "collage") */
.wrap > .pageband + * { margin-top: -36px; position: relative; z-index: 1; }
.wrap > .pageband + .banner { margin-top: 12px; }
.wrap > .pageband + .bg-note { margin-top: 12px; }

/* the hero inside the band is flattened: the band is the card now */
.pageband .card.hero {
  background: transparent; border: 0; box-shadow: none; padding: 0; margin: 0;
  border-radius: 0; overflow: visible; position: relative;
  /* token re-scope: everything nested (money, captions, hairlines, chips)
     reads the band palette without knowing it sits on navy */
  --ink: var(--band-ink);
  --ink2: var(--band-ink2);
  --ink3: var(--band-ink2);
  --pos: var(--band-pos);
  --neg: var(--band-neg);
  --warn: var(--band-warn);
  --neg-bright: var(--band-neg);   /* dots and comparison bars are fills: light-surface reds read 1.7-2.4:1 on navy */
  --serious: var(--band-warn);
  --pos-bright: var(--band-pos);
  --accent-ink: var(--band-accent);
  --accent-deep: var(--band-accent);
  --border: var(--band-line);
  --hairline: var(--band-line);
  --surface2: var(--band-fill);
  --surface3: rgba(255, 255, 255, 0.16);
  --track: rgba(255, 255, 255, 0.18);
}
.pageband .card.hero::after { display: none; }
.pageband .card.hero.mt { margin-top: 0; }
/* solid overrides — controls that must stay opaque on navy */
.pageband .eyebrow { color: var(--band-ink2); }
.pageband .eyebrow::before { background: var(--band-underline); }
.pageband input, .pageband select { background: #fff; color: #111827; border-color: transparent; }
.pageband input::placeholder { color: #111827; opacity: 0.6; }
.pageband .segctl { background: var(--band-fill); }
.pageband .segctl button { color: rgba(255, 255, 255, 0.85); }
.pageband .segctl button.on { background: #fff; color: var(--band); box-shadow: none; }
/* beats the later .segctl button.on:hover (0,3,1): hovering the chosen segment must not turn it navy on navy */
.pageband .segctl button.on:hover { background: color-mix(in srgb, #fff 88%, var(--band)); color: var(--band); }
.pageband .segctl button:focus-visible { outline-color: var(--band-accent); }
.pageband .btn:not(.ghost):not(.danger-solid) { background: #fff; color: var(--band); }
.pageband .btn:not(.ghost):not(.danger-solid):hover { background: color-mix(in srgb, #fff 88%, var(--band)); }
.pageband .btn.ghost, .btn.onband { background: transparent; color: #fff; border: 1.5px solid rgba(255, 255, 255, 0.55); }
.pageband .btn.ghost:hover, .btn.onband:hover { background: var(--band-fill); border-color: rgba(255, 255, 255, 0.8); color: #fff; }
.pageband .btn:focus-visible, .btn.onband:focus-visible { outline: 2px solid var(--band-accent); outline-offset: 2px; }
.pageband .pill.neutral { color: #fff; background: rgba(255, 255, 255, 0.16); }
.pageband .mstep .ms-btn { background: var(--band-fill); color: #fff; border-color: var(--band-line); }
.pageband .stat-chip { background: var(--band-fill); border-color: var(--band-line); }
.pageband .stat-chip b, .pageband .stat-chip .cl { color: #fff; }
.pageband .clickable:focus-visible { outline: 2px solid var(--band-accent); outline-offset: 2px; }
.pageband .cmp-row .c-label { color: var(--band-ink2); }
.pageband .linkbtn, .pageband .linklike { color: var(--band-accent); }

.hero-value {
  font-size: 40px; font-weight: 800; letter-spacing: -0.025em; line-height: 1.05;
  margin: 4px 0 6px; word-break: break-word; font-variant-numeric: tabular-nums;
}
.hero-caption { color: var(--ink2); font-size: 13px; font-weight: 500; }
.hero-caption .amt { font-weight: 700; }

/* two heroes side by side inside one band (Accounts: cash vs owed) */
.hero-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; }
.hero-pair > * { min-width: 0; }
.hero-pair > * + * { border-left: 1px solid var(--band-line); padding-left: 24px; }

/* hero with something on the right (ring, IVA toggle) */
.hero-split { display: flex; align-items: center; gap: 28px; position: relative; z-index: 1; }
.hero-split .hs-main { flex: 1; min-width: 0; }
.hero-split .hs-side { flex: 0 0 auto; }
@media (max-width: 760px) {
  .pageband { padding: 16px 16px 44px; }
  .pageband::after { height: 16px; }
  .wrap > .pageband + * { margin-top: -22px; }
  .hero-value { font-size: 30px; }
  .hero-pair { grid-template-columns: 1fr; gap: 14px; }
  .hero-pair > * + * { border-left: 0; padding-left: 0; border-top: 1px solid var(--band-line); padding-top: 14px; }
  .hero-split { flex-direction: column; align-items: flex-start; gap: 16px; }
  .hero-split .hs-side { align-self: center; }
}

/* ============================ stat tiles ============================ */
/* number first, label second, an outlined circle icon chip on top. The DOM
   order from tile() is label -> value; flex order flips it visually. */
.tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(176px, 1fr)); gap: 14px; }
.tile { position: relative; --tone: var(--accent); padding: 16px; display: flex; flex-direction: column; align-items: flex-start; }
.tile.tone-good { --tone: var(--pos); }
.tile.tone-warn { --tone: var(--warn); }
.tile.tone-crit { --tone: var(--neg); }
.tile.tone-accent { --tone: var(--accent); }
.tile.tone-s1 { --tone: var(--s1); }
.tile.tone-s2 { --tone: var(--s2); }
.tile .t-ico {
  order: 0; width: 40px; height: 40px; border-radius: 50%; margin-bottom: 10px; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1.5px solid color-mix(in srgb, var(--tone) 55%, var(--border));
  background: color-mix(in srgb, var(--tone) 10%, transparent);
  color: color-mix(in srgb, var(--tone) 80%, var(--ink));
  transition: border-color 0.16s;
}
.t-ico svg { width: 18px; height: 18px; }
.tile .t-value { order: 1; font-size: 24px; font-weight: 800; letter-spacing: -0.025em; line-height: 1.1; margin-top: 0; font-variant-numeric: tabular-nums; }
.tile .t-label { order: 2; font-size: 12px; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; color: var(--ink3); margin-top: 5px; }
.tile .t-sub { order: 3; color: var(--ink3); font-size: 12px; margin-top: 4px; }
.card.tile, .card.clickable { transition: transform 0.16s, box-shadow 0.16s, border-color 0.16s; }
.card.tile:hover, .card.clickable:hover { transform: translateY(-2px); box-shadow: var(--shadow-lift); }
.card.tile:hover { border-color: color-mix(in srgb, var(--tone) 34%, var(--border)); }
.card.tile:hover .t-ico { border-color: color-mix(in srgb, var(--tone) 80%, var(--border)); }
@media (max-width: 760px) {
  .tile { padding: 13px; }
  .tile .t-ico { width: 32px; height: 32px; margin-bottom: 8px; }
  .tile .t-ico svg { width: 16px; height: 16px; }
  .tile .t-value { font-size: clamp(16px, 4.6vw, 20px); white-space: nowrap; } /* "−$100.503.000" never orphans its sign */
}

/* a summary zone that drills into the data it represents (tile, bar row,
   legend key) — see navAttrs() in components.js. .tile already has its own
   hover (lift), everything else gets a plain opacity cue. */
.clickable { cursor: pointer; }
.clickable:not(.tile):not(.card):hover { opacity: 0.82; }
.clickable:focus-visible { outline: 2px solid var(--accent-deep); outline-offset: 2px; border-radius: 8px; }
.tile.clickable:focus-visible { border-radius: var(--radius); }

/* ============================ grids ============================ */
.ovgrid2 { display: grid; grid-template-columns: 7fr 5fr; gap: 16px; margin-top: 16px; }
.grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-top: 16px; }
/* grid items may never inherit their content's intrinsic width: a mounted
   SVG chart would otherwise prop the card open and re-measure itself against
   the inflated width forever (horizontal scroll on phones) */
.tiles > *, .ovgrid2 > *, .grid2 > *, .ob-grid > *, .cvrow > * { min-width: 0; }
@media (max-width: 1000px) { .ovgrid2, .grid2 { grid-template-columns: 1fr; } }
.mt { margin-top: 16px; }
.mt-sm { margin-top: 8px; }
/* ============================ bars & meters ============================ */
/* one mark, one colour: data bars are solid --accent-deep (band-safe via the
   hero re-scope), money bars keep their bright pos/neg fills */
.hbar-row { display: flex; align-items: center; gap: 12px; padding: 5px 4px; border-radius: 9px; }
.hbar-row:hover { background: var(--surface2); }
.hbar-label {
  flex: 0 0 30%; max-width: 210px; font-size: 12.5px; color: var(--ink2);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; text-align: right;
  display: flex; align-items: center; justify-content: flex-end; gap: 6px;
}
.hbar-label .sdot { flex: 0 0 auto; }
.hbar-label .hbar-txt { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hbar-track { flex: 1; display: flex; align-items: center; gap: 9px; min-width: 0; }
.hbar-bar { height: 14px; border-radius: 999px; background: var(--accent-deep); min-width: 4px; flex: 0 1 auto; }
.hbar-bar.dim { opacity: 0.45; }
.hbar-val { font-size: 12.5px; color: var(--ink); font-variant-numeric: tabular-nums; white-space: nowrap; font-weight: 600; }
.axis-note { color: var(--ink3); font-size: 11.5px; margin-top: 10px; }
.axis-note.warn { color: var(--warn); }

/* Comparison rows (assets vs debts) */
.cmp { margin-top: 18px; display: flex; flex-direction: column; gap: 12px; }
.cmp-row { display: grid; grid-template-columns: 62px 1fr auto; align-items: center; gap: 12px; }
.cmp-row .c-label { font-size: 12.5px; color: var(--ink2); text-align: right; }
.cmp-row .c-bar { height: 10px; border-radius: 999px; background: var(--accent-deep); min-width: 4px; }
.cmp-row .c-bar.pos { background: var(--pos-bright); }
.cmp-row .c-bar.neg { background: var(--neg-bright); }
.cmp-row .c-val { font-size: 14px; font-weight: 700; font-variant-numeric: tabular-nums; white-space: nowrap; }

/* Status dots + inline stat line */
.sdot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; flex: none; }
.sdot.crit { background: var(--neg-bright); }
.sdot.serious { background: var(--serious); }
.sdot.warn { background: var(--warn-bright); }
.sdot.good { background: var(--pos-bright); }
.sdot.b { background: var(--s1); }
.sdot.p { background: var(--s2); }
.sdot.acc { background: var(--accent-deep); }
.sdot.idle { background: var(--ink3); }
.statline { display: flex; flex-wrap: wrap; gap: 16px; margin-top: 14px; font-size: 12.5px; color: var(--ink2); }
.statline .key { display: inline-flex; align-items: center; gap: 7px; }
.statline .key b { font-variant-numeric: tabular-nums; font-weight: 700; color: var(--ink); }
.statline .key.clickable:hover { opacity: 1; text-decoration: underline; text-underline-offset: 3px; }

/* Split bar (Business / Personal) */
.split-bar { display: flex; height: 26px; border-radius: 999px; overflow: hidden; gap: 3px; }
.split-bar .seg { height: 100%; border-radius: 999px; }
.split-bar .gap { display: none; }
.legend { display: flex; flex-direction: column; gap: 9px; margin-top: 16px; font-size: 12.5px; color: var(--ink2); }
.legend .key { display: flex; align-items: center; gap: 9px; }
.legend .key .k-name { flex: 1; }
.legend .key .k-val { font-variant-numeric: tabular-nums; color: var(--ink); font-weight: 700; }
.legend .key .k-pct { color: var(--ink3); width: 40px; text-align: right; font-variant-numeric: tabular-nums; }
.swatch { width: 10px; height: 10px; border-radius: 4px; display: inline-block; }

/* Month vs plan meter */
.meter-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; margin-bottom: 12px; flex-wrap: wrap; }
.meter-head .m-spent { font-size: 24px; font-weight: 800; letter-spacing: -0.025em; font-variant-numeric: tabular-nums; }
.meter-head .m-plan { font-size: 12.5px; color: var(--ink3); }
.meter-head .m-plan b { color: var(--ink2); font-variant-numeric: tabular-nums; }
.meter-track { height: 12px; border-radius: 999px; background: var(--track); overflow: hidden; display: flex; gap: 2px; }
.meter-fill { height: 100%; border-radius: 999px; background: var(--accent-deep); }
.meter-over { height: 100%; background: var(--warn-bright); border-radius: 999px; }
.meter-line { display: flex; justify-content: space-between; gap: 10px; margin-top: 9px; font-size: 12px; color: var(--ink3); }

/* Chart mounts (JS-measured inline SVG; the SVGs read the same tokens) */
.chart-mount { position: relative; width: 100%; margin-top: 16px; }
.chart-mount svg { display: block; max-width: 100%; }
.rail-mount svg { display: block; max-width: 100%; }
.chart-mount .hoverline { pointer-events: none; }
.rail-mount { position: relative; width: 100%; margin-top: 10px; }

/* Chart legend (dots + label) */
.clegend { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; font-size: 12px; color: var(--ink2); }
.clegend .ck { display: inline-flex; align-items: center; gap: 7px; }
.clegend .ck i { width: 9px; height: 9px; border-radius: 50%; display: inline-block; font-style: normal; }
.clegend .ck.dash i { width: 14px; height: 0; border-radius: 0; border-top: 2px dashed currentColor; opacity: 0.6; }
/* ============================ tables ============================ */
/* calm tables: a soft header strip, hairline rows, numbers tabular + 600,
   day-group heads as a sentence-case band with the short underline bar */
.tablebox { overflow-x: auto; }
table { border-collapse: collapse; width: 100%; font-size: 13px; }
th {
  text-align: left; color: var(--ink3); font-weight: 600; font-size: 11px;
  text-transform: uppercase; letter-spacing: 0.06em;
  padding: 9px 12px; border-bottom: 1px solid var(--border); white-space: nowrap;
  background: var(--surface2);
}
thead th:first-child { border-top-left-radius: 8px; border-bottom-left-radius: 8px; }
thead th:last-child { border-top-right-radius: 8px; border-bottom-right-radius: 8px; }
td { padding: 10px 12px; border-bottom: 1px solid var(--hairline); vertical-align: middle; }
tr:last-child td { border-bottom: none; }
tbody tr { transition: background-color 0.12s; }
tbody tr:hover { background: var(--surface2); }
td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
td.num { font-weight: 600; }
td.dim { color: var(--ink3); font-weight: 400; }
td.nm { font-weight: 600; }
tr.day-head td {
  padding: 14px 12px 6px; color: var(--ink2); font-size: 12px; font-weight: 600; line-height: 1.2;
  letter-spacing: 0; text-transform: none; border-bottom: 1px solid var(--border);
  background: var(--surface2); /* the group label is a title (spec E.6); its subtotal is put back on --font below */
}
tr.day-head td::before {
  content: ""; display: inline-block; width: 14px; height: 3px; border-radius: 2px;
  background: var(--underline); margin-right: 8px; vertical-align: 2px;
}
tr.day-head:hover { background: none; }
tr.day-head .day-sum {
  white-space: nowrap; /* a subtotal is a money figure: never broken or truncated */
  float: right; font-family: var(--font); font-variant-numeric: tabular-nums; color: var(--ink2);
  text-transform: none; letter-spacing: 0; font-weight: 600;
}
tr.inactive td, tr.inact td { opacity: 0.55; }
tr.inact td.acts { opacity: 1; }

/* row avatar — the initial of the row, tinted from a low-saturation set so
   it never competes with the status colors */
.rav {
  width: 34px; height: 34px; border-radius: 11px; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 13px; font-weight: 800; letter-spacing: -0.02em;
  background: var(--surface2); color: var(--ink2); text-transform: uppercase;
}
.rav.t0 { background: color-mix(in srgb, var(--accent) 15%, var(--surface)); color: var(--accent-ink); }
/* emoji avatar (Movements): the emoji is the picture — no tint, no letters */
.rav.emo { background: var(--surface2); color: inherit; font-size: 17px; font-weight: 400; }
/* photo avatar (Inventory): a real product photo, cropped to the same tile */
.rav.img { background-color: var(--surface2); background-size: cover; background-position: center; }
.rav.t1 { background: color-mix(in srgb, #6366f1 15%, var(--surface)); color: var(--rav1); }
.rav.t2 { background: color-mix(in srgb, #14b8a6 16%, var(--surface)); color: var(--rav2); }
.rav.t3 { background: color-mix(in srgb, #a855f7 15%, var(--surface)); color: var(--rav3); }
.rav.t4 { background: color-mix(in srgb, #64748b 18%, var(--surface)); color: var(--ink2); }
.rav.t5 { background: color-mix(in srgb, #f59e0b 17%, var(--surface)); color: var(--rav5); }
.namecell { display: flex; align-items: center; gap: 11px; min-width: 0; }
.namecell .nc-t { min-width: 0; }
.namecell .nc-t b { display: block; font-weight: 600; font-size: 13px; }
.namecell .nc-t span { display: block; font-size: 11.5px; color: var(--ink3); }

/* Movements & Account activity (8 sep 2026): the description is what the
   row is actually about, so it is the only field with a column of its own
   (#mov-table td.mv-desc gets width:100%, same trick as .paytable's th.nm —
   see the CSS a few sections below — so date/amount/actions stay
   content-width and description takes whatever is left). Category, type,
   payment method and who logged it move to one wrapped line underneath it
   instead of four columns that only fit on a wide screen. */
#mov-table td.mv-date { width: 1%; white-space: nowrap; }
#mov-table td.mv-desc, #mov-table th.mv-desc-h { width: 100%; }
.mv-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 5px 10px; margin-top: 6px; }
.mv-meta .cellbtn { padding: 2px 6px; margin: -2px -6px; }
.mv-meta .mv-pm, .mv-meta .mv-by { font-size: 12px; color: var(--ink3); }
.mv-meta .mv-when { display: none; font-size: 12px; color: var(--ink3); font-variant-numeric: tabular-nums; }
/* phones: the day heads already carry the date, so the date column goes
   (the date moves into the meta line), the action circles shrink and the
   cells lose side padding — the row then fits a 390px screen without a
   sideways scroll (checkbox · description · amount · actions) */
@media (max-width: 520px) {
  #mov-table th.mv-date-h, #mov-table td.mv-date { display: none; }
  .mv-meta .mv-when { display: inline; }
  #mov-table th, #mov-table td { padding-left: 3px; padding-right: 3px; }
  /* the coarse-pointer rule reserves 40px for the checkbox column; the 20px box itself stays */
  #mov-table th.cbcell, #mov-table td.cbcell { min-width: 0; padding-right: 2px; }
  /* the type chip + Income pill may break in two instead of fixing the description's minimum width */
  .mv-meta .cellbtn { flex-wrap: wrap; }
  #mov-table td.acts .ract-btn { width: 26px; height: 26px; }
  #mov-table td.acts .ract { gap: 2px; }
}

/* Inline note disclosure (tap-friendly: hover tooltips never gate data) */
details.note-d { display: inline-block; vertical-align: baseline; }
details.note-d summary { list-style: none; display: inline; cursor: pointer; color: var(--ink3); padding: 0 3px; }
details.note-d summary::-webkit-details-marker { display: none; }
details.note-d summary:hover { color: var(--accent-ink); }
details.note-d .note-body { display: block; color: var(--ink3); font-size: 12px; margin-top: 2px; max-width: 280px; white-space: normal; }

.rowfoot {
  color: var(--ink2); font-size: 12.5px; padding: 13px 12px 0; margin-top: 2px;
  border-top: 1px solid var(--hairline);
  display: flex; justify-content: space-between; flex-wrap: wrap; gap: 6px;
}
.rowfoot b { font-variant-numeric: tabular-nums; color: var(--ink); font-weight: 700; }

/* sortable headers: the active one carries an inset navy bar */
th.sortable { cursor: pointer; user-select: none; }
th.sortable:hover { color: var(--ink2); }
th.sortable:focus-visible { outline: 2px solid var(--accent-deep); outline-offset: -2px; border-radius: 0; }
th.sortable.on { color: var(--accent-ink); box-shadow: inset 0 -2px 0 var(--accent-deep); }
th .sarrow { font-size: 10px; letter-spacing: 0; }

.ordbar { padding: 0 0 10px; }
.ordnote { color: var(--ink3); font-size: 12px; }
.linkbtn { color: var(--accent-ink); font-size: 12px; font-weight: 700; text-decoration: underline; text-underline-offset: 3px; padding: 0; }
.linkbtn:hover { color: var(--accent-deep); }

/* drag handles */
th.gripcell, td.gripcell { width: 1%; padding-left: 6px; padding-right: 0; }
.grip {
  display: inline-flex; align-items: center; justify-content: center;
  width: 24px; height: 24px; border-radius: 8px; color: var(--ink3);
  cursor: grab; touch-action: none;
}
.grip:hover { background: var(--surface2); color: var(--ink2); }
.grip:focus-visible { outline: 2px solid var(--accent-deep); outline-offset: 1px; }
.grip svg { width: 15px; height: 15px; }
tbody.dnd-on { cursor: grabbing; user-select: none; }
tbody.dnd-on .grip { cursor: grabbing; }
tbody.dnd-on tr:hover { background: none; }
tbody tr.dragging { background: var(--accent-soft) !important; box-shadow: var(--shadow-pop); position: relative; z-index: 2; }
tbody tr.dragging td { border-bottom-color: var(--border); }
@media (pointer: coarse) { .grip { width: 34px; height: 34px; } .grip svg { width: 18px; height: 18px; } }
/* ============================ chips & pills ============================ */
.chip {
  display: inline-flex; align-items: center; gap: 5px; padding: 3px 10px;
  border-radius: 999px; font-size: 12px; font-weight: 600; background: var(--surface2);
  color: var(--ink2); white-space: nowrap;
}
.chip.warn { color: var(--warn-ink); background: color-mix(in srgb, var(--warn-bright) var(--tint), transparent); }
.typechip { display: inline-flex; align-items: center; gap: 7px; font-size: 12.5px; color: var(--ink2); white-space: nowrap; }
.pill {
  display: inline-flex; align-items: center; gap: 5px; padding: 3px 10px;
  border-radius: 999px; font-size: 11.5px; font-weight: 700; letter-spacing: 0.01em; white-space: nowrap;
}
.pill.crit { color: var(--neg); background: color-mix(in srgb, var(--neg-bright) var(--tint), transparent); }
.pill.serious { color: var(--serious-ink); background: color-mix(in srgb, var(--serious) var(--tint), transparent); }
.pill.warn { color: var(--warn-ink); background: color-mix(in srgb, var(--warn-bright) var(--tint), transparent); }
.pill.good { color: var(--pos-ink); background: color-mix(in srgb, var(--pos-bright) var(--tint), transparent); }
.pill.neutral { color: var(--ink2); background: var(--surface2); }
.pill.open { color: var(--accent-ink); background: color-mix(in srgb, var(--accent) var(--tint), transparent); }
/* a leading dot on every toned pill: the non-colour cue (neutral has none) */
.pill.crit::before, .pill.warn::before, .pill.good::before, .pill.open::before, .pill.serious::before {
  content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor;
  display: inline-block; flex: none; margin-right: 1px; vertical-align: 1px;
}

/* Utilization mini-meter */
.util { display: inline-flex; align-items: center; gap: 8px; }
.util .u-track { width: 58px; height: 6px; border-radius: 999px; background: var(--track); overflow: hidden; }
.util .u-fill { height: 100%; border-radius: 999px; background: var(--accent-deep); }
.util .u-fill.warn { background: var(--warn-bright); }
.util .u-fill.crit { background: var(--neg-bright); }
.util .u-pct { font-size: 11.5px; color: var(--ink3); font-variant-numeric: tabular-nums; }
/* ============================ filters ============================ */
/* Filter rows and segmented controls. Navy Ledger: the chosen segment is a
   navy fill with white ink (navy = "you can act here"); inside a .pageband
   the white-on-navy variant is applied by the band's own overrides. */
.filters { display: flex; flex-wrap: wrap; gap: 9px; margin: 0 0 14px; align-items: center; }
.filters select, .filters input[type="search"], .filters input[type="text"] {
  background: var(--surface2); border: 1px solid var(--border); border-radius: 10px;
  padding: 0 12px; font-size: 13px; min-width: 0; height: 38px;
}
.filters input[type="search"] { flex: 1; min-width: 170px; }
.filters select:focus, .filters input:focus {
  outline: none; border-color: var(--accent-deep); background: var(--surface);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent-deep) 20%, transparent);
}
.segctl { display: inline-flex; background: var(--surface2); border-radius: 10px; padding: 3px; gap: 2px; }
.segctl button {
  padding: 6px 13px; min-height: 30px; border-radius: 8px; font-size: 12.5px; font-weight: 600;
  color: var(--ink2); white-space: nowrap; transition: background-color 0.12s, color 0.12s;
}
.segctl button:hover { color: var(--ink); background: color-mix(in srgb, var(--accent) 8%, transparent); }
.segctl button.on { background: var(--accent); color: var(--btn-ink); box-shadow: none; }
.segctl button.on:hover { background: var(--accent2); color: var(--btn-ink); }
.segctl button:focus-visible { outline: 2px solid var(--accent-deep); outline-offset: -2px; border-radius: 8px; }
.segctl button.on:focus-visible { outline-color: var(--band-accent); }
.segctl.mini { margin-bottom: 6px; }
.segctl.mini button { font-size: 11.5px; padding: 4px 10px; min-height: 26px; }
@media (pointer: coarse) { .segctl button { min-height: 36px; } .segctl.mini button { min-height: 30px; } }
.count-note { color: var(--ink3); font-size: 12px; margin-left: auto; white-space: nowrap; }
.count-note b { color: var(--ink2); font-variant-numeric: tabular-nums; }
.tablebox .filters { margin: 4px 0 10px; }
/* Movements period-view: granularity segmented control + prev/label/next
   (or the two custom-range date inputs) right above the regular filter row. */
.pv-gran { max-width: 100%; overflow-x: auto; -webkit-overflow-scrolling: touch; scrollbar-width: none; margin: 0 0 10px; }
.pv-gran::-webkit-scrollbar { display: none; }
.pv-nav { display: flex; align-items: center; gap: 10px; margin: 0 0 12px; }
.pv-label { font-size: 13px; font-weight: 700; color: var(--ink); min-width: 0; }
.pv-to { color: var(--ink3); font-size: 12.5px; }
.pv-nav input[type="date"] {
  border: 1px solid var(--border); background: var(--surface2); border-radius: 10px;
  padding: 7px 10px; font-size: 12.5px; color: var(--ink); font-family: inherit; min-height: 38px;
}
.pv-nav input[type="date"]:focus {
  outline: none; border-color: var(--accent-deep); background: var(--surface);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent-deep) 20%, transparent);
}
.pv-nav button:disabled { opacity: 0.4; cursor: default; }
/* Accounts page: a list of accounts (left) + the selected one's balance
   history (right). Stacks under 900px, list above detail. Wider than before
   (320px, was 260px) so a full account name has room to wrap onto a second
   line instead of being ellipsised — see the "Accounts — net worth toggle"
   changelog ("full names, each in its own box").
   v4 "Navy Ledger": boxed rows (radius 10) with a navy notch on the selected
   one, a Poppins group eyebrow with the 22x3 underline, navy checkboxes. */
.acct-layout { display: grid; grid-template-columns: 320px 1fr; gap: 18px; align-items: start; }
.acct-list { display: flex; flex-direction: column; gap: 16px; min-width: 0; }
.acct-group-title {
  position: relative; font-size: 11px; font-weight: 600; /* Poppins: requested from U0's display list */
  text-transform: uppercase; letter-spacing: 0.07em; color: var(--ink3);
  margin: 0 0 10px 2px; padding-bottom: 7px;
}
/* the "a block starts here" grammar: a short navy bar under the eyebrow */
.acct-group-title::after { content: ""; position: absolute; left: 0; bottom: 0; width: 22px; height: 3px; border-radius: 2px; background: var(--underline); }
.acct-group { display: flex; flex-direction: column; gap: 8px; }
/* each account is its own bordered box (not a flat hover-row) so it reads as
   one discrete item even when its name wraps to two lines */
.acct-card {
  display: flex; align-items: flex-start; gap: 10px; width: 100%;
  padding: 10px 12px; border: 1px solid var(--border); border-radius: 10px;
  background: var(--surface); color: var(--ink); text-align: left; cursor: pointer; flex-wrap: wrap;
  transition: background-color 0.15s, border-color 0.15s, box-shadow 0.15s;
}
/* flags under an account (money traced here that nothing counts yet, a row
   excluded from net worth): their own full-width line, indented past the
   checkbox, so they never squeeze the name or the amount */
.acct-card .acct-flags { flex: 0 0 100%; padding-left: 26px; margin-top: 2px; }
.acct-card:hover { background: var(--surface2); }
/* selected = navy notch on the left edge + the accent wash: the one "you are
   looking at this" cue, readable at arm's length */
.acct-card.on { border-color: var(--accent-deep); background: var(--accent-soft); box-shadow: inset 3px 0 0 var(--accent); }
.acct-card.on:hover { background: var(--accent-soft); }
.acct-card:focus-visible { outline: 2px solid var(--accent-deep); outline-offset: 2px; }
.acct-card .acct-sel-cb { flex: none; margin-top: 3px; width: 16px; height: 16px; accent-color: var(--accent); cursor: pointer; }
.acct-card .acct-body { display: flex; align-items: center; justify-content: space-between; gap: 10px; flex: 1; min-width: 0; }
.acct-card .amt { font-size: 12.5px; font-weight: 700; white-space: nowrap; flex: none; font-variant-numeric: tabular-nums; }
.acct-card .namecell { min-width: 0; flex: 1; }
.acct-card .nc-t { min-width: 0; }
/* the whole point of this redesign: full names, wrapping instead of an
   ellipsis (the old .acct-row forced white-space:nowrap here) */
.acct-card .nc-t b, .acct-card .nc-t span { white-space: normal; overflow-wrap: anywhere; overflow: visible; text-overflow: clip; }
/* Credit utilization, right on the account card: a credit line is read as
   "how much of the cupo is gone", so the bar + % sit under the balance the
   same way the Debts page's card does it, in the compact shape this list
   needs. Amber past 70%, red past 90% (or overdue) — utilBar() in
   views/accounts.js picks the tone. */
.acct-util { flex: 0 0 100%; padding-left: 26px; margin-top: 7px; display: flex; flex-direction: column; gap: 4px; }
.acct-util .au-bar { display: flex; align-items: center; gap: 8px; }
.acct-util .au-track { flex: 1; height: 7px; border-radius: 999px; background: var(--track); overflow: hidden; }
.acct-util .au-track i { display: block; height: 100%; border-radius: 999px; background: var(--accent-deep); }
.acct-util .au-pct { flex: none; font-size: 11.5px; font-weight: 700; color: var(--ink2); font-variant-numeric: tabular-nums; min-width: 30px; text-align: right; }
.acct-util .au-lbl { font-size: 11px; color: var(--ink3); }
.acct-util .au-lbl b { font-weight: 700; }
.acct-util.warn .au-track i { background: var(--warn-bright); }
.acct-util.warn .au-pct { color: var(--warn); }
.acct-util.crit .au-track i { background: var(--neg-bright); }
.acct-util.crit .au-pct { color: var(--neg); }
.acct-selectall { display: flex; align-items: center; justify-content: space-between; gap: 8px; font-size: 12.5px; font-weight: 600; color: var(--ink2); margin: 2px 0 2px; }
.acct-selectall label { display: flex; align-items: center; gap: 8px; cursor: pointer; min-width: 0; }
.acct-selectall input { width: 16px; height: 16px; accent-color: var(--accent); cursor: pointer; }
.acct-detail { min-width: 0; border-top: 1px solid var(--line); padding-top: 16px; }
@media (width > 900px) { .acct-detail { border-top: none; border-left: 1px solid var(--line); padding-top: 0; padding-left: 20px; } } /* the exact complement of the stacking (max-width: 900px) below — at 900px both used to match */
/* the period segmented control sits in its own row inside the detail panel —
   .segctl is inline-flex (sized to content, so it hugs the left edge); here
   it should read as centered above the ledger instead. "safe center" matters
   because this row also has to fit 9 buttons (All/Yearly/.../Custom) on a
   narrow panel: plain `center` on an overflowing flex container clips the
   *start* of the content behind an inaccessible negative scroll offset (the
   "All" button would be unreachable) — `safe` falls back to flex-start the
   moment it would overflow, so nothing is ever clipped, only left-aligned. */
.acct-detail #f-period { display: flex; justify-content: safe center; }
/* the account-only entries, under the movements table that now shares this
   panel (7 sep 2026 — Movements folded into Accounts) */
.acct-activity { margin-top: 26px; padding-top: 18px; border-top: 1px solid var(--line); }
.acct-activity .h3row { margin-bottom: 10px; }
.acct-detail-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 10px; }
.acct-detail-head .namecell { min-width: 0; }
.acct-detail-head .amt.big { white-space: nowrap; }
.acct-amt.big { font-size: 20px; font-weight: 800; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; }
.acct-detail-acts { margin-bottom: 14px; }
.acct-ledger td { vertical-align: top; }
.acct-ledger .hint { margin-top: 2px; }
@media (max-width: 900px) {
  .acct-layout { grid-template-columns: 1fr; }
}
/* thumbs: the checkbox is the only sub-target inside a 44px+ card row */
@media (pointer: coarse) {
  .acct-card { min-height: 48px; }
  .acct-card .acct-sel-cb, .acct-selectall input { width: 20px; height: 20px; }
  .acct-card .acct-flags, .acct-util { padding-left: 30px; }
}
/* ============================ buttons ============================ */
/* Navy Ledger: a primary button is the navy fill with white ink (the ONE
   colour that means "you can act here"); radius 10 everywhere except the
   topbar's #btn-add pill (shell). Font family (Poppins) comes from base. */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  padding: 0 18px; height: 40px; border-radius: 10px;
  font-weight: 600; font-size: 13.5px; letter-spacing: 0;
  background: var(--accent); color: var(--btn-ink);
  border: 1px solid transparent; white-space: nowrap;
  transition: background-color 0.15s, transform 0.12s, border-color 0.15s;
}
.btn:hover { background: var(--accent2); }
.btn:active { transform: translateY(1px); }
.btn:focus-visible { outline: 2px solid var(--accent-deep); outline-offset: 2px; }
.btn svg { width: 16px; height: 16px; }
.btn.ghost { background: var(--surface); color: var(--ink); border-color: var(--border); font-weight: 600; }
.btn.ghost:hover { background: var(--surface2); border-color: color-mix(in srgb, var(--accent) 35%, var(--border)); }
.btn.danger-outline { background: var(--surface); color: var(--neg); border-color: color-mix(in srgb, var(--neg) 45%, transparent); font-weight: 600; }
.btn.danger-outline:hover { background: color-mix(in srgb, var(--neg) 9%, transparent); }
.btn.danger-solid { background: var(--crit); border-color: var(--crit); color: var(--on-crit); }
.btn.danger-solid:hover { background: var(--crit); filter: brightness(1.08); }
.btn.sm { height: 32px; padding: 0 13px; font-size: 12.5px; border-radius: 10px; }
.btn:disabled { opacity: 0.5; cursor: default; transform: none; }
.btn:disabled:hover { background: var(--accent); }
.btn.ghost:disabled:hover { background: var(--surface); }
.btn.block { width: 100%; }

/* the fixed positioning lives on the wrapper (not .fab itself) so the "+ Add"
   popover — an absolutely-positioned child, see addPop() in shell.js — can
   anchor to it and open upward, above the FAB, instead of off past the
   bottom of the viewport */
.fab-wrap { position: fixed; right: 18px; bottom: 18px; z-index: 30; display: none; }
@media (max-width: 1000px) { .fab-wrap { display: block; } }
.fab {
  width: 56px; height: 56px; padding: 0; border-radius: 50%; justify-content: center;
  background: var(--accent); color: var(--btn-ink);
  box-shadow: 0 10px 26px color-mix(in srgb, var(--accent) 38%, transparent);
  display: inline-flex; align-items: center;
}
.fab .plus { font-family: var(--font); font-size: 28px; font-weight: 500; line-height: 1; transition: transform 0.15s; }
.fab.on .plus { transform: rotate(45deg); }
.fab .fab-label { display: none; }
.fab:hover { background: var(--accent2); }
.fab:active { transform: scale(0.97); }
/* 19 two-line items are taller than a phone screen: the menu scrolls inside
   itself instead of opening past the top edge where it cannot be reached */
.fab-wrap .pop { top: auto; bottom: calc(100% + 8px); right: 0; }
.fab-wrap .pop.addpop { max-height: calc(100dvh - 150px); overflow-y: auto; }
@media (prefers-reduced-motion: reduce) { .fab .plus { transition: none; } }

/* ============================ forms ============================ */
.field { margin-bottom: 14px; }
.field label { display: block; font-size: 12.5px; color: var(--ink2); margin-bottom: 6px; font-weight: 600; }
.field input, .field select, .field textarea {
  width: 100%; background: var(--surface2); border: 1px solid var(--border);
  border-radius: 10px; padding: 0 12px; font-size: 14px; height: 42px;
  transition: border-color 0.12s, background-color 0.12s, box-shadow 0.12s;
}
.field textarea { height: auto; padding: 10px 12px; min-height: 84px; resize: vertical; }
.field input:focus, .field select:focus, .field textarea:focus {
  outline: none; border-color: var(--accent-deep); background: var(--surface);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent-deep) 20%, transparent);
}
.field input[type="checkbox"], .field input[type="radio"] { width: auto; height: auto; accent-color: var(--accent); }
.frow { display: flex; gap: 12px; }
.frow .field { flex: 1; min-width: 0; }
.req { color: var(--neg); }
.field-gap { margin-top: 12px; }
/* an informational note inside a form: the banner family's info variant
   (accent left bar) on the input surface */
.fnote {
  font-size: 12.5px; color: var(--ink2); background: var(--surface2); border-radius: 10px;
  border-left: 3px solid var(--accent); padding: 11px 13px; margin-bottom: 14px;
}
.fnote b { color: var(--ink); }
.fhint { font-size: 11.5px; color: var(--ink3); margin-top: 5px; }
.fhint.warn { color: var(--neg); }
.fhint a { color: var(--accent-ink); font-weight: 600; text-decoration: none; }
.fhint a:hover { text-decoration: underline; text-underline-offset: 3px; }

/* invoice drop zone — Purchases/Sales "add from invoice" card. The whole
   box is the drop target and the click target (a hidden file input behind
   it), not just a button that opens a picker screen. The icon sits in the
   template's outlined circle chip. */
.invoice-drop {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 7px; text-align: center; padding: 30px 18px;
  border: 1.5px dashed color-mix(in srgb, var(--accent) 40%, var(--border)); border-radius: 12px;
  background: var(--surface2); cursor: pointer; transition: border-color .15s, background .15s;
}
.invoice-drop:hover, .invoice-drop:focus-visible { border-color: var(--accent-deep); border-style: solid; background: var(--accent-soft); outline: none; }
.invoice-drop:focus-visible { box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent-deep) 20%, transparent); }
.invoice-drop.drag { border-color: var(--accent-deep); border-style: solid; background: var(--accent-soft); }
.invoice-drop input[type="file"] { display: none; }
.invoice-drop .id-ic, .id-ic {
  width: 48px; height: 48px; border: 1.5px solid var(--accent-ink); border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center; color: var(--accent-ink);
  background: var(--surface); margin-bottom: 4px; flex: none;
}
.invoice-drop .id-ic svg, .id-ic svg { width: 22px; height: 22px; }
.invoice-drop .id-title { font-weight: 700; font-size: 14.5px; color: var(--ink); }
.invoice-drop .id-sub { font-size: 12.5px; color: var(--ink2); max-width: 380px; }
.pickfix {
  height: 38px; display: flex; align-items: center; gap: 2px; padding: 0 12px;
  border: 1px solid var(--border); border-radius: 10px; background: var(--surface2); font-size: 13px;
}

/* live arithmetic of an inventory movement */
.calcbox { border: 1px solid var(--border); border-radius: 10px; padding: 10px 12px; margin: 4px 0 14px; background: var(--surface2); }
.calcrow {
  display: grid; grid-template-columns: 66px 1fr 1fr 1fr; gap: 8px;
  font-size: 12.5px; color: var(--ink2); padding: 3px 0; font-variant-numeric: tabular-nums;
}
.calcrow span:not(.cl) { text-align: right; }
.calcrow .cl { color: var(--ink3); font-size: 11px; text-transform: uppercase; letter-spacing: 0.06em; }
.calcrow.strong { color: var(--accent-ink); font-weight: 700; border-top: 1px solid var(--border); padding-top: 7px; margin-top: 4px; }
.calcrow.strong .cl { color: var(--accent-ink); }
.calcnote { font-size: 11.5px; color: var(--ink3); margin-top: 7px; }
/* two-column variant (account balances: label + one money value, vs the
   inventory forms' label + qty/total/unit) */
.calcrow.two { grid-template-columns: 90px 1fr; }
@media (max-width: 560px) {
  .calcrow { grid-template-columns: 1fr 1fr 1fr; }
  .calcrow .cl { grid-column: 1 / -1; }
  .calcrow.two { grid-template-columns: 90px 1fr; }
  .calcrow.two .cl { grid-column: auto; }
}

/* Account balances panel (Settings -> Manage account balances) */
.bal-list { display: flex; flex-direction: column; gap: 2px; margin-bottom: 6px; max-height: 360px; overflow-y: auto; }
.bal-row { display: flex; align-items: center; gap: 10px; padding: 9px 4px; border-bottom: 1px solid var(--hairline); border-radius: 0; }
.bal-row:last-child { border-bottom: none; }
.bal-row:hover { background: var(--surface2); }
.bal-name { flex: 1; min-width: 0; font-weight: 500; }
.bal-amt { font-family: var(--font); font-variant-numeric: tabular-nums; font-weight: 600; font-size: 13px; white-space: nowrap; }
.bal-acts { display: flex; gap: 6px; flex-shrink: 0; }
.bal-acts .btn svg { width: 14px; height: 14px; }

/* the purchase-or-manual question */
.choicegrid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
@media (max-width: 620px) { .choicegrid { grid-template-columns: 1fr; } }
.choice {
  text-align: left; display: flex; flex-direction: column; gap: 6px; cursor: pointer;
  border: 1px solid var(--border); border-radius: 12px; padding: 16px;
  background: var(--surface); color: var(--ink); font: inherit;
  transition: border-color 0.12s, background-color 0.12s;
}
.choice b { font-size: 13.5px; }
.choice span { font-size: 12px; color: var(--ink3); line-height: 1.4; }
.choice:hover { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 7%, var(--surface)); }
.choice:focus-visible { outline: 2px solid var(--accent-deep); outline-offset: 2px; }

/* the universal "+ Add" menu: a popover anchored to its trigger button (see
   addPop() in shell.js), stacked pop-items like the bell/user popovers —
   each carries a two-line label (what + hint) instead of the single line
   .pop-item normally shows, and its icon sits in the template's 28px
   outlined circle chip (the SVG is the first child of every item). */
.pop.addpop { min-width: 250px; max-height: calc(100vh - 90px); overflow-y: auto; }
.pop.addpop .pop-item > svg {
  width: 28px; height: 28px; padding: 6px; box-sizing: border-box;
  border: 1.5px solid var(--accent-ink); border-radius: 50%; color: var(--accent-ink); flex: none;
  transition: background-color 0.12s, color 0.12s;
}
.pop.addpop .pop-item:hover > svg { background: var(--accent); border-color: var(--accent); color: var(--btn-ink); }
.pop-item .pi-t { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.pop-item .pi-t b { font-size: 13px; font-weight: 600; color: var(--ink); }
.pop-item .pi-h { font-size: 11px; font-weight: 400; color: var(--ink3); line-height: 1.3; }
/* ============================ modal / toast / tooltip ============================ */
/* Navy Ledger: every dialog opens on a navy header strip (its first <h2>);
   on phones the same strip carries the bottom sheet's grab bar. The two
   photobox lightboxes have no <h2> and get no strip, by design. */
.overlay {
  position: fixed; inset: 0; background: rgba(6, 11, 21, 0.55); z-index: 70;
  -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px);
  display: flex; align-items: flex-start; justify-content: center; padding: 44px 14px;
  overflow-y: auto;
}
/* --mpx/--mpy = the modal's own padding, so the header strip's negative
   margins can never drift from it (the phone sheet re-declares both) */
.modal {
  --mpx: 22px; --mpy: 22px;
  position: relative; /* anchors the × close button */
  background: var(--surface); border: 1px solid var(--border); border-radius: 16px;
  padding: var(--mpy) var(--mpx); width: 100%; max-width: 470px; box-shadow: var(--shadow-pop);
}
/* × close button (18 sep 2026): the only pointer way out besides the
   dialog's own buttons — the backdrop no longer closes anything. Sits in
   the top-right corner; over the navy header strip it takes the strip's
   ink, over a strip-less dialog (the photo lightboxes) the page ink. Hidden
   while the dialog is sticky (a run in progress owns the screen). */
.modal-x {
  position: absolute; top: 10px; right: 10px; z-index: 2;
  width: 32px; height: 32px; padding: 0; border: 0; border-radius: 8px;
  display: inline-flex; align-items: center; justify-content: center;
  background: transparent; color: var(--ink2); cursor: pointer;
  transition: background 0.12s, color 0.12s;
}
.modal-x svg { width: 18px; height: 18px; }
.modal-x:hover { background: var(--surface2); color: var(--ink); }
.modal-x:focus-visible { outline: 2px solid var(--accent-deep); outline-offset: 2px; }
.modal:has(> h2:first-child) .modal-x { color: var(--band-ink2); }
.modal:has(> h2:first-child) .modal-x:hover { background: rgba(255, 255, 255, 0.14); color: var(--band-ink); }
.modal:has(> h2:first-child) .modal-x:focus-visible { outline-color: var(--band-ink); }
.overlay[data-sticky] .modal-x { display: none; }
@media (pointer: coarse) {
  .modal-x { width: 40px; height: 40px; top: 6px; right: 6px; }
}
.modal.wide { max-width: 760px; }
/* A products table needs the window, not a column of it: everything the
   reviewer is approving has to be readable without sideways scrolling. */
.modal.xwide { max-width: 1360px; }
.overlay:has(> .modal.xwide) { padding: 24px 16px; }
@media (prefers-reduced-motion: no-preference) {
  .modal { animation: modal-in 0.17s ease-out; }
  @keyframes modal-in { from { opacity: 0; transform: translateY(6px) scale(0.99); } }
}
.modal h2 {
  position: relative; font-size: 16px; font-weight: 600; letter-spacing: 0;
  padding-bottom: 8px; margin-bottom: 14px;
}
.modal h2::after {
  content: ""; position: absolute; left: 0; bottom: 0;
  width: 28px; height: 3px; border-radius: 2px; background: var(--underline);
}
.modal > h2:first-child {
  margin: calc(-1 * var(--mpy)) calc(-1 * var(--mpx)) 18px; padding: 15px var(--mpx);
  padding-right: calc(var(--mpx) + 36px); /* room for the × */
  background: var(--band); color: var(--band-ink); line-height: 1.25;
  border-radius: 15px 15px 0 0;
}
.modal > h2:first-child::after { display: none; }
.modal > h2:first-child .hint { color: var(--band-ink2); font-weight: 500; font-size: inherit; line-height: inherit; }
.modal h3 {
  position: relative; font-size: 14px; font-weight: 600; letter-spacing: 0;
  padding-bottom: 7px; margin: 0 0 10px;
}
.modal h3.mt { margin-top: 20px; }
.modal h3::after {
  content: ""; position: absolute; left: 0; bottom: 0;
  width: 24px; height: 3px; border-radius: 2px; background: var(--underline);
}
.modal .actions { display: flex; justify-content: flex-end; gap: 9px; margin-top: 20px; }
.confirm-body { font-size: 13.5px; color: var(--ink2); }
.confirm-body b { color: var(--ink); }
.confirm-body .dim { color: var(--ink3); }
.check-line { display: flex; gap: 9px; align-items: flex-start; margin-top: 16px; font-size: 13px; color: var(--ink2); cursor: pointer; }
.check-line input { width: auto; height: auto; margin-top: 2px; accent-color: var(--crit); }
.del-list { margin: 8px 0 0; padding-left: 18px; }
.del-list li { font-size: 12.5px; line-height: 1.6; }

/* bottom sheet (≤640px): the strip carries the grab bar and the sheet slides
   up. Radius / max-height / padding / 46px actions live in the phone shell
   block; only the paddings are mirrored here so the strip stays flush. */
@media (max-width: 640px) {
  .modal { --mpx: 18px; --mpy: 20px; }
  .modal > h2:first-child { border-radius: 17px 17px 0 0; padding-top: 12px; }
  .modal > h2:first-child::before {
    content: ""; display: block; width: 36px; height: 4px; border-radius: 999px;
    background: var(--band-line); margin: -6px auto 10px;
  }
  @media (prefers-reduced-motion: no-preference) {
    .modal { animation: sheet-in 0.22s cubic-bezier(0.22, 1, 0.36, 1); }
    @keyframes sheet-in { from { opacity: 0.6; transform: translateY(24px); } to { opacity: 1; transform: none; } }
  }
}

#toast {
  position: fixed; left: 50%; bottom: 26px; transform: translateX(-50%); z-index: 90;
  background: var(--ink); color: var(--bg); padding: 11px 18px; border-radius: 10px;
  font-size: 13.5px; font-weight: 600; opacity: 0; pointer-events: none; transition: opacity 0.25s;
  max-width: 90vw; text-align: center; box-shadow: var(--shadow-pop);
}
#toast.show { opacity: 1; }

/* =====================================================================
   Undo bar — a 5-second window to reverse the last add/edit/delete (see
   assets/js/undo.js). Bottom-center like #toast, and effectively mutually
   exclusive with it: a successful CRUD write no longer raises a toast
   (the task card in the header covers that), so the two rarely compete
   for the same spot. Lives outside #app so shell.js's re-renders never
   touch it, same reasoning as #toast/#tooltip.
   Navy Ledger: a navy pill with a white Undo button (navy = actionable).
   The countdown is a thin bar along the bottom edge, shrunk from full
   width to nothing over the same 5s the JS timer actually enforces — it
   is decorative (see .no-motion below), not the source of truth. */
#undo-bar {
  position: fixed; left: 50%; bottom: 26px; transform: translateX(-50%) translateY(10px);
  z-index: 91; display: flex; align-items: center; gap: 12px;
  background: var(--accent); color: var(--btn-ink); padding: 9px 10px 11px 16px;
  border-radius: 999px; box-shadow: var(--shadow-pop);
  opacity: 0; pointer-events: none; transition: opacity 0.2s, transform 0.2s;
  max-width: min(92vw, 420px); overflow: hidden;
}
#undo-bar.show { opacity: 1; pointer-events: auto; transform: translateX(-50%) translateY(0); }
#undo-bar .undo-msg { font-size: 13.5px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
#undo-bar .undo-btn {
  flex: none; height: 30px; padding: 0 14px; border-radius: 999px; border: none;
  background: #fff; color: var(--accent); font-weight: 800; font-size: 12.5px;
  letter-spacing: 0.01em; cursor: pointer;
}
#undo-bar .undo-btn:hover { background: var(--accent-soft); }
#undo-bar .undo-btn:active { transform: scale(0.97); }
#undo-bar .undo-btn:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
#undo-bar .undo-clock {
  position: absolute; left: 0; bottom: 0; height: 3px; width: 100%;
  background: rgba(255, 255, 255, 0.8); transform-origin: left;
}
#undo-bar.run .undo-clock { transition: transform 5s linear; transform: scaleX(0); }
@media (prefers-reduced-motion: reduce) {
  #undo-bar { transition: opacity 0.2s; }
  #undo-bar .undo-clock { display: none; }
}
@media (max-width: 760px) {
  #undo-bar { bottom: calc(84px + env(safe-area-inset-bottom)); max-width: min(92vw, 340px); }
}

#tooltip {
  position: fixed; z-index: 95; background: var(--ink); color: var(--bg);
  font-size: 12px; padding: 7px 11px; border-radius: 8px; pointer-events: none;
  opacity: 0; transition: opacity 0.12s; max-width: 280px; box-shadow: var(--shadow-pop);
}
#tooltip b { font-variant-numeric: tabular-nums; }
/* ============================ empty states ============================ */
/* the one place a centered line is right: nothing to scan, one sentence */
.empty { color: var(--ink3); font-size: 13px; padding: 24px 8px; text-align: center; }
.empty-ghost { padding: 12px 0 4px; }
.empty-ghost .g-bar { height: 16px; border: 1.5px dotted var(--border); border-radius: 999px; margin: 9px auto; }
.empty-ghost .g-note { color: var(--ink3); font-size: 12.5px; margin-top: 12px; text-align: center; }
/* ============================ login ============================
   Navy Ledger: the whole screen is the page band (navy gradient + the
   same faint line-art as .pageband, full-bleed), with one white card
   floating on it. Two-step rail (Password -> Code) is CSS-only on the
   existing <span class="st"> markup: numbers/labels are pseudo-elements,
   the dotted connector is the container's ::before placed between the
   two circles with `order`. */
.login-wrap {
  position: relative; isolation: isolate;
  min-height: 100vh; min-height: 100dvh;
  display: flex; align-items: center; justify-content: center; padding: 24px 16px;
  background: linear-gradient(135deg, var(--band), var(--band2));
  color: var(--band-ink);
}
/* line-art: same grid as .pageband, faded in from the top-left */
.login-wrap::before {
  content: ""; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background-image:
    repeating-linear-gradient(90deg, var(--band-line) 0 1px, transparent 1px 64px),
    repeating-linear-gradient(0deg, var(--band-line) 0 1px, transparent 1px 64px);
  opacity: 0.35;
  -webkit-mask-image: linear-gradient(115deg, transparent 45%, #000 100%);
  mask-image: linear-gradient(115deg, transparent 45%, #000 100%);
}
.login-card {
  position: relative; z-index: 1;
  background: var(--surface); color: var(--ink); border: 0; border-radius: 16px;
  padding: 24px; width: 100%; max-width: 400px;
  box-shadow: var(--shadow-pop), 0 0 0 1px var(--border); /* the ring keeps an edge on the dark band */
}
.login-card .logo {
  display: flex; align-items: center; gap: 12px;
  font-size: 21px; font-weight: 700; letter-spacing: -0.02em; margin-bottom: 6px;
}
/* the mark sits on a white circle (same treatment as the rail's logo tile);
   the 1px ring keeps the circle visible on the white card in light mode */
.login-card .logo .mark {
  width: 40px; height: 40px; border-radius: 50%; background: #fff; padding: 5px;
  box-shadow: 0 0 0 1px var(--border);
}
.login-card .logo .mark img { filter: none; }
.login-card .sub { color: var(--ink3); font-size: 13.5px; margin-bottom: 18px; line-height: 1.5; }

/* --- the two-step rail ------------------------------------------------ */
.login-steps {
  display: flex; align-items: flex-start; justify-content: center; gap: 0;
  margin: 14px 0 0; padding-bottom: 22px; margin-bottom: 18px;
}
.login-steps .st {
  flex: none; order: 0; position: relative;
  width: 28px; height: 28px; border-radius: 50%;
  border: 1.5px solid var(--accent-ink); background: transparent; color: var(--accent-ink);
  display: inline-flex; align-items: center; justify-content: center;
  font: 700 12px/1 var(--font); font-variant-numeric: tabular-nums;
}
.login-steps .st:last-child { order: 2; }
/* the number inside the circle */
.login-steps .st::before { content: "1"; }
.login-steps .st:last-child::before { content: "2"; }
/* step 1 shows a check once step 2 is the current one */
.login-steps .st.on:has(+ .st.on)::before { content: "\2713"; font-size: 13px; }
.login-steps .st.on {
  background: var(--accent); border-color: var(--accent); color: var(--btn-ink);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent);
}
/* labels under the circles */
.login-steps .st::after {
  position: absolute; top: calc(100% + 6px); left: 50%; transform: translateX(-50%);
  font: 600 11px/1 var(--font-display); letter-spacing: 0.02em; white-space: nowrap;
  color: var(--ink3);
}
.login-steps .st:first-child::after { content: "Password"; }
.login-steps .st:last-child::after { content: "Code"; }
.login-steps .st.on::after { color: var(--ink2); }
/* the dotted connector, drawn by the container and slotted between the two */
.login-steps::before {
  content: ""; order: 1; flex: none; width: 64px; height: 0; margin: 13px 8px 0;
  border-top: 2px dotted color-mix(in srgb, var(--accent) 45%, var(--border));
}
.login-steps:has(.st.on + .st.on)::before { border-top-color: var(--accent-deep); }

/* --- fields ------------------------------------------------------------ */
.login-card input#l-code {
  width: 100%; height: 54px; text-align: center; font-size: 24px; font-weight: 800;
  letter-spacing: 0.42em; text-indent: 0.42em; font-family: var(--font);
  background: var(--surface2); border: 1px solid var(--border); border-radius: 10px;
  font-variant-numeric: tabular-nums;
}
.login-card input#l-pw, .login-card input#l-handle {
  width: 100%; height: 48px; padding: 0 14px; font-size: 15px;
  background: var(--surface2); border: 1px solid var(--border); border-radius: 10px; color: var(--ink);
}
.login-card input#l-handle { margin-bottom: 10px; }
.login-card input#l-code:focus, .login-card input#l-pw:focus, .login-card input#l-handle:focus {
  outline: none; border-color: var(--accent-deep); background: var(--surface);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent-deep) 20%, transparent);
}
.login-card .btn.block { height: 44px; }
.login-err { color: var(--neg); font-size: 12.5px; font-weight: 500; margin-top: 12px; min-height: 17px; }
.login-swap { display: block; width: 100%; text-align: center; margin-top: 10px; padding: 8px 0; min-height: 36px; }
.login-guest { margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--line); text-align: center; }
.login-guest .hint { margin-top: 6px; }
.login-foot {
  margin-top: 18px; padding-top: 14px; border-top: 1px solid var(--hairline);
  font-size: 12.5px; color: var(--ink3); display: flex; justify-content: space-between; align-items: center;
}
.linklike { color: var(--accent-ink); font-size: 12.5px; font-weight: 600; text-decoration: underline; text-underline-offset: 3px; }
.linklike:hover { color: var(--accent-deep); }
.login-foot .linklike { padding: 6px 0; }

/* Verify-step loading bar: collapsed to nothing until a request is in
   flight (max-height 0 -> 4px), green fill racing toward its ceiling with
   a diagonal sheen sweeping across it so a fast landing still reads as
   motion, not a static stripe. Turns red without changing shape on a
   failed/expired code, same red used everywhere else for "did not work". */
.login-loadbar {
  max-height: 0; opacity: 0; margin-top: 0; overflow: hidden;
  border-radius: 999px; background: var(--track);
  transition: max-height 0.18s ease, opacity 0.15s ease, margin-top 0.18s ease;
}
.login-loadbar.on { max-height: 4px; opacity: 1; margin-top: 12px; }
.login-loadbar .ll-fill {
  position: relative; display: block; height: 4px; width: 0%; overflow: hidden;
  background: var(--pos-bright); border-radius: inherit;
  transition: width 0.12s linear, background-color 0.15s;
}
.login-loadbar.fail .ll-fill { background: var(--neg-bright); }
.login-loadbar .ll-fill::after {
  content: ''; position: absolute; inset: 0; width: 60%;
  background: linear-gradient(90deg, transparent, rgba(255,255,255,0.65), transparent);
  animation: ll-sheen 0.85s ease-in-out infinite;
}
@keyframes ll-sheen {
  0% { transform: translateX(-120%); }
  100% { transform: translateX(220%); }
}
@media (prefers-reduced-motion: reduce) {
  .login-loadbar .ll-fill::after { animation: none; }
}
/* phone: the card fills the 16px gutters, the band stays full-bleed */
@media (max-width: 480px) {
  .login-wrap { padding: 16px; align-items: flex-start; padding-top: max(24px, 8vh); }
  .login-card { max-width: none; padding: 22px 20px; }
}
/* ============================ row actions ============================ */
/* 30px circles; on coarse pointers the VISUAL size stays and the hit area
   grows through a ::after overlay (40x40), so row heights do not change */
.ract { display: inline-flex; gap: 3px; vertical-align: middle; }
.ract-btn {
  width: 30px; height: 30px; border-radius: 50%; border: 1px solid transparent;
  background: transparent; color: var(--ink3); cursor: pointer; position: relative;
  display: inline-flex; align-items: center; justify-content: center; padding: 0;
  transition: background-color 0.14s, color 0.14s;
}
.ract-btn:hover { background: var(--accent-soft); color: var(--accent-ink); }
.ract-btn.danger:hover { color: var(--neg); background: var(--neg-soft); }
.ract-btn:focus-visible { outline: 2px solid var(--accent-deep); outline-offset: 1px; }
.ract-btn svg { width: 15px; height: 15px; }
td.acts { text-align: right; white-space: nowrap; width: 1%; }
.inact-toggle { margin-top: 12px; }
@media (pointer: coarse) {
  .ract-btn::after { content: ""; position: absolute; inset: -5px; border-radius: 50%; }
  .paytable td.acts .ract-btn::after { inset: -7px; }
  .grip { position: relative; }
  .grip::after { content: ""; position: absolute; inset: -3px; }
}

/* ============================ selection + bulk bar ============================ */
th.cbcell, td.cbcell { width: 1%; padding-right: 4px; }
.cbcell input[type="checkbox"] { width: 18px; height: 18px; display: block; margin: 0; accent-color: var(--accent); cursor: pointer; }
.cbcell input[type="checkbox"]:disabled { cursor: default; opacity: 0.4; }
tbody tr.sel { background: color-mix(in srgb, var(--accent) 9%, transparent); }
tbody tr.sel:hover { background: color-mix(in srgb, var(--accent) 14%, transparent); }
@media (pointer: coarse) {
  td.cbcell, th.cbcell { min-width: 40px; }
  .cbcell input[type="checkbox"] { width: 20px; height: 20px; }
}

/* the bulk bar is a navy band while a selection exists: white text, white
   ghost buttons, the delete button keeps its crit fill */
.bulkbar { display: none; }
.bulkbar.on {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  position: sticky; top: calc(var(--tb-h) + 8px); z-index: 15;
  margin: 0 0 14px; padding: 10px 14px;
  border: 1px solid transparent;
  border-radius: 14px; background: var(--accent); color: var(--btn-ink); box-shadow: var(--shadow-lift);
}
.bulkbar .bb-count { font-size: 13px; color: var(--btn-ink); }
.bulkbar .bb-count b { color: var(--btn-ink); font-variant-numeric: tabular-nums; }
.bulkbar .bb-sum { font-size: 13px; color: var(--btn-ink); font-variant-numeric: tabular-nums; font-weight: 700; }
.bulkbar .bb-actions { margin-left: auto; display: inline-flex; gap: 8px; }
.bulkbar.on .btn.ghost { background: transparent; color: var(--btn-ink); border-color: rgba(255, 255, 255, 0.55); }
.bulkbar.on .btn.ghost:hover { background: var(--band-fill); border-color: rgba(255, 255, 255, 0.8); color: var(--btn-ink); }
.bulkbar.on .btn:focus-visible { outline: 2px solid var(--band-accent); outline-offset: 2px; }

/* ============================ payment cycle ============================
   v4 "Navy Ledger": the stepper, chips and ticks use navy for "you can act
   here" and the outlined-circle chip for the day; status tints are the
   soft tokens (--neg-soft / --pos-soft / --warn-soft), never bright fills
   behind text. Inside a .pageband U2 re-maps the stepper to the band tokens. */
.mstep { display: inline-flex; align-items: center; gap: 4px; }
.mstep .ms-btn {
  width: 30px; height: 30px; border-radius: 8px; line-height: 1; font-size: 16px;
  color: var(--ink2); display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--border); background: var(--surface);
  transition: background-color 0.15s, border-color 0.15s, color 0.15s;
}
.mstep .ms-btn:hover { background: var(--surface2); color: var(--ink); border-color: var(--accent-deep); }
.mstep .ms-btn:focus-visible { outline: 2px solid var(--accent-deep); outline-offset: 2px; }
.mstep .ms-btn:disabled { opacity: 0.35; cursor: default; }
.mstep .ms-btn:disabled:hover { background: var(--surface); color: var(--ink2); border-color: var(--border); }
.mstep .ms-label { min-width: 76px; text-align: center; font-size: 12.5px; font-weight: 700; letter-spacing: 0.01em; color: var(--ink); text-transform: none; font-variant-numeric: tabular-nums; }
.mstep .ms-now {
  margin-left: 4px; padding: 4px 10px; border-radius: 999px; font-size: 10.5px; font-weight: 700;
  letter-spacing: 0.05em; text-transform: uppercase;
  color: var(--accent-ink); background: var(--accent-soft);
  border: 1px solid color-mix(in srgb, var(--accent) 30%, var(--border));
}
.mstep .ms-now:hover { background: color-mix(in srgb, var(--accent) 20%, var(--surface)); }
.mstep .ms-now:focus-visible { outline: 2px solid var(--accent-deep); outline-offset: 2px; }
@media (pointer: coarse) { .mstep .ms-btn { width: 36px; height: 36px; } }

.segbar {
  display: flex; height: 12px; border-radius: 999px; overflow: hidden;
  background: var(--track); margin: 18px 0 14px; gap: 3px;
}
.segbar.empty { gap: 0; }
.segbar .sg { height: 100%; min-width: 5px; --tone: var(--accent-deep); background: var(--tone); border-radius: 999px; }
.segbar .sg.tone-good { --tone: var(--pos-bright); }
.segbar .sg.tone-crit { --tone: var(--neg-bright); }
.segbar .sg.tone-warn { --tone: var(--warn-bright); }
.segbar .sg.tone-open { --tone: color-mix(in srgb, var(--accent-deep) 55%, var(--track)); }

.chips-row { display: flex; flex-wrap: wrap; gap: 8px; }
/* a white chip with a 1px ring in its tone: the dot carries the colour, the
   number stays in ink */
.stat-chip {
  display: inline-flex; align-items: center; gap: 7px; padding: 6px 12px 6px 10px;
  border-radius: 999px; font-size: 12.5px; color: var(--ink2); --tone: var(--accent-deep);
  border: 1px solid var(--tone);
  background: var(--surface);
}
.stat-chip.tone-good { --tone: var(--pos-bright); }
.stat-chip.tone-crit { --tone: var(--neg-bright); }
.stat-chip.tone-warn { --tone: var(--warn-bright); }
.stat-chip.tone-open { --tone: var(--accent-deep); }
.stat-chip .cdot { width: 8px; height: 8px; border-radius: 50%; background: var(--tone); flex: none; }
.stat-chip b { color: var(--ink); font-variant-numeric: tabular-nums; font-weight: 700; font-family: var(--font); }
.stat-chip em { font-style: normal; font-size: 11px; color: var(--ink2); background: var(--surface2); border-radius: 999px; padding: 1px 7px; }

.ring { position: relative; width: 116px; height: 116px; --tone: var(--accent-deep); }
.ring.tone-good { --tone: var(--pos-bright); }
.ring.tone-idle { --tone: var(--ink3); }
.ring svg { width: 100%; height: 100%; display: block; }
.ring .r-track { stroke: var(--track); }
.ring .r-fill { stroke: var(--tone); transition: stroke-dashoffset 0.5s cubic-bezier(0.22, 1, 0.36, 1); }
.ring .r-mid { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; gap: 2px; }
.ring .r-mid b { font-size: 28px; font-weight: 800; letter-spacing: -0.03em; color: var(--ink); font-variant-numeric: tabular-nums; }
.ring .r-mid b i { font-style: normal; font-size: 14px; color: var(--ink3); margin-left: 1px; }
.ring .r-mid span { font-size: 10.5px; color: var(--ink3); max-width: 86px; line-height: 1.25; }

/* the paid tick: a 22px navy-outlined box; ticked = green fill with the DARK
   ink (--on-good — white on --pos-bright is 2.5:1) */
.paycell { width: 1%; white-space: nowrap; }
th.paycell { font-size: 10px; letter-spacing: 0.06em; color: var(--ink3); }
.pcheck { position: relative; display: inline-flex; cursor: pointer; }
.pcheck input { position: absolute; opacity: 0; width: 100%; height: 100%; margin: 0; cursor: pointer; }
.pcheck .pc-box {
  width: 22px; height: 22px; border-radius: 8px; display: inline-flex;
  align-items: center; justify-content: center;
  border: 1.5px solid color-mix(in srgb, var(--accent) 55%, var(--border));
  background: var(--surface); color: transparent;
  transition: background-color 0.15s, border-color 0.15s, color 0.15s;
}
.pcheck .pc-box svg { width: 13px; height: 13px; }
.pcheck:hover .pc-box { border-color: var(--accent-deep); background: var(--accent-soft); }
.pcheck.on .pc-box { background: var(--pos-bright); border-color: var(--pos-bright); color: var(--on-good); }
.pcheck.on:hover .pc-box { filter: brightness(1.06); }
.pcheck input:focus-visible + .pc-box { outline: 2px solid var(--accent-deep); outline-offset: 2px; }
.pcheck.busy { opacity: 0.5; pointer-events: none; }
.pcheck.busy .pc-box { animation: pulse 0.9s ease-in-out infinite; }
@keyframes pulse { 50% { opacity: 0.45; } }
.pc-na { display: inline-block; width: 22px; text-align: center; color: var(--ink3); cursor: help; }
/* thumbs: the label is the hit area — grow it, not the box */
@media (pointer: coarse) { .pcheck::after { content: ""; position: absolute; inset: -11px; } }

/* the two cycle tables */
.paygrid { grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr); } /* the 7-column table gets the wider column, the tip card the rest */
@media (max-width: 1400px) { .paygrid { grid-template-columns: 1fr; } } /* at 1280 the 7-column table hid its actions in 1.6fr */
.paytable th, .paytable td { padding: 9px 8px; }
.paytable th { letter-spacing: 0.05em; }
.paytable th:first-child, .paytable td:first-child { padding-left: 2px; }
.paytable th:last-child, .paytable td:last-child { padding-right: 2px; }
.paytable th.nm, .paytable td.nm { width: 100%; }
.paytable th:not(.nm), .paytable td:not(.nm) { width: 1%; white-space: nowrap; }
.paytable td.acts .ract-btn { width: 26px; height: 26px; }
/* why Edit/Delete are greyed out on a permission row (Manage staff).
   The [hidden] rule has to be explicit: `display: block` above would
   otherwise beat the browser's own [hidden] { display: none }. */
.perm-hint { display: block; font-size: 11px; font-weight: 500; color: var(--ink3); margin-top: 2px; }
.perm-hint[hidden] { display: none; }
.paytable input[type="checkbox"]:disabled { opacity: 0.45; cursor: not-allowed; }
tr.paid-row { background: color-mix(in srgb, var(--pos-bright) var(--tint-soft), transparent); }
tr.paid-row td.nm { color: var(--ink2); }
tr.paid-row td.num { color: var(--ink3); }
tr.paid-row:hover { background: color-mix(in srgb, var(--pos-bright) 12%, transparent); }
tr.over-row { background: var(--neg-soft); }
tr.over-row:hover { background: color-mix(in srgb, var(--neg-bright) 16%, var(--surface)); }
tr.over-row td.nm { color: var(--ink); font-weight: 700; }
tr.idle-row td.nm, tr.idle-row td.num { color: var(--ink3); }

/* ---- status-grouped payment tables (Fixed expenses / Subscriptions) ----
   A group head is a strip with a 3px tone bar on its left edge (the same
   "a block starts here" bar as card titles), not a full tinted row. --tone is
   set on the <tr> and inherited by the inner .pg-head-row. */
.paygroup:not(:first-child) .pg-head td { border-top: 6px solid transparent; }
.pg-head { --tone: var(--accent-deep); }
.pg-head td { padding: 0 !important; }
.pg-head-row {
  padding: 8px 8px 7px 10px; font-size: 11.5px; font-weight: 700;
  letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink2);
  white-space: nowrap; display: flex; align-items: center; gap: 8px;
  border-left: 3px solid var(--tone); background: var(--surface2); border-radius: 0 8px 8px 0;
}
.pg-head .sdot { flex: none; }
.pg-head em {
  font-style: normal; font-weight: 700; color: var(--ink2); background: var(--surface3);
  border-radius: 999px; padding: 1px 8px; text-transform: none; letter-spacing: normal;
  font-variant-numeric: tabular-nums;
}
.pg-head b { margin-left: auto; font-variant-numeric: tabular-nums; text-transform: none; letter-spacing: normal; font-size: 12.5px; color: var(--ink); }
.pg-head.tone-crit { --tone: var(--neg); }
.pg-head.tone-warn { --tone: var(--warn); }
.pg-head.tone-good { --tone: var(--pos); }
.pg-head.tone-idle { --tone: var(--border); }
.pg-head.tone-idle .pg-head-row, .pg-head.tone-idle b { color: var(--ink3); }

/* ---- the "Money & the universe" card (Fixed & subs, where Subscriptions
   used to sit before it moved below Fixed expenses) ---- */
.tipcard .tip-body {
  display: flex; gap: 12px; align-items: flex-start; font-size: 14.5px;
  line-height: 1.5; color: var(--ink); margin: 6px 0 10px;
}
/* the glyph sits in the same outlined circle chip as tiles and drop zones */
.tip-mark {
  flex: none; font-size: 17px; line-height: 1; width: 36px; height: 36px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1.5px solid color-mix(in srgb, var(--accent) 55%, var(--border));
  background: color-mix(in srgb, var(--accent) 8%, transparent);
}

.axis-note .lg { margin-left: 12px; display: inline-flex; align-items: center; gap: 6px; }
.axis-note .lg::before { content: ""; width: 8px; height: 8px; border-radius: 50%; background: var(--ink3); }
.axis-note .lg.good::before { background: var(--pos-bright); }
.axis-note .lg.crit::before { background: var(--neg-bright); }
.axis-note .lg.acc::before { background: var(--accent-deep); }

/* ---- obligation rows (payment-cycle bucket lists) ----
   tick · day chip (34px outlined circle) · name · status · amount */
.ob-list { display: flex; flex-direction: column; }
.ob-row { display: flex; align-items: center; gap: 11px; padding: 8px 8px; border-radius: 10px; border-bottom: 1px solid var(--hairline); min-height: 44px; }
.ob-row:last-child { border-bottom: none; }
.ob-row:hover { background: var(--surface2); }
.ob-row.is-over { background: var(--neg-soft); }
.ob-row.is-over:hover { background: color-mix(in srgb, var(--neg-bright) 16%, var(--surface)); }
.ob-row.is-over + .ob-row.is-over { border-top: 1px solid color-mix(in srgb, var(--neg) 18%, transparent); margin-top: 1px; }
.ob-tick { flex: none; display: inline-flex; }
.ob-day {
  flex: none; width: 34px; height: 34px; border-radius: 50%; box-sizing: border-box;
  border: 1.5px solid var(--accent-ink); color: var(--accent-ink); background: transparent;
  display: inline-flex; align-items: center; justify-content: center;
  font: 700 12px/1 var(--font); font-variant-numeric: tabular-nums; letter-spacing: -0.01em;
}
/* a DATE in the same chip (Daily savings rows): pill-shaped so "09-02" never wraps */
.ob-day.ob-date { width: auto; min-width: 34px; border-radius: 999px; padding: 0 9px; font-size: 11px; letter-spacing: 0; }
.ob-row.is-over .ob-day { border-color: var(--neg); color: var(--neg); background: transparent; }
.ob-name { flex: 1; min-width: 0; font-size: 13px; font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ob-name .ob-kind { color: var(--ink3); font-size: 11.5px; margin-left: 6px; font-weight: 400; }
.ob-st { flex: none; }
@media (max-width: 520px) { .ob-st { display: none; } }
.ob-amt { flex: none; font-size: 13px; font-weight: 700; font-variant-numeric: tabular-nums; white-space: nowrap; font-family: var(--font); }
.ob-clear { display: flex; align-items: center; gap: 11px; padding: 18px 4px; font-size: 13px; color: var(--ink2); }
.ob-check {
  width: 30px; height: 30px; border-radius: 50%; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--pos-soft); color: var(--pos-ink); font-weight: 800;
  border: 1.5px solid color-mix(in srgb, var(--pos-bright) 55%, var(--border));
}

/* ---- upcoming payments ---- */
.up-row { display: flex; align-items: center; gap: 11px; padding: 7px 0; border-bottom: 1px solid var(--hairline); min-height: 44px; }
.up-row:last-of-type { border-bottom: none; }
.up-tick { flex: none; display: inline-flex; }
/* the date chip: month over day, boxed on --surface2 with a 1px ring — the
   same chip family as .ob-day, in the two-line shape a date needs */
.up-when {
  flex: 0 0 50px; text-align: center; font-size: 10px; font-weight: 700; color: var(--ink3);
  background: var(--surface2); border: 1px solid var(--border); border-radius: 10px; padding: 4px 2px; line-height: 1.2;
  text-transform: uppercase; letter-spacing: 0.06em; font-family: var(--font);
}
.up-when .d { display: block; font-size: 15px; color: var(--ink); letter-spacing: -0.02em; font-variant-numeric: tabular-nums; }
.up-what { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13px; font-weight: 600; }
.up-what .kind { color: var(--ink3); font-size: 11.5px; font-weight: 500; }
.up-amt { font-variant-numeric: tabular-nums; font-size: 13px; color: var(--ink); white-space: nowrap; font-weight: 700; font-family: var(--font); }
.up-foot { display: flex; justify-content: space-between; margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--border); font-size: 12.5px; color: var(--ink2); }
.up-foot b { font-variant-numeric: tabular-nums; color: var(--ink); font-weight: 800; }
.up-row.is-over .up-when { color: var(--neg); background: var(--neg-soft); border-color: var(--neg); }
.up-row.is-over .up-when .d { color: var(--neg); }
.up-row.is-paid { background: var(--pos-soft); border-radius: 10px; padding-left: 6px; padding-right: 6px; }
.up-row.is-paid .up-what { color: var(--ink2); text-decoration: line-through; text-decoration-color: var(--ink3); }
.up-row.is-paid .up-what .pill { text-decoration: none; }
.up-row.is-paid .up-amt { color: var(--ink3); }
.up-row.is-paid .up-when { color: var(--ink3); background: var(--surface); }
.up-row.is-paid .up-when .d { color: var(--ink2); }
.up-more { font-size: 12px; color: var(--ink3); margin-top: 10px; }
.up-paid-line { display: flex; justify-content: space-between; margin-top: 7px; font-size: 12.5px; color: var(--ink2); }
.up-paid-line b { font-variant-numeric: tabular-nums; }
/* =====================================================================
   Credit lines — the pricing card of the v4 reference, applied to every
   debt that has a Credit_limit. It answers used / available / cut-off day,
   which a table row can only whisper. WHITE card, navy header strip; the
   strip turns --crit (overdue) or amber (past 70%) — the ink on each strip
   is its own pair (--on-crit / white / #241703), never a page token.
   ===================================================================== */
.cvrow { display: grid; grid-template-columns: repeat(auto-fill, minmax(288px, 1fr)); gap: 16px; }
.cardviz {
  position: relative; overflow: hidden;
  border-radius: 14px; padding: 18px;
  background: var(--surface); color: var(--ink);
  border: 1px solid var(--border);
  box-shadow: var(--shadow-card);
  min-height: 196px; --cvtone: var(--accent-deep);
  display: flex; flex-direction: column; gap: 12px;
  transition: box-shadow 0.16s, transform 0.16s;
}
.cardviz.clickable { cursor: pointer; }
.cardviz.clickable:hover { box-shadow: var(--shadow-lift); transform: translateY(-2px); }
.cardviz.clickable:focus-visible { outline: 2px solid var(--accent-deep); outline-offset: 2px; }
.cardviz::before { display: none; }
.cardviz.tone-crit { --cvtone: var(--neg-bright); }
.cardviz.tone-warn { --cvtone: var(--warn-bright); }
.cardviz > * { position: relative; z-index: 1; }
/* the header strip bleeds to the card's edges (negative margins = padding) */
.cv-top {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 10px;
  margin: -18px -18px 12px; padding: 12px 18px;
  background: var(--band); color: var(--band-ink); border-radius: 14px 14px 0 0;
}
.cardviz.tone-crit .cv-top { background: var(--crit); color: var(--on-crit); }
.cardviz.tone-warn .cv-top { background: var(--warn-ink); color: #fff; }
/* dark --warn-ink is a light yellow: the amber strip needs the dark ink there */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .cardviz.tone-warn .cv-top { background: var(--warn-bright); color: #241703; }
}
:root[data-theme="dark"] .cardviz.tone-warn .cv-top { background: var(--warn-bright); color: #241703; }
.cv-brand { font-size: 11px; font-weight: 800; letter-spacing: 0.14em; text-transform: uppercase; color: var(--band-ink2); }
.cv-name { font-size: 14.5px; font-weight: 700; letter-spacing: -0.015em; text-align: right; max-width: 60%; color: inherit; }
.cv-name span { display: block; font-size: 11px; font-weight: 500; color: var(--band-ink2); letter-spacing: 0.02em; }
/* on a crit/warn strip the secondary text follows the strip's own ink */
.cardviz.tone-crit .cv-brand, .cardviz.tone-crit .cv-name span,
.cardviz.tone-warn .cv-brand, .cardviz.tone-warn .cv-name span { color: inherit; opacity: 0.8; }
.cv-amt { font-size: 26px; font-weight: 800; letter-spacing: -0.03em; font-variant-numeric: tabular-nums; line-height: 1.1; color: var(--ink); font-family: var(--font); }
.cv-lbl { font-size: 11.5px; color: var(--ink3); margin-top: -8px; }
.cv-bar { height: 7px; border-radius: 999px; background: var(--track); overflow: hidden; }
.cv-bar i { display: block; height: 100%; border-radius: 999px; background: var(--cvtone); }
/* Two rows: balance status + status tags, then payment day + management
   actions — kept apart so the day value never shares a corner with
   anything else (that collision, with the old decorative chip glyph that
   used to sit bottom-right, was the reported overlap bug). */
.cv-foot { margin-top: auto; display: flex; align-items: flex-end; justify-content: space-between; gap: 12px; font-size: 11.5px; color: var(--ink3); }
.cv-foot b { display: block; font-size: 13.5px; font-weight: 700; color: var(--ink); font-variant-numeric: tabular-nums; margin-top: 2px; }
.cv-foot2 { display: flex; align-items: center; justify-content: space-between; gap: 12px; font-size: 11.5px; color: var(--ink2); padding-top: 10px; border-top: 1px solid var(--hairline); }
.cv-day b { font-variant-numeric: tabular-nums; color: var(--ink); font-weight: 700; }
.cv-tags { display: flex; gap: 6px; flex-wrap: wrap; justify-content: flex-end; }
.cv-tag {
  font-size: 10.5px; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase;
  padding: 3px 9px; border-radius: 999px;
  background: var(--surface2); color: var(--ink2);
}
.cv-tag.crit { background: var(--neg-soft); color: var(--neg); }
.cv-tag.warn { background: var(--warn-soft); color: var(--warn-ink); }
.cv-acts { display: inline-flex; gap: 2px; }

/* ============================ inventory ============================ */
.inv-scroll { max-height: 460px; overflow-y: auto; }
/* the only table with a vertical scroll box: its strip stays put */
.inv-scroll thead th { position: sticky; top: 0; z-index: 2; background: var(--surface2); }
.mono, td.mono { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 12px; }
.ivabox { display: flex; flex-direction: column; align-items: flex-end; gap: 8px; text-align: right; }
.ivahint { font-size: 11.5px; color: var(--ink3); max-width: 200px; }
@media (max-width: 760px) { .ivabox { align-items: flex-start; text-align: left; } }
.invlink-row { display: flex; align-items: baseline; gap: 14px; flex-wrap: wrap; }
.invlink-amt { font-size: 22px; font-weight: 800; letter-spacing: -0.025em; font-variant-numeric: tabular-nums; font-family: var(--font); }
.invlink-row .dim { font-size: 12.5px; color: var(--ink3); }

/* ---- bulk add grid (paste + manual rows share one table) ---- */
.bulkgrid-wrap { max-height: 320px; overflow-y: auto; border: 1px solid var(--border); border-radius: 10px; }
.bulkgrid { width: 100%; border-collapse: collapse; }
/* thead = the table strip (Poppins comes from the central `th` rule) */
.bulkgrid thead th {
  position: sticky; top: 0; background: var(--surface2); z-index: 1;
  font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.06em; color: var(--ink3);
  padding: 9px 8px; text-align: left; border-bottom: 1px solid var(--border);
}
.bulkgrid td { padding: 4px; border-bottom: 1px solid var(--hairline); }
.bulkgrid tr:last-child td { border-bottom: none; }
.bulkgrid input {
  width: 100%; background: transparent; border: 1px solid transparent; border-radius: 8px;
  padding: 7px 8px; font-size: 13px; height: 34px; color: var(--ink);
}
.bulkgrid input:focus { outline: none; border-color: var(--accent-deep); background: var(--surface); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent-deep) 20%, transparent); }
.bulkgrid td.num input { text-align: right; font-variant-numeric: tabular-nums; }
/* an invalid row: soft red wash + a 3px red bar on its left edge */
.bulkgrid tr.bg-err td, .bulkgrid tr.bg-err-msg td { background: var(--neg-soft); }
.bulkgrid tr.bg-err td:first-child, .bulkgrid tr.bg-err-msg td:first-child { box-shadow: inset 3px 0 0 var(--neg); }
.bulkgrid tr.bg-err td:not(:last-child) input { border-color: color-mix(in srgb, var(--neg) 55%, var(--border)); }
.bulkgrid tr.bg-err-msg td { padding: 0 8px 7px 11px; font-size: 11.5px; color: var(--neg); border-bottom: 1px solid var(--hairline); }
#bi-warn .axis-note { margin-bottom: 12px; }

/* ---- inventory item photo ---- */
.invphoto-box { display: flex; align-items: center; gap: 14px; margin-bottom: 16px; }
.invphoto-prev {
  width: 84px; height: 84px; border-radius: 12px; flex: none; background: var(--surface2);
  background-size: cover; background-position: center;
  display: flex; align-items: center; justify-content: center; color: var(--ink3);
  border: 1px solid var(--border);
}
.invphoto-prev svg { width: 30px; height: 30px; }
.invphoto-meta { font-size: 12px; color: var(--ink3); min-width: 0; }
/* ============================ task progress ============================
   Non-blocking by design: a write and the reload after it are one
   operation for the user, but they must stay free to keep working (open
   another form, switch views) while it runs. So this lives INSIDE the
   topbar itself instead of a floating overlay: #tb-fill is a 4px track
   along the header's own bottom edge whose bar grows left-to-right
   in green as the (eased, imaginary-but-honest) percentage climbs, and
   #tb-status is the header's own middle slot (next to the search box)
   printing the stage + percentage — easy to notice, impossible to
   mistake for a frozen screen. Both are empty and take no visible space
   when idle. Only #tb-status accepts clicks, and only once it has
   something to dismiss (a failure); everything else stays clickable.
   The bar is green from the first instant (Alejandro, 24 ago 2026) and
   turns red only on failure. */
.tb-fill {
  position: absolute; left: 0; right: 0; bottom: -1px; height: 4px;
  overflow: hidden; pointer-events: none; z-index: 2;
}
.tb-fill .tb-fill-bar {
  display: block; height: 100%; width: 0; background: var(--pos-bright);
  border-radius: 0 2px 2px 0;
  transition: background-color 0.2s;
}
.tb-fill[data-state="fail"] .tb-fill-bar { background: var(--neg-bright); }

.tb-status {
  flex: 1 1 auto; min-width: 0;
  display: flex; align-items: center; justify-content: center; gap: 8px;
  pointer-events: none;
}
.tb-status:empty { display: none; }
.tb-stat-ico { position: relative; width: 20px; height: 20px; flex: none; color: var(--pos); }
.tb-stat-ico svg { position: absolute; inset: 0; width: 100%; height: 100%; }
.tb-stat-ico .t-mop { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; }
.tb-stat-t { font-size: 12.5px; font-weight: 700; color: var(--ink); white-space: nowrap; }
.tb-stat-s {
  font-size: 12.5px; color: var(--ink3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* the percentage is the same navy chip as the net-worth ticker */
.tb-stat-pct {
  font-size: 11.5px; font-weight: 800; color: var(--accent-ink); background: var(--accent-soft);
  padding: 2px 9px; border-radius: 999px; line-height: 1.4;
  font-family: var(--font); font-variant-numeric: tabular-nums;
}
.tb-status[data-state="run"] .t-check, .tb-status[data-state="run"] .t-cross,
.tb-status[data-state="ok"] .t-mop, .tb-status[data-state="ok"] .t-cross,
.tb-status[data-state="fail"] .t-mop, .tb-status[data-state="fail"] .t-check { display: none; }
.tb-status[data-state="ok"] .tb-stat-ico { color: var(--pos); }
.tb-status[data-state="fail"] .tb-stat-ico { color: var(--neg); }
.tb-status[data-state="ok"] .tb-stat-t { color: var(--pos); }
.tb-status[data-state="fail"] .tb-stat-t { color: var(--neg); }
.tb-status[data-state="ok"] .tb-stat-pct { background: var(--pos-soft); color: var(--pos-ink); }
.tb-status[data-state="fail"] .tb-stat-pct { background: var(--neg-soft); color: var(--neg); }
.tb-status.dismissable { pointer-events: auto; cursor: pointer; }
@media (max-width: 760px) {
  .tb-stat-s { display: none; }
}

/* ============================ motion ============================ */
.spin { animation: spin 0.9s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }

@media (prefers-reduced-motion: no-preference) {
  #app.anim .hbar-bar, #app.anim .meter-fill, #app.anim .cmp-row .c-bar,
  #app.anim .split-bar .seg, #app.anim .u-fill, #app.anim .cv-bar i, #app.anim .segbar .sg {
    animation: grow-x 0.45s cubic-bezier(0.22, 1, 0.36, 1) backwards;
    transform-origin: left center;
  }
  @keyframes grow-x { from { transform: scaleX(0); } }
  #app.anim .ring .r-fill { animation: ring-in 0.7s cubic-bezier(0.22, 1, 0.36, 1) backwards; }
  @keyframes ring-in { from { stroke-dashoffset: 289; } }
  .pcheck.on .pc-box svg { animation: tick-pop 0.28s cubic-bezier(0.22, 1, 0.36, 1); }
  @keyframes tick-pop { 0% { transform: scale(0.4); opacity: 0; } 60% { transform: scale(1.15); } 100% { transform: scale(1); } }

  /* the band's content fades up once per data load (same gate as count-up) */
  #app.anim .pageband .pb-in > * { animation: band-in 0.22s ease-out backwards; }
  @keyframes band-in { from { opacity: 0; transform: translateY(6px); } }
  /* an opened accordion bucket (.cyc-b is U4's selector; only the motion lives here) */
  #app .cyc-b.open + .cyc-list { animation: acc-in 0.16s ease-out; }
  @keyframes acc-in { from { opacity: 0; transform: translateY(-4px); } }
  /* the current step's circle scales in */
  .steps li.on i { animation: step-in 0.2s cubic-bezier(0.22, 1, 0.36, 1); }
  @keyframes step-in { from { transform: scale(0.72); } }

  .tb-status:not(.no-motion) { animation: task-in 0.18s ease-out; }
  @keyframes task-in { from { opacity: 0; transform: translateY(-3px); } }
  .tb-status:not(.no-motion)[data-state="run"] .t-mop { animation: mop-bob 1.5s ease-in-out infinite; }
  @keyframes mop-bob { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-1.5px); } }
  .tb-status:not(.no-motion)[data-state="ok"] .t-check .t-ring,
  .tb-status:not(.no-motion)[data-state="fail"] .t-cross .t-ring {
    stroke-dasharray: 145; stroke-dashoffset: 145; animation: t-draw 0.4s ease-out forwards;
  }
  .tb-status:not(.no-motion)[data-state="ok"] .t-check .t-path,
  .tb-status:not(.no-motion)[data-state="fail"] .t-cross .t-path {
    stroke-dasharray: 60; stroke-dashoffset: 60; animation: t-draw 0.3s 0.22s ease-out forwards;
  }
  @keyframes t-draw { to { stroke-dashoffset: 0; } }
  .tb-status:not(.no-motion)[data-state="ok"] .tb-stat-ico { animation: t-pop 0.34s 0.2s cubic-bezier(0.22, 1, 0.36, 1) both; }
  @keyframes t-pop { 0% { transform: scale(0.82); } 60% { transform: scale(1.12); } 100% { transform: scale(1); } }
}

/* reduced motion: every keyframe above is off, hover lifts become
   colour-only, the decorative undo clock is hidden */
@media (prefers-reduced-motion: reduce) {
  .pageband .pb-in > *, .cyc-list, .steps li i { animation: none !important; }
  .pcheck.busy .pc-box { animation: none; } /* the tick in flight still reads as busy from its .5 opacity */
  .card.tile, .card.clickable, .tile .t-ico { transition: none; }
  .card.tile:hover, .card.clickable:hover { transform: none; box-shadow: var(--shadow-card); }
  .undo-clock { display: none; }
}
/* ============================ shell extras ============================ */
/* the user chip of the topbar: a navy circle avatar, white initial */
.userchip {
  display: inline-flex; align-items: center; gap: 9px;
  height: 40px; padding: 0 10px 0 6px; border-radius: 10px; flex: none;
  border: 1px solid var(--border); background: var(--surface);
  color: var(--ink); font-size: 13px; font-weight: 700;
  transition: background-color 0.15s, border-color 0.15s;
}
.userchip:hover { background: var(--surface2); }
.userchip:focus-visible { outline: 2px solid var(--accent-deep); outline-offset: 2px; }
.userchip.on { border-color: color-mix(in srgb, var(--accent) 45%, var(--border)); background: var(--accent-soft); }
.userchip .avatar {
  width: 28px; height: 28px; border-radius: 50%; font-size: 12px;
  background: var(--accent); color: var(--btn-ink); border: 0;
}
.userchip svg { width: 15px; height: 15px; color: var(--ink3); }
@media (max-width: 760px) { .userchip .uc-n { display: none; } .userchip { padding: 0 6px; gap: 4px; } }

/* the topbar subtitle doubles as the net-worth ticker once the hero
   of the Overview scrolls out of sight — as an accent-soft chip */
.tb-sub .s-nw { display: none; }
.tb-sub .s-nw b { font-weight: 800; font-variant-numeric: tabular-nums; }
/* the description gives way, the data's state never does (shell.js topbar):
   "updated 09:01" — and "offline — retrying" / "updating…" when they apply —
   stay whole however long the page's description is */
.tb-sub .s-def { display: flex; min-width: 0; }
.tb-sub .s-desc { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tb-sub .s-state { flex: none; white-space: nowrap; }
.tb-sub .s-state::before { content: "·"; margin: 0 0.4em; }
/* on a phone the plain time stamp is the first thing to go; a warning stays */
@media (max-width: 620px) { .tb-sub .s-state.upd { display: none; } }
.tb.scrolled .tb-sub .s-def { display: none; }
.tb.scrolled .tb-sub { overflow: visible; margin-top: 2px; }
.tb.scrolled .tb-sub .s-nw {
  display: inline-flex; align-items: baseline; gap: 5px;
  background: var(--accent-soft); color: var(--accent-ink);
  padding: 2px 10px; border-radius: 999px; font-weight: 800; line-height: 1.3;
}
.tb.scrolled .tb-sub .s-nw b.amt { color: inherit; }

/* the topbar CTA only exists where the FAB does not */
@media (max-width: 1000px) { .only-wide { display: none !important; } }

/* the search sheet: same field and same results, laid out down the modal */
.gsearch.sheet { display: block; width: 100%; flex: none; }
.gsearch.sheet .pop {
  position: static; width: 100%; max-width: none; min-width: 0;
  box-shadow: none; border: none; background: none; padding: 6px 0 0; margin-top: 6px;
  border-top: 1px solid var(--hairline); animation: none;
}
.gsearch.sheet .pop-scroll { max-height: 52vh; }
.gsearch.sheet .sr-foot { display: none; }

/* the loading spinner wrapper used by the refresh button */
#btn-refresh .spin { display: inline-flex; color: var(--accent-ink); }

/* ============================ view extras ============================ */
/* the three plan figures under the month chart: big centered numbers
   separated by hairline verticals */
.trio {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 0;
  margin-top: 18px; padding: 8px 0 0; border-top: 1px solid var(--hairline);
}
.trio > * { display: flex; flex-direction: column; align-items: center; text-align: center; padding: 8px 6px; min-width: 0; }
.trio > * + * { border-left: 1px solid var(--hairline); }
.trio .k { order: 2; font-size: 11px; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; color: var(--ink3); margin-top: 4px; }
.trio .v { order: 1; font-size: 20px; font-weight: 800; letter-spacing: -0.025em; margin-top: 0; font-variant-numeric: tabular-nums; word-break: break-word; }
@media (max-width: 460px) {
  .trio { grid-template-columns: 1fr 1fr; }
  .trio > :nth-child(2n + 1) { border-left: 0; }
  .trio > :nth-child(n + 3) { border-top: 1px solid var(--hairline); }
}


/* =====================================================================
   Steps (.steps) — the dotted "where am I" rail for multi-screen jobs.
   Markup: components.js stepBar(): <ol class="steps"><li class="done">
   <i>✓</i><span>Label<small>sub</small></span></li>… The <i> already
   carries ✓ / × / the number, so the circle only needs colour. Each <li>
   is an equal-width column (circle over label); the connector is the
   li's ::before, stretched between the previous circle and its own.
   ===================================================================== */
.steps { list-style: none; display: flex; align-items: flex-start; margin: 2px 0 16px; padding: 0; }
.steps li { position: relative; flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; align-items: center; gap: 6px; }
.steps li i {
  width: 28px; height: 28px; border-radius: 50%; flex: none; position: relative; z-index: 1;
  border: 1.5px solid var(--accent-ink); color: var(--accent-ink); background: var(--surface);
  display: inline-flex; align-items: center; justify-content: center;
  font-family: var(--font); font-style: normal; font-weight: 700; font-size: 12px; line-height: 1;
  font-variant-numeric: tabular-nums;
}
.steps li.done i { background: var(--accent); border-color: var(--accent); color: var(--btn-ink); }
.steps li.on i {
  background: var(--accent); border-color: var(--accent); color: var(--btn-ink);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent);
}
.steps li.fail i { background: var(--crit); border-color: var(--crit); color: var(--on-crit); }
.steps li span { font-size: 11.5px; font-weight: 600; color: var(--ink3); text-align: center; line-height: 1.2; }
.steps li.done span { color: var(--ink2); }
.steps li.on span { color: var(--accent-ink); white-space: nowrap; }
.steps li.fail span { color: var(--neg); }
.steps li span small { display: block; font-size: 10.5px; font-weight: 500; color: var(--ink3); margin-top: 1px; }
/* connector: from the previous circle's right edge to this circle's left edge */
.steps li + li::before {
  content: ""; position: absolute; top: 13px; z-index: 0;
  left: calc(-50% + 14px + 6px); right: calc(50% + 14px + 6px);
  border-top: 2px dotted color-mix(in srgb, var(--accent) 45%, var(--border));
}
.steps li.done + li::before { border-top-color: var(--accent-deep); }
/* inside a band: outlines and labels re-map to the band palette */
.pageband .steps li i { border-color: var(--band-accent); color: var(--band-accent); background: transparent; }
.pageband .steps li.done i, .pageband .steps li.on i { background: #fff; border-color: #fff; color: var(--band); }
.pageband .steps li.on i { box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.22); }
.pageband .steps li span, .pageband .steps li span small { color: var(--band-ink2); }
.pageband .steps li.on span { color: #fff; }
.pageband .steps li + li::before { border-top-color: var(--band-line); }
.pageband .steps li.done + li::before { border-top-color: var(--band-accent); }
/* phones: only the current label shows, circles shrink to 24px */
@media (max-width: 480px) {
  .steps li i { width: 24px; height: 24px; font-size: 11px; }
  .steps li span { display: none; }
  .steps li.on span { display: block; }
  .steps li + li::before { top: 11px; left: calc(-50% + 12px + 5px); right: calc(50% + 12px + 5px); }
}

/* Tasks checklist: the row toggle is a native checkbox drawn as a navy
   circle with a check (views/tasksnotes.js) */
#app [data-tact="toggle"] {
  appearance: none; -webkit-appearance: none; width: 22px; height: 22px; margin: 0;
  border: 1.5px solid var(--accent); border-radius: 50%; background: var(--surface);
  display: inline-grid; place-content: center; cursor: pointer; vertical-align: middle;
}
#app [data-tact="toggle"]:checked { background: var(--accent); }
#app [data-tact="toggle"]:checked::after { content: "✓"; color: var(--btn-ink); font-size: 13px; font-weight: 800; line-height: 1; }
#app [data-tact="toggle"]:focus-visible { outline: 2px solid var(--accent-deep); outline-offset: 2px; }
@media (pointer: coarse) {
  #app [data-tact="toggle"] { width: 24px; height: 24px; }
  #app td:has(> [data-tact="toggle"]) { min-width: 44px; }
}
/* =====================================================================
   Payment cycle card — one figure, one segmented track and EXPANDABLE
   status buckets (overdue / due soon / later / paid / not due) styled as
   the v4 FAQ accordion: every bucket is a boxed row, the open one turns
   navy-soft and its list continues the box below it (one item, one box).
   ===================================================================== */
.cyc-fig { display: flex; align-items: flex-end; justify-content: space-between; gap: 14px; flex-wrap: wrap; }
.cyc-num .amt { display: block; font-size: 30px; font-weight: 800; letter-spacing: -0.025em; line-height: 1.05; font-variant-numeric: tabular-nums; font-family: var(--font); }
@media (max-width: 700px) { .cyc-num .amt { font-size: 26px; } }
.cyc-num .cyc-lbl { font-size: 12.5px; color: var(--ink3); font-weight: 500; margin-top: 4px; }
.cyc-pct { text-align: right; font-size: 12.5px; color: var(--ink3); }
.cyc-pct b { font-size: 15px; font-weight: 800; color: var(--ink); font-variant-numeric: tabular-nums; }
.cyc-pct span { display: block; }
.card .cyc-fig + .segbar { margin: 14px 0 10px; }

.cyc-buckets { display: flex; flex-direction: column; }
.cyc-b {
  display: flex; align-items: center; gap: 9px; width: 100%;
  padding: 11px 12px; margin-top: 6px; min-height: 44px;
  border: 1px solid var(--border); border-radius: 10px; background: var(--surface);
  font-size: 13px; font-weight: 600; color: var(--ink); text-align: left;
  transition: background-color 0.15s, border-color 0.15s, color 0.15s;
}
.cyc-b:hover { background: var(--surface2); }
.cyc-b:focus-visible { outline: 2px solid var(--accent-deep); outline-offset: 2px; }
/* the open header: navy-soft, and its bottom corners open into the list */
.cyc-b.open {
  background: var(--accent-soft); color: var(--accent-ink);
  border-color: color-mix(in srgb, var(--accent) 40%, var(--border));
  border-radius: 10px 10px 0 0;
}
.cyc-b .sdot { flex: none; }
/* the bucket's label takes the slack, so the count/amount stay right-aligned
   with each other across buckets (it had no rule at all before) */
.cyc-b .cyc-bl { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cyc-b em {
  font-style: normal; font-size: 11px; font-weight: 700; color: var(--accent-ink);
  background: color-mix(in srgb, var(--accent) 12%, transparent); border-radius: 999px; padding: 1px 8px;
  font-variant-numeric: tabular-nums;
}
.cyc-b b { margin-left: auto; font-variant-numeric: tabular-nums; font-weight: 700; font-family: var(--font); }
.cyc-b .chev { flex: none; display: inline-flex; color: var(--ink3); transition: transform 0.15s; }
.cyc-b.open .chev { color: var(--accent-ink); }
.cyc-b .chev svg { width: 15px; height: 15px; }
.cyc-b.open .chev { transform: rotate(180deg); }
.cyc-list { padding: 0 0 8px; }
/* the list is the same box, continued: no top edge, rounded bottom */
.cyc-b.open + .cyc-list {
  border: 1px solid color-mix(in srgb, var(--accent) 40%, var(--border)); border-top: 0;
  border-radius: 0 0 10px 10px; padding: 4px 6px 8px; margin-bottom: 2px; background: var(--surface);
}
.cyc-railwrap { border-top: 1px solid var(--hairline); padding-top: 10px; margin-top: 10px; }
/* =====================================================================
   Phone shell (≤760px) — bottom tab bar with every page, the FAB riding
   above it, and the common tools kept in reach. The sections no longer
   fit as fixed-width slots — the bar scrolls horizontally instead of
   squeezing every icon unreadably thin, same pattern as the filter rows
   below; the active tab is scrolled into view by shell.js. The topbar
   drops to 56px here (--tb-h), which moves .bulkbar and #tb-status too.
   ===================================================================== */
.tabbar { display: none; }
@media (max-width: 760px) {
  :root { --tb-h: 56px; }
  .tb-title { font-size: 17px; }
  .tabbar {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 34;
    display: flex; gap: 2px;
    overflow-x: auto; -webkit-overflow-scrolling: touch; scrollbar-width: none;
    padding: 6px 8px calc(6px + env(safe-area-inset-bottom));
    background: color-mix(in srgb, var(--surface) 94%, transparent);
    -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
    border-top: 1px solid var(--hairline);
  }
  .tabbar::-webkit-scrollbar { display: none; }
  .tab-item {
    position: relative; flex: 0 0 auto; width: 64px; min-width: 0; min-height: 52px;
    display: flex; flex-direction: column; align-items: center; gap: 5px;
    padding: 6px 2px 5px; border-radius: 10px;
    font-size: 10.5px; font-weight: 600; color: var(--ink3); letter-spacing: 0.01em;
  }
  .tab-item svg { width: 20px; height: 20px; }
  .tab-item span { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .tab-item.on { color: var(--accent-ink); background: var(--accent-soft); }
  /* the underline device under the icon, so the active tab reads mid-scroll */
  .tab-item.on::after {
    content: ""; position: absolute; left: 50%; top: 27px; margin-left: -10px;
    width: 20px; height: 3px; border-radius: 2px; background: var(--accent);
  }
  .tab-item:focus-visible { outline: 2px solid var(--accent-deep); outline-offset: -2px; }
  .tab-item .tdot {
    position: absolute; top: 3px; right: calc(50% - 15px);
    width: 8px; height: 8px; border-radius: 50%;
    background: var(--crit); border: 2px solid var(--surface); box-sizing: content-box;
  }

  /* room for the tab bar */
  .wrap { padding-bottom: calc(86px + env(safe-area-inset-bottom)); }
  footer.foot { margin-bottom: calc(76px + env(safe-area-inset-bottom)); }
  .fab-wrap { bottom: calc(72px + env(safe-area-inset-bottom)); right: 14px; }
  .fab { width: 52px; height: 52px; }
  #toast { bottom: calc(84px + env(safe-area-inset-bottom)); }

  /* filter rows become one horizontally-scrollable line of tools — the
     card-header tool strips (Contacts/Deals/Tasks/Debts/Inventory/Suppliers
     use .h3row .tools for the same job) get it too, or they wrap to three
     stacked rows at 375px */
  .filters, .h3row .tools { flex-wrap: nowrap; overflow-x: auto; -webkit-overflow-scrolling: touch; scrollbar-width: none; padding-bottom: 2px; }
  .filters::-webkit-scrollbar, .h3row .tools::-webkit-scrollbar { display: none; }
  .h3row .tools { max-width: 100%; }
  .h3row .tools > * { flex: none; }
  .filters input[type="search"] { min-width: 150px; }
  .h3row .tools input[type="search"], .h3row .tools input[type="text"] { min-width: 140px; }
  .filters .count-note, .h3row .tools .count-note { display: none; }
  .segctl button { padding: 6px 11px; }
  .pv-gran .segctl, .pv-gran.segctl { flex-wrap: nowrap; }
  .pv-nav { flex-wrap: wrap; }
  .pv-nav input[type="date"] { flex: 1; min-width: 0; }

  /* denser stat tiles: two per row instead of a long stack (the chip stays
     the outlined circle of the desktop tile, only smaller) */
  .tiles { grid-template-columns: 1fr 1fr; gap: 10px; }
  .tile { padding: 13px; }
  .ovgrid2, .grid2 { gap: 12px; }
  .mt { margin-top: 12px; }

  /* Dashboard: the next action (Upcoming) precedes the pace chart */
  .ovgrid2.upfirst > :last-child { order: -1; }
}

/* low-priority table columns disappear on phones — the data stays one tap
   away in the edit form; the row keeps name, amount, state and actions */
@media (max-width: 700px) {
  .hide-m { display: none !important; }
  td { padding: 10px 8px; }
  th { padding: 8px 8px; }
}

/* the fixed/subs cycle tables (checkbox + amount + day + status pill +
   3 action icons) are the tightest rows in the app — hide-m alone still
   leaves them wider than a phone screen, so the paycell/pill/action
   padding shrinks further here on top of the generic rule above */
@media (max-width: 700px) {
  .paytable th, .paytable td { padding: 7px 5px; }
  .paytable th.paycell, .paytable td.paycell { padding-right: 2px; }
  .paytable .pill { padding: 3px 7px; font-size: 11px; }
  .pg-head-row { white-space: normal; flex-wrap: wrap; row-gap: 2px; padding: 7px 5px 5px; }
}

/* bigger touch targets where the pointer is a finger */
@media (pointer: coarse) {
  .ract-btn { width: 36px; height: 36px; }
  .pcheck .pc-box { width: 25px; height: 25px; border-radius: 9px; }
  .pcheck .pc-box svg { width: 14px; height: 14px; }
}

/* modals become bottom sheets on phones: reachable with one thumb and the
   keyboard never pushes them off screen (the grab bar and the slide-in
   live with the modal rules) */
@media (max-width: 640px) {
  .overlay { padding: 0; align-items: flex-start; }
  .modal {
    margin-top: auto; /* bottom sheet when short, scrolls from the top when tall */
    max-width: none; border-radius: 18px 18px 0 0;
    max-height: 94dvh; overflow-y: auto;
    padding: 20px 18px calc(18px + env(safe-area-inset-bottom));
  }
  .modal .actions .btn { height: 46px; }
  .frow { flex-wrap: wrap; }
  .frow .field { flex: 1 1 45%; }
}

/* =====================================================================
   Perf pass (29 ago 2026): stale-snapshot hint, archived-history bar
   ===================================================================== */

/* topbar hint while the dashboard on screen came from the encrypted local
   snapshot and the real read is still in flight (see swr.js / S.staleData) */
.tb-stale { color: var(--accent-ink); font-weight: 600; }
.tb-offline { color: var(--neg); font-weight: 600; }

/* Movements: "Load <year>" bar for archived Expenses years */
.archbar {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 10px 12px; margin: 0 0 10px;
  background: var(--surface); border: 1px solid var(--border); border-left: 3px solid var(--accent);
  border-radius: 10px;
}
.archbar .archnote { font-size: 12.5px; color: var(--ink3); }

/* archived rows render read-only, slightly receded */
tr.arch-row td { opacity: .72; }
tr.arch-row .archtag {
  font-size: 11px; letter-spacing: .02em; text-transform: uppercase;
  white-space: nowrap; color: var(--ink3);
}
/* =====================================================================
   Suppliers file card + price catalog (29 ago 2026; Navy Ledger 14 sep 2026)
   Navy = actionable, --surface2 boxes for reference data, emerald only on
   the best offer. No literal colours except on a picture background.
   ===================================================================== */

/* the open supplier's contact block: label/value pairs on a --surface2
   slab, wrapping into as many columns as fit, so a supplier with two fields
   on file does not leave four empty cells */
.supinfo {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
  gap: 10px 18px; margin: 4px 0 12px;
  background: var(--surface2); border: 1px solid var(--hairline);
  border-radius: var(--radius-sm); padding: 12px 14px;
}
.supinfo > .hint { grid-column: 1 / -1; }
.supinfo-item { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.supinfo-item .k { font-size: 11px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: var(--ink3); }
.supinfo-item .v { font-size: 13.5px; font-weight: 500; color: var(--ink); overflow-wrap: anywhere; }
.supinfo-item .v a { color: var(--accent-ink); text-decoration: underline; text-underline-offset: 3px; }
/* brands carried read as tags (accent-soft / accent-ink), not neutral chips */
.supbrands { display: flex; flex-wrap: wrap; gap: 6px; margin: 0 0 12px; }
.supbrands .chip { background: var(--accent-soft); color: var(--accent-ink); font-weight: 600; }

/* the row of the supplier currently open below: a wash plus the navy notch
   the selected account card uses, so "open" is one device everywhere */
tbody tr.on > td { background: color-mix(in srgb, var(--accent) 10%, transparent); }
tbody tr.on > td:first-child { box-shadow: inset 3px 0 0 var(--accent); }

/* price movement against the previous list — a magnitude, so it stays
   quiet; up is what costs you more, hence the negative tint */
.pricedelta { font-size: 11px; font-weight: 700; white-space: nowrap; font-variant-numeric: tabular-nums; }
.pricedelta.up { color: var(--neg); }
.pricedelta.down { color: var(--pos); }

/* one-line note under an invoice grid row (price vs the supplier's list):
   an info banner inside a table — accent bar on the left, quiet text */
.bulkgrid tr.bg-note td {
  padding: 2px 10px 8px 12px; font-size: 11.5px; color: var(--ink2);
  border-bottom: 1px solid var(--hairline);
  box-shadow: inset 3px 0 0 var(--accent);
}

/* ---- Catalog page ---- */
/* base look of the search input. Written at element specificity (:where)
   so the band's white-on-navy override (.pageband input, which this part
   would otherwise out-cascade) wins whenever the search sits in the band. */
.catsearch { display: flex; gap: 9px; margin-top: 16px; }
:where(.catsearch) input {
  flex: 1; min-width: 0; height: 42px; padding: 0 14px; font: inherit; font-size: 14px;
  color: var(--ink); background: var(--surface2); border: 1px solid var(--border); border-radius: var(--radius-sm);
}
:where(.catsearch) input:focus {
  outline: none; border-color: var(--accent-deep);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent-deep) 20%, transparent);
}
.pageband .catsearch input:focus { box-shadow: 0 0 0 3px color-mix(in srgb, var(--band-accent) 45%, transparent); }

/* the old single-column .prod card was replaced by .pcard (see the
   PRODUCT CARDS block in 95-pip-pcard.css, 6 sep 2026) */
table.offers { width: 100%; border-collapse: collapse; margin-top: 8px; }
table.offers th {
  font-size: 11px; letter-spacing: .06em; text-transform: uppercase; color: var(--ink3);
  text-align: left; font-weight: 600; padding: 7px 10px;
  background: var(--surface2); border-bottom: 1px solid var(--border);
}
table.offers th:first-child { border-radius: 8px 0 0 8px; }
table.offers th:last-child { border-radius: 0 8px 8px 0; }
table.offers th.num, table.offers td.num { text-align: right; }
table.offers td { padding: 8px 10px; border-bottom: 1px solid var(--hairline); font-size: 13px; }
table.offers td.num { font-weight: 600; font-variant-numeric: tabular-nums; }
table.offers tr:last-child td { border-bottom: none; }
table.offers tr.best td { background: var(--pos-soft); }
table.offers tr.best td:first-child { box-shadow: inset 3px 0 0 var(--pos-bright); }
table.offers .hint { font-size: 11px; color: var(--ink3); }

@media (max-width: 760px) {
  .catsearch { flex-direction: column; }
  /* stacked: flex:1 would make the basis 0 in the column direction and
     collapse the field to one text line — keep its 42px */
  :where(.catsearch) input { flex: none; }
  .catsearch .btn { width: 100%; }
  .supinfo { padding: 10px 12px; gap: 8px 14px; }
}

/* ---------------- Fase 4 (31 ago 2026): Tasks & Notes ---------------- */
/* a note: a white box with the underlined title every card uses */
.notecard {
  background: var(--surface); border: 1px solid var(--border); border-radius: 12px;
  padding: 12px 14px 14px; margin-top: 10px; box-shadow: var(--shadow-card);
}
.notecard .h3row { align-items: flex-start; }
.notecard .h3row > b {
  position: relative; display: block; padding-bottom: 8px; margin-bottom: 2px;
  font-size: 14px; font-weight: 600; letter-spacing: -0.01em; color: var(--ink);
}
.notecard .h3row > b::after {
  content: ""; position: absolute; left: 0; bottom: 0; width: 28px; height: 3px;
  border-radius: 2px; background: var(--underline);
}
.notecard p { margin: 8px 0 0; font-size: 13.5px; line-height: 1.5; white-space: pre-wrap; color: var(--ink2); }
/* === CATALOG INGEST (pricelistocr) — 6 sep 2026, Navy Ledger 14 sep ====
   The price-list flow: setup, PDF page picker, reading progress, price
   column mapping, review-by-exception. Everything here is scoped to that
   flow's own ids/classes so it cannot reach any other screen; the shared
   pieces it uses (.bulkgrid, .segctl, .field, .frow, .fhint, .tile, .btn)
   are the portal's existing ones and are NOT redefined.
   Look: --surface2 boxes at radius 10, navy for what is chosen, --neg-soft
   for what needs a look, Poppins only on the small sub-heads.
   ==================================================================== */
.pl-split { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
@media (max-width: 760px) { .pl-split { grid-template-columns: 1fr; } }
.cbline { display: flex; align-items: center; gap: 8px; font-weight: 600; }
.cbline input { width: auto; margin: 0; accent-color: var(--accent); }

/* the drafts strip at the top of the setup screen — an info banner */
.pl-draft {
  display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap;
  border: 1px solid var(--border); border-left: 3px solid var(--accent);
  border-radius: var(--radius-sm); padding: 9px 12px; margin-bottom: 10px;
  background: var(--surface); font-size: 12.5px; color: var(--ink);
}
.pl-draft-acts { display: flex; gap: 6px; }
.pl-draft .dim { color: var(--ink3); }

/* page picker */
.pl-thumbs {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(104px, 1fr)); gap: 10px;
  max-height: 360px; overflow-y: auto; padding: 8px; border: 1px solid var(--border);
  border-radius: var(--radius-sm); background: var(--surface2);
}
.pl-thumb { position: relative; display: block; cursor: pointer; }
.pl-thumb input { position: absolute; top: 6px; left: 6px; z-index: 2; width: auto; margin: 0; accent-color: var(--accent); }
.pl-thumb-img {
  display: block; width: 100%; aspect-ratio: 3 / 4; border-radius: 8px; border: 1px solid var(--border);
  background: var(--surface) center/contain no-repeat;
}
/* a ticked page carries the navy ring; an unticked one fades */
.pl-thumb input:checked + .pl-thumb-img { box-shadow: 0 0 0 2px var(--accent-deep); border-color: transparent; }
.pl-thumb input:not(:checked) + .pl-thumb-img { opacity: 0.35; }
.pl-thumb input:focus-visible + .pl-thumb-img { outline: 2px solid var(--accent-deep); outline-offset: 2px; }
.pl-thumb-n { display: block; text-align: center; font-size: 11px; color: var(--ink3); margin-top: 3px; font-variant-numeric: tabular-nums; }

/* reading progress */
.pl-read-stat { display: flex; gap: 14px; flex-wrap: wrap; font-size: 13px; align-items: baseline; }
.pl-read-stat b { font-variant-numeric: tabular-nums; }
.pl-read-stat span { color: var(--ink3); }
.pl-read-stat .warn { color: var(--warn); }
.pl-progress { height: 6px; border-radius: 99px; background: var(--track); overflow: hidden; margin: 10px 0 4px; }
.pl-progress span { display: block; height: 100%; width: 0; border-radius: 99px; background: var(--pos-bright); transition: width 0.3s; }

/* review */
/* the sub-heads inside the flow ("Needs a look", "Everything else"): a
   display face with the short underline every block title carries */
.pl-h3 {
  position: relative; font-family: var(--font-display); font-size: 13.5px; font-weight: 600;
  letter-spacing: -0.01em; margin: 18px 0 8px; padding-bottom: 7px; color: var(--ink);
}
.pl-h3::after {
  content: ""; position: absolute; left: 0; bottom: 0; width: 24px; height: 3px;
  border-radius: 2px; background: var(--underline);
}
.pl-tabletools { display: flex; gap: 10px; align-items: center; justify-content: space-between; flex-wrap: wrap; margin-bottom: 6px; }
.pl-tabletools input[type="search"] { max-width: 260px; }
.pl-pager { display: flex; align-items: center; gap: 8px; font-size: 12px; color: var(--ink3); font-variant-numeric: tabular-nums; }
.pl-pager input { width: 62px; text-align: center; }
.bulkgrid-wrap.tall { max-height: 420px; }
/* inside a full-width dialog the lists get the height too — a 320px window
   onto 85 products is a keyhole */
.modal.xwide .bulkgrid-wrap { max-height: min(46vh, 480px); }
.modal.xwide .bulkgrid-wrap.tall { max-height: min(56vh, 620px); }
.modal.xwide .pl-edit-grid { gap: 10px 12px; }
.modal.xwide .pl-f { min-width: 132px; }
.modal.xwide .pl-f.wide { flex: 1 1 340px; }
#pl-body .dim, #pl-flag-body .dim, .pl-edit-grid .dim, #pl-pick-foot .dim { color: var(--ink3); font-size: 11.5px; }
#pl-body tr.pl-row { cursor: pointer; }
#pl-body tr.pl-row:hover td { background: var(--surface2); }
.pl-thumbcell { width: 1%; }
.pl-crop {
  display: inline-block; width: 54px; height: 54px; border-radius: 8px; border: 1px solid var(--border);
  background: var(--surface2) center/cover no-repeat;
}
.pl-crop.sm { width: 34px; height: 34px; border-radius: 6px; }

/* one product, open for editing (used by the flagged list and by a clicked
   row). A flagged one (.pl-edit.bg-err) takes the --neg-soft + red bar the
   bulk grid already gives error rows; an unflagged one opens on --surface2. */
.pl-edit td { background: var(--surface2); }
.pl-why { font-size: 11.5px; font-weight: 700; color: var(--neg); margin-bottom: 6px; }
.pl-edit-grid { display: flex; flex-wrap: wrap; gap: 8px; align-items: flex-end; }
.pl-f { display: flex; flex-direction: column; gap: 3px; min-width: 108px; }
.pl-f.wide { flex: 1 1 240px; }
.pl-f > span { font-size: 11px; font-weight: 600; color: var(--ink2); }
.pl-f input, .pl-f select { font-size: 12.5px; padding: 6px 8px; height: 34px; border-radius: var(--radius-xs); }
.pl-drop-row { align-self: flex-end; }

/* the photo phase runs long enough to need its own cancel — the progress
   band in the header is deliberately not clickable.
   z-index 92 (just above #undo-bar, which is 91) because the upload is meant
   to run while the user keeps working: at 62 it disappeared behind the very
   dialog the design invites them to open, taking its Cancel with it. It is
   also lifted off the 26px slot #toast and #undo-bar share, and follows the
   safe area on a phone the way they do. */
.ingest-photos {
  position: fixed; left: 50%; transform: translateX(-50%); bottom: 74px; z-index: 92;
  display: flex; align-items: center; gap: 10px;
  background: var(--surface); color: var(--ink);
  border: 1px solid color-mix(in srgb, var(--accent-deep) 40%, var(--border)); border-radius: 99px;
  padding: 8px 10px 8px 16px; box-shadow: var(--shadow-pop); font-size: 12.5px; font-weight: 600;
  max-width: calc(100vw - 24px);
}
/* the run is over and some pictures did not land: the pill stays, with the
   refs that have no photo and a retry that reuses the crops still in memory */
.ingest-photos.failed {
  border-radius: 14px; border-color: var(--neg); align-items: flex-start;
  padding: 10px 10px 10px 14px; flex-wrap: wrap;
}
.ingest-photos .ip-txt { display: flex; flex-direction: column; gap: 2px; max-width: min(560px, calc(100vw - 220px)); }
/* the reason, in Google's words plus what to do about it — the pill used to
   show only a count, which is the same for a missing Drive authorisation and
   a dead network (6 sep 2026) */
.ingest-photos .ip-why { font-weight: 500; color: var(--ink2); font-size: 12px; line-height: 1.35; }
.ingest-photos .ip-refs { font-weight: 500; color: var(--ink3); font-size: 11.5px; }
@media (max-width: 760px) {
  .ingest-photos { bottom: calc(84px + env(safe-area-inset-bottom)); }
  .pl-edit-grid .pl-f { min-width: 92px; }
  .pl-thumbs { grid-template-columns: repeat(auto-fill, minmax(84px, 1fr)); }
}

/* === CATALOG SEARCH + PICKER (agent C, 6 sep 2026) ===================
   The Catalog page rebuilt on the local index (catalogindex.js) and the
   shared catalog typeahead (catalogpicker.js). Everything here is either
   a new class or a MORE SPECIFIC override of an existing one — no rule
   above this block is modified.
   Colour rules: --accent for fills, --accent-deep for data marks/focus
   rings, --accent-ink for accented text. Amounts stay tabular. ========= */

/* ---- index status line under the search box ---- */
.cat-ixnote {
  margin-top: 8px; font-size: 11.5px; color: var(--ink3);
  display: flex; flex-wrap: wrap; gap: 4px; align-items: baseline;
}
.cat-ixnote a { color: var(--accent-ink); font-weight: 600; text-decoration: none; }
.cat-ixnote a:hover { text-decoration: underline; }

/* ---- product thumbnails (Drive only; CSP blocks every other host) ----
   the picture itself sits on white on purpose: a supplier's cut-out shot is
   white-backed, and on a dark surface it would show as a white slab */
.cat-thumb {
  flex: none; width: 54px; height: 54px; border-radius: var(--radius-sm); overflow: hidden;
  background: var(--surface2); border: 1px solid var(--border);
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 13px; font-weight: 700; color: var(--ink3); letter-spacing: .02em;
}
.cat-thumb img { width: 100%; height: 100%; object-fit: contain; display: block; background: #fff; }
.cat-thumb.sm { width: 34px; height: 34px; border-radius: 9px; font-size: 11px; }

/* ---- supplier price-list rows ---- */
/* Brand over the manufacturer's reference, with the supplier's own order code
   demoted underneath: the pair a person actually says ("Behringer HA400") now
   opens the row, where an opaque internal number ("7337") used to (6 sep
   2026). Fixed-ish width so the product column keeps the rest. */
.catref { white-space: nowrap; vertical-align: top; padding-right: 14px; }
.catref .cr-brand,
.catref .cr-mpn { display: block; }
/* Half again as big as the rest of the row, on purpose: brand + reference is
   what a person scans this table FOR (6 sep 2026). Sizes unchanged; brand in
   the accent text colour, reference in ink, tabular Inter (not monospace,
   the same face the product card gives it). */
.catref .cr-brand { font-size: 15px; font-weight: 700; letter-spacing: .04em; color: var(--accent-ink); text-transform: uppercase; line-height: 1.25; }
.catref .cr-mpn { font-family: var(--font); font-variant-numeric: tabular-nums; font-size: 18.5px; font-weight: 700; color: var(--ink); line-height: 1.3; letter-spacing: -0.01em; }
.catref .cr-mpn.dim { color: var(--ink3); font-weight: 600; }

/* One characteristic per line. A price list ships the whole spec sheet as one
   paragraph; read as a block it is exhausting, so descLines() breaks it and
   this stacks it. */
.speclines { display: flex; flex-direction: column; gap: 1px; }
.speclines > span { display: block; }
.speclines > span + span { color: var(--ink2); }
.plrow { display: flex; align-items: flex-start; gap: 9px; min-width: 0; }
.plrow-t { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.plrow-chips { display: flex; flex-wrap: wrap; gap: 4px; }
.plrow-chips:empty { display: none; }
tbody tr.retired td { color: var(--ink3); }
tbody tr.retired td .catref .cr-mpn, tbody tr.retired td .catref .cr-brand { color: var(--ink3); text-decoration: line-through; }

/* ---- the shared catalog picker popover ----
   Appended to <body> and positioned with fixed coordinates because every
   caller sits inside a modal (.overlay, z-index 70) with its own scroll:
   a popover parented to the field would be clipped by it. 80 keeps it above
   that overlay and below the toast (90) and the undo pill (91). Look comes
   from the base .pop (radius 12, surface, border, shadow-pop). */
.pop.catpick {
  position: fixed; z-index: 80; top: auto; right: auto; left: 0;
  min-width: 320px; max-width: min(520px, calc(100vw - 16px));
  padding: 6px;
}
.cp-search { display: flex; align-items: center; gap: 8px; padding: 4px 6px 8px; }
.cp-search .cp-ic { display: inline-flex; color: var(--ink3); }
.cp-search .cp-ic svg { width: 15px; height: 15px; }
.cp-search input {
  flex: 1; min-width: 0; height: 36px; padding: 0 10px;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--surface2); color: var(--ink); font: inherit; font-size: 13px;
}
.cp-search input:focus {
  outline: none; background: var(--surface); border-color: var(--accent-deep);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent-deep) 20%, transparent);
}
.cp-body { max-height: min(52vh, 360px); overflow-y: auto; }
.cp-note { padding: 4px 8px 6px; font-size: 11px; color: var(--ink3); }
.cp-row {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 6px 8px; border-radius: var(--radius-sm); text-align: left; min-height: 40px;
}
.cp-row:hover { background: var(--surface2); }
/* the keyboard cursor: the same accent wash + hairline ring the cell editor's
   chosen option uses */
.cp-row.cur {
  background: var(--accent-soft);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent-deep) 40%, transparent);
}
.cp-row:focus-visible { outline: 2px solid var(--accent-deep); outline-offset: -2px; }
.cp-row .cp-thumb {
  flex: none; width: 34px; height: 34px; border-radius: 9px; overflow: hidden;
  background: var(--surface2); border: 1px solid var(--border);
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 11px; font-weight: 700; color: var(--ink3);
}
.cp-row .cp-thumb img { width: 100%; height: 100%; object-fit: contain; display: block; background: #fff; }
.cp-row .cp-t { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.cp-row .cp-t b { font-size: 12.5px; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cp-row.cur .cp-t b { color: var(--accent-ink); }
.cp-row .cp-t i { font-style: normal; font-size: 11px; color: var(--ink3); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cp-row .cp-a {
  flex: none; text-align: right; display: flex; flex-direction: column; gap: 1px;
  font-size: 12px; font-weight: 600; color: var(--ink2); font-variant-numeric: tabular-nums;
}
.cp-row .cp-a i { font-style: normal; font-size: 11px; font-weight: 500; color: var(--ink3); }

/* the magnifier that opens it, inside a review grid's action cell */
.bulkgrid td.bg-acts { white-space: nowrap; text-align: right; width: 1%; }
.bulkgrid td.bg-acts .ract-btn + .ract-btn { margin-left: 3px; }
.ract-btn.cp-btn svg { width: 15px; height: 15px; }

@media (max-width: 760px) {
  .cat-thumb { width: 44px; height: 44px; border-radius: 10px; }
  .cat-thumb.md { width: 52px; height: 52px; }
  .pop.catpick { min-width: 0; left: 8px !important; right: 8px; width: auto !important; }
  .cp-body { max-height: 46vh; }
}
@media (pointer: coarse) {
  .cp-row { min-height: 44px; }
}

/* ---- price-list ingest (6 sep 2026, text layer) ----
   Two screens changed shape. The reading screen has to LOOK alive while
   three Gemini calls sit there for half a minute each (a static 90% reads
   as a crash), and the mapping screen went from a select per column to two
   radio groups plus a margin, because the catalog stores exactly two prices
   however many the page prints. */

/* the run is working: an indeterminate stripe, because there is nothing
   honest to measure between one landed batch and the next */
.pl-alive {
  height: 3px; border-radius: 99px; background: var(--track);
  overflow: hidden; margin: 8px 0 10px; position: relative;
}
.pl-alive > span {
  position: absolute; top: 0; bottom: 0; width: 34%; border-radius: 99px;
  background: linear-gradient(90deg, transparent, var(--accent-deep), transparent);
  animation: pl-sweep 1.5s linear infinite;
}
.pl-alive.idle > span { animation: none; opacity: 0.35; width: 100%; background: var(--track); }
@keyframes pl-sweep {
  from { transform: translateX(-100%); }
  to { transform: translateX(320%); }
}
@media (prefers-reduced-motion: reduce) {
  .pl-alive > span { animation: none; width: 100%; opacity: 0.5; }
}

/* one line per call in flight, so a stall is visible AS a stall */
.pl-batches { display: flex; flex-direction: column; gap: 4px; margin-bottom: 8px; }
.pl-batch {
  display: flex; align-items: center; gap: 8px; font-size: 12px;
  color: var(--ink2); padding: 6px 10px; border-radius: 9px; background: var(--surface2);
}
.pl-batch b { font-weight: 700; color: var(--ink); white-space: nowrap; }
.pl-batch > span:not(.pl-batch-dot):not(.pl-batch-age) {
  flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.pl-batch-age { flex: none; font-variant-numeric: tabular-nums; color: var(--ink3); }
.pl-batch-dot {
  flex: none; width: 7px; height: 7px; border-radius: 50%;
  background: var(--accent-deep); animation: pl-pulse 1.2s ease-in-out infinite;
}
.pl-batch.warn { color: var(--warn); }
.pl-batch.warn .pl-batch-dot { background: var(--warn-bright); }
.pl-batch.idle { opacity: 0.6; }
.pl-batch.idle .pl-batch-dot { background: var(--ink3); animation: none; }
@keyframes pl-pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: 0.35; transform: scale(0.7); }
}
@media (prefers-reduced-motion: reduce) {
  .pl-batch-dot { animation: none; }
}
.pl-eta { font-size: 11.5px; color: var(--ink3); margin-bottom: 8px; font-variant-numeric: tabular-nums; }

/* mapping: which printed column is the cost, which is the sale price.
   Native radios in the navy accent; the ticked cell takes the accent wash so
   the chosen column reads across the row, not only at the dot. */
.pl-rolegrid th.ctr, .pl-rolegrid td.ctr { text-align: center; width: 1%; white-space: nowrap; }
.pl-rolegrid thead th .dim { font-weight: 500; font-size: 10.5px; color: var(--ink3); text-transform: none; letter-spacing: 0; }
.pl-rolegrid .pl-rb { display: inline-flex; align-items: center; justify-content: center; padding: 6px 12px; min-height: 32px; cursor: pointer; border-radius: 8px; }
.pl-rolegrid .pl-rb input { width: 16px; height: 16px; margin: 0; cursor: pointer; accent-color: var(--accent); }
.pl-rolegrid .pl-rb:has(input:checked) { background: var(--accent-soft); }
.pl-rolegrid td.ctr:has(input:checked) { background: color-mix(in srgb, var(--accent) 8%, transparent); }
.pl-rolegrid .pl-rb input:focus-visible { outline: 2px solid var(--accent-deep); outline-offset: 2px; }
.pl-rolegrid tr.pl-nosale td { color: var(--ink3); font-size: 12px; }
/* what was just ticked, in words — the radio grid alone is a puzzle */
.pl-picksum {
  margin-top: 10px; padding: 10px 12px; border-radius: var(--radius-sm);
  background: var(--surface2); border: 1px solid var(--hairline);
  font-size: 12.5px; color: var(--ink2);
}
.pl-picksum b { color: var(--accent-ink); }
.pl-picksum .warn { color: var(--warn); }
.pl-align { margin: 0 0 10px; }

.pl-margin { align-items: flex-end; }
.pl-pct { display: flex; align-items: center; gap: 6px; }
.pl-pct input { max-width: 110px; }
.pl-pct > span { font-weight: 700; color: var(--ink3); }
.pl-example {
  margin-top: 8px; font-size: 12.5px; color: var(--ink2);
  background: var(--surface2); border-radius: var(--radius-sm); padding: 8px 12px;
}
.pl-example b { font-variant-numeric: tabular-nums; color: var(--ink); }

/* a sale price the margin produced, not the supplier */
.pl-calc {
  font-style: normal; font-size: 9.5px; font-weight: 700; letter-spacing: 0.03em;
  text-transform: uppercase; color: var(--ink2); border: 1px solid var(--border);
  background: var(--surface2);
  border-radius: 5px; padding: 0 3px; cursor: help; vertical-align: middle;
}
/* money is money everywhere: right-aligned, tabular, "$600.000" in the box */
.pl-edit-grid input.pl-money { text-align: right; font-variant-numeric: tabular-nums; }

/* ---- waiting heartbeat (6 sep 2026, progress.js): once the current step has
   held >= 5s (Gemini OCR, an Apps Script cold start) progress.js sets
   data-waiting="1" on #tb-fill/#tb-status so a long stall reads as actively
   working instead of frozen. Motion only without reduced-motion / .no-motion.
   The sheen is white mixed from the band ink token — the bar is navy while
   it runs, so the stripes read on it in both themes. */
@media (prefers-reduced-motion: no-preference) {
  .tb-status:not(.no-motion)[data-waiting="1"] .tb-stat-ico { animation: tb-pulse 1.4s ease-in-out infinite; }
  @keyframes tb-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }
  .tb-fill:not(.no-motion)[data-waiting="1"] .tb-fill-bar {
    background-image: repeating-linear-gradient(90deg,
      transparent 0px, transparent 8px,
      color-mix(in srgb, var(--band-ink) 45%, transparent) 8px,
      color-mix(in srgb, var(--band-ink) 45%, transparent) 16px);
    background-size: 32px 100%;
    animation: tb-sheen 0.9s linear infinite;
  }
  @keyframes tb-sheen { to { background-position: 32px 0; } }
}

/* ---- tag input (chips) (6 sep 2026, modals.js tagField): Suppliers "Brands
   carried" today, reusable for any comma-list field. Mirrors .field input. ---- */
.tagbox {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
  width: 100%; min-height: 42px; background: var(--surface2);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: 6px 8px; cursor: text;
}
.tagbox:focus-within {
  border-color: var(--accent-deep); background: var(--surface);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent-deep) 20%, transparent);
}
.tagbox input { flex: 1; min-width: 120px; border: none; background: transparent; padding: 0; height: 26px; font-size: 14px; color: var(--ink); box-shadow: none; }
.tagbox input:focus { outline: none; box-shadow: none; border: none; background: transparent; }
.tag {
  display: inline-flex; align-items: center; gap: 5px;
  background: var(--accent-soft); color: var(--accent-ink);
  font-size: 12.5px; font-weight: 600; padding: 3px 9px; border-radius: 99px;
  cursor: pointer; line-height: 1.4;
}
.tag-x {
  position: relative; display: inline-flex; align-items: center; justify-content: center;
  width: 20px; height: 20px; margin: -3px -6px -3px -2px; /* 20px target, chip height unchanged */
  border: none; background: none; padding: 0; color: inherit; opacity: .7; font-size: 13px; line-height: 1; cursor: pointer;
}
.tag-x:hover { opacity: 1; }
.tag-x:focus-visible { outline: 2px solid var(--accent-deep); outline-offset: 2px; border-radius: 50%; }

/* ---- inventory movement detail + voided ledger rows (6 sep 2026, invmovedetail.js) ---- */
.mvdl { display: grid; grid-template-columns: 1fr 1fr; gap: 0 18px; margin: 4px 0 14px; }
.mvdl > div { display: flex; justify-content: space-between; align-items: baseline; gap: 12px;
  padding: 8px 0; border-bottom: 1px solid var(--hairline); font-size: 13px; min-width: 0; }
.mvdl > div.wide { grid-column: 1 / -1; }
.mvdl span { color: var(--ink3); white-space: nowrap; }
.mvdl b { color: var(--ink); font-weight: 600; text-align: right; overflow-wrap: anywhere; font-variant-numeric: tabular-nums; }
@media (max-width: 700px) { .mvdl { grid-template-columns: 1fr; } }
.mv-banner {
  font-size: 12.5px; border-radius: var(--radius-sm); padding: 10px 12px; margin-bottom: 14px;
  background: var(--surface2); color: var(--ink2); border-left: 3px solid var(--accent);
}
.mv-banner.crit { color: var(--neg); background: var(--neg-soft); border-left-color: var(--neg); }
.mv-what { font-size: 13px; color: var(--ink2); margin: 10px 0 0; padding-left: 18px; }
.mv-what li { margin: 6px 0; }
.mv-what b { color: var(--ink); }
.mv-toggle { font-size: 12px; font-weight: 600; color: var(--accent-ink); white-space: nowrap; }
/* a voided movement and its reversal: struck, in the quiet ink, never faded
   below the readable floor */
tr.mv-void > td { text-decoration: line-through; color: var(--ink3); }
tr.mv-void > td .amt, tr.mv-void > td b { color: var(--ink3); }
tr.mv-void > td .pill, tr.mv-rev > td .pill { text-decoration: none; }
tr.mv-rev > td { color: var(--ink3); }
tr.mv-rev > td .amt, tr.mv-rev > td b { color: var(--ink3); }
/* === READING WINDOW — the price-list read as a PIP (6 sep 2026) ==========
   The read used to be a sticky modal: 20+ minutes of Gemini calls behind a
   scrim, the whole portal frozen for a phase that needs nothing from the
   user. It is now a small floating window appended to <body> (mountPip in
   pricelistocr.js): it stays on top while views, forms and writes go on
   underneath, drags by its header, collapses to one line, and docks above
   the tab bar on a phone. z-index 92 — the same tier as the photo pill and
   for the same reason: its Stop must stay reachable over the dialog (70)
   the user is now invited to keep opening.
   Navy Ledger (14 sep 2026): the head is a navy strip that recolours with
   the run's state — green landed, amber partial, crimson failed — so the
   outcome reads from across the room; the body stays a plain surface. */
.pl-pip {
  position: fixed; right: 18px; bottom: 18px; z-index: 92;
  width: 400px; max-width: calc(100vw - 24px); max-height: min(70vh, 560px);
  display: flex; flex-direction: column; overflow: hidden;
  background: var(--surface); border: 1px solid var(--border); border-radius: 14px;
  box-shadow: var(--shadow-pop);
}
.pl-pip.mini { width: 320px; }
.pl-pip.dragging { box-shadow: var(--shadow-pop), 0 0 0 2px var(--accent-deep); }
.pl-pip-head {
  display: flex; align-items: center; gap: 10px; padding: 10px 10px 10px 14px;
  background: var(--band); color: var(--band-ink);
  cursor: grab; user-select: none; -webkit-user-select: none; touch-action: none;
}
.pl-pip.dragging .pl-pip-head { cursor: grabbing; }
/* the state dot sits on a white ring so it reads on every head colour */
.pl-pip-dot {
  flex: none; width: 9px; height: 9px; border-radius: 50%;
  background: var(--band-accent); box-shadow: 0 0 0 2px var(--band-ink);
  animation: pl-pulse 1.2s ease-in-out infinite;
}
.pl-pip-ttl { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.pl-pip-ttl b {
  font-size: 13px; font-weight: 700; color: inherit;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.pl-pip-ttl span {
  font-size: 11.5px; color: var(--band-ink2); font-variant-numeric: tabular-nums;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.pl-pip-btn {
  flex: none; width: 30px; height: 30px; padding: 0; border-radius: 9px;
  border: 1px solid var(--band-line); background: var(--band-fill); color: inherit;
  display: inline-flex; align-items: center; justify-content: center; cursor: pointer;
}
.pl-pip-btn:hover { background: var(--band-line); }
.pl-pip-btn:focus-visible { outline: 2px solid var(--band-accent); outline-offset: 2px; }
.pl-pip-btn svg { transition: transform 0.15s; }
.pl-pip.mini .pl-pip-btn svg { transform: rotate(180deg); }
/* the 4px bar under the head: green fill on the head's own colour */
.pl-pip .pl-pip-bar {
  margin: 0; height: 4px; border-radius: 0; flex: none;
  background-color: var(--band); background-image: linear-gradient(var(--band-line), var(--band-line));
}
.pl-pip .pl-pip-bar span { background: var(--pos-bright); }
/* state on the head (pipState in pricelistocr.js): done / warn / fail */
.pl-pip[data-state="done"] .pl-pip-head, .pl-pip[data-state="done"] .pl-pip-bar { background-color: var(--pos-bright); color: var(--on-good); }
.pl-pip[data-state="warn"] .pl-pip-head, .pl-pip[data-state="warn"] .pl-pip-bar { background-color: var(--warn-bright); color: #241703; }
.pl-pip[data-state="fail"] .pl-pip-head, .pl-pip[data-state="fail"] .pl-pip-bar { background-color: var(--crit); color: var(--on-crit); }
.pl-pip[data-state="done"] .pl-pip-ttl span,
.pl-pip[data-state="warn"] .pl-pip-ttl span,
.pl-pip[data-state="fail"] .pl-pip-ttl span { color: inherit; opacity: .82; }
.pl-pip[data-state="done"] .pl-pip-btn,
.pl-pip[data-state="warn"] .pl-pip-btn,
.pl-pip[data-state="fail"] .pl-pip-btn { border-color: color-mix(in srgb, currentColor 30%, transparent); background: color-mix(in srgb, currentColor 12%, transparent); }
.pl-pip[data-state="done"] .pl-pip-btn:focus-visible,
.pl-pip[data-state="warn"] .pl-pip-btn:focus-visible,
.pl-pip[data-state="fail"] .pl-pip-btn:focus-visible { outline-color: currentColor; }
.pl-pip[data-state="done"] .pl-pip-dot { background: var(--on-good); box-shadow: 0 0 0 2px var(--band-ink); animation: none; }
.pl-pip[data-state="warn"] .pl-pip-dot { background: #241703; box-shadow: 0 0 0 2px var(--band-ink); animation: none; }
.pl-pip[data-state="fail"] .pl-pip-dot { background: var(--on-crit); box-shadow: 0 0 0 2px var(--band-ink); animation: none; }
.pl-pip[data-state="done"] .pl-pip-bar span { background: var(--on-good); opacity: .35; }
.pl-pip[data-state="warn"] .pl-pip-bar span { background: #241703; opacity: .35; }
.pl-pip[data-state="fail"] .pl-pip-bar span { background: var(--on-crit); opacity: .35; }
.pl-pip-body { padding: 12px 14px 14px; overflow-y: auto; min-height: 0; background: var(--surface); }
.pl-pip-body .fnote { margin-bottom: 8px; }
.pl-pip-body .actions { display: flex; justify-content: flex-end; gap: 8px; flex-wrap: wrap; margin-top: 12px; }
.pl-pip.mini .pl-pip-body { display: none; }
@media (prefers-reduced-motion: reduce) {
  .pl-pip-dot { animation: none; }
  .pl-pip-btn svg { transition: none; }
}
/* between the tab-bar breakpoint and the FAB's: the FAB sits in the same
   corner, so the window's default spot moves up over it */
@media (max-width: 1000px) and (min-width: 761px) { .pl-pip { bottom: 88px; } }
/* a phone: docked above the tab bar, full width, never dragged; the FAB and
   the bottom-centre pills step up out of its way */
@media (max-width: 760px) {
  .pl-pip {
    left: 8px !important; right: 8px !important; top: auto !important;
    bottom: calc(70px + env(safe-area-inset-bottom)) !important;
    width: auto; max-width: none; max-height: 60vh; border-radius: 14px;
  }
  .pl-pip.mini { width: auto; }
  .pl-pip-head { cursor: default; }
  body.has-pip .fab-wrap { bottom: calc(136px + env(safe-area-inset-bottom)); }
  body.has-pip #toast, body.has-pip #undo-bar, body.has-pip .ingest-photos {
    bottom: calc(148px + env(safe-area-inset-bottom));
  }
}

/* =====================================================================
   PRODUCT CARDS — the catalog, redesigned (6 sep 2026; Navy Ledger 14 sep)
   ---------------------------------------------------------------------
   Alejandro: "the catalog photos are far too small", with a reference
   layout — a real card per product: picture, brand + reference as the
   headline, and the prices as a row of labelled figures underneath.

   Three rules the block below follows:
     - the picture is sized in the CARD, never by the image's own pixels:
       object-fit:contain on white, because a product shot on a price list
       is always cut out on a white page and letterboxing it beats cropping
       a loudspeaker in half. That white is the one literal here: a picture
       background, not a text surface.
     - .as-list is the SAME markup at a smaller size (one column, thumbnail
       instead of a panel, prices inline). No second set of card classes.
     - a product on promotion is green — the border, the flag over the
       photo and the promo figure — because it is the one thing here that
       expires. Green is the money-positive family (--pos), the same one
       the rest of the portal uses for "good news about an amount".
   The prices read as a mini stats strip: 10px caps label under each figure,
   the leading cost in the accent text colour, the floor in amber.
   ================================================================== */

.prodgrid {
  display: grid; gap: 12px;
  /* sized to the pictures that exist: a supplier's PDF embeds its product
     shots at 60-140px, so the card is a compact horizontal one and three of
     them fit a desktop row instead of one enormous frame around a stamp */
  grid-template-columns: repeat(auto-fill, minmax(360px, 1fr));
}
@media (max-width: 800px) { .prodgrid { grid-template-columns: 1fr; } }

.pcard {
  display: flex; gap: 0; min-width: 0;
  border: 1px solid var(--border); border-radius: 12px; overflow: hidden;
  background: var(--surface); box-shadow: var(--shadow-card);
  flex-direction: row; align-items: stretch;
  transition: transform 0.16s ease, box-shadow 0.16s ease, border-color 0.16s ease;
}
.pcard:hover { transform: translateY(-2px); box-shadow: var(--shadow-lift); }
.pcard.promo { border-color: var(--pos-bright); }
@media (prefers-reduced-motion: reduce) {
  .pcard { transition: none; }
  .pcard:hover { transform: none; }
}

/* ---- the picture, at its own resolution ---- */
/* an inset square tile, never a full-height panel: stretched down the side
   of a card that happens to carry an offers table, a 100px photo became a
   tall white column with a stamp in the middle of it */
.pcard-pic {
  position: relative; flex: none; align-self: flex-start;
  width: 104px; height: 104px; background: #fff;
  margin: 11px 0 11px 11px;
  border: 1px solid var(--border); border-radius: 10px;
  display: flex; align-items: center; justify-content: center; cursor: zoom-in;
  padding: 5px;
}
/* never stretched: these files are 60-140px wide and upscaling them in a
   card only makes them mushy (see fitPhotos in views/catalog.js) */
.pcard-pic img { max-width: 100%; max-height: 100%; object-fit: contain; display: block; }
.pcard-pic.none { cursor: default; background: var(--surface2); }
.pcard-nopic {
  font-size: 10px; letter-spacing: .06em; text-transform: uppercase; color: var(--ink3);
  text-align: center; line-height: 1.3;
}
.promo-flag {
  position: absolute; top: 6px; left: 6px;
  /* dark ink on the green fill, never white: --pos-bright is a mid green in
     light and a brighter one in dark, and white on either is around 2:1
     (B.3: #06281C on --pos-bright = 6.23 / 8.23). */
  background: var(--pos-bright); color: #06281C;
  font-size: 10.5px; font-weight: 800; letter-spacing: .02em;
  padding: 2px 7px; border-radius: 999px;
  font-variant-numeric: tabular-nums;
}

/* ---- identity, description, prices ---- */
.pcard-main { padding: 11px 13px 12px; min-width: 0; flex: 1; display: flex; flex-direction: column; }
.pcard-head { display: flex; gap: 12px; align-items: flex-start; justify-content: space-between; }
.pcard-id { min-width: 0; flex: 1; display: flex; flex-direction: column; gap: 2px; }
.pcard-brand {
  font-family: var(--font-display); font-size: 11.5px; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: var(--accent-ink); line-height: 1.2;
}
/* the manufacturer's reference: a code that is often digits, so it stays in
   the body face with tabular figures — never the display face */
.pcard-mpn {
  font-family: var(--font); font-variant-numeric: tabular-nums;
  font-size: 17px; font-weight: 700; color: var(--ink); line-height: 1.25; letter-spacing: -0.01em;
  word-break: break-word;
}
.pcard-name { font-size: 14px; font-weight: 700; color: var(--ink); line-height: 1.3; }
.pcard-chips { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 7px; }

/* ---- the whole description, always on the card ---- */
.pcard-desc { font-size: 11.5px; color: var(--ink2); margin-top: 7px; line-height: 1.45; }
.pcard-specs {
  font-size: 11px; color: var(--ink3); margin-top: 5px;
  font-variant-numeric: tabular-nums;
  flex-direction: row; flex-wrap: wrap; gap: 2px 12px;
}
.pcard-specs.speclines > span + span { color: var(--ink3); }

/* ---- the price column: a mini stats strip ---- */
.pcard-money {
  flex: none; width: 136px; display: flex; flex-direction: column; gap: 6px; text-align: right;
}
.pmoney b {
  display: block; font-variant-numeric: tabular-nums; line-height: 1.15; word-break: break-word;
  font-size: 13px; font-weight: 700; color: var(--ink);
}
.pmoney span {
  display: block; font-size: 10px; font-weight: 600; letter-spacing: .05em; text-transform: uppercase;
  color: var(--ink3); margin-top: 2px;
}
.pmoney.lead b { font-size: 18px; font-weight: 800; color: var(--accent-ink); }
.pmoney.lead.good b { color: var(--pos); }
/* the struck list price beside a promo figure */
.pmoney b s { font-size: 11px; font-weight: 600; color: var(--ink3); margin-left: 4px; text-decoration-thickness: 1.5px; }
.pmoney.box {
  background: var(--surface2); border-radius: var(--radius-xs); padding: 6px 8px;
}
/* the authorised floor: it is a limit, not a price, so it reads as a rule —
   amber, labelled, never mistaken for what to charge */
.pmoney.floor b { color: var(--warn-ink); }
.pmoney.floor span { color: var(--warn-ink); }
.pmoney.dimline b { font-size: 11.5px; font-weight: 600; color: var(--ink3); }

/* ---- who sells it + the actions ---- */
.pcard-foot {
  margin-top: auto; padding-top: 9px; display: flex; align-items: flex-end;
  justify-content: space-between; gap: 8px;
}
.pcard-sup { font-size: 11.5px; font-weight: 700; color: var(--ink2); min-width: 0; }
.pcard-sup .hint { display: block; font-size: 10.5px; font-weight: 400; color: var(--ink3); }
.pcard-acts { display: inline-flex; gap: 6px; flex: none; }
/* two circles: the camera outlined, the "+" filled navy — navy means act */
.pcard-cam, .pcard-add {
  flex: none; width: 30px; height: 30px; border-radius: 50%; cursor: pointer;
  border: 1.5px solid var(--accent-ink); background: transparent; color: var(--accent-ink);
  display: inline-flex; align-items: center; justify-content: center;
  transition: background-color 0.12s, color 0.12s, border-color 0.12s;
}
.pcard-cam svg { width: 15px; height: 15px; }
.pcard-cam:hover { background: var(--accent-soft); }
/* the "+" — the fast path from a price to a quotation */
.pcard-add {
  background: var(--accent); color: var(--btn-ink); border-color: var(--accent);
  font-size: 19px; font-weight: 700; line-height: 1;
}
.pcard-add:hover { background: var(--accent2); border-color: var(--accent2); color: var(--btn-ink); }
.pcard-cam:focus-visible, .pcard-add:focus-visible { outline: 2px solid var(--accent-deep); outline-offset: 2px; }
@media (pointer: coarse) {
  .pcard-cam, .pcard-add { position: relative; }
  .pcard-cam::after, .pcard-add::after { content: ""; position: absolute; inset: -6px; }
}

.pcard-own {
  margin-top: 9px; padding-top: 9px; border-top: 1px dashed var(--border);
  font-size: 11px; color: var(--ink2); display: flex; flex-wrap: wrap; gap: 4px 12px;
}
.pcard-own .clickable { cursor: pointer; }
.pcard-own .clickable:hover { color: var(--accent-ink); }
.pcard-own .neg { color: var(--neg); font-weight: 600; }
.pcard-own .pos { color: var(--pos); font-weight: 600; }
.pcard .offers { margin-top: 9px; }
.pcard .offers s { color: var(--ink3); font-weight: 400; margin-left: 4px; }

/* ---- the same cards as one full-width row each ---- */
.prodgrid.as-list { grid-template-columns: 1fr; gap: 6px; }
.prodgrid.as-list .pcard { border-radius: 10px; }
.prodgrid.as-list .pcard-pic { width: 76px; height: 76px; margin: 9px 0 9px 9px; padding: 4px; }
.prodgrid.as-list .pcard-main { padding: 9px 12px; }
.prodgrid.as-list .pcard-mpn { font-size: 15px; }
.prodgrid.as-list .pmoney.lead b { font-size: 16px; }
/* the description stays: it is the point of having it on the card at all
   (6 sep 2026) — only the padding gets tighter */
.prodgrid.as-list .pcard-desc { margin-top: 5px; }
@media (max-width: 700px) {
  .pcard-pic, .prodgrid.as-list .pcard-pic { width: 72px; height: 72px; }
  /* a pill that says a whole sentence ("2 suppliers · $494.000 - $553.350")
     wraps on a phone instead of running off the side of the card */
  .pcard-chips .pill { white-space: normal; }
  .pcard-money { width: 108px; }
  .pmoney.lead b { font-size: 15px; }
}
/* a very narrow phone: the price column goes under the text rather than
   squeezing both into nothing */
@media (max-width: 460px) {
  .pcard-head { flex-direction: column; gap: 8px; }
  .pcard-money { width: auto; flex-direction: row; flex-wrap: wrap; gap: 10px 16px; text-align: left; }
}

/* ---- the promotion filter, as a switch ---- */
.promo-toggle {
  display: inline-flex; align-items: center; gap: 7px; cursor: pointer;
  border: 1px solid var(--border); background: var(--surface); color: var(--ink2);
  border-radius: 999px; padding: 6px 13px; min-height: 32px; font-size: 12.5px; font-weight: 600;
  font-family: inherit;
}
.promo-toggle .pt-dot {
  width: 12px; height: 12px; border-radius: 50%; background: var(--track); flex: none;
}
.promo-toggle.on {
  border-color: color-mix(in srgb, var(--pos-bright) 60%, var(--border));
  background: var(--pos-soft); color: var(--pos-ink);
}
.promo-toggle.on .pt-dot { background: var(--pos-bright); }
.promo-toggle:focus-visible { outline: 2px solid var(--accent-deep); outline-offset: 2px; }

.promo-teaser {
  display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap;
  border-left: 3px solid var(--pos-bright);
}
.promo-teaser .pt-l { font-size: 13px; color: var(--ink2); display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.promo-teaser .pt-tag {
  background: var(--pos-soft); color: var(--pos-ink); border-radius: 999px;
  padding: 3px 10px; font-size: 11px; font-weight: 800; letter-spacing: .04em; text-transform: uppercase;
}
/* the promo colour where it is text rather than a chip (price-list review) */
.promo-ink { color: var(--pos); }

/* ---- the supplier's own price list ---- */
.cat-thumb.md { width: 64px; height: 64px; border-radius: var(--radius-sm); cursor: zoom-in; }
/* the authorised floor in a table: amber, so it never reads as one more
   price you may charge (6 sep 2026) */
.minprice { color: var(--warn-ink); font-weight: 700; font-variant-numeric: tabular-nums; }

.pltable tr.on-promo td { background: var(--pos-soft); }
.pltable tr.on-promo td:first-child { box-shadow: inset 3px 0 0 var(--pos-bright); }
.pltable td.promo-cell { color: var(--pos-ink); font-weight: 700; }
.pltable td.promo-cell s { color: var(--ink3); font-weight: 400; margin-left: 4px; }
.pltable td.promo-cell .hint { color: var(--ink3); font-weight: 400; }

/* ---- a photo, full size (the lightbox modal; no header strip by design) ---- */
.photobox {
  display: flex; align-items: center; justify-content: center;
  background: #fff; border: 1px solid var(--border); border-radius: 12px;
  padding: 10px; min-height: 200px;
}
.photobox img { max-width: 100%; max-height: 68vh; object-fit: contain; display: block; }

/* ---- the quote basket ---- */
.cartgrid td { vertical-align: middle; }
.cart-name { display: inline-flex; flex-direction: column; gap: 1px; min-width: 0; vertical-align: middle; }
.cart-name .dim { font-size: 11px; }
.cartgrid .cat-thumb { vertical-align: middle; margin-right: 8px; }
.cart-qty { width: 72px; text-align: right; font-variant-numeric: tabular-nums; }
.cart-price { width: 118px; text-align: right; font-variant-numeric: tabular-nums; }
.cart-go {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 14px;
  margin-top: 14px;
}
/* the two destinations (quotation / remisión): boxed choices, navy primary */
.cart-goboxcol {
  background: var(--surface2); border: 1px solid var(--hairline); border-radius: 12px; padding: 14px 14px 13px;
  display: flex; flex-direction: column; gap: 7px;
}
.cart-goh {
  position: relative; font-size: 13.5px; font-weight: 700; color: var(--ink); padding-bottom: 8px;
}
.cart-goh::after {
  content: ""; position: absolute; left: 0; bottom: 0; width: 24px; height: 3px;
  border-radius: 2px; background: var(--underline);
}
.cart-goboxcol .btn { align-self: flex-start; margin-top: 4px; }
/* ==================================================================
   ONE CATALOG PHOTO — paste or file (catalogphoto.js)
   Two previews side by side, because the question being answered is
   "is the new one better than what is there", not "is this an image".
   Navy Ledger (14 sep 2026): the drop zone is the portal's dashed
   accent box; the previews sit on white because they hold a picture.
   ================================================================== */
.cphoto-row { display: flex; gap: 12px; }
.cphoto-slot { flex: 1; min-width: 0; }
.cphoto-cap {
  display: block; font-size: 11px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase;
  color: var(--ink3); margin-bottom: 5px;
}
.cphoto-prev {
  height: 132px; border: 1px solid var(--border); border-radius: 12px; background: #fff;
  display: flex; align-items: center; justify-content: center; overflow: hidden;
}
.cphoto-prev img { max-width: 100%; max-height: 100%; object-fit: contain; display: block; }
.cphoto-none { font-size: 11px; color: var(--ink3); }
.cphoto-drop {
  margin-top: 12px; border: 1.5px dashed color-mix(in srgb, var(--accent) 40%, var(--border));
  border-radius: 12px; padding: 16px 12px; text-align: center; cursor: text; background: var(--surface2);
  display: flex; flex-direction: column; gap: 3px; min-height: 64px; justify-content: center;
  transition: background-color 0.12s, border-color 0.12s;
}
.cphoto-drop b { font-size: 13.5px; font-weight: 700; color: var(--ink); }
.cphoto-drop span { font-size: 12px; color: var(--ink2); }
.cphoto-drop:hover { border-color: var(--accent-deep); }
.cphoto-drop:focus { outline: 2px solid var(--accent-deep); outline-offset: 2px; }
.cphoto-drop.over { border-style: solid; border-color: var(--accent-deep); background: var(--accent-soft); }
.cphoto-drop.over b { color: var(--accent-ink); }
.cphoto-drop.has { border-style: solid; border-color: var(--pos-bright); background: var(--pos-soft); }
.cphoto-drop.has b { color: var(--pos-ink); }
@media (max-width: 460px) {
  .cphoto-row { flex-direction: column; }
  .cphoto-prev { height: 112px; }
}

/* ============ REMISIÓN SETTLEMENT + RECIBOS DE CAJA (9 sep 2026) ============
   The settle form is a small grid (which lines, how many, at what price) and
   the Receipts page is an ordinary table — almost everything reuses the
   existing .bulkgrid / .filters / .pill vocabulary. These are the few bits
   that had no equivalent yet. */

/* the kind picker (Sold / Returned / Kept) fills the modal instead of
   sitting in a filter row, so the three options read as a real choice
   (the navy chosen segment comes from the base .segctl rule) */
.segctl.wide { display: flex; width: 100%; margin: 10px 0 0; }
.segctl.wide button { flex: 1; min-height: 36px; }

/* the inventory code under an item's name inside the settle grid */
.st-code { margin-top: 4px; }
.st-code input { width: 100%; font-size: 12px; height: 32px; border-radius: var(--radius-xs); }

/* "3 out on R-1001" under an item in the Inventory table. Deliberately a
   note and not a badge: those units are still counted in the stock figure
   next to it, and the wording has to carry that. */
.outnote {
  margin-top: 3px; font-size: 11px; color: var(--ink3);
  display: inline-flex; align-items: center; gap: 4px;
}

/* a cancelled receipt: kept in the table, struck through in the quiet ink,
   the same treatment a voided inventory movement gets */
#app tr.void td { color: var(--ink3); text-decoration: line-through; }
#app tr.void td .amt, #app tr.void td b { color: var(--ink3); }
#app tr.void td .pill { text-decoration: none; }
#app tr.void td.acts { text-decoration: none; }

/* =====================================================================
   Navy Ledger v4 — adversarial review fixes (15 sep 2026)
   Kept together at the end so they win the cascade over the sections they
   correct; each line names the finding it closes.
   ===================================================================== */

/* B-css-4: the 26px row actions of the phone movements table get a 40px-tall
   hit area; horizontal inset stays 0 so edit and delete never overlap */
@media (pointer: coarse) {
  #mov-table td.acts .ract-btn::after { inset: -7px 0; border-radius: 8px; }
  .tag-x::after { content: ""; position: absolute; inset: -6px; }                 /* B-css-5 */
  .sb-item, .sb-foot .sb-item { min-height: 40px; }                                /* D-live-7: fingers keep 40px */
  #sf-perms input[type="checkbox"] { width: 22px; height: 22px; }                  /* D-live-9 */
}

/* A-spec-10: the 10-column debts table fits its card at 1280 */
#debts-table th, #debts-table td { padding-left: 8px; padding-right: 8px; }
#debts-table td.acts .ract { gap: 2px; }

/* A-spec-7: two-word page titles stay on one line in the phone topbar */
@media (max-width: 620px) {
  .tb { gap: 8px; }
  .tb-head { flex: 1 1 auto; }
  .tb-title { font-size: 16px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .tb-tools { gap: 4px; }
}

/* D-live-3: while a write is in flight on a phone the status takes the
   title's place instead of being painted over it */
@media (max-width: 760px) {
  .tb:has(> .tb-status:not(:empty)) .tb-head { display: none; }
  .tb-status:not(:empty) { justify-content: flex-start; overflow: hidden; }
  .tb-stat-t { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
}

/* D-live-2: topbar popovers (alerts, session) span the phone screen
   instead of hanging off its left edge */
@media (max-width: 760px) {
  .tb-tools .pop {
    position: fixed; left: 8px; right: 8px; top: calc(var(--tb-h) + 6px);
    width: auto; min-width: 0; max-width: none;
    max-height: calc(100dvh - var(--tb-h) - 96px); overflow-y: auto;
  }
}

/* A-spec-6/D-live-8: a Daily savings row wraps its button to a second line
   so "Profit $30.084" (what the 10% comes from) stays readable */
@media (max-width: 520px) {
  .ob-row.sav { flex-wrap: wrap; row-gap: 6px; }
  .ob-row.sav .btn { flex: 1 0 100%; }
}

/* D-live-5: account balances rows — name + amount on line one, buttons below */
@media (max-width: 640px) {
  .bal-row { flex-wrap: wrap; row-gap: 6px; }
  .bal-name { flex: 1 1 55%; }
  .bal-acts { flex: 1 0 100%; justify-content: flex-end; }
}

/* D-live-6: payment-method mapping — each slot stacks label over a full-width select on phones */
@media (max-width: 640px) {
  .pa-table, .pa-table tbody, .pa-table tr, .pa-table td { display: block; width: auto; }
  .pa-table td:not(.nm) { width: auto; white-space: normal; } /* beats .paytable td:not(.nm) { width: 1% } */
  .pa-table thead { display: none; }
  .pa-table td { padding-left: 4px; padding-right: 4px; }
  .pa-table td.nm { border-bottom: 0; padding-bottom: 2px; font-weight: 600; }
}

/* D-live-10: the "+ Add" menu fits without an inner scroll — two columns
   beside the desktop button, hint lines dropped under the phone FAB */
@media (min-width: 1001px) {
  .pop.addpop { display: grid; grid-template-columns: 1fr 1fr; column-gap: 4px; min-width: 540px; max-width: calc(100vw - 24px); }
  .pop.addpop > .pop-h { grid-column: 1 / -1; }
}
@media (max-width: 760px) {
  .pop.addpop .pi-h { display: none; }
  .fab-wrap .pop.addpop {
    display: grid; grid-template-columns: 1fr 1fr; column-gap: 2px;
    width: min(380px, calc(100vw - 28px)); min-width: 0; max-width: none;
  }
  .fab-wrap .pop.addpop > .pop-h { grid-column: 1 / -1; }
  .fab-wrap .pop.addpop .pop-item { gap: 8px; padding: 6px 8px; }
}

/* ---- WISHLIST (17 sep 2026) ----
   New selectors only — nothing above this block is touched. Reuses the
   existing .paytable/.pg-head grouped-table language (Fixed & subs, ver
   "STATUS-GROUPED TABLES" above), .card.tile, .pill, .chip, .segbar,
   .tablebox, .ract/.ract-btn and .inact-toggle wholesale; the pieces below
   are only what those didn't already cover: the list switcher chips, the
   small muted "Store · link" line under an item name, and the late-plan
   warning color. All tokens are existing --var()s from the header. */

.wl-switch { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin: 16px 0 12px; } /* clear of the tiles that float over the band */
.wl-chip {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 7px 13px; border-radius: 999px; border: 1px solid var(--border);
  background: var(--surface); color: var(--ink2); font-size: 13px; font-weight: 600;
  cursor: pointer; transition: background 0.15s, color 0.15s, border-color 0.15s;
}
.wl-chip:hover { border-color: var(--accent-deep); color: var(--ink); }
.wl-chip.on { background: var(--accent); color: var(--btn-ink); border-color: var(--accent); }
.wl-chip em { font-style: normal; opacity: 0.85; font-size: 11px; margin-left: 1px; }
.wl-chip-new { border-style: dashed; color: var(--ink3); }
.wl-chip:focus-visible { outline: 2px solid var(--accent-deep); outline-offset: 2px; }
.wl-switch-acts { display: flex; gap: 8px; margin: 0 0 16px; }
.wl-switch-acts .btn.danger { color: var(--neg); }
.wl-switch-acts .btn.danger:hover { background: var(--neg-soft); }

.wl-meta { font-size: 11.5px; color: var(--ink3); margin-top: 2px; }
.wl-meta a { color: var(--accent-ink); text-decoration: none; }
.wl-meta a:hover { text-decoration: underline; }

/* generic, scoped-to-this-page dim/late text (the codebase's own .dim is
   only ever defined in combination with another selector, e.g. td.dim —
   these avoid reusing that bare class name anywhere it could leak). */
.wl-dim { color: var(--ink3); }
.wl-late { color: var(--neg); font-weight: 600; }

.wl-iconpick { display: flex; flex-wrap: wrap; gap: 6px; margin: -6px 0 14px; }
.wl-icon-btn {
  width: 34px; height: 34px; display: inline-flex; align-items: center; justify-content: center;
  font-size: 17px; line-height: 1; border-radius: 10px; border: 1px solid var(--border);
  background: var(--surface2); cursor: pointer;
}
.wl-icon-btn:hover { border-color: var(--accent-deep); background: var(--accent-soft); }
.wl-icon-btn:focus-visible { outline: 2px solid var(--accent-deep); outline-offset: 1px; }

@media (pointer: coarse) {
  .wl-chip { min-height: 36px; }
  .wl-icon-btn { width: 38px; height: 38px; }
}

/* wishlist on a phone: the Plan/Status column folds under the item name, and
   the three row actions stack so nothing hides behind a sideways scroll */
.wl-plan-m { display: none; }
@media (max-width: 700px) {
  .wl-plan-m { display: block; margin-top: 4px; white-space: normal; font-size: 12.5px; }
  .card .paytable td.nm { white-space: normal; }
  #wl-items-table .ract, .wl-bought .ract { flex-direction: column; gap: 4px; }
}

/* =====================================================================
   Catalog enrichment (catalogenrich.js) — matching a Supplier_Catalog /
   Inventory row against Audiocity's own store and reviewing the join
   before anything is copied. Reuses .tiles/.axis-note/.fnote/.cbline/
   .field/.frow/.pl-h3/.pl-tabletools/.pl-pager/.bulkgrid-wrap/.pl-crop/
   .pl-thumbcell/.chip wholesale (same vocabulary pricelistocr.js already
   built for a near-identical review-by-exception screen); only what is
   genuinely new to this screen — the current->proposed comparison and the
   candidate radio picker for an ambiguous match — gets its own rules here.
   ===================================================================== */
.enr-card {
  border: 1px solid var(--border); border-radius: 10px; padding: 12px 14px;
  margin-bottom: 10px; background: var(--surface);
}
.enr-card-head { display: flex; flex-direction: column; gap: 2px; margin-bottom: 8px; }
/* every .dim this screen prints outside a plain td.dim (already global):
   the description line under a brand/model, "no change", "no photo", the
   candidate's vendor/sku line — one rule for the three containers they
   ever land in, so none of them is silently left un-dimmed */
.enr-card .dim, #enr-body .dim, #enr-none-body .dim { color: var(--ink3); font-size: 12px; }

.enr-cands { display: flex; flex-direction: column; gap: 2px; }
.enr-cand {
  display: flex; align-items: center; gap: 10px; padding: 6px 8px;
  border-radius: 8px; cursor: pointer;
}
.enr-cand:hover { background: var(--surface2); }
.enr-cand input { width: auto; margin: 0; flex: none; accent-color: var(--accent); }
.enr-cand-t { display: flex; flex-direction: column; gap: 1px; font-size: 12.5px; min-width: 0; }
.enr-cand-t b { overflow-wrap: anywhere; }
.enr-cand.skip .enr-cand-t b { color: var(--ink3); font-weight: 600; }
.enr-cand.solo { cursor: default; }

.enr-link { font-size: 11.5px; color: var(--accent-ink); text-decoration: none; white-space: nowrap; }
.enr-link:hover { text-decoration: underline; text-underline-offset: 2px; }

/* current -> proposed, wherever a row is open for real inspection */
.enr-diff {
  margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--hairline);
  display: flex; flex-direction: column; gap: 6px;
}
.enr-cmp { display: flex; align-items: center; gap: 8px; font-size: 12.5px; flex-wrap: wrap; }
.enr-cmp-lb {
  min-width: 76px; font-weight: 700; color: var(--ink2); font-size: 10.5px;
  text-transform: uppercase; letter-spacing: 0.04em;
}
.enr-cmp-old { color: var(--ink3); text-decoration: line-through; }
.enr-cmp-arrow { color: var(--ink3); }
.enr-cmp-new { color: var(--accent-ink); font-weight: 700; }

/* the dense "exact matches" table: click a row to expand it in place */
#enr-body tr.enr-row { cursor: pointer; }
#enr-body tr.enr-row:hover td { background: var(--surface2); }
#enr-body tr.enr-row.off td { color: var(--ink3); }
#enr-body tr.enr-row.off .chip { opacity: 0.7; }
#enr-body tr.enr-row.expanded { cursor: default; }
#enr-body tr.enr-row.expanded td { background: var(--surface2); }

.enr-none { margin-top: 14px; }
.enr-none summary { cursor: pointer; font-weight: 700; font-size: 13px; color: var(--ink2); padding: 4px 0; }
.enr-none summary:hover { color: var(--accent-ink); }
.enr-none[open] summary { margin-bottom: 8px; }

/* the hand-classification selects in "No match in the store" — .bulkgrid
   otherwise only styles <input>, and a native <select> needs a real border
   (unlike the cell's borderless-until-focus text inputs) to read as an
   actual control rather than dead text sitting in a table cell */
.bulkgrid select {
  width: 100%; background: var(--surface2); border: 1px solid var(--border); border-radius: 8px;
  padding: 5px 6px; font-size: 12.5px; height: 32px; color: var(--ink);
}
.bulkgrid select:focus {
  outline: none; border-color: var(--accent-deep);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent-deep) 20%, transparent);
}
