- Sticky labels: each bar's title slides down inside its rect under the sticky timeline so it stays visible while any part of the rect is in view, then slides off when the rect's bottom passes. - Auto-wrap long bar names into multiple <tspan> lines so labels no longer overrun their box. - Drop browser-native <title> tooltips; the custom tooltip remains. - Demote depth-1 nameSize to depth-2 size when a bar shows its name but not its euro amount, so it doesn't outshout column-2 titles. - Collapse the depth-4 column when the zoomed-in scope has no Breakdown items; surviving columns expand to fill the canvas. - Tighter year-budget centering, runtime gap (GAP_Y) instead of d3 partition padding, gutter masks for year-frame strokes. - Two-hue OKLCH palette tuned so all labels can stay a single colour per flow (light on Aufwendungen, dark on Erträge). - Sidebar: clickable Aufw/Ertr/Saldo to swap flow; Beschreibung + collapsed Erläuterungen extracted from PG sections. - Sticky timeline axis with active-year tick in the active flow's colour; URL state sync for path/year/flow. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
202 lines
7.1 KiB
CSS
202 lines
7.1 KiB
CSS
/* ──────────────────────────────────────────────────────────────────────────
|
||
ms-haushalt · global tokens, reset, and base typography.
|
||
See .impeccable.md for the design context this expresses.
|
||
────────────────────────────────────────────────────────────────────────── */
|
||
|
||
@import "@fontsource-variable/geist";
|
||
@import "@fontsource-variable/geist-mono";
|
||
|
||
/* Figtree Variable — display + body face. SIL OFL, served from
|
||
public/fonts. Roman + italic axes, weight 300–900. */
|
||
@font-face {
|
||
font-family: "Figtree Variable";
|
||
src: url("/fonts/Figtree-VariableFont_wght.woff2") format("woff2-variations");
|
||
font-weight: 300 900;
|
||
font-style: normal;
|
||
font-display: swap;
|
||
}
|
||
@font-face {
|
||
font-family: "Figtree Variable";
|
||
src: url("/fonts/Figtree-Italic-VariableFont_wght.woff2") format("woff2-variations");
|
||
font-weight: 300 900;
|
||
font-style: italic;
|
||
font-display: swap;
|
||
}
|
||
|
||
:root {
|
||
/* ── Color (OKLCH, paper-feeling, two-hue flow palette) ───────────── */
|
||
/* Background: warm uncoated stock, never pure white. Tinted toward the
|
||
Aufwendungen hue family by 0.005-0.01 chroma per the impeccable
|
||
"tint neutrals toward brand hue" principle. */
|
||
--paper: oklch(98.4% 0.006 60);
|
||
--paper-deep: oklch(96.0% 0.008 60);
|
||
|
||
/* Ink: warmed near-black. Same principle inverted. */
|
||
--ink: oklch(20% 0.02 50);
|
||
--ink-soft: oklch(38% 0.02 55);
|
||
--ink-mute: oklch(58% 0.015 60);
|
||
|
||
/* Hairlines & faint surfaces. */
|
||
--rule: oklch(86% 0.012 60);
|
||
--rule-soft: oklch(92% 0.008 60);
|
||
|
||
/* Two-hue flow palette per design brief §9.5: deep purple for
|
||
Aufwendungen (money leaving), orange for Erträge (money coming in).
|
||
The two ranges sit on opposite sides of the label-contrast
|
||
threshold so all labels on each flow's icicle can be a single
|
||
colour: light text on the dark purples, dark text on the light
|
||
oranges. Mirrors the JS PALETTE in src/lib/colors.ts. */
|
||
--flow-aufwand-h: 295; /* deep plum-purple */
|
||
--flow-aufwand-c: 0.19;
|
||
--flow-ertrag-h: 55; /* warm orange / amber */
|
||
--flow-ertrag-c: 0.15;
|
||
|
||
/* Solid ink-on-paper renders of each flow accent (used for active
|
||
toggle state, breadcrumb separators, slider handle, etc.). The
|
||
accent uses Ldark / Llight values that match the bar palette's
|
||
darkest-purple and lightest-readable-orange respectively. */
|
||
--flow-aufwand: oklch(34% var(--flow-aufwand-c) var(--flow-aufwand-h));
|
||
--flow-ertrag: oklch(68% var(--flow-ertrag-c) var(--flow-ertrag-h));
|
||
|
||
/* ── Spacing (4pt scale, semantic names) ──────────────────────────── */
|
||
--space-2xs: 4px;
|
||
--space-xs: 8px;
|
||
--space-sm: 12px;
|
||
--space-md: 16px;
|
||
--space-lg: 24px;
|
||
--space-xl: 32px;
|
||
--space-2xl: 48px;
|
||
--space-3xl: 64px;
|
||
--space-4xl: 96px;
|
||
|
||
/* ── Type scale ───────────────────────────────────────────────────── */
|
||
/* Display sizes are fluid (editorial, not app-chrome), per the
|
||
typography rule. Body and UI sizes are fixed rem. The display
|
||
range is intentionally tighter than the previous version — the
|
||
hero needs to stay above the fold on a typical 13" laptop. */
|
||
--size-display-1: clamp(2.625rem, 5.5vw + 1rem, 5.25rem); /* hero stack */
|
||
--size-display-2: clamp(1.75rem, 2.5vw + 0.5rem, 2.5rem); /* sidebar heading */
|
||
--size-display-3: 1.375rem; /* secondary */
|
||
|
||
--size-body: 1rem; /* 16px */
|
||
--size-body-lg: 1.0625rem;/* 17px lede */
|
||
--size-caption: 0.8125rem;/* 13px */
|
||
--size-eyebrow: 0.6875rem;/* 11px small caps */
|
||
--size-micro: 0.75rem; /* 12px sidebar dt labels */
|
||
|
||
/* ── Faces ────────────────────────────────────────────────────────── */
|
||
/* Display + body: Figtree (variable, SIL OFL, served from
|
||
public/fonts). Numbers: Geist Mono. The `--face-serif` /
|
||
`--face-sans` token names are legacy aliases — both now point to
|
||
Figtree since it carries display and body alike. */
|
||
--face-display: "Figtree Variable", -apple-system, BlinkMacSystemFont,
|
||
"Segoe UI", system-ui, sans-serif;
|
||
--face-serif: var(--face-display);
|
||
--face-sans: var(--face-display);
|
||
--face-mono: "Geist Mono Variable", "Geist Variable", ui-monospace,
|
||
"SF Mono", "Menlo", monospace;
|
||
|
||
/* ── Layout rhythm ────────────────────────────────────────────────── */
|
||
--measure: 65ch;
|
||
--page-max: 1280px;
|
||
--page-pad-x: clamp(1rem, 4vw, 3rem);
|
||
}
|
||
|
||
/* ── Reset ──────────────────────────────────────────────────────────── */
|
||
*,
|
||
*::before,
|
||
*::after {
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
html {
|
||
-webkit-text-size-adjust: 100%;
|
||
text-size-adjust: 100%;
|
||
font-feature-settings:
|
||
"kern" 1,
|
||
"liga" 1,
|
||
"calt" 1,
|
||
"ss01" 1;
|
||
/* German-specific OpenType: lining figures for tabular data, oldstyle
|
||
elsewhere. Enabled per-element where useful. */
|
||
}
|
||
|
||
body {
|
||
margin: 0;
|
||
background: var(--paper);
|
||
color: var(--ink);
|
||
font-family: var(--face-serif);
|
||
font-size: var(--size-body);
|
||
line-height: 1.55;
|
||
font-feature-settings:
|
||
"kern" 1,
|
||
"liga" 1,
|
||
"calt" 1;
|
||
/* Slight optical correction for warmly-tinted paper background. */
|
||
-webkit-font-smoothing: antialiased;
|
||
-moz-osx-font-smoothing: grayscale;
|
||
}
|
||
|
||
p {
|
||
margin: 0 0 var(--space-md);
|
||
max-width: var(--measure);
|
||
}
|
||
|
||
h1, h2, h3, h4 {
|
||
margin: 0;
|
||
font-family: var(--face-serif);
|
||
font-weight: 800;
|
||
line-height: 0.95;
|
||
letter-spacing: -0.01em;
|
||
font-variant-ligatures: discretionary-ligatures;
|
||
}
|
||
|
||
a {
|
||
color: inherit;
|
||
text-decoration-thickness: 1px;
|
||
text-underline-offset: 0.18em;
|
||
}
|
||
|
||
button {
|
||
font: inherit;
|
||
color: inherit;
|
||
background: none;
|
||
border: 0;
|
||
padding: 0;
|
||
cursor: pointer;
|
||
}
|
||
|
||
/* ── Utility patterns we use repeatedly ─────────────────────────────── */
|
||
|
||
.eyebrow {
|
||
font-family: var(--face-sans);
|
||
font-size: var(--size-eyebrow);
|
||
font-weight: 500;
|
||
letter-spacing: 0.14em;
|
||
text-transform: uppercase;
|
||
color: var(--ink-mute);
|
||
}
|
||
|
||
.caption {
|
||
font-family: var(--face-sans);
|
||
font-size: var(--size-caption);
|
||
color: var(--ink-soft);
|
||
line-height: 1.4;
|
||
}
|
||
|
||
.tabular {
|
||
font-family: var(--face-mono);
|
||
font-variant-numeric: tabular-nums lining-nums;
|
||
font-feature-settings: "tnum" 1, "lnum" 1;
|
||
letter-spacing: -0.01em;
|
||
}
|
||
|
||
@media (prefers-reduced-motion: reduce) {
|
||
*,
|
||
*::before,
|
||
*::after {
|
||
animation-duration: 0.001ms !important;
|
||
transition-duration: 0.001ms !important;
|
||
}
|
||
}
|