@import "../styles/globals.css"; /* The @font-face rules are deliberately NOT here. Bun's CSS bundler resolves every url() from disk and inlines woff2 as base64 — relative OR absolute, and `loader: { ".woff2": "file" }` does not change it. That made this file 303 KB of render-blocking bytes and defeated the unicode-range subsetting, since a subset embedded in the stylesheet downloads whether or not one character needs it. They live in src/fontCss.ts and are injected at boot by src/frontend.tsx, which is the only arrangement that is identical in dev and in a subpath production build without a placeholder file. The cost is that the faces are requested after the JS parses rather than from the HTML; `font-display: swap` keeps text readable throughout. */ /* * ============================================================================ * Budget 2.0 design foundation * ============================================================================ * * Two layers, deliberately separate: * * 1. `--budget-*` semantic tokens, defined once per theme below. These are * the app's own vocabulary — paper, ink, rule, surplus, overspend — and * are the ONLY colours the app's own components should name. * 2. shadcn's variable set, pointed at layer 1. Re-theming shadcn rather * than fighting it means every primitive (dialog, select, badge, button) * inherits both themes for free. * * WHY THE PAIRS MATTER. AGENTS.md's rule is "never define a bare `--accent`", * and it was paid for with an app-wide contrast bug: shadcn uses `--accent` as * a hover BACKGROUND and `--accent-foreground` as the text on it, so setting * one without the other repaints the background and leaves the text behind. * Every shadcn pair below is therefore redefined TOGETHER, in both themes. * * The brand teal is `--primary`, NOT `--accent`. That is the mapping the * earlier bug got wrong: shadcn's `--accent` is a subtle hover tint, and a * saturated teal there turns every hover into a coloured slab with dark text * on it. `--accent` stays a tint of the surface. * * Every text pair here was measured, not eyeballed: the weakest is 5.50:1 * (muted on paper, light theme) against a 4.5:1 requirement, and field * borders clear the 3:1 non-text requirement on both of their backgrounds. */ :root { /* --- Layer 1: the app's own vocabulary (light) --- */ --budget-paper: #f3f5f7; /* page ground: cool statement paper, NOT cream */ --budget-raised: #ffffff; /* cards and rows sitting above the paper */ --budget-ink: #16212b; /* primary text: ink navy, not black */ --budget-ink-muted: #546575; /* labels, captions, secondary figures */ --budget-rule: #dce3e9; /* hairlines between rows */ --budget-rule-strong: #768899; /* field borders; needs 3:1, hairlines do not */ --budget-accent: #0d6b60; /* muted teal: links, primary actions, spend fill */ --budget-accent-ink: #ffffff; /* text ON the teal */ --budget-tint: #e8edf1; /* hover wash, table header fill */ --surplus: #1f6640; /* carried-forward surplus */ --overspend: #a5382a; /* negative balances only */ /* The chevron a native