:root {
  --bg: #f5f6f8;
  --panel: #ffffff;
  --border: #e2e5ea;
  --text: #1c1f26;
  --muted: #6b7280;
  --brand: #2f6f4f;
  --brand-dark: #234f38;
  --brand-light: #e6f2ec;
  --danger: #c0392b;
  --danger-light: #fbe9e7;
  --in-color: #115e59;
  --out-color: #c0392b;
  --radius: 10px;
  font-size: 15px;

  /* Sidebar color-tagging (per user's request): each bank, cash books as a
     whole, and each category ledger gets its own light background tint so
     they're easy to tell apart at a glance. Each pair is a light ~14% tint
     (for the row/header background) plus a darker step of the same hue (for
     text/left-border accent), picked from the app's existing palette logic —
     same relationship as --brand-light is to --brand-dark above. Assigned in
     app.js: see bankColorTag() / categoryColorTag() / CASH_TAG. */
  --tag-red-bg: #fbe6e5;     --tag-red-text: #a33534;     /* AmBank */
  --tag-yellow-bg: #fcf2db;  --tag-yellow-text: #ab7400;  /* Maybank */
  --tag-blue-bg: #e1ecf9;    --tag-blue-text: #1e569a;    /* Hong Leong Bank */
  --tag-violet-bg: #e9e6f9;  --tag-violet-text: #473a9a;  /* Cash books (all) */
  --tag-orange-bg: #fceae3;  --tag-orange-text: #a94b25;  /* auto-assigned */
  --tag-aqua-bg: #dff4ec;    --tag-aqua-text: #137e58;    /* auto-assigned */
  --tag-magenta-bg: #fcedf2; --tag-magenta-text: #a75976; /* auto-assigned */
  --tag-green-bg: #dbeedb;   --tag-green-text: #005e00;   /* auto-assigned */
}

* { box-sizing: border-box; }

body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", Arial, sans-serif;
  background: var(--bg);
  color: var(--text);
}

[hidden] { display: none !important; }

button {
  font-family: inherit;
  font-size: 14px;
  cursor: pointer;
  border: 1px solid var(--border);
  background: var(--panel);
  color: var(--text);
  padding: 8px 14px;
  border-radius: 8px;
  transition: background 0.15s, border-color 0.15s;
}
button:hover { background: #f0f1f3; }
button.primary {
  background: var(--brand);
  border-color: var(--brand);
  color: #fff;
}
button.primary:hover { background: var(--brand-dark); }
button.danger { color: var(--danger); }
button.danger:hover { background: var(--danger-light); }
.file-link {
  display: inline-block;
  font-size: 13px;
  padding: 6px 12px;
  border: 1px solid var(--border);
  border-radius: 8px;
  color: var(--text);
  text-decoration: none;
  background: var(--panel);
  transition: background 0.15s, border-color 0.15s;
}
.file-link:hover { background: #f0f1f3; }
.file-link.primary { background: var(--brand); border-color: var(--brand); color: #fff; }
.file-link.primary:hover { background: var(--brand-dark); }
.attachment-badge {
  text-decoration: none; font-size: 14px; margin-right: 4px;
  /* It's a <button> (for click-handler consistency with the rest of the row
     actions, since an in-page viewer can't be a plain href navigation) but
     should still read as a bare icon like it did as an <a>, not a boxed button. */
  background: none; border: none; padding: 0; line-height: 1;
}
button:disabled { opacity: 0.5; cursor: not-allowed; }

input, select {
  font-family: inherit;
  font-size: 14px;
  padding: 7px 10px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: #fff;
  color: var(--text);
}
input:focus, select:focus { outline: 2px solid var(--brand-light); border-color: var(--brand); }

/* ---------- Login ---------- */
#login-screen {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
}
.login-box {
  background: var(--panel);
  padding: 36px 32px;
  border-radius: var(--radius);
  border: 1px solid var(--border);
  width: 320px;
  box-shadow: 0 8px 24px rgba(0,0,0,0.06);
}
.login-box h1 { font-size: 20px; margin: 0 0 4px; }
.login-box p.sub { color: var(--muted); margin: 0 0 20px; font-size: 13px; }
.login-box label { display: block; font-size: 13px; margin: 12px 0 4px; color: var(--muted); }
.login-box input, .login-box select { width: 100%; }
.login-box button { width: 100%; margin-top: 18px; }
.login-error { color: var(--danger); font-size: 13px; margin-top: 10px; min-height: 16px; }
/* Overrides the generic ".login-box button" full-width/margin rule above (an id
   selector beats a class+tag one) so this reads as a subtle text link under the
   error message, not a second big button competing with the real Login one. */
#forgot-password-link {
  width: auto;
  margin: 10px auto 0;
  display: block;
  background: none;
  border: none;
  padding: 0;
  color: var(--brand);
  font-size: 13px;
  text-decoration: underline;
  cursor: pointer;
}
#forgot-password-link:hover { color: var(--brand-dark); }

/* ---------- App shell ---------- */
#app {
  display: grid;
  grid-template-columns: 240px 1fr;
  min-height: 100vh;
}
#sidebar {
  background: var(--panel);
  border-right: 1px solid var(--border);
  padding: 18px 12px;
  display: flex;
  flex-direction: column;
  /* Pin the sidebar to the viewport and let it scroll on its own — otherwise, with
     a long book list and a long transactions table in #main, the sidebar-footer
     (修改密码/管理类别/管理公司实体/管理员工账号/登出) ends up far below the fold at the
     very bottom of the whole page, effectively invisible without scrolling all the
     way past the ledger table first. */
  position: sticky;
  top: 0;
  height: 100vh;
  overflow-y: auto;
}
.sidebar-top { display: flex; align-items: center; justify-content: space-between; gap: 6px; }
#sidebar-collapse-btn { flex: 0 0 auto; padding: 3px 9px; font-size: 13px; line-height: 1.4; }
/* Fully hides #sidebar and lets #main take the freed width — see the
   #sidebar-collapse-btn/#sidebar-reopen-btn click handlers in app.js. Not
   persisted anywhere, so it resets to expanded on the next login/reload,
   same as the section-collapse state above. */
#app.sidebar-collapsed { grid-template-columns: 1fr; }
#app.sidebar-collapsed #sidebar { display: none; }
/* Reserve room so #sidebar-reopen-btn (fixed top-left) never sits on top of
   the page title/heading text that would otherwise start right at the edge. */
#app.sidebar-collapsed #main { padding-left: 56px; }
/* Stays reachable while the sidebar is hidden — see the HTML comment by
   #sidebar-reopen-btn for why it lives outside #sidebar entirely. */
#sidebar-reopen-btn {
  position: fixed; top: 14px; left: 14px; z-index: 20;
  padding: 6px 11px; font-size: 14px; border-radius: 999px;
  box-shadow: 0 2px 8px rgba(0,0,0,0.15);
}
#sidebar h2 {
  font-size: 15px;
  margin: 18px 8px 6px;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-size: 12px;
}
#sidebar h2:first-child { margin-top: 4px; }
/* Collapsible section headers (bank/cash/category) — :not([hidden]) matters
   here: staff/accountant accounts have cash-section-title/category-section-title
   set to [hidden] via applyRoleUI(), and an unconditional `display: flex` would
   override the browser's own [hidden]{display:none} rule and make them show up
   anyway, since author styles always win over the user-agent stylesheet. */
.sidebar-section-header:not([hidden]) { display: flex; align-items: center; justify-content: space-between; cursor: pointer; user-select: none; gap: 6px; }
.sidebar-section-header:hover { color: var(--brand-dark); }
/* Styled as a visible round chip (not just a faint text glyph) so it reads
   as a clickable toggle button at a glance, per user feedback. */
.sidebar-section-header .collapse-arrow {
  display: inline-flex; align-items: center; justify-content: center;
  width: 20px; height: 20px; border-radius: 50%;
  background: var(--border); color: var(--text);
  font-size: 11px; font-weight: 700;
  transition: transform 0.15s ease, background 0.15s ease, color 0.15s ease;
  flex: 0 0 auto;
}
.sidebar-section-header:hover .collapse-arrow { background: var(--brand-light); color: var(--brand-dark); }
.sidebar-section-header.collapsed .collapse-arrow { transform: rotate(-90deg); }
.book-list.collapsed { display: none; }
/* Border/padding moved to .sidebar-top (its flex container) so the border
   spans the full header width, not just the brand text's own box. */
.sidebar-top { padding: 4px 8px 10px; border-bottom: 1px solid var(--border); margin-bottom: 4px; }
/* white-space: nowrap matters on mobile: #sidebar becomes a flex row there
   (see the (max-width: 800px) block) competing for width with the book
   lists, and CJK text can otherwise wrap between ANY two characters once
   squeezed, turning "记账系统" into one character per line. */
.brand { font-weight: 700; font-size: 17px; white-space: nowrap; }
.book-list { list-style: none; margin: 0; padding: 0; }
.book-item {
  padding: 9px 10px;
  border-radius: 8px;
  cursor: pointer;
  font-size: 14px;
  display: flex;
  justify-content: space-between;
  gap: 6px;
  align-items: baseline;
}
.bank-group-header {
  padding: 8px 10px 2px;
  font-size: 11.5px;
  font-weight: 700;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.03em;
  display: flex;
  align-items: center;
  justify-content: space-between;
  cursor: pointer;
  user-select: none;
}
.bank-group-header:hover { color: var(--brand-dark); }
/* Same round-chip treatment as .sidebar-section-header's arrow above, just a
   touch smaller to match this sub-header's smaller text. */
.bank-group-header .collapse-arrow {
  display: inline-flex; align-items: center; justify-content: center;
  width: 17px; height: 17px; border-radius: 50%;
  background: var(--border); color: var(--text);
  font-size: 10px; font-weight: 700;
  transition: transform 0.15s ease, background 0.15s ease, color 0.15s ease;
  flex: 0 0 auto;
}
.bank-group-header:hover .collapse-arrow { background: var(--brand-light); color: var(--brand-dark); }
.bank-group-header.collapsed .collapse-arrow { transform: rotate(-90deg); }
.book-item.indent { padding-left: 20px; font-size: 13.5px; }
.book-item:hover { background: var(--bg); }
.book-item.active { background: var(--brand-light); color: var(--brand-dark); font-weight: 600; }
.book-item .balance { font-size: 12px; color: var(--muted); white-space: nowrap; }
.book-item.active .balance { color: var(--brand-dark); }

/* ---------- sidebar color tags (per-bank / cash / per-category) ---------- */
/* Tag classes (tag-red, tag-yellow, ...) are added in app.js via
   bankColorTag()/categoryColorTag()/CASH_TAG — see the comment there for
   which name maps to which color. Applies to both the bank sub-group
   header row AND the book-item rows under/within it, so each block reads
   as one colored zone. */
.bank-group-header.tag-red { background: var(--tag-red-bg); color: var(--tag-red-text); border-radius: 6px; border-top: 3px solid var(--tag-red-text); margin-top: 8px; padding-top: 6px; }
.bank-group-header.tag-red:hover { background: var(--tag-red-bg); color: var(--tag-red-text); }
.bank-group-header.tag-red .collapse-arrow { background: rgba(255,255,255,0.65); color: var(--tag-red-text); }
.book-item.tag-red { background: var(--tag-red-bg); border-left: 3px solid var(--tag-red-text); padding-left: 7px; }
.book-item.indent.tag-red { padding-left: 17px; }
.book-item.tag-red:hover { filter: brightness(0.96); }
.book-item.tag-red.active { background: var(--brand-light); color: var(--brand-dark); border-left: 3px solid var(--tag-red-text); }

.bank-group-header.tag-yellow { background: var(--tag-yellow-bg); color: var(--tag-yellow-text); border-radius: 6px; border-top: 3px solid var(--tag-yellow-text); margin-top: 8px; padding-top: 6px; }
.bank-group-header.tag-yellow:hover { background: var(--tag-yellow-bg); color: var(--tag-yellow-text); }
.bank-group-header.tag-yellow .collapse-arrow { background: rgba(255,255,255,0.65); color: var(--tag-yellow-text); }
.book-item.tag-yellow { background: var(--tag-yellow-bg); border-left: 3px solid var(--tag-yellow-text); padding-left: 7px; }
.book-item.indent.tag-yellow { padding-left: 17px; }
.book-item.tag-yellow:hover { filter: brightness(0.96); }
.book-item.tag-yellow.active { background: var(--brand-light); color: var(--brand-dark); border-left: 3px solid var(--tag-yellow-text); }

.bank-group-header.tag-blue { background: var(--tag-blue-bg); color: var(--tag-blue-text); border-radius: 6px; border-top: 3px solid var(--tag-blue-text); margin-top: 8px; padding-top: 6px; }
.bank-group-header.tag-blue:hover { background: var(--tag-blue-bg); color: var(--tag-blue-text); }
.bank-group-header.tag-blue .collapse-arrow { background: rgba(255,255,255,0.65); color: var(--tag-blue-text); }
.book-item.tag-blue { background: var(--tag-blue-bg); border-left: 3px solid var(--tag-blue-text); padding-left: 7px; }
.book-item.indent.tag-blue { padding-left: 17px; }
.book-item.tag-blue:hover { filter: brightness(0.96); }
.book-item.tag-blue.active { background: var(--brand-light); color: var(--brand-dark); border-left: 3px solid var(--tag-blue-text); }

.bank-group-header.tag-violet { background: var(--tag-violet-bg); color: var(--tag-violet-text); border-radius: 6px; border-top: 3px solid var(--tag-violet-text); margin-top: 8px; padding-top: 6px; }
.bank-group-header.tag-violet:hover { background: var(--tag-violet-bg); color: var(--tag-violet-text); }
.bank-group-header.tag-violet .collapse-arrow { background: rgba(255,255,255,0.65); color: var(--tag-violet-text); }
.book-item.tag-violet { background: var(--tag-violet-bg); border-left: 3px solid var(--tag-violet-text); padding-left: 7px; }
.book-item.indent.tag-violet { padding-left: 17px; }
.book-item.tag-violet:hover { filter: brightness(0.96); }
.book-item.tag-violet.active { background: var(--brand-light); color: var(--brand-dark); border-left: 3px solid var(--tag-violet-text); }

.bank-group-header.tag-orange { background: var(--tag-orange-bg); color: var(--tag-orange-text); border-radius: 6px; border-top: 3px solid var(--tag-orange-text); margin-top: 8px; padding-top: 6px; }
.bank-group-header.tag-orange:hover { background: var(--tag-orange-bg); color: var(--tag-orange-text); }
.bank-group-header.tag-orange .collapse-arrow { background: rgba(255,255,255,0.65); color: var(--tag-orange-text); }
.book-item.tag-orange { background: var(--tag-orange-bg); border-left: 3px solid var(--tag-orange-text); padding-left: 7px; }
.book-item.indent.tag-orange { padding-left: 17px; }
.book-item.tag-orange:hover { filter: brightness(0.96); }
.book-item.tag-orange.active { background: var(--brand-light); color: var(--brand-dark); border-left: 3px solid var(--tag-orange-text); }

.bank-group-header.tag-aqua { background: var(--tag-aqua-bg); color: var(--tag-aqua-text); border-radius: 6px; border-top: 3px solid var(--tag-aqua-text); margin-top: 8px; padding-top: 6px; }
.bank-group-header.tag-aqua:hover { background: var(--tag-aqua-bg); color: var(--tag-aqua-text); }
.bank-group-header.tag-aqua .collapse-arrow { background: rgba(255,255,255,0.65); color: var(--tag-aqua-text); }
.book-item.tag-aqua { background: var(--tag-aqua-bg); border-left: 3px solid var(--tag-aqua-text); padding-left: 7px; }
.book-item.indent.tag-aqua { padding-left: 17px; }
.book-item.tag-aqua:hover { filter: brightness(0.96); }
.book-item.tag-aqua.active { background: var(--brand-light); color: var(--brand-dark); border-left: 3px solid var(--tag-aqua-text); }

.bank-group-header.tag-magenta { background: var(--tag-magenta-bg); color: var(--tag-magenta-text); border-radius: 6px; border-top: 3px solid var(--tag-magenta-text); margin-top: 8px; padding-top: 6px; }
.bank-group-header.tag-magenta:hover { background: var(--tag-magenta-bg); color: var(--tag-magenta-text); }
.bank-group-header.tag-magenta .collapse-arrow { background: rgba(255,255,255,0.65); color: var(--tag-magenta-text); }
.book-item.tag-magenta { background: var(--tag-magenta-bg); border-left: 3px solid var(--tag-magenta-text); padding-left: 7px; }
.book-item.indent.tag-magenta { padding-left: 17px; }
.book-item.tag-magenta:hover { filter: brightness(0.96); }
.book-item.tag-magenta.active { background: var(--brand-light); color: var(--brand-dark); border-left: 3px solid var(--tag-magenta-text); }

.bank-group-header.tag-green { background: var(--tag-green-bg); color: var(--tag-green-text); border-radius: 6px; border-top: 3px solid var(--tag-green-text); margin-top: 8px; padding-top: 6px; }
.bank-group-header.tag-green:hover { background: var(--tag-green-bg); color: var(--tag-green-text); }
.bank-group-header.tag-green .collapse-arrow { background: rgba(255,255,255,0.65); color: var(--tag-green-text); }
.book-item.tag-green { background: var(--tag-green-bg); border-left: 3px solid var(--tag-green-text); padding-left: 7px; }
.book-item.indent.tag-green { padding-left: 17px; }
.book-item.tag-green:hover { filter: brightness(0.96); }
.book-item.tag-green.active { background: var(--brand-light); color: var(--brand-dark); border-left: 3px solid var(--tag-green-text); }

.sidebar-footer { margin-top: auto; padding-top: 12px; border-top: 1px solid var(--border); font-size: 13px; }
.sidebar-footer .user-name { font-weight: 600; margin-bottom: 6px; }
.sidebar-footer button { width: 100%; margin-top: 6px; font-size: 13px; padding: 6px 10px; }
/* The 7 admin buttons (修改密码/管理类别/...) live inside this panel, revealed by
   #open-management-btn. User asked for a centered popup rather than the list
   expanding in place and pushing the sidebar taller — so #management-menu
   itself IS the dimmed, full-viewport backdrop (position: fixed; inset: 0,
   same recipe as .modal-backdrop above), and .management-menu-panel is the
   actual centered card. position: fixed means its containing block is the
   viewport, not #sidebar, so it's unaffected by #sidebar's own scrolling/
   overflow — no risk of the overflow-clipping trap that bit the mobile footer
   dropdown earlier (see the position:fixed comment on .sidebar-footer below). */
.management-menu {
  position: fixed; inset: 0; background: rgba(0,0,0,0.35);
  display: flex; align-items: center; justify-content: center; z-index: 50;
}
.management-menu-panel {
  background: var(--panel); border-radius: var(--radius); padding: 20px;
  width: 320px; max-width: 90vw; max-height: 80vh; overflow-y: auto;
}
/* Buttons here are no longer descendants of .sidebar-footer (see the HTML
   comment by #management-menu for why), so they need their own sizing rather
   than inheriting ".sidebar-footer button" — same look as before (and as the
   rest of the sidebar-footer buttons) either way. */
.management-menu-panel button { width: 100%; margin-top: 6px; font-size: 13px; padding: 6px 10px; }
.management-menu-panel button:first-child { margin-top: 0; }
/* Only shown on narrow (mobile) screens — toggles .sidebar-footer open there.
   See the (max-width: 800px) block below. */
#mobile-menu-btn { display: none; }
#mobile-books-btn { display: none; }

/* min-width: 0 defeats the CSS Grid/Flex "automatic minimum size" quirk: without
   it, a wide un-wrappable child (the ledger table's nowrap cells, a long RM
   number in a summary card) can force #main — and the #app grid track it sits
   in — wider than the viewport, dragging everything else in #main (cards,
   filters, search box) off-screen with it even though those elements would
   otherwise fit fine on their own. overflow-x stays hidden here (not auto) so
   #main itself never becomes the thing that scrolls horizontally — only
   #ledger-wrap below does, scoped to just the table that actually needs it. */
#main { padding: 22px 28px; overflow-x: hidden; min-width: 0; }
/* The rounded-corner border used to live on table.ledger itself (with its own
   overflow: hidden to clip the sticky header's background to those corners),
   but position: sticky resolves against the nearest ancestor whose overflow is
   not visible — with that overflow: hidden on the table itself, the table
   became that ancestor instead of this wrap, which silently broke the sticky
   actions column below (right: 0 had nothing meaningful to stick against, so
   it just scrolled normally). Moving the border/radius/clip here instead
   keeps the same look while leaving #ledger-wrap as the one unambiguous
   scroll + sticky container. */
#ledger-wrap { overflow-x: auto; max-width: 100%; border: 1px solid var(--border); border-radius: var(--radius); background: var(--panel); }
#main-header { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 16px; flex-wrap: wrap; gap: 10px; }
#book-title { font-size: 20px; font-weight: 700; margin: 0; }
#book-subtitle { color: var(--muted); font-size: 13px; margin-top: 2px; }
.actions-row { display: flex; gap: 8px; flex-wrap: wrap; }

.summary-cards { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px; margin-bottom: 18px; }
.card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 14px 16px;
  overflow: hidden;
  min-width: 0;
}
.card .label { font-size: 12px; color: var(--muted); margin-bottom: 4px; }
/* overflow-wrap lets a long, comma-free RM amount (e.g. RM31,123,212.00) break
   and wrap onto a second line on a narrow card instead of forcing the whole
   grid column — and everything else in #main alongside it — wider than the
   screen just to fit one unbroken run of digits. */
.card .value { font-size: 19px; font-weight: 700; overflow-wrap: anywhere; }
.card .value.in { color: var(--in-color); }
.card .value.out { color: var(--out-color); }

.filters { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; margin-bottom: 14px; max-width: 100%; }
.filters .grow { flex: 1; min-width: 160px; }
/* Defeats the same flex "automatic minimum size" issue as #main above: a
   <select>'s content-based intrinsic width (the longest option's text) or a
   native <input type="month">'s platform-default width can otherwise force
   its flex item wider than the space flex-wrap actually gave it on a row,
   pushing it past the right edge instead of shrinking/wrapping like the rest. */
.filters select, .filters input { min-width: 0; max-width: 100%; }
.quick-range { display: flex; gap: 6px; }
.quick-range-btn { padding: 5px 12px; font-size: 12.5px; border-radius: 999px; }
.quick-range-btn.active { background: var(--brand-light); border-color: var(--brand); color: var(--brand-dark); font-weight: 600; }

table.ledger { width: 100%; border-collapse: collapse; background: var(--panel); }
table.ledger th, table.ledger td { padding: 9px 10px; border-bottom: 1px solid var(--border); font-size: 13.5px; text-align: left; white-space: nowrap; vertical-align: top; }
table.ledger th { background: #fafbfc; color: var(--muted); font-weight: 600; position: sticky; top: 0; }
table.ledger td.detail-cell, table.ledger th.detail-cell { white-space: normal; min-width: 200px; }
/* whole-row tint so income/expense rows are scannable at a glance, matching the
   original spreadsheet's row-highlight style — not just the amount cell's text color */
table.ledger tbody tr.row-in { background: rgba(17, 94, 89, 0.07); }
table.ledger tbody tr.row-out { background: rgba(192, 57, 43, 0.06); }
table.ledger tr:hover td { background: #fafbfc; }
table.ledger td.num { text-align: right; font-variant-numeric: tabular-nums; }
table.ledger td.in { color: var(--in-color); }
table.ledger td.out { color: var(--out-color); }
table.ledger td.balance { font-weight: 600; font-variant-numeric: tabular-nums; }
/* The 操作 (actions) column is the last one, and with this many columns it's
   the first thing to get scrolled out of view on a phone (#ledger-wrap scrolls
   the table horizontally — see the mobile-overflow fix above). Pinning it to
   the right edge of that scroll area means the edit/delete/history buttons are
   always reachable without having to swipe over first. It needs its own opaque
   background (matching each row's income/expense tint, since td itself is
   normally transparent and just shows the <tr> background through) — a sticky
   cell is lifted into its own layer, so without this the columns scrolling
   underneath would show through it. */
table.ledger th.sticky-col, table.ledger td.sticky-col {
  position: sticky;
  right: 0;
  z-index: 2;
  box-shadow: -4px 0 6px -4px rgba(0, 0, 0, 0.18);
}
table.ledger th.sticky-col { z-index: 3; }
table.ledger tbody tr.row-in td.sticky-col { background: #eef4f3; }
table.ledger tbody tr.row-out td.sticky-col { background: #fbf3f2; }
table.ledger tbody tr:hover td.sticky-col { background: #fafbfc; }
.cat-tag { display: inline-block; padding: 2px 8px; border-radius: 999px; background: var(--brand-light); color: var(--brand-dark); font-size: 12px; }
.row-actions { display: flex; gap: 4px; }
.row-actions button { padding: 4px 8px; font-size: 12px; }
.empty-state { padding: 40px; text-align: center; color: var(--muted); }

/* ---------- Modal ---------- */
.modal-backdrop {
  position: fixed; inset: 0; background: rgba(0,0,0,0.35);
  display: flex; align-items: center; justify-content: center; z-index: 50;
}
.modal {
  background: var(--panel); border-radius: var(--radius); padding: 24px;
  width: 480px; max-width: 92vw; max-height: 88vh; overflow-y: auto;
}
.modal h3 { margin-top: 0; }
.form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.form-grid .full { grid-column: 1 / -1; }
.form-grid label { display: block; font-size: 12.5px; color: var(--muted); margin-bottom: 4px; }
.form-grid input, .form-grid select { width: 100%; }
.modal-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 18px; }
.checkbox-row { display: flex; align-items: center; gap: 6px; margin-top: 4px; }
.checkbox-row input { width: auto; }
.error-text { color: var(--danger); font-size: 13px; min-height: 16px; margin-top: 8px; }
.hint-text { color: var(--muted); font-size: 12px; margin-top: 8px; line-height: 1.5; }

/* Tap-to-select suggestion dropdown (Company/Transfer Detail fields) — see
   attachSuggest() in app.js for why this replaced <input list>+<datalist>. */
.suggest-wrap { position: relative; }
.suggest-list {
  position: absolute; left: 0; right: 0; top: calc(100% + 2px);
  background: var(--panel); border: 1px solid var(--border); border-radius: 8px;
  box-shadow: 0 8px 20px rgba(0,0,0,0.15); max-height: 180px; overflow-y: auto;
  z-index: 60; /* above .modal's own z-index (50) */
}
.suggest-list[hidden] { display: none; }
.suggest-item { padding: 9px 11px; font-size: 13.5px; cursor: pointer; }
.suggest-item:hover, .suggest-item.active { background: var(--brand-light); }

@media (max-width: 800px) {
  #app { grid-template-columns: 1fr; }
  #sidebar { position: sticky; top: 0; z-index: 10; flex-direction: row; overflow-x: auto; overflow-y: visible; align-items: center; height: auto; }
  .book-list { display: flex; gap: 6px; }
  #mobile-menu-btn { display: inline-flex; align-items: center; justify-content: center; margin-left: auto; flex: 0 0 auto; font-size: 16px; padding: 6px 12px; }
  /* The footer (logout/change password/manage categories/staff/backups/recycle
     bin...) can no longer stack underneath the sidebar once the sidebar itself
     becomes a horizontal bar — it used to just be hidden outright here, which
     meant there was NO way to log out or reach any of those on a phone. It's
     now a dropdown panel instead, opened via #mobile-menu-btn (see app.js).
     position: fixed (not absolute) is deliberate: #sidebar has
     overflow-x: auto above, and per the CSS overflow spec, setting one axis
     non-visible forces the other to clip too — an absolutely-positioned child
     (whose containing block IS #sidebar) would get silently clipped to the
     bar's own height and never actually be seen. Fixed positioning's
     containing block is the viewport instead, which escapes that clipping;
     app.js sets its exact `top` (just under the bar) when it opens, since the
     bar's height isn't a fixed value. */
  .sidebar-footer {
    display: none;
    position: fixed; left: 0; right: 0; margin-top: 0;
    background: var(--panel); border: 1px solid var(--border); border-top: none;
    border-radius: 0 0 var(--radius) var(--radius);
    max-height: 70vh; overflow-y: auto;
    box-shadow: 0 10px 24px rgba(0,0,0,0.15);
    z-index: 15; padding: 12px;
  }
  .sidebar-footer.mobile-open { display: block; }
  /* The whole-sidebar collapse feature is desktop-only: on mobile the sidebar
     is already a compact horizontal bar, so hiding it entirely doesn't apply.
     Hide its controls, and force the sidebar back on even if it was left
     collapsed from a wider screen before the viewport shrank. */
  #sidebar-collapse-btn, #sidebar-reopen-btn { display: none !important; }
  #app.sidebar-collapsed #sidebar { display: flex; }
  #app.sidebar-collapsed #main { padding-left: 16px; }
  /* Keep the brand/title block at its natural size in the mobile horizontal
     bar instead of letting it get squeezed by the long book lists next to
     it (same reasoning as #mobile-menu-btn's flex: 0 0 auto below). */
  .sidebar-top { flex: 0 0 auto; }
  #main { padding: 16px; }
  /* Single column, not 2 — this app's RM amounts can run long (e.g.
     RM31,123,212.00 on a busy bank book), and splitting the already-narrow
     phone width in half left those numbers with too little room, wrapping
     awkwardly or (before the min-width:0/overflow-wrap fixes above) forcing
     the whole row wider than the screen. One column gives each card the
     full width for its number. */
  .summary-cards { grid-template-columns: 1fr; }
  /* .filters already wraps each control onto its own row at this width once
     .grow's 160px min-width is accounted for — let the search box do the
     same instead of insisting on 160px when the row has far less than that
     to give it. */
  .filters .grow { min-width: 0; }

  /* ---------- mobile book picker (#mobile-books-btn / #book-nav-panel) ----------
     Replaces picking a book by scrolling through up to three nested levels of
     tiny horizontal chips (category bar → bank sub-group → book) squeezed into
     the top bar — easy to lose your place in and slow to use with one thumb.
     #mobile-books-btn opens #book-nav-panel as a full-width dropdown instead
     (same position:fixed recipe as .sidebar-footer above, and for the same
     reason — see that rule's comment for why it has to be fixed, not
     absolute). Inside the open panel, .book-list below reverts from the
     horizontal chip row back to a plain vertical list — the exact same look
     the 240px desktop sidebar already uses, just full-width here, so this
     reuses that proven styling rather than inventing a new one. */
  #mobile-books-btn {
    display: inline-flex; align-items: center; justify-content: center;
    flex: 1 1 auto; min-width: 0; margin: 0 6px;
    font-size: 13px; padding: 6px 10px;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  #book-nav-panel {
    display: none;
    position: fixed; left: 0; right: 0; margin-top: 0;
    background: var(--panel); border: 1px solid var(--border); border-top: none;
    border-radius: 0 0 var(--radius) var(--radius);
    max-height: 70vh; overflow-y: auto;
    box-shadow: 0 10px 24px rgba(0,0,0,0.15);
    z-index: 15; padding: 12px;
  }
  #book-nav-panel.mobile-open { display: block; }
  #book-nav-panel .book-list { display: block; }

  /* ---------- mobile ledger: cards instead of a cramped scrolling table ----------
     On a ~390px phone the old table had room for maybe 2.5 columns (date, a
     clipped company name, then the sticky actions column eating what was left)
     before everything else — amount, balance, category, remark — disappeared
     off-screen to the right, needing a horizontal swipe to find. Each <td>
     already carries a data-label (the column's own localized header text, for
     the ::before caption below) and a data-field (a fixed, non-localized key
     used only here for re-ordering/emphasis — data-label's text changes with
     the UI language and can't be matched in CSS) — see renderLedger() in
     app.js. No horizontal scrolling or sticky column needed any more, since
     every field is just stacked in the card instead of laid out side by side. */
  #ledger-wrap { overflow-x: visible; border: none; background: none; }
  table.ledger { display: block; width: 100%; }
  table.ledger thead { display: none; }
  table.ledger tbody { display: block; }
  table.ledger tr {
    display: flex; flex-direction: column;
    /* table.ledger tbody tr.row-in/row-out (above, not width-scoped) still
       apply here and tint the card itself — no need to repeat them. */
    border: 1px solid var(--border); border-radius: var(--radius);
    padding: 12px 14px; margin-bottom: 10px;
  }
  table.ledger td {
    display: flex; justify-content: space-between; align-items: baseline; gap: 10px;
    padding: 3px 0; border-bottom: none; font-size: 13.5px; white-space: normal;
  }
  /* A field with nothing in it (no receipt number, no category, not yet
     reconciled...) collapses away entirely instead of showing an empty
     "标签:" line — :empty only matches when there's truly no child content,
     which an empty-string template interpolation produces exactly (the
     ::before caption is generated content and doesn't count toward it). */
  table.ledger td:empty { display: none; }
  table.ledger td::before {
    content: attr(data-label); color: var(--muted); font-size: 11.5px; flex: 0 0 auto;
  }
  /* Date + Company lead every card, unlabeled — the two things actually being
     scanned for — followed by whichever amount applies, then balance. */
  table.ledger td[data-field="date"] { order: 1; color: var(--muted); font-size: 12.5px; }
  table.ledger td[data-field="company"] { order: 2; font-weight: 600; font-size: 14.5px; }
  table.ledger td[data-field="date"]::before, table.ledger td[data-field="company"]::before { content: none; }
  table.ledger td[data-field="in"], table.ledger td[data-field="out"] { order: 3; font-size: 16px; font-weight: 700; }
  table.ledger td[data-field="balance"] { order: 4; }
  table.ledger td[data-field="source"],
  table.ledger td[data-field="detail"],
  table.ledger td[data-field="receipt"],
  table.ledger td[data-field="transfer"],
  table.ledger td[data-field="category"],
  table.ledger td[data-field="reconciled"],
  table.ledger td[data-field="remark"] { order: 5; }
  /* Actions move from a sticky right-hand column (meaningless without
     horizontal scroll any more) to a button row at the card's bottom. */
  table.ledger td.sticky-col {
    position: static; box-shadow: none; background: transparent !important;
    order: 99; display: flex; flex-wrap: wrap; gap: 6px; justify-content: flex-start;
    margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--border);
  }
  table.ledger td.sticky-col::before { content: none; }
}

/* ---------- Monthly statement ---------- */
.statement-backdrop {
  position: fixed; inset: 0; background: rgba(0,0,0,0.45);
  display: flex; align-items: flex-start; justify-content: center;
  overflow-y: auto; z-index: 60; padding: 30px 16px;
}
.statement-sheet {
  background: #fff; border-radius: var(--radius); padding: 32px 36px;
  width: 760px; max-width: 100%;
}
.statement-toolbar { display: flex; justify-content: flex-end; gap: 8px; margin-bottom: 16px; }
.statement-head { text-align: center; margin-bottom: 22px; }
.statement-head h2 { margin: 0 0 4px; font-size: 20px; }
.statement-head .sub { color: var(--muted); font-size: 13px; }
.statement-summary { display: flex; justify-content: space-between; gap: 12px; margin: 18px 0; flex-wrap: wrap; }
.statement-summary div { font-size: 13px; }
.statement-summary .amt { font-weight: 700; font-size: 15px; display: block; margin-top: 2px; }
table.statement-table { width: 100%; border-collapse: collapse; margin-top: 8px; }
table.statement-table th, table.statement-table td { padding: 6px 8px; border-bottom: 1px solid var(--border); font-size: 12.5px; text-align: left; }
table.statement-table td.num, table.statement-table th.num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
table.statement-table td.in { color: var(--in-color); }
table.statement-table td.out { color: var(--out-color); }
.statement-catsum { margin-top: 22px; }
.statement-catsum h4 { margin: 0 0 8px; font-size: 13px; color: var(--muted); }
.statement-catsum table { width: 100%; border-collapse: collapse; }
.statement-catsum td { padding: 4px 8px; font-size: 12.5px; border-bottom: 1px dashed var(--border); }
.statement-catsum td.num { text-align: right; font-variant-numeric: tabular-nums; }

/* ---------- Attachment PDF viewer ---------- */
/* Appended straight to <body> (not #modal-root) so it can open on top of an
   already-open tx-edit modal (z-index 50) without closing it underneath. */
.pdf-viewer-backdrop {
  position: fixed; inset: 0; background: rgba(0,0,0,0.55);
  display: flex; align-items: center; justify-content: center; z-index: 70;
  padding: 24px;
}
.pdf-viewer-sheet {
  background: var(--panel); border-radius: var(--radius);
  width: min(900px, 96vw); height: min(90vh, 1100px);
  display: flex; flex-direction: column; overflow: hidden;
  box-shadow: 0 12px 40px rgba(0,0,0,0.3);
}
.pdf-viewer-toolbar {
  display: flex; justify-content: flex-end; align-items: center; gap: 8px;
  padding: 10px 14px; border-bottom: 1px solid var(--border); flex: 0 0 auto;
}
.pdf-viewer-frame { flex: 1 1 auto; width: 100%; border: none; background: #525659; }
.pdf-viewer-frame.image-view { object-fit: contain; height: 100%; }

@media print {
  /* #login-screen and #app are the ONLY other top-level elements in <body> (besides
     #modal-root, which holds the statement) — hiding them with display:none removes
     them from layout entirely. The previous approach used visibility:hidden on
     "body *", which keeps every hidden element's box taking up space, so the real
     printed content ended up pushed dozens of blank pages down. */
  #login-screen, #app { display: none !important; }
  .statement-backdrop {
    position: static; inset: auto; background: none; padding: 0;
    display: block; overflow: visible; height: auto; min-height: 0;
  }
  .statement-sheet { width: 100%; max-width: 100%; margin: 0; box-shadow: none; padding: 0; }
  .statement-toolbar { display: none; }
}
