/* ===========================================================================
 * vanshOS — design system v1.0
 *
 * Translates the Brand Book tokens onto every class already used by the
 * 42 Jinja templates. No template markup changes required.
 *
 * Sections:
 *   1.  @font-face        — self-hosted Geist / Spectral / Geist Mono
 *   2.  Tokens            — palette, type, rhythm
 *   3.  Reset + base typo
 *   4.  Brand mark        — the vanshOS logotype
 *   5.  Layout            — topbar / subnav / main / page-wide / two-col
 *   6.  Buttons           — .btn / .btn--primary / .btn--ghost
 *   7.  Forms             — .form / .form-grid / inputs / textareas
 *   8.  Cards             — .auth-card / .error-card / .dashboard
 *   9.  Flash messages
 *  10.  Tables            — .data-table / .admin-table / .bulk-table / matrix
 *  11.  KPI grid
 *  12.  Filter bar
 *  13.  Pagination
 *  14.  Direction badges  — inflow / outflow / other
 *  15.  Wizard progress
 *  16.  Misc utilities    — .muted / .kv / .color-chip / .preferred-banks
 * ===========================================================================
 */

/* ---------------------------------------------------------------------------
 * 1. @font-face — Latin subset only. Other Unicode ranges fall back to the
 *    OS-provided sans/serif stack via the var(--sans) / var(--serif) chain.
 * --------------------------------------------------------------------------- */
@font-face {
  font-family: "Geist";
  font-style: normal;
  font-weight: 300;
  font-display: swap;
  src: url("/static/fonts/geist-300.woff2") format("woff2");
}
@font-face {
  font-family: "Geist";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/static/fonts/geist-400.woff2") format("woff2");
}
@font-face {
  font-family: "Geist";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("/static/fonts/geist-500.woff2") format("woff2");
}
@font-face {
  font-family: "Geist";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("/static/fonts/geist-600.woff2") format("woff2");
}
@font-face {
  font-family: "Geist Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/static/fonts/geist-mono-400.woff2") format("woff2");
}
@font-face {
  font-family: "Geist Mono";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("/static/fonts/geist-mono-500.woff2") format("woff2");
}
@font-face {
  font-family: "Spectral";
  font-style: normal;
  font-weight: 300;
  font-display: swap;
  src: url("/static/fonts/spectral-300.woff2") format("woff2");
}
@font-face {
  font-family: "Spectral";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/static/fonts/spectral-400.woff2") format("woff2");
}
@font-face {
  font-family: "Spectral";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("/static/fonts/spectral-500.woff2") format("woff2");
}
@font-face {
  font-family: "Spectral";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("/static/fonts/spectral-600.woff2") format("woff2");
}
@font-face {
  font-family: "Spectral";
  font-style: italic;
  font-weight: 300;
  font-display: swap;
  src: url("/static/fonts/spectral-300-italic.woff2") format("woff2");
}
@font-face {
  font-family: "Spectral";
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url("/static/fonts/spectral-400-italic.woff2") format("woff2");
}
@font-face {
  font-family: "Spectral";
  font-style: italic;
  font-weight: 500;
  font-display: swap;
  src: url("/static/fonts/spectral-500-italic.woff2") format("woff2");
}

/* ---------------------------------------------------------------------------
 * 2. Tokens — straight from the Brand Book.
 * --------------------------------------------------------------------------- */
:root {
  /* Surface — warm paper */
  --ivory:  #f6f4ef;
  --paper:  #fbfaf6;
  --sand:   #ece6d8;
  --bone:   #e6e0d1;
  /* Elevated surface for floating cards, dropdowns, input backgrounds.
     Same tone as --paper in the light themes; per-theme overrides
     adjust this for dark themes (where it needs to be brighter than
     --paper so floating popovers read cleanly above the page). */
  --card:   #ffffff;

  /* Foreground "on dark" — always the SAME light cream regardless of
     theme. Use this for text that sits on an intrinsically dark
     background like the .btn--primary forest fill or the matrix
     header band. NOT redefined in any [data-theme=...] block — that's
     the whole point: it stays light so contrast doesn't break when
     the page tokens flip to a dark palette.
     The bug pattern this fixes: ``color: var(--paper)`` on a
     forest-bg button gives cream text in light theme (good) but
     dark-grey-near-black text in ink theme (invisible). */
  --on-dark: #f6f4ef;

  /* Ink — primary text + alpha ladder for muted/dividers */
  --ink:     #181a17;
  --ink-80:  rgba(24, 26, 23, 0.80);
  --ink-65:  rgba(24, 26, 23, 0.65);
  --ink-45:  rgba(24, 26, 23, 0.45);
  --ink-25:  rgba(24, 26, 23, 0.25);
  --ink-12:  rgba(24, 26, 23, 0.12);
  --ink-07:  rgba(24, 26, 23, 0.07);

  /* Forest — primary brand (green) */
  --forest:       #1a2e1f;
  --forest-deep:  #0f1d13;
  --forest-mid:   #2a4631;
  --forest-pale:  #d6dcd2;

  /* Bronze — secondary brand (warm tan) */
  --bronze:       #7a6648;
  --bronze-mid:   #a08868;
  --bronze-pale:  #d9cdb7;

  /* State */
  --positive: #406b48;
  --warn:     #a47428;
  --negative: #8a3a2e;
  --positive-bg: #e3ebdb;
  --warn-bg:     #f4ebda;
  --negative-bg: #f0dfd9;

  /* Type stacks */
  --serif: "Spectral", "Times New Roman", Georgia, serif;
  --sans:  "Geist", -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif;
  --mono:  "Geist Mono", ui-monospace, "SF Mono", Menlo, monospace;

  /* Rhythm */
  --rule:       1px solid var(--ink-12);
  --rule-soft:  1px solid var(--ink-07);
  --pad-page:   clamp(24px, 4vw, 56px);
  --col-max:    1160px;
  --radius-sm:  4px;
  --radius:     6px;
  --radius-lg:  10px;
}

/* ---------------------------------------------------------------------------
 * 3. Reset + base typography
 * --------------------------------------------------------------------------- */
* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--ivory);
  color: var(--ink);
  font-family: var(--sans);
  font-weight: 400;
  font-size: 14.5px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  font-feature-settings: "ss01", "cv01";
}

h1, h2, h3, h4 {
  font-family: var(--serif);
  font-weight: 400;
  color: var(--ink);
  letter-spacing: -0.005em;
  margin: 0;
}
h1 { font-size: 2.05rem; line-height: 1.15; font-weight: 300; letter-spacing: -0.015em; }
h2 { font-size: 1.55rem; line-height: 1.2;  font-weight: 400; }
h3 { font-size: 1.2rem;  line-height: 1.25; font-weight: 500; }
h4 { font-size: 1rem;    line-height: 1.3;  font-weight: 500; }

p   { margin: 0 0 0.65em; }
a   { color: var(--forest); text-decoration: none; border-bottom: 1px solid var(--ink-12); transition: border-color 0.1s ease; }
a:hover { border-bottom-color: var(--forest); }
em, i { font-family: var(--serif); font-style: italic; font-weight: 400; }
code, pre, .mono { font-family: var(--mono); font-size: 0.92em; }

::selection { background: var(--bronze-pale); color: var(--ink); }

/* ---------------------------------------------------------------------------
 * 4. Brand mark — the vanshOS logotype, used in the topbar
 * --------------------------------------------------------------------------- */
.brand-mark {
  font-family: var(--serif);
  font-weight: 300;
  font-size: 1.55rem;
  line-height: 1;
  letter-spacing: -0.02em;
  border-bottom: none;
  display: inline-flex;
  align-items: baseline;
}
/* Lock the casing of every brand-mark span explicitly. Without these
   rules, an ancestor with ``text-transform: uppercase`` (e.g. the
   dashboard-hero eyebrow or any sectioned label) collapses the brand
   into "VANSHOS". The per-span text-transform overrides the inherited
   transform so the brand always reads as "vanshOS" — lowercase "v" +
   lowercase "ansh" + uppercase "OS" — no matter where it's mounted. */
.brand-mark__v    { color: var(--ink); text-transform: lowercase; }
.brand-mark__ansh { color: var(--forest); font-style: italic; text-transform: lowercase; }
.brand-mark__os   { color: var(--bronze); text-transform: uppercase; }

/* Inline variant — used inside text runs (e.g. eyebrows, footers) where
   the brand should size with surrounding text instead of behaving like
   a logo block. The .brand-mark base class sets font-size: 1.55rem
   (logo size); .brand-mark--inline drops that override so the brand
   inherits its parent's font-size. */
.brand-mark--inline {
  font-size: inherit;
  letter-spacing: normal;
  line-height: inherit;
}

/* ---------------------------------------------------------------------------
 * 5. Layout — sidebar-beside-main (Astra Personal-style)
 * --------------------------------------------------------------------------- */

/* The signed-in layout: 240px fixed sidebar + fluid main column. On mobile
 * the sidebar slides in from the left when `.layout--nav-open` is set by
 * Alpine. The overlay (under the sidebar) catches clicks to close it. */
.layout {
  display: grid;
  grid-template-columns: 240px 1fr;
  min-height: 100vh;
  background: var(--ivory);
}

.layout__hamburger {
  display: none;  /* shown only on narrow screens via media query */
  position: fixed;
  top: 14px;
  left: 14px;
  z-index: 50;
  width: 40px;
  height: 40px;
  align-items: center;
  justify-content: center;
  background: var(--paper);
  border: var(--rule);
  border-radius: var(--radius);
  color: var(--ink);
  cursor: pointer;
  transition: background 0.1s ease;
}
.layout__hamburger:hover { background: var(--ink-07); }

.layout__overlay {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(24, 26, 23, 0.4);
  z-index: 35;
}

/* Signed-out layout: thin top bar holding just the brand mark, then a
 * centered auth card via the existing .auth-card styles. */
.auth-topbar {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 32px 24px 8px;
}

.main {
  padding: 32px var(--pad-page) 80px;
  /* Constrain the inner column to a comfortable reading width, but the
   * main element itself fills the grid cell so the page bg extends fully. */
}
.main > * { max-width: var(--col-max); margin-left: auto; margin-right: auto; }
.main--auth > * { max-width: 100%; margin: 0; }
.main--auth { padding-top: 0; padding-bottom: 64px; }

/* ---------------------------------------------------------------------------
 * 5a. Sidebar — dark forest theme (per the v1.1 sidebar reference)
 *
 * Tokens scoped to the sidebar so the rest of the app stays on the
 * warm-paper palette. ``--sb-ink`` etc. are local-only.
 * --------------------------------------------------------------------------- */
.sidebar {
  /* Local palette — dark forest base + warm bronze accents */
  --sb-bg:          #131b14;             /* near forest-deep with a slight olive tilt */
  --sb-bg-2:        #1a2620;             /* hover/active background a touch lighter */
  --sb-rule:        rgba(214, 220, 210, 0.08);
  --sb-ink:         #e8e5dc;             /* primary text on dark */
  --sb-ink-muted:   rgba(232, 229, 220, 0.55);
  --sb-ink-faint:   rgba(232, 229, 220, 0.35);
  --sb-brand-os:    #c1a173;             /* lighter bronze for OS — reads better on dark */

  display: flex;
  flex-direction: column;
  background: var(--sb-bg);
  color: var(--sb-ink);
  border-right: none;
  padding: 26px 0 20px;
  min-height: 100vh;
  position: sticky;
  top: 0;
  align-self: start;
  max-height: 100vh;
  overflow-y: auto;
  z-index: 40;
  scrollbar-color: rgba(232, 229, 220, 0.18) transparent;
}

/* Brand mark adjustments inside the dark sidebar:
 *   - 'v' becomes light paper instead of ink
 *   - 'ansh' stays italic but in lighter forest tint
 *   - 'OS' uses the bumped-up bronze for legibility
 */
.sidebar .brand-mark__v    { color: var(--sb-ink); }
.sidebar .brand-mark__ansh { color: rgba(232, 229, 220, 0.78); }
.sidebar .brand-mark__os   { color: var(--sb-brand-os); }

.sidebar__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 24px 22px;
  margin-bottom: 8px;
  border-bottom: 1px solid var(--sb-rule);
}
.sidebar__brand { font-size: 1.55rem; line-height: 1; }

.sidebar__close {
  display: none;  /* visible only on mobile */
  background: transparent;
  border: none;
  cursor: pointer;
  color: var(--sb-ink-muted);
  padding: 6px;
}
.sidebar__close:hover { color: var(--sb-ink); }

.sidebar__nav {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 20px;
  padding: 4px 14px 0;
}

.sidebar__section {
  display: flex;
  flex-direction: column;
  gap: 1px;
}
.sidebar__section-title {
  font-family: var(--sans);
  font-weight: 500;
  font-size: 10.5px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--sb-ink-faint);
  margin: 4px 12px 8px;
}

.sidebar__item {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 9px 12px;
  border-radius: var(--radius);
  text-decoration: none;
  color: var(--sb-ink-muted);
  font-size: 13.5px;
  font-weight: 400;
  border-bottom: none;
  transition: background 0.12s ease, color 0.12s ease;
}
.sidebar__item:hover {
  background: var(--sb-bg-2);
  color: var(--sb-ink);
  border-bottom: none;
}
.sidebar__item--active {
  background: var(--sb-bg-2);
  color: var(--sb-ink);
  position: relative;
}
.sidebar__item--active::before {
  content: "";
  position: absolute;
  left: -14px;
  top: 8px;
  bottom: 8px;
  width: 3px;
  background: var(--bronze-mid);
  border-radius: 0 2px 2px 0;
}

/* Sidebar nav icons — bronze on the dark forest backdrop. */
.nav__icon {
  flex-shrink: 0;
  width: 17px;
  height: 17px;
  color: #b89968;                      /* slightly muted bronze for icon line */
  transition: color 0.12s ease;
}
.sidebar__item:hover .nav__icon  { color: var(--sb-brand-os); }
.sidebar__item--active .nav__icon { color: var(--sb-brand-os); }

.sidebar__label { flex: 1; }

/* Sidebar footer: user card with avatar (top row) + version (bottom
 * line) — pinned to the bottom of the sidebar. The version line uses
 * a much smaller font and the bronze tone so it doesn't compete with
 * the user info above it. */
.sidebar__footer {
  margin-top: auto;
  padding: 16px 20px 14px;
  border-top: 1px solid var(--sb-rule);
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.sidebar__footer-row {
  display: flex;
  align-items: center;
  gap: 11px;
}
.sidebar__version {
  font-size: 10.5px;
  letter-spacing: 0.04em;
  color: var(--sb-ink-50, rgba(232, 229, 220, 0.55));
  text-align: center;
  line-height: 1.3;
  user-select: text;  /* let people copy the version for bug reports */
}
.sidebar__version-num {
  font-variant-numeric: tabular-nums;
  color: var(--sb-ink-70, rgba(232, 229, 220, 0.75));
}
.sidebar__version-env {
  margin-left: 4px;
  padding: 1px 6px;
  border-radius: 3px;
  background: var(--bronze);
  color: #2a2009;
  font-size: 9.5px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.sidebar__user-avatar {
  flex-shrink: 0;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: linear-gradient(135deg, #8a7350, #6a5638);
  color: #f3eddd;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--serif);
  font-weight: 500;
  font-size: 14px;
  letter-spacing: 0;
}
.sidebar__user {
  flex: 1;
  min-width: 0;
}
.sidebar__user-name {
  font-size: 13px;
  font-weight: 500;
  color: var(--sb-ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.sidebar__user-meta {
  font-size: 11px;
  font-weight: 400;
  color: var(--sb-ink-faint);
  letter-spacing: 0.04em;
}
.sidebar__signout {
  background: transparent;
  border: none;
  cursor: pointer;
  color: var(--sb-ink-muted);
  padding: 6px;
  border-radius: var(--radius-sm);
  transition: background 0.12s ease, color 0.12s ease;
}
.sidebar__signout:hover { background: var(--sb-bg-2); color: var(--sb-ink); }

/* Small-size button variant — used in the sidebar footer */
.btn--sm {
  padding: 5px 9px;
  font-size: 12px;
  gap: 4px;
}

/* ---------------------------------------------------------------------------
 * 5b. Responsive sidebar (hamburger on mobile)
 * --------------------------------------------------------------------------- */
@media (max-width: 900px) {
  .layout {
    grid-template-columns: 1fr;  /* sidebar overlays instead of co-occupying */
  }
  .layout__hamburger { display: inline-flex; }
  .sidebar {
    position: fixed;
    top: 0; left: 0; bottom: 0;
    width: 280px;
    transform: translateX(-100%);
    transition: transform 0.22s cubic-bezier(0.2, 0.7, 0.2, 1);
    box-shadow: 0 10px 40px rgba(24, 26, 23, 0.18);
  }
  .layout--nav-open .sidebar { transform: translateX(0); }
  .layout--nav-open .layout__overlay { display: block; }
  .sidebar__close { display: inline-flex; }
  .main { padding-top: 72px;  /* leave room for the fixed hamburger */ }
}

.page-wide {
  /* The new sidebar layout already constrains width + padding via .main and
   * .main > * (above). page-wide becomes a semantic wrapper — no extra
   * padding/max-width so we don't double-pad. */
  width: 100%;
}
.page-header { margin-bottom: 24px; }
.page-header h1 { margin: 0 0 6px; }
.page-header p, .page-header .muted { color: var(--ink-65); font-size: 0.95rem; }

.two-col {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 28px;
  margin-bottom: 28px;
}
@media (max-width: 760px) { .two-col { grid-template-columns: 1fr; gap: 18px; } }

/* Sub-navigation — sits just under the topbar on dashboard / admin sections */
.subnav {
  display: flex;
  gap: 2px;
  align-items: center;
  padding: 8px var(--pad-page);
  background: var(--paper);
  border-bottom: var(--rule);
  font-size: 13.5px;
  font-weight: 500;
  overflow-x: auto;
}
.subnav__item {
  padding: 8px 14px;
  text-decoration: none;
  color: var(--ink-80);
  border-bottom: 2px solid transparent;
  white-space: nowrap;
  letter-spacing: 0.005em;
  transition: color 0.1s ease, border-color 0.1s ease;
}
.subnav__item:hover { color: var(--forest); border-bottom: 2px solid transparent; }
.subnav__item.active,
.subnav__item--active { color: var(--ink); border-bottom-color: var(--bronze); }
.subnav__item--right  { margin-left: auto; }
.subnav__sep { color: var(--ink-25); margin: 0 4px; }

/* Masters sub-nav — chip style for the inner masters tabs */
.masters-subnav {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  padding: 0 0 16px;
  margin-bottom: 20px;
  border-bottom: var(--rule);
}
.masters-subnav__item {
  padding: 6px 12px;
  text-decoration: none;
  color: var(--ink-80);
  border-radius: 999px;
  border-bottom: none;
  font-size: 13px;
  font-weight: 500;
}
.masters-subnav__item:hover { background: var(--ink-07); border-bottom: none; }
.masters-subnav__item--active {
  background: var(--forest);
  color: var(--on-dark);
}
.masters-subnav__item--active:hover { background: var(--forest-deep); }

/* ---------------------------------------------------------------------------
 * 6. Buttons
 * --------------------------------------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 8px 16px;
  border: 1px solid var(--ink-12);
  border-radius: var(--radius);
  background: var(--paper);
  color: var(--ink);
  cursor: pointer;
  text-decoration: none;
  font-family: var(--sans);
  font-size: 13.5px;
  font-weight: 500;
  line-height: 1.3;
  letter-spacing: 0.005em;
  transition: background 0.1s ease, border-color 0.1s ease, color 0.1s ease, transform 0.05s ease;
}
.btn:hover  { background: var(--ink-07); border-color: var(--ink-25); }
.btn:active { transform: translateY(1px); }
.btn:focus-visible {
  outline: 2px solid var(--bronze);
  outline-offset: 2px;
}

.btn--primary {
  background: var(--forest);
  /* --on-dark stays light cream in every theme; --paper would flip
     to a dark surface colour on the ink theme and make the button
     text unreadable. */
  color: var(--on-dark);
  border-color: var(--forest);
}
.btn--primary:hover  { background: var(--forest-deep); border-color: var(--forest-deep); }

.btn--ghost {
  background: transparent;
  border-color: transparent;
  color: var(--ink-80);
}
.btn--ghost:hover { background: var(--ink-07); color: var(--ink); border-color: transparent; }

.btn--danger {
  color: var(--negative);
  border-color: var(--ink-12);
}
.btn--danger:hover { background: var(--negative-bg); border-color: var(--negative); }

.inline-form { display: inline; }

/* ---------------------------------------------------------------------------
 * 7. Forms
 * --------------------------------------------------------------------------- */
.form {
  display: flex;
  flex-direction: column;
  gap: 18px;
}
.form__field { display: flex; flex-direction: column; gap: 6px; }
.form__label {
  font-size: 12.5px;
  color: var(--ink-65);
  font-weight: 500;
  letter-spacing: 0.02em;
  text-transform: uppercase;
}

.form input[type="text"],
.form input[type="password"],
.form input[type="email"],
.form input[type="number"],
.form input[type="date"],
.form select,
.form textarea {
  padding: 10px 12px;
  border: 1px solid var(--ink-12);
  border-radius: var(--radius);
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 14px;
  font-weight: 400;
  line-height: 1.4;
  transition: border-color 0.1s ease, background 0.1s ease;
}
.form input:focus,
.form select:focus,
.form textarea:focus {
  outline: none;
  border-color: var(--forest);
  background: #fff;
  box-shadow: 0 0 0 3px var(--forest-pale);
}
.form input::placeholder,
.form textarea::placeholder { color: var(--ink-45); }

.form-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 16px 20px;
  margin-bottom: 20px;
}

.account-form textarea,
.form textarea {
  resize: vertical;
  min-height: 84px;
}

.data-entry-form { max-width: 720px; }

/* ---------------------------------------------------------------------------
 * 8. Cards — auth / error / dashboard placeholder
 * --------------------------------------------------------------------------- */
.auth-card,
.error-card,
.dashboard {
  background: var(--paper);
  border: var(--rule);
  border-radius: var(--radius-lg);
  padding: 32px 36px;
  max-width: 440px;
  margin: 72px auto;
  box-shadow: 0 2px 24px var(--ink-07);
}
.dashboard { max-width: 720px; padding: 28px 32px; margin: 32px auto; }

.auth-card__title {
  font-family: var(--serif);
  font-weight: 300;
  font-size: 1.8rem;
  letter-spacing: -0.02em;
  margin: 0 0 8px;
}
.auth-card + .muted,
.auth-card__title + .muted { margin-bottom: 24px; }

/* ---------------------------------------------------------------------------
 * 9. Flash messages
 * --------------------------------------------------------------------------- */
.flash-region { padding: 12px var(--pad-page) 0; }
.flash {
  padding: 11px 16px;
  border-radius: var(--radius);
  margin-bottom: 8px;
  border: 1px solid transparent;
  font-size: 13.5px;
  font-weight: 500;
}
.flash--error   { background: var(--negative-bg); border-color: var(--negative); color: var(--negative); }
.flash--success { background: var(--positive-bg); border-color: var(--positive); color: var(--positive); }
.flash--warn    { background: var(--warn-bg);     border-color: var(--warn);     color: var(--warn); }
/* Info — muted neutral. Used for login welcome + "nothing changed"
   style low-key acknowledgements. Was forest-pale green which read
   too close to "success". Switched to a paper/bronze-pale tint with
   ink text so it reads as a calm hint, not an achievement banner. */
.flash--info    {
  background: color-mix(in srgb, var(--paper) 70%, var(--bronze-pale) 30%);
  border-color: var(--bronze-pale);
  color: var(--ink);
  font-weight: 400;
}

/* ---------------------------------------------------------------------------
 * 10. Tables
 * --------------------------------------------------------------------------- */
.table-wrap {
  overflow-x: auto;
  margin-bottom: 24px;
  border: var(--rule);
  border-radius: var(--radius);
  background: var(--paper);
}
.table-wrap--narrow { max-width: 520px; }

.data-table,
.admin-table,
.bulk-table {
  border-collapse: collapse;
  width: 100%;
  font-size: 13.5px;
  background: var(--paper);
}

.data-table th,
.data-table td,
.admin-table th,
.admin-table td,
.bulk-table th,
.bulk-table td {
  padding: 10px 14px;
  vertical-align: top;
  text-align: left;
  border-bottom: 1px solid var(--ink-07);
}
.data-table tr:last-child td,
.admin-table tr:last-child td,
.bulk-table tr:last-child td { border-bottom: none; }

.data-table th,
.admin-table th,
.bulk-table th {
  background: transparent;
  font-weight: 500;
  font-size: 11.5px;
  color: var(--ink-65);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  border-bottom: var(--rule);
  padding-top: 12px;
  padding-bottom: 10px;
}

.data-table tr:hover td,
.admin-table tr:hover td { background: var(--ink-07); }

.data-table .num,
.admin-table .num {
  text-align: right;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  font-family: var(--mono);
  font-size: 13px;
  font-weight: 400;
  color: var(--ink);
}
.data-table .num--total {
  font-weight: 500;
  background: var(--bone);
  color: var(--ink);
}
.data-table .totals-row td {
  background: var(--bone);
  border-top: var(--rule);
  font-weight: 500;
}

.data-table--matrix .sticky {
  position: sticky;
  left: 0;
  background: var(--paper);
  z-index: 1;
  border-right: var(--rule);
}

/* Distinct forest-tinted header band on the balance matrix. The plain
 * .data-table th rule above renders a faint uppercase label on a
 * transparent background, which made the matrix's "HOLDER / Bank /
 * Bank / …" row blend into the data. The matrix has many columns and
 * its own visual weight, so we give it a tighter contrast band.
 * Sticky-left cell ("Holder") sits on top of the same colour. */
.data-table--matrix thead th,
.data-table--matrix thead th.sticky {
  background: var(--forest);
  /* --on-dark = light cream regardless of theme. On ink theme,
     --paper is dark, so using it as text colour against forest bg
     produced a dark-on-dark unreadable header. */
  color: var(--on-dark);
  font-weight: 600;
  letter-spacing: 0.07em;
  border-bottom: 0;
  /* Round the band's corners only on the outer cells. */
}
.data-table--matrix thead tr th:first-child { border-top-left-radius:  8px; }
.data-table--matrix thead tr th:last-child  { border-top-right-radius: 8px; }
/* Stronger separation between header and data — a hairline of bronze
 * underneath the forest band, picking up the brand accent. */
.data-table--matrix thead tr { box-shadow: inset 0 -2px 0 var(--bronze); }

.admin-table { font-size: 13.5px; }
.admin-table input[type="text"],
.admin-table input[type="password"],
.admin-table input[type="number"],
.admin-table input[type="date"],
.admin-table input[type="email"],
.admin-table select,
.admin-table textarea {
  padding: 6px 8px;
  border: 1px solid var(--ink-12);
  border-radius: var(--radius-sm);
  background: var(--paper);
  /* Without this explicit colour, inputs fall back to OS-default
     black — which becomes black-on-dark and invisible under the
     forest / ink dark themes (paper is dark there). --ink flips to
     paper-cream on dark themes so the text stays readable in every
     theme. */
  color: var(--ink);
  font: inherit;
}
.admin-table .inline-form { display: inline-block; margin-right: 6px; }

.bulk-table { font-size: 12.5px; }
.bulk-table input,
.bulk-table select,
.bulk-table textarea {
  padding: 5px 7px;
  border: 1px solid var(--ink-12);
  border-radius: var(--radius-sm);
  font: inherit;
  width: 100%;
  background: var(--paper);
  color: var(--ink);
}
.bulk-actions {
  display: flex;
  gap: 14px;
  align-items: center;
  margin-top: 16px;
}

/* ---------------------------------------------------------------------------
 * 11. KPI grid
 * --------------------------------------------------------------------------- */
.kpi-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 14px;
  margin-bottom: 28px;
}
.kpi {
  background: var(--paper);
  border: var(--rule);
  border-radius: var(--radius);
  padding: 16px 18px;
  transition: border-color 0.1s ease;
}
.kpi:hover { border-color: var(--ink-25); }
.kpi__label {
  color: var(--ink-65);
  font-size: 11.5px;
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  margin-bottom: 6px;
}
.kpi__value {
  font-family: var(--serif);
  font-size: 1.6rem;
  font-weight: 400;
  line-height: 1.1;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
}

/* ---------------------------------------------------------------------------
 * 12. Filter bar
 * --------------------------------------------------------------------------- */
.filter-bar {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  align-items: end;
  padding: 14px 16px;
  background: var(--paper);
  border: var(--rule);
  border-radius: var(--radius);
  margin-bottom: 18px;
}
.filter-bar label {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 11.5px;
  color: var(--ink-65);
  font-weight: 500;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.filter-bar select,
.filter-bar input[type="text"],
.filter-bar input[type="date"] {
  padding: 7px 10px;
  border: 1px solid var(--ink-12);
  border-radius: var(--radius-sm);
  background: var(--paper);
  font: 400 13.5px/1.4 var(--sans);
  color: var(--ink);
  min-width: 150px;
}
.filter-bar select:focus,
.filter-bar input:focus {
  outline: none;
  border-color: var(--forest);
  box-shadow: 0 0 0 2px var(--forest-pale);
}
.filter-bar button { align-self: end; }

/* Window picker — segmented control for 7d / 30d / MTD / etc. */
.window-picker { display: inline-flex; gap: 4px; margin-bottom: 18px; }
.window-picker .btn { padding: 6px 12px; font-size: 12.5px; }
.window-picker .btn.active,
.window-picker .btn--active {
  background: var(--forest);
  color: var(--on-dark);
  border-color: var(--forest);
}

/* ---------------------------------------------------------------------------
 * 13. Pagination
 * --------------------------------------------------------------------------- */
.pagination {
  display: flex;
  gap: 14px;
  align-items: center;
  margin-top: 16px;
  padding-top: 14px;
  border-top: var(--rule-soft);
}
.pagination__pos { color: var(--ink-65); font-size: 13px; font-variant-numeric: tabular-nums; }

/* ---------------------------------------------------------------------------
 * 14. Direction badges — inflow / outflow / other
 * --------------------------------------------------------------------------- */
.dir {
  display: inline-block;
  padding: 2px 10px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  border: 1px solid transparent;
  font-feature-settings: "tnum";
}
.dir--inflow  { background: var(--positive-bg); color: var(--positive); border-color: var(--positive); }
.dir--outflow { background: var(--negative-bg); color: var(--negative); border-color: var(--negative); }
.dir--other   { background: var(--ink-07);     color: var(--ink-65);   border-color: var(--ink-12); }

/* ---------------------------------------------------------------------------
 * 15. Wizard progress
 * --------------------------------------------------------------------------- */
.wizard-progress {
  display: flex;
  gap: 6px;
  margin-bottom: 22px;
  font-size: 12.5px;
}
.wizard-progress__step {
  padding: 6px 14px;
  border-radius: 999px;
  background: var(--paper);
  color: var(--ink-65);
  border: 1px solid var(--ink-12);
  font-weight: 500;
  font-feature-settings: "tnum";
}
.wizard-progress__step--current {
  background: var(--forest);
  color: var(--on-dark);
  border-color: var(--forest);
}
.wizard-progress__step--done {
  background: var(--forest-pale);
  color: var(--forest-deep);
  border-color: var(--forest-pale);
}

/* ---------------------------------------------------------------------------
 * 16. Misc utilities
 * --------------------------------------------------------------------------- */
.muted { color: var(--ink-65); font-size: 0.92em; }

.kv {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 8px 22px;
  font-size: 14px;
}
.kv dt {
  color: var(--ink-65);
  font-size: 12.5px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  font-weight: 500;
}
.kv dd { margin: 0; font-variant-numeric: tabular-nums; }

.color-chip {
  display: inline-block;
  width: 14px;
  height: 14px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--ink-12);
  vertical-align: middle;
  margin-left: 6px;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,0.5);
}

.preferred-banks {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 8px 18px;
  margin: 14px 0 18px;
}
.preferred-banks label {
  display: flex;
  gap: 8px;
  align-items: center;
  font-size: 13.5px;
}

.export-menu {
  display: flex;
  gap: 8px;
  align-items: center;
  margin: 14px 0;
  font-size: 12.5px;
  color: var(--ink-65);
}

/* HTMX swap indicator — show a tiny pulse on elements being updated */
.htmx-request { opacity: 0.55; pointer-events: none; transition: opacity 0.1s ease; }

/* ---------------------------------------------------------------------------
 * Transactions filters — pill multi-selects + period pills + KPI strip.
 * Replaces the old single-dropdown filter bar with the streamlit-style
 * "Group by · Holder / Bank / Time" interface.
 * --------------------------------------------------------------------------- */
.txn-filters {
  background: var(--paper);
  border: var(--rule);
  border-radius: var(--radius);
  padding: 18px 20px 14px;
  margin-bottom: 18px;
  display: flex;
  flex-direction: column;
  gap: 18px;
}

.filter-group {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.filter-group--row {
  flex-direction: row;
  flex-wrap: wrap;
  gap: 12px;
  align-items: flex-end;
}
/* Card-bordered variant. Wraps each Group-by section (Holder / Bank /
   Time / the secondary input row) in its own subtle inset border so the
   user reads the filter region as a series of discrete bins instead of
   a flat blob. The outer ``.txn-filters`` already has its own outer
   border — these are the *inner* dividers. */
.filter-group--card {
  border: 1px solid var(--ink-12);
  border-radius: 10px;
  padding: 12px 14px;
  background: rgba(255, 255, 255, 0.02);
}
/* On dark themes the 2% white inset is barely visible against the
   already-dark .txn-filters; bump the contrast a touch. */
[data-theme="forest"] .filter-group--card,
[data-theme="ink"] .filter-group--card {
  background: rgba(0, 0, 0, 0.18);
  border-color: rgba(255, 255, 255, 0.08);
}

/* ── Per-section colour accents ────────────────────────────────────────
 * Each filter-group--card variant carries a thin 4px coloured left-rule
 * + a faint tint of the same hue, so the user can visually anchor
 * "which filter family is this" at a glance without reading the label.
 *
 * Same intensity recipe as ``.saved-reports-hero``: subtle background
 * tint + a coloured left border. The four accent colours all live in
 * the existing palette so the page reads as one cohesive system, not
 * a Christmas tree.
 *
 *   👥 Holder  → forest      (primary green — people = primary subject)
 *   🏦 Bank    → bronze      (banking/money associations)
 *   📅 Time    → leaf        (lighter green — calendar/period)
 *   🔍 Search  → clay        (warm red — "search/scope" intensity)
 *
 * The dark-theme overrides flip the background to a brighter rgba()
 * so the tint stays visible against the already-dark card-surface;
 * the left-rule colour itself doesn't flip (forest/bronze/leaf/clay
 * are fixed tokens that read on every theme).
 */
.filter-group--card--holder {
  border-left: 4px solid var(--forest);
  background: color-mix(in srgb, var(--forest-pale) 18%, var(--paper));
}
[data-theme="forest"] .filter-group--card--holder,
[data-theme="ink"] .filter-group--card--holder {
  background: rgba(45, 90, 55, 0.18);
}

.filter-group--card--bank {
  border-left: 4px solid var(--bronze);
  background: color-mix(in srgb, var(--bronze-pale) 18%, var(--paper));
}
[data-theme="forest"] .filter-group--card--bank,
[data-theme="ink"] .filter-group--card--bank {
  background: rgba(160, 136, 104, 0.18);
}

.filter-group--card--time {
  /* Use leaf — distinct enough from forest to differentiate at a
     glance, still in the green family so it doesn't fight the rest. */
  border-left: 4px solid #2d5a37;
  background: color-mix(in srgb, #2d5a37 8%, var(--paper));
}
[data-theme="forest"] .filter-group--card--time,
[data-theme="ink"] .filter-group--card--time {
  background: rgba(105, 175, 120, 0.14);
}

.filter-group--card--search {
  /* Clay — the same warm-red token used for negative-direction
     amounts and the destructive data-tools button. Here it's just
     an accent rule, no danger semantics; the colour reads as "this
     is a focused-search/inputs zone" against the cooler greens
     above. */
  border-left: 4px solid #a4452f;
  background: color-mix(in srgb, #a4452f 6%, var(--paper));
}
[data-theme="forest"] .filter-group--card--search,
[data-theme="ink"] .filter-group--card--search {
  background: rgba(204, 110, 90, 0.12);
}
/* The card itself owns the leading label, so squeeze the gap-to-content
   a touch — pills sit closer to their group title. */
.filter-group--card > .filter-group__label:first-child {
  margin-bottom: 8px;
}
/* When the card hosts the secondary row of mixed inputs, switch to a
   CSS grid so every field gets the same column width (instead of the
   flexbox-with-min-width arrangement that left Category orphaned on
   its own row at certain breakpoints). auto-fit + minmax means the row
   wraps cleanly without leaving ragged trailing whitespace. */
.filter-group--card.filter-group--grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
  gap: 12px 14px;
  align-items: end;
}
/* The free-text search field is the one input that benefits from extra
   breathing room — let it span two grid tracks where space allows. */
.filter-group--card.filter-group--grid > .filter-field--grow {
  grid-column: span 2;
  min-width: 0;          /* override default 240 inside the grid */
}
.filter-group__label {
  font-family: var(--sans);
  font-weight: 500;
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-65);
}
.filter-group__sub {
  font-size: 10.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-45);
  font-weight: 500;
  margin: 6px 0 -2px 2px;
}

/* Compact inline field used in the secondary filter row.
   ``min-width: 0`` lets the field shrink inside a CSS grid track so the
   grid's minmax() floor wins (otherwise the 140px min would conflict
   with auto-fit and cause overflow at narrow widths). The legacy
   flex-row layout still gets the visual width via the grid's minmax. */
.filter-field {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
}
.filter-field--grow { flex: 1; min-width: 240px; }
/* Inputs inside the grid card fill their track width — without this
   they default to UA intrinsic width (e.g. ``e.g. 10000`` placeholder
   width) and look uneven. */
.filter-group--grid .filter-field input,
.filter-group--grid .filter-field select {
  width: 100%;
  box-sizing: border-box;
}
.filter-field__label {
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-65);
}
.filter-field input[type="text"],
.filter-field input[type="date"],
.filter-field select {
  padding: 7px 10px;
  border: 1px solid var(--ink-12);
  border-radius: var(--radius-sm);
  background: var(--paper);
  font: 400 13.5px/1.4 var(--sans);
  color: var(--ink);
}
.filter-field input:focus,
.filter-field select:focus {
  outline: none;
  border-color: var(--forest);
  box-shadow: 0 0 0 2px var(--forest-pale);
}

.filter-actions {
  display: flex;
  gap: 10px;
  justify-content: flex-end;
  margin-top: 2px;
}

/* ---- Pill grid ---- */
.pills {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.pill {
  /* Hide the underlying checkbox; the <label> IS the pill */
  display: inline-flex;
  align-items: center;
  padding: 5px 12px;
  border: 1px solid var(--ink-12);
  border-radius: 999px;
  background: var(--paper);
  color: var(--ink-80);
  font-size: 12.5px;
  font-weight: 500;
  cursor: pointer;
  user-select: none;
  transition: background 0.1s ease, color 0.1s ease, border-color 0.1s ease;
  line-height: 1.3;
}
.pill input[type="checkbox"] {
  /* Visually hide but keep keyboard accessible */
  position: absolute;
  opacity: 0;
  pointer-events: none;
  width: 0; height: 0;
}
.pill:hover {
  background: var(--ink-07);
  border-color: var(--ink-25);
  color: var(--ink);
}
/* Pill selected-state styling.
 *
 * Two trigger rules per pill type — comma-OR'd:
 *   .pill--active        — class added at server render time
 *   .pill:has(input:checked)
 *                        — applied as soon as the user clicks the
 *                          checkbox, even before HTMX returns the new
 *                          HTML (HTMX only swaps #txn-rows, not the
 *                          filter form itself, so the server-rendered
 *                          class wouldn't update without this rule).
 *
 * :has() supported in Chrome 105+ / Edge 105+ / Safari 15.4+ /
 * Firefox 121+ — covers ~98% of users today. Older browsers fall
 * back to the .pill--active behaviour (only correct on server-render).
 */
.pill--active,
.pill:has(input[type="checkbox"]:checked) {
  background: var(--forest);
  color: var(--on-dark);
  border-color: var(--forest);
}
.pill--active:hover,
.pill:has(input[type="checkbox"]:checked):hover {
  background: var(--forest-deep);
  border-color: var(--forest-deep);
  color: var(--on-dark);
}

/* Year pills use bronze tone, month pills use sand-bronze — visual
 * variety for the time strip without overwhelming the eye. */
.pill--year.pill--active,
.pill--year:has(input[type="checkbox"]:checked) {
  background: var(--bronze);
  border-color: var(--bronze);
  color: var(--on-dark);
}
.pill--year.pill--active:hover,
.pill--year:has(input[type="checkbox"]:checked):hover {
  background: #66553c;
  border-color: #66553c;
}
.pill--month.pill--active,
.pill--month:has(input[type="checkbox"]:checked) {
  background: var(--bronze-mid);
  border-color: var(--bronze-mid);
  color: var(--on-dark);
}
.pill--month.pill--active:hover,
.pill--month:has(input[type="checkbox"]:checked):hover {
  background: var(--bronze);
  border-color: var(--bronze);
}

/* Subtle indicator on the un-selected pill state too — so people see
 * the cluster is interactive even at rest. The active state's strong
 * tint then reads as the answer to "what did I pick". */
.pill { position: relative; }

/* Keyboard focus indicator on the wrapping label (since the checkbox is hidden) */
.pill input[type="checkbox"]:focus-visible + span {
  outline: 2px solid var(--bronze);
  outline-offset: 3px;
  border-radius: 999px;
}

/* ---- Transactions export card ---- */
.export-card {
  background: var(--paper);
  border: var(--rule);
  border-radius: var(--radius);
  padding: 16px 20px;
  margin: 14px 0 18px;
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.export-card__row {
  display: flex;
  gap: 18px;
  align-items: flex-start;
  flex-wrap: wrap;
}
.export-card__label {
  font-size: 11.5px;
  font-weight: 500;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: var(--ink-65);
  min-width: 90px;
  padding-top: 6px;     /* baseline-align with the radios + buttons */
}
.export-card__styles {
  border: none;
  padding: 0;
  margin: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 6px 14px;
  flex: 1;
}
.export-style {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 6px 10px;
  border-radius: var(--radius-sm);
  cursor: pointer;
  font-size: 13.5px;
  color: var(--ink-80);
  transition: background 0.1s ease;
}
.export-style:hover { background: var(--ink-07); }
.export-style input[type="radio"] {
  accent-color: var(--forest);
  flex-shrink: 0;
  margin-top: 2px;
}
.export-style em {
  font-family: var(--serif);
  font-style: italic;
  color: var(--ink-65);
  font-size: 12.5px;
  margin-left: 2px;
}
/* Subtle indicator on the chosen style (uses :has() for the checked
 * radio; the rest of the row stays plain).  */
.export-style:has(input[type="radio"]:checked) {
  background: var(--forest-pale);
  color: var(--forest-deep);
}
.export-card__formats {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}
/* "visually-hidden" — hide from sight but keep for screen readers */
.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0,0,0,0);
  white-space: nowrap; border: 0;
}

/* ---- Transactions KPI strip ---- */
.txn-kpis {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 12px;
  margin-bottom: 14px;
}
@media (max-width: 760px) {
  .txn-kpis { grid-template-columns: repeat(2, 1fr); }
}
.txn-kpi {
  background: var(--paper);
  border: var(--rule);
  border-radius: var(--radius);
  padding: 14px 18px;
  border-left: 3px solid var(--ink-12);
}
.txn-kpi--in  { border-left-color: var(--positive); }
.txn-kpi--out { border-left-color: var(--negative); }
.txn-kpi--net { border-left-color: var(--bronze); }
.txn-kpi__label {
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: var(--ink-65);
  margin-bottom: 6px;
}
.txn-kpi__value {
  font-family: var(--serif);
  font-size: 1.45rem;
  font-weight: 400;
  line-height: 1.1;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
}
.txn-kpi__caption {
  font-size: 11.5px;
  color: var(--ink-65);
  margin-top: 3px;
}
.txn-kpi--in  .txn-kpi__value { color: var(--positive); }
.txn-kpi--out .txn-kpi__value { color: var(--negative); }

/* ---------------------------------------------------------------------------
 * Dashboard hero (Balance Matrix landing page)
 * Visual reference: vanshOS Brand Book v1.0 + family-office dashboard mock.
 * --------------------------------------------------------------------------- */
.dashboard-hero {
  padding: 8px 0 28px;
  margin-bottom: 12px;
  border-bottom: var(--rule);
}
.dashboard-hero__eyebrow {
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-65);
  margin-bottom: 16px;
}
.dashboard-hero__sep { margin: 0 4px; }

.dashboard-hero__row {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 24px;
  flex-wrap: wrap;
}
.dashboard-hero__greeting {
  font-family: var(--serif);
  font-weight: 300;
  font-size: clamp(2.2rem, 4vw, 3.4rem);
  line-height: 1.05;
  letter-spacing: -0.025em;
  color: var(--ink);
  margin: 0;
}
.dashboard-hero__greeting em {
  font-family: var(--serif);
  font-style: italic;
  font-weight: 400;
  color: var(--forest);
}
.dashboard-hero__actions {
  display: flex;
  gap: 10px;
  flex-shrink: 0;
}
.muted-on-dark { color: rgba(255, 255, 255, 0.55); font-weight: 400; }

/* ---- KPI strip ---- */
.dash-kpis {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 14px;
  margin: 28px 0 28px;
}
.dash-kpi {
  background: var(--paper);
  border: var(--rule);
  border-radius: var(--radius);
  padding: 20px 22px;
}
.dash-kpi__label {
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-65);
  margin-bottom: 10px;
}
.dash-kpi__value {
  font-family: var(--serif);
  font-size: 2rem;
  font-weight: 300;
  line-height: 1.05;
  color: var(--ink);
  letter-spacing: -0.015em;
  font-variant-numeric: tabular-nums;
  margin-bottom: 4px;
}
/* Full-precision rupee figure sitting under the compact headline.
   Audit-value line — small + muted so the eye reads the headline
   first, but anyone reconciling against a bank statement can still
   see the exact paise without leaving the page. */
.dash-kpi__full {
  font-size: 12px;
  color: var(--ink-65);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0;
  margin-bottom: 6px;
}
.dash-kpi__caption {
  font-size: 12px;
  color: var(--ink-65);
}

/* OD-additional sub-line on the Net Balance tile. Sits between the
   audit-precision figure and the caption — calls out the OD line of
   credit as available headroom without folding it into the headline
   net number. Bronze accent to visually distinguish "credit
   available" from "money owned" (the figures above it). */
.dash-kpi__od {
  font-size: 12.5px;
  color: var(--bronze);
  font-variant-numeric: tabular-nums;
  margin: -2px 0 6px;
  line-height: 1.3;
}
.dash-kpi__od strong { font-weight: 600; }
[data-theme="forest"] .dash-kpi__od,
[data-theme="ink"]    .dash-kpi__od { color: var(--bronze-mid); }

/* ---- Two-card row ---- */
.dash-grid {
  display: grid;
  grid-template-columns: 2fr 1fr;
  gap: 18px;
  margin-bottom: 18px;
}
@media (max-width: 920px) { .dash-grid { grid-template-columns: 1fr; } }

.dash-card {
  background: var(--paper);
  border: var(--rule);
  border-radius: var(--radius);
  padding: 22px 24px;
}
.dash-card--wide { grid-column: 1 / -1; margin-bottom: 18px; }

.dash-card__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 14px;
  margin-bottom: 16px;
}
.dash-card__title {
  font-family: var(--serif);
  font-weight: 400;
  font-size: 1.1rem;
  letter-spacing: -0.005em;
  color: var(--ink);
  margin: 0;
}
.dash-card__sub {
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-45);
}
.dash-card__legend {
  font-size: 12px;
  color: var(--ink-65);
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.dash-card__legend-dot {
  display: inline-block;
  width: 8px; height: 8px;
  border-radius: 50%;
}
.dash-card__legend-dot--ink { background: var(--forest); }

.dash-card__empty {
  padding: 36px 8px;
  text-align: center;
  color: var(--ink-45);
  font-size: 13px;
}

/* ---- Net-worth chart axis ---- */
.dash-card__chart { position: relative; }
.dash-card__chart-axis {
  display: flex;
  justify-content: space-between;
  font-size: 11px;
  color: var(--ink-45);
  margin-top: 6px;
  letter-spacing: 0.04em;
  font-variant-numeric: tabular-nums;
}

/* ---- Allocation donut + legend ---- */
.dash-allocation {
  display: flex;
  align-items: center;
  gap: 22px;
  flex-wrap: wrap;
}
.dash-donut {
  position: relative;
  width: 132px;
  height: 132px;
  border-radius: 50%;
  flex-shrink: 0;
}
.dash-donut__hole {
  position: absolute;
  inset: 22px;
  background: var(--paper);
  border-radius: 50%;
}
.dash-allocation__legend {
  list-style: none;
  margin: 0;
  padding: 0;
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 7px;
  min-width: 160px;
}
.dash-allocation__legend li {
  display: flex;
  align-items: center;
  gap: 9px;
  font-size: 13px;
  color: var(--ink-80);
}
.dash-allocation__swatch {
  display: inline-block;
  width: 10px; height: 10px;
  border-radius: 2px;
  flex-shrink: 0;
}
.dash-allocation__name { flex: 1; }
.dash-allocation__pct {
  font-variant-numeric: tabular-nums;
  font-weight: 500;
  color: var(--ink);
  font-feature-settings: "tnum";
}

/* ---- Activity feed ---- */
.dash-activity {
  list-style: none;
  margin: 0;
  padding: 0;
}
.dash-activity__row {
  display: grid;
  grid-template-columns: 32px 1fr auto auto;
  gap: 14px;
  align-items: center;
  padding: 12px 0;
  border-bottom: var(--rule-soft);
  font-size: 13.5px;
}
.dash-activity__row:last-child { border-bottom: none; }

.dash-activity__icon {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.dash-activity__icon--in {
  background: var(--positive-bg);
  color: var(--positive);
}
.dash-activity__icon--out {
  background: var(--negative-bg);
  color: var(--negative);
}

.dash-activity__body { min-width: 0; }
.dash-activity__title {
  font-weight: 500;
  color: var(--ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  font-family: var(--serif);
  font-size: 0.95rem;
}
.dash-activity__meta {
  font-size: 11px;
  color: var(--ink-45);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  margin-top: 2px;
}
.dash-activity__sep { margin: 0 4px; }

.dash-activity__account {
  font-size: 12.5px;
  color: var(--ink-65);
  text-align: right;
  white-space: nowrap;
}
.dash-activity__amount {
  font-family: var(--mono);
  font-size: 13.5px;
  font-weight: 500;
  text-align: right;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum";
}
.dash-activity__amount--in  { color: var(--positive); }
.dash-activity__amount--out { color: var(--negative); }

/* ---------------------------------------------------------------------------
 * Import-wizard "Importing into …" card
 * Sits at the top of each wizard step so the user can confirm the account
 * context before committing. See app/templates/imports/_account_header.html.
 * --------------------------------------------------------------------------- */
.import-account-card {
  background: var(--paper);
  border: var(--rule);
  border-left: 3px solid var(--forest);
  border-radius: var(--radius);
  padding: 14px 18px;
  margin: 0 0 20px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.import-account-card--warn {
  border-left-color: var(--warn);
  background: var(--warn-bg);
}
.import-account-card__label {
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-65);
}
.import-account-card__primary {
  font-family: var(--serif);
  font-size: 1.25rem;
  font-weight: 400;
  line-height: 1.25;
  color: var(--ink);
  letter-spacing: -0.005em;
}
.import-account-card__primary .mono {
  font-family: var(--mono);
  font-size: 0.92em;
  font-weight: 500;
  color: var(--ink);
  font-feature-settings: "tnum";
}
.import-account-card__meta {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 18px;
  font-size: 13px;
  color: var(--ink-65);
  margin-top: 4px;
}
.import-account-card__meta strong {
  font-weight: 500;
  color: var(--ink-80);
}
.import-account-card__meta .mono {
  font-family: var(--mono);
  font-size: 0.92em;
}

/* ---------------------------------------------------------------------------
 * Scrollbars — make the OS-default chrome match the warm palette on
 * WebKit. Firefox uses scrollbar-color (color/track pair).
 * --------------------------------------------------------------------------- */
* {
  scrollbar-width: thin;
  scrollbar-color: var(--ink-25) transparent;
}
*::-webkit-scrollbar { width: 10px; height: 10px; }
*::-webkit-scrollbar-track { background: transparent; }
*::-webkit-scrollbar-thumb { background: var(--ink-12); border-radius: 999px; border: 2px solid transparent; background-clip: padding-box; }
*::-webkit-scrollbar-thumb:hover { background: var(--ink-25); background-clip: padding-box; border: 2px solid transparent; }

/* ============================================================================
 * Reports builder — /reports
 *
 * Streamlit-style ad-hoc report builder with search chips, 3-level groupby,
 * treemap + bar visualisations, and a collapsible drill-down tree.
 * ============================================================================ */

/* Chip multiselect for search terms ---------------------------------------- */
.report-builder__terms {
  display: flex; flex-wrap: wrap; gap: 8px; align-items: center;
  padding: 6px 8px;
  background: var(--paper);
  border: 1px solid var(--ink-12);
  border-radius: 10px;
  min-height: 40px;
}
.chip {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 4px 4px 4px 10px;
  background: var(--bronze-pale);
  border-radius: 999px;
  font-size: 0.875rem; color: var(--ink);
}
.chip input[name="terms"] {
  background: transparent; border: 0; outline: 0;
  font: inherit; color: inherit;
  min-width: 80px; width: 14ch;
  padding: 0; margin: 0;
}
.chip__x {
  background: transparent; border: 0;
  color: var(--bronze); cursor: pointer;
  width: 22px; height: 22px; border-radius: 999px;
  font-size: 1.05rem; line-height: 1; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  transition: background 0.1s ease;
}
.chip__x:hover { background: rgba(0,0,0,0.08); }

.btn--small { padding: 4px 10px; font-size: 0.85rem; }

/* ============================================================================
 * Reports builder — two-box AND-of-OR search-term layout
 * Two side-by-side chip-input boxes with a visible "AND" badge
 * between them. Each box behaves like the old single-box (OR within
 * the box); rows must hit BOTH boxes to be kept.
 * ============================================================================ */
.report-builder__terms-pair {
  /* Wider than the legacy single-box layout — the two boxes need
     breathing room. */
  width: 100%;
}
.report-builder__terms-row {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  gap: 16px;
  align-items: stretch;
  margin-top: 6px;
}
.report-builder__terms-box {
  background: var(--paper);
  border: 1px solid var(--ink-12);
  border-radius: 10px;
  padding: 10px 12px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.report-builder__terms-box-label {
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-65);
}
.report-builder__terms-and {
  align-self: center;
  font-family: var(--serif);
  font-size: 0.95rem;
  font-weight: 500;
  letter-spacing: 0.18em;
  color: var(--bronze);
  padding: 6px 10px;
  border: 1px dashed var(--bronze-pale);
  border-radius: 999px;
  background: var(--card);
}
@media (max-width: 720px) {
  .report-builder__terms-row {
    grid-template-columns: 1fr;
  }
  .report-builder__terms-and {
    justify-self: center;
  }
}

/* ============================================================================
 * Generic bordered "filter card" — same look as the AND/OR term boxes
 * above, applied to other filter clusters (holder pills, bank pills,
 * grouping + date/direction row). Gives the builder a clear visual
 * hierarchy where each filter family is its own card.
 * ============================================================================ */
.filter-card {
  background: var(--paper);
  border: 1px solid var(--ink-12);
  border-radius: 10px;
  padding: 10px 14px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 12px;
}
.filter-card > .filter-group__label {
  /* The label inside a card hugs the top edge — overrides the default
     ``.filter-group__label`` margin so cards look tight. */
  margin-bottom: 2px;
}
/* Row variant — children flow left-to-right and wrap if the viewport
   gets tight. Used for the grouping-and-date card where 6 fields
   share one line on a wide screen but stack gracefully below ~960px. */
.filter-card--row {
  flex-direction: column;
}
.filter-card--row .filter-card__items {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 14px 18px;
}
.filter-card--row .filter-field {
  /* Each select / date input takes its natural width but doesn't
     squeeze below a usable minimum. */
  flex: 0 1 auto;
  min-width: 140px;
}

/* Stacked action buttons living at the right edge of a row-card.
   Pushes itself to the extreme right via auto-margin (works even
   when the flex container wraps), anchors to the bottom of the
   row so the buttons line up with the inputs' baselines. */
.filter-card__actions {
  margin-left: auto;
  display: flex;
  flex-direction: column;
  gap: 6px;
  align-self: flex-end;
  min-width: 110px;
}
.filter-card__actions .btn {
  width: 100%;
}

@media (max-width: 720px) {
  .filter-card--row .filter-field {
    flex: 1 1 calc(50% - 9px);
  }
  /* On narrow viewports the action stack moves to its own row at
     the end of the card; full width buttons read better there. */
  .filter-card__actions {
    margin-left: 0;
    width: 100%;
  }
}

/* ============================================================================
 * Reports — Saved-reports hero + contextual save-current strip
 * ============================================================================
 *
 * Two distinct affordances on /reports, intentionally NOT side-by-side:
 *
 *   .saved-reports-hero   ENTRY point at the TOP of /reports. Accent-
 *                         highlighted card with one-tap chips for every
 *                         saved report + a dropdown fallback. The front
 *                         door for power users who reach /reports
 *                         already knowing which view they want.
 *
 *   .save-current-strip   EXIT point after the builder. Muted by
 *                         default; switches to an accent-highlighted
 *                         state via .save-current-strip--ready when the
 *                         server-side "dirty" flag detects any non-
 *                         default filter.
 *
 * Together they replace the legacy .saved-reports 2-column grid that
 * sat below the builder and gave equal visual weight to two actions
 * with very different intents.
 * ============================================================================ */

/* ── Top hero: Apply a saved report ──────────────────────────────────────── */
.saved-reports-hero {
  background: color-mix(in srgb, var(--forest-pale) 35%, var(--paper));
  border: 1px solid color-mix(in srgb, var(--forest) 30%, var(--ink-12));
  border-left: 4px solid var(--forest);
  border-radius: 12px;
  padding: 16px 20px;
  margin: 0 0 20px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
[data-theme="forest"] .saved-reports-hero,
[data-theme="ink"] .saved-reports-hero {
  background: rgba(255, 255, 255, 0.04);
  border-color: rgba(255, 255, 255, 0.16);
  border-left-color: var(--bronze);
}

.saved-reports-hero__head {
  display: flex;
  align-items: center;
  gap: 10px;
}
.saved-reports-hero__icon { font-size: 1.1rem; line-height: 1; }
.saved-reports-hero__title {
  font-family: var(--serif);
  font-size: 1.05rem;
  font-weight: 500;
  letter-spacing: 0.01em;
  color: var(--ink);
}
.saved-reports-hero__count {
  margin-left: auto;
  font-size: 0.8rem;
  letter-spacing: 0.04em;
}

/* Chip grid — one-tap apply per saved report. */
.saved-reports-hero__chips {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.saved-reports-hero__chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 12px;
  border: 1px solid var(--ink-12);
  border-radius: 999px;
  background: var(--paper);
  color: var(--ink);
  font-size: 0.86rem;
  font-weight: 500;
  text-decoration: none;
  transition: background 0.1s ease, border-color 0.1s ease, color 0.1s ease;
  line-height: 1.3;
}
.saved-reports-hero__chip:hover {
  background: var(--forest);
  border-color: var(--forest);
  color: var(--on-dark);
}
.saved-reports-hero__chip-arrow {
  font-size: 0.7rem;
  opacity: 0.7;
  transition: opacity 0.1s ease;
}
.saved-reports-hero__chip:hover .saved-reports-hero__chip-arrow { opacity: 1; }

/* Dropdown fallback for browsing the full list. */
.saved-reports-hero__pick {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-top: 2px;
}
.saved-reports-hero__pick-label {
  font-size: 0.82rem;
  color: var(--ink-65);
  font-weight: 500;
  letter-spacing: 0.04em;
}
.saved-reports-hero__pick-select {
  min-width: 240px;
  padding: 6px 10px;
  border: 1px solid var(--ink-12);
  border-radius: 8px;
  background: var(--paper);
  color: var(--ink);
  font: 400 13.5px/1.4 var(--sans);
}

/* Manage row inside a <details> — collapsed by default. */
.saved-reports-hero__manage {
  margin-top: 2px;
  border-top: 1px dashed var(--ink-12);
  padding-top: 10px;
}
[data-theme="forest"] .saved-reports-hero__manage,
[data-theme="ink"] .saved-reports-hero__manage {
  border-top-color: rgba(255, 255, 255, 0.10);
}
.saved-reports-hero__manage > summary {
  cursor: pointer;
  font-size: 0.85rem;
  letter-spacing: 0.04em;
  padding: 4px 0;
}
.saved-reports-hero__manage-list {
  list-style: none;
  margin: 8px 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 4px 16px;
}
.saved-reports-hero__manage-list .inline-form {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 4px 0;
}
.saved-reports-hero__manage-name {
  flex: 1;
  font-size: 0.88rem;
  color: var(--ink);
}

.saved-reports-hero__empty {
  margin: 4px 0 0;
  font-size: 0.92rem;
  line-height: 1.5;
}

/* ── Save-current strip ─────────────────────────────────────────────────── */
.save-current-strip {
  margin-top: 16px;
  padding: 12px 16px;
  border: 1px dashed var(--ink-12);
  border-radius: 10px;
  background: rgba(0, 0, 0, 0.02);
  transition: background 0.15s ease, border-color 0.15s ease,
              box-shadow 0.15s ease;
}
[data-theme="forest"] .save-current-strip,
[data-theme="ink"] .save-current-strip {
  background: rgba(255, 255, 255, 0.02);
  border-color: rgba(255, 255, 255, 0.10);
}

/* Dirty state — filters set, save is meaningful. Accent highlight so
   the primary action catches the eye exactly when there's something
   worth saving. */
.save-current-strip--ready {
  border: 1px solid var(--bronze);
  border-left: 4px solid var(--bronze);
  background: color-mix(in srgb, var(--bronze-pale) 28%, var(--paper));
  box-shadow: 0 1px 6px -2px rgba(122, 102, 72, 0.25);
}
[data-theme="forest"] .save-current-strip--ready,
[data-theme="ink"] .save-current-strip--ready {
  background: rgba(255, 255, 255, 0.06);
  border-color: var(--bronze);
  border-left-color: var(--bronze);
}

.save-current-strip__form {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
}
.save-current-strip__lead {
  display: flex;
  align-items: center;
  gap: 12px;
  flex: 0 1 320px;
  min-width: 0;
}
.save-current-strip__icon { font-size: 1.4rem; line-height: 1; }
.save-current-strip__copy { line-height: 1.3; min-width: 0; }
.save-current-strip__title {
  font-size: 0.95rem;
  font-weight: 500;
  color: var(--ink);
}
.save-current-strip__hint {
  font-size: 0.78rem;
  letter-spacing: 0.01em;
  margin-top: 2px;
}
.save-current-strip__name {
  flex: 1 1 240px;
  min-width: 200px;
  padding: 8px 12px;
  border: 1px solid var(--ink-12);
  border-radius: 8px;
  background: var(--paper);
  color: var(--ink);
  font: 400 13.5px/1.4 var(--sans);
}
.save-current-strip__name:disabled {
  opacity: 0.55;
  cursor: not-allowed;
  background: rgba(0, 0, 0, 0.03);
}
[data-theme="forest"] .save-current-strip__name:disabled,
[data-theme="ink"] .save-current-strip__name:disabled {
  background: rgba(0, 0, 0, 0.20);
}
.save-current-strip__form .btn:disabled {
  opacity: 0.35;
  cursor: not-allowed;
}

@media (max-width: 760px) {
  .save-current-strip__form { flex-direction: column; align-items: stretch; }
  .save-current-strip__lead { flex: 0 0 auto; }
  .save-current-strip__name { width: 100%; }
}

/* Chart wrappers ----------------------------------------------------------- */
.report-charts {
  display: grid; grid-template-columns: 1fr 1fr; gap: 20px;
}
@media (max-width: 980px) {
  .report-charts { grid-template-columns: 1fr; }
}
.report-chart {
  background: var(--paper);
  border: 1px solid var(--ink-12);
  border-radius: 12px;
  padding: 14px 16px;
}
.report-chart > summary {
  font-family: var(--serif); font-weight: 600;
  color: var(--forest); cursor: pointer;
  padding: 2px 0 10px 0;
  list-style: none;
}
.report-chart > summary::-webkit-details-marker { display: none; }
.report-chart > summary::before {
  content: "▸"; display: inline-block; width: 18px;
  color: var(--bronze); transition: transform 0.15s ease;
}
.report-chart[open] > summary::before { transform: rotate(90deg); }

/* Drill-down tree --------------------------------------------------------- */
.report-tree {
  background: var(--paper);
  border: 1px solid var(--ink-12);
  border-radius: 12px;
  padding: 6px 10px;
}
.report-node { margin: 4px 0; padding-left: 6px; border-left: 2px solid var(--forest-pale); }
.report-node[open] { border-left-color: var(--bronze); }
.report-node > summary {
  cursor: pointer;
  padding: 6px 4px;
  border-radius: 6px;
  list-style: none;
  display: flex; align-items: center; gap: 10px;
}
.report-node > summary:hover { background: var(--ivory); }
.report-node > summary::-webkit-details-marker { display: none; }
.report-node > summary::before {
  content: "▸"; display: inline-block; width: 14px;
  color: var(--bronze);
}
.report-node[open] > summary::before { content: "▾"; }
.report-node__axis {
  text-transform: uppercase; letter-spacing: 0.05em;
  font-size: 0.7rem; color: var(--bronze);
  background: var(--bronze-pale);
  padding: 2px 8px; border-radius: 4px;
  flex-shrink: 0;
}
/* Label takes whatever width it needs and `flex-grow: 1` makes it
   expand to fill the remaining row width — this pushes the totals
   cluster to the right edge of the row, where the user expects them. */
.report-node__label {
  font-weight: 500; color: var(--ink);
  flex-grow: 1;
}
.report-node__totals {
  display: inline-flex; gap: 14px; align-items: baseline;
  font-size: 0.85rem; font-variant-numeric: tabular-nums;
}
/* Each total cell is a fixed-width [sign][amount] flexbox: sign
   sticks to the LEFT edge, amount snaps to the RIGHT edge. Combined
   with the same min-width across all three cells AND across all
   rows, the numbers line up vertically — easy to scan a drill-down
   column. ``min-width`` chosen so a typical 8-digit Indian-formatted
   amount (e.g. ₹1,89,407.00) fits without ever overflowing.

   Use theme-aware tokens instead of hardcoded earth-tone hex.
   --positive / --negative are bumped to bright leaf-green / coral
   under dark themes (see lines ~2879-2892), so the drill-down
   amounts stay readable on forest + ink + auto-dark. */
.report-node__inflow,
.report-node__outflow,
.report-node__net {
  display: inline-flex;
  justify-content: space-between;
  align-items: baseline;
  min-width: 13ch;
  gap: 4px;
}
.report-node__inflow  { color: var(--positive); }
.report-node__outflow { color: var(--negative); }
.report-node__net     { font-weight: 600; color: var(--forest); }
.report-node__sign {
  flex-shrink: 0;
  font-weight: 600;
}
.report-node__amt {
  flex-shrink: 0;
}
.report-node__body { padding: 4px 0 4px 22px; }

.data-table--compact th,
.data-table--compact td { padding: 4px 8px; font-size: 0.85rem; }

/* ============================================================================
 * Data entry — /entry add-transaction page
 * ============================================================================ */

/* 2-column grid for date/cheque + amount/direction rows */
.form-grid--2 {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px 20px;
  margin-bottom: 16px;
}
@media (max-width: 600px) {
  .form-grid--2 { grid-template-columns: 1fr; }
}
/* Grid items default to min-width: auto, which lets a <select> with long
   <option> text (e.g. transaction particulars) blow out its 1fr track and
   shove the second column off-screen. Forcing min-width: 0 on the items
   and width: 100% on the inputs/selects inside keeps each column inside
   its 1fr slot regardless of intrinsic content width. */
.form-grid--2 > * { min-width: 0; }
.form-grid--2 .form__field > select,
.form-grid--2 .form__field > input { width: 100%; max-width: 100%; }

.form__field--radio-group {
  border: 0; padding: 0; margin: 0;
  display: flex; flex-direction: column;
}
.form__field--radio-group > legend.form__label { padding: 0; margin-bottom: 6px; }
.form__field--radio-group > .radio-pill + .radio-pill { margin-top: 6px; }

.radio-pill {
  display: inline-flex; align-items: center;
  gap: 10px; padding: 8px 14px;
  border: 1px solid var(--ink-12);
  border-radius: 10px;
  background: var(--paper); cursor: pointer;
  transition: border-color 0.1s ease, background 0.1s ease;
  font-size: 0.95rem;
}
.radio-pill:hover { border-color: var(--bronze); }
.radio-pill input[type="radio"] { margin: 0; accent-color: var(--forest); }
.radio-pill em { color: var(--ink-65); font-style: normal; font-size: 0.85rem; }
.radio-pill:has(input[type="radio"]:checked) {
  background: var(--forest-pale); border-color: var(--forest);
  /* --forest-pale is a fixed light sage in every theme (never redefined
     in any [data-theme=...] block), but --ink flips to light cream on
     dark themes. Without an explicit color, the checked-pill text reads
     light-on-light-sage → invisible. Pin the text to the dark ink value
     so the checked pill is always legible regardless of theme. */
  color: #181a17;
}
.radio-pill:has(input[type="radio"]:checked) em { color: rgba(24, 26, 23, 0.65); }

.data-entry-tabs { gap: 4px; flex-wrap: wrap; }
.data-entry-help { margin-top: 10px; max-width: 760px; }
.data-entry-picker { margin: 16px 0 12px 0; gap: 12px; align-items: flex-end; }
.data-entry-balance {
  padding: 8px 12px;
  background: var(--bronze-pale);
  border-radius: 8px;
  display: inline-flex; gap: 10px;
  margin: 8px 0 16px 0;
  font-size: 0.95rem;
}
.data-entry-empty { padding: 32px 0; font-size: 1.05rem; }

.data-entry-recent {
  margin-top: 32px;
  padding: 18px 22px;
  background: var(--paper);
  border: 1px solid var(--ink-12);
  border-radius: 12px;
}
.data-entry-recent__title {
  font-family: var(--serif);
  font-size: 1.1rem; font-weight: 600;
  color: var(--forest); margin: 0 0 12px 0;
}
.data-entry-recent__title .muted { font-weight: 400; font-size: 0.9rem; }

.form__hint { font-size: 0.85rem; }
.form__actions { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; }

/* When .form__actions sits as a final direct child of a .form (the
   common "submit row at the bottom of a vertical form" pattern), give
   it real breathing room from the last input + a dashed top divider
   so the primary CTA reads as a distinct zone instead of crowding the
   text box above it. The 18px gap from .form alone wasn't enough —
   users were perceiving the button as visually stuck to the last field.
   The :last-child guard keeps mid-form action rows (rare but possible)
   from getting the divider. */
.form > .form__actions:last-child {
  margin-top: 6px;
  padding-top: 18px;
  border-top: 1px dashed var(--ink-12);
}
/* Dashed dividers in dark themes need a brighter rule color or they
   disappear into the card surface. */
[data-theme="forest"] .form > .form__actions:last-child,
[data-theme="ink"] .form > .form__actions:last-child {
  border-top-color: rgba(255, 255, 255, 0.10);
}

/* ============================================================================
 * Bank-wise summary cards — /overview Balance Matrix page
 *
 * Ports the streamlit "Total bank-wise · summary cards" grid (app.py:1659).
 * Each card has:
 *   - a 4px coloured top ribbon driven by the matched BankAlertTier
 *     (the colour is set via the inline `--ribbon` custom property so the
 *     template can data-bind without us emitting one class per tier).
 *   - a small tier-label pill in the top-right with the same colour.
 *   - the bank name in serif (Spectral), the balance in large sans
 *     (Geist), and a tiny holder-count footer.
 *   - an optional pulse animation on cards in the highest tier
 *     (`.bank-card--pulse`) — draws the eye to the most-urgent ones.
 * ============================================================================ */
.bank-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 18px;
  margin: 16px 0 32px 0;
}

.bank-card {
  position: relative;
  background: var(--paper);
  border: 1px solid var(--ink-12);
  border-top: 4px solid var(--ribbon, var(--bronze));
  border-radius: 12px;
  padding: 18px 20px 14px 20px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  transition: transform 0.12s ease, box-shadow 0.12s ease;
}
.bank-card:hover {
  transform: translateY(-2px);
  box-shadow: 0 6px 20px rgba(24, 26, 23, 0.08);
}

.bank-card__head {
  display: flex; justify-content: space-between; align-items: flex-start;
  gap: 10px; margin-bottom: 4px;
}
.bank-card__name {
  font-family: var(--serif); font-weight: 600;
  font-size: 1.05rem; color: var(--forest-deep);
  margin: 0; line-height: 1.2;
  /* Long bank names ("STANDARD CHARTERED") get to wrap rather than
     overflow the card; CHF is intentional. */
  word-break: break-word;
}
.bank-card__tier-badge {
  font-size: 0.65rem; font-weight: 600;
  letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--on-dark);
  padding: 3px 8px; border-radius: 4px;
  flex-shrink: 0; line-height: 1.2;
  white-space: nowrap;
}
.bank-card__pref {
  font-size: 0.7rem; color: var(--bronze);
  font-weight: 500; flex-shrink: 0;
  white-space: nowrap;
}

.bank-card__amount {
  font-family: var(--sans);
  font-size: 1.5rem; font-weight: 600;
  color: var(--ink); letter-spacing: -0.01em;
  font-variant-numeric: tabular-nums;
  margin-top: 4px;
}
/* Sibling line carrying the exact rupee total for the bank card.
   Matches the KPI strip's audit-value pattern. Only renders when
   the balance is ≥ ₹1 L (below that, the compact filter already
   shows the full number, so a sibling line would be redundant). */
.bank-card__full {
  font-size: 0.78rem;
  color: var(--ink-65);
  font-variant-numeric: tabular-nums;
  margin-top: 2px;
  line-height: 1.2;
}
.bank-card__usd {
  font-size: 0.85rem; line-height: 1.4;
  margin-top: -2px;
}
.bank-card__usd-equiv {
  margin-left: 6px; font-size: 0.78rem;
  color: var(--ink-45);
}

.bank-card__foot {
  margin-top: auto; padding-top: 8px;
  font-size: 0.8rem;
  border-top: 1px solid var(--ink-07);
}

/* Pulse animation for top-tier cards. Subtle — just enough to draw the
 * eye without becoming distracting. Respects prefers-reduced-motion. */
.bank-card--pulse {
  animation: bank-card-pulse 2.4s ease-in-out infinite;
}
@keyframes bank-card-pulse {
  0%, 100% { box-shadow: 0 0 0 0 var(--ribbon, transparent); }
  50%      { box-shadow: 0 0 0 6px rgba(0, 0, 0, 0); }
}
@media (prefers-reduced-motion: reduce) {
  .bank-card--pulse { animation: none; }
}

/* ============================================================================
 * Bank card click-to-drill (extends the .bank-card grid above)
 *
 * The cards are now <button>s so the keyboard + screen-reader story is
 * built-in (Enter/Space + aria-expanded). Click toggles an Alpine.js
 * ``active`` state on the wrapper; one drill panel per bank lives in
 * the DOM and only the active one is visible.
 * ============================================================================ */

/* Make the card behave like a real card while remaining a <button> */
.bank-card--btn {
  font: inherit; color: inherit; text-align: left;
  width: 100%; cursor: pointer;
  appearance: none;
  /* Override default button paddings — .bank-card already has its own */
}
.bank-card--btn:focus-visible {
  outline: 2px solid var(--forest);
  outline-offset: 2px;
}
.bank-card--open {
  /* Make the active card visually anchored — bronze ring + slight
     lift so it's obvious which card's data is shown below. */
  box-shadow: 0 0 0 2px var(--bronze), 0 6px 20px rgba(24, 26, 23, 0.10);
  transform: translateY(-2px);
}

.bank-card__chevron {
  font-size: 0.95rem; color: var(--bronze);
  transition: transform 0.12s ease;
  margin-left: auto;  /* push to the right inside the footer flex */
  display: inline-block;
}
.bank-card__chevron--open { transform: rotate(180deg); }
.bank-card__foot {
  display: flex; justify-content: space-between; align-items: center;
}

[x-cloak] { display: none !important; }

/* Drill panel ------------------------------------------------------------- */
.bank-drill {
  margin: 16px 0 32px 0;
  padding: 18px 22px 22px 22px;
  background: var(--paper);
  border: 1px solid var(--ink-12);
  border-left: 3px solid var(--bronze);
  border-radius: 12px;
}
.bank-drill__head {
  display: flex; justify-content: space-between; align-items: center;
  margin-bottom: 14px;
}
.bank-drill__title {
  font-family: var(--serif); font-weight: 600;
  font-size: 1.15rem; color: var(--forest-deep);
  margin: 0;
}
.bank-drill__icon { margin-right: 4px; }
.bank-drill__close {
  background: transparent; border: 0; cursor: pointer;
  width: 32px; height: 32px; border-radius: 999px;
  color: var(--bronze); font-size: 1.4rem; line-height: 1;
  display: inline-flex; align-items: center; justify-content: center;
  transition: background 0.1s ease;
}
.bank-drill__close:hover { background: var(--bronze-pale); }

.bank-drill__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 14px;
}

/* Category section inside the drill panel.
   Each section gets a thin top-rule + a header line carrying the
   category name and its section subtotal, then the existing holder
   card grid sits below. Stacks of sections separate visually without
   reading as separate cards — same panel, just labelled bands. */
.bank-drill__section {
  margin-top: 18px;
}
.bank-drill__section:first-child {
  margin-top: 8px;
}
.bank-drill__section-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  padding: 6px 0 10px;
  margin-bottom: 6px;
  border-bottom: 1px solid var(--ink-12);
}
.bank-drill__section-name {
  font-family: var(--sans);
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  color: var(--bronze);
}
.bank-drill__section-meta {
  font-size: 0.82rem;
  font-variant-numeric: tabular-nums;
}

/* Holder mini-card inside the drill panel --------------------------------- */
.holder-card {
  position: relative;
  background: var(--paper);
  border: 1px solid var(--ink-12);
  border-top: 4px solid var(--ribbon, var(--bronze));
  border-radius: 10px;
  padding: 14px 16px 12px 16px;
  display: flex; flex-direction: column; gap: 4px;
}
.holder-card__tier-badge {
  position: absolute; top: -8px; right: 10px;
  font-size: 0.62rem; font-weight: 600;
  letter-spacing: 0.05em; text-transform: uppercase;
  color: var(--on-dark);
  padding: 3px 7px; border-radius: 4px;
  line-height: 1; white-space: nowrap;
}
.holder-card__name {
  font-family: var(--serif); font-weight: 600;
  font-size: 0.95rem; color: var(--forest-deep);
  line-height: 1.2;
}
.holder-card__amount {
  font-family: var(--sans);
  font-size: 1.2rem; font-weight: 600;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
  margin-top: 2px;
}
.holder-card__sub {
  font-size: 0.78rem;
  line-height: 1.35;
  margin-top: 2px;
}
.holder-card__sep { color: var(--ink-25); margin: 0 4px; }

.holder-card--pulse {
  animation: bank-card-pulse 2.4s ease-in-out infinite;
}

/* ============================================================================
 * Holder mini-cards — account-type colouring
 *
 * Each holder card now carries a typed chip ("SAVING" / "CURRENT" / "OD"
 * / "OTHER") with a colour that matches a faint background wash on the
 * card. The colours are picked from the brand palette:
 *
 *   SAVING   — forest tint  (long-term, "growth" mood)
 *   CURRENT  — bronze tint  (operating capital, "working" mood)
 *   OD       — clay tint    (debit, "attention" mood)
 *   OTHER    — neutral grey (fallback for unknown types)
 *
 * The card's top ribbon (alert tier) still wins the eye — the type
 * colour is a secondary signal layered on top of paper. This keeps the
 * alert hierarchy intact while giving an instant read of the kind of
 * account.
 * ============================================================================ */

/* Base chip — a small uppercase pill. */
.holder-card__type {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 4px;
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  line-height: 1.4;
  white-space: nowrap;
}

/* Saving — forest green */
.holder-card__type--saving {
  background: var(--forest-pale);
  color: var(--forest-deep);
}
.holder-card--type-saving {
  background:
    linear-gradient(
      to bottom,
      color-mix(in srgb, var(--forest-pale) 40%, transparent) 0%,
      transparent 80%
    ),
    var(--paper);
}

/* Current — bronze warm */
.holder-card__type--current {
  background: var(--bronze-pale);
  color: var(--bronze);
}
.holder-card--type-current {
  background:
    linear-gradient(
      to bottom,
      color-mix(in srgb, var(--bronze-pale) 35%, transparent) 0%,
      transparent 80%
    ),
    var(--paper);
}

/* OD — clay (red-brown) — picked up automatically when the helper
   surfaces an "OD" account type in the future. */
.holder-card__type--od {
  background: #f3d6c8;
  color: #6e2e1d;
}
.holder-card--type-od {
  background:
    linear-gradient(
      to bottom,
      rgba(243, 214, 200, 0.40) 0%,
      transparent 80%
    ),
    var(--paper);
}

/* Other — neutral fallback */
.holder-card__type--other {
  background: var(--ink-07);
  color: var(--ink-65);
}

/* ─── AccountType badge colours ──────────────────────────────────────────
 * The badge now reflects the holder's real entity shape from
 * ``AccountType`` (Individual / Company / HUF / Partnership / Trust)
 * — set per-account in masters. Category sits in the section header
 * above the card, so it doesn't need to repeat on the badge.
 *
 * Each chip class colours the badge text; the corresponding
 * ``--type-<slug>`` class paints a faint tint on the whole card so
 * a glance separates personal vs company vs trust without reading.
 * ─────────────────────────────────────────────────────────────────────── */

/* Individual — most common, leans warm-green to feel personal. */
.holder-card__type--individual {
  background: var(--forest-pale);
  color: var(--forest-deep);
}
.holder-card--type-individual {
  background:
    linear-gradient(
      to bottom,
      color-mix(in srgb, var(--forest-pale) 40%, transparent) 0%,
      transparent 80%
    ),
    var(--paper);
}

/* Company — bronze, signals the entity side of the book. */
.holder-card__type--company {
  background: var(--bronze-pale);
  color: var(--bronze);
}
.holder-card--type-company {
  background:
    linear-gradient(
      to bottom,
      color-mix(in srgb, var(--bronze-pale) 35%, transparent) 0%,
      transparent 80%
    ),
    var(--paper);
}

/* HUF — golden sand; sits between Individual and Company shape. */
.holder-card__type--huf {
  background: #f0e3c4;
  color: #6b5b2e;
}
.holder-card--type-huf {
  background:
    linear-gradient(
      to bottom,
      rgba(240, 227, 196, 0.45) 0%,
      transparent 80%
    ),
    var(--paper);
}

/* Partnership — clay (warm red-brown). Reuses the same hue family
   the old --od class used; partnerships are joint commercial entities,
   feels right to lean warmer. */
.holder-card__type--partnership {
  background: #f3d6c8;
  color: #6e2e1d;
}
.holder-card--type-partnership {
  background:
    linear-gradient(
      to bottom,
      rgba(243, 214, 200, 0.40) 0%,
      transparent 80%
    ),
    var(--paper);
}

/* Trust — formal, cool grey-blue tone. */
.holder-card__type--trust {
  background: #d9def0;
  color: #2c3a5e;
}
.holder-card--type-trust {
  background:
    linear-gradient(
      to bottom,
      rgba(217, 222, 240, 0.45) 0%,
      transparent 80%
    ),
    var(--paper);
}

/* Tighten the sub-line spacing once the chip replaces the plain text. */
.holder-card__sub {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
  font-size: 0.78rem;
  line-height: 1.35;
  margin-top: 4px;
}

/* ============================================================================
 * Transaction row actions — Phase 2 of the access model.
 * The pencil + trash buttons in the last column of /transactions.
 * Compact + low-contrast at rest; pop on hover.
 * ============================================================================ */
.txn-row-actions {
  white-space: nowrap;
  text-align: right;
}

/* Narration cell on /transactions + the recent-entries panel.
   Narration can be very long (multi-sentence notes); cap the column
   at ~24 chars and let the rest spill to a ``title`` tooltip on hover.
   Muted colour + smaller line-height so the narration reads as
   subordinate context to the bolder Particulars in the column beside
   it. */
.txn-narration {
  max-width: 240px;
  font-size: 0.85rem;
  color: var(--ink-65);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ============================================================================
 * Data tools — admin-only destructive bulk-mutation wizards
 * Lives under /admin/data-tools/*. The visual language leans cautious:
 * card surfaces are slightly recessed, the typed-CONFIRM gate uses
 * mono-spaced inputs + a red Execute button so the action's gravity
 * is unmissable.
 * ============================================================================ */
.data-tools-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(360px, 1fr));
  gap: 18px;
  margin-top: 18px;
}
.data-tools-card {
  background: var(--card);
  border: 1px solid var(--ink-12);
  border-radius: 12px;
  padding: 18px 20px;
}
.data-tools-card h2 {
  margin: 0 0 8px;
  font-size: 1.15rem;
}
.data-tools-card__bullets {
  margin: 12px 0 14px 0;
  padding-left: 18px;
  font-size: 0.88rem;
  line-height: 1.55;
}
.data-tools-card__bullets li { margin-bottom: 2px; }

/* Confirmation panel — last step before the destructive POST. */
.data-tools-confirm {
  max-width: 720px;
  margin-top: 18px;
  padding: 20px 24px;
  background: var(--card);
  border: 2px solid var(--negative);
  border-radius: 12px;
  box-shadow: 0 4px 22px -6px rgba(138, 58, 46, 0.20);
}
.data-tools-confirm__head {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 14px;
  padding-bottom: 12px;
  border-bottom: 1px dashed var(--ink-12);
}
.data-tools-confirm__icon {
  font-size: 1.3rem;
  color: var(--negative);
}
.data-tools-confirm__title {
  font-family: var(--serif);
  font-size: 1.2rem;
  font-weight: 500;
  color: var(--ink);
}
.data-tools-confirm__details {
  display: grid;
  grid-template-columns: 140px 1fr;
  gap: 8px 16px;
  margin: 0;
  font-size: 0.92rem;
}
.data-tools-confirm__details dt {
  color: var(--ink-65);
  font-weight: 500;
  text-transform: uppercase;
  font-size: 0.72rem;
  letter-spacing: 0.06em;
  padding-top: 3px;
}
.data-tools-confirm__details dd { margin: 0; }
.data-tools-confirm__chip {
  display: inline-block;
  padding: 3px 10px;
  margin: 2px 4px 2px 0;
  background: var(--paper);
  border: 1px solid var(--ink-12);
  border-radius: 6px;
  font-family: var(--mono);
  font-size: 0.85rem;
  color: var(--ink);
}
.data-tools-confirm__chip--target {
  background: var(--bronze-pale);
  border-color: var(--bronze);
  color: var(--bronze);
  font-weight: 600;
}
.data-tools-confirm__notes {
  margin: 16px 0 0;
  padding: 12px 14px 12px 32px;
  background: var(--paper);
  border-radius: 8px;
  font-size: 0.88rem;
  color: var(--ink-80);
  line-height: 1.5;
}
.data-tools-confirm__warning {
  margin-top: 18px;
  padding: 12px 16px;
  background: var(--negative-bg);
  border-left: 4px solid var(--negative);
  border-radius: 4px;
  font-size: 0.92rem;
  color: var(--ink);
}
.data-tools-confirm__form {
  margin-top: 18px;
  padding-top: 16px;
  border-top: 1px dashed var(--ink-12);
}

/* Disabled Execute button: muted + no-cursor. Re-uses .btn--danger
   tokens elsewhere. */
.data-tools-confirm__form .btn--danger[disabled] {
  opacity: 0.35;
  cursor: not-allowed;
  pointer-events: none;
}

/* Very faint hint above a DataGrid table — used to mention things
   like "Narration is hidden by default". Deliberately low-contrast:
   visible if you're looking for it, ignorable if you're not. */
.datagrid-faint-hint {
  margin: 4px 0 8px;
  font-size: 0.72rem;
  color: var(--ink-45);
  font-style: italic;
  letter-spacing: 0.01em;
}
.datagrid-faint-hint em {
  font-style: normal;
  font-weight: 500;
  color: var(--ink-65);
}
.txn-row-action {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px; height: 26px;
  border-radius: 6px;
  border: 0; background: transparent;
  color: var(--bronze);
  font-size: 14px; line-height: 1; cursor: pointer;
  text-decoration: none;
  transition: background 0.1s ease, color 0.1s ease;
}
.txn-row-action:hover {
  background: var(--bronze-pale);
  color: var(--forest-deep);
}
.txn-row-action--danger { color: #a4452f; }   /* clay */
.txn-row-action--danger:hover {
  background: #f3d6c8;
  color: #6e2e1d;
}
.txn-row-actions .inline-form {
  display: inline-block;
  margin-left: 2px;
}

/* ============================================================================
 * Matrix-extras cells on /overview Balance Matrix
 * Signed-amount columns get coloured per direction: green for credits
 * (positive — broker owes us), red for debits (negative — we owe).
 * ============================================================================ */
.matrix-extra-cell      { font-variant-numeric: tabular-nums; }
.matrix-extra-cell--pos { color: var(--positive); }
.matrix-extra-cell--neg { color: var(--negative); }

/* ============================================================================
 * View-toggle — segmented control on /overview's Balance Matrix header.
 * Three pills (Bank balances / Holdings & limits / Full matrix). The
 * active one fills with forest; inactive ones sit in bronze-pale.
 * Plain <a> links: clicking is a real navigation to /overview?view=...
 * so the URL stays bookmarkable.
 * ============================================================================ */
.view-toggle {
  display: inline-flex;
  gap: 0;
  padding: 3px;
  margin: 12px 0 0 0;
  background: var(--paper);
  border: 1px solid var(--ink-12);
  border-radius: 999px;
  /* Sits inline so it doesn't push the page-header description down too far */
  vertical-align: middle;
}
.view-toggle__pill {
  /* Reset anchor underline (inherited from base `a`) */
  border-bottom: 0;
  padding: 7px 16px;
  border-radius: 999px;
  font-size: 0.85rem;
  font-weight: 500;
  color: var(--ink-65);
  text-decoration: none;
  letter-spacing: 0.01em;
  white-space: nowrap;
  transition: background 0.1s ease, color 0.1s ease;
}
.view-toggle__pill:hover { background: var(--bronze-pale); color: var(--ink); }
.view-toggle__pill--active {
  background: var(--forest);
  color: var(--on-dark);
}
.view-toggle__pill--active:hover {
  background: var(--forest-deep);
  color: var(--on-dark);
}
@media (max-width: 640px) {
  /* On narrow screens the pills stack into a column rather than overflow */
  .view-toggle { flex-direction: column; gap: 2px; }
  .view-toggle__pill { width: 100%; text-align: center; }
}

/* ============================================================================
 * Theme tokens — per-user theme via data-theme attribute on <html>.
 *
 * Paper is the :root default (already defined at the top of this file).
 * Sand / Forest / Ink override the surface + ink tokens; the brand
 * accents (--forest, --bronze) stay the same in every theme so logos
 * + tier badges keep their identity.
 *
 * "auto" follows the OS prefers-color-scheme media query — light = Paper,
 * dark = Forest. Users who explicitly pick paper/sand/forest/ink always
 * get their pick regardless of OS.
 *
 * See app/services/auth_service.py::VALID_THEMES + the picker at
 * /profile.
 * ============================================================================ */

/* ─── Sand: warmer paper.  Surface gets warmer, ink stays dark. ─── */
[data-theme="sand"] {
  --ivory:  #ece6d8;
  --paper:  #f3eede;
  --sand:   #ddd1ba;
  --bone:   #d2c6a9;
  /* Card surface stays solid (slightly off the sand paper). */
  --card:   #fbf6ea;
  /* Ink + alpha ladder stay default; forest + bronze stay default. */
}

/* ─── Dark theme token overrides shared by Forest + Ink ─── */
[data-theme="forest"],
[data-theme="ink"] {
  /* Surfaces → dark green / black; cards lift slightly with a +1
     shade so the layered hierarchy is still legible. */
  --ink:     #f6f4ef;          /* text + foreground flips to paper */
  --ink-80:  rgba(246, 244, 239, 0.85);
  --ink-65:  rgba(246, 244, 239, 0.70);
  --ink-45:  rgba(246, 244, 239, 0.50);
  --ink-25:  rgba(246, 244, 239, 0.28);
  --ink-12:  rgba(246, 244, 239, 0.14);
  --ink-07:  rgba(246, 244, 239, 0.08);
  /* Rules use ink alpha already, so they automatically lighten too. */
}

[data-theme="forest"] {
  --ivory:  #1a2e1f;            /* page = forest */
  --paper:  #233c2a;            /* cards a touch lighter */
  --sand:   #2a4631;
  --bone:   #2e4b35;
  /* Card surface (floating popovers, inputs, dropdowns) — a notch
     brighter than --paper so it pops above the page even when it
     overlaps another --paper panel. Solid colour, not rgba, so the
     dropdown doesn't bleed through to whatever sits beneath. */
  --card:   #2e4a36;
}

[data-theme="ink"] {
  --ivory:  #181a17;            /* near-black page */
  --paper:  #232520;            /* card surface */
  --sand:   #2a2c26;
  --bone:   #2e302b;
  --card:   #2c2e28;
}

/* ─── Auto: follow OS preference. Light OS = Paper (no override
        needed); Dark OS = Forest. ─── */
@media (prefers-color-scheme: dark) {
  [data-theme="auto"] {
    --ivory:  #1a2e1f;
    --paper:  #233c2a;
    --sand:   #2a4631;
    --bone:   #2e4b35;
    --card:   #2e4a36;
    --ink:     #f6f4ef;
    --ink-80:  rgba(246, 244, 239, 0.85);
    --ink-65:  rgba(246, 244, 239, 0.70);
    --ink-45:  rgba(246, 244, 239, 0.50);
    --ink-25:  rgba(246, 244, 239, 0.28);
    --ink-12:  rgba(246, 244, 239, 0.14);
    --ink-07:  rgba(246, 244, 239, 0.08);
  }
}

/* ─── Dark-mode tweaks for individual components ─── */

/* Brand mark: "v" + "ansh" both pick up the dark-text colour by
   default. On dark themes we want them in paper so they're visible
   against the dark surface. ".os" stays bronze in every theme. */
[data-theme="forest"] .brand-mark__v,
[data-theme="ink"] .brand-mark__v,
[data-theme="forest"] .brand-mark__ansh,
[data-theme="ink"] .brand-mark__ansh {
  color: var(--ink);
  font-style: normal;  /* dropping the italic on dark — improves legibility */
}
@media (prefers-color-scheme: dark) {
  [data-theme="auto"] .brand-mark__v,
  [data-theme="auto"] .brand-mark__ansh {
    color: var(--ink);
    font-style: normal;
  }
}

/* Balance Matrix header band uses --forest as its background — on a
   forest theme that'd be invisible (forest-on-forest). Swap to bronze
   so the band still pops. Ink theme keeps forest since the page is
   actually black, not green. */
[data-theme="forest"] .data-table--matrix thead th,
[data-theme="forest"] .data-table--matrix thead th.sticky {
  background: var(--bronze);
}
[data-theme="forest"] .data-table--matrix thead tr {
  box-shadow: inset 0 -2px 0 var(--forest-mid);
}

/* totals-row in tables uses --bone for background. On dark themes
   that's already a darker shade, so contrast is fine — no override
   needed. The num--total cells inherit the same. */

/* Bank card pulse animation uses --ribbon (data-bound) as a shadow.
   In dark themes the card itself is dark, so the box-shadow is barely
   visible against the surrounding ivory which is also dark. Boost
   the pulse intensity on dark themes. */
[data-theme="forest"] .bank-card--pulse,
[data-theme="ink"] .bank-card--pulse {
  animation-name: bank-card-pulse-dark;
}
@keyframes bank-card-pulse-dark {
  0%, 100% { box-shadow: 0 0 0 0 var(--ribbon, transparent); }
  50%      { box-shadow: 0 0 0 8px rgba(255, 255, 255, 0.04); }
}

/* Input fields — the default uses bone-coloured borders which work
   light or dark. But the input background was paper which now
   matches the card surface. Slightly darken so they read as
   "interactive" against the card. */
[data-theme="forest"] .form input[type="text"],
[data-theme="forest"] .form input[type="date"],
[data-theme="forest"] .form input[type="number"],
[data-theme="forest"] .form input[type="email"],
[data-theme="forest"] .form input[type="password"],
[data-theme="forest"] .form select,
[data-theme="forest"] .form textarea,
[data-theme="ink"] .form input[type="text"],
[data-theme="ink"] .form input[type="date"],
[data-theme="ink"] .form input[type="number"],
[data-theme="ink"] .form input[type="email"],
[data-theme="ink"] .form input[type="password"],
[data-theme="ink"] .form select,
[data-theme="ink"] .form textarea {
  background: rgba(0, 0, 0, 0.18);
  color: var(--ink);
}

/* Native <option> elements inside an open <select> popup are rendered by
   the OS, not the page. Chrome on Windows draws the popup with a light
   system background; the option text inherits the <select>'s color which
   on our dark themes (forest/ink) is near-white → invisible light-on-light.
   Force a safe dark-on-light scheme on every <option> so dropdowns are
   readable regardless of theme. (The closed-select display, which IS
   page-DOM, still uses the theme color via the rule above.) */
.form select option,
select option {
  color: #1a1a1a;
  background: #ffffff;
}

/* ============================================================================
 * Theme picker on /profile
 * ============================================================================ */
.theme-picker {
  display: flex; flex-wrap: wrap; gap: 12px;
  margin: 12px 0 24px 0;
}
.theme-swatch {
  display: flex; flex-direction: column; align-items: stretch;
  width: 132px;
  border: 2px solid var(--ink-12);
  border-radius: 10px;
  background: var(--paper);
  cursor: pointer;
  padding: 0;
  overflow: hidden;
  transition: border-color 0.1s ease, transform 0.1s ease, box-shadow 0.1s ease;
}
.theme-swatch:hover {
  border-color: var(--bronze);
  transform: translateY(-1px);
}
.theme-swatch--active {
  border-color: var(--forest);
  box-shadow: 0 0 0 3px var(--forest-pale);
}
.theme-swatch__preview {
  height: 60px;
  display: grid;
  grid-template-columns: 1fr 1fr;
}
.theme-swatch__half { display: block; }
.theme-swatch__label {
  font-family: var(--serif);
  font-weight: 500;
  font-size: 0.95rem;
  padding: 8px 10px 4px;
  color: var(--ink);
}
.theme-swatch__sub {
  font-size: 0.72rem;
  letter-spacing: 0.04em;
  color: var(--ink-65);
  padding: 0 10px 8px;
  text-transform: uppercase;
}

/* Per-theme preview colours — these don't depend on the active theme
   because we want all 5 swatches to always look the same. */
.theme-swatch--paper  .theme-swatch__half:first-child  { background: #f6f4ef; }
.theme-swatch--paper  .theme-swatch__half:last-child   { background: #1a2e1f; }
.theme-swatch--sand   .theme-swatch__half:first-child  { background: #ece6d8; }
.theme-swatch--sand   .theme-swatch__half:last-child   { background: #7a6648; }
.theme-swatch--forest .theme-swatch__half:first-child  { background: #1a2e1f; }
.theme-swatch--forest .theme-swatch__half:last-child   { background: #7a6648; }
.theme-swatch--ink    .theme-swatch__half:first-child  { background: #181a17; }
.theme-swatch--ink    .theme-swatch__half:last-child   { background: #7a6648; }
.theme-swatch--auto   .theme-swatch__half:first-child  { background: linear-gradient(135deg, #f6f4ef 50%, #1a2e1f 50%); }
.theme-swatch--auto   .theme-swatch__half:last-child   { background: linear-gradient(135deg, #f6f4ef 50%, #7a6648 50%); }

/* ============================================================================
 * Dark-theme readability overrides (forest / ink / auto-dark)
 *
 * The Paper theme's state colours (#406b48 olive-green, #8a3a2e clay)
 * read as muddy on dark backgrounds. We brighten them to fully-saturated
 * leaf-green + coral-red so they stay readable in low-light themes,
 * AND swap the bg tokens from light tints to translucent overlays so
 * flash/badge backgrounds stay subtle.
 *
 * We also flip a handful of component-level text colours that were
 * hard-coded to --forest / --forest-deep — invisible on a forest page.
 * ============================================================================ */

/* Shared state-colour overrides across both dark themes. */
[data-theme="forest"],
[data-theme="ink"] {
  --positive:    #7fc78a;                       /* bright leaf green */
  --negative:    #f47a6a;                       /* bright coral red */
  --warn:        #e9b25a;                       /* honey */
  --positive-bg: rgba(127, 199, 138, 0.10);
  --warn-bg:     rgba(233, 178, 90, 0.10);
  --negative-bg: rgba(244, 122, 106, 0.10);
}

/* And mirror the same overrides under auto (OS-dark) so all three
   dark cases share one ruleset. */
@media (prefers-color-scheme: dark) {
  [data-theme="auto"] {
    --positive:    #7fc78a;
    --negative:    #f47a6a;
    --warn:        #e9b25a;
    --positive-bg: rgba(127, 199, 138, 0.10);
    --warn-bg:     rgba(233, 178, 90, 0.10);
    --negative-bg: rgba(244, 122, 106, 0.10);
  }
}

/* ─── Active view-toggle pill ────────────────────────────────────
   Default fill = --forest. On the Forest theme the page IS forest,
   so the active pill becomes invisible. Bronze instead — same brand
   palette, full contrast on both forest and ink. */
[data-theme="forest"] .view-toggle__pill--active,
[data-theme="ink"] .view-toggle__pill--active {
  background: var(--bronze);
  color: var(--on-dark);
}
[data-theme="forest"] .view-toggle__pill--active:hover,
[data-theme="ink"] .view-toggle__pill--active:hover {
  background: var(--bronze-mid);
}
@media (prefers-color-scheme: dark) {
  [data-theme="auto"] .view-toggle__pill--active {
    background: var(--bronze);
    color: var(--on-dark);
  }
  [data-theme="auto"] .view-toggle__pill--active:hover {
    background: var(--bronze-mid);
  }
}

/* ─── Section headings that hard-code --forest as text ─────────── */
/* On dark themes those text colours are forest-on-forest = invisible.
   Flip to --ink, which is paper-coloured under the dark token map. */
[data-theme="forest"] h2,
[data-theme="forest"] h3,
[data-theme="ink"] h2,
[data-theme="ink"] h3,
[data-theme="forest"] .bank-card__name,
[data-theme="ink"]    .bank-card__name,
[data-theme="forest"] .holder-card__name,
[data-theme="ink"]    .holder-card__name,
[data-theme="forest"] .bank-drill__title,
[data-theme="ink"]    .bank-drill__title,
[data-theme="forest"] .data-entry-recent__title,
[data-theme="ink"]    .data-entry-recent__title,
[data-theme="forest"] .dashboard-hero__greeting,
[data-theme="ink"]    .dashboard-hero__greeting,
[data-theme="forest"] .report-chart > summary,
[data-theme="ink"]    .report-chart > summary,
[data-theme="forest"] .report-node__net,
[data-theme="ink"]    .report-node__net {
  color: var(--ink);
}
/* The italic "<em>name</em>" inside the greeting uses --forest as a
   brand accent. On dark themes that's the same colour family as the
   page, so the name becomes nearly invisible. Swap to bronze-mid —
   warmer brand accent that pops against both forest-deep and ink
   backgrounds. */
[data-theme="forest"] .dashboard-hero__greeting em,
[data-theme="ink"]    .dashboard-hero__greeting em {
  color: var(--bronze-mid);
}
@media (prefers-color-scheme: dark) {
  [data-theme="auto"] h2,
  [data-theme="auto"] h3,
  [data-theme="auto"] .bank-card__name,
  [data-theme="auto"] .holder-card__name,
  [data-theme="auto"] .bank-drill__title,
  [data-theme="auto"] .data-entry-recent__title,
  [data-theme="auto"] .dashboard-hero__greeting,
  [data-theme="auto"] .report-chart > summary,
  [data-theme="auto"] .report-node__net {
    color: var(--ink);
  }
  [data-theme="auto"] .dashboard-hero__greeting em {
    color: var(--bronze-mid);
  }
}

/* ─── Sidebar token corrections for sand theme ─────────────────────
   The sidebar uses its own --sb-* token scope (dark always); the page
   tokens we flipped don't affect it. But Sand has a slightly warmer
   page colour that creates a thin contrast band against the sidebar —
   harmless. No override needed here, but logged for future tweaks. */

/* ─── Matrix header band: tighten text contrast on Forest ──────── */
/* Already bronze background. Force the text to ivory (always light)
   so it doesn't pick up a low-contrast `--paper` that varies per
   theme. Same for the totals row's first cell. */
[data-theme="forest"] .data-table--matrix thead th,
[data-theme="forest"] .data-table--matrix thead th.sticky {
  color: #f6f4ef;       /* hard-coded ivory; readable on any bronze */
  font-weight: 700;
  letter-spacing: 0.08em;
}

/* ============================================================================
 * Dark-theme activity icon visibility
 *
 * The chevron-up / chevron-down circles next to each activity row use
 * the --positive-bg / --negative-bg tokens. On dark themes those are
 * rgba overlays at 10% opacity — almost invisible against dark
 * surfaces. Bump to 22% so the coloured ring around the arrow is
 * actually a ring, not a ghost. Also thicken the SVG stroke from
 * 1.5px to 2px so the chevron itself stays crisp at small sizes.
 * ============================================================================ */
[data-theme="forest"] .dash-activity__icon--in,
[data-theme="ink"]    .dash-activity__icon--in {
  background: rgba(127, 199, 138, 0.22);
}
[data-theme="forest"] .dash-activity__icon--out,
[data-theme="ink"]    .dash-activity__icon--out {
  background: rgba(244, 122, 106, 0.22);
}
[data-theme="forest"] .dash-activity__icon svg,
[data-theme="ink"]    .dash-activity__icon svg {
  stroke-width: 2;
}

/* Same set under prefers-color-scheme:dark for the auto theme. */
@media (prefers-color-scheme: dark) {
  [data-theme="auto"] .dash-activity__icon--in {
    background: rgba(127, 199, 138, 0.22);
  }
  [data-theme="auto"] .dash-activity__icon--out {
    background: rgba(244, 122, 106, 0.22);
  }
  [data-theme="auto"] .dash-activity__icon svg {
    stroke-width: 2;
  }
}

/* ============================================================================
 * Matrix-extras GRID data entry
 * Recorder types new closing balances into a holder × column grid that
 * mirrors the Balance Matrix on /overview. Each cell shows the current
 * latest balance as muted text above an empty input — recorder reads
 * the current value, types only what changed today, submits the whole
 * grid in one POST.
 * ============================================================================ */
.matrix-grid-form {
  /* Wider than the single-row form — the grid can have many columns. */
  max-width: none;
}

.matrix-grid-toolbar {
  display: flex;
  align-items: flex-end;
  gap: 24px;
  flex-wrap: wrap;
  margin-bottom: 16px;
}
.matrix-grid-toolbar .form__field {
  max-width: 200px;
}
.matrix-grid-toolbar__hint {
  flex: 1 1 320px;
  margin: 0;
  font-size: 0.88rem;
  line-height: 1.45;
}

.matrix-grid-wrap {
  /* Horizontal scroll if many columns — date column stays sticky-left. */
  overflow-x: auto;
  margin-bottom: 16px;
}

.matrix-grid-table {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
}
.matrix-grid-table th,
.matrix-grid-table td {
  vertical-align: top;
}
.matrix-grid-table thead th {
  font-weight: 600;
  font-size: 0.88rem;
  padding: 10px 12px;
  border-bottom: 1px solid var(--ink-12);
  white-space: nowrap;
}
.matrix-grid-table__col-sub {
  font-weight: 400;
  font-size: 0.72rem;
  color: var(--ink-65);
  margin-top: 2px;
  white-space: nowrap;
}
.matrix-grid-table__holder-col,
.matrix-grid-table__holder-cell {
  text-align: left;
  position: sticky;
  left: 0;
  background: var(--card);
  z-index: 1;
  min-width: 180px;
  padding: 12px 14px;
  font-weight: 500;
  border-right: 1px solid var(--ink-12);
}
.matrix-grid-table__od-col {
  border-left: 2px solid var(--ink-12);
}

.matrix-grid-cell {
  padding: 8px 10px;
  min-width: 140px;
  border-bottom: 1px solid var(--ink-07);
}
.matrix-grid-cell--od {
  border-left: 2px solid var(--ink-12);
  background: var(--paper);
}
.matrix-grid-cell__current {
  font-size: 0.82rem;
  color: var(--ink-65);
  font-variant-numeric: tabular-nums;
  text-align: right;
  margin-bottom: 4px;
  min-height: 1.1em;
}
.matrix-grid-cell__input {
  width: 100%;
  padding: 6px 8px;
  font-size: 0.9rem;
  font-variant-numeric: tabular-nums;
  text-align: right;
  border: 1px solid var(--ink-12);
  border-radius: 6px;
  background: var(--card);
  color: var(--ink);
}
.matrix-grid-cell__input:focus {
  outline: none;
  border-color: var(--bronze);
  box-shadow: 0 0 0 2px rgba(122, 102, 72, 0.18);
}
.matrix-grid-cell__input::placeholder {
  color: var(--ink-45);
  font-style: italic;
}

/* Zebra-stripe the rows so a recorder filling 10+ holders doesn't lose
   their place when scanning across many columns. */
.matrix-grid-table tbody tr:nth-child(even) th,
.matrix-grid-table tbody tr:nth-child(even) td {
  background: var(--paper);
}
.matrix-grid-table tbody tr:nth-child(even) .matrix-grid-cell--od {
  /* Re-darken the OD column relative to the row's base */
  background: var(--ink-07);
}

/* Dark-theme tweaks: borders and the sticky holder column need to use
   ink-on-dark colours so the grid stays legible. */
[data-theme="forest"] .matrix-grid-cell--od,
[data-theme="ink"]    .matrix-grid-cell--od {
  background: rgba(255, 255, 255, 0.04);
}
[data-theme="forest"] .matrix-grid-table tbody tr:nth-child(even) th,
[data-theme="forest"] .matrix-grid-table tbody tr:nth-child(even) td,
[data-theme="ink"]    .matrix-grid-table tbody tr:nth-child(even) th,
[data-theme="ink"]    .matrix-grid-table tbody tr:nth-child(even) td {
  background: rgba(255, 255, 255, 0.025);
}
@media (prefers-color-scheme: dark) {
  [data-theme="auto"] .matrix-grid-cell--od {
    background: rgba(255, 255, 255, 0.04);
  }
  [data-theme="auto"] .matrix-grid-table tbody tr:nth-child(even) th,
  [data-theme="auto"] .matrix-grid-table tbody tr:nth-child(even) td {
    background: rgba(255, 255, 255, 0.025);
  }
}

/* ============================================================================
 * Messaging — inbox / sent / compose / broadcast / view
 * Internal peer-to-peer messages + admin broadcast. Visual model is
 * email-inbox-lite: bold-on-unread rows in a tight table, with a
 * pulled-back single-message view for reading + delete.
 * ============================================================================ */

/* Sub-nav strip — uses the masters-subnav base style but adds an
   ``.messages-badge`` chip for the unread count beside "Inbox". */
.messages-subnav {
  margin-top: 8px;
}
.messages-badge {
  display: inline-block;
  padding: 1px 8px;
  margin-left: 4px;
  font-size: 0.72rem;
  font-weight: 600;
  background: var(--bronze);
  color: var(--on-dark);
  border-radius: 999px;
  font-variant-numeric: tabular-nums;
}

/* Inbox / sent rows -------------------------------------------------------- */
.messages-table {
  width: 100%;
}
.messages-table__from { width: 200px; }
.messages-table__when { width: 160px; }
.messages-table__actions { width: 90px; text-align: right; }

.messages-row a {
  /* Inbox table rows lean on existing data-table styling; reset the
     border-bottom on anchors so the row doesn't get a dashed underline
     under every clickable cell. */
  border-bottom: 0;
  color: inherit;
}
.messages-row a:hover {
  color: var(--bronze);
}
.messages-row__from {
  font-weight: 500;
}
.messages-row__subject {
  display: block;
  font-weight: 400;
}
.messages-row--unread .messages-row__from,
.messages-row--unread .messages-row__subject {
  font-weight: 600;
  color: var(--ink);
}
.messages-row__broadcast {
  margin-left: 4px;
  font-size: 0.85em;
  vertical-align: middle;
}
.messages-row__actions {
  text-align: right;
}

/* Compose / broadcast form ------------------------------------------------- */
.messages-form {
  max-width: 720px;
  margin-top: 18px;
}
.messages-form textarea {
  width: 100%;
  padding: 10px 12px;
  font-family: var(--sans);
  font-size: 0.92rem;
  line-height: 1.5;
  border: 1px solid var(--ink-12);
  border-radius: 6px;
  background: var(--card);
  color: var(--ink);
  resize: vertical;
}
.messages-form textarea:focus {
  outline: none;
  border-color: var(--bronze);
  box-shadow: 0 0 0 2px rgba(122, 102, 72, 0.18);
}

/* Broadcast recipient preview banner --------------------------------------- */
.messages-broadcast-banner {
  margin-top: 18px;
  padding: 12px 16px;
  background: var(--paper);
  border: 1px dashed var(--bronze-pale);
  border-radius: 8px;
  font-size: 0.88rem;
  line-height: 1.6;
}
.messages-recipient-chip {
  display: inline-block;
  padding: 2px 10px;
  margin: 2px 4px 2px 0;
  background: var(--card);
  border: 1px solid var(--ink-12);
  border-radius: 999px;
  font-size: 0.8rem;
  white-space: nowrap;
}

/* Single-message view ------------------------------------------------------ */
.messages-view {
  max-width: 760px;
  padding: 20px 24px;
  background: var(--card);
  border: 1px solid var(--ink-12);
  border-radius: 12px;
}
.messages-view__head {
  display: flex;
  align-items: baseline;
  gap: 12px;
  flex-wrap: wrap;
}
.messages-view__subject {
  margin: 0;
  font-size: 1.4rem;
  font-weight: 500;
  color: var(--ink);
}
.messages-view__broadcast {
  font-size: 0.78rem;
  padding: 3px 10px;
  background: var(--bronze-pale);
  color: var(--bronze);
  border-radius: 999px;
  font-weight: 500;
}
.messages-view__meta {
  margin: 14px 0 18px;
  font-size: 0.85rem;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 4px 24px;
  padding: 10px 12px;
  background: var(--paper);
  border-radius: 8px;
}
.messages-view__role-chip {
  display: inline-block;
  margin-left: 4px;
  padding: 1px 8px;
  background: var(--bronze-pale);
  color: var(--bronze);
  font-size: 0.7rem;
  font-weight: 500;
  border-radius: 999px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.messages-view__body {
  white-space: pre-wrap;     /* preserve newlines from the textarea */
  font-size: 0.95rem;
  line-height: 1.6;
  color: var(--ink);
  margin: 0 0 24px;
}
.messages-view__foot {
  display: flex;
  align-items: center;
  gap: 8px;
  padding-top: 14px;
  border-top: 1px solid var(--ink-12);
}

/* Sidebar unread badge ----------------------------------------------------- */
.sidebar__badge {
  display: inline-block;
  margin-left: auto;
  padding: 1px 7px;
  font-size: 0.7rem;
  font-weight: 600;
  background: var(--bronze);
  color: var(--on-dark);
  border-radius: 999px;
  font-variant-numeric: tabular-nums;
}

/* ============================================================================
 * Broadcast banners on /overview
 *
 * One announcement card per pending broadcast. The visual language
 * intentionally departs from the rest of the page — saffron-amber
 * gradient + saturated left edge + filled-pill label + a one-shot
 * attention pulse — so the eye locks onto it the moment the page
 * paints. The pulse fades to nothing after ~5 seconds so the card
 * doesn't become a permanent distraction.
 *
 * Colour anchor: ``--saffron`` warm gold, distinct from the bronze /
 * forest used elsewhere. Locally scoped — defined inside the block
 * so an admin retheme of the whole app doesn't accidentally pick it
 * up.
 * ========================================================================== */
.broadcast-banners {
  --saffron:      #d4922a;
  --saffron-deep: #a76d18;
  --saffron-pale: #fbe9c6;
  --saffron-tint: #fff7e3;

  display: flex;
  flex-direction: column;
  gap: 12px;
  margin: 18px 0 4px;
}
.broadcast-banner {
  position: relative;
  background:
    linear-gradient(
      135deg,
      var(--saffron-tint)  0%,
      color-mix(in srgb, var(--saffron-pale) 55%, var(--card)) 100%
    );
  border: 1px solid var(--saffron-pale);
  border-left: 6px solid var(--saffron);
  border-radius: 10px;
  padding: 14px 18px 14px 22px;
  box-shadow:
    0 1px 0 rgba(0, 0, 0, 0.04),
    0 2px 18px -8px rgba(212, 146, 42, 0.45);
  /* One-shot attention pulse on first paint. Fades out after the
     animation so the card sits quiet once the user has registered it. */
  animation: broadcast-pulse 1.4s ease-out 0s 3;
}
@keyframes broadcast-pulse {
  0%   { box-shadow: 0 1px 0 rgba(0, 0, 0, 0.04),
                     0 0 0  0   rgba(212, 146, 42, 0.55); }
  60%  { box-shadow: 0 1px 0 rgba(0, 0, 0, 0.04),
                     0 0 0 12px rgba(212, 146, 42, 0.00); }
  100% { box-shadow: 0 1px 0 rgba(0, 0, 0, 0.04),
                     0 2px 18px -8px rgba(212, 146, 42, 0.45); }
}
/* Respect user-set "reduce motion" preference — no pulse for them. */
@media (prefers-reduced-motion: reduce) {
  .broadcast-banner { animation: none; }
}

.broadcast-banner__head {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  margin-bottom: 6px;
  font-size: 0.82rem;
  color: var(--saffron-deep);
}
.broadcast-banner__icon {
  font-size: 1.1em;
}
/* Filled saffron pill — the visual anchor that screams "announcement".
   Pulls the eye even at thumbnail scale. */
.broadcast-banner__label {
  display: inline-block;
  padding: 2px 10px;
  background: var(--saffron);
  color: #fff;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  border-radius: 999px;
  font-size: 0.7rem;
}
.broadcast-banner__sent {
  margin-left: 4px;
  color: var(--saffron-deep);
  opacity: 0.75;
}
.broadcast-banner__close {
  margin-left: auto;
  background: transparent;
  border: 0;
  color: var(--saffron-deep);
  font-size: 1.2rem;
  line-height: 1;
  width: 28px;
  height: 28px;
  border-radius: 999px;
  cursor: pointer;
  transition: background 0.1s ease, color 0.1s ease;
}
.broadcast-banner__close:hover {
  background: rgba(167, 109, 24, 0.12);
  color: var(--saffron-deep);
}
.broadcast-banner__subject {
  font-family: var(--serif);
  font-size: 1.2rem;
  font-weight: 500;
  line-height: 1.3;
  color: var(--ink);
  margin-bottom: 4px;
}
.broadcast-banner__body {
  font-size: 0.92rem;
  line-height: 1.5;
  color: var(--ink-80);
  white-space: pre-wrap;
  margin-bottom: 12px;
}
.broadcast-banner__foot {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}
.broadcast-banner__foot .btn--primary {
  /* Override the global forest-green primary with a saffron variant
     so the CTA sits inside the banner's colour family. */
  background: var(--saffron);
  border-color: var(--saffron);
  color: #fff;
}
.broadcast-banner__foot .btn--primary:hover {
  background: var(--saffron-deep);
  border-color: var(--saffron-deep);
}
.broadcast-banner__hint {
  font-size: 0.78rem;
  color: var(--saffron-deep);
  opacity: 0.85;
}
.broadcast-banner__hint a {
  color: var(--saffron-deep);
  border-bottom-color: var(--saffron-deep);
}

/* Dark themes — softer saffron tint so it still pops against an ink
   background without burning the retina. Text colour flips light so
   contrast stays AA. */
[data-theme="forest"] .broadcast-banner,
[data-theme="ink"]    .broadcast-banner {
  background:
    linear-gradient(
      135deg,
      rgba(212, 146, 42, 0.18) 0%,
      rgba(212, 146, 42, 0.07) 100%
    );
  border-color: rgba(212, 146, 42, 0.40);
  border-left-color: var(--saffron);
  box-shadow:
    0 2px 22px -6px rgba(212, 146, 42, 0.30);
}
[data-theme="forest"] .broadcast-banner__head,
[data-theme="ink"]    .broadcast-banner__head,
[data-theme="forest"] .broadcast-banner__hint,
[data-theme="ink"]    .broadcast-banner__hint,
[data-theme="forest"] .broadcast-banner__sent,
[data-theme="ink"]    .broadcast-banner__sent {
  color: #f3c879;
}
[data-theme="forest"] .broadcast-banner__hint a,
[data-theme="ink"]    .broadcast-banner__hint a {
  color: #f3c879;
  border-bottom-color: #f3c879;
}
[data-theme="forest"] .broadcast-banner__close,
[data-theme="ink"]    .broadcast-banner__close {
  color: #f3c879;
}
[data-theme="forest"] .broadcast-banner__close:hover,
[data-theme="ink"]    .broadcast-banner__close:hover {
  background: rgba(243, 200, 121, 0.18);
}
@media (prefers-color-scheme: dark) {
  [data-theme="auto"] .broadcast-banner {
    background:
      linear-gradient(
        135deg,
        rgba(212, 146, 42, 0.18) 0%,
        rgba(212, 146, 42, 0.07) 100%
      );
    border-color: rgba(212, 146, 42, 0.40);
    border-left-color: var(--saffron);
  }
  [data-theme="auto"] .broadcast-banner__head,
  [data-theme="auto"] .broadcast-banner__hint,
  [data-theme="auto"] .broadcast-banner__sent,
  [data-theme="auto"] .broadcast-banner__close {
    color: #f3c879;
  }
}

/* ============================================================================
 * Audit log — date-grouped drill-down
 * One ``.audit-day`` per yyyy-mm-dd; the header is a button that
 * toggles ``open`` in its own Alpine scope. Closed by default for
 * older days; today expanded. Visual model mirrors the bank-drill
 * panel on /overview — same paper-card-on-paper feel.
 * ============================================================================ */
.audit-day {
  margin-top: 14px;
  border: 1px solid var(--ink-12);
  border-radius: 10px;
  background: var(--card);
  overflow: hidden;
}
.audit-day__head {
  /* Full-width clickable header; the entire bar toggles open/close. */
  display: flex;
  align-items: baseline;
  gap: 16px;
  flex-wrap: wrap;
  width: 100%;
  padding: 12px 16px;
  background: var(--paper);
  border: 0;
  border-bottom: 1px solid transparent;
  cursor: pointer;
  text-align: left;
  font-family: var(--sans);
  font-size: 0.95rem;
  color: var(--ink);
  transition: background 0.1s ease, border-color 0.1s ease;
}
.audit-day__head:hover {
  background: var(--ink-07);
}
.audit-day__head--open {
  /* Bottom border returns when expanded so the body reads as a
     joined section below the header rather than a disconnected
     panel. */
  border-bottom-color: var(--ink-12);
}
.audit-day__chevron {
  display: inline-block;
  width: 1em;
  color: var(--bronze);
  font-size: 0.9em;
  transition: transform 0.12s ease;
}
.audit-day__chevron--open {
  transform: rotate(90deg);
}
.audit-day__date {
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--ink);
}
.audit-day__count {
  font-size: 0.85rem;
  font-variant-numeric: tabular-nums;
}
.audit-day__breakdown {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-left: auto;          /* pushes the breakdown chips to the right */
  font-size: 0.78rem;
}
.audit-day__cat-chip {
  background: var(--bronze-pale);
  color: var(--bronze);
  padding: 2px 8px;
  border-radius: 999px;
  font-weight: 500;
  white-space: nowrap;
}
.audit-day__body {
  padding: 4px 4px 8px;
}
.audit-day__table {
  width: 100%;
  margin: 0;
}
.audit-day__table thead th {
  font-size: 0.74rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-65);
  font-weight: 500;
  padding: 8px 12px;
}
.audit-day__table tbody td {
  padding: 8px 12px;
  vertical-align: top;
  border-top: 1px solid var(--ink-07);
}
.audit-day__col-time { width: 90px; font-variant-numeric: tabular-nums; }
.audit-day__col-user { width: 140px; }
.audit-day__col-cat  { width: 110px; }

.audit-row__time {
  font-family: var(--mono, monospace);
  font-size: 0.85rem;
  color: var(--ink-80);
}
.audit-row__user {
  font-weight: 500;
  font-size: 0.88rem;
}
.audit-row__cat {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 0.72rem;
  font-weight: 500;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  background: var(--ink-07);
  color: var(--ink-65);
}
/* Per-category chip colours so the eye can pick out exports etc. */
.audit-row__cat--exports { background: #e3f0e0; color: #2d5d2a; }
.audit-row__cat--entry,
.audit-row__cat--data    { background: var(--bronze-pale); color: var(--bronze); }
.audit-row__cat--masters { background: #f0e3c4; color: #6b5b2e; }
.audit-row__cat--reports { background: #d9def0; color: #2c3a5e; }
.audit-row__cat--auth    { background: #f3d6c8; color: #6e2e1d; }

.audit-row__summary {
  line-height: 1.4;
  font-size: 0.92rem;
}
.audit-row__raw {
  font-size: 0.72rem;
  margin-top: 4px;
}
.audit-row__raw code {
  font-family: var(--mono, monospace);
  background: var(--ink-07);
  padding: 1px 6px;
  border-radius: 4px;
}

/* Dark-theme tweaks — flip backgrounds to ink so the audit cards
   don't glow against a dark page. */
[data-theme="forest"] .audit-day,
[data-theme="ink"]    .audit-day {
  background: rgba(255, 255, 255, 0.04);
}
[data-theme="forest"] .audit-day__head,
[data-theme="ink"]    .audit-day__head {
  background: rgba(255, 255, 255, 0.06);
}
[data-theme="forest"] .audit-day__head:hover,
[data-theme="ink"]    .audit-day__head:hover {
  background: rgba(255, 255, 255, 0.10);
}
@media (prefers-color-scheme: dark) {
  [data-theme="auto"] .audit-day {
    background: rgba(255, 255, 255, 0.04);
  }
  [data-theme="auto"] .audit-day__head {
    background: rgba(255, 255, 255, 0.06);
  }
  [data-theme="auto"] .audit-day__head:hover {
    background: rgba(255, 255, 255, 0.10);
  }
}

/* ============================================================================
 * DataGrid v0 — progressive table enhancement
 * Styles for the JS module in static/js/data-grid.js. Applies to any
 * <table data-grid-key="…"> the module finds; adds:
 *   • sortable header affordance + sort indicator
 *   • drag-to-reorder visual states
 *   • inline per-column filter input row beneath the headers
 *   • dropdown of column checkboxes (Columns ⌄ button)
 *   • toolbar styling (Columns + Reset buttons sitting above the table)
 * ============================================================================ */

/* Tinted header zone for DataGrid-enabled tables — scoped via the
   ``[data-grid-key]`` attribute so we don't change every other table
   in the app. Uses the per-theme ``--sand`` token (one step warmer
   than --paper), which already adapts cleanly across paper / sand /
   forest / ink / auto themes:
       paper   #ece6d8   warm cream tint over paper-white
       sand    #ddd1ba   denser cream
       forest  #2a4631   slightly lighter than forest paper (#233c2a)
       ink     #2a2c26   slightly lighter than ink paper (#232520)
   The filter row below keeps the existing ``--paper`` background, so
   the thead becomes a 2-step staircase: header (sand) → filter
   (paper) → data rows (no fill). Reads as a clear "header zone". */
table[data-grid-key] thead tr:first-child th {
  background: var(--sand);
  color: var(--ink);
  font-weight: 600;
}
/* The sortable hover is overridden on the column-header row so the
   sand background doesn't get clobbered to ink-07 on mouseover —
   instead we darken the sand slightly. */
table[data-grid-key] thead tr:first-child th.data-grid__sortable:hover {
  background: var(--bone);
}

/* Header cells become clickable + draggable. */
.data-grid__sortable {
  user-select: none;
  position: relative;
}
.data-grid__sortable:hover {
  background: var(--ink-07);
}
.data-grid__sort-ind {
  font-size: 0.78em;
  color: var(--bronze);
  margin-left: 4px;
  font-weight: 600;
}

/* Drag states. */
.data-grid__dragging {
  opacity: 0.4;
}
.data-grid__drag-target {
  outline: 2px dashed var(--bronze);
  outline-offset: -2px;
  background: rgba(122, 102, 72, 0.10);
}

/* Filter row injected by the JS module — second row of <thead>. */
.data-grid__filter-row > th {
  padding: 4px 6px;
  background: var(--paper);
  border-bottom: 1px solid var(--ink-12);
}
.data-grid__filter-input {
  width: 100%;
  padding: 4px 8px;
  font-size: 0.82rem;
  font-family: var(--sans);
  border: 1px solid var(--ink-12);
  border-radius: 4px;
  background: var(--card);
  color: var(--ink);
  font-weight: 400;
}
.data-grid__filter-input:focus {
  outline: none;
  border-color: var(--bronze);
  box-shadow: 0 0 0 2px rgba(122, 102, 72, 0.18);
}
.data-grid__filter-input::placeholder {
  color: var(--ink-45);
  font-style: italic;
}

/* Columns dropdown — JS-positioned via getBoundingClientRect on the
   anchor button + position:fixed. Lives at document.body root so no
   flex / overflow ancestor can squish it (avoids the v1 alignment
   bug where it was a flex child of .data-grid-toolbar). The ``top``
   and ``right`` come from JS inline styles; the rule below only
   handles visuals. */
.data-grid__cols-dropdown {
  z-index: 1000;
  min-width: 200px;
  max-height: 60vh;
  overflow-y: auto;
  background: var(--card);
  border: 1px solid var(--ink-12);
  border-radius: 8px;
  padding: 8px 12px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.14);
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 0.88rem;
}
.data-grid__cols-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 4px 0;
  cursor: pointer;
  user-select: none;
}
.data-grid__cols-item:hover {
  color: var(--bronze);
}

/* Toolbar wrapper around the Columns + Reset buttons sitting above
   each DataGrid-enabled table. The dropdown's absolute-positioning
   needs a relative anchor so set position:relative here. */
.data-grid-toolbar {
  position: relative;
  display: flex;
  gap: 8px;
  align-items: center;
  margin: 12px 0 8px;
}
.data-grid-toolbar__spacer {
  flex: 1;
}

/* Dark-theme tweaks for the filter row.
   Dropdown background is no longer overridden here — it inherits
   --card from the per-theme token blocks above, which now defines a
   SOLID per-theme value (forest = #2e4a36, ink = #2c2e28). The old
   rgba-on-white overrides made the floating dropdown nearly
   transparent and unreadable; the solid token is what we want for
   a popover. */
[data-theme="forest"] .data-grid__filter-row > th,
[data-theme="ink"]    .data-grid__filter-row > th {
  background: rgba(255, 255, 255, 0.04);
}
@media (prefers-color-scheme: dark) {
  [data-theme="auto"] .data-grid__filter-row > th {
    background: rgba(255, 255, 255, 0.04);
  }
}
