/* ============================================================
   Moneylight — Global stylesheet (YNAB-inspired)

   Ported from the budget-tracker prototype's design system. The
   look is intentionally identical; only the delivery differs
   (Django staticfiles + {% static %} instead of FastAPI's hardcoded
   /static path). Token-based: edit the §1 custom properties to
   retheme everything in one place.

   Architecture:
     1. TOKENS      — CSS custom properties. Edit these to retheme.
     2. RESET       — element baselines.
     3. HEADINGS    — single source of truth for forecast section
                      labels and table column headers.
     4. APP LAYOUT  — sidebar + main grid.
     5. SIDEBAR     — left nav, logo.
     6. MAIN/PAGE   — page containers, headers.
     7. PANEL       — card / panel container.
     8. FORMS       — form rows, fields, errors.
     9. BUTTONS     — .btn variants.
    10. TABLES      — .list-table layout (heading typography in §3).
    11. TOAST       — flash messages.
    12. FORECAST    — weekly grid columns + section banding.
    13. UTIL        — minimal, single-purpose utility classes.
   ============================================================ */

/* 1. TOKENS ================================================== */
:root {
  /* Surfaces */
  --bg: #f4f3f6;
  --panel: #ffffff;
  --ink: #1f1f2e;
  --muted: #6b6f80;
  --rule: #e3e3eb;
  --rule-strong: #d4d4dc;
  --surface-subtle: #f7f7fa;

  /* Sidebar */
  --sidebar-bg: #1c1f58;
  --sidebar-ink: #ffffff;
  --sidebar-hover: rgba(255, 255, 255, 0.06);
  --sidebar-active: #4c45c7;
  --sidebar-rule: rgba(255, 255, 255, 0.08);

  /* Accent (primary purple/indigo) */
  --accent: #3b3b97;
  --accent-hover: #2c2c80;
  --accent-soft: #e8e7f7;
  --accent-soft-ink: #3b3b97;

  /* Status colors */
  --good: #2f6f1f;
  --good-bg: #c5e98a;
  --good-bg-ink: #1f3d0c;
  --warn: #b45309;
  --bad: #b91c1c;
  --bad-soft: #fde2e4;
  --bad-soft-ink: #b91c1c;

  /* Forecast section banding (YNAB-style group rows) */
  --section-band: #f8f6f2;
  --week-head-bg: #54586b;

  /* Heading typography — controls every forecast section label
     AND every table column header. Edit these tokens to restyle all
     headings in one place. The rule that applies them lives in §3. */
  --heading-size: var(--font-size-base);
  --heading-color: var(--ink);
  --heading-transform: capitalize;
  --heading-weight: 700;
  --heading-letter-spacing: .06em;

  /* Typography */
  --font-family: "Figtree", "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Arial, sans-serif;
  --font-size-xs: 13px;
  --font-size-sm: 14px;
  --font-size-base: 14.5px;
  --font-size-md: 15px;
  --font-size-lg: 18px;
  --font-size-xl: 22px;
  --line-height: 1.45;

  /* Spacing */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 24px;

  /* Borders, radius, shadow */
  --radius-sm: 4px;
  --radius-md: 6px;
  --radius-lg: 8px;
  --radius-pill: 999px;
  --shadow-card: 0 1px 2px rgba(0, 0, 0, .04), 0 4px 12px rgba(0, 0, 0, .04);

  --sidebar-width: 300px;
}

/* 2. RESET =================================================== */
*,
*::before,
*::after {
  box-sizing: border-box;
}

body,
input,
select,
textarea,
button {
  font-family: var(--font-family);
}

body {
  font-size: var(--font-size-base);
  line-height: var(--line-height);
  background: var(--bg);
  color: var(--ink);
  margin: 0;
  padding: 0;
}

::placeholder {
  font-family: inherit;
  color: var(--muted);
  opacity: 1;
}

input[type="date"]:not(:focus)::-webkit-datetime-edit {
  color: var(--muted);
}

select:has(option[value=""]:checked) {
  color: var(--muted);
}

select option {
  color: var(--ink);
}

h1 {
  font-size: var(--font-size-xl);
  margin: 0 0 var(--space-1);
  font-weight: 700;
  letter-spacing: -0.01em;
}

h2 {
  font-size: var(--font-size-xs);
  margin: 0 0 var(--space-2);
  color: var(--muted);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .08em;
}

a {
  color: var(--accent);
}

/* 3. HEADINGS ================================================ */
/* SINGLE SOURCE OF TRUTH for the forecast section labels and table
   column headers. Edit the --heading-* tokens in §1 to change them
   all at once. */
.section-label,
.line.headline .name,
.necessities-summary,
.bills-total-row,
.col-label,
.list-table th {
  font-size: var(--heading-size);
  font-weight: var(--heading-weight);
  text-transform: var(--heading-transform);
  letter-spacing: var(--heading-letter-spacing);
}

.section-label,
.necessities-summary,
.bills-total-row,
.col-label,
.list-table th {
  color: var(--heading-color);
}

/* 4. APP LAYOUT ============================================== */
.app-layout {
  display: grid;
  grid-template-columns: var(--sidebar-width) 1fr;
  min-height: 100vh;
}

.main {
  padding: var(--space-5);
  min-width: 0;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

/* 5. SIDEBAR ================================================= */
.sidebar {
  background: var(--sidebar-bg);
  color: var(--sidebar-ink);
  padding: var(--space-4) 0 var(--space-4);
  position: sticky;
  top: 0;
  align-self: start;
  height: 100vh;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.sidebar-brand {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: 0 var(--space-4) var(--space-3);
  border-bottom: 1px solid var(--sidebar-rule);
}

.sidebar-brand .logo {
  width: 36px;
  height: 36px;
  flex: 0 0 36px;
}

.sidebar-brand .brand-text {
  font-size: var(--font-size-lg);
  font-weight: 600;
  line-height: 1.2;
  color: var(--sidebar-ink);
}

.sidebar-nav {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 0 var(--space-2);
}

.sidebar-nav a {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: 8px var(--space-3);
  border-radius: var(--radius-md);
  color: var(--sidebar-ink);
  text-decoration: none;
  font-size: var(--font-size-base);
  font-weight: 400;
}

.sidebar-nav a:hover {
  background: var(--sidebar-hover);
}

.sidebar-nav a.active {
  background: var(--sidebar-active);
  color: #ffffff;
}

.sidebar-nav a .ico {
  width: 24px;
  height: 24px;
  flex: 0 0 24px;
  color: currentColor;
}

/* 6. MAIN / PAGE ============================================= */
.header {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  margin-bottom: var(--space-4);
  flex-wrap: wrap;
}

.header h1 {
  margin: 0;
}

.header .meta {
  color: var(--muted);
  font-size: var(--font-size-sm);
}

/* Width constraint for the management / settings pages (the forecast grid is
   deliberately full-width; everything else reads better capped). */
.page-narrow {
  max-width: 820px;
}

.page-setup {
  max-width: 540px;
  margin: 80px auto;
  /* Offset the 80px top+bottom margins so the footer still pins to the bottom
     of the viewport without forcing a scrollbar. */
  min-height: calc(100vh - 160px);
}

/* Landing hero: the connect page has no sidebar, so this carries the brand.
   Deep-indigo card (matches the sidebar) with the wallet logo + wordmark, so
   the setup screen reads as part of the same app. */
.setup-hero {
  background: var(--sidebar-bg);
  color: var(--sidebar-ink);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  padding: var(--space-5) var(--space-4);
  text-align: center;
  margin-bottom: var(--space-4);
}

.setup-hero .logo {
  width: 56px;
  height: 56px;
}

.setup-hero h1 {
  color: #ffffff;
  margin: var(--space-3) 0 var(--space-1);
}

.setup-tagline {
  color: rgba(255, 255, 255, 0.78);
  margin: 0;
  font-size: var(--font-size-md);
}

/* Value-prop list on the connect card. Green check marks tie to the brand. */
.setup-points {
  list-style: none;
  padding: 0;
  margin: var(--space-3) 0 var(--space-4);
  display: grid;
  gap: var(--space-2);
}

.setup-points li {
  position: relative;
  padding-left: var(--space-5);
}

.setup-points li::before {
  content: "✓";
  position: absolute;
  left: 0;
  color: var(--good);
  font-weight: 700;
}

.btn-block {
  display: flex;
  width: 100%;
}

.footer {
  color: var(--muted);
  font-size: var(--font-size-xs);
  /* Push to the bottom of the flex-column .main so it pins to the page bottom
     instead of floating after short content. */
  margin-top: auto;
  padding-top: var(--space-5);
  text-align: center;
}

.footer-brand {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  margin-bottom: var(--space-2);
}

.footer-logo {
  width: 20px;
  height: 20px;
}

.footer-name {
  font-weight: 600;
  color: var(--ink);
}

.footer-disclaimer {
  /* Wide enough that the required (unabridged) YNAB disclaimer wraps to ~3 rows
     instead of stacking tall, keeping the footer short. */
  max-width: 94ch;
  margin: 0 auto;
  line-height: 1.45;
}

.footer-copy {
  margin: var(--space-2) 0 0;
}

/* Version stamp: secondary to the copyright, monospace digits read as a build id. */
.footer-version {
  margin-left: var(--space-2);
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

.footer-links {
  margin: var(--space-2) 0 0;
}

.footer-links a {
  color: var(--muted);
}

.footer-links a:hover {
  color: var(--ink);
}

/* 7. PANEL =================================================== */
.panel {
  background: var(--panel);
  border: 1px solid var(--rule);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  padding: var(--space-4);
  margin-bottom: var(--space-4);
}

.panel h2 {
  color: var(--ink);
  font-size: var(--font-size-md);
  font-weight: 700;
  text-transform: none;
  letter-spacing: 0;
  margin-bottom: var(--space-3);
}

/* 8. FORMS =================================================== */
.form-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-3);
  margin-bottom: var(--space-3);
}

.form-row-1 {
  grid-template-columns: 1fr;
}

.form-row label {
  display: block;
}

/* Management add/edit forms render a series of labeled fields via
   manage/_form_fields.html, wrapped in .form-edit-grid for a 2-column layout. */
.form-edit-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-3);
  /* Top-align cells, not bottom: the cadence <select> grows a "More
     frequencies…" toggle beneath it, so bottom-aligning would float the paired
     field (e.g. Next date) down to the toggle's baseline. Labels are uniform
     height, so top-aligning keeps every input on the same line. */
  align-items: start;
}

.form-edit-grid > label {
  display: block;
}

.form-edit-grid .full-span {
  grid-column: 1 / -1;
}

.form-actions {
  text-align: right;
  margin-top: var(--space-3);
}

/* Wraps a Django CheckboxSelectMultiple. Django renders an outer <div> of
   per-option <div><label><input>…</label></div>; grid that inner wrapper into 2
   columns. The wrapper class goes on a div the template adds around the field. */
.checkbox-list > div {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 6px var(--space-4);
  margin: var(--space-2) 0;
}

.checkbox-list label {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-1) 0;
  font-size: var(--font-size-base);
}

.checkbox-inline {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: 6px 0;
}

/* Bottom-align a checkbox cell with the input next to it in a .form-row grid. */
.align-end {
  align-self: end;
}

.crud-section-note {
  color: var(--muted);
  font-size: var(--font-size-sm);
  margin-top: 0;
}

.field-label {
  font-size: var(--font-size-sm);
  color: var(--muted);
  margin-bottom: var(--space-1);
  display: block;
  font-weight: 500;
}

.cadence-more-toggle {
  display: inline-block;
  margin-top: var(--space-1);
  padding: 0;
  border: 0;
  background: none;
  color: var(--accent);
  font: inherit;
  font-size: var(--font-size-sm);
  cursor: pointer;
}

.cadence-more-toggle:hover {
  text-decoration: underline;
}

input[type=text],
input[type=number],
input[type=date],
input[type=password],
select {
  width: 100%;
  padding: 7px var(--space-2);
  border: 1px solid var(--rule-strong);
  border-radius: var(--radius-sm);
  font: inherit;
  background: var(--panel);
}

input:focus,
select:focus {
  outline: 2px solid var(--accent-soft);
  border-color: var(--accent);
}

.add-block {
  background: var(--surface-subtle);
  padding: var(--space-3);
  border-radius: var(--radius-md);
  border: 1px solid var(--rule);
  margin-bottom: var(--space-4);
}

.add-block h3 {
  margin: 0 0 var(--space-2);
  font-size: var(--font-size-xs);
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: .08em;
  font-weight: 700;
}

.field-error {
  color: var(--bad);
  font-size: var(--font-size-sm);
  display: block;
  margin-top: var(--space-1);
}

/* 9. BUTTONS ================================================= */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 7px var(--space-3);
  border-radius: var(--radius-md);
  font: inherit;
  font-weight: 600;
  cursor: pointer;
  border: 1px solid transparent;
  background: var(--accent-soft);
  color: var(--accent-soft-ink);
  text-decoration: none;
  line-height: 1.2;
}

.btn:hover {
  background: #dad8f0;
}

.btn-primary {
  background: var(--accent);
  color: #ffffff;
  border-color: var(--accent);
}

.btn-primary:hover {
  background: var(--accent-hover);
  border-color: var(--accent-hover);
}

.btn-sm {
  padding: 4px 10px;
  font-size: var(--font-size-sm);
}

.btn-danger {
  color: var(--bad);
  background: var(--bad-soft);
  border-color: transparent;
}

.btn-danger:hover {
  background: #f8cdd1;
}

/* A borderless text button — used for inline row actions (Edit, etc.). */
.btn-link {
  background: none;
  border: 0;
  color: var(--accent);
  cursor: pointer;
  font: inherit;
  font-weight: 600;
  padding: 0;
  text-decoration: none;
}

.btn-link:hover {
  text-decoration: underline;
}

.btn-link.danger {
  color: var(--bad);
}

form.inline {
  display: inline;
}

/* 10. TABLES (list pages) ==================================== */
.list-table {
  width: 100%;
  border-collapse: collapse;
}

.list-table th,
.list-table td {
  padding: var(--space-2) 10px;
  text-align: left;
  font-size: var(--font-size-base);
}

/* When the manage Edit link jumps to #row-<id>, keep the row clear of the mobile
   sticky topbar (and off the very top edge on desktop). */
.list-table tr[id] {
  scroll-margin-top: 64px;
}

.list-table th {
  /* Typography is set by §3 HEADINGS so all column headers match. */
  border-bottom: 1px solid var(--rule);
}

.list-table th.num,
.list-table td.num {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.list-table th.center,
.list-table td.center {
  text-align: center;
  font-variant-numeric: tabular-nums;
}

.list-table td.actions {
  text-align: right;
  white-space: nowrap;
}

.list-table tr+tr td {
  border-top: 1px solid var(--rule);
}

.list-table tr.inactive td {
  color: var(--muted);
}

/* Budgets list: equal-width action buttons (right-aligned via td.actions) so the
   picker reads as a tidy column with aligned right edges, instead of buttons of
   different widths floating mid-cell. */
.budget-action {
  min-width: 8.5em;
}

.list-header {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--space-3);
  margin-bottom: var(--space-3);
  min-height: 1px;
}

.list-empty {
  color: var(--muted);
  text-align: center;
  padding: var(--space-5);
}

/* YNAB import: full-width, grouped + collapsible category picker.
   Full width (no max cap) so the category name field has room — the name column is
   the only `1fr`, so it absorbs all leftover width and shows the full YNAB category
   name. Each group is one grid so the header row aligns with the inputs, and every
   group grid is the same width -> columns line up across groups. */
.page-wide {
  max-width: none;
}

.import-group {
  border: 1px solid var(--rule);
  border-radius: var(--radius-md);
  margin-bottom: var(--space-3);
  overflow: hidden;
}

.import-group > summary {
  cursor: pointer;
  font-weight: 600;
  padding: var(--space-3) var(--space-4);
  background: var(--surface-subtle);
  user-select: none;
}

.import-group-count {
  color: var(--muted);
  font-weight: 400;
  font-size: 0.85em;
  margin-left: var(--space-2);
}

.import-dupe-note {
  display: block;
  margin-top: var(--space-1);
  color: var(--muted);
  font-size: 0.8em;
}

.import-grid {
  display: grid;
  width: 100%;
  gap: var(--space-2) var(--space-3);
  /* Align cells to the top, not center: a Name cell can grow a second line (the
     "N already from this category" dupe-note or a field error) and centering would
     drag the checkbox + other inputs down off the field's first line. */
  align-items: start;
  padding: var(--space-3) var(--space-4);
}

.import-grid.bills {
  grid-template-columns: 36px minmax(160px, 1fr) 120px 190px 72px 170px;
}

.import-grid.necessities {
  /* No trailing 1fr: the name column is capped so the amount + period fields sit right
     next to the name instead of being pushed to the far right. Extra row width stays
     empty on the right. */
  grid-template-columns: 36px minmax(260px, 560px) 120px 160px;
}

/* A logical row whose cells join the group grid, so header + inputs share columns. */
.import-rowc {
  display: contents;
}

.import-head-cell {
  font-weight: 600;
  font-size: 0.85em;
  color: var(--muted);
}

.import-check {
  display: flex;
  justify-content: center;
  align-items: center;
  /* Match the input's outer height (14.5px line × 1.45 + 7px×2 padding + 1px×2
     border ≈ 37px) so the checkbox centers on the field's first line even though
     the grid now top-aligns cells. */
  min-height: 37px;
}

.import-grid input[type="text"],
.import-grid input[type="date"],
.import-grid select {
  width: 100%;
}

/* Batch dupe-confirm banner shown on the import screen when selected rows duplicate
   an existing category (the import is held until "Import anyway"). */
.import-confirm {
  background: var(--surface-subtle);
  border: 1px solid var(--rule-strong);
  border-left: 3px solid var(--warn);
  border-radius: var(--radius-md);
  padding: var(--space-3);
  margin-bottom: var(--space-4);
}

.import-confirm h3 {
  margin: 0 0 var(--space-2);
  font-size: var(--font-size-xs);
  color: var(--warn);
  text-transform: uppercase;
  letter-spacing: .08em;
  font-weight: 700;
}

.import-confirm-list {
  margin: var(--space-2) 0 0;
  padding-left: var(--space-4);
}

/* Manage-page duplicate prompt (bills / necessities add forms). Reuses the
   .add-block shell but takes the import screen's amber left-accent + heading so
   "possible duplicate" reads consistently everywhere it appears. */
.dupe-confirm {
  border-left: 3px solid var(--warn);
}

.dupe-confirm h3 {
  color: var(--warn);
}

/* 11. TOAST ================================================== */
.toast {
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-md);
  margin-bottom: var(--space-3);
  font-size: var(--font-size-base);
  background: var(--good-bg);
  color: var(--good-bg-ink);
}

.toast-error {
  background: var(--bad-soft);
  color: var(--bad-soft-ink);
}

.toasts {
  list-style: none;
  padding: 0;
  margin: 0 0 var(--space-3);
}

/* 12. FORECAST GRID + WEEK CARDS ============================= */
.forecast-grid {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(420px, 1fr);
  gap: var(--space-3);
  overflow-x: auto;
  padding-bottom: var(--space-2);

  /* Per-section min-heights to align rows across week columns. The
     --max-* vars are set inline on .forecast-grid from the view. */
  --row-h: 40px;
  --section-label-h: 40px;
}

.week {
  background: var(--panel);
  border: 1px solid var(--rule);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  display: flex;
  flex-direction: column;
  min-height: 100%;
  overflow: hidden;
}

.week-head {
  padding: var(--space-4) var(--space-3);
  border-bottom: 1px solid var(--rule);
  background: var(--week-head-bg);
  color: var(--sidebar-ink);
}

.week-head strong {
  font-weight: 700;
}

.week-head .range {
  font-size: var(--font-size-sm);
  color: var(--sidebar-ink);
  margin-top: 2px;
}

.week-head .pay {
  font-size: var(--font-size-sm);
  margin-top: 2px;
  color: var(--sidebar-ink);
}

.week-body {
  padding: 0;
  flex: 1;
  display: flex;
  flex-direction: column;
}

.section {
  margin: 0;
  border-bottom: 1px solid var(--rule);
}

.section:last-child {
  border-bottom: 0;
}

.section-paychecks {
  min-height: calc(var(--section-label-h) + max(var(--max-paychecks), 1) * var(--row-h));
}

.section-bills {
  min-height: calc(var(--section-label-h) + max(var(--max-bills), 1) * var(--row-h));
}

.section-necessities {
  min-height: 38px;
}

/* Section labels — YNAB-style banded header row. Typography from §3. */
.section-label {
  background: var(--section-band);
  padding: 8px var(--space-3);
  border-bottom: 1px solid var(--rule);
}

/* Starting funds: YNAB Ready-to-Assign green banner. */
.line.headline {
  background: var(--good-bg);
  color: var(--good-bg-ink);
  padding: 10px var(--space-3);
  font-weight: 700;
  border-bottom: 1px solid var(--rule);
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-size: var(--font-size-md);
}

.line.headline .money {
  font-size: var(--font-size-md);
}

/* Generic row inside a section. */
.line {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--space-2);
  padding: 7px var(--space-3);
  font-variant-numeric: tabular-nums;
  font-size: var(--font-size-base);
  border-bottom: 1px solid var(--rule);
}

.line:last-child {
  border-bottom: 0;
}

.line.dim {
  color: var(--muted);
}

.line.bill.paid .name,
.line.paycheck.paid .name,
.line.override-row.paid .name {
  text-decoration: line-through;
  color: var(--muted);
}

.line.bill.paid .money,
.line.paycheck.paid .money,
.line.override-row.paid .money {
  color: var(--muted);
}

.line.bill.paid .paid-toggle,
.line.override-row.paid .paid-toggle {
  color: var(--good);
}

.line.bill.autopay:not(.paid) .name {
  color: var(--ink);
  opacity: 0.85;
}

/* Click-to-edit values in the grid are <a hx-get> with no href, so the browser
   would give them the default/text cursor. Force the pointer so the dotted-underline
   affordance is reinforced on hover, matching .btn / .paid-toggle / .override-edit. */
.paycheck-date,
.bill-date-edit,
.bill-amount-edit,
.necessity-amount-edit {
  cursor: pointer;
}

.paycheck-date {
  color: var(--accent);
  text-decoration: none;
  border-bottom: 1px dotted var(--accent);
}

.paycheck-date:hover {
  color: var(--accent);
  border-bottom-style: solid;
}

.line.paycheck.paid .paycheck-date {
  color: var(--muted);
  border-bottom-color: var(--muted);
}

.paycheck-edit-row {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
}

.paycheck-edit-field {
  display: flex;
  flex-direction: column;
  flex: 1 1 0;
  min-width: 0;
}

.paycheck-edit-actions {
  display: flex;
  gap: var(--space-1);
  align-items: center;
}

.paycheck-edit-row input[type=date],
.paycheck-edit-row input[type=text] {
  font-size: var(--font-size-sm);
  padding: var(--space-1) 6px;
  border: 1px solid var(--rule-strong);
  border-radius: var(--radius-sm);
  width: 100%;
  box-sizing: border-box;
}

.paycheck-edit-reset {
  margin: var(--space-1) var(--space-3) var(--space-2);
}

/* Inline bill amount-override editor */
.bill-edit-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-1);
  padding: var(--space-2) var(--space-3);
}

.bill-edit-label {
  flex: 1 1 auto;
  min-width: 0;
}

.bill-edit-row input[type=text],
.bill-edit-row input[type=date] {
  font-size: var(--font-size-sm);
  padding: var(--space-1) 6px;
  border: 1px solid var(--rule-strong);
  border-radius: var(--radius-sm);
  width: 6em;
  text-align: right;
}

.bill-edit-row input[type=date] {
  width: auto;
  text-align: left;
}

.bill-edit-reset {
  margin: var(--space-1) var(--space-3) var(--space-2);
}

/* Clickable amount on a bill row */
.bill-amount-edit {
  text-decoration: none;
  border-bottom: 1px dotted var(--bad);
}

.bill-amount-edit:hover {
  border-bottom-style: solid;
}

.line.bill.paid .bill-amount-edit {
  border-bottom-color: var(--muted);
}

.bill-date-edit {
  color: var(--accent);
  text-decoration: none;
  border-bottom: 1px dotted var(--accent);
}

.bill-date-edit:hover {
  border-bottom-style: solid;
}

/* Credit-card / autopay indicators */
.bill-cc-badge {
  font-size: 0.85em;
  vertical-align: -0.05em;
  margin-left: 2px;
  opacity: 0.85;
  cursor: help;
}

.bill-autopay-badge {
  cursor: help;
}

/* Clickable amount on a necessity row (per-week override) */
.necessity-amount-edit {
  text-decoration: none;
  border-bottom: 1px dotted var(--muted);
  color: inherit;
}

.necessity-amount-edit:hover {
  border-bottom-style: solid;
  color: var(--accent);
}

.necessity-edit-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-1);
  padding: var(--space-2) var(--space-3);
}

.necessity-edit-label {
  flex: 1 1 auto;
  min-width: 0;
}

.necessity-edit-row input[type=text] {
  font-size: var(--font-size-sm);
  padding: var(--space-1) 6px;
  border: 1px solid var(--rule-strong);
  border-radius: var(--radius-sm);
  width: 6em;
  text-align: right;
}

.necessity-edit-reset {
  margin: var(--space-1) var(--space-3) var(--space-2);
}

.bills-total-row {
  /* Layout only. Heading typography comes from §3 HEADINGS. */
  padding: 10px var(--space-3);
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
  background: var(--section-band);
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.money {
  font-variant-numeric: tabular-nums;
}

.money.neg {
  color: var(--bad);
}

.money.pos {
  color: var(--good);
}

.week-foot {
  border-top: 1px solid var(--rule);
  background: var(--surface-subtle);
}

/* CC owed row sits between Bills Total and Ending Balance. */
.cc-owed-row {
  padding: 8px var(--space-3);
  border-bottom: 1px solid var(--rule);
}

/* Collapsible pending-charge breakdown under CC Balance. Mirrors
   .necessities-details structurally; .cc-owed-row supplies the summary padding. */
.pending-details {
  width: 100%;
}

.pending-details>summary {
  list-style: none;
  cursor: pointer;
}

.pending-details>summary::-webkit-details-marker {
  display: none;
}

.pending-summary {
  user-select: none;
}

.pending-summary .caret {
  display: inline-block;
  font-size: 20px;
  color: var(--muted);
  transition: transform .15s ease;
  margin-right: var(--space-1);
  vertical-align: middle;
}

.pending-details[open] .pending-summary .caret {
  transform: rotate(90deg);
}

.pending-breakdown {
  background: var(--surface-subtle);
  border-bottom: 1px solid var(--rule);
}

.pending-group {
  background: var(--panel);
}

.pending-group+.pending-group {
  border-top: 1px dashed var(--rule);
}

/* Top-align so the charge amount stays with the first line of a long,
   wrapping category name (the name+badge can span several lines). */
.pending-group-head {
  align-items: flex-start;
}

/* Name + tag flow as normal inline content so the pill wraps below a long
   name instead of floating vertically-centered beside the wrapped text. */
.pending-group-head .name {
  font-weight: 600;
}

/* Spacing lives on the name (not margin-left on the tag) so the pill wraps
   flush-left under the category name instead of indented by its own margin. */
.pending-group-head .name .pending-cat-name {
  margin-right: var(--space-2);
}

.pending-group-meta {
  display: inline-flex;
  align-items: baseline;
  gap: var(--space-2);
}

/* Tag pills. The neutral palette (default + -cc) uses the page rule color since
   Moneylight has no dedicated "available zero" token. */
.pending-tag {
  display: inline-block;
  padding: 1px 8px;
  font-size: var(--font-size-xs);
  border-radius: var(--radius-pill);
  letter-spacing: .02em;
  background: var(--rule);
  color: var(--muted);
}

.pending-tag-covered {
  background: var(--good-bg);
  color: var(--good-bg-ink);
}

.pending-tag-cc {
  background: var(--rule);
  color: var(--muted);
}

.pending-tag-partial {
  background: var(--accent-soft);
  color: var(--accent-soft-ink);
}

.pending-tag-payment {
  background: var(--good-bg);
  color: var(--good-bg-ink);
}

.pending-item {
  padding-left: var(--space-5);
  font-size: var(--font-size-sm);
}

.pending-item-date {
  color: var(--muted);
  margin-right: var(--space-2);
  font-variant-numeric: tabular-nums;
}

/* Notes + tag legend at the top of the expanded breakdown. */
.pending-notes {
  padding: var(--space-3);
  background: var(--panel);
  border-bottom: 2px solid var(--rule-strong);
  font-size: var(--font-size-sm);
  color: var(--muted);
  line-height: 1.4;
}

.pending-notes-intro {
  margin: 0 0 var(--space-2);
  color: var(--ink);
  font-weight: 600;
}

.pending-legend {
  margin: 0;
  display: grid;
  grid-template-columns: max-content 1fr;
  column-gap: var(--space-2);
  row-gap: 6px;
  align-items: center;
}

.pending-legend dt {
  margin: 0;
  font-weight: normal;
}

.pending-legend dd {
  margin: 0;
}

/* Reconciliation footer: Total scheduled = in Necessities + in CC Balance. */
.pending-reconcile {
  padding: 8px var(--space-3);
  font-size: var(--font-size-xs);
  color: var(--muted);
  background: var(--panel);
  border-top: 1px solid var(--rule);
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-2);
  justify-content: flex-start;
}

.pending-reconcile-part {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  font-variant-numeric: tabular-nums;
}

.pending-reconcile-part .money {
  color: var(--ink);
  font-weight: 600;
}

.pending-reconcile-sep {
  color: var(--muted);
  font-weight: 400;
}

/* Ending Balance row reuses the green Starting Funds banner styling
   (.line.headline). The `ending` modifier adds the threshold tint. */
.ending-row {
  border-bottom: 0;
}

.ending {
  font-weight: 700;
}

.ending.bad {
  color: var(--bad);
}

.ending.warn {
  color: var(--warn);
}

.badge {
  display: inline-block;
  padding: 1px var(--space-3);
  font-size: var(--font-size-xs);
  border-radius: var(--radius-pill);
  background: var(--bad);
  color: white;
  margin-left: 6px;
  vertical-align: middle;
  text-transform: capitalize;
}

/* Paid toggle (☐/✓) on bills */
.paid-toggle {
  background: transparent;
  border: 0;
  cursor: pointer;
  padding: 0 2px;
  font-size: var(--font-size-md);
  line-height: 1;
  color: var(--muted);
}

.paid-toggle:hover {
  color: var(--good);
}

/* Collapsible necessities */
.necessities-details {
  width: 100%;
}

.necessities-details>summary {
  list-style: none;
  cursor: pointer;
}

.necessities-details>summary::-webkit-details-marker {
  display: none;
}

.necessities-summary {
  /* Layout only. Heading typography comes from §3 HEADINGS. */
  user-select: none;
  background: var(--section-band);
  padding: 8px var(--space-3);
  border-bottom: 1px solid var(--rule);
  border-top: 1px solid var(--rule);
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.necessities-summary .caret {
  display: inline-block;
  font-size: 20px;
  color: var(--muted);
  transition: transform .15s ease;
  margin-right: var(--space-1)
}

.necessities-details[open] .necessities-summary .caret {
  transform: rotate(90deg);
}

.necessities-summary .money {
  font-size: var(--font-size-sm);
  text-transform: none;
}

.necessities-breakdown {
  padding: 0;
  background: var(--panel);
}

.necessities-breakdown .line {
  padding-left: var(--space-5);
}

/* Ad-hoc override rows */
.override-row {
  display: flex;
  align-items: center;
  gap: var(--space-1);
}

.override-row form {
  display: inline;
}

.override-delete,
.override-edit {
  background: transparent;
  border: 0;
  color: var(--muted);
  cursor: pointer;
  padding: 0 2px;
  font-size: var(--font-size-lg);
  line-height: 1;
  text-decoration: none;
}

.override-edit:hover {
  color: var(--accent);
}

.override-delete:hover {
  color: var(--bad);
}

.override-edit-form {
  margin: 0;
  padding: var(--space-2) var(--space-3);
  background: var(--surface-subtle);
  border-bottom: 1px solid var(--rule);
}

.override-edit-form .form-row {
  grid-template-columns: 1fr;
  gap: 6px;
  margin: 0;
}

.override-edit-form input,
.override-edit-form select {
  font-size: var(--font-size-sm);
  padding: var(--space-1) 6px;
}

.add-override {
  padding: var(--space-2) var(--space-3);
  background: var(--surface-subtle);
  border-bottom: 1px solid var(--rule);
  border-top: 1px solid var(--rule);
  font-size: var(--font-size-sm);
}

.add-override summary {
  cursor: pointer;
  color: var(--accent);
  font-weight: 600;
  list-style: none;
}

.add-override summary::-webkit-details-marker {
  display: none;
}

.add-override .form-row {
  grid-template-columns: 1fr;
  gap: 6px;
  margin: var(--space-2) 0;
}

.add-override input,
.add-override select {
  font-size: var(--font-size-sm);
  padding: var(--space-1) 6px;
}

/* Header sub-form (horizon + refresh) */
.header-form {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: var(--font-size-base);
}

.header-form input[type=number] {
  width: 60px;
  padding: var(--space-1) 6px;
  border: 1px solid var(--rule-strong);
  border-radius: var(--radius-sm);
  font: inherit;
}

/* Inline grid error banner (HTMX edit failures). */
.grid-error {
  background: var(--bad-soft);
  color: var(--bad-soft-ink);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-md);
  margin-bottom: var(--space-3);
}

/* 13. UTIL =================================================== */
.text-muted {
  color: var(--muted);
}

.text-good {
  color: var(--good);
}

.text-right {
  text-align: right;
}

.text-xs {
  font-size: var(--font-size-xs);
}

.text-inline {
  display: inline;
}

.ml-auto {
  margin-left: auto;
}

.ml-2 {
  margin-left: var(--space-2);
}

.my-2 {
  margin: var(--space-2) 0 var(--space-4);
}

.grow {
  flex: 1;
}

/* Mobile-only chrome (hamburger top bar + drawer scrim). Hidden by default; the
   §14 RESPONSIVE media query reveals them under the breakpoint. */
.mobile-topbar,
.sidebar-overlay {
  display: none;
}

/* 14. RESPONSIVE ============================================= */
/* Desktop-first: everything above is the ≥769px layout. Below the breakpoint the
   fixed 300px sidebar would swallow a phone viewport, so it becomes an off-canvas
   drawer (toggled by the hamburger in .mobile-topbar) and the 2-column form/grid
   layouts collapse to a single column. The forecast grid keeps its horizontal
   scroll by design — a week is wider than a phone — but its min column shrinks so
   one week fills the screen with the next peeking in to signal scrollability. */
@media (max-width: 768px) {
  /* Layout: drop the sidebar grid column; main goes full width. */
  .app-layout {
    grid-template-columns: 1fr;
  }

  .main {
    padding: var(--space-4);
  }

  /* Sidebar → off-canvas drawer, slid out until .open is toggled on. */
  .sidebar {
    position: fixed;
    top: 0;
    left: 0;
    bottom: 0;
    height: 100vh;
    width: min(82vw, var(--sidebar-width));
    z-index: 50;
    transform: translateX(-100%);
    transition: transform .25s ease;
  }

  .sidebar.open {
    transform: translateX(0);
    box-shadow: 0 0 40px rgba(0, 0, 0, .35);
  }

  /* Scrim: present in the DOM but invisible until .visible, so it can transition. */
  .sidebar-overlay {
    display: block;
    position: fixed;
    inset: 0;
    z-index: 40;
    background: rgba(0, 0, 0, .45);
    opacity: 0;
    visibility: hidden;
    transition: opacity .25s ease, visibility .25s ease;
  }

  .sidebar-overlay.visible {
    opacity: 1;
    visibility: visible;
  }

  /* Top bar carrying the hamburger. Negative side margins bleed it to the edges of
     the padded .main (margins must match .main's mobile padding above). */
  .mobile-topbar {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    position: sticky;
    top: 0;
    z-index: 30;
    margin: calc(-1 * var(--space-4)) calc(-1 * var(--space-4)) var(--space-4);
    padding: var(--space-3) var(--space-4);
    background: var(--sidebar-bg);
    color: var(--sidebar-ink);
  }

  .mobile-menu-btn {
    display: inline-flex;
    align-items: center;
    padding: var(--space-1);
    border: 0;
    background: transparent;
    color: var(--sidebar-ink);
    cursor: pointer;
  }

  .mobile-menu-btn svg {
    width: 26px;
    height: 26px;
  }

  .mobile-brand {
    font-size: var(--font-size-lg);
    font-weight: 600;
  }

  /* Forecast header: the desktop row uses ml-auto to push the controls right,
     which reads as broken once it wraps. Stack it instead. */
  .header {
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-2);
  }

  .header .ml-auto,
  .header .ml-2 {
    margin-left: 0;
  }

  /* Forms: every 2-column grid collapses to one column so fields aren't crushed. */
  .form-row,
  .form-edit-grid,
  .checkbox-list > div {
    grid-template-columns: 1fr;
  }

  /* Forecast grid: one full-width week per screen (the .main padding supplies the
     side gutters); horizontal scroll advances one week at a time. */
  .forecast-grid {
    grid-auto-columns: 100%;
  }

  /* List tables → stacked cards. Six columns don't fit a phone, so each row
     becomes a bordered card and every data cell shows its column name (from the
     td's data-label) beside the value. The header row is hidden; the inline edit
     form (a single colspan cell, no data-label) stays a normal full-width block.
     .list-table-plain opts out (see budgets.html) — a 2-column table reads better
     left as a table on a phone. */
  .list-table:not(.list-table-plain),
  .list-table:not(.list-table-plain) tbody {
    display: block;
  }

  .list-table:not(.list-table-plain) thead {
    display: none;
  }

  .list-table:not(.list-table-plain) tr {
    display: block;
    border: 1px solid var(--rule);
    border-radius: var(--radius-md);
    padding: var(--space-1) var(--space-3);
    margin-bottom: var(--space-3);
  }

  .list-table:not(.list-table-plain) td {
    display: block;
  }

  .list-table:not(.list-table-plain) tr + tr td {
    border-top: 0;  /* cancel the desktop row separators */
  }

  .list-table:not(.list-table-plain) td[data-label] {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: var(--space-3);
    padding: var(--space-1) 0;
    text-align: right;
  }

  .list-table:not(.list-table-plain) td[data-label]::before {
    content: attr(data-label);
    font-weight: 600;
    color: var(--muted);
    text-align: left;
    white-space: nowrap;
  }

  .list-table:not(.list-table-plain) td.actions {
    display: flex;
    justify-content: flex-end;
    gap: var(--space-2);
    margin-top: var(--space-1);
    padding: var(--space-2) 0 var(--space-1);
    border-top: 1px solid var(--rule);
    white-space: normal;
  }

  /* Import pages: the fixed multi-column grid is wider than a phone, so each
     category row becomes a stacked card with per-field labels (from data-label);
     the grid header row is hidden. */
  .import-grid {
    display: block;
    padding: var(--space-3);
  }

  .import-head-cell {
    display: none;
  }

  .import-rowc {
    display: block;
    border: 1px solid var(--rule);
    border-radius: var(--radius-md);
    padding: var(--space-3);
    margin-bottom: var(--space-3);
  }

  .import-rowc .import-cell {
    display: block;
    margin-bottom: var(--space-2);
  }

  .import-rowc .import-cell:last-child {
    margin-bottom: 0;
  }

  .import-rowc .import-check {
    min-height: 0;  /* drop the desktop 37px centering box */
  }

  .import-rowc [data-label]::before {
    content: attr(data-label);
    display: block;
    font-size: var(--font-size-sm);
    font-weight: 500;
    color: var(--muted);
    margin-bottom: var(--space-1);
  }

  /* The leading "include this category" checkbox: label inline beside the box,
     set off from the fields below with a divider. */
  .import-rowc > label.import-check {
    justify-content: flex-start;
    align-items: center;
    gap: var(--space-2);
    margin-bottom: var(--space-2);
    padding-bottom: var(--space-2);
    border-bottom: 1px solid var(--rule);
    font-weight: 600;
  }

  .import-rowc > label.import-check[data-label]::before {
    display: inline;
    margin-bottom: 0;
    color: var(--ink);
  }
}
