/* ============================================================================
   Family Budget — shared stylesheet
   ----------------------------------------------------------------------------
   Ported from baby-feeding-tracker's Apple-style token system
   (baby-feeding-tracker/frontend/styles.css) so the two household apps look like
   one family. index.html and login.html both link this file; neither carries a
   <style> block of its own any more.

   RULES (same as the baby tracker): never put a raw hex, a raw font-size or a
   loose px space into a component rule — use the token whose *meaning* matches.
   Dark mode is token overrides only.

   Money colors: an expense is NEVER red. Red is reserved for errors and Delete.
   Expense is amber — --expense-text on white surfaces (4.8:1), the yellow
   --on-accent-highlight only on the blue summary banner (3.6:1, large numerals).
   Contrast figures below were computed for both modes; re-check after a change.
   ========================================================================== */

:root {
  --font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
               'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', 'Noto Sans CJK SC', sans-serif;

  /* Accent — systemBlue. Fill and text roles carry different values on purpose. */
  --accent:              #0071E3;
  --accent-pressed:      #005BBA;
  --accent-text:         #0040DD;
  --accent-strong:       #003CAB;
  --accent-tint:         #E5F0FF;
  --accent-tint-strong:  #CCE1FF;
  --accent-surface:      #1E6FC4;   /* the summary banner — lower chroma than --accent */

  /* Status */
  --success:             #1F7A37;   /* Add button fill, white label */
  --success-text:        #1F7A37;
  --success-pressed:     #17612B;
  --success-tint:        #DDF5E3;
  --danger:              #C9000F;   /* errors + Delete only — never money */
  --danger-strong:       #96000B;
  --danger-tint:         #FFEBEA;
  --danger-border:       #F09189;
  --danger-alert-bg:     #FFE4E1;
  --danger-border-soft:  #FFD5D2;
  --warning-tint:        #FFF3CC;
  --warning-strong:      #7A5500;

  /* Money */
  /* The baby tracker's --chart-pee amber, so the two apps' bar charts match. Bars are
     drawn like its diaper chart: a 1px outline in this color over a ~53% fill. */
  --expense-text:        #9A6800;   /* 4.8:1 on white */
  --expense-fill:        #9A6800;
  --expense-tint:        #FFF1E0;
  --income-text:         #1F7A37;
  --income-fill:         #248A3D;
  --income-tint:         #DDF5E3;

  /* Category palette (doughnut + legend). Top 5 + Other. ≥3:1 on the card. */
  --cat-1:               #0071E3;   /* blue */
  --cat-2:               #D97400;   /* orange */
  --cat-3:               #5856D6;   /* indigo */
  --cat-4:               #0A8FA8;   /* teal */
  --cat-5:               #D12F7A;   /* pink */
  --cat-other:           #8E8E93;   /* gray */

  /* On-accent — drawn on the blue banner, not on a light surface */
  --on-accent:           #FFFFFF;
  --on-accent-muted:     rgba(255,255,255,.86);
  --on-accent-highlight: #FFD60A;   /* spending headline — the baby tracker's next-feed yellow */
  --on-accent-income:    #B4F5C4;   /* income headline, 4.1:1 */
  --on-accent-separator: rgba(255,255,255,.24);
  --on-accent-track:     rgba(0,0,0,.18);    /* segmented track / select on the banner */
  --on-accent-hover:     rgba(255,255,255,.14);
  --on-accent-selected:  #FFFFFF;
  --on-accent-selected-fg: #14538F;

  /* Text hierarchy — compressed for AA, as in the baby tracker */
  --label-primary:       #1C1C1E;
  --label-secondary:     #3A3A3C;
  --label-tertiary:      #5A5A5E;
  --label-quaternary:    #6E6E73;
  --label-faint:         #AEAEB2;

  /* Surfaces */
  --bg-grouped:          #F2F2F7;
  --bg-card:             #FFFFFF;
  --bg-subtle:           #F9F9FB;
  --bg-hover:            #EFEFF4;
  --bg-hover-strong:     #E5E5EA;
  --bg-field:            #EFEFF4;

  /* Segmented control */
  --seg-track:           #E5E5EA;
  --seg-selected:        #FFFFFF;

  /* Lines */
  --separator:           #D1D1D6;
  --separator-section:   #E5E5EA;
  --separator-soft:      #EFEFF4;

  /* Charts — Chart.js reads these back through token() in js/core.js */
  --chart-grid:          #EFEFF4;
  --chart-tick:          #6E6E73;
  --chart-avg:           #5A5A5E;
  --chart-divider:       rgba(60,60,67,.29);   /* week/month/year boundaries in the scrolling chart */
  --chart-tooltip-bg:    rgba(28,28,30,.92);
  --chart-tooltip-fg:    #FFFFFF;

  /* Type scale */
  --text-stat:           28px;
  --text-banner:         26px;
  --text-xl:             22px;   /* login heading */
  --text-title:          20px;
  --text-lg:             18px;
  --text-base:           16px;
  --text-md:             15px;
  --text-body:           14px;
  --text-sm:             13px;
  --text-xs:             12px;
  --text-2xs:            11px;
  --text-3xs:            10px;

  --weight-regular:      400;
  --weight-medium:       500;
  --weight-semibold:     600;
  --weight-bold:         700;

  --leading-tight:       1;
  --leading-snug:        1.25;
  --leading-normal:      1.45;

  --eyebrow-size:        12px;   /* was 11px in capitals; lower-case letters read smaller */
  --eyebrow-weight:      600;
  --eyebrow-tracking:    .5px;

  /* Spacing — 8pt rhythm */
  --space-0-5:           2px;
  --space-1:             4px;
  --space-1-5:           6px;
  --space-2:             8px;
  --space-2-5:           10px;
  --space-3:             12px;
  --space-4:             16px;
  --space-5:             20px;
  --space-6:             24px;
  --space-7:             28px;

  /* Shape */
  --radius-card:         12px;
  --radius-banner:       14px;
  --radius-control:      9px;
  --radius-sm:           7px;
  --radius-chip:         8px;
  --radius-pill:         999px;
  --radius-swatch:       3px;

  /* Layout */
  --sidebar-w:           232px;
  --drawer-w:            400px;
  --avatar-size:         28px;

  /* Depth & focus */
  --shadow-card:         0 1px 3px rgba(0,0,0,0.06);
  --shadow-seg:          0 1px 2px rgba(0,0,0,.12);
  --shadow-banner:       0 1px 3px rgba(0,0,0,0.10);
  --shadow-login:        0 4px 20px rgba(0,0,0,0.06);
  --shadow-drawer:       2px 0 12px rgba(0,0,0,.10);
  --shadow-drawer-right: -4px 0 16px rgba(0,0,0,.08);
  --scrim:               rgba(0,0,0,.25);
  --focus-ring:          0 0 0 2px rgba(0,122,255,.28);
  --focus-ring-lg:       0 0 0 3px rgba(0,122,255,.28);
  --focus-ring-on-accent: 0 0 0 2px rgba(255,255,255,.7);
}

/* ============================================================================
   Dark mode — token overrides ONLY. Follows the OS setting, like the baby tracker.
   ========================================================================== */
@media (prefers-color-scheme: dark) {
  :root {
    --accent:              #0D6FD6;
    --accent-pressed:      #409CFF;
    --accent-text:         #6FB6FF;
    --accent-strong:       #A6D0FF;
    --accent-tint:         #12365C;
    --accent-tint-strong:  #1B4E80;
    --accent-surface:      #14538F;

    --success:             #1F7A37;
    --success-text:        #5BD97C;
    --success-pressed:     #1B6E31;
    --success-tint:        #0F3A1C;
    --danger:              #FF6B61;
    --danger-strong:       #FF9E96;
    --danger-tint:         #3A1C19;
    --danger-alert-bg:     #4C201C;
    --danger-border:       #8E3B34;
    --danger-border-soft:  #5E2A25;
    --warning-tint:        #3D2F0A;
    --warning-strong:      #FFD666;

    --expense-text:        #E3AE45;   /* the baby tracker's dark --chart-pee, 8.4:1 */
    --expense-fill:        #E3AE45;
    --expense-tint:        #3A2810;
    --income-text:         #5BD97C;
    --income-fill:         #30D158;
    --income-tint:         #0F3A1C;

    --cat-1:               #0A84FF;
    --cat-2:               #FF9F0A;
    --cat-3:               #7D7AFF;
    --cat-4:               #40C8E0;
    --cat-5:               #FF6FAE;
    --cat-other:           #8E8E93;

    --on-accent-selected-fg: #14538F;

    --label-primary:       #F2F2F7;
    --label-secondary:     #D8D8DD;
    --label-tertiary:      #B4B4BA;
    --label-quaternary:    #9A9AA0;
    --label-faint:         #6A6A6E;

    --bg-grouped:          #000000;
    --bg-card:             #1C1C1E;
    --bg-subtle:           #2C2C2E;
    --bg-hover:            #2C2C2E;
    --bg-hover-strong:     #3A3A3C;
    --bg-field:            #2C2C2E;

    --seg-track:           #3A3A3C;
    --seg-selected:        #636366;

    --separator:           #38383A;
    --separator-section:   #2C2C2E;
    --separator-soft:      #2C2C2E;

    --chart-grid:          #2C2C2E;
    --chart-tick:          #9A9AA0;
    --chart-avg:           #B4B4BA;
    --chart-divider:       rgba(235,235,245,.26);
    --chart-tooltip-bg:    rgba(240,240,245,.94);
    --chart-tooltip-fg:    #1C1C1E;

    --shadow-card:         none;
    --shadow-seg:          none;
    --shadow-banner:       none;
    --shadow-login:        0 4px 20px rgba(0,0,0,.5);
    --shadow-drawer:       2px 0 12px rgba(0,0,0,.5);
    --shadow-drawer-right: -4px 0 16px rgba(0,0,0,.5);
    --scrim:               rgba(0,0,0,.5);
    --focus-ring:          0 0 0 2px rgba(10,132,255,.45);
    --focus-ring-lg:       0 0 0 3px rgba(10,132,255,.45);
  }
}

/* ---------- Base ---------- */
* { box-sizing: border-box; margin: 0; padding: 0; }
[hidden] { display: none !important; }
body { font-family: var(--font-sans); background: var(--bg-grouped); color: var(--label-primary);
       line-height: var(--leading-normal); -webkit-font-smoothing: antialiased; }
body:not(.login-page) { font-size: var(--text-body); }
.visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden;
                   clip: rect(0 0 0 0); white-space: nowrap; }

button:focus-visible, select:focus-visible, [tabindex]:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
}
button, input, select {
  transition: background-color .15s ease, border-color .15s ease,
              box-shadow .15s ease, color .15s ease;
}

/* ---------- Top bar ----------
   The page's title on the left, its controls on the right (PLAN.md §3.1). The brand,
   the user and Log out live in the menu. */
.page-header { background: var(--bg-card); padding: var(--space-3) var(--space-5);
               border-bottom: 1px solid var(--separator); display: flex; align-items: center;
               gap: var(--space-3); flex-wrap: wrap; row-gap: var(--space-2); }
.page-header h1 { font-size: var(--text-title); font-weight: var(--weight-bold); color: var(--label-primary);
                  white-space: nowrap; }
.page-header .spacer { flex: 1; }
.top-actions { display: flex; align-items: center; gap: var(--space-2); }
.top-actions:empty { display: none; }
/* The page's one green button (PLAN.md §3.1): commit color, like Add and Save. */
.add-btn { display: inline-flex; align-items: center; gap: var(--space-1-5); border: none; cursor: pointer;
           font-family: inherit; font-size: var(--text-sm); font-weight: var(--weight-semibold);
           background: var(--success); color: var(--on-accent); white-space: nowrap;
           padding: var(--space-2) var(--space-3); border-radius: var(--radius-control); }
.add-btn:hover { background: var(--success-pressed); }
.add-btn:focus-visible { outline: none; box-shadow: var(--focus-ring-lg); }
.add-btn .icon { width: 18px; height: 18px; }
.page-header button.menu-btn { border: none; background: none; line-height: 0;
                               cursor: pointer; padding: var(--space-1-5); color: var(--label-secondary);
                               border-radius: var(--radius-sm); font-family: inherit; }
.page-header button.menu-btn:hover { background: var(--bg-hover); }
.container { max-width: 900px; margin: 0 auto; padding: var(--space-4); }

/* ---------- Menu: a drawer on a phone, a sidebar on a wide screen ---------- */
.nav-scrim { display: none; position: fixed; inset: 0; background: var(--scrim); z-index: 20; }
.nav-scrim.open { display: block; }
.nav-drawer { position: fixed; top: 0; left: 0; bottom: 0; width: var(--sidebar-w); background: var(--bg-card);
              box-shadow: var(--shadow-drawer); z-index: 21; transform: translateX(-100%);
              transition: transform .2s ease; padding: var(--space-4) var(--space-2) var(--space-2);
              display: flex; flex-direction: column; gap: var(--space-0-5); overflow-y: auto; }
.nav-drawer.open { transform: translateX(0); }
.nav-brand { display: flex; align-items: center; gap: var(--space-2); color: var(--accent-text);
             font-size: var(--text-lg); font-weight: var(--weight-bold);
             padding: var(--space-1) var(--space-3) var(--space-4); }
.nav-drawer .nav-item { display: flex; align-items: center; gap: var(--space-2-5);
                        border-radius: var(--radius-control); padding: var(--space-2-5) var(--space-3);
                        font-size: var(--text-md); color: var(--label-secondary); text-decoration: none; }
.nav-drawer .nav-item .icon { width: 18px; height: 18px; }
.nav-drawer .nav-item:hover { background: var(--bg-subtle); }
.nav-drawer .nav-item.active { color: var(--accent-text); font-weight: var(--weight-semibold); background: var(--accent-tint); }
.nav-drawer .nav-item:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.nav-grow { flex: 1; min-height: var(--space-4); }
.nav-user { display: flex; align-items: center; gap: var(--space-2); margin-top: var(--space-1);
            padding: var(--space-3) var(--space-3) var(--space-1); border-top: 1px solid var(--separator-section); }
.avatar { width: var(--avatar-size); height: var(--avatar-size); border-radius: 50%; flex: none;
          background: var(--accent-tint-strong); color: var(--accent-strong);
          display: grid; place-items: center; font-weight: var(--weight-bold); font-size: var(--text-xs); }
.avatar:empty { visibility: hidden; }
.user-chip { flex: 1; min-width: 0; font-size: var(--text-sm); color: var(--label-secondary);
             display: inline-flex; align-items: center; }
.user-chip .role { display: inline-block; margin-left: var(--space-1-5); font-size: var(--text-3xs);
                   font-weight: var(--weight-bold); text-transform: uppercase; letter-spacing: .4px;
                   padding: var(--space-0-5) var(--space-1-5); border-radius: var(--radius-chip);
                   background: var(--accent-tint); color: var(--accent-text); }
.user-chip .role.admin { background: var(--warning-tint); color: var(--warning-strong); }
.nav-logout { border: none; background: none; cursor: pointer; color: var(--label-tertiary); line-height: 0;
              padding: var(--space-1-5); border-radius: var(--radius-sm); }
.nav-logout .icon { width: 18px; height: 18px; }
.nav-logout:hover { background: var(--bg-hover); color: var(--label-primary); }

/* ---------- Eyebrow labels ---------- */
/* Written in title case, not set in capitals (owner, 2026-10-05): "Paid So Far in
   October" reads; the capitals did not. So the text is title case at its source
   (index.html, and titleCase() in js/core.js for what a script writes), and nothing
   here transforms it. */
.card h2, .form-grid label, thead th, .summary-banner .k, .dg-banner .k, .stat-k, body.login-page label {
  font-size: var(--eyebrow-size);
  font-weight: var(--eyebrow-weight);
}
.card h2 { font-size: var(--text-sm); font-weight: var(--weight-bold); color: var(--label-secondary);
           margin-bottom: var(--space-3); }
.muted { color: var(--label-quaternary); font-size: var(--text-xs); font-weight: var(--weight-regular);
         text-transform: none; letter-spacing: 0; }

/* ---------- Cards ---------- */
.card { background: var(--bg-card); border-radius: var(--radius-card); padding: var(--space-4);
        margin-bottom: var(--space-3); box-shadow: var(--shadow-card); }
.card-head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2);
             flex-wrap: wrap; margin-bottom: var(--space-3); }
.card-head h2 { margin-bottom: 0; }
.card h2.sub-title { margin-top: var(--space-5); }

/* ---------- Error banner ---------- */
.banner { background: var(--danger-alert-bg); border: 1px solid var(--danger-border); color: var(--danger-strong);
          border-radius: var(--radius-card); padding: var(--space-3); margin-bottom: var(--space-3);
          font-size: var(--text-body); }

/* ---------- Forms ---------- */
.form-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: var(--space-2); }
.form-grid .wide { grid-column: 1 / -1; }
.form-grid label { display: block; color: var(--label-tertiary); margin-bottom: var(--space-1); }
.form-grid input, .form-grid select, table input, table select {
  width: 100%; min-width: 0; padding: var(--space-2); font-size: var(--text-body); font-family: inherit;
  background: var(--bg-field); color: var(--label-primary);
  border: 1px solid transparent; border-radius: var(--radius-control);
}
.form-grid input[type=date] { -webkit-appearance: none; appearance: none; }
.form-grid input:focus, .form-grid select:focus, table input:focus, table select:focus {
  outline: none; border-color: var(--accent); box-shadow: var(--focus-ring);
}
.form-actions { margin-top: var(--space-3); display: flex; gap: var(--space-2); align-items: center; flex-wrap: wrap; }
button.primary, button.secondary { border: none; border-radius: var(--radius-control);
  padding: var(--space-2) var(--space-4); font-size: var(--text-body); font-weight: var(--weight-semibold);
  font-family: inherit; cursor: pointer; white-space: nowrap; }
button.primary { background: var(--success); color: var(--on-accent); }
button.primary:hover { background: var(--success-pressed); }
button.primary:active { transform: scale(.97); }
button.primary:disabled { opacity: .6; cursor: default; }
button.secondary { background: var(--bg-hover); color: var(--label-secondary); }
button.secondary:hover { background: var(--bg-hover-strong); }
.form-msg { font-size: var(--text-xs); }
.form-msg.ok { color: var(--success-text); }
.form-msg.err { color: var(--danger); }
.hint { font-size: var(--text-xs); color: var(--label-quaternary); margin-top: var(--space-2); }

/* ---------- Summary banner ----------
   The baby tracker's .last-feed banner: blue surface, white labels, the headline
   number in yellow. Controls on it are on-accent variants (a dark translucent
   track, white selected segment) because the gray segmented control reads as
   mud on blue. */
.summary-banner { background: var(--accent-surface); color: var(--on-accent); border-radius: var(--radius-banner);
                  padding: var(--space-4); margin-bottom: var(--space-3); box-shadow: var(--shadow-banner);
                  display: grid; grid-template-columns: 1fr auto 1fr; align-items: center;
                  row-gap: var(--space-3);
                  /* Two lines: controls, then the four figures. The equal 1fr side columns
                     keep the stepper centred. */
                  grid-template-areas: "tabs stepper today" "stats stats stats"; }
#period-toggle { grid-area: tabs; justify-self: start; }
.sb-stepper { grid-area: stepper; justify-self: center; }
.sb-today   { grid-area: today; justify-self: end; }
/* The figures share one hairline: no column gap, padding inside each instead. */
.sb-stats   { grid-area: stats; display: grid; grid-template-columns: repeat(4, 1fr);
              border-top: 1px solid var(--on-accent-separator); }
.sb-tabs { display: flex; gap: var(--space-0-5); background: var(--on-accent-track);
           border-radius: var(--radius-control); padding: var(--space-0-5); }
.sb-tabs .tab { border: none; font-family: inherit; cursor: pointer; background: transparent;
                color: var(--on-accent-muted); border-radius: var(--radius-sm);
                padding: var(--space-1) var(--space-3); font-size: var(--text-xs); white-space: nowrap; }
.sb-tabs .tab:hover { color: var(--on-accent); background: var(--on-accent-hover); }
.sb-tabs .tab.active { background: var(--on-accent-selected); color: var(--on-accent-selected-fg);
                       font-weight: var(--weight-semibold); box-shadow: var(--shadow-seg); }
.sb-tabs .tab:focus-visible, .sb-stepper button:focus-visible, .sb-today:focus-visible { box-shadow: var(--focus-ring-on-accent); }
.sb-stepper { display: flex; align-items: center; gap: var(--space-1); }
.sb-stepper button { border: none; font-family: inherit; cursor: pointer; background: var(--on-accent-track);
                     color: var(--on-accent); border-radius: var(--radius-control); width: 32px; height: 32px;
                     font-size: var(--text-lg); line-height: var(--leading-tight); }
.sb-stepper button:hover:not(:disabled), .sb-today:hover:not(:disabled) { background: var(--on-accent-hover); }
.sb-stepper button:disabled, .sb-today:disabled { opacity: .35; cursor: default; }
/* Fixed width so the › arrow doesn't jump as the label changes length
   ("May 2026" vs "Sep 28 – Oct 4, 2026"). */
.sb-range { min-width: 11em; text-align: center; font-size: var(--text-md); font-weight: var(--weight-bold);
            white-space: nowrap; }
.sb-today { border: none; font-family: inherit; cursor: pointer; background: var(--on-accent-track);
            color: var(--on-accent); border-radius: var(--radius-pill); height: 32px;
            padding: 0 var(--space-4); font-size: var(--text-xs); font-weight: var(--weight-bold); }
.sb-item { padding-top: var(--space-3); min-width: 0; }
.sb-item .v { font-size: var(--text-banner); font-weight: var(--weight-bold); line-height: var(--leading-tight);
              font-variant-numeric: tabular-nums; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sb-item .v.spend { color: var(--on-accent-highlight); }
.sb-item .v.income { color: var(--on-accent-income); }
.sb-item .k { color: var(--on-accent-muted); margin-top: var(--space-1-5); }

/* ---------- Month banner (Dashboard) ----------
   The year over the month (a button that opens the picker), a hairline, then Income,
   Spending and Saved with the savings rate. Income pale green, spending yellow —
   never red. Same on-accent tokens as the Cash Flow banner. */
.month-banner { background: var(--accent-surface); color: var(--on-accent); border-radius: var(--radius-banner);
                padding: var(--space-4); margin-bottom: var(--space-3); box-shadow: var(--shadow-banner);
                display: grid; grid-template-columns: auto repeat(3, 1fr); align-items: center;
                column-gap: var(--space-3); }
/* The top bar stays on screen while a page scrolls; js/shell.js (syncPinOffsets)
   measures its height into --header-h for anything that sticks below it. */
.page-header { position: sticky; top: 0; z-index: 12; }
.mb-pick { position: relative; padding-right: var(--space-4); margin-right: var(--space-1);
           border-right: 1px solid var(--on-accent-separator); }
.mb-month-btn { display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-0-5);
                border: none; background: none; color: var(--on-accent); font-family: inherit; cursor: pointer;
                border-radius: var(--radius-control); padding: var(--space-1) var(--space-2);
                margin: calc(-1 * var(--space-1)) calc(-1 * var(--space-2)); }
.mb-month-btn:hover { background: var(--on-accent-hover); }
.mb-month-btn:focus-visible { outline: none; box-shadow: var(--focus-ring-on-accent); }
.mb-year { font-size: var(--text-xs); color: var(--on-accent-muted); }
.mb-month { font-size: var(--text-banner); font-weight: var(--weight-bold); line-height: var(--leading-tight);
            white-space: nowrap; }
.mb-caret { font-size: var(--text-xs); margin-left: var(--space-1); vertical-align: middle; }
.mb-stat { min-width: 0; }
.mb-stat .k { font-size: var(--text-xs); color: var(--on-accent-muted); }
.mb-stat .v { font-size: var(--text-banner); font-weight: var(--weight-bold); line-height: var(--leading-tight);
              font-variant-numeric: tabular-nums; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.mb-stat .v.spend { color: var(--on-accent-highlight); }
.mb-stat .v.income { color: var(--on-accent-income); }
.mb-rate { font-size: var(--text-sm); font-weight: var(--weight-medium); color: var(--on-accent-muted); }
.mb-rate:empty { display: none; }

/* The month picker: a rounded card under the month, year stepper over a 3×4 grid. */
.month-picker { position: absolute; top: calc(100% + var(--space-2)); left: calc(-1 * var(--space-2)); z-index: 15;
                width: 248px; background: var(--bg-card); color: var(--label-primary);
                border-radius: var(--radius-card); box-shadow: var(--shadow-drawer); padding: var(--space-3); }
.mp-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: var(--space-2); }
.mp-year { font-weight: var(--weight-bold); font-size: var(--text-md); }
.mp-head button { border: none; background: var(--bg-subtle); color: var(--label-primary); font-family: inherit;
                  cursor: pointer; width: 32px; height: 32px; border-radius: var(--radius-control);
                  font-size: var(--text-lg); line-height: var(--leading-tight); }
.mp-head button:hover:not(:disabled) { background: var(--bg-hover); }
.mp-head button:disabled { opacity: .35; cursor: default; }
.mp-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-1); }
.mp-month { border: none; background: none; color: var(--label-primary); font-family: inherit; cursor: pointer;
            border-radius: var(--radius-control); padding: var(--space-2) 0; font-size: var(--text-body); }
.mp-month:hover:not(:disabled) { background: var(--bg-subtle); }
.mp-month:disabled { color: var(--label-faint); cursor: default; }
.mp-month[aria-current="true"] { background: var(--accent); color: var(--on-accent); font-weight: var(--weight-semibold); }
.mp-head button:focus-visible, .mp-month:focus-visible { outline: none; box-shadow: var(--focus-ring); }

/* ---------- Charts ---------- */
/* Scrolling chart: a fixed HTML dollar axis beside a horizontally scrolling canvas
   (the baby tracker's .chart-shell pattern). js/cashflow.js sizes #trend-wrap so the
   selected period exactly fills the visible width. */
.chart-shell { display: flex; align-items: stretch; }
.chart-y-axis { position: relative; flex: 0 0 48px; height: 240px; color: var(--chart-tick); font-size: var(--text-2xs); }
.chart-y-tick { position: absolute; right: var(--space-2); transform: translateY(-50%); white-space: nowrap;
                font-variant-numeric: tabular-nums; }
.chart-scroll { flex: 1 1 auto; min-width: 0; overflow-x: auto; overflow-y: hidden; -webkit-overflow-scrolling: touch;
                scrollbar-width: none; cursor: grab; }
.chart-scroll::-webkit-scrollbar { display: none; }
.chart-scroll.dragging { cursor: grabbing; }
.chart-wrap { position: relative; height: 240px; min-width: 100%; }
#trend-chart { touch-action: pan-x pan-y; }
/* A chart that fills its card: Chart.js sizes the canvas to this box. */
.chart-box { position: relative; height: 240px; }
.chart-box.pace-box { height: 200px; }
#cf-chart { cursor: pointer; }
/* The HTML legend beside a chart: Chart.js's own is drawn in the canvas and cannot
   read the tokens. */
.legend { display: flex; flex-wrap: wrap; gap: var(--space-1) var(--space-4); font-size: var(--text-xs);
          color: var(--label-tertiary); }
.legend > span { display: inline-flex; align-items: center; gap: var(--space-1-5); }
.legend > span[hidden] { display: none; }
.swatch { width: 12px; height: 12px; border-radius: var(--radius-swatch); display: inline-block; flex: none; }
.swatch.income { background: var(--income-fill); }
.swatch.spend { background: var(--expense-fill); }
.swatch.saved { background: var(--accent); }
.swatch.then { background: var(--cat-other); }
.swatch.paid { background: var(--success); }

/* ---------- Filter chips ---------- */
.chips { display: flex; gap: var(--space-1-5); flex-wrap: wrap; }
.chip { border: none; font-family: inherit; cursor: pointer; background: var(--accent-tint); color: var(--accent-text);
        border-radius: var(--radius-pill); padding: var(--space-1) var(--space-3); font-size: var(--text-xs);
        font-weight: var(--weight-semibold); }
.chip:hover { background: var(--accent-tint-strong); }

/* Transaction search — a full-width row under the card head, styled like the form fields. */
.txn-search { display: block; width: 100%; min-width: 0; margin-bottom: var(--space-3);
              padding: var(--space-2) var(--space-3); font-size: var(--text-body); font-family: inherit;
              background: var(--bg-field); color: var(--label-primary);
              border: 1px solid transparent; border-radius: var(--radius-control); }
.txn-search:focus { outline: none; border-color: var(--accent); box-shadow: var(--focus-ring); }

/* ---------- Table (inset grouped list) ---------- */
table { width: 100%; border-collapse: collapse; }
thead th { text-align: left; padding: var(--space-2); color: var(--label-quaternary);
           border-bottom: 1px solid var(--separator-soft); white-space: nowrap; }
tbody td { padding: var(--space-2); border-bottom: 1px solid var(--separator-soft); vertical-align: middle; }
tbody tr:last-child td { border-bottom: none; }
th.num, td.amt { text-align: right; }
td.amt { font-variant-numeric: tabular-nums; font-weight: var(--weight-semibold); white-space: nowrap; }
td.amt.expense { color: var(--expense-text); }
td.amt.income { color: var(--income-text); }
td.date { white-space: nowrap; color: var(--label-tertiary); font-variant-numeric: tabular-nums; }
td.note, td.by { color: var(--label-tertiary); }
td.by { white-space: nowrap; }

/* ---------- Icons ----------
   The sprite is inlined in index.html (tools/build_icons.py) and rows reference it
   with <use href="#icon-…">. It must take no layout space; display:none would work
   in current browsers, but zero-size + hidden is the form that has always worked. */
.icon-sprite { position: absolute; width: 0; height: 0; overflow: hidden; }
/* stroke, not fill: Lucide icons are stroked outlines, and currentColor is what
   makes one file serve light and dark mode. */
.icon { width: 20px; height: 20px; flex: none; stroke: currentColor; fill: none; vertical-align: middle; }

/* ---------- Transactions table ----------
   Four columns: icon · note · by · amount. The category name is carried by the icon
   (and spelled out in the detail dialog), which leaves the note the whole middle of
   the row — it is the part that says what a charge actually was. */
.txn-table td.col-icon, .txn-table th.col-icon { width: 1%; padding-right: 0; white-space: nowrap; }
.txn-table .cat-icon { color: var(--label-tertiary); display: block; }
/* max-width:0 with width:100% is the table-layout trick that lets a cell ellipsis:
   without it the cell grows to fit and the row scrolls instead of truncating. */
.txn-table td.note { max-width: 0; width: 100%; overflow: hidden; text-overflow: ellipsis;
                     white-space: nowrap; color: var(--label-primary); }
/* A row with no note shows its category instead, in the quieter note color. */
.txn-table td.note.is-category { color: var(--label-tertiary); }
.txn-table td.by, .txn-table th.col-by { width: 1%; white-space: nowrap; color: var(--label-tertiary);
                                         font-size: var(--text-xs); }

/* The whole row is the control that opens the detail dialog. */
.txn-table tr.txn-row { cursor: pointer; }
.txn-table tr.txn-row:hover td { background: var(--bg-hover); }
.txn-table tr.txn-row:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* Day sections: each date gets a tinted header row with its total, and the
   transactions of that day sit under it. */
tbody tr.day-row th, tbody tr.day-row td { background: var(--bg-subtle); padding: var(--space-2-5) var(--space-2) var(--space-1-5);
  border-bottom: 1px solid var(--separator-soft);
  font-size: var(--text-xs); font-weight: var(--weight-semibold); color: var(--label-secondary);
  text-align: left; white-space: nowrap; }
tbody tr.day-row td.amt { text-align: right; font-weight: var(--weight-semibold); color: var(--label-tertiary); }
tbody tr.day-row th:first-child { border-radius: var(--radius-sm) 0 0 var(--radius-sm); }
tbody tr.day-row td:last-child { border-radius: 0 var(--radius-sm) var(--radius-sm) 0; }
/* A little air above every day after the first, so days read as separate sections. */
tbody tr.day-row.gap th, tbody tr.day-row.gap td { border-top: var(--space-3) solid var(--bg-card); }
td.actions { text-align: right; white-space: nowrap; }
/* Review-queue buttons only. The transactions table has no per-row buttons: a row
   opens the detail dialog, and Edit/Delete live there. */
button.approve, button.reject, button.reprocess {
  border: none; font-family: inherit; cursor: pointer; border-radius: var(--radius-sm);
  padding: var(--space-1) var(--space-2); font-size: var(--text-xs); margin-left: var(--space-1);
  background: var(--bg-hover); color: var(--accent-text);
}
button.reprocess:hover { background: var(--accent-tint); }
button.approve { background: var(--success); color: var(--on-accent); font-weight: var(--weight-semibold); }
button.approve:hover { background: var(--success-pressed); }
button.reject { color: var(--label-secondary); }
button.reject:hover { background: var(--bg-hover-strong); }
button:disabled { opacity: .6; cursor: default; }
.empty { color: var(--label-quaternary); font-size: var(--text-body); text-align: center; padding: var(--space-5); }

/* Review queue */
.conf { font-size: var(--text-2xs); padding: var(--space-0-5) var(--space-2); border-radius: var(--radius-pill); }
.conf.high { background: var(--success-tint); color: var(--success-text); }
.conf.mid  { background: var(--warning-tint); color: var(--warning-strong); }
.conf.low  { background: var(--bg-hover); color: var(--label-tertiary); }
#pending-tbody select, #pending-tbody input { padding: var(--space-1) var(--space-1-5); font-size: var(--text-sm); }
#pending-tbody select.type-edit { display: block; margin-bottom: var(--space-1); }
#pending-tbody input.amt-edit { width: 92px; text-align: right; font-variant-numeric: tabular-nums; }
.why { font-size: var(--text-xs); color: var(--label-quaternary); }
.why.pre { white-space: pre-wrap; }
/* A forwarded email is full of long unbroken tracking URLs. Without anywhere-wrapping
   one of them sets the merchant cell's minimum width, and Category, Amount and Approve
   are pushed off screen. The cap keeps a ~2,000-character excerpt to a scrollable box. */
.why.pre { overflow-wrap: anywhere; max-height: 16rem; overflow-y: auto; }

/* The menu's queue count. */
.nav-count { display: inline-block; min-width: 1.4em; margin-left: var(--space-1);
             padding: 0 var(--space-1-5); border-radius: var(--radius-pill);
             background: var(--accent-tint); color: var(--accent-text);
             font-size: var(--text-2xs); font-weight: var(--weight-bold); text-align: center; }
.nav-count[hidden] { display: none; }
/* Analysis — duplicate groups. Each group is a bordered block so the rows inside
   it read as one candidate charge rather than as a flat list. */
.dup-group { border: 1px solid var(--separator-soft); border-radius: var(--radius-card);
             padding: var(--space-2) var(--space-3); margin-bottom: var(--space-3); }
.dup-head { display: flex; align-items: baseline; gap: var(--space-2); flex-wrap: wrap;
            margin-bottom: var(--space-1); }
.dup-amount { font-weight: var(--weight-semibold); font-size: var(--text-md);
              font-variant-numeric: tabular-nums; }
.dup-amount.expense { color: var(--expense-text); }
.dup-amount.income  { color: var(--income-text); }
.dup-head .dup-action { margin-left: auto; padding: 0 var(--space-1); }
#dup-dismissed-box { margin-top: var(--space-4); }
.dup-group .txn-table td { padding-top: var(--space-1-5); padding-bottom: var(--space-1-5); }

/* Recurring bills — a rule per row: icon · name over its schedule · amount. The
   label cell takes the width; its schedule line wraps rather than truncating. */
.rec-table td.rec-label { width: 100%; }
.rec-table td.amt { vertical-align: top; }
.txn-table tr.txn-row.readonly { cursor: default; }
.txn-table tr.txn-row.readonly:hover td { background: none; }
/* The Recurring page (PLAN.md §3.8): four tiles, then the month's bills beside a
   calendar. A bill's status is a pill; spending stays amber and a late bill is the
   warning colour — never red. */
body[data-view="recurring"] .container { max-width: 1240px; }
.bill-tiles { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 var(--space-3); }
.bill-tile .stat-v { margin: var(--space-1) 0; }
.bill-tile .muted { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bill-grid { display: grid; grid-template-columns: minmax(0, 1fr); column-gap: var(--space-3);
             align-items: start; }
@media (min-width: 900px) {
  .bill-tiles { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .bill-grid { grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); }
}
.bill-head-actions { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap;
                     justify-content: flex-end; }
/* Narrower than a queue row's title: a bill row also carries a status pill. */
.bill-row .mini-title { flex-basis: 90px; }
.bill-row.open { cursor: pointer; border-radius: var(--radius-sm); }
.bill-row.open:hover { background: var(--bg-subtle); }
.bill-row.open:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.bill-row.done .mini-title b { color: var(--label-tertiary); }
.bill-status { font-size: var(--text-2xs); font-weight: var(--weight-semibold); white-space: nowrap;
               padding: var(--space-0-5) var(--space-2); border-radius: var(--radius-pill);
               background: var(--bg-hover); color: var(--label-tertiary); }
.bill-status.paid { background: var(--success-tint); color: var(--success-text); }
.bill-status.due { background: var(--accent-tint); color: var(--accent-text); }
.bill-status.late { background: var(--warning-tint); color: var(--warning-strong); }
.bill-stepper { display: flex; align-items: center; gap: var(--space-1); font-size: var(--text-sm);
                font-weight: var(--weight-semibold); }
.bill-stepper > button:not(.link-btn) { border: none; font-family: inherit; cursor: pointer;
                background: var(--bg-hover); color: var(--label-primary); width: 28px; height: 28px;
                border-radius: var(--radius-sm); font-size: var(--text-md); line-height: 1; }
.bill-stepper > button:not(.link-btn):hover:not(:disabled) { background: var(--bg-hover-strong); }
.bill-stepper > button:disabled { opacity: .35; cursor: default; }
.bill-stepper > button:focus-visible { outline: none; box-shadow: var(--focus-ring); }
/* Wide enough for the longest label, so the arrows never move as the month changes:
   "September" on the calendar, "September 2025" on Budget. */
.bill-stepper > span { min-width: 6em; text-align: center; white-space: nowrap; }
#bg-month { min-width: 9em; }
.bill-cal { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: var(--space-1);
            margin-bottom: var(--space-3); }
.bill-cal .cal-h { text-align: center; font-size: var(--text-2xs); color: var(--label-quaternary);
                   font-weight: var(--weight-semibold); padding-bottom: var(--space-1); }
.bill-cal .cal-d { min-height: 44px; border-radius: var(--radius-sm); background: var(--bg-subtle);
                   padding: var(--space-1) var(--space-1-5); font-size: var(--text-xs);
                   color: var(--label-secondary); font-variant-numeric: tabular-nums; }
.bill-cal .cal-d.off { background: none; }
.bill-cal .cal-d.today { box-shadow: inset 0 0 0 2px var(--accent); color: var(--accent-text);
                         font-weight: var(--weight-semibold); }
.bill-cal .cal-dots { display: flex; flex-wrap: wrap; gap: 3px; margin-top: var(--space-1); }
.bill-cal .cal-dots i { width: 6px; height: 6px; border-radius: 50%; background: var(--cat-other); }
.bill-cal .cal-dots i.paid { background: var(--success); }
.bill-cal .cal-dots i.due { background: var(--expense-fill); }
#bill-dismissed-box { margin-top: var(--space-4); }
.rf-every { display: flex; gap: var(--space-2); }
.rf-every input { width: 5em; flex: none; }

/* Digest — one month against its own history (app/digest.py). The banner is the
   Trends banner's surface and on-accent controls: spent in yellow, income pale
   green. In the cards, more spending is amber and less is green — never red, and
   a missed bill is a warning, not an error. */
.dg-banner { background: var(--accent-surface); color: var(--on-accent); border-radius: var(--radius-banner);
             padding: var(--space-4); margin-bottom: var(--space-3); box-shadow: var(--shadow-banner);
             display: grid; grid-template-columns: repeat(3, 1fr); row-gap: var(--space-3); }
.dg-controls { grid-column: 1 / -1; display: flex; align-items: center; justify-content: center;
               gap: var(--space-2); flex-wrap: wrap; }
.dg-stat { padding-top: var(--space-3); border-top: 1px solid var(--on-accent-separator); text-align: center; }
.dg-stat .v { font-size: var(--text-stat); font-weight: var(--weight-bold); line-height: var(--leading-tight);
              font-variant-numeric: tabular-nums; }
.dg-stat .v.spend { color: var(--on-accent-highlight); }
.dg-stat .v.income { color: var(--on-accent-income); }
.dg-stat .k { color: var(--on-accent-muted); margin-top: var(--space-1-5); }
.dg-summary p { font-size: var(--text-body); color: var(--label-primary); margin-bottom: var(--space-1-5); }
.dg-summary { margin-bottom: var(--space-3); }
.dg-table td.dg-label { width: 100%; }
.dg-table td.amt { vertical-align: top; white-space: nowrap; }
.dg-table td.date { vertical-align: top; white-space: nowrap; }
.dg-table td.amt.dg-more { color: var(--expense-text); }
.dg-table td.amt.dg-less { color: var(--income-text); }
.dg-flag { font-size: var(--text-2xs); padding: var(--space-0-5) var(--space-2); border-radius: var(--radius-pill);
           margin-left: var(--space-2); white-space: nowrap; }
.dg-flag.over { background: var(--expense-tint); color: var(--expense-text); }
.dg-flag.under { background: var(--income-tint); color: var(--income-text); }
.dg-flag.seasonal { background: var(--bg-hover); color: var(--label-tertiary); }
.dg-table td.dg-missed .why { color: var(--warning-strong); font-weight: var(--weight-semibold); }
.dg-list { list-style: none; padding: 0; margin: 0; }
.dg-list li { font-size: var(--text-body); padding: var(--space-1-5) 0; border-bottom: 1px solid var(--separator-soft); }
.dg-list li:last-child { border-bottom: none; }
.dg-ytd { font-size: var(--text-body); margin-bottom: var(--space-2); }
@media (max-width: 760px) {
  .dg-stat .v { font-size: var(--text-lg); }
}

/* Ask the ledger (app/ask.py). A sentence over a plain table: spending amber, income
   green — never red. A refused or empty answer is a neutral note, not an error; the
   fix is to rephrase. */
.ask-form { display: flex; gap: var(--space-2); align-items: center; }
.ask-form .txn-search { margin-bottom: 0; flex: 1; }
.ask-recent { margin-top: var(--space-3); }
.ask-recent:empty, #ask-status:empty, #ask-assumptions:empty, #ask-meta:empty { display: none; }
.ask-recent .chip { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ask-sentence { font-size: var(--text-body); font-weight: var(--weight-semibold); color: var(--label-primary); }
.ask-sentence.ask-problem { font-weight: var(--weight-regular); color: var(--label-secondary); }
.ask-table-wrap { overflow-x: auto; margin-top: var(--space-2); }
.ask-sql { margin-top: var(--space-3); }
.ask-sql summary { cursor: pointer; font-size: var(--text-xs); color: var(--label-tertiary); }
.ask-sql pre { margin-top: var(--space-2); padding: var(--space-3); background: var(--bg-subtle);
               border-radius: var(--radius-control); color: var(--label-secondary); font-size: var(--text-xs);
               font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
               white-space: pre-wrap; overflow-wrap: anywhere; }
.modal-actions .spacer { flex: 1; }
#rf-msg:empty, #rf-preview:empty { display: none; }
#rf-msg { margin-top: var(--space-3); }

/* ---------- Pager ---------- */
.pager { display: flex; align-items: center; justify-content: center; gap: var(--space-3); margin-top: var(--space-3);
         font-size: var(--text-xs); color: var(--label-tertiary); }
.pager button { border: none; font-family: inherit; cursor: pointer; background: var(--bg-hover);
                color: var(--label-secondary); border-radius: var(--radius-control);
                padding: var(--space-1-5) var(--space-3); font-size: var(--text-xs); }
.pager button:hover:not(:disabled) { background: var(--bg-hover-strong); }
.pager button:disabled { opacity: .45; cursor: default; }

/* ---------- Transactions page (PLAN.md §3.4) ----------
   A wider page than the others: the list plus a Summary panel on the right, which
   drops below the list when there is no room for both. */
body[data-view="transactions"] .container, body[data-view="review"] .container { max-width: 1240px; }
.tabs { display: flex; gap: var(--space-5); border-bottom: 1px solid var(--separator-section);
        margin-bottom: var(--space-4); }
.tab-link { border: none; background: none; cursor: pointer; font-family: inherit;
            padding: var(--space-2) 0; font-size: var(--text-md); font-weight: var(--weight-medium);
            color: var(--label-tertiary); display: inline-flex; align-items: center; gap: var(--space-1-5); }
.tab-link:hover { color: var(--label-primary); }
.tab-link.active { color: var(--accent-text); font-weight: var(--weight-semibold);
                   box-shadow: inset 0 -2px 0 var(--accent); }
.tab-link:focus-visible { outline: none; box-shadow: var(--focus-ring); border-radius: var(--radius-sm); }
.tab-count { background: var(--bg-hover); color: var(--label-secondary); border-radius: var(--radius-pill);
             font-size: var(--text-2xs); font-weight: var(--weight-bold); padding: 0 var(--space-1-5); }
.tab-count[hidden] { display: none; }

.tx-layout { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-4); align-items: start; }
.tx-layout > .card { margin-bottom: 0; }
.tx-filters { display: flex; flex-wrap: wrap; gap: var(--space-2); align-items: center;
              margin-bottom: var(--space-3); }
.tx-filters .txn-search { flex: 1 1 220px; width: auto; margin-bottom: 0; }
.tx-filters .tx-vendor { flex: 0 1 180px; }
/* The vendor filter: a button that reads like the selects beside it, over a
   searchable list (vendorPick() in js/core.js). */
.vendor-pick { position: relative; min-width: 0; }
.vp-btn { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2);
  width: 100%; padding: var(--space-2) var(--space-3); cursor: pointer; text-align: left;
  font-size: var(--text-sm); font-family: inherit; font-weight: var(--weight-medium);
  background: var(--bg-field); color: var(--label-primary);
  border: 1px solid transparent; border-radius: var(--radius-control); }
.vp-btn:focus { outline: none; border-color: var(--accent); box-shadow: var(--focus-ring); }
.vp-btn::after { content: ""; flex: none; width: 7px; height: 7px; margin-top: -4px;
  border-right: 2px solid currentColor; border-bottom: 2px solid currentColor; transform: rotate(45deg); }
.vp-pop { position: absolute; top: calc(100% + var(--space-1)); left: 0; z-index: 15;
  width: max(100%, 260px); background: var(--bg-card); color: var(--label-primary);
  border-radius: var(--radius-card); box-shadow: var(--shadow-drawer); padding: var(--space-2); }
.vp-pop[hidden] { display: none; }
.vp-search { display: block; width: 100%; padding: var(--space-2) var(--space-3);
  font-size: var(--text-sm); font-family: inherit; background: var(--bg-field); color: var(--label-primary);
  border: 1px solid transparent; border-radius: var(--radius-control); }
.vp-search:focus { outline: none; border-color: var(--accent); box-shadow: var(--focus-ring); }
.vp-list { list-style: none; margin: var(--space-2) 0 0; padding: 0; max-height: 280px; overflow-y: auto; }
.vp-list li[role="option"] { display: flex; justify-content: space-between; gap: var(--space-3);
  padding: var(--space-2) var(--space-3); border-radius: var(--radius-control); cursor: pointer;
  font-size: var(--text-sm); }
.vp-list li[role="option"]:hover, .vp-list li[role="option"]:focus { outline: none; background: var(--bg-hover); }
.vp-list li[aria-selected="true"] { font-weight: var(--weight-semibold); color: var(--accent-text); }
.vp-count { color: var(--label-tertiary); font-variant-numeric: tabular-nums; }
.vp-empty { padding: var(--space-2) var(--space-3); color: var(--label-tertiary); font-size: var(--text-sm); }
.tx-filters select, .tx-dates input { min-width: 0; padding: var(--space-2) var(--space-3);
  font-size: var(--text-sm); font-family: inherit; font-weight: var(--weight-medium);
  background: var(--bg-field); color: var(--label-primary);
  border: 1px solid transparent; border-radius: var(--radius-control); }
.tx-filters select:focus, .tx-dates input:focus { outline: none; border-color: var(--accent);
                                                  box-shadow: var(--focus-ring); }
.tx-filters select[hidden], .tx-dates[hidden], .tx-filters .link-btn[hidden] { display: none; }
.tx-dates { display: inline-flex; gap: var(--space-2); }
.link-btn { border: none; background: none; cursor: pointer; font-family: inherit; color: var(--accent-text);
            font-size: var(--text-sm); font-weight: var(--weight-semibold); padding: var(--space-2); }
.link-btn:hover { text-decoration: underline; }
.tx-head { display: flex; align-items: baseline; gap: var(--space-1-5); margin-bottom: var(--space-2); }
.tx-head h2 { margin-bottom: 0; }
.tx-export { margin-left: auto; text-decoration: none; white-space: nowrap; }

/* The category column, Transactions page only. */
.txn-table td.col-cat, .txn-table th.col-cat { width: 1%; white-space: nowrap; padding-right: var(--space-4);
                                               color: var(--label-tertiary); font-size: var(--text-sm); }

/* The Summary panel: label / value pairs. */
.kv { display: grid; grid-template-columns: auto 1fr; column-gap: var(--space-4); row-gap: var(--space-2-5);
      margin: var(--space-3) 0 0; }
.kv dt { font-size: var(--eyebrow-size); font-weight: var(--eyebrow-weight);
         letter-spacing: var(--eyebrow-tracking); color: var(--label-tertiary); align-self: center; }
.kv dd { margin: 0; font-size: var(--text-body); font-variant-numeric: tabular-nums; text-align: right;
         overflow-wrap: anywhere; }
.kv dd.expense { color: var(--expense-text); }
.kv dd.income { color: var(--income-text); }

@media (min-width: 1200px) {
  .tx-layout { grid-template-columns: minmax(0, 1fr) 300px; }
  .tx-side { position: sticky; top: calc(var(--header-h, 0px) + var(--space-4)); }
}
@media (max-width: 760px) {
  /* A phone keeps item 12's row: icon · vendor/note · by · amount. */
  .txn-table td.col-cat, .txn-table th.col-cat { display: none; }
  .tx-filters select, .tx-filters .vendor-pick { flex: 1 1 40%; }
  /* A half-width control in the right column: a wider list would leave the screen. */
  .vp-pop { width: 100%; }
}

/* ---------- Review page (PLAN.md §3.4, mockup 04) ----------
   One card per queued item. The selected one is outlined; the panel beside it
   shows its email. */
.rv-head { display: flex; align-items: baseline; gap: var(--space-1-5); margin-bottom: var(--space-3); }
.rv-head h2 { margin-bottom: 0; }
.rv-head .spacer { flex: 1; }
.rv-list { display: flex; flex-direction: column; gap: var(--space-3); }
.rv-item { border: 1px solid var(--separator-section); border-radius: var(--radius-card);
           padding: var(--space-3); display: flex; flex-direction: column; gap: var(--space-2); }
.rv-item.selected { border-color: var(--accent); box-shadow: var(--focus-ring); }
.rv-item:focus-visible { outline: none; border-color: var(--accent); box-shadow: var(--focus-ring-lg); }
.rv-top { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }
.rv-badge { width: 32px; height: 32px; border-radius: 50%; background: var(--bg-hover); flex: none;
            color: var(--label-secondary); display: grid; place-items: center; }
.rv-badge .icon { width: 16px; height: 16px; }
.rv-title { flex: 1 1 160px; min-width: 0; }
.rv-title b { display: block; font-weight: var(--weight-medium); overflow: hidden; text-overflow: ellipsis;
              white-space: nowrap; }
.rv-title small { display: block; color: var(--label-quaternary); font-size: var(--text-xs); }
.rv-fields { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
.rv-fields select, .rv-fields input { padding: var(--space-1-5) var(--space-2); font-size: var(--text-sm);
  font-family: inherit; background: var(--bg-field); color: var(--label-primary);
  border: 1px solid transparent; border-radius: var(--radius-control); }
.rv-fields input.amt-edit { width: 7.5rem; text-align: right; font-weight: var(--weight-semibold);
                            font-variant-numeric: tabular-nums; }
.rv-fields input.vendor-edit { width: 10rem; }
.rv-fields input.note-edit { width: 11rem; }
.rv-fields input.amt-edit.expense { color: var(--expense-text); }
.rv-fields input.amt-edit.income { color: var(--income-text); }
.rv-fields select:focus, .rv-fields input:focus { outline: none; border-color: var(--accent); box-shadow: var(--focus-ring); }
.rv-fields button { margin-left: 0; }
.rv-email { background: var(--bg-subtle); border-radius: var(--radius-control); padding: var(--space-2) var(--space-3);
            font-size: var(--text-xs); color: var(--label-tertiary); }
.rv-email summary { display: flex; align-items: center; gap: var(--space-2); cursor: pointer; min-width: 0; }
.rv-email summary span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.rv-email summary .icon { width: 14px; height: 14px; }
.rv-email[open] summary span { white-space: normal; }
.rv-email .why.pre { margin-top: var(--space-2); }
.rv-why { background: var(--warning-tint); color: var(--warning-strong); border-radius: var(--radius-control);
          padding: var(--space-2) var(--space-3); font-size: var(--text-xs); }
.rv-side .rv-excerpt { background: var(--bg-subtle); border-radius: var(--radius-control);
                       padding: var(--space-2) var(--space-3); margin-top: var(--space-3); }
@media (max-width: 760px) {
  .rv-keys { display: none; }
  .rv-fields { width: 100%; }
  .rv-fields select { flex: 1 1 40%; }
}

/* ---------- Wide screens: the drawer is a permanent left sidebar ----------
   Same element and width as the phone drawer; js/shell.js (syncNavMode) keeps its
   aria-hidden and tab order in step with this breakpoint. */
@media (min-width: 1024px) {
  .nav-drawer { transform: none; transition: none; box-shadow: none; z-index: 5;
                border-right: 1px solid var(--separator); }
  .nav-scrim, .nav-scrim.open { display: none; }
  body:not(.login-page) { padding-left: var(--sidebar-w); }
  .page-header button.menu-btn { display: none; }
}

/* Too narrow for the one-line control row: the period tabs and Today on top, then
   the stepper full width, then the figures two by two. */
@media (max-width: 760px) {
  .summary-banner { grid-template-columns: 1fr auto;
                    grid-template-areas: "tabs today" "stepper stepper" "stats stats"; }
  .sb-stepper { justify-self: stretch; justify-content: space-between; }
  .sb-range { min-width: 0; }
  .sb-stats { grid-template-columns: 1fr 1fr; }
  .sb-item:nth-child(n + 3) { padding-bottom: 0; }
  .sb-item { padding-bottom: var(--space-3); }
  /* The month on its own line, then the three figures. */
  .month-banner { grid-template-columns: repeat(3, 1fr); row-gap: var(--space-3); }
  .mb-pick { grid-column: 1 / -1; padding-right: 0; margin-right: 0; border-right: none;
             padding-bottom: var(--space-3); border-bottom: 1px solid var(--on-accent-separator); }
  .mb-rate { display: block; }
}

@media (max-width: 640px) {
  .container { padding: var(--space-3); }
  .card { padding: var(--space-3); }
  .page-header { padding: var(--space-2) var(--space-3); }
  .sb-item .v { font-size: var(--text-lg); }
  .mb-stat .v { font-size: var(--text-base); }
  .mb-month { font-size: var(--text-lg); }
  .chart-wrap, .chart-y-axis, .chart-box { height: 200px; }
  .chart-box.pace-box { height: 170px; }
  .bar-row { gap: var(--space-2); grid-template-columns: auto minmax(0, 1fr) 2.6em auto; }
  .bar-row .amt { min-width: 0; }
  .mini-actions { flex: 1 1 100%; justify-content: flex-end; }
  .chart-y-axis { flex-basis: 40px; }
  thead th, tbody td { padding: var(--space-1-5) var(--space-1); }
  th.hide-sm, td.hide-sm { display: none; }
}

/* ---------- Dashboard and Cash Flow (PLAN.md §3.2, §3.5) ----------
   Card pages: a wide main column beside a narrower one, stacking into one column
   when there is no room for both. */
body[data-view="dashboard"] .container, body[data-view="cashflow"] .container { max-width: 1240px; }
.dash-grid, .cf-grid { display: grid; grid-template-columns: minmax(0, 1fr); column-gap: var(--space-3);
                       align-items: start; }
.dash-col { min-width: 0; }
@media (min-width: 900px) {
  .dash-grid { grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); }
  .cf-grid { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
}
/* A card's link to the page it summarizes. */
.card-link { color: var(--accent-text); font-size: var(--text-xs); font-weight: var(--weight-semibold);
             text-decoration: none; white-space: nowrap; border-radius: var(--radius-sm); }
.card-link:hover { text-decoration: underline; }
.card-link:focus-visible, .bar-row:focus-visible, .mini-edit:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.card-total { font-size: var(--text-body); font-weight: var(--weight-semibold); font-variant-numeric: tabular-nums;
              text-transform: none; letter-spacing: 0; margin-left: var(--space-1); }
.card-total.expense { color: var(--expense-text); }
.card-total.income { color: var(--income-text); }

/* A segmented control on a card (the banner has its own on-accent one, .sb-tabs). */
.seg { display: inline-flex; gap: var(--space-0-5); background: var(--seg-track);
       border-radius: var(--radius-control); padding: var(--space-0-5); }
.seg button { border: none; font-family: inherit; cursor: pointer; background: transparent;
              color: var(--label-tertiary); border-radius: var(--radius-sm);
              padding: var(--space-1) var(--space-3); font-size: var(--text-xs); white-space: nowrap; }
.seg button:hover:not(:disabled) { color: var(--label-primary); }
.seg button.active { background: var(--seg-selected); color: var(--label-primary);
                     font-weight: var(--weight-semibold); box-shadow: var(--shadow-seg); }

/* A card's own figures: the number over its label. Spending is amber, a fall is
   green — never red. */
.stat-row { display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-6); margin-bottom: var(--space-3); }
.stat-v { font-size: var(--text-xl); font-weight: var(--weight-bold); line-height: var(--leading-snug);
          font-variant-numeric: tabular-nums; }
.stat-v.expense { color: var(--expense-text); }
.stat-v.income { color: var(--income-text); }
.stat-k { color: var(--label-quaternary); }
.pace-box + .legend { margin-top: var(--space-2); }

/* Short rows on a card: badge · title over a small line · amount · actions. Not the
   transactions table — these are queue items and bills, and they open nothing. */
.mini-row { display: flex; align-items: center; gap: var(--space-2) var(--space-3); flex-wrap: wrap;
            padding: var(--space-2) 0; border-bottom: 1px solid var(--separator-soft); }
.mini-row:last-child { border-bottom: none; }
.mini-title { flex: 1 1 140px; min-width: 0; }
.mini-title b { display: block; font-weight: var(--weight-medium); overflow: hidden; text-overflow: ellipsis;
                white-space: nowrap; }
.mini-title small { display: block; color: var(--label-quaternary); font-size: var(--text-xs);
                    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mini-amt { font-variant-numeric: tabular-nums; font-weight: var(--weight-semibold); white-space: nowrap; }
.mini-amt.expense { color: var(--expense-text); }
.mini-amt.income { color: var(--income-text); }
.mini-actions { display: flex; align-items: center; gap: var(--space-2); }
.mini-actions button { margin-left: 0; }
.mini-edit { color: var(--accent-text); font-size: var(--text-xs); font-weight: var(--weight-semibold);
             text-decoration: none; padding: var(--space-1) var(--space-1-5); border-radius: var(--radius-sm); }
.mini-edit:hover { text-decoration: underline; }
#short-lines { margin-bottom: 0; }
#short-lines p:last-child { margin-bottom: 0; }

/* Breakdown rows: badge · name over a thin bar · share · amount. The whole row is a
   link to the Transactions page with that filter. Vendors have a long tail, so a
   ranked list says more than a chart with twenty slices of 2%. */
.bar-list { list-style: none; }
.bar-row { display: grid; grid-template-columns: auto minmax(0, 1fr) 3.2em auto; align-items: center;
           gap: var(--space-3); padding: var(--space-2) var(--space-1-5); border-radius: var(--radius-sm);
           color: var(--label-primary); text-decoration: none; }
.bar-row:hover { background: var(--bg-hover); }
.bar-badge { width: 32px; height: 32px; border-radius: 50%; background: var(--bg-hover); flex: none;
             color: var(--label-secondary); display: grid; place-items: center;
             font-size: var(--text-xs); font-weight: var(--weight-bold); }
.bar-badge .icon { width: 16px; height: 16px; }
.bar-name { display: block; font-weight: var(--weight-medium); overflow: hidden; text-overflow: ellipsis;
            white-space: nowrap; }
.bar-track { display: block; height: 4px; margin-top: var(--space-1-5); border-radius: var(--radius-pill);
             background: var(--bg-hover); overflow: hidden; }
.bar-track i { display: block; height: 100%; border-radius: var(--radius-pill); }
.bar-track.spend i { background: var(--expense-fill); }
.bar-track.income i { background: var(--income-fill); }
.bar-row .pct { font-variant-numeric: tabular-nums; color: var(--label-quaternary); font-size: var(--text-xs);
                text-align: right; }
.bar-row .amt { font-variant-numeric: tabular-nums; font-weight: var(--weight-semibold); white-space: nowrap;
                text-align: right; min-width: 5.5em; }

/* ---------- Reports (PLAN.md §3.6) ----------
   Five tabs, which scroll sideways on a phone rather than wrap. Spending is amber,
   income green, what was kept blue — never red; the ring's slices are the category
   palette, which says "a different thing", not "good" or "bad". */
.rp-tabs { overflow-x: auto; white-space: nowrap; scrollbar-width: none; }
.rp-tabs::-webkit-scrollbar { display: none; }
.rp-tabs .tab-link { flex: none; }
.rp-filters .rp-vendor { flex: 1 1 140px; }
.rp-stats { margin-bottom: 0; }
.rp-split { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-4); align-items: center; }
.rp-split[hidden] { display: none; }
.rp-donut { height: 220px; }
#rp-chart { cursor: pointer; }
.rp-legend { list-style: none; }
/* A legend line is a button: it narrows the transactions below to its slice. */
.rp-leg { display: grid; grid-template-columns: auto minmax(0, 1fr) 3.2em auto; align-items: center;
          gap: var(--space-3); width: 100%; border: none; background: none; cursor: pointer;
          font-family: inherit; font-size: var(--text-body); color: var(--label-primary); text-align: left;
          padding: var(--space-2) var(--space-1-5); border-radius: var(--radius-sm); }
.rp-leg:hover { background: var(--bg-hover); }
.rp-leg[aria-pressed="true"] { background: var(--accent-tint); }
.rp-leg:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.rp-leg .name { font-weight: var(--weight-medium); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rp-leg .pct { font-variant-numeric: tabular-nums; color: var(--label-quaternary); font-size: var(--text-xs);
               text-align: right; }
.rp-leg .amt { font-variant-numeric: tabular-nums; font-weight: var(--weight-semibold); white-space: nowrap;
               text-align: right; min-width: 5.5em; }
@media (min-width: 760px) {
  .rp-split { grid-template-columns: 240px minmax(0, 1fr); }
}

/* Where it went: plain SVG (js/sankey.js). The bars are solid, the ribbons the same
   color at low opacity, and the labels carry a card-colored halo so they read over
   a ribbon. */
.sankey { width: 100%; overflow: hidden; }
.sankey[hidden] { display: none; }
.sankey-svg { display: block; max-width: 100%; }
.sk-node.income, .sk-link.income { fill: var(--income-fill); }
.sk-node.spend, .sk-link.spend { fill: var(--expense-fill); }
.sk-node.saved, .sk-link.saved { fill: var(--accent); }
.sk-node.short, .sk-link.short { fill: var(--cat-other); }
.sk-node.pool { fill: var(--label-tertiary); }
.sk-link { fill-opacity: .26; }
.sk-a:hover .sk-node, .sk-a:focus-visible .sk-node { stroke: var(--label-primary); stroke-width: 1px; }
.sk-a:focus-visible { outline: none; }
.sk-label { font-size: var(--text-xs); fill: var(--label-primary); pointer-events: none;
            paint-order: stroke; stroke: var(--bg-card); stroke-width: 3px; stroke-linejoin: round; }
.sk-label .n { font-weight: var(--weight-semibold); }
.sk-label .v { fill: var(--label-tertiary); font-variant-numeric: tabular-nums; }

/* By group, against the usual: the bar is this range, the tick is the usual. */
.swatch.tick { width: 2px; background: var(--label-primary); }
.usual-track { display: block; position: relative; margin-top: var(--space-1-5); }
.usual-track .bar-track { margin-top: 0; }
.usual-track b { position: absolute; top: -3px; width: 2px; height: 10px; margin-left: -1px;
                 background: var(--label-primary); border-radius: var(--radius-pill); }
.bar-name small { color: var(--label-quaternary); font-weight: var(--weight-regular);
                  font-size: var(--text-xs); margin-left: var(--space-1-5); }

/* Year over year. */
.yy-range { min-width: 4em; }
.yy-table { width: 100%; border-collapse: collapse; }
.yy-table th { text-align: left; font-size: var(--eyebrow-size); font-weight: var(--eyebrow-weight);
               color: var(--label-tertiary); padding: var(--space-1-5) 0; }
.yy-table th.amt, .yy-table td.amt { text-align: right; padding-left: var(--space-3);
                                     font-variant-numeric: tabular-nums; }
.yy-table td { padding: var(--space-1-5) 0; border-top: 1px solid var(--separator-soft); font-size: var(--text-body); }
.yy-cat { font-size: var(--text-body); font-weight: var(--weight-medium); white-space: normal; }
@media (max-width: 760px) {
  .yy-table th.amt:nth-child(3), .yy-table td.amt:nth-child(3) { display: none; }
  /* On a phone the usual goes under the group's name rather than being cut off. */
  .bar-name small { display: block; margin-left: 0; }
}

/* Settings: the profile lines sit beside their labels, not across the card. */
.st-profile { max-width: 28rem; margin-top: 0; }
.st-profile dd { text-align: left; }

/* ---------- Transaction detail dialog ----------
   Opened by clicking a row. On a phone it is where the full note can finally be
   read, so it sits at the bottom of the screen where a thumb is, and grows up. */
.modal-scrim { position: fixed; inset: 0; z-index: 60; background: var(--scrim);
               display: flex; align-items: center; justify-content: center; padding: var(--space-4); }
.modal { background: var(--bg-card); border-radius: var(--radius-banner); box-shadow: var(--shadow-login);
         width: 100%; max-width: 460px; max-height: 90vh; overflow-y: auto; padding: var(--space-5); }
.modal-head { display: flex; align-items: center; gap: var(--space-2-5); margin-bottom: var(--space-4); }
.modal-head h2 { flex: 1; min-width: 0; font-size: var(--text-lg); font-weight: var(--weight-bold);
                 color: var(--label-primary); margin: 0; }
/* The same icon as the row it was opened from, so the dialog reads as that row. */
.tm-badge { display: inline-flex; align-items: center; justify-content: center; flex: none;
            width: 40px; height: 40px; border-radius: var(--radius-pill);
            background: var(--bg-hover); color: var(--label-secondary); }
.tm-badge .icon { width: 22px; height: 22px; }
.modal-close { border: none; background: none; cursor: pointer; font-family: inherit;
               font-size: var(--text-title); line-height: 1; color: var(--label-quaternary);
               padding: var(--space-1) var(--space-2); border-radius: var(--radius-sm); }
.modal-close:hover { background: var(--bg-hover); color: var(--label-primary); }

.tm-amount { font-size: var(--text-stat); font-weight: var(--weight-bold);
             font-variant-numeric: tabular-nums; line-height: var(--leading-tight); }
.tm-amount.expense { color: var(--expense-text); }
.tm-amount.income { color: var(--income-text); }
.tm-meta { margin-top: var(--space-4); display: grid; gap: var(--space-2); }
.tm-meta > div { display: flex; gap: var(--space-3); align-items: baseline; }
.tm-meta dt { flex: none; width: 88px; color: var(--label-quaternary); font-size: var(--text-xs); }
.tm-meta dd { margin: 0; color: var(--label-secondary); font-size: var(--text-body); min-width: 0; }
/* The whole note, wrapped — the one thing the table cannot show. */
.tm-meta dd.tm-note { color: var(--label-primary); overflow-wrap: anywhere; white-space: pre-wrap; }
/* The "rename everywhere" opt-in: quiet, and only on screen once it applies. */
.tm-rename { margin-top: var(--space-3); background: var(--accent-tint);
             border-radius: var(--radius-sm); padding: var(--space-2-5) var(--space-3); }
.tm-rename label { display: flex; gap: var(--space-2); align-items: flex-start; cursor: pointer;
                   color: var(--accent-text); font-size: var(--text-sm); }
.tm-rename input { margin: 0; flex: none; accent-color: var(--accent); }

.modal-actions { margin-top: var(--space-5); display: flex; gap: var(--space-2); align-items: center; }
/* Edit only opens the form — green is the commit color (Add, Save), so it would
   read as "this does something" when it does not. */
button.tinted { background: var(--accent-tint); color: var(--accent-text); border: none; cursor: pointer;
                font-family: inherit; font-size: var(--text-body); font-weight: var(--weight-semibold);
                padding: var(--space-2) var(--space-4); border-radius: var(--radius-control); }
button.tinted:hover { background: var(--accent-tint-strong); }
/* Delete keeps red — with one Delete on screen instead of a column of them, red is
   the warning it is meant to be rather than the table's dominant color. */
button.danger { background: var(--danger-tint); color: var(--danger); border: none; cursor: pointer;
                font-family: inherit; font-size: var(--text-body); font-weight: var(--weight-semibold);
                padding: var(--space-2) var(--space-4); border-radius: var(--radius-control); }
button.danger:hover { background: var(--danger); color: var(--on-accent); }
#tm-msg:empty { display: none; }

/* Tags: chips with the tag icon, in the detail view. */
.tm-tags { display: flex; flex-wrap: wrap; gap: var(--space-1-5); }
.tag-chip { display: inline-flex; align-items: center; gap: var(--space-1); background: var(--bg-hover);
            border-radius: var(--radius-pill); padding: var(--space-0-5) var(--space-2-5);
            font-size: var(--text-xs); color: var(--label-secondary); font-weight: var(--weight-medium); }
.tag-chip .icon { width: 12px; height: 12px; }
/* A forwarded-email row: the statement's wording, and the email on request. */
.tm-origin { margin-top: var(--space-3); padding-top: var(--space-3); border-top: 1px solid var(--separator-soft); }
.tm-origin .tm-meta { margin-top: 0; }
.tm-email { margin-top: var(--space-2); }
.tm-email summary { cursor: pointer; color: var(--accent-text); font-size: var(--text-sm);
                    font-weight: var(--weight-semibold); }
.tm-email .why.pre { background: var(--bg-subtle); border-radius: var(--radius-control);
                     padding: var(--space-2) var(--space-3); margin-top: var(--space-1); }

/* The row the drawer shows. */
.txn-table tr.txn-row.selected td { background: var(--accent-tint); }

/* ---------- The drawer: the same dialog, docked right on a wide screen ----------
   Not modal: the scrim takes no clicks and paints nothing, so the list stays usable
   and the next row is one click away. js/txn-dialog.js syncTxnPresentation() sets
   .as-drawer from SIDEBAR_MQ, the same breakpoint as the sidebar. */
.modal-scrim.as-drawer { background: none; pointer-events: none; padding: 0;
                         justify-content: flex-end; align-items: stretch; z-index: 30; }
.modal-scrim.as-drawer .modal { pointer-events: auto; max-width: var(--drawer-w); max-height: none; height: 100%;
                                border-radius: 0; box-shadow: var(--shadow-drawer-right);
                                border-left: 1px solid var(--separator-section); }
/* While it is open the page makes room for it rather than sliding under it, so the
   Amount column and the Summary panel stay readable. */
body.drawer-open:not(.login-page) { padding-right: var(--drawer-w); }
/* PLAN.md §3.4: the Summary panel is for when no row is selected; the drawer takes
   its place, and the list gets the width back. */
body.drawer-open .tx-side { display: none; }
body.drawer-open .tx-layout { grid-template-columns: minmax(0, 1fr); }
#tm-msg { margin-top: var(--space-3); }

/* Room for the add form's fields two or three to a row. */
.modal.modal-wide { max-width: 560px; }

@media (max-width: 640px) {
  .modal-scrim { align-items: flex-end; padding: 0; }
  /* On a phone the button is its + alone; the label stays in the page for screen
     readers, only visually hidden. */
  .add-btn { padding: var(--space-2); }
  .add-btn .add-label { position: absolute; width: 1px; height: 1px; overflow: hidden;
                        clip: rect(0 0 0 0); white-space: nowrap; }
  .modal { max-width: none; max-height: 92vh; border-radius: var(--radius-banner) var(--radius-banner) 0 0;
           padding: var(--space-4) var(--space-4) var(--space-6); }
}

/* ============================================================================
   Login page — scoped under body.login-page (it styles bare label/input/button).
   ========================================================================== */
body.login-page { min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: var(--space-4); }
body.login-page .card { margin-bottom: 0; border-radius: var(--radius-banner); padding: var(--space-7) var(--space-6);
                        width: 100%; max-width: 340px; box-shadow: var(--shadow-login); }
body.login-page h1 { font-size: var(--text-xl); color: var(--accent-text); margin-bottom: var(--space-1); }
body.login-page .sub { font-size: var(--text-sm); color: var(--label-tertiary); margin-bottom: var(--space-5); }
body.login-page label { display: block; color: var(--label-secondary); margin: var(--space-3) 0 var(--space-1-5); }
body.login-page input { width: 100%; padding: var(--space-3); font-size: var(--text-md); font-family: inherit;
                        background: var(--bg-field); color: var(--label-primary);
                        border: 1px solid transparent; border-radius: var(--radius-control); }
body.login-page input:focus { outline: none; border-color: var(--accent); box-shadow: var(--focus-ring-lg); }
body.login-page button { width: 100%; margin-top: var(--space-5); padding: var(--space-3); border: none;
                         border-radius: var(--radius-control); background: var(--accent); color: var(--on-accent);
                         font-size: var(--text-md); font-weight: var(--weight-semibold); font-family: inherit; cursor: pointer; }
body.login-page button:hover { background: var(--accent-pressed); }
body.login-page button:active { transform: scale(.97); }
body.login-page button:disabled { opacity: .6; cursor: default; }
body.login-page .error { color: var(--danger); font-size: var(--text-sm); margin-top: var(--space-3); min-height: 18px; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    transition-duration: .01ms !important;
    animation-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ---------- Budget page (PLAN.md §3.7) ----------
   A row is icon · name · bar · Budget · Actual · Remaining on one line; on a phone
   the bar and the three figures drop under the name, each figure with its caption.
   The bar is what was spent and the tick is the usual month. Spending stays amber;
   over budget is the warning amber and says "over" — never red. */
body[data-view="budget"] .container { max-width: 1240px; }
.bg-grid { display: grid; grid-template-columns: minmax(0, 1fr); column-gap: var(--space-3);
           align-items: start; }
@media (min-width: 1100px) {
  .bg-grid { grid-template-columns: minmax(0, 1fr) 320px; }
}
.bg-row { display: grid; align-items: center; column-gap: var(--space-3);
          /* Sized from the body text, not in em: the heading row's type is smaller,
             and em columns put its labels to the left of the figures under them. */
          grid-template-columns: 32px minmax(0, 1.5fr) minmax(60px, 1fr)
            calc(7 * var(--text-body)) calc(7 * var(--text-body)) calc(9.5 * var(--text-body));
          grid-template-areas: "icon name bar budget actual rem";
          padding: var(--space-2) var(--space-1-5); border-bottom: 1px solid var(--separator-soft); }
.bg-row > .rv-badge { grid-area: icon; }
.bg-name { grid-area: name; min-width: 0; }
.bg-bar { grid-area: bar; margin-top: 0; }
.bg-num[data-k="Budget"] { grid-area: budget; }
.bg-num[data-k="Actual"] { grid-area: actual; }
.bg-num[data-k^="Remaining"] { grid-area: rem; }
.bg-num { text-align: right; font-variant-numeric: tabular-nums; font-size: var(--text-body); }
.bg-num.over { color: var(--expense-text); font-weight: var(--weight-semibold); }
.bg-num.income { color: var(--income-text); font-weight: var(--weight-semibold); }
.bg-head, .bg-group { grid-template-areas: "name name bar budget actual rem"; }
.bg-head { padding-top: 0; font-size: var(--eyebrow-size); font-weight: var(--eyebrow-weight);
           color: var(--label-tertiary); border-bottom-color: var(--separator-section); }
.bg-head .bg-num { font-size: inherit; }
/* A row's budget sits inside a field; the label and the group total line up with its text. */
.bg-head .bg-num[data-k="Budget"], .bg-group .bg-num[data-k="Budget"] {
  padding-right: calc(var(--space-2) + 1px); }
.bg-group { background: var(--bg-subtle); border-radius: var(--radius-sm); border-bottom: none;
            margin-top: var(--space-2); font-weight: var(--weight-semibold); }
.bg-link { color: var(--label-primary); font-weight: var(--weight-medium); text-decoration: none; }
.bg-link:hover { text-decoration: underline; }
.bg-link:focus-visible { outline: none; box-shadow: var(--focus-ring); border-radius: var(--radius-sm); }
.bg-name .bill-status { margin-left: var(--space-2); }
.bg-name small { display: block; color: var(--label-quaternary); font-size: var(--text-xs); }
.bg-num { overflow-wrap: anywhere; }
.bg-bar .bar-track, #bg-total-track, #db-track { height: 6px; }
#bg-total-track, #db-track { display: block; margin: var(--space-2) 0 var(--space-3); }
.bar-track.over i { background: var(--warning-strong); }
.bg-set, .bg-input { font-family: inherit; font-size: var(--text-body); font-variant-numeric: tabular-nums;
                     text-align: right; width: 100%; padding: var(--space-1-5) var(--space-2);
                     background: var(--bg-field); color: var(--label-primary);
                     border: 1px solid transparent; border-radius: var(--radius-control); }
.bg-set { cursor: pointer; }
.bg-set:hover { background: var(--bg-hover-strong); }
.bg-set.unset { color: var(--accent-text); font-weight: var(--weight-semibold); }
.bg-set:focus-visible, .bg-input:focus { outline: none; border-color: var(--accent);
                                         box-shadow: var(--focus-ring); }
#bg-msg:empty, #cg-msg:empty { display: none; }
#bg-msg, #cg-msg { margin-bottom: var(--space-2); }
#bg-split .kv dd, #bg-split dd { font-size: var(--text-sm); }
@media (max-width: 760px) {
  .bg-row { grid-template-columns: 32px repeat(3, minmax(0, 1fr)); row-gap: var(--space-1-5);
            grid-template-areas: "icon name name name" "icon bar bar bar" "icon budget actual rem"; }
  .bg-group { grid-template-areas: "name name name name" ". budget actual rem"; }
  .bg-head { display: none; }
  .bg-num { text-align: left; white-space: normal; }
  .bg-num::before { content: attr(data-k); display: block; font-size: var(--text-3xs);
                    font-weight: var(--weight-semibold);
                    color: var(--label-quaternary); }
  .bg-set, .bg-input { text-align: left; }
}

/* Settings › Categories & groups: a select per label, the look of a filter. */
.cg-table th { text-align: left; }
.cg-table td { padding: var(--space-1-5) var(--space-2) var(--space-1-5) 0; vertical-align: middle; }
.cg-name { white-space: nowrap; font-weight: var(--weight-medium); }
.cg-name .icon { width: 16px; height: 16px; vertical-align: -3px; color: var(--label-secondary); }
.cg-table select { max-width: 100%; padding: var(--space-1-5) var(--space-2); font-size: var(--text-sm);
                   font-family: inherit; background: var(--bg-field); color: var(--label-primary);
                   border: 1px solid transparent; border-radius: var(--radius-control); }
.cg-table select:focus { outline: none; border-color: var(--accent); box-shadow: var(--focus-ring); }

/* ---------- Accounts page (PLAN.md §3.3) ----------
   A row is name · a small line of its month-ends · balance · Update; on a phone the
   line and the figure drop under the name. Balances are typed by hand, so every row
   says how old its figure is. A debt is not red: red is for errors and Delete. */
body[data-view="accounts"] .container { max-width: 1240px; }
.ac-row { display: grid; align-items: center; column-gap: var(--space-3);
          grid-template-columns: minmax(0, 1fr) 80px 9em 4.5em;
          grid-template-areas: "name spark amt act";
          padding: var(--space-2) var(--space-1-5); border-bottom: 1px solid var(--separator-soft); }
.ac-name { grid-area: name; min-width: 0; }
.ac-name small { display: block; color: var(--label-quaternary); font-size: var(--text-xs);
                 overflow-wrap: anywhere; }
.ac-name .bill-status { margin-left: var(--space-2); }
.ac-spark { grid-area: spark; width: 80px; height: 24px; display: block; }
.ac-spark polyline { fill: none; stroke: var(--accent); stroke-width: 1.5;
                     stroke-linejoin: round; stroke-linecap: round;
                     vector-effect: non-scaling-stroke; }
.ac-amt { grid-area: amt; text-align: right; font-variant-numeric: tabular-nums;
          font-size: var(--text-body); font-weight: var(--weight-semibold); overflow-wrap: anywhere; }
.ac-act { grid-area: act; text-align: right; }
.ac-group { grid-template-areas: "name name amt act"; background: var(--bg-subtle);
            border-radius: var(--radius-sm); border-bottom: none; margin-top: var(--space-2); }
.ac-group .ac-name small { display: inline; margin-left: var(--space-2); }
.ac-open { border: none; background: none; padding: 0; cursor: pointer; font-family: inherit;
           font-size: var(--text-body); font-weight: var(--weight-medium); color: var(--label-primary);
           text-align: left; }
.ac-open:hover { text-decoration: underline; }
.ac-open:focus-visible { outline: none; box-shadow: var(--focus-ring); border-radius: var(--radius-sm); }
#ac-msg:empty, #ad-msg:empty, #af-msg:empty { display: none; }
#ac-msg { margin-bottom: var(--space-2); }
#ad-msg { margin-top: var(--space-2); }
.ac-sum-head { display: flex; justify-content: space-between; align-items: baseline;
               gap: var(--space-3); margin: var(--space-4) 0 var(--space-2);
               padding-bottom: var(--space-1-5); border-bottom: 1px solid var(--separator-section);
               font-weight: var(--weight-semibold); }
.ac-sum-head:first-of-type { margin-top: 0; }
.ac-sum-head b { font-variant-numeric: tabular-nums; }
.ab-go { display: flex; align-items: flex-end; }
.ad-history { max-height: 260px; overflow-y: auto; }
.ad-h { font-size: var(--eyebrow-size); font-weight: var(--eyebrow-weight); color: var(--label-tertiary);
        margin: var(--space-5) 0 var(--space-1); }
.nw-row { align-items: center; justify-content: space-between; flex-wrap: nowrap; }
.nw-spark { flex: 0 1 120px; min-width: 60px; }
.nw-spark .ac-spark { width: 100%; height: 36px; }
@media (max-width: 760px) {
  .ac-row { grid-template-columns: minmax(0, 1fr) auto auto; row-gap: var(--space-1);
            grid-template-areas: "name name name" "spark amt act"; }
  .ac-group { grid-template-areas: "name amt act"; }
  .ac-spark { justify-self: start; }
}

/* ---------- Goals page (PLAN.md §3.9) ----------
   One card per goal: icon · name · status, the amount of its target, a bar with a
   mark where the straight line is today, and one plain line. The bar is the accent;
   reached is the success green; behind is the warning amber and says "Behind",
   never red. */
body[data-view="goals"] .container { max-width: 1240px; }
.goal-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
              gap: 0 var(--space-3); }
.goal-card { min-width: 0; }
.goal-head { display: flex; align-items: center; gap: var(--space-2); margin-bottom: var(--space-2); }
.goal-head .goal-name { flex: 1; min-width: 0; overflow-wrap: anywhere; font-weight: var(--weight-semibold); }
.goal-amt { font-variant-numeric: tabular-nums; color: var(--label-tertiary); font-size: var(--text-sm);
            overflow-wrap: anywhere; }
.goal-amt b { color: var(--label-primary); font-size: var(--text-body); font-weight: var(--weight-semibold); }
.bar-track.goal { height: 6px; }
.bar-track.goal i { background: var(--accent); }
.bar-track.goal.reached i { background: var(--income-fill); }
.bar-track.goal.behind i { background: var(--warning-strong); }
.goal-line { margin: var(--space-2) 0 0; font-size: var(--text-sm); color: var(--label-secondary); }
.goal-follows { margin: var(--space-1) 0 0; font-size: var(--text-xs); color: var(--label-quaternary); }
.goal-card .link-btn { margin-top: var(--space-2); padding: 0; }
#gl-msg:empty, #gd-msg:empty, #gf-msg:empty { display: none; }
#gd-msg { margin-top: var(--space-2); }
#gd-line { margin: 0 0 var(--space-3); }
.goal-minis { display: grid; gap: var(--space-3); }
.goal-mini { display: block; color: inherit; text-decoration: none; border-radius: var(--radius-sm); }
.goal-mini:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.goal-mini-top { display: flex; justify-content: space-between; align-items: baseline; gap: var(--space-3); }
.goal-mini-top b { font-weight: var(--weight-medium); min-width: 0; overflow: hidden; text-overflow: ellipsis;
                   white-space: nowrap; }
.goal-mini-top span { font-variant-numeric: tabular-nums; font-size: var(--text-sm); color: var(--label-tertiary);
                      white-space: nowrap; }
.goal-mini small { display: block; margin-top: var(--space-1); font-size: var(--text-xs);
                   color: var(--label-quaternary); }
