/* -- Tokens ---------------------------------------------------------------
   Two themes, and the page no longer hard-stamps either: it was pinned to
   data-theme="dark" with a #34d17e accent, which is why it read as a terminal
   rather than a ledger.

   The rule that fixes it is not "less dark". It is that the accent and the
   money colours are different families. They used to be the same green, so
   every selected tab, pressed chip and active link glowed money-green -- and
   that single collision is most of what made it feel like a console. Now the
   accent is a navy, and green means exactly one thing: money coming in.

   Light warms its neutrals a few degrees toward paper; a blue-grey grey reads
   technical whatever sits on top of it. Dark keeps a slight blue cast so the
   navy accent has somewhere to sit.                                          */
:root {
  --bg:#f7f6f3; --surface:#ffffff; --raised:#f0eee9; --muted:#e5e2db;
  --fg:#1c1b18; --fg-2:#4a4842; --fg-3:#6c6960;
  /* --line is a row rule, decorative, so it may stay faint. --line-2 draws the
     edge of a button or an input, which WCAG treats as a UI component at 3:1 --
     and it is the cue that tells someone what is clickable. #c5c0b4 managed 1.68
     and looked washed out for exactly that reason. */
  --line:#ded9d0; --line-2:#8b8475;

  /* UI accent. Institutional, desaturated, never used for a figure. */
  --accent:#2e4b7a; --accent-fg:#ffffff; --accent-soft:#e8ecf4;

  /* Money. Semantic only: these two never style a control. */
  --credit:#256b4d; --credit-soft:#e4efe9;
  --debit:#97362b;

  --warn:#835710; --warn-soft:#f8efdc;
  --danger:#97362b; --danger-soft:#f7e7e4;

  /* Tinted to the ground rather than pure black, so edges read as paper. */
  --shadow:0 1px 2px rgba(40,34,24,.05), 0 10px 28px -14px rgba(40,34,24,.16);
  --scrim:rgba(46,39,28,.42);
  --r:6px;
  --s1:4px; --s2:8px; --s3:12px; --s4:16px; --s5:20px; --s6:28px;
  --mono:"IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  --sans:"Source Sans 3", system-ui, -apple-system, "Segoe UI", sans-serif;
  --serif:"Source Serif 4", Georgia, "Times New Roman", serif;
  --ease:cubic-bezier(.2,0,0,1);
  --rail:0px;                 /* detail pane width, set by JS */
}

/* Dark, for whoever wants it. Only the tokens are redefined -- every component
   styles through them, so a colour can never be defined for one theme only.
   System preference decides when nothing is stamped; an explicit choice wins in
   both directions. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
  --bg:#14161b; --surface:#1b1e25; --raised:#22262f; --muted:#2c313c;
  --fg:#e8eaee; --fg-2:#b4bac6; --fg-3:#8b929f;
  --line:#2a2f39; --line-2:#6f7889;

  /* Same discipline as light: navy family for controls, never for a figure. */
  --accent:#84a6d8; --accent-fg:#0e1620; --accent-soft:#1e2734;

  /* Muted sage, not terminal green. This is the colour that made the old
     interface read as a console -- #34d17e at full chroma on near-black. */
  --credit:#7cc0a0; --credit-soft:#17251f;
  --debit:#e39a8d;

  --warn:#d8ab60; --warn-soft:#2a2114;
  --danger:#e39a8d; --danger-soft:#2b1a17;

  --shadow:0 1px 2px rgba(0,0,0,.45), 0 12px 32px -16px rgba(0,0,0,.7);
  --scrim:rgba(0,0,0,.62);
  }
}
:root[data-theme="dark"] {
  --bg:#14161b; --surface:#1b1e25; --raised:#22262f; --muted:#2c313c;
  --fg:#e8eaee; --fg-2:#b4bac6; --fg-3:#8b929f;
  --line:#2a2f39; --line-2:#6f7889;

  /* Same discipline as light: navy family for controls, never for a figure. */
  --accent:#84a6d8; --accent-fg:#0e1620; --accent-soft:#1e2734;

  /* Muted sage, not terminal green. This is the colour that made the old
     interface read as a console -- #34d17e at full chroma on near-black. */
  --credit:#7cc0a0; --credit-soft:#17251f;
  --debit:#e39a8d;

  --warn:#d8ab60; --warn-soft:#2a2114;
  --danger:#e39a8d; --danger-soft:#2b1a17;

  --shadow:0 1px 2px rgba(0,0,0,.45), 0 12px 32px -16px rgba(0,0,0,.7);
  --scrim:rgba(0,0,0,.62);
}

*,*::before,*::after { box-sizing:border-box; }
[hidden] { display:none !important; }
html,body { height:100%; }
body {
  margin:0; background:var(--bg); color:var(--fg);
  font:400 15px/1.45 var(--sans);
  -webkit-font-smoothing:antialiased;
}
.mono { font-family:var(--mono); font-variant-numeric:tabular-nums; }
h1,h2,h3 { margin:0; }

/* ── App shell: grid, never overlap, fill the viewport ─────────────────── */
.app {
  min-height:100dvh; height:100dvh;
  display:grid;
  grid-template-rows:auto auto auto minmax(0,1fr) auto;
  grid-template-areas:"head" "banner" "src" "body" "foot";
}

/* ── Header ────────────────────────────────────────────────────────────── */
.head {
  grid-area:head;
  display:flex; align-items:center; gap:var(--s4);
  padding:0 var(--s5); height:60px;
  background:var(--surface); border-bottom:1px solid var(--line);
}
.brand { display:flex; align-items:baseline; gap:var(--s2); }
.brand h1 { font:600 20px/1.1 var(--serif); letter-spacing:-.015em; }
.brand .book {
  font:500 12px/1 var(--sans); text-transform:uppercase; letter-spacing:.08em;
  color:var(--fg-3); padding:4px 7px; border:1px solid var(--line-2); border-radius:var(--r);
}
.head .sep { width:1px; height:22px; background:var(--line); flex:none; }
.head { flex-wrap:nowrap; }
.runsel { min-width:0; flex:0 1 auto; text-overflow:ellipsis; }
.brand { flex:none; }
.runsel {
  font:400 13px/1 var(--mono); background:var(--raised); color:var(--fg-2);
  border:1px solid var(--line); border-radius:var(--r); padding:7px var(--s2); height:32px;
}
.head-right { margin-left:auto; display:flex; align-items:center; gap:var(--s3); }

/* Tie-out: a safety signal, so it reads as a status, not a label. */
.tieout {
  display:flex; align-items:center; gap:var(--s2);
  font:500 13px/1 var(--sans); padding:7px var(--s3); border-radius:var(--r);
  border:1px solid transparent;
}
.tieout .dot { width:7px; height:7px; border-radius:50%; flex:none; }
.tieout.ok   { color:var(--credit); background:var(--credit-soft); border-color:var(--credit); }
.tieout.ok .dot { background:var(--credit); }
.tieout.fail { color:var(--danger); background:var(--danger-soft); border-color:var(--danger); font-weight:600; }
.tieout.fail .dot { background:var(--danger); }

.btn {
  font:500 13px/1 var(--sans); cursor:pointer; white-space:nowrap;
  background:var(--raised); color:var(--fg); border:1px solid var(--line-2);
  border-radius:var(--r); padding:0 var(--s4); height:34px;
  transition:background 140ms var(--ease), border-color 140ms var(--ease), transform 80ms var(--ease);
}
.btn:hover:not(:disabled) { background:var(--muted); border-color:var(--fg-3); }
.btn:active:not(:disabled) { transform:translateY(1px); }
.btn-primary { background:var(--accent); color:var(--accent-fg); border-color:var(--accent); font-weight:600; }
.btn-primary:hover:not(:disabled) { background:var(--accent); filter:brightness(1.08); }
.btn:disabled { opacity:.42; cursor:not-allowed; }
.btn-ghost { background:transparent; }

/* ── Task banner: says what to do, replaces a silently disabled button ─── */
.banner {
  grid-area:banner;
  display:flex; align-items:center; gap:var(--s3);
  padding:var(--s4) var(--s5);
  border-bottom:1px solid var(--line);
  font-size:15px;
}
.banner[hidden] { display:none; }
.banner.todo { background:var(--warn-soft); color:var(--warn); border-bottom-color:var(--warn); }
.banner.done { background:var(--accent-soft); color:var(--fg); border-bottom-color:var(--accent); }
.banner svg { flex:none; }
.banner b { font-weight:600; }
.banner .hint { color:inherit; opacity:.8; }

/* ── Body: two independent panes. Nothing is ever covered. ─────────────── */
.body {
  grid-area:body; min-height:0;
  display:grid; grid-template-columns:minmax(0,1fr) var(--rail);
  transition:grid-template-columns 180ms var(--ease);
}
.queue { min-width:0; min-height:0; overflow:hidden; display:grid;
  /* tabs, anomalies, bulk bar, filters, then the scroller takes what is left.
     Each of the middle three hides itself when it has nothing to say. */
  grid-template-rows:auto auto auto auto minmax(0,1fr);
  border-right:1px solid var(--line); }

/* ── Tabs ──────────────────────────────────────────────────────────────── */
.tabs {
  display:flex; gap:var(--s2); padding:0 var(--s5);
  background:var(--surface); border-bottom:1px solid var(--line);
}
.tabs button {
  display:flex; align-items:center; gap:var(--s2);
  font:500 14px/1 var(--sans); cursor:pointer; background:none; color:var(--fg-3);
  border:0; border-bottom:2px solid transparent; padding:0 var(--s3); height:46px;
  transition:color 140ms var(--ease);
}
.tabs button:hover { color:var(--fg-2); }
.tabs button[aria-selected="true"] { color:var(--fg); border-bottom-color:var(--accent); }
.tabs .n {
  font:600 12px/1 var(--mono); padding:3px 6px; border-radius:99px;
  background:var(--muted); color:var(--fg-2); min-width:20px; text-align:center;
}
.tabs button[aria-selected="true"] .n { background:var(--accent-soft); color:var(--accent); }
.tabs button[data-status="blocked"] .n.hot { background:var(--danger-soft); color:var(--danger); }

/* ── Table: the scroll container is the pane, so sticky needs no offset ── */
.scroll { overflow:auto; min-height:0; }
table { width:100%; table-layout:fixed; border-collapse:separate; border-spacing:0; }
thead th {
  position:sticky; top:0; z-index:2;
  background:var(--raised); color:var(--fg-3);
  font:600 11px/1 var(--sans); text-transform:uppercase; letter-spacing:.07em;
  text-align:left; padding:var(--s3) var(--s4); white-space:nowrap;
  border-bottom:1px solid var(--line);
}
tbody td {
  padding:var(--s3) var(--s4); border-bottom:1px solid var(--line);
  vertical-align:middle; background:var(--bg);
}
tbody tr { cursor:pointer; }
/* Tracking a date across to its ledger seven columns away is exactly where an
   eye slips. The tint is barely there on purpose -- enough to anchor a row,
   not enough to read as a highlight. */
tbody tr:nth-child(even) td { background:var(--raised); }
tbody tr:hover td { background:var(--raised); }
tbody tr[aria-selected="true"] td { background:var(--muted); }
/* Selection reads as a left rail, not an outline box -- calmer at density. */
tbody tr[aria-selected="true"] td:first-child { box-shadow:inset 3px 0 0 var(--accent); }

.c-date  { width:96px; }
.c-party { width:auto; }
.c-narr  { width:32%; }
.c-amt   { width:124px; text-align:right; }
.c-status{ width:132px; }
.c-led   { width:200px; }
th.c-amt { text-align:right; }

/* Split mode: the detail pane already shows narration and the target ledger
   in full, so drop those columns and fold the ledger into the party cell.
   Six columns do not fit beside a 400px rail; four do, comfortably. */
.body.split .c-narr,
.body.split .c-led { display:none; }
.posts { display:none; }
.body.split .chan  { display:none; }
.body.split .posts { display:block; }

td.c-date { font:400 13px/1.3 var(--mono); color:var(--fg-2); white-space:nowrap; }
td.c-party { font-weight:500; }
td.c-party .chan {
  display:block; font:400 12px/1.3 var(--mono); color:var(--fg-3); margin-top:2px;
}
/* This shows what Tally will display, which for most rows is still the bank's
   own string -- `UPI-UDAL-8815696881@HDFC-...` has no readable form, which is why
   rewriting it is a feature. So the column's real job is to show WHICH rows will
   read sensibly in a Day Book: a rewritten one is set in the body face at full
   contrast, a raw one is greyed and monospaced so it reads as machine output.
   Clipped at two lines rather than one, and the original is on hover either way. */
td.c-narr {
  font:400 12px/1.45 var(--mono); color:var(--fg-3);
  max-width:0; overflow:hidden;
}
td.c-narr.written { font:400 14px/1.4 var(--sans); color:var(--fg); }
td.c-narr .line {
  display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical;
  overflow:hidden; overflow-wrap:anywhere;
}
td.c-amt {
  text-align:right; font:600 15px/1.3 var(--mono); font-variant-numeric:tabular-nums;
  white-space:nowrap; letter-spacing:-.01em;
}
td.c-led { font-size:13px; color:var(--fg-2); }
td.c-led .none { color:var(--fg-3); font-style:italic; }
/* A debit is the ordinary case, so it takes the ordinary colour: an amount
   column of red reads as a column of errors. The minus sign carries the
   direction, and green marks the rarer thing worth spotting. --debit is kept
   for the detail pane, where a single figure can afford the emphasis. */
.dr { color:var(--fg); } .cr { color:var(--credit); }
.d-amt.dr { color:var(--debit); }

/* Status: shape + text + colour, never colour alone. */
.status { display:inline-flex; align-items:center; gap:6px; font:500 12px/1 var(--sans); white-space:nowrap; }
.status .dot { width:8px; height:8px; border-radius:50%; flex:none; }
.status.blocked  { color:var(--danger); } .status.blocked .dot  { background:var(--danger); }
.status.suspense { color:var(--warn); }   .status.suspense .dot { background:var(--warn);
  border-radius:2px; transform:rotate(45deg); }
.status.resolved { color:var(--fg-3); }   .status.resolved .dot { background:var(--credit); }
/* A transfer pair: recorded, but by the other row. A hollow dot reads as
   "accounted for elsewhere" rather than "done here".                        */
.status.paired { color:var(--fg-3); }
.status.paired .dot { background:transparent; border:2px solid var(--fg-3); }
.vtype { display:block; font:400 11px/1.3 var(--mono); color:var(--fg-3); margin-top:3px; margin-left:14px; }

/* ── Empty state: composed, not "no data" ──────────────────────────────── */
.empty { display:grid; place-content:center; justify-items:center; gap:var(--s4);
  padding:var(--s6) var(--s5); text-align:center; height:100%; }
.empty[hidden] { display:none; }
.empty svg { color:var(--line-2); }
.empty h3 { font:600 18px/1.25 var(--serif); }
.empty p { margin:0; max-width:44ch; color:var(--fg-3); font-size:15px; line-height:1.65;
  text-wrap:pretty; }

/* ── Detail pane: a grid column, so it displaces rather than covers ────── */
.detail {
  min-width:0; min-height:0; overflow:hidden; background:var(--surface);
  display:grid; grid-template-rows:minmax(0,1fr) auto;
}
.detail[hidden] { display:none; }
.d-scroll {
  overflow-y:auto; min-height:0;
  display:flex; flex-direction:column; gap:var(--s5); padding:var(--s5);
}
.d-kicker {
  font:600 11px/1 var(--sans); text-transform:uppercase; letter-spacing:.1em;
  color:var(--fg-3);
}
.d-head { display:flex; justify-content:space-between; align-items:flex-start; gap:var(--s3); }
.d-party { font:600 19px/1.25 var(--serif); letter-spacing:-.01em;
  overflow-wrap:anywhere; text-wrap:balance; }
.d-amt { font:600 22px/1.2 var(--mono); font-variant-numeric:tabular-nums; white-space:nowrap; }
.d-amt small { display:block; font:400 11px/1.4 var(--sans); color:var(--fg-3);
  text-align:right; letter-spacing:.06em; text-transform:uppercase; margin-top:3px; }

.facts { display:grid; grid-template-columns:auto 1fr; gap:var(--s2) var(--s3); font-size:13px; }
.facts dt { color:var(--fg-3); white-space:nowrap; }
.facts dd { margin:0; color:var(--fg-2); font-family:var(--mono); font-size:12px; overflow-wrap:anywhere; }

.why {
  display:flex; gap:var(--s2); padding:var(--s3);
  background:var(--warn-soft); border-left:3px solid var(--warn);
  border-radius:0 var(--r) var(--r) 0; font-size:13px; line-height:1.5; color:var(--warn);
}
.why svg { flex:none; margin-top:1px; }

.field { display:flex; flex-direction:column; gap:var(--s2); }
.field > .label { font:600 12px/1 var(--sans); color:var(--fg-2); }
.field > .label .sub { font-weight:400; color:var(--fg-3); }

.chips { display:flex; flex-direction:column; gap:var(--s2); }
.chip {
  display:flex; align-items:center; gap:var(--s3); width:100%; text-align:left;
  font:500 13px/1.3 var(--sans); cursor:pointer; min-height:40px;
  background:var(--raised); color:var(--fg);
  border:1px solid var(--line-2); border-radius:var(--r); padding:var(--s2) var(--s3);
  transition:border-color 140ms var(--ease), background 140ms var(--ease);
}
.chip:hover { border-color:var(--fg-3); }
.chip[aria-pressed="true"] { border-color:var(--accent); background:var(--accent-soft); color:var(--accent); }
.chip .k {
  font:600 11px/1 var(--mono); flex:none; width:18px; height:18px;
  display:grid; place-items:center; border-radius:3px;
  background:var(--muted); color:var(--fg-3); border:1px solid var(--line);
}
.chip[aria-pressed="true"] .k { background:var(--accent); color:var(--accent-fg); border-color:var(--accent); }
.chip .grp { margin-left:auto; font:400 11px/1 var(--mono); color:var(--fg-3); }

input[type="search"] {
  font:400 13px/1 var(--sans); background:var(--raised); color:var(--fg);
  border:1px solid var(--line-2); border-radius:var(--r); padding:0 var(--s3); height:36px; width:100%;
}
input[type="search"]::placeholder { color:var(--fg-3); }

.results { list-style:none; margin:0; padding:0; max-height:180px; overflow-y:auto;
  border:1px solid var(--line); border-radius:var(--r); background:var(--bg); }
.results:empty { display:none; }
.results li {
  display:flex; align-items:center; gap:var(--s3); min-height:36px;
  padding:var(--s2) var(--s3); cursor:pointer; border-bottom:1px solid var(--line); font-size:13px;
}
.results li:last-child { border-bottom:0; }
.results li:hover { background:var(--raised); }
.results li[aria-selected="true"] { background:var(--accent-soft); color:var(--accent); }
.results .grp { margin-left:auto; font:400 11px/1 var(--mono); color:var(--fg-3); }

.check { display:flex; align-items:center; gap:var(--s2); font-size:13px;
  min-height:40px; padding:0 var(--s3); background:var(--raised);
  border:1px solid var(--line); border-radius:var(--r); cursor:pointer; }
.check input { width:16px; height:16px; accent-color:var(--accent); cursor:pointer; }
.check[hidden] { display:none; }

.d-actions {
  display:flex; gap:var(--s2);
  padding:var(--s4) var(--s5); background:var(--surface);
  border-top:1px solid var(--line);
}
.d-actions .btn { flex:1; height:40px; }

/* ── Footer ────────────────────────────────────────────────────────────── */
.foot {
  grid-area:foot; display:flex; align-items:center; gap:var(--s4); flex-wrap:wrap;
  padding:0 var(--s5); height:44px;
  background:var(--surface); border-top:1px solid var(--line);
  font-size:12px; color:var(--fg-3);
}
.foot .k { display:inline-flex; align-items:center; gap:5px; }
kbd {
  font:600 11px/1 var(--mono); background:var(--raised); color:var(--fg-2);
  border:1px solid var(--line-2); border-bottom-width:2px; border-radius:3px; padding:3px 5px;
}

.skip { position:absolute; left:-9999px; }
.skip:focus { left:var(--s2); top:var(--s2); z-index:20; background:var(--surface);
  padding:var(--s2); border:1px solid var(--accent); border-radius:var(--r); }
:focus-visible { outline:2px solid var(--accent); outline-offset:2px; }

@media (prefers-reduced-motion: reduce) { *,*::before,*::after {
  transition:none !important; animation:none !important; } }

/* Mobile: single column, detail becomes a full-width sheet on its own row. */
@media (max-width:900px) {
  .body { grid-template-columns:minmax(0,1fr); }
  .detail { position:fixed; inset:52px 0 38px 0; z-index:12; box-shadow:var(--shadow); }
  .c-narr, .c-led { display:none; }
  .head { gap:var(--s2); padding:0 var(--s3); }
  .brand .book, .runsel { display:none; }
  .btn { height:44px; }              /* touch target minimum */
  .tabs button { height:44px; }
}
/* ── Book switcher ─────────────────────────────────────────────────────── */
.bookpick {
  font:600 13px/1 var(--sans); background:var(--raised); color:var(--fg);
  border:1px solid var(--line-2); border-radius:var(--r); padding:0 var(--s2); height:32px;
}

/* ── Statement bar: the source leg, always on screen ───────────────────── */
.srcbar {
  grid-area:src; display:flex; align-items:center; gap:var(--s3); flex-wrap:wrap;
  padding:var(--s3) var(--s5); background:var(--raised);
  border-bottom:1px solid var(--line); font-size:13px;
}
.srcbar .leg { display:flex; align-items:center; gap:var(--s2); }
.srcbar .lbl {
  font:600 11px/1 var(--sans); text-transform:uppercase; letter-spacing:.08em; color:var(--fg-3);
}
.srcbar .val { font:500 13px/1 var(--mono); color:var(--fg); }
.srcbar .arrow { color:var(--fg-3); }
.srcbar .meta { margin-left:auto; display:flex; gap:var(--s3); color:var(--fg-3);
  font-family:var(--mono); font-size:12px; }
.linkbtn { font:500 12px/1 var(--sans); background:none; border:0; cursor:pointer;
  color:var(--accent); text-decoration:underline; text-underline-offset:2px; padding:4px; }

/* ── Allocation (split) editor ─────────────────────────────────────────── */
.allocs { display:flex; flex-direction:column; gap:var(--s2); }
.alloc {
  display:flex; align-items:center; gap:var(--s2);
  background:var(--raised); border:1px solid var(--line); border-radius:var(--r);
  padding:var(--s2); min-height:40px;
}
.alloc .name { flex:1; min-width:0; font-size:13px; overflow:hidden;
  text-overflow:ellipsis; white-space:nowrap; }
.alloc input.amt {
  width:96px; text-align:right; font:500 13px/1 var(--mono);
  background:var(--bg); color:var(--fg); border:1px solid var(--line-2);
  border-radius:var(--r); height:30px; padding:0 var(--s2);
}
.alloc .rm { font:500 16px/1 var(--sans); background:none; border:0; cursor:pointer;
  color:var(--fg-3); width:28px; height:28px; border-radius:var(--r); }
.alloc .rm:hover { color:var(--danger); background:var(--muted); }
.remainder {
  display:flex; justify-content:space-between; align-items:center;
  font:500 13px/1 var(--sans); padding:var(--s2) var(--s3); border-radius:var(--r);
}
.remainder .n { font-family:var(--mono); }
.remainder.zero { color:var(--credit); background:var(--credit-soft); }
.remainder.open { color:var(--warn); background:var(--warn-soft); }
.tmplrow { display:flex; align-items:center; gap:var(--s2); flex-wrap:wrap; }
.tag {
  display:inline-flex; align-items:center; gap:6px;
  font:500 11px/1 var(--sans); text-transform:uppercase; letter-spacing:.06em;
  padding:4px 7px; border-radius:99px; border:1px solid var(--line-2); color:var(--fg-3);
}
.tag.auto { color:var(--accent); border-color:var(--accent); background:var(--accent-soft); }

/* ── Upload / identify screen ──────────────────────────────────────────── */
.sheet { position:fixed; inset:0; z-index:30; display:grid; place-items:center;
  background:var(--scrim); padding:var(--s4); }
.sheet[hidden] { display:none; }
.card {
  width:min(640px,100%); max-height:90dvh; overflow:auto;
  background:var(--surface); border:1px solid var(--line-2);
  border-radius:10px; box-shadow:var(--shadow);
  display:flex; flex-direction:column; gap:var(--s4); padding:var(--s5);
}
.card h2 { font:600 19px/1.2 var(--serif); letter-spacing:-.01em; }
.card .sub { font-size:13px; color:var(--fg-3); margin:0; }
.idgrid { display:grid; grid-template-columns:auto 1fr; gap:var(--s2) var(--s4); font-size:13px; }
.idgrid dt { color:var(--fg-3); white-space:nowrap; }
.idgrid dd { margin:0; font-family:var(--mono); font-size:13px; color:var(--fg); overflow-wrap:anywhere; }
.idgrid dd.big { font-size:16px; font-weight:600; }
.checks { display:flex; flex-direction:column; gap:var(--s2); }
.chk { display:flex; gap:var(--s2); align-items:flex-start; font-size:13px; line-height:1.5;
  padding:var(--s2) var(--s3); border-radius:var(--r); border-left:3px solid transparent; }
.chk.pass { color:var(--credit); background:var(--credit-soft); border-left-color:var(--credit); }
.chk.fail { color:var(--danger); background:var(--danger-soft); border-left-color:var(--danger); }
.chk svg { flex:none; margin-top:2px; }
.card-actions { display:flex; gap:var(--s2); }
.card-actions .btn { flex:1; height:40px; }


/* ── Busy state ────────────────────────────────────────────────────────────
   Saving re-resolves the whole run, which takes a second or two. Without a
   visible signal the click reads as "nothing happened" and gets repeated.   */
.detail.busy { position:relative; }
.detail.busy .d-scroll { opacity:.45; pointer-events:none; }
.detail.busy::after {
  content:""; position:absolute; inset:0; z-index:4; cursor:progress;
}
.btn:disabled { position:relative; }
#confirm:disabled[aria-hidden="false"] { opacity:.75; }
.detail.busy #confirm {
  opacity:1; cursor:progress;
  background:var(--accent); color:var(--accent-fg); border-color:var(--accent);
}
.detail.busy #confirm::before {
  content:""; display:inline-block; width:12px; height:12px; margin-right:8px;
  vertical-align:-1px; border-radius:50%;
  border:2px solid currentColor; border-top-color:transparent;
  animation:spin 700ms linear infinite;
}
@keyframes spin { to { transform:rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .detail.busy #confirm::before { animation:none; border-top-color:currentColor; opacity:.4; }
}

/* ── Narration editor ──────────────────────────────────────────────────────
   The bank's own wording is unreadable in a Day Book. This is what Tally shows. */
#narr {
  font:400 13px/1.5 var(--sans); background:var(--raised); color:var(--fg);
  border:1px solid var(--line-2); border-radius:var(--r);
  padding:var(--s2) var(--s3); width:100%; resize:vertical; min-height:52px;
}
#narr:placeholder-shown { color:var(--fg-3); }
.narrfoot { display:flex; align-items:center; justify-content:space-between; gap:var(--s2); }
#narr-state { font:500 11px/1 var(--sans); text-transform:uppercase;
  letter-spacing:.06em; color:var(--fg-3); }
#narr-state.edited { color:var(--warn); }

/* ── Inline new-ledger form ────────────────────────────────────────────────
   Replaces a browser prompt(), which sandboxed contexts block outright, said
   nothing on cancel, and took free text for a value that must be one of 28. */
.newledger {
  display:flex; flex-direction:column; gap:var(--s2);
  border:1px solid var(--accent); border-radius:var(--r);
  background:var(--accent-soft); padding:var(--s3); margin-top:var(--s2);
}
.newledger[hidden] { display:none; }
.nl-title { font:600 12px/1 var(--sans); color:var(--accent);
  text-transform:uppercase; letter-spacing:.07em; }
.nl-row { display:flex; flex-direction:column; gap:4px;
  font:500 12px/1 var(--sans); color:var(--fg-2); }
.nl-row input, .nl-row select {
  font:400 13px/1 var(--sans); background:var(--surface); color:var(--fg);
  border:1px solid var(--line-2); border-radius:var(--r); height:34px; padding:0 var(--s2);
  width:100%;
}
.nl-note { margin:0; font-size:12px; line-height:1.5; color:var(--fg-2); }
.nl-err { font-size:12px; line-height:1.5; color:var(--danger);
  background:var(--danger-soft); border-left:3px solid var(--danger);
  padding:var(--s2); border-radius:0 var(--r) var(--r) 0; }
.nl-err[hidden] { display:none; }
.nl-actions { display:flex; gap:var(--s2); }
.nl-actions .btn { flex:1; height:36px; }

/* ── Share / fund purchase ─────────────────────────────────────────────────
   Shares are stock items in these books, so this row becomes a Purchase voucher
   with a quantity and a rate rather than a ledger posting. The bank gives the
   amount and the date; the allotment advice gives the other three numbers.     */
.invest {
  display:flex; flex-direction:column; gap:var(--s2);
  border:1px solid var(--line-2); border-radius:var(--r);
  background:var(--raised); padding:var(--s3);
}
.invest[hidden] { display:none; }
.invest input {
  font:400 13px/1 var(--sans); background:var(--surface); color:var(--fg);
  border:1px solid var(--line-2); border-radius:var(--r); height:34px;
  padding:0 var(--s2); width:100%;
}
.iv-pair { display:grid; grid-template-columns:1fr 1fr; gap:var(--s2); }
#invest-toggle-field[hidden] { display:none; }

/* A download link inside the banner. The XML only ever existed on the server
   before this, which made a successful emit look like nothing had happened. */
.banner a.dl {
  color:inherit; font-weight:600; text-decoration:underline;
  text-underline-offset:3px; white-space:nowrap;
}
.banner a.dl:hover { text-decoration-thickness:2px; }

/* "self-contained" emit: declares every referenced master so the file imports into
   a company that does not have them. Compact, because the header is tight. */
.selfc {
  display:flex; align-items:center; gap:6px; flex:none; cursor:pointer;
  font:500 11px/1.2 var(--sans); color:var(--fg-3);
  text-transform:uppercase; letter-spacing:.05em; max-width:88px;
}
.selfc input { width:14px; height:14px; accent-color:var(--accent); cursor:pointer; flex:none; }
@media (max-width:900px) { .selfc { display:none; } }

/* -- Appearance control ---------------------------------------------------
   A labelled select rather than a sun/moon glyph. The person most likely to
   want light mode is also least likely to read an icon as a theme switch.   */
.appearance {
  margin-left:auto; display:flex; align-items:center; gap:var(--s2);
  font:500 12px/1 var(--sans); color:var(--fg-3);
}
.appearance select {
  font:500 12px/1 var(--sans); background:var(--raised); color:var(--fg-2);
  border:1px solid var(--line-2); border-radius:var(--r);
  padding:0 var(--s2); height:28px; cursor:pointer;
}
.appearance select:hover { color:var(--fg); border-color:var(--fg-3); }

/* -- Statement overview ---------------------------------------------------
   Stat tiles and horizontal bars, not a chart: four headline figures and two
   short ranked lists. No categorical palette, because every bar is directly
   labelled -- colour would be carrying nothing that the label does not already
   say. One hue per side instead (navy for money out, green for money in), and
   --warn reserved for the one status that needs attention. All three already
   clear 4.5:1 on both surfaces, well past the 3:1 a graphical object needs.   */
.overview { display:flex; flex-direction:column; gap:var(--s6); padding:var(--s5); }
.overview[hidden] { display:none; }

.ov-head h2 { font:600 20px/1.25 var(--serif); letter-spacing:-.01em; }
.ov-head p {
  margin:6px 0 0; color:var(--fg-3); font-size:13px;
  font-variant-numeric:tabular-nums;
}

/* The four figures the statement itself states. */
.stats {
  display:grid; gap:var(--s3);
  grid-template-columns:repeat(auto-fit, minmax(11rem, 1fr));
}
.stat {
  display:flex; flex-direction:column; gap:5px;
  padding:var(--s3) var(--s4); background:var(--surface);
  border:1px solid var(--line); border-radius:var(--r);
}
.stat .k {
  font:600 11px/1 var(--sans); text-transform:uppercase; letter-spacing:.08em;
  color:var(--fg-3);
}
.stat .v {
  font:600 22px/1.15 var(--mono); font-variant-numeric:tabular-nums;
  letter-spacing:-.02em;
}
.stat .sub { font:400 12px/1.3 var(--sans); color:var(--fg-3); }
.stat .sub.ok   { color:var(--credit); }
.stat .sub.bad  { color:var(--danger); font-weight:600; }

/* The sentence that reframes the out-total. Most of what leaves a bank account
   has not been spent, and that is the least obvious thing on this page. */
.ov-lede {
  margin:0; max-width:62ch; font-size:15px; line-height:1.6; color:var(--fg-2);
  text-wrap:pretty;
}
.ov-lede b { color:var(--fg); font-weight:600; }

.ov-cols { display:grid; gap:var(--s6); grid-template-columns:repeat(auto-fit, minmax(20rem, 1fr)); }
.ov-col h3 {
  font:600 12px/1 var(--sans); text-transform:uppercase; letter-spacing:.08em;
  color:var(--fg-3); margin-bottom:var(--s3);
}
.ov-col h4 {
  font:600 12px/1 var(--sans); text-transform:uppercase; letter-spacing:.08em;
  color:var(--fg-3); margin:var(--s5) 0 var(--s3);
}

.bars { list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:var(--s3); }
.bars li { display:grid; grid-template-columns:1fr auto; gap:4px var(--s3); }
.bars .name { font-size:14px; color:var(--fg); }
.bars .amt {
  font:600 14px/1.3 var(--mono); font-variant-numeric:tabular-nums;
  text-align:right; white-space:nowrap;
}
.bars .share { font-size:12px; color:var(--fg-3); font-variant-numeric:tabular-nums; }
/* Thin mark, rounded data-end, anchored to the baseline. The track is the
   surface a shade down rather than a border, so the bar is the only ink. */
.bars .track {
  grid-column:1 / -1; height:8px; border-radius:4px;
  background:var(--muted); overflow:hidden;
}
.bars .fill { display:block; height:100%; border-radius:4px; min-width:3px; }
.bars .fill.out  { background:var(--accent); }
.bars .fill.in   { background:var(--credit); }
.bars .fill.todo { background:var(--warn); }
.bars li.todo .name { color:var(--warn); font-weight:600; }

.ranked { list-style:none; margin:0; padding:0; display:flex; flex-direction:column; }
.ranked li {
  display:flex; align-items:baseline; gap:var(--s3);
  padding:7px 0; border-bottom:1px solid var(--line); font-size:14px;
}
.ranked li:last-child { border-bottom:0; }
.ranked .nm { flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis;
  white-space:nowrap; color:var(--fg-2); }
.ranked .ct { font-size:12px; color:var(--fg-3); font-variant-numeric:tabular-nums; }
.ranked .amt {
  font:500 14px/1 var(--mono); font-variant-numeric:tabular-nums; white-space:nowrap;
}

.ov-notes { display:grid; grid-template-columns:auto 1fr; gap:var(--s2) var(--s4);
  margin:0; font-size:14px; }
.ov-notes dt { color:var(--fg-3); white-space:nowrap; }
.ov-notes dd { margin:0; color:var(--fg-2); }
.ov-notes dd .n { font-family:var(--mono); font-variant-numeric:tabular-nums;
  color:var(--fg); font-weight:500; }

/* Undecided is a status, so it gets a word and a shape, never colour alone. */
.ov-todo {
  display:flex; align-items:flex-start; gap:var(--s3);
  padding:var(--s3) var(--s4); border-radius:var(--r);
  background:var(--warn-soft); color:var(--warn);
  border:1px solid var(--warn); font-size:14px; line-height:1.5;
}
.ov-todo[hidden] { display:none; }
.ov-todo b { font-weight:600; }
.muted { color:var(--fg-3); font-size:14px; margin:0; }

/* Two zones. The first is what the bank printed and is exact no matter how much
   is still undecided; the second is what the decisions add and is partial until
   they are all made. Keeping them apart is the difference between a figure you
   can quote and one that is still moving. */
.ov-zone { display:flex; flex-direction:column; gap:var(--s4); }
.ov-zone-h {
  display:flex; align-items:baseline; gap:var(--s3); flex-wrap:wrap;
  font:600 13px/1 var(--sans); text-transform:uppercase; letter-spacing:.09em;
  color:var(--fg-2); padding-bottom:var(--s2); border-bottom:1px solid var(--line);
}
.ov-note {
  font:400 12px/1 var(--sans); text-transform:none; letter-spacing:0;
  color:var(--fg-3);
}
.ov-note.ok { color:var(--credit); }

.ov-progress { display:flex; flex-direction:column; gap:6px; }
.ov-progress .track {
  height:8px; border-radius:4px; background:var(--muted); overflow:hidden;
}
.ov-progress .fill { display:block; height:100%; border-radius:4px; background:var(--accent); }
.ov-progress .lbl { font-size:13px; color:var(--fg-3); font-variant-numeric:tabular-nums; }
.ov-progress .lbl b { color:var(--warn); font-weight:600; }
.ov-progress[hidden] { display:none; }

/* The source bar carries one leg per account, because a run can hold several
   statements -- which is exactly what lets a transfer between two of your own
   accounts collapse into one Contra instead of posting twice. */
.srcbar .legs { display:flex; flex-wrap:wrap; gap:var(--s2) var(--s5); min-width:0; }
.srcbar .legmeta {
  font:400 12px/1 var(--mono); color:var(--fg-3); font-variant-numeric:tabular-nums;
}
.srcbar .leg { gap:var(--s2); }

/* The In Tally? screen. Same furniture as the overview, plus a four-column row
   that puts our statement row beside the Tally voucher it matched. */
.steps { margin:0; padding-left:1.3rem; display:flex; flex-direction:column; gap:var(--s2);
  font-size:15px; line-height:1.6; color:var(--fg-2); max-width:60ch; }
.steps li::marker { color:var(--fg-3); font-variant-numeric:tabular-nums; }
.steps b { color:var(--fg); font-weight:600; }

.ranked.recon li {
  display:grid; gap:var(--s3); align-items:baseline;
  grid-template-columns:5.5rem minmax(0,1fr) 7rem minmax(0,1.4fr);
}
.ranked.recon .d {
  font:400 13px/1.3 var(--mono); color:var(--fg-3); white-space:nowrap;
}
.ranked.recon .amt { text-align:right; }
.ranked.recon .vch {
  font-size:13px; color:var(--fg-3); overflow:hidden;
  text-overflow:ellipsis; white-space:nowrap;
}
@media (max-width:900px) {
  .ranked.recon li { grid-template-columns:5rem minmax(0,1fr) 6rem; }
  .ranked.recon .vch { grid-column:1 / -1; }
}

/* The filter row belongs to the Review view and hides with it. Buttons rather
   than tabs, because these narrow one list -- they are not alternative places to
   be, which is what the tab bar above now means. */
.filters {
  display:flex; gap:var(--s2); flex-wrap:wrap; align-items:center;
  padding:var(--s3) var(--s5);
  background:var(--bg); border-bottom:1px solid var(--line);
}
.filters[hidden] { display:none; }
.filters button {
  display:flex; align-items:center; gap:var(--s2);
  font:500 13px/1 var(--sans); cursor:pointer; min-height:32px;
  background:var(--surface); color:var(--fg-2);
  border:1px solid var(--line-2); border-radius:99px; padding:0 var(--s3);
  transition:background 140ms var(--ease), color 140ms var(--ease),
             border-color 140ms var(--ease);
}
.filters button:hover { color:var(--fg); border-color:var(--fg-3); }
.filters button[aria-pressed="true"] {
  background:var(--accent); color:var(--accent-fg); border-color:var(--accent);
  font-weight:600;
}
.filters .n {
  font:600 12px/1 var(--mono); font-variant-numeric:tabular-nums;
  padding:2px 6px; border-radius:99px; min-width:20px; text-align:center;
  background:var(--muted); color:var(--fg-2);
}
.filters button[aria-pressed="true"] .n {
  background:color-mix(in srgb, var(--accent-fg) 22%, transparent);
  color:var(--accent-fg);
}
.filters button[data-status="blocked"] .n.hot { background:var(--danger-soft); color:var(--danger); }
.filters button[aria-pressed="true"] .n.hot {
  background:color-mix(in srgb, var(--accent-fg) 22%, transparent);
  color:var(--accent-fg);
}

/* Rules. Each card carries the consequence rather than just the definition: what
   it decided here, what disagrees with it, and a couple of the actual rows. */
.decs { list-style:none; margin:0; padding:0; display:flex; flex-direction:column;
  gap:var(--s3); }
.dec {
  background:var(--surface); border:1px solid var(--line); border-radius:var(--r);
  padding:var(--s3) var(--s4);
}
.dec.flagged { border-left:3px solid var(--warn); }
.dec.idle { background:transparent; }
.dec-head { display:flex; align-items:center; gap:var(--s3); flex-wrap:wrap; }
.dec-key {
  font:500 13px/1.3 var(--mono); color:var(--fg);
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap; max-width:22ch;
}
.dec-arrow { color:var(--fg-3); }
.dec-led { font-size:14px; font-weight:500; }
.dec-use {
  margin-left:auto; font:400 13px/1 var(--mono); color:var(--fg-3);
  font-variant-numeric:tabular-nums; white-space:nowrap;
}
.dec.idle .dec-use { font-style:italic; }
.dec-forget { height:30px; padding:0 var(--s3); font-size:13px; }

.dec-flags { list-style:none; margin:var(--s3) 0 0; padding:0;
  display:flex; flex-direction:column; gap:4px; }
.dec-flags li { font-size:13.5px; line-height:1.5; color:var(--warn); font-weight:500; }

.dec-eg { list-style:none; margin:var(--s3) 0 0; padding:var(--s2) 0 0;
  border-top:1px solid var(--line); display:flex; flex-direction:column; gap:3px; }
.dec-eg li { display:grid; grid-template-columns:5.5rem 6.5rem minmax(0,1fr);
  gap:var(--s3); font-size:13px; color:var(--fg-3); }
.dec-eg .d { font-family:var(--mono); }
.dec-eg .a { font-family:var(--mono); text-align:right;
  font-variant-numeric:tabular-nums; color:var(--fg-2); }
.dec-eg .n { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }

/* Close. One card per registered account: what it covers, what is emitted, and --
   the part nothing else can show -- the periods with no statement at all. */
.clss { list-style:none; margin:0; padding:0; display:flex; flex-direction:column;
  gap:var(--s3); }
.cls { background:var(--surface); border:1px solid var(--line);
  border-radius:var(--r); padding:var(--s3) var(--s4); }
.cls.flagged { border-left:3px solid var(--warn); }
.cls.empty { background:transparent; }
.cls-head { display:flex; align-items:center; gap:var(--s2) var(--s3); flex-wrap:wrap; }
.cls-led { font-size:15px; font-weight:600; }
.cls-cover { margin-left:auto; font:400 13px/1.4 var(--mono); color:var(--fg-3);
  font-variant-numeric:tabular-nums; }
.cls.empty .cls-cover { font-style:italic; }
.cls-gap { margin:var(--s3) 0 0; font-size:13.5px; line-height:1.5; color:var(--warn); }
.cls-gap b { font-weight:600; }
.cls-spans { list-style:none; margin:var(--s3) 0 0; padding:var(--s2) 0 0;
  border-top:1px solid var(--line); display:flex; flex-direction:column; gap:4px; }
.cls-spans li { display:grid; gap:var(--s3); font-size:13px; color:var(--fg-3);
  grid-template-columns:11rem 5rem 11rem minmax(0,1fr); align-items:baseline; }
.cls-spans .d { font-family:var(--mono); }
.cls-spans .r { font-family:var(--mono); text-align:right;
  font-variant-numeric:tabular-nums; }
.cls-spans .st.done { color:var(--credit); }
.cls-spans .st.todo { color:var(--warn); }
.cls-spans .f { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }

/* Anomalies sit above the rows in Review. Collapsed once there are more than a
   few, because a panel that pushes the work off screen gets closed and stays shut. */
.anomalies {
  padding:var(--s3) var(--s5); background:var(--warn-soft);
  border-bottom:1px solid var(--warn); color:var(--warn);
}
.anomalies[hidden] { display:none; }
.anomalies summary { cursor:pointer; font-size:14px; }
.anomalies summary b { font-weight:600; }
.anomalies .hint { color:inherit; opacity:.75; font-weight:400; }
.anoms { list-style:none; margin:var(--s3) 0 0; padding:0;
  display:flex; flex-direction:column; gap:var(--s2); }
.anoms li { display:grid; grid-template-columns:7rem minmax(0,1fr); gap:var(--s3); }
.anom-amt { font:600 14px/1.5 var(--mono); font-variant-numeric:tabular-nums;
  text-align:right; }
.anom-body { display:flex; flex-direction:column; gap:2px; font-size:14px;
  line-height:1.45; }
.anom-why { color:inherit; opacity:.85; font-size:13px; }

/* Skeletons, shaped like the content they stand in for so the layout does not jump
   when the real thing arrives. Overview recomputes the whole run and takes about a
   second; with nothing on screen for that second the app reads as broken and the
   natural response is to click again. */
.sk-wrap { display:flex; flex-direction:column; gap:var(--s4); padding:var(--s5); }
.sk {
  display:block; height:13px; border-radius:4px; background:var(--muted);
  animation:sk-pulse 1.4s ease-in-out infinite;
}
.sk-box { height:74px; border-radius:var(--r); }
.sk-stats { display:grid; gap:var(--s3);
  grid-template-columns:repeat(auto-fit, minmax(11rem, 1fr)); }
@keyframes sk-pulse { 0%,100% { opacity:1; } 50% { opacity:.45; } }
@media (prefers-reduced-motion: reduce) { .sk { animation:none; opacity:.7; } }

/* For screen readers only: the skeleton is decoration, so the state has to be
   said out loud somewhere. */
.sr {
  position:absolute; width:1px; height:1px; padding:0; margin:-1px;
  overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0;
}

/* Bulk selection. The bar appears only when something is selected, so it costs
   nothing when working a row at a time. */
.c-pick { width:38px; }
th.c-pick, td.c-pick { text-align:center; padding-left:var(--s3); padding-right:0; }
td.c-pick input, th.c-pick input {
  width:17px; height:17px; accent-color:var(--accent); cursor:pointer;
}
tbody tr.picked td { background:var(--accent-soft); }
tbody tr.picked td:first-child { box-shadow:inset 3px 0 0 var(--accent); }

.bulkbar {
  display:flex; align-items:center; gap:var(--s3); flex-wrap:wrap;
  padding:var(--s3) var(--s5);
  background:var(--accent-soft); border-bottom:1px solid var(--accent);
  font-size:14px;
}
.bulkbar[hidden] { display:none; }
.bulk-count { font-variant-numeric:tabular-nums; }
.bulk-count b { font-weight:600; }
.bulk-led { display:flex; align-items:center; gap:var(--s2); flex:1 1 22rem; min-width:0; }
.bulk-led input {
  flex:1; min-width:0; font:400 14px/1 var(--sans);
  background:var(--surface); color:var(--fg);
  border:1px solid var(--line-2); border-radius:var(--r); height:34px; padding:0 var(--s3);
}
.bulk-remember { flex:none; min-height:34px; background:var(--surface); }
.bulkbar .btn { height:34px; }

/* An action that cannot be taken says so where the action is, rather than failing
   after the click. */
.check.blocked { opacity:.7; cursor:not-allowed; }
.check.blocked input { cursor:not-allowed; }
.router-why {
  border-left:3px solid var(--warn); background:var(--warn-soft); color:var(--warn);
  padding:var(--s2) var(--s3); border-radius:0 var(--r) var(--r) 0;
  font-size:13px; line-height:1.5; margin:0;
}
.router-why[hidden] { display:none; }
.bulk-remember input:disabled { cursor:not-allowed; }

/* A closed account is finished rather than outstanding, so it says so instead of
   sitting silently among the ones still waiting for a statement. */
.tag.closed { color:var(--fg-3); border-style:dashed; }
.cls-done { margin:var(--s3) 0 0; font-size:13.5px; color:var(--fg-3); }
