/* ==========================================================================
   PostWarden — design system
   Grounded in the artifact this software replaces: columnar ledger paper.
   Pale green stock, green ruling, the red vertical rule beside money
   columns, tabular monospaced figures, and the accountant's double-rule
   under balanced totals.
   ========================================================================== */
:root {
  /* Ledger — pale green ledger stock. The default; also the palette
     every other theme is a deliberate departure from. */
  --paper:        #f7faf4;
  --paper-deep:   #eff5ea;   /* tinted money columns */
  --surface:      #ffffff;   /* cards, inputs — the "clean sheet" on top of paper */
  --ink:          #1d2b22;
  --ink-soft:     #55665c;
  --rule:         #ccdcc8;   /* ledger ruling */
  --rule-strong:  #8fae8b;
  --red:          #a63c2e;   /* the red rule; imbalance; credit accent */
  --ok:           #2f6b45;
  --focus:        #3e6f8e;
  --accent:       var(--ink);
  --accent-hover: #2c4034;
  --accent-text:  var(--paper);
  --serif: "Iowan Old Style", "Palatino Linotype", "Book Antiqua", Georgia, serif;
  --sans: system-ui, -apple-system, "Segoe UI", sans-serif;
  --mono: ui-monospace, "SF Mono", "Cascadia Code", Consolas, Menlo, monospace;
}

/* Midnight — a night ledger: dark stock, brass-stamp accent. */
:root[data-theme="midnight"] {
  --paper:        #10140f;
  --paper-deep:   #171d15;
  --surface:      #1b221a;
  --ink:          #ecefe8;
  --ink-soft:     #9caa96;
  --rule:         #262f24;
  --rule-strong:  #435041;
  --red:          #e2685a;
  --ok:           #6fcf8e;
  --focus:        #7fb0d9;
  --accent:       #c9a227;
  --accent-hover: #ddb939;
  --accent-text:  #14180f;
}

/* Parchment — warm, aged sepia stock. */
:root[data-theme="parchment"] {
  --paper:        #f5ecd9;
  --paper-deep:   #ecdfc2;
  --surface:      #fffaf1;
  --ink:          #3b2c1f;
  --ink-soft:     #7a6650;
  --rule:         #ddc9a3;
  --rule-strong:  #b89b6b;
  --red:          #9c3b2c;
  --ok:           #4c6b3a;
  --focus:        #4a6f8e;
  --accent:       var(--ink);
  --accent-hover: #54402c;
  --accent-text:  var(--paper);
}

/* Slate — cool, contemporary, least "paper" of the four. */
:root[data-theme="slate"] {
  --paper:        #f4f6f8;
  --paper-deep:   #e9edf1;
  --surface:      #ffffff;
  --ink:          #1b2430;
  --ink-soft:     #5b6b7c;
  --rule:         #d6dde3;
  --rule-strong:  #aebbc7;
  --red:          #b3392c;
  --ok:           #1f7a52;
  --focus:        #2f6fb0;
  --accent:       var(--ink);
  --accent-hover: #2c3a4c;
  --accent-text:  var(--paper);
}

/* Ink — a fountain-pen ledger: indigo ink on pale blue-white stock. */
:root[data-theme="ink"] {
  --paper:        #f5f6fb;
  --paper-deep:   #eaecf7;
  --surface:      #ffffff;
  --ink:          #1f2247;
  --ink-soft:     #5b5f8a;
  --rule:         #d7d9ec;
  --rule-strong:  #a9aed6;
  --red:          #a13c4a;
  --ok:           #2f6b5e;
  --focus:        #3e5fa6;
  --accent:       var(--ink);
  --accent-hover: #33366b;
  --accent-text:  var(--paper);
}

/* Terracotta — warm rust-and-clay stock, a hotter warmth than Parchment. */
:root[data-theme="terracotta"] {
  --paper:        #fbf3ec;
  --paper-deep:   #f3e3d4;
  --surface:      #ffffff;
  --ink:          #4a2c1e;
  --ink-soft:     #8a6653;
  --rule:         #e8d2bd;
  --rule-strong:  #c99b76;
  --red:          #a3392a;
  --ok:           #4f6b3a;
  --focus:        #4a728e;
  --accent:       #b1502c;
  --accent-hover: #c56439;
  --accent-text:  #fbf3ec;
}

/* Graphite — a second dark theme, cool where Midnight is warm: slate-black
   stock, cyan accent. */
:root[data-theme="graphite"] {
  --paper:        #14181c;
  --paper-deep:   #1b2126;
  --surface:      #1f262c;
  --ink:          #e6ecef;
  --ink-soft:     #90a0a8;
  --rule:         #262f35;
  --rule-strong:  #3c4a52;
  --red:          #e2685a;
  --ok:           #5fbf9e;
  --focus:        #6fb0d8;
  --accent:       #46a8c9;
  --accent-hover: #5cbdde;
  --accent-text:  #0e1518;
}

/* Rose — dusty mauve stock, the softest of the eight. */
:root[data-theme="rose"] {
  --paper:        #fbf1f2;
  --paper-deep:   #f3dfe1;
  --surface:      #ffffff;
  --ink:          #4a2530;
  --ink-soft:     #8c6570;
  --rule:         #ecd2d6;
  --rule-strong:  #cd9aa4;
  --red:          #a3324a;
  --ok:           #4c6b52;
  --focus:        #6a5a8e;
  --accent:       #9c4f63;
  --accent-hover: #b0637a;
  --accent-text:  #fbf1f2;
}

/* Mono — true grayscale, no hue anywhere except the functional red/green
   the ledger itself needs (imbalance, credit, income). The most
   minimalist of the set: black-on-white, one weight of accent. */
:root[data-theme="mono"] {
  --paper:        #fafafa;
  --paper-deep:   #f0f0f0;
  --surface:      #ffffff;
  --ink:          #1a1a1a;
  --ink-soft:     #6b6b6b;
  --rule:         #e0e0e0;
  --rule-strong:  #b0b0b0;
  --red:          #b3261e;
  --ok:           #2e7d4f;
  --focus:        #4a4a4a;
  --accent:       var(--ink);
  --accent-hover: #333333;
  --accent-text:  var(--paper);
}

/* Linen — the barest warmth, a whisper of Parchment rather than a shout.
   Still minimalist: one restrained hue, everything else grayscale. */
:root[data-theme="linen"] {
  --paper:        #faf8f5;
  --paper-deep:   #f1ede6;
  --surface:      #ffffff;
  --ink:          #2b2620;
  --ink-soft:     #7a7367;
  --rule:         #e6dfd4;
  --rule-strong:  #cabfae;
  --red:          #a8402f;
  --ok:           #4f7350;
  --focus:        #6b7280;
  --accent:       var(--ink);
  --accent-hover: #443c32;
  --accent-text:  var(--paper);
}

/* Budget — clean cool-white and a saturated indigo accent, styled after
   Actual Budget's default look rather than this app's own ledger-paper
   idiom: flat surfaces, no warmth, one confident brand color instead of
   ink-as-accent. */
:root[data-theme="budget"] {
  --paper:        #f7f8fb;
  --paper-deep:   #eef0f6;
  --surface:      #ffffff;
  --ink:          #242530;
  --ink-soft:     #6e7186;
  --rule:         #e3e5ee;
  --rule-strong:  #c7cbdc;
  --red:          #d0393e;
  --ok:           #2ba672;
  --focus:        #6366f1;
  --accent:       #5442f5;
  --accent-hover: #6f5ffa;
  --accent-text:  #ffffff;
}

/* Forest — a third dark theme, distinct from Midnight (warm brass) and
   Graphite (cool cyan): full pine-green-black with a mossy accent, the
   ledger's own green pushed into a night register. */
:root[data-theme="forest"] {
  --paper:        #0f1811;
  --paper-deep:   #16211a;
  --surface:      #1a2620;
  --ink:          #e3ede6;
  --ink-soft:     #93a897;
  --rule:         #24332a;
  --rule-strong:  #3d5445;
  --red:          #e2776a;
  --ok:           #7ed9a0;
  --focus:        #6fa8d8;
  --accent:       #4f9863;
  --accent-hover: #5fac74;
  --accent-text:  #0d140f;
}

/* Plum — dusty grape stock, the only theme that goes violet rather than
   pink (Rose) or indigo (Ink). */
:root[data-theme="plum"] {
  --paper:        #f7f2f7;
  --paper-deep:   #eee3ee;
  --surface:      #ffffff;
  --ink:          #3a2440;
  --ink-soft:     #7c6382;
  --rule:         #e2d1e4;
  --rule-strong:  #bd9ac2;
  --red:          #a3324a;
  --ok:           #4c6b52;
  --focus:        #5a5a8e;
  --accent:       #7a4a8f;
  --accent-hover: #8f5da4;
  --accent-text:  #f7f2f7;
}

/* Cobalt — a confident, saturated blue, brighter and more "brand" than
   the muted cool tones of Slate, Ink, or Budget. */
:root[data-theme="cobalt"] {
  --paper:        #f4f8fc;
  --paper-deep:   #e7f0fa;
  --surface:      #ffffff;
  --ink:          #142033;
  --ink-soft:     #4f6478;
  --rule:         #d3e2f0;
  --rule-strong:  #9fc0e0;
  --red:          #c23b3b;
  --ok:           #1f8a5f;
  --focus:        #2f6fb0;
  --accent:       #1c6fd6;
  --accent-hover: #3480e0;
  --accent-text:  #f4f8fc;
}

/* Contrast — an accessibility theme: pure black on white, no soft grays
   in the borders that carry structure (rule-strong), for readers who
   need the sharpest edges the design system can offer. */
:root[data-theme="contrast"] {
  --paper:        #ffffff;
  --paper-deep:   #eeeeee;
  --surface:      #ffffff;
  --ink:          #000000;
  --ink-soft:     #333333;
  --rule:         #555555;
  --rule-strong:  #000000;
  --red:          #b30000;
  --ok:           #006622;
  --focus:        #0000cc;
  --accent:       #000000;
  --accent-hover: #262626;
  --accent-text:  #ffffff;
}

* { box-sizing: border-box; }
html { font-size: 16px; }
body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  line-height: 1.45;
}
/* Plain in-content links (flash banners, page-sub pointers, Help body
   copy) default to the browser's own blue, which several themes —
   Midnight especially, on a dark ledger stock — render nearly
   unreadable. Use the same accent as buttons instead, so a link reads
   as "the app's color" rather than "whatever blue this browser ships."
   :where() keeps this at zero specificity on purpose: a real ":visited"
   pseudo-class is (0,1,1), which would otherwise beat any plain single
   class (.dim, .quiet-link, .username-link — all (0,1,0)) the moment a
   link had been clicked once, silently recoloring exactly the links
   that are supposed to stay muted. Elements with their own link color
   (.topbar nav a, .sidebar a, .quiet-link, .pager a, a.amount-link,
   a.button-link, .help-icon, ...) keep it, visited or not. */
:where(a, a:visited) { color: var(--accent); }
:where(a:hover, a:visited:hover) { color: var(--accent-hover); }

/* -- top bar ------------------------------------------------------------- */
.topbar {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 1.5rem; flex-wrap: wrap;
  padding: 0.9rem 1.5rem 0.7rem;
  border-bottom: 1px solid var(--rule-strong);
  background: var(--paper);
}
.topbar-left { padding-left: 2.3rem; } /* clears the fixed hamburger button */
.wordmark {
  font-family: var(--serif);
  font-size: 1.7rem; font-weight: 600; letter-spacing: 0.01em;
  color: var(--ink); text-decoration: none;
}
/* The current page's own name, e.g. "Dashboard" — the app's actual
   name trails after it, de-emphasized, the way a browser tab reads
   "Page · Site" rather than leading with the site every time. */
.wordmark-brand { color: var(--ink-soft); font-weight: 400; }
.topbar-right { display: flex; align-items: baseline; gap: 1.3rem; flex-wrap: wrap; }
.topbar nav { display: flex; gap: 1.1rem; flex-wrap: wrap; }
.topbar nav a {
  color: var(--ink-soft); text-decoration: none; font-size: 0.92rem;
  padding-bottom: 0.15rem; border-bottom: 2px solid transparent;
}
.topbar nav a:hover { color: var(--ink); }
.topbar nav a.active { color: var(--ink); border-bottom-color: var(--red); }

/* -- hamburger sidebar --------------------------------------------------- */
/* Hover the button (or the panel) to preview the menu; click to pin it
   open — see sidebar.js. Pinned state pushes the page over via the
   html.sidebar-pinned rules below instead of overlaying it. */
.sidebar-toggle {
  position: fixed; top: 0.85rem; left: 1.1rem; z-index: 60;
  width: 1.7rem; height: 1.7rem; padding: 0;
  background: none; border: none; cursor: pointer;
  display: flex; flex-direction: column; justify-content: center; gap: 4px;
}
.sidebar-toggle span {
  display: block; height: 2px; width: 100%;
  background: var(--ink-soft); border-radius: 1px;
}
.sidebar-toggle:hover span { background: var(--ink); }
html.sidebar-pinned .sidebar-toggle span { background: var(--ink); }

.sidebar {
  position: fixed; top: 0; left: 0; bottom: 0; width: 14rem; z-index: 55;
  background: var(--surface); border-right: 1px solid var(--rule-strong);
  padding: 3.4rem 0 1rem; overflow-y: auto;
  transform: translateX(-100%);
  transition: transform 160ms ease;
  box-shadow: 2px 0 16px rgba(0, 0, 0, 0.15);
}
.sidebar.open { transform: translateX(0); }
html.sidebar-pinned .sidebar { box-shadow: none; }

.sidebar-group { padding: 0 0.4rem 1.2rem 1.5rem; }
.sidebar-label {
  font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.07em;
  color: var(--ink-soft); margin-bottom: 0.35rem;
}
.sidebar a {
  display: block; padding: 0.3rem 0 0.3rem 0.6rem; margin-left: -0.6rem;
  color: var(--ink-soft); text-decoration: none; font-size: 0.92rem;
  border-left: 2px solid transparent;
}
.sidebar a:hover { color: var(--ink); }
.sidebar a.active { color: var(--ink); border-left-color: var(--red); }

/* Pinned: push the page content over instead of the sidebar overlaying it.
   Only the fixed-width centered blocks need it — the sidebar itself and
   the hamburger button are already fixed to the viewport. */
html.sidebar-pinned .topbar,
html.sidebar-pinned main,
html.sidebar-pinned .footer { margin-left: 14rem; }

.theme-picker select {
  font: inherit; font-size: 0.8rem; color: var(--ink-soft);
  background: var(--surface); border: 1px solid var(--rule-strong);
  padding: 0.2rem 0.4rem; border-radius: 2px;
}
/* The theme picker sits inline in the topbar, not a full-width form field
   — keep its enhanced combobox compact instead of stretching to fill. */
.theme-picker .combobox { width: auto; }
.theme-picker .combobox-input {
  width: 7rem; font-size: 0.8rem; color: var(--ink-soft);
  padding: 0.2rem 0.4rem;
}
.logout-form { display: flex; align-items: center; gap: 0.5rem; }
.username-link { text-decoration: none; border-bottom: 2px solid transparent; padding-bottom: 0.15rem; }
.username-link:hover { color: var(--ink); }
.username-link.active { color: var(--ink); border-bottom-color: var(--red); }
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

main { max-width: 1080px; margin: 1.6rem auto 3rem; padding: 0 1.25rem; }
.footer {
  max-width: 1080px; margin: 0 auto 1.5rem; padding: 0.8rem 1.25rem 0;
  border-top: 1px solid var(--rule); color: var(--ink-soft);
  font-size: 0.78rem;
}

/* -- login: a split screen instead of the standard topbar/main chrome —
   see login.html's {% block chrome %} override. Brand on the left (the
   one place in the app "PostWarden" gets to be the whole point, rather than
   a muted trailer after the page name), the form on the right. */
.auth-split { display: flex; min-height: 100vh; }
.auth-brand {
  flex: 1 1 50%; position: relative;
  display: flex; align-items: center; justify-content: center;
  background: var(--paper-deep); border-right: 1px solid var(--rule-strong);
  padding: 2rem;
}
.auth-wordmark {
  font-family: var(--serif); font-size: 4.5rem; font-weight: 600;
  letter-spacing: 0.01em; color: var(--ink);
}
.auth-version {
  position: absolute; bottom: 1.3rem; left: 1.5rem;
  font-size: 0.78rem; color: var(--ink-soft);
}
.auth-panel { flex: 1 1 50%; display: flex; align-items: center; justify-content: flex-start; padding: 2rem 2rem 2rem 3rem; }
.auth-form-wrap { width: 100%; max-width: 20rem; }
.auth-form-wrap h2:first-child { margin-top: 0; }
@media (max-width: 720px) {
  .auth-split { flex-direction: column; }
  .auth-brand {
    flex: none; min-height: 10rem;
    border-right: none; border-bottom: 1px solid var(--rule-strong);
  }
  .auth-wordmark { font-size: 3rem; }
  .auth-version { bottom: 0.7rem; }
  .auth-panel { flex: none; padding: 2.5rem 2rem; }
}

h1 { font-family: var(--serif); font-size: 1.55rem; font-weight: 600;
     margin: 0 0 0.25rem; }
h2 { font-family: var(--serif); font-size: 1.15rem; font-weight: 600;
     margin: 1.8rem 0 0.6rem; }
.page-sub { color: var(--ink-soft); font-size: 0.88rem; margin: 0 0 1.2rem; }

/* A page's <h1>, plus a small "?" that links to its own section of
   Help — replaces the "How this works →" sentence every screen used to
   carry under its title. That line read like a manual taped to the
   front of the product; a page a newcomer could actually buy shouldn't
   need a caption explaining itself before it's even been looked at. */
/* The page's own name now lives in the topbar (see .wordmark-brand) —
   this just reserves a row for the "?" help icon, right-aligned, above
   whatever the page actually starts with. */
.page-head { display: flex; justify-content: flex-end; margin-bottom: 0.6rem; }
.help-icon {
  flex: none; display: inline-flex; align-items: center; justify-content: center;
  width: 1.35rem; height: 1.35rem; border-radius: 50%;
  border: 1px solid var(--rule-strong); color: var(--ink-soft);
  font-family: var(--serif); font-size: 0.8rem; text-decoration: none;
}
.help-icon:hover { background: var(--paper-deep); color: var(--ink); border-color: var(--ink-soft); }

/* -- flash messages ------------------------------------------------------ */
.flash {
  padding: 0.6rem 0.9rem; margin-bottom: 1rem; font-size: 0.9rem;
  border-left: 3px solid;
}
.flash-ok  { border-color: var(--ok);  background: color-mix(in srgb, var(--ok) 14%, var(--paper));  color: var(--ok); }
.flash-err { border-color: var(--red); background: color-mix(in srgb, var(--red) 14%, var(--paper)); color: var(--red); }

/* -- the ledger table ---------------------------------------------------- */
table.ledger {
  width: 100%; border-collapse: collapse; font-size: 0.9rem;
  background: var(--paper);
}
table.ledger th {
  text-align: left; font-weight: 600; font-size: 0.78rem;
  letter-spacing: 0.05em; text-transform: uppercase; color: var(--ink-soft);
  padding: 0.45rem 0.6rem;
  border-bottom: 1.5px solid var(--rule-strong);
}
table.ledger td {
  padding: 0.4rem 0.6rem;
  border-bottom: 1px solid var(--rule);
  vertical-align: baseline;
}
table.ledger .num, table.ledger th.num {
  text-align: right;
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
/* tinted money columns with the red rule on their left — ledger paper's
   signature detail */
table.ledger td.money, table.ledger th.money { background: var(--paper-deep); }
table.ledger td.money-first, table.ledger th.money-first {
  border-left: 1px solid color-mix(in srgb, var(--red) 45%, transparent);
}
table.ledger tr.type-head td {
  font-family: var(--serif); font-weight: 600; font-size: 0.95rem;
  padding-top: 0.9rem; border-bottom: 1px solid var(--rule-strong);
  background: transparent;
}
table.ledger tr.subtotal td {
  font-weight: 600; color: var(--ink-soft); border-bottom: 1px solid var(--rule-strong);
}
/* the accountant's double underline — only under totals that balance */
tr.grand td { font-weight: 700; border-bottom: none; padding-top: 0.7rem; }
tr.grand td.num { border-bottom: 3.5px double var(--ink); }
tr.grand.out-of-balance td.num { border-bottom: 3.5px double var(--red); color: var(--red); }

.neg { color: var(--red); }
.dim { color: var(--ink-soft); }
.small { font-size: 0.8rem; }
.mono { font-family: var(--mono); font-variant-numeric: tabular-nums; }

/* -- forms --------------------------------------------------------------- */
/* Plain class selector, not "form.bar" — a .bar grouping a few fields
   side by side shows up as a <div> just as often as a <form> (a filter
   bar submits, a field group inside a bigger form doesn't), and only the
   <form> ones were ever actually matching this. Every <div class="bar">
   in the app (the entry grid's own field row included) was quietly
   never flexed at all, each field stacking full-width on its own line
   instead of sitting next to its neighbors. */
.bar {
  display: flex; gap: 0.7rem; align-items: flex-end; flex-wrap: wrap;
  margin-bottom: 1.1rem;
}
label.field { display: flex; flex-direction: column; gap: 0.2rem;
              font-size: 0.78rem; color: var(--ink-soft); }
input, select, textarea {
  font: inherit; color: var(--ink);
  background: var(--surface); border: 1px solid var(--rule-strong);
  padding: 0.35rem 0.5rem; border-radius: 2px;
}
input:focus, select:focus, textarea:focus, button:focus-visible {
  outline: 2px solid var(--focus); outline-offset: 1px;
}
input[type="number"], input.amount {
  font-family: var(--mono); text-align: right;
  font-variant-numeric: tabular-nums;
}

/* A file input's outer box already picks up the plain input/select/
   textarea rule above (bordered, --surface background) — this only
   restyles its embedded native button (::file-selector-button is the
   standard pseudo-element; -webkit-file-upload-button is the same thing
   for older WebKit) to match every other button in the app instead of
   the browser's own unstyled default. */
input[type="file"] { padding: 0.3rem; cursor: pointer; }
input[type="file"]::file-selector-button,
input[type="file"]::-webkit-file-upload-button {
  font: inherit; cursor: pointer;
  background: var(--accent); color: var(--accent-text);
  border: 1px solid var(--accent); padding: 0.3rem 0.8rem; border-radius: 2px;
  margin-right: 0.6rem;
}
input[type="file"]:hover::file-selector-button,
input[type="file"]:hover::-webkit-file-upload-button {
  background: var(--accent-hover); border-color: var(--accent-hover);
}

/* .button-link: a plain navigation <a> styled to match <button> exactly,
   for the rare case that's a link to another page rather than a form
   action — a <button> nested inside an <a> is invalid HTML (interactive
   content inside interactive content) and double-stops on Tab, so this
   is the real fix rather than reaching for that. */
button, a.button-link {
  font: inherit; cursor: pointer;
  background: var(--accent); color: var(--accent-text);
  border: 1px solid var(--accent); padding: 0.42rem 1rem; border-radius: 2px;
  display: inline-block; text-decoration: none;
}
button:hover, a.button-link:hover { background: var(--accent-hover); border-color: var(--accent-hover); }
button:disabled { background: var(--rule); border-color: var(--rule);
                  color: var(--ink-soft); cursor: not-allowed; }
button.quiet {
  background: transparent; color: var(--ink); border-color: var(--rule-strong);
  padding: 0.25rem 0.7rem; font-size: 0.82rem;
}
button.quiet:hover { background: var(--paper-deep); }
.checkline { display: flex; align-items: center; gap: 0.4rem;
             font-size: 0.85rem; color: var(--ink); font-family: var(--sans); }
/* A bulk-action toggle sitting over a list (Staging's "select all") is
   a different kind of thing from a --data-field checkbox like "require
   balanced entries" — it's a control over the list below it, not one
   more setting, so it borrows that list's own mono/uppercase (the same
   treatment table.ledger's headers and the Journal's dates already use)
   instead of reading as just another sentence-case form field. */
.list-toggle {
  font-family: var(--mono); text-transform: uppercase; letter-spacing: 0.05em;
  font-size: 0.78rem; color: var(--ink-soft);
}

/* Custom checkbox/radio — accent-color alone still leaves the browser's
   own native shape (rounded, its own font/background/border), which
   reads as an OS control dropped into a page styled nothing like the
   rest of the app's square-cornered, --surface/--rule-strong inputs.
   appearance:none strips that native rendering so these match a select
   box exactly, and the check/dot is drawn by hand since accent-color has
   nothing left to paint once appearance is gone. */
input[type="checkbox"], input[type="radio"] {
  appearance: none; -webkit-appearance: none;
  width: 1rem; height: 1rem; margin: 0; padding: 0; flex: none; cursor: pointer;
  background: var(--surface); border: 1px solid var(--rule-strong);
  position: relative;
}
input[type="checkbox"] { border-radius: 2px; }
input[type="radio"] { border-radius: 50%; }
input[type="checkbox"]:checked, input[type="radio"]:checked {
  background: var(--accent); border-color: var(--accent);
}
input[type="checkbox"]:checked::after {
  content: ""; position: absolute; left: 3px; top: 0px;
  width: 4px; height: 8px;
  border: solid var(--accent-text); border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}
input[type="radio"]:checked::after {
  content: ""; position: absolute; left: 3px; top: 3px;
  width: 4px; height: 4px; border-radius: 50%;
  background: var(--accent-text);
}

/* A same-row link styled to sit quietly next to a button — "Export CSV"
   beside "Filter"/"Refresh", not competing for primary-action weight. */
.quiet-link {
  color: var(--ink-soft); font-size: 0.82rem; padding-bottom: 0.5rem;
  text-decoration: none; border-bottom: 1px dotted var(--rule-strong);
}
.quiet-link:hover { color: var(--ink); border-bottom-color: var(--ink); }

/* An amount on a report that drills through to the Journal postings
   behind it — looks like plain text until hovered, so a report doesn't
   read like it's full of links at a glance (see Income Statement,
   Balance Sheet). */
a.amount-link { color: inherit; text-decoration: none; }
a.amount-link:hover { text-decoration: underline; }

.pager {
  display: flex; align-items: center; justify-content: center; gap: 1.2rem;
  margin-top: 1.4rem; font-size: 0.88rem;
}
.pager a { color: var(--ink); text-decoration: none; border-bottom: 1px solid var(--rule-strong); }
.pager a:hover { border-bottom-color: var(--ink); }

/* -- combobox (searchable <select>, see combobox.js) ---------------------- */
.combobox { position: relative; width: 100%; }
.combobox-native { display: none; }
.combobox-input { width: 100%; cursor: pointer; }
.combobox-input:focus { cursor: text; }
.combobox-panel {
  position: absolute; z-index: 20; top: calc(100% + 2px); left: 0; right: 0;
  max-height: 14rem; overflow-y: auto;
  background: var(--surface); border: 1px solid var(--rule-strong);
  border-radius: 2px; box-shadow: 0 4px 14px rgba(0, 0, 0, 0.15);
}
.combobox-option { padding: 0.35rem 0.6rem; cursor: pointer; font-size: 0.9rem; }
/* Strong accent, not the subtle paper-deep tint used elsewhere for hover —
   this is the one thing arrow-key navigation shows you, so it needs to be
   unmistakable at a glance, not just barely-different from the panel. */
.combobox-option.active, .combobox-option:hover {
  background: var(--accent); color: var(--accent-text);
}
.combobox-option.selected { font-weight: 600; }
.combobox-option.combobox-create { font-style: italic; color: var(--accent); }
.combobox-option.combobox-create.active, .combobox-option.combobox-create:hover {
  font-style: italic; color: var(--accent-text);
}
.combobox-empty { padding: 0.35rem 0.6rem; color: var(--ink-soft); font-size: 0.85rem; }
.combobox-empty.combobox-error { color: var(--red); }

/* -- chevron (shared by the date picker, tree collapse/expand, and the
   number stepper) -----------------------------------------------------
   A solid clip-path triangle, not a font glyph (whose angle is whatever
   the font happens to draw) — an open corner, like the ">" it's
   standing in for, not a solid arrowhead: a filled triangle (tried
   briefly) reads as a totally different mark once you actually look at
   it, however thin it's made. Two straight border edges meeting at a
   right angle, rotated so the vertex points the right way. Thin on
   purpose (1.5px) but not hairline-thin — this needs to stay legible at
   a glance, not just technically visible.
   Rotated with transform, same as any other rotated element — this is
   NOT the clip-path shape an earlier pass here used, which had its own
   separate bug (this browser distorts a *clip-path* rotated by exactly
   ±90deg; a plain border box rotates however you'd expect, at any
   angle, confirmed by every other rotated element in this file working
   fine). Points right by default; .chevron-{up,down,left} cover every
   other direction actually used. */
.chevron {
  display: inline-block; flex: none;
  width: 0.4rem; height: 0.4rem;
  border-top: 1.5px solid currentColor;
  border-right: 1.5px solid currentColor;
  transform: rotate(45deg); /* right */
}
.chevron-down { transform: rotate(135deg); }
.chevron-up   { transform: rotate(-45deg); }
.chevron-left { transform: rotate(225deg); }

/* -- date picker (calendar popup, see datepicker.js) ----------------------- */
.datepicker { position: relative; width: 100%; }
.date-input { width: 100%; padding-right: 1.9rem; font-family: var(--mono); }
.date-trigger {
  position: absolute; top: 50%; right: 0.3rem; transform: translateY(-50%);
  width: 1.3rem; height: 1.3rem;
  background: transparent; border: none; color: var(--ink-soft); cursor: pointer;
  padding: 0; border-radius: 2px; line-height: 1;
  display: flex; align-items: center; justify-content: center;
}
.date-trigger:hover { background: var(--paper-deep); }
.date-panel {
  position: absolute; z-index: 20; top: calc(100% + 2px); left: 0; width: 16rem;
  background: var(--surface); border: 1px solid var(--rule-strong);
  border-radius: 2px; box-shadow: 0 4px 14px rgba(0, 0, 0, 0.15); padding: 0.5rem;
}
.date-panel-head {
  display: flex; align-items: center; justify-content: space-between;
  font-weight: 600; font-size: 0.85rem; margin-bottom: 0.4rem;
}
.date-nav {
  background: transparent; border: 1px solid var(--rule-strong); color: var(--ink);
  width: 1.6rem; height: 1.6rem; border-radius: 2px; cursor: pointer; padding: 0;
}
.date-nav:hover { background: var(--paper-deep); }
.date-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; }
.date-dow {
  font-size: 0.68rem; text-align: center; color: var(--ink-soft); padding-bottom: 0.2rem;
}
.date-day {
  background: transparent; border: none; padding: 0.3rem 0; text-align: center;
  font-size: 0.82rem; font-variant-numeric: tabular-nums; cursor: pointer;
  border-radius: 2px; color: var(--ink);
}
.date-day:hover { background: var(--paper-deep); }
.date-day.today { border: 1px solid var(--rule-strong); }
.date-day.selected { background: var(--accent); color: var(--accent-text); }
.date-panel-foot { margin-top: 0.4rem; text-align: right; }

/* -- number stepper (see number-stepper.js) -------------------------------- */
.number-stepper { position: relative; display: block; width: 100%; }
.number-input {
  width: 100%; padding-right: 1.5rem; font-size: 0.85rem;
  -moz-appearance: textfield;
}
.number-input::-webkit-inner-spin-button,
.number-input::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }
.number-step {
  position: absolute; right: 0.2rem; width: 1.1rem; height: 0.7rem;
  background: transparent; border: none; color: var(--ink-soft); cursor: pointer;
  padding: 0; border-radius: 2px; line-height: 1;
  display: flex; align-items: center; justify-content: center;
}
.number-step:hover { background: var(--paper-deep); }
.number-step:disabled { opacity: 0.35; cursor: default; }
.number-step:disabled:hover { background: transparent; }
.step-up { top: 0.15rem; }
.step-down { bottom: 0.15rem; }

/* -- journal entry screen ------------------------------------------------ */
table.entry-grid input { width: 100%; border-color: var(--rule); }
table.entry-grid td { padding: 0.2rem 0.3rem; }
table.entry-grid .col-account { width: 42%; }
table.entry-grid .col-amount  { width: 15%; }

/* -- budget grid (see budget-grid.js) ------------------------------------- */
.budget-cell { width: 100%; border-color: var(--rule); }
.balance-bar {
  display: flex; gap: 2rem; align-items: baseline; flex-wrap: wrap;
  margin-top: 0.9rem; padding: 0.7rem 0.9rem;
  background: var(--paper-deep);
  border-left: 3px solid var(--rule-strong);
  font-family: var(--mono); font-variant-numeric: tabular-nums;
  font-size: 0.95rem;
}
.balance-bar.balanced { border-left-color: var(--ok); }
.balance-bar.balanced .diff { color: var(--ok);
  border-bottom: 3.5px double var(--ok); }
.balance-bar.unbalanced .diff { color: var(--red); }
.balance-bar .lbl { color: var(--ink-soft); font-family: var(--sans);
  font-size: 0.75rem; letter-spacing: 0.05em; text-transform: uppercase;
  margin-right: 0.4rem; }

/* -- entries browser ----------------------------------------------------- */
details.entry {
  border: 1px solid var(--rule); border-left: 3px solid var(--rule-strong);
  margin-bottom: 0.55rem; background: var(--surface);
}
details.entry[open] { border-left-color: var(--red); }
details.entry summary {
  display: grid;
  grid-template-columns: 6.5rem 5.5rem 1fr auto auto;
  gap: 0.9rem; align-items: baseline;
  padding: 0.5rem 0.8rem; cursor: pointer; list-style: none;
  font-size: 0.9rem;
}
details.entry summary::-webkit-details-marker { display: none; }
details.entry summary:hover { background: var(--paper-deep); }
details.entry .lines { padding: 0.2rem 0.8rem 0.7rem; }

/* The "+ New entry" panel reuses .entry's box/border look but isn't a
   data row — plain text instead of the date/id/description/amount grid,
   and a dashed border so it reads as an action, not a posted entry. */
details.entry-new { border-style: dashed; }
details.entry-new summary {
  display: block; font-weight: 600; color: var(--accent);
}
.badge {
  display: inline-block; font-size: 0.68rem; letter-spacing: 0.05em;
  text-transform: uppercase; padding: 0.1rem 0.45rem;
  border: 1px solid var(--rule-strong); color: var(--ink-soft);
  border-radius: 2px;
}
.badge.rev { border-color: var(--red); color: var(--red); }
.badge.tag {
  border-color: var(--focus); color: var(--focus); text-decoration: none;
}
.badge.tag:hover { background: var(--paper-deep); }

/* -- tag chip input (see tags.js) ----------------------------------------- */
.tag-input {
  position: relative; display: flex; flex-wrap: wrap; gap: 0.3rem;
  align-items: center; border: 1px solid var(--rule-strong);
  background: var(--surface); border-radius: 2px; padding: 0.3rem 0.4rem;
  min-height: 2.2rem;
}
.tag-input input[type="text"] {
  flex: 1; min-width: 6rem; border: none; padding: 0.1rem 0; background: transparent;
}
.tag-input input[type="text"]:focus { outline: none; }
.tag-chip {
  display: inline-flex; align-items: center; gap: 0.3rem;
  background: var(--paper-deep); border: 1px solid var(--rule-strong);
  border-radius: 999px; padding: 0.1rem 0.4rem 0.1rem 0.6rem; font-size: 0.8rem;
  white-space: nowrap;
}
.tag-chip-remove {
  background: transparent; border: none; color: var(--ink-soft); cursor: pointer;
  font-size: 0.9rem; line-height: 1; padding: 0;
}
.tag-chip-remove:hover { color: var(--red); }

/* -- two-column layout: sticky side nav + main content --------------------
   Originally just the Accounts page's level browser; generic enough
   (and useful enough — see Help's own section nav) that it's named for
   what it is rather than its first caller. */
.two-col { display: flex; gap: 1.5rem; align-items: flex-start; }
.side-nav {
  flex: 0 0 12rem; display: flex; flex-direction: column; gap: 0.15rem;
  border: 1px solid var(--rule); background: var(--surface); padding: 0.6rem;
  position: sticky; top: 1rem;
}
.side-nav a {
  display: block; padding: 0.35rem 0.5rem; text-decoration: none;
  color: var(--ink); border-radius: 2px; font-size: 0.9rem;
}
.side-nav a:hover { background: var(--paper-deep); }
.side-nav a.active { background: var(--accent); color: var(--accent-text); font-weight: 600; }
.side-nav a.dim { color: var(--ink-soft); font-size: 0.82rem; }
.two-col-main { flex: 1; min-width: 0; }
.two-col-main > h2:first-child { margin-top: 0; }

td.acct-name { font-variant-numeric: tabular-nums; }
/* table.ledger td's own padding shorthand (below) beats a bare .depth-N
   on specificity — (1 class, 2 elements) vs (1 class, 0 elements) — so
   padding-left silently lost to it unless these are qualified the same
   way. Six levels' worth of headroom, not just four: vertical-dimension
   trees (accounts.dimension_levels) can go deeper than the old fixed
   4-level chart ever did. */
table.ledger td.acct-name.depth-1 { padding-left: 0; font-weight: 600; }
table.ledger td.acct-name.depth-2 { padding-left: 1.3rem; }
table.ledger td.acct-name.depth-3 { padding-left: 2.6rem; }
table.ledger td.acct-name.depth-4 { padding-left: 3.9rem; }
table.ledger td.acct-name.depth-5 { padding-left: 5.2rem; }
table.ledger td.acct-name.depth-6 { padding-left: 6.5rem; }
.inactive { opacity: 0.45; }

/* Collapse/expand — see accounts.js. The arrow is reserved space on every
   row (empty for leaves) so names always line up regardless of hover
   state; it's only visible (revealed on hover) or interactive on summary
   rows, and stays visible once a group is collapsed so the state itself
   is legible without hovering. */
.tree-toggle {
  display: inline-block; width: 0.9rem; margin-right: 0.15rem;
  text-align: center; vertical-align: middle;
}
/* Same border-corner recipe as the shared .chevron shape (see above),
   drawn on ::before rather than a real child element since every
   template just emits an empty <span class="tree-toggle">. Hidden by
   default; only rows with children (data-postable="0" /
   data-has-children="1") turn it on, so leaf rows keep the
   reserved-but-empty span for alignment. Expanded (default) points
   down (135deg), collapsed points right (45deg). Nudged up-left of its
   own box: an unrotated corner sits low-and-right within it (its
   weight is in the bottom-right, being built from the top+right
   edges), so centering it against the account name next to it needs a
   small manual offset rather than pure vertical-align. */
.tree-toggle::before { content: ""; }
tr[data-postable="0"] .tree-toggle::before,
tr[data-has-children="1"] .tree-toggle::before {
  display: inline-block; width: 0.4rem; height: 0.4rem;
  border-top: 1.5px solid currentColor; border-right: 1.5px solid currentColor;
  transform: rotate(135deg); /* down */
  position: relative; top: -0.1rem; left: -0.08rem;
  transition: transform 120ms ease;
}
tr.collapsed .tree-toggle::before {
  transform: rotate(45deg); /* right */
}
tr[data-postable="0"] .acct-name { cursor: pointer; }
tr[data-postable="0"] .tree-toggle {
  color: var(--ink-soft); opacity: 0; transition: opacity 120ms ease;
}
tr[data-postable="0"] .acct-name:hover .tree-toggle { opacity: 1; }
tr[data-postable="0"]:hover td { background: var(--paper-deep); }
tr.collapsed .tree-toggle { opacity: 1; }

/* Same tree on Trial Balance/Balance Sheet (see report-tree.js) — but the
   toggle stays visible without hovering: scanning a report's numbers is
   the point, not browsing structure, so which rows expand shouldn't be a
   secret until you happen to mouse over them. */
tr[data-has-children="1"] .acct-name { cursor: pointer; }
tr[data-has-children="1"] .tree-toggle { color: var(--ink-soft); }
tr[data-has-children="1"]:hover td { background: var(--paper-deep); }

/* Inline "add category here" gap rows, Actual Budget-style — see
   accounts.js. Each is a slim hover strip between two account rows until
   clicked, then expands to show the form. */
tr.add-gap td {
  padding: 0; border: none; height: 0.45rem; position: relative;
}
tr.add-gap .add-gap-trigger {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: 1.3rem; height: 1.3rem; padding: 0; line-height: 1;
  border-radius: 999px; border: 1px solid var(--rule-strong);
  background: var(--surface); color: var(--ink-soft); cursor: pointer;
  font-size: 0.85rem; opacity: 0; transition: opacity 120ms ease;
}
tr.add-gap:hover .add-gap-trigger { opacity: 1; }
tr.add-gap:hover td { background: var(--paper-deep); }
tr.add-gap.open td { height: auto; padding: 0.45rem 0.6rem; background: var(--paper-deep); }
tr.add-gap.open .add-gap-trigger { display: none; }
tr.add-gap .add-gap-form {
  display: none; align-items: center; gap: 0.5rem; flex-wrap: wrap;
}
tr.add-gap.open .add-gap-form { display: flex; }
tr.add-gap .add-gap-form input[type="text"] { flex: 1; min-width: 10rem; }
tr.add-gap .add-gap-type { max-width: 11rem; }

/* -- dashboard ------------------------------------------------------------ */
.stat-row {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  gap: 1rem; margin-bottom: 2rem;
}
.stat-tile {
  background: var(--surface); border: 1px solid var(--rule);
  border-top: 3px solid var(--accent); padding: 0.9rem 1rem;
}
.stat-label {
  font-size: 0.78rem; color: var(--ink-soft); text-transform: uppercase;
  letter-spacing: 0.05em; margin-bottom: 0.3rem;
}
.stat-value {
  font-family: var(--mono); font-size: 1.5rem; font-weight: 600;
  font-variant-numeric: tabular-nums; margin-bottom: 0.4rem;
}
.stat-value.neg { color: var(--red); }

.panel {
  background: var(--surface); border: 1px solid var(--rule);
  padding: 1rem 1.1rem; margin-top: 1.4rem;
}
.panel h2 { margin-top: 0; }
.grid-form { display: grid; grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
             gap: 0.8rem; align-items: end; }

@media (max-width: 720px) {
  details.entry summary { grid-template-columns: 1fr auto; }
  .topbar { gap: 0.6rem; }
  /* Too narrow to push content over without squeezing it unusably —
     pinned still overlays here, same as an unpinned hover-preview. */
  .sidebar { width: 82vw; box-shadow: 2px 0 16px rgba(0, 0, 0, 0.15); }
  html.sidebar-pinned .topbar,
  html.sidebar-pinned main,
  html.sidebar-pinned .footer { margin-left: 0; }
}
@media (prefers-reduced-motion: no-preference) {
  .flash { animation: slide-in 160ms ease-out; }
  @keyframes slide-in { from { opacity: 0; transform: translateY(-4px); } }

  /* A theme switch recolors most of the page at once; ease it rather
     than snap, since it's a deliberate user action, not page load. */
  body, .topbar, table.ledger, .panel, details.entry, .balance-bar,
  input, select, textarea, button {
    transition: background-color 160ms ease, color 160ms ease,
                border-color 160ms ease;
  }
}
