/* ---- nav ---- */
.nav-item {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 7px 10px; border-radius: 0; color: var(--ink);
  margin-bottom: 1px; text-align: left;
  font-family: var(--mono); font-weight: 700; font-size: 11.5px;
  letter-spacing: .07em; text-transform: uppercase;
}
.nav-item:hover { background: var(--hover); }
/* Active nav = photocopy negative: solid ink block, paper text. */
.nav-item.active { background: var(--ink); color: var(--bg); font-weight: 700; }
.nav-item .badge { margin-left: auto; }
/* Badge on the active (ink-block) item inverts, or it vanishes ink-on-ink. */
.nav-item.active .badge { background: var(--bg); color: var(--ink); }
.nav-section { font-family: var(--mono); font-weight: 700; font-size: 10px; text-transform: uppercase; letter-spacing: .14em; color: var(--faint); margin: 16px 10px 5px; }
.nav-brand {
  display: block; padding: 2px 10px 12px;
  font-family: var(--display); font-weight: 900; font-size: 19px;
  text-transform: uppercase; line-height: 1;
}
.nav-brand .dot { display: none; }
.nav-brand .brand-sub {
  display: block; margin-top: 5px;
  font-family: var(--mono); font-weight: 700; font-size: 8.5px;
  letter-spacing: .16em; color: var(--mut);
}
.nav-user { display: flex; align-items: center; gap: 8px; padding: 10px 10px 8px; margin-top: 10px; border-top: 1px solid var(--bd); font-family: var(--mono); font-size: 11px; color: var(--mut); }
.nav-user button { color: var(--accent); font-weight: 700; text-transform: uppercase; letter-spacing: .05em; }

.tab {
  flex: 1; min-width: 62px; display: flex; flex-direction: column; align-items: center; gap: 2px;
  padding: 7px 4px 6px; color: var(--mut); position: relative;
  font-family: var(--mono); font-weight: 700; font-size: 9px;
  letter-spacing: .06em; text-transform: uppercase;
}
.tab.active { color: var(--ink); box-shadow: inset 0 3px 0 var(--accent); }
.tab svg.icon { width: 20px; height: 20px; }
.tab .badge { position: absolute; top: 2px; left: calc(50% + 8px); }

/* ---- badges & chips ---- */
.badge {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 17px; height: 17px; padding: 0 5px; border-radius: 0;
  background: var(--ink); color: var(--bg);
  font-family: var(--mono); font-size: 10.5px; font-weight: 700;
}
.badge.warn { background: var(--warn); color: #f6f2e7; }
.badge.amber { background: var(--amber); color: #f6f2e7; }
.badge.muted { background: var(--chip-bg); color: var(--mut); }
.chip {
  display: inline-flex; align-items: center; gap: 5px;
  background: var(--chip-bg); border: 1px solid var(--bd); border-radius: 0;
  padding: 1px 8px; font-family: var(--mono); font-size: 11px; color: var(--mut);
  white-space: nowrap;
}
.chip.accent { color: var(--accent); }
.chip.warn { color: var(--warn); }
.chip.amber { color: var(--amber); }

/* Status pills reprinted as flat ink stamps: mono caps, hairline box,
   no pastel background tints. Kept un-rotated — these sit inside rows
   and tables, and data stays straight (the tilt budget lives on .stamp). */
.status-badge {
  font-family: var(--mono); font-weight: 700; font-size: 9.5px;
  letter-spacing: .08em; text-transform: uppercase;
  padding: 1px 6px; border: 1.5px solid currentColor; border-radius: 0;
  white-space: nowrap; display: inline-block; background: none;
}
.st-pending_review, .st-queued, .st-pending { color: var(--mut); }
.st-confirmed, .st-approved, .st-verified, .st-promoted, .st-resolved, .st-active { color: var(--ok); }
.st-error, .st-promotion_failed, .st-dismissed, .st-rejected { color: var(--warn); }
.st-undone, .st-expired, .st-superseded, .st-archived { color: var(--faint); }
.st-parsed { color: var(--amber); }

/* Confidence reads as bracketed mono — [92%] — no box needed. */
.conf-badge {
  font-family: var(--mono); font-weight: 700; font-size: 11px;
  border: 0; padding: 0; color: var(--mut); white-space: nowrap;
}
.conf-badge::before { content: "["; }
.conf-badge::after { content: "]"; }
.conf-high { color: var(--ok); }
.conf-mid { color: var(--amber); }
.conf-low { color: var(--over); }

.provenance-badge { font-family: var(--mono); font-size: 10px; letter-spacing: .05em; text-transform: uppercase; padding: 1px 6px; border-radius: 0; background: var(--chip-bg); color: var(--mut); white-space: nowrap; }
.provenance-badge.manual { color: var(--accent); }
.provenance-badge.learned, .provenance-badge.learned_from_correction { color: var(--amber); }
.provenance-badge.seeded, .provenance-badge.seeded_from_backfill { color: var(--mut); }
/* Rule-set consolidation (2026-07-05): distinct color from 'seeded' so the
   post-consolidation substring-cluster rules read differently from the
   (now largely retired) exact-per-merchant seeded rules. */
.provenance-badge.consolidated { color: var(--ok); }

/* ---- buttons ----
   The press: buttons sit on a hard shadow and physically collapse into
   it on :active (120ms). Keyboard-triggered actions animate nothing. */
.btn {
  display: inline-flex; align-items: center; gap: 6px;
  border: 2px solid var(--bd-strong); border-radius: 0;
  padding: 5px 12px; background: var(--surface); color: var(--ink);
  font-family: var(--mono); font-weight: 700; font-size: 12px;
  letter-spacing: .05em; text-transform: uppercase; white-space: nowrap;
  box-shadow: var(--shadow-sm);
  transition: transform 120ms var(--ease), box-shadow 120ms var(--ease);
}
@media (hover: hover) and (pointer: fine) {
  .btn:hover { transform: translate(-1px, -1px); box-shadow: var(--shadow); }
}
.btn:active { transform: translate(2px, 2px); box-shadow: 0 0 0 var(--shadow-ink); }
.btn.primary { background: var(--accent); border-color: var(--bd-strong); color: var(--accent-ink); }
.btn.danger { color: var(--over); border-color: var(--over); }
.btn.small { padding: 3px 9px; font-size: 10.5px; }
.btn:disabled { opacity: .45; cursor: default; box-shadow: none; transform: none; }
.iconbtn { display: inline-flex; padding: 6px; border-radius: 0; color: var(--mut); }
.iconbtn:hover { background: var(--hover); color: var(--ink); }
/* Caution variant for the review queue's per-row "Delete as duplicate"
   button (a destructive action, so it reads danger-colored, distinct from
   the neutral confirm check beside it). */
.iconbtn.danger { color: var(--over); }
.iconbtn.danger:hover { background: var(--over); color: #f6f2e7; }
/* Disabled icon button (e.g. per-row "Delete as duplicate" when the near-dup
   partner isn't confirmed yet — see review-queue.js): inert + faded, and
   the danger hover is suppressed so it can't look actionable. */
.iconbtn:disabled { opacity: .38; cursor: not-allowed; }
.iconbtn.danger:disabled:hover { background: none; color: var(--over); }
.btn-row { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }

/* ---- inputs ----
   Hairline at rest, ink on focus. Labels are mono caps above the field. */
.input, select.input, textarea.input {
  width: 100%; background: var(--surface); border: 2px solid var(--bd);
  border-radius: 0; padding: 6px 10px;
}
.input:focus, select.input:focus, textarea.input:focus { border-color: var(--bd-strong); outline: none; }
textarea.input { resize: vertical; min-height: 64px; }
select.input { padding: 5px 8px; }
label.field { display: block; margin-bottom: 10px; }
label.field > span { display: block; font-family: var(--mono); font-weight: 700; font-size: 10px; letter-spacing: .1em; text-transform: uppercase; color: var(--mut); margin-bottom: 4px; }
.field-row { display: flex; gap: 10px; flex-wrap: wrap; }
.field-row > label.field { flex: 1; min-width: 140px; }
fieldset.field-group { border: 2px solid var(--bd); border-radius: 0; padding: 10px 12px; margin: 0 0 12px; }
fieldset.field-group legend { font-family: var(--mono); font-weight: 700; font-size: 10px; color: var(--mut); padding: 0 6px; text-transform: uppercase; letter-spacing: .1em; }
.form-error { color: var(--warn); font-size: 12.5px; margin: 6px 0; }
.checkline { display: flex; align-items: center; gap: 7px; padding: 4px 0; }

/* ---- cards / rows (generic list-row shape shared across screens) ---- */
.row-card {
  display: flex; align-items: flex-start; gap: 10px;
  background: var(--card); border: 1px solid var(--bd);
  border-radius: 0; padding: 9px 12px; margin-bottom: 6px;
  position: relative; transition: background .12s;
}
.row-card:hover { background: var(--hover); }
.row-card.selected { background: var(--sel); border-color: var(--bd-strong); }
.row-card.cursor { outline: 2px solid var(--ink); outline-offset: 1px; }
/* Full rust border, not a side stripe (house rule: no accent stripes). */
.row-card.needs-repair { border: 1.5px solid var(--warn); }

/* Plain data tables (transactions/categories/merchants/rules/imports):
   ledger lines — a 2px ink rule under the header, hairlines between rows. */
table.data-table { width: 100%; font-size: 13px; }
table.data-table th, table.data-table td { padding: 7px 10px; border-bottom: 1px solid var(--bd); text-align: left; vertical-align: middle; }
table.data-table th {
  color: var(--mut); font-family: var(--mono); font-weight: 700; font-size: 10px;
  text-transform: uppercase; letter-spacing: .1em; white-space: nowrap;
  position: sticky; top: 0; background: var(--surface);
  border-bottom: 2px solid var(--bd-strong);
}
table.data-table tbody tr:hover { background: var(--hover); cursor: pointer; }
table.data-table tbody tr.cursor { outline: 2px solid var(--ink); outline-offset: -2px; }
table.data-table tbody tr.selected { background: var(--sel); }
table.data-table td.num, table.data-table th.num { text-align: right; font-variant-numeric: tabular-nums; }
table.data-table td.num { font-family: var(--mono); font-size: 12.5px; }
table.data-table th.sortable { cursor: pointer; user-select: none; }
table.data-table th.check-col, table.data-table td.check-col { width: 32px; padding-right: 0; }
table.data-table th.sortable .arrow { margin-left: 3px; opacity: .6; }
/* Account column (Transactions): full account names run long ("CommBank
   Ultimate Awards") — cap and ellipsize so Merchant keeps its width; the
   full name rides in the cell's title attribute. The max-width is what
   makes text-overflow actually engage inside a table cell. */
table.data-table td.account-col {
  max-width: 150px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* The extra Account column would crush Merchant at the shared 640px
   .table-scroll minimum — widen just this table; narrow viewports scroll. */
#txn-table-wrap table.data-table { min-width: 780px; }

/* ---- ledger summary block (Overview) ----
   One ruled column: mono labels left, money right, hairlines between
   lines, a 2px ink rule then the big net figure over a double rule —
   the accountant's closing line. Replaces the old stat-card grid. */
.ledger-block {
  background: var(--card); border: 2px solid var(--bd-strong);
  box-shadow: var(--shadow); padding: 14px 18px 12px;
  max-width: 560px; margin-bottom: 24px;
}
.ledger-block .lb-head {
  display: flex; justify-content: space-between; align-items: center; gap: 10px;
  padding-bottom: 10px; border-bottom: 2px solid var(--bd-strong);
}
.ledger-line {
  display: flex; justify-content: space-between; align-items: baseline; gap: 12px;
  padding: 8px 0; border-bottom: 1px solid var(--bd);
}
.ledger-line .ll-label, .ledger-total .ll-label {
  font-family: var(--mono); font-weight: 700; font-size: 10.5px;
  letter-spacing: .09em; text-transform: uppercase; color: var(--mut);
}
.ledger-line .ll-amt { font-family: var(--mono); font-weight: 700; font-size: 14px; }
.ledger-total {
  display: flex; justify-content: space-between; align-items: baseline; gap: 12px;
  padding: 10px 0 8px; border-bottom: 4px double var(--bd-strong);
}
.ledger-total .lt-amt {
  font-family: var(--display); font-size: clamp(22px, 4vw, 30px); line-height: 1;
  letter-spacing: -.01em;
}
.lb-note { font-family: var(--mono); font-size: 10.5px; padding-top: 8px; }

/* ---- toasts: ink slips (photocopy negative), deadpan ---- */
#toasts { position: fixed; bottom: 18px; left: 50%; transform: translateX(-50%); z-index: 200; display: flex; flex-direction: column; gap: 8px; align-items: center; }
@media (max-width: 900px) { #toasts { bottom: calc(74px + env(safe-area-inset-bottom)); } }
.toast {
  background: var(--ink); color: var(--bg); border-radius: 0;
  padding: 9px 16px; display: flex; gap: 14px; align-items: center;
  animation: toast-in .18s var(--ease); max-width: min(92vw, 480px);
  font-family: var(--mono); font-size: 12.5px;
}
.toast button { color: inherit; font-weight: 700; text-decoration: underline; text-underline-offset: 2px; text-transform: uppercase; }
@keyframes toast-in { from { opacity: 0; transform: translateY(8px); } }
.toast.err { background: var(--over); color: #f6f2e7; }
.toast.err button { color: #f6f2e7; }

/* ---- panels / sheets (detail drawer, forms) ---- */
#overlay:not(:empty) { position: fixed; inset: 0; z-index: 100; }
.scrim { position: absolute; inset: 0; background: rgba(12, 10, 7, .45); animation: fade .15s; }
@keyframes fade { from { opacity: 0; } }
.panel {
  position: absolute; top: 0; right: 0; bottom: 0; width: min(480px, 100vw);
  background: var(--surface); border-left: 2px solid var(--bd-strong);
  display: flex; flex-direction: column;
  animation: slide-in .18s var(--ease);
}
@keyframes slide-in { from { transform: translateX(40px); opacity: .4; } }
@media (max-width: 768px) {
  .panel { width: 100vw; border-left: 0; }
}
.panel-h { display: flex; align-items: center; gap: 8px; padding: 12px 14px; border-bottom: 2px solid var(--bd-strong); }
.panel-h h3 { margin: 0; font-family: var(--display); font-weight: 900; font-size: 15px; text-transform: uppercase; letter-spacing: .01em; flex: 1; }
.panel-b { flex: 1; overflow-y: auto; padding: 14px 16px 30px; }
.panel-f { display: flex; gap: 8px; padding: 10px 14px calc(10px + env(safe-area-inset-bottom)); border-top: 2px solid var(--bd-strong); }

.f-grid { display: grid; grid-template-columns: 110px 1fr; gap: 9px 10px; align-items: start; margin: 12px 0; }
.f-grid label { color: var(--mut); font-family: var(--mono); font-weight: 700; font-size: 10px; letter-spacing: .08em; text-transform: uppercase; padding-top: 5px; }
.f-grid .f-val { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; }
/* Transaction detail panel's Amazon block (Phase 1, amazon-invlinks): the
   generic .f-val is a wrapping flex ROW (built for a single line of inline
   chips) — this modifier stacks its lines (order/account, matched USD
   amount, order link) vertically instead. */
.f-val.amazon-block { flex-direction: column; align-items: flex-start; gap: 4px; }

.fence { border: 2px solid var(--bd); border-radius: 0; margin: 8px 0; overflow: hidden; }
.fence > summary { padding: 7px 10px; background: var(--chip-bg); cursor: pointer; font-family: var(--mono); font-weight: 700; font-size: 11px; letter-spacing: .06em; text-transform: uppercase; color: var(--mut); list-style: none; display: flex; gap: 6px; align-items: center; }
.fence > div { padding: 9px 11px; white-space: pre-wrap; font-size: 12.5px; }

.timeline { margin: 10px 0 0; padding: 0; list-style: none; }
.timeline li { display: flex; gap: 9px; padding: 6px 0; border-bottom: 1px solid var(--bd); font-size: 12.5px; }
.timeline li:last-child { border-bottom: 0; }
.timeline .tl-dot { width: 7px; height: 7px; border-radius: 0; background: var(--bd-strong); margin-top: 6px; flex: 0 0 auto; }
.timeline .tl-body { flex: 1; min-width: 0; word-break: break-word; }
.timeline .tl-meta { color: var(--faint); font-family: var(--mono); font-size: 10.5px; margin-bottom: 1px; }

/* ---- modal ---- */
.modal-scrim { position: fixed; inset: 0; z-index: 150; background: rgba(12,10,7,.45); display: flex; align-items: center; justify-content: center; animation: fade .15s; padding: 16px; }
.modal {
  background: var(--surface); border: 2px solid var(--bd-strong); border-radius: 0;
  box-shadow: var(--shadow-lg); padding: 18px 20px; width: min(480px, 100%); max-height: 84vh; overflow-y: auto;
}
.modal h3 { margin: 0 0 10px; font-family: var(--display); font-weight: 900; font-size: 16px; text-transform: uppercase; }
.kbd-grid { display: grid; grid-template-columns: auto 1fr; gap: 7px 16px; margin-top: 10px; }
kbd {
  font-family: var(--mono); font-size: 11px; background: var(--chip-bg);
  border: 1.5px solid var(--bd-strong); border-bottom-width: 3px; border-radius: 0; padding: 1px 6px;
}

/* ---- bulk select bar ----
   Floating bar FIXED to the viewport bottom (owner-approved 2026-07-05:
   selecting rows deep in a 200-row page must not require scrolling back
   up to act). Centered, capped width. z-index 25 keeps it above table
   rows/sticky headers and BELOW panel/modal chrome (100+/150) — same
   layer it occupied as a sticky top bar. Opaque card ground + 2px ink
   border + hard shadow (the old translucent tint was unreadable floating
   over row content). The category picker inside it auto-flips its
   dropdown upward down here — see category-picker.js's drop-up logic. */
.bulkbar {
  position: fixed; bottom: 14px; left: 50%; transform: translateX(-50%);
  z-index: 25; display: flex; gap: 8px; align-items: center;
  background: var(--card);
  border: 2px solid var(--bd-strong); border-radius: 0;
  padding: 7px 12px; flex-wrap: wrap;
  box-shadow: var(--shadow);
  max-width: min(92vw, 720px);
}
/* While the fixed bar is visible, review-queue.js puts this on the pager
   (the view's last in-flow element) so the page can always scroll the
   final rows + pager clear of the bar — without it the bar would
   permanently cover the end of the list. 120px ≈ a TWO-line wrapped bar
   (count + picker + button wrap below ~600px widths — Opus r7 finding
   #4) + the 14px bottom offset + breathing room. */
.bulkbar-clearance { padding-bottom: 120px; }
/* Mobile (Opus r7 finding #2): the ≤900px tab bar is fixed bottom at
   z-index 40 — above the bulk bar's 25 — so without an offset the bar's
   lower half (the Confirm button) sat BEHIND it. Mirror the #toasts
   accommodation (tabbar height + safe-area inset), and grow the
   clearance to cover the raised, wrapped pill. */
@media (max-width: 900px) {
  .bulkbar { bottom: calc(74px + env(safe-area-inset-bottom)); }
  .bulkbar-clearance { padding-bottom: calc(190px + env(safe-area-inset-bottom)); }
}
/* The bulk bar's category picker (review-queue.js): compact fixed width so
   it sits inline between the count and the Confirm button instead of
   stretching across the bar (.cat-picker-closed is width:100% of its
   root). The picker's dropdown list already outranks this bar — see
   .cat-picker-list's z-index comment below. */
.bulkbar-cat-picker { width: 240px; flex: 0 0 auto; }

/* ---- category tree (reads as a zine table of contents) ---- */
.tree { list-style: none; margin: 0; padding: 0; }
.tree-node { margin: 1px 0; }
.tree-row {
  display: flex; align-items: center; gap: 7px; padding: 6px 8px; border-radius: 0;
  border-bottom: 1px solid var(--bd);
}
.tree-row:hover { background: var(--hover); }
.tree-row .twisty { width: 16px; color: var(--faint); flex: 0 0 auto; text-align: center; font-family: var(--mono); }
.tree-row .name { flex: 1; min-width: 0; }
.tree-row .name input.input { padding: 2px 6px; }
.tree-row .count { color: var(--faint); font-family: var(--mono); font-size: 11px; }
.tree-children { padding-left: 22px; }
.tree-node.orphan .tree-row { color: var(--mut); }

/* ---- review queue specific ---- */
.review-row { display: flex; gap: 12px; align-items: flex-start; }
.review-row .rr-main { flex: 1; min-width: 0; }
.review-row .rr-merchant { font-weight: 600; }
.review-row .rr-meta { display: flex; flex-wrap: wrap; gap: 4px 10px; margin-top: 3px; font-size: 12px; color: var(--mut); align-items: center; }
.review-row .rr-suggestion { margin-top: 6px; display: flex; align-items: center; gap: 6px; font-size: 12.5px; }
.review-row .rr-amt { flex: 0 0 auto; text-align: right; min-width: 84px; }
.review-row .rr-actions { flex: 0 0 auto; display: flex; gap: 4px; }
.near-dup-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-top: 8px; }
@media (max-width: 640px) { .near-dup-pair { grid-template-columns: 1fr; } }
.near-dup-card { border: 1.5px dashed var(--bd-strong); border-radius: 0; padding: 8px 10px; font-size: 12.5px; background: var(--chip-bg); }
.near-dup-card .ndc-label { color: var(--mut); font-family: var(--mono); font-weight: 700; text-transform: uppercase; font-size: 9.5px; letter-spacing: .1em; margin-bottom: 4px; }
/* Amazon investigation link (Phase 1, amazon-invlinks): a compact
   click-through affordance under the suggestion cell for review-queue rows
   that DO have a live Amazon match but show no category suggestion. `a`
   already renders --accent (base.css's plain `a` rule), so no color
   override is needed here — just sizing/spacing consistent with
   .rr-suggestion. Reused as-is by the transaction detail panel's Amazon
   block (views/transactions.js). The plain-text fallback (rendered when
   the stored URL fails the https://www.amazon.com/ prefix safety check)
   reuses .mut for its muted color instead of this class. */
.rr-amazon-line { margin-top: 4px; font-size: 12.5px; }
.rr-amazon-link { display: inline-flex; align-items: center; gap: 4px; }
.rr-amazon-link svg.icon { width: 13px; height: 13px; vertical-align: -2px; }
/* (.category-palette rules removed 2026-07-05 with the review queue's 1-9
   quick-assign palette — owner decision; no other view used them.) */

/* ---- brain candidate cards ---- */
.candidate-card .cc-preview { font-family: var(--mono); font-size: 12px; white-space: pre-wrap; background: var(--code-bg); border-radius: 0; padding: 9px 11px; margin-top: 8px; word-break: break-word; }
.candidate-card .cc-meta { display: flex; flex-wrap: wrap; gap: 4px 10px; font-size: 12px; color: var(--mut); margin-top: 4px; }
.callout { display: flex; gap: 8px; align-items: flex-start; padding: 9px 12px; border-radius: 0; font-size: 13px; margin: 8px 0; }
.callout.warn { color: var(--warn); border: 2px solid var(--warn); }
.callout.info { color: var(--mut); border: 2px solid var(--bd); }
.callout.amber { color: var(--amber); border: 2px solid var(--amber); }

/* ---- tabs (status filter, brain candidate status, imports history) ---- */
.tabstrip { display: flex; gap: 4px; border-bottom: 2px solid var(--bd-strong); margin-bottom: 14px; overflow-x: auto; }
.tabstrip button {
  padding: 7px 12px; font-family: var(--mono); font-weight: 700; font-size: 11px;
  letter-spacing: .06em; text-transform: uppercase;
  color: var(--mut); border-bottom: 3px solid transparent; white-space: nowrap;
}
.tabstrip button.active { color: var(--ink); border-bottom-color: var(--accent); }
.tabstrip button .badge { margin-left: 5px; }

/* ---- filter bar ---- */
.filterbar { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; margin-bottom: 14px; }
.filterbar .input, .filterbar select.input { width: auto; min-width: 110px; }
.filterbar .search-input { flex: 1; min-width: 160px; }

/* ---- pagination ---- */
.pager { display: flex; align-items: center; gap: 10px; justify-content: center; padding: 14px 0; font-family: var(--mono); font-size: 12px; color: var(--mut); }

/* ---- dropzone (imports) ---- */
.dropzone {
  border: 2px dashed var(--bd-strong); border-radius: 0; padding: 34px 16px;
  text-align: center; color: var(--mut); transition: border-color .12s, background .12s;
  font-family: var(--mono); font-weight: 700; font-size: 12px;
  letter-spacing: .08em; text-transform: uppercase;
}
.dropzone.dragover { border-color: var(--accent); background: var(--sel); color: var(--ink); }
.dropzone input[type="file"] { display: none; }

/* ---- reports ---- */
.chart-wrap { position: relative; height: 320px; margin: 10px 0 20px; }
.rollup-parent-row { font-weight: 700; cursor: pointer; }
.rollup-parent-row .twisty { display: inline-block; width: 14px; }
.rollup-child-row td:first-child { padding-left: 26px; color: var(--mut); }

/* ---- settings ---- */
.info-box { display: grid; grid-template-columns: max-content 1fr; gap: 5px 14px; font-size: 12.5px; background: var(--chip-bg); border: 1px solid var(--bd); border-radius: 0; padding: 12px 14px; }
.info-box dt { color: var(--mut); }
.info-box dd { margin: 0; font-family: var(--mono); }
.token-reveal { background: var(--code-bg); border: 1px solid var(--bd); border-radius: 0; padding: 10px; font-family: var(--mono); font-size: 12.5px; word-break: break-all; user-select: all; }

/* ---- category picker (search-as-you-type <select> replacement) ----
   See static/js/components/category-picker.js's module doc comment for
   the native-<select> type-to-find problem this replaces. Closed state
   reuses .input styling (a plain-looking button); open state swaps in a
   text input + absolutely-positioned dropdown list anchored to this
   control (position: relative on the root). */
.cat-picker { position: relative; }
.cat-picker-closed {
  display: block; width: 100%; text-align: left; cursor: pointer;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.cat-picker-closed.faint { color: var(--faint); }
.cat-picker.open .cat-picker-closed { display: none; }
.cat-picker-inputwrap { display: none; }
.cat-picker.open .cat-picker-inputwrap { display: block; }
.cat-picker-input { position: relative; z-index: 1; }
.cat-picker-list {
  display: none; list-style: none; margin: 0; padding: 4px;
  position: absolute; top: calc(100% + 3px); left: 0; right: 0;
  max-height: calc(12 * 30px); overflow-y: auto;
  background: var(--surface); border: 2px solid var(--bd-strong);
  border-radius: 0; box-shadow: var(--shadow);
  z-index: 30; /* above table rows/sticky headers (table.data-table th is
                  position: sticky with no explicit z-index, i.e. z-index:
                  auto/0 in the same stacking context) and above the
                  bulkbar (z-index: 25) so a picker opened in a table row
                  or the bulk-categorize bar is never clipped underneath
                  either. Below .panel/.modal-scrim's 100+/150 — a picker
                  living INSIDE a panel is a descendant of that panel, so
                  it paints on top of the panel's own content regardless
                  of the panel's stacking context, and never needs to
                  outrank the panel/modal chrome itself. */
}
.cat-picker.open .cat-picker-list { display: block; }
/* Auto-flip (2026-07-05): category-picker.js toggles .drop-up on the root
   ONCE per open when the space below the control can't fit the list's
   max-height but the space above fits it better — the list then opens
   UPWARD. Serves the fixed bottom bulk bar (always flips up there) and
   pickers in the last rows of tall tables/panels that used to clip. */
.cat-picker.drop-up .cat-picker-list { top: auto; bottom: calc(100% + 3px); }
.cat-picker-row {
  padding: 5px 8px; border-radius: 0; font-size: 13px;
  cursor: pointer; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.cat-picker-row.child { padding-left: 20px; color: var(--mut); }
.cat-picker-row.dim { color: var(--faint); }
.cat-picker-row.active { background: var(--ink); color: var(--bg); }
.cat-picker-row.empty-row { color: var(--mut); border-bottom: 1px solid var(--bd); margin-bottom: 3px; padding-bottom: 7px; }
.cat-picker-row mark { background: var(--sel); color: inherit; border-radius: 0; padding: 0 1px; }
.cat-picker-row.active mark { background: none; color: inherit; }
.cat-picker-sep { color: var(--faint); }
.cat-picker-empty { padding: 8px; font-size: 12.5px; color: var(--mut); }

/* ---- login page ---- */
.login-wrap { min-height: 100%; display: flex; align-items: center; justify-content: center; padding: 20px; }
.login-card { width: min(360px, 100%); background: var(--card); border: 2px solid var(--bd-strong); border-radius: 0; padding: 28px 26px; box-shadow: var(--shadow-lg); }
.login-card .nav-brand { justify-content: center; text-align: center; padding: 0 0 18px; font-size: 24px; }

/* ---- topbar contents (2026-07-26 — the bar finally earns its 40px) ---- */
.topbar-title {
  font-family: var(--mono); font-weight: 700; font-size: 12px;
  letter-spacing: .1em; text-transform: uppercase; color: var(--ink);
}
.topbar-theme { display: inline-flex; align-items: center; gap: 6px; }
.topbar-theme svg.icon { width: 14px; height: 14px; }

/* ---- More bottom sheet (mobile nav, 2026-07-26) ---- */
#more-sheet .more-sheet-scrim {
  position: fixed; inset: 0; z-index: 60; background: color-mix(in srgb, var(--shadow-ink) 45%, transparent);
}
#more-sheet .more-sheet-panel {
  position: fixed; left: 0; right: 0; z-index: 61;
  bottom: calc(44px + env(safe-area-inset-bottom)); /* sits ABOVE the tab bar, which stays visible/tappable */
  background: var(--surface);
  border-top: 2px solid var(--bd-strong); border-bottom: 2px solid var(--bd-strong);
  box-shadow: 0 -3px 0 var(--shadow-ink);
  display: grid; grid-template-columns: 1fr 1fr;
}
#more-sheet .more-sheet-item {
  display: flex; align-items: center; gap: 10px; position: relative;
  padding: 13px 16px; color: var(--ink);
  font-family: var(--mono); font-weight: 700; font-size: 11px;
  letter-spacing: .08em; text-transform: uppercase;
  border-bottom: 1px solid var(--bd);
}
#more-sheet .more-sheet-item:nth-child(odd) { border-right: 1px solid var(--bd); }
#more-sheet .more-sheet-item svg.icon { width: 18px; height: 18px; }
#more-sheet .more-sheet-item.active { background: var(--ink); color: var(--bg); }
#more-sheet .more-sheet-item .badge { position: static; margin-left: auto; }

/* The More tab is a <button> now (it opens the sheet instead of navigating) — inherit the .tab look, kill button chrome. */
button.tab-more { background: none; border: 0; cursor: pointer; }
