Something went wrong. Try again.
A self-hosted household ledger.
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303/* Quantum design tokens — see DESIGN.md. All colors OKLCH, warm ink on warm paper. */
:root { /* neutrals */ --bg: oklch(0.975 0.004 85); --bg-raised: oklch(0.99 0.003 85); --bg-sunken: oklch(0.955 0.005 85); --text: oklch(0.245 0.012 85); --text-muted: oklch(0.49 0.012 85); --text-faint: oklch(0.62 0.01 85); --border: oklch(0.9 0.006 85); --border-strong: oklch(0.82 0.008 85);
/* accent — spruce */ --accent: oklch(0.46 0.07 190); --accent-hover: oklch(0.4 0.075 190); --accent-bg: oklch(0.94 0.02 190); --on-accent: oklch(0.98 0.005 190);
/* semantic */ --pos: oklch(0.52 0.1 175); --neg: oklch(0.53 0.13 30); --warn: oklch(0.6 0.11 75); --warn-bg: oklch(0.95 0.03 85);
/* charts (line color validated: chroma >= 0.1 against light surface) */ --chart-1: oklch(0.55 0.13 180); --chart-2: oklch(0.65 0.11 75); --chart-3: oklch(0.55 0.1 300); --chart-4: oklch(0.68 0.1 130); --chart-5: oklch(0.58 0.11 25); --chart-6: oklch(0.62 0.08 250); --chart-7: oklch(0.7 0.09 55);
--shadow-overlay: 0 4px 16px oklch(0 0 0 / 0.1);
/* per-category palette (poline-generated, validated for BOTH surfaces — single set, no dark override; regenerate: deno run -A scripts/generate-category-palette.ts) */ --cat-0: oklch(0.54 0.14 16); --cat-1: oklch(0.58 0.1 218); --cat-2: oklch(0.62 0.14 61); --cat-3: oklch(0.54 0.12 240); --cat-4: oklch(0.58 0.115 83); --cat-5: oklch(0.62 0.14 275); --cat-6: oklch(0.54 0.11 104); --cat-7: oklch(0.58 0.14 308); --cat-8: oklch(0.62 0.14 143); --cat-9: oklch(0.54 0.14 328); --cat-10: oklch(0.6 0.1 191); --cat-11: oklch(0.62 0.14 354);
/* typography */ --font-ui: 'Inter Variable', system-ui, -apple-system, 'Segoe UI', sans-serif; --font-mono: ui-monospace, 'Cascadia Mono', Consolas, monospace; --text-xs: 0.8125rem; --text-sm: 0.875rem; --text-md: 1rem; --text-lg: 1.25rem; --text-xl: 1.5625rem; --text-2xl: 1.9375rem;
/* spacing (4px base) */ --space-1: 4px; --space-2: 8px; --space-3: 12px; --space-4: 16px; --space-5: 24px; --space-6: 32px; --space-7: 48px; --space-8: 64px;
/* shape */ --radius-sm: 5px; --radius-md: 8px;
/* motion */ --ease-out: cubic-bezier(0.22, 1, 0.36, 1); --dur-micro: 120ms; --dur-panel: 180ms;
color-scheme: light;}
:root[data-theme='dark'] { --bg: oklch(0.19 0.006 85); --bg-raised: oklch(0.225 0.007 85); --bg-sunken: oklch(0.165 0.006 85); --text: oklch(0.92 0.008 85); --text-muted: oklch(0.7 0.01 85); --text-faint: oklch(0.55 0.01 85); --border: oklch(0.3 0.008 85); --border-strong: oklch(0.38 0.01 85);
--accent: oklch(0.8 0.08 185); --accent-hover: oklch(0.85 0.075 185); --accent-bg: oklch(0.27 0.03 190); --on-accent: oklch(0.2 0.02 190);
--pos: oklch(0.78 0.11 170); --neg: oklch(0.74 0.13 30); --warn: oklch(0.8 0.11 80); --warn-bg: oklch(0.26 0.03 80);
--chart-1: oklch(0.65 0.12 190); --chart-2: oklch(0.75 0.11 75); --chart-3: oklch(0.65 0.1 300); --chart-4: oklch(0.78 0.1 130); --chart-5: oklch(0.68 0.11 25); --chart-6: oklch(0.72 0.08 250); --chart-7: oklch(0.8 0.09 55);
--shadow-overlay: 0 4px 16px oklch(0 0 0 / 0.35);
color-scheme: dark;}
@media (prefers-color-scheme: dark) { :root:not([data-theme='light']) { --bg: oklch(0.19 0.006 85); --bg-raised: oklch(0.225 0.007 85); --bg-sunken: oklch(0.165 0.006 85); --text: oklch(0.92 0.008 85); --text-muted: oklch(0.7 0.01 85); --text-faint: oklch(0.55 0.01 85); --border: oklch(0.3 0.008 85); --border-strong: oklch(0.38 0.01 85);
--accent: oklch(0.8 0.08 185); --accent-hover: oklch(0.85 0.075 185); --accent-bg: oklch(0.27 0.03 190); --on-accent: oklch(0.2 0.02 190);
--pos: oklch(0.78 0.11 170); --neg: oklch(0.74 0.13 30); --warn: oklch(0.8 0.11 80); --warn-bg: oklch(0.26 0.03 80);
--chart-1: oklch(0.65 0.12 190); --chart-2: oklch(0.75 0.11 75); --chart-3: oklch(0.65 0.1 300); --chart-4: oklch(0.78 0.1 130); --chart-5: oklch(0.68 0.11 25); --chart-6: oklch(0.72 0.08 250); --chart-7: oklch(0.8 0.09 55);
--shadow-overlay: 0 4px 16px oklch(0 0 0 / 0.35);
color-scheme: dark; }}
/* base */
*,*::before,*::after { box-sizing: border-box;}
html,body { margin: 0; padding: 0; height: 100%;}
body { background: var(--bg); color: var(--text); font-family: var(--font-ui); font-size: var(--text-sm); line-height: 1.5; -webkit-font-smoothing: antialiased;}
h1,h2,h3 { line-height: 1.2; font-weight: 600; margin: 0;}
h1 { font-size: var(--text-xl);}
h2 { font-size: var(--text-lg);}
h3 { font-size: var(--text-md);}
p { margin: 0; max-width: 70ch;}
a { color: var(--accent); text-decoration: none;}
a:hover { text-decoration: underline;}
code { font-family: var(--font-mono); font-size: 0.9em;}
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px;}
/* form controls */
button,input,select { font: inherit; color: inherit;}
input[type='text'],input[type='password'],input[type='search'],select { background: var(--bg-raised); border: 1px solid var(--border-strong); border-radius: var(--radius-sm); padding: var(--space-2) var(--space-3); /* selects ignore line-height in their native rendering; a shared min-height (text line + padding + border) keeps inputs and selects one height in mixed filter/form rows */ line-height: 1.5; min-height: calc(1.5em + var(--space-2) * 2 + 2px); transition: border-color var(--dur-micro) var(--ease-out);}
input[type='text']:focus,input[type='search']:focus,select:focus { border-color: var(--accent); outline: none;}
input[type='search'] { -webkit-appearance: none; appearance: none;}
button { border-radius: var(--radius-sm); border: 1px solid var(--border-strong); background: var(--bg-raised); padding: var(--space-2) var(--space-4); font-weight: 500; cursor: pointer; transition: background var(--dur-micro) var(--ease-out), border-color var(--dur-micro) var(--ease-out);}
button:hover { background: var(--bg-sunken);}
button.primary { background: var(--accent); border-color: var(--accent); color: var(--on-accent);}
button.primary:hover { background: var(--accent-hover); border-color: var(--accent-hover);}
button:disabled { opacity: 0.5; cursor: default;}
/* numerals */
.tnum { font-variant-numeric: tabular-nums;}
@media (prefers-reduced-motion: reduce) { *, *::before, *::after { transition-duration: 0ms !important; animation-duration: 0ms !important; }}