/* Corporate Design „Nest", Hellmodus.
   Die drei Neutralen kommen wörtlich aus der CD (#DCEFE7, #E8F7F1, #F2FFFB),
   ebenso das Mint. Alles Übrige ist daraus abgeleitet und gemessen — siehe
   docs/corporate-design.md. */
:root {
  --bg: #E8F7F1;
  --surface: #F2FFFB;
  --surface-muted: #DCEFE7;
  --text: #12242E;
  --text-muted: #566e70;
  /* Pfeil der Auswahlfelder. Als Token, damit der Dunkelmodus ihn tauschen
     kann — `currentColor` funktioniert in `background-image` nicht. */
  /* Zweiter Pfeil im Akzent: er markiert ein Feld, aus dem man WÄHLT.
     --select-arrow bleibt der stille für alles Übrige. */
  --select-arrow-accent: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2212%22%20height%3D%228%22%20viewBox%3D%220%200%2012%208%22%3E%3Cpath%20d%3D%22M1%201.5%206%206.5l5-5%22%20fill%3D%22none%22%20stroke%3D%22%23009979%22%20stroke-width%3D%221.8%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%2F%3E%3C%2Fsvg%3E");
  /* Der native Kalenderknopf lässt sich nur über einen Filter einfärben. */
  --calendar-tint: invert(38%) sepia(85%) saturate(1200%) hue-rotate(135deg) brightness(92%) contrast(101%);
  --select-arrow: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2212%22%20height%3D%228%22%20viewBox%3D%220%200%2012%208%22%3E%3Cpath%20d%3D%22M1%201.5%206%206.5l5-5%22%20fill%3D%22none%22%20stroke%3D%22%23566e70%22%20stroke-width%3D%221.8%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%2F%3E%3C%2Fsvg%3E");
  /* --border trennt Flächen (keine Mindestanforderung), --border-control
     umrandet Bedienelemente und muss deshalb 3:1 gegen die Fläche erreichen. */
  --border: #DCEFE7;
  --border-control: #738b8d;
  /* --accent ist eine FLÄCHE, --accent-ink ist SCHRIFT. Das ist im Hellmodus
     kein Feinschliff, sondern zwingend: das CD-Mint hat auf #F2FFFB gerade
     1,5:1 und wäre als Textfarbe unlesbar — auch das dunklere #3FAF95 kommt
     nur auf 2,6:1. --accent-ink ist dieselbe Hue (OKLCH 172,8°), nur so weit
     abgedunkelt, dass 4,5:1 stehen. Im Dunkelmodus fallen beide zusammen. */
  --accent: #5FE3C0;
  --accent-ink: #00795c;
  /* --accent-strong ist dieselbe Hue zwischen Fläche und Tinte: für Marken und
     Ringe, die 3:1 brauchen, aber keine 4,5:1 wie Schrift — Fokusring,
     Fortschrittsbalken. Im Dunkelmodus fällt es mit --accent zusammen. */
  --accent-strong: #009979;
  --accent-hover: #3FAF95;
  --accent-soft: #c2f8e6;
  /* Schriftfarbe auf Akzentflächen. Das Mint ist in BEIDEN Modi hell, dort
     trägt nur dunkle Schrift die geforderten 4,5:1. */
  --on-accent: #12242E;
  --positive: #157a26;
  --negative: #b64340;
  /* Schrift auf --negative. Nicht --on-accent: der ist seit dem CD dunkel und
     stünde auf Rot bei 2,9:1. */
  --on-negative: #ffffff;
  --warning: #9c5a00;
  --tone-positive-surface: #d7f2d7;
  --tone-negative-surface: #ffe6e1;
  --tone-warning-surface: #ffe9cc;
  --tone-neutral-surface: #c7f3f6;
  --person-blue-surface: #d0eaff;
  --person-blue-text: #1467c2;
  --person-green-surface: #d2f2d2;
  --person-green-text: #007b1e;
  --person-amber-surface: #fee3c0;
  --person-amber-text: #9c5600;
  --person-violet-surface: #ede0ff;
  --person-violet-text: #7b50b8;
  --person-rose-surface: #ffdadc;
  --person-rose-text: #b4384d;
  --person-slate-surface: #d2ecfc;
  --person-slate-text: #546b79;
  /* Die Schauflaeche (Begruessungsblock) ist in beiden Modi dunkel und braucht
     deshalb eine eigene Farbfamilie statt sieben Einzelliteralen: zwei
     Flaechentoene, ein Schimmer und drei Abstufungen dessen, was darauf liegt. */
  --display-from: #12242E;
  --display-to: #1e3a45;
  --display-glow: rgb(95 227 192 / 26%);
  --on-display: #F2FFFB;
  --on-display-muted: rgb(242 255 251 / 68%);
  --display-line: rgb(242 255 251 / 16%);
  --display-fill: rgb(242 255 251 / 10%);
  /* Abdunklung hinter Dialogen. */
  --scrim: rgb(9 18 23 / 52%);
  /* Geprüfte Diagrammpalette, feste Reihenfolge. Sie steht hier und nicht als
     Konstante in js/charts.js, damit der Dunkelmodus sie überschreiben kann.
     --chart-other traegt die Sammelkategorie „Sonstige" und ist bewusst
     unbunt — sie steht fuer alles, was keine eigene Farbe mehr bekommt.

     Die Aqua-Familie ist hier BEWUSST NICHT dabei, obwohl sie zur geprüften
     Achterreihe gehört: sie liegt in derselben Hue wie das Marken-Mint, und
     eine Datenreihe in der Farbe des Hauptknopfs gibt sich als Bedienelement
     aus. Von den verbleibenden sieben Familien wurden alle 2.520 Reihenfolgen
     durchgerechnet; diese hat den grössten kleinsten Abstand in beiden Modi
     (Rot-Grün-Schwäche 17,6 hell / 13,0 dunkel bei Ziel 8). */
  --chart-1: #eb6834;
  --chart-2: #2a78d6;
  --chart-3: #eda100;
  --chart-4: #4a3aa7;
  --chart-5: #008300;
  --chart-6: #e87ba4;
  --chart-other: #566e70;
  /* Schattenfamilie, von ruhend bis schwebend. */
  --shadow-sm: 0 1px 2px rgb(20 25 30 / 6%);
  --shadow-raised: 0 1px 4px rgb(0 0 0 / 8%);
  --shadow-hover: 0 8px 24px rgb(25 32 45 / 6%);
  --shadow: 0 10px 34px rgb(25 32 45 / 7%);
  --shadow-float: 0 6px 18px rgb(25 32 45 / 22%);
  /* Radienskala. Drei Stufen plus Pille und Kreis; die fuenf Zwischenwerte
     8/11/13/14/22 sind darin aufgegangen. `--radius` hiess vorher die mittlere
     Kartenstufe und ist zu `--radius-lg` geworden, damit die Namen der Groesse
     folgen. */
  --radius-sm: 8px;    /* Segmente und Reiter in einer Spur, Markenzeichen */
  --radius-md: 12px;   /* Knoepfe, Eingabefelder, Symbolfelder */
  --radius-lg: 20px;   /* Karten und Flaechen */
  --radius-pill: 999px;
  --radius-round: 50%;
  /* Abstandsskala. Alle padding/margin/gap in css/ kommen aus diesen acht
     Stufen; Ausnahmen sind an Ort und Stelle als optische Korrektur vermerkt. */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 24px;
  --space-6: 32px;
  --space-7: 48px;
  --space-8: 64px;
  /* Typografieskala, in rem notiert, damit die Basisschriftgroesse des Browsers
     weiter wirkt. Die kleinste Stufe ist 12px — darunter faellt nichts mehr. */
  --text-xs: .75rem;    /* 12px */
  --text-sm: .875rem;   /* 14px */
  --text-md: 1rem;      /* 16px */
  --text-lg: 1.25rem;   /* 20px */
  --text-xl: 1.625rem;  /* 26px */
  --text-2xl: 2.125rem; /* 34px */
  /* Abweichung von der vorgegebenen Skala, siehe Bericht: die sechs Stufen
     enden bei 34px, die Schauzeile des Begruessungsblocks lag bei 58px. Ohne
     eine Anzeigestufe faellt sie auf Kennzahlenniveau und die Rangfolge der
     Seite bricht zusammen. 48px setzt das Verhaeltnis der Skala (~1,3) fort. */
  --text-3xl: 3rem;     /* 48px */
  /* Drei Gewichte. Die frueheren Zwischenstufen 650/680/720/740/750/760 waren
     nicht darstellbar: ohne @font-face liefert keine Schriftfamilie im Stack
     eine eigene Schnittfleche dafuer, und `font-synthesis: none` verbietet das
     Nachbilden — gemessen rendern 600 bis 800 identisch. */
  --weight-regular: 400;
  --weight-medium: 500;
  --weight-bold: 700;
  /* Drei Zeilenhoehen: eng fuer Schauzeilen, normal fuer Bedienelemente,
     weit fuer Fliesstext. */
  --leading-tight: 1.1;
  --leading-snug: 1.4;
  --leading-relaxed: 1.55;
  /* Drei Laufweiten: weit fuer Versalien, eng und enger fuer Ueberschriften. */
  --tracking-wide: .08em;
  --tracking-tight: -.02em;
  --tracking-tighter: -.04em;
  --sidebar-width: 248px;
  --content-width: 1240px;
  /* Seitenleiste plus Inhaltsblock: die Breite, die auf sehr breiten
     Bildschirmen als Ganzes zentriert wird. */
  --shell-width: calc(var(--content-width) + 64px + var(--sidebar-width));
  /* Kein Inter mehr an erster Stelle: die Familie war ohne @font-face und ohne
     mitgelieferte Datei eingebunden und erschien nur, wenn sie zufaellig auf dem
     Geraet installiert war — die Schrift wechselte also je nach Geraet. Da die
     Datei in diesem Release nicht ausgeliefert werden kann, steht jetzt die
     Systemschrift vorne: sie ist ueberall vorhanden und bringt echte
     Schnittflaechen fuer 400, 500 und 700 mit. */
  font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  color: var(--text);
  background: var(--bg);
  font-synthesis: none;
  text-rendering: optimizeLegibility;
}

* { box-sizing: border-box; }
html { min-width: 320px; background: var(--bg); }
body { min-height: 100vh; margin: 0; background: var(--bg); }
#auth-root[hidden] { display: none; }
.auth-shell { position: fixed; z-index: 200; inset: 0; display: grid; place-items: center; padding: var(--space-5); background: radial-gradient(circle at 85% 10%, color-mix(in srgb, var(--accent) 18%, transparent), transparent 32%), var(--bg); }
.auth-card { width: min(100%, 420px); padding: clamp(var(--space-5), 6vw, var(--space-7)); }
.auth-card h1 { margin-bottom: var(--space-3); }
.auth-copy { margin: 0 0 var(--space-5); color: var(--text-muted); line-height: var(--leading-relaxed); }
.auth-status { min-height: 1.4em; margin: var(--space-4) 0 var(--space-1); color: var(--text-muted); font-size: var(--text-sm); line-height: var(--leading-snug); }
.auth-status.error { color: var(--negative); }
.auth-status.success { color: var(--positive); }
button, input, select { font: inherit; }
button, a { -webkit-tap-highlight-color: transparent; }
button { color: inherit; }
a { color: inherit; text-decoration: none; }
svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 1.8; }

/* Deckend statt durchscheinend: der frühere 35-%-Mix erreichte gegen die
   Seitenfläche nur 1,6:1 und war damit praktisch unsichtbar. */
:focus-visible {
  outline: 2px solid var(--accent-strong);
  outline-offset: 2px;
}

/* Auf akzent- oder negativgefüllten Knöpfen läge ein Akzentring unsichtbar auf
   der eigenen Fläche. Der innere Ring in --on-accent trennt ihn von der Füllung,
   der äußere Ring in --text trägt den Kontrast gegen den Seitenhintergrund. */
.primary-button:focus-visible,
.danger-button:focus-visible {
  outline-color: var(--text);
  box-shadow: 0 0 0 2px var(--on-accent);
}
/* Der Begrüßungsblock ist in beiden Modi dunkel; dort trägt nur ein heller Ring. */
.welcome-card .primary-button:focus-visible {
  outline-color: var(--on-display);
  box-shadow: 0 0 0 2px var(--display-from);
}

.skip-link {
  position: fixed;
  z-index: 100;
  top: 8px;
  left: 8px;
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-md);
  background: var(--text);
  color: var(--surface);
  transform: translateY(-150%);
}
.skip-link:focus { transform: translateY(0); }

.eyebrow {
  margin: 0 0 var(--space-1);
  color: var(--text-muted);
  font-size: var(--text-xs);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
}
h1, h2, h3, p { overflow-wrap: anywhere; }
h1 { margin: 0; font-size: clamp(var(--text-xl), 3vw, var(--text-2xl)); letter-spacing: var(--tracking-tighter); }
h2 { margin: 0; font-size: var(--text-lg); letter-spacing: var(--tracking-tight); }
h3 { margin: 0; font-size: var(--text-md); }
.muted { color: var(--text-muted); }

/* Der Innenabstand gehoert an DIESE Regel, nicht an die einzelnen Varianten.
   Vorher setzte ihn jede Variante selbst — `.section-card` 24px, `.asset-card`
   16px, `.balance-card` gar keinen. Auf einer Seite standen dadurch drei
   Karten untereinander, deren Text an drei verschiedenen Stellen begann; bei
   der Saldo-Karte klebte er direkt am Rahmen.
   Geprueft wird das ab R3.4 von `npm run check:spacing`. */
.card {
  padding: var(--space-5);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface);
  box-shadow: var(--shadow-sm);
}

.metric-card { min-height: 142px; }
.metric-label { margin: 0 0 var(--space-5); color: var(--text-muted); font-size: var(--text-sm); font-weight: var(--weight-bold); }
.metric-value { margin: 0; font-size: clamp(var(--text-xl), 3vw, var(--text-2xl)); font-weight: var(--weight-bold); letter-spacing: var(--tracking-tighter); }
.metric-meta { margin: var(--space-2) 0 0; color: var(--text-muted); font-size: var(--text-xs); }

/* Kopfflaeche: ein Bauteil, drei Fuellungen. Aufbau, Innenabstand, Radius und
   Ausrichtung sind gemeinsam; die Varianten setzen nur noch die Flaeche und die
   Schriftfarbe. Vorher waren es drei vollstaendige Parametersaetze. */
.welcome-card,
.household-summary,
.asset-total-card {
  display: flex;
  min-height: 170px;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--space-5);
  border-radius: var(--radius-lg);
}
/* Banner, keine Karten: sie tragen den Seitenkopf und dürfen grosszügiger
   sein. `.asset-total-card` trägt dagegen seit R3.4 die Klasse `card` und
   bekommt seinen Abstand von dort — geprüft von `npm run check:spacing`. */
.welcome-card,
.household-summary { padding: var(--space-6); }

.welcome-card {
  position: relative;
  overflow: hidden;
  background:
    radial-gradient(circle at 85% 18%, var(--display-glow), transparent 28%),
    linear-gradient(135deg, var(--display-from), var(--display-to));
  color: var(--on-display);
  box-shadow: var(--shadow);
}
.welcome-card::after {
  position: absolute;
  width: 220px;
  height: 220px;
  border: 1px solid var(--display-line);
  border-radius: var(--radius-round);
  content: "";
  inset: -80px -55px auto auto;
}
.welcome-copy { position: relative; z-index: 1; max-width: 610px; }
.welcome-card .eyebrow { color: var(--on-display-muted); }
.welcome-card h2 { max-width: 590px; margin: 0 0 var(--space-4); font-size: clamp(var(--text-2xl), 5vw, var(--text-3xl)); line-height: var(--leading-tight); letter-spacing: var(--tracking-tighter); }
.welcome-card p:not(.eyebrow) { max-width: 540px; margin: 0; color: var(--on-display-muted); font-size: var(--text-md); line-height: var(--leading-relaxed); }
.welcome-card .primary-button { position: relative; z-index: 1; flex: 0 0 auto; background: var(--on-display); color: var(--display-from); }
.welcome-icon {
  display: grid;
  width: 44px;
  height: 44px;
  margin-bottom: var(--space-6);
  place-items: center;
  border: 1px solid var(--display-line);
  border-radius: var(--radius-md);
  background: var(--display-fill);
}

.quick-action-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-3); }
.quick-action {
  display: grid;
  min-height: 118px;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-4);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface);
}
.quick-action:hover { border-color: color-mix(in srgb, var(--accent-strong) 38%, var(--border)); box-shadow: var(--shadow-hover); transform: translateY(-2px); }
.quick-action-icon, .insight-icon {
  display: grid;
  width: 42px;
  height: 42px;
  place-items: center;
  border-radius: var(--radius-md);
  background: var(--accent-soft);
  color: var(--accent-ink);
}
.quick-action strong { display: block; font-size: var(--text-sm); }
.quick-action small { display: block; margin-top: var(--space-1); color: var(--text-muted); font-size: var(--text-xs); line-height: var(--leading-snug); }
.quick-action-chevron { width: 17px; color: var(--text-muted); }

.insight-list { display: grid; gap: var(--space-4); }
.insight { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: var(--space-3); align-items: flex-start; }
.insight-icon { width: 38px; height: 38px; border-radius: var(--radius-md); }
.insight-icon svg { width: 18px; height: 18px; }
/* Ausnahme von der Abstandsskala: 1px optischer Ausgleich. Die Überschrift
   steht neben einem 38px-Symbolfeld und sitzt ohne diesen Versatz eine
   Haarlinie zu hoch gegen dessen Oberkante. */
.insight h3 { margin: 1px 0 var(--space-1); font-size: var(--text-sm); }
.insight p { margin: 0; color: var(--text-muted); font-size: var(--text-xs); line-height: var(--leading-relaxed); }
.task-list { display: grid; gap: var(--space-2); }

.dashboard-task { display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: center; gap: var(--space-3); }
.dashboard-task span { display: grid; gap: var(--space-1); }
.dashboard-task small { color: var(--text-muted); font-size: var(--text-xs); }

.household-summary { background: var(--accent-soft); }
.household-summary h2 { margin: 0 0 var(--space-2); font-size: clamp(var(--text-xl), 4vw, var(--text-2xl)); letter-spacing: var(--tracking-tighter); }
.household-summary p:not(.eyebrow) { max-width: 620px; margin: 0; color: var(--text-muted); line-height: var(--leading-relaxed); }
.household-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.data-list { display: grid; }
/* Listenzeile: ein Bauteil, eine Hoehe. Neun Muster hatten sechs Mindesthoehen,
   zwei Spaltenabstaende und drei Innenabstaende — der Aufbau war ueberall
   derselbe. Was jede Zeile weiter selbst bestimmt, ist nur ihre Spaltenteilung.
   Einzige Hoehenvariante ist die Buchungszeile: sie traegt zwei Textzeilen,
   Betrag und Aktionen in einer Reihe. */
.data-row,
.transaction-row,
.settings-row,
.history-row,
.planner-task,
.widget-picker-row,
.global-search-result,
.task-item,
.cashflow-summary > div {
  min-height: 56px;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) 0;
  border: 0;
  border-bottom: 1px solid var(--border);
}
.transaction-row { min-height: 72px; }

.data-row { display: grid; grid-template-columns: auto minmax(0, 1fr); }

.person-avatar {
  align-items: center;
  border-radius: var(--radius-round);
  display: inline-flex;
  font-size: var(--text-sm);
  font-weight: var(--weight-bold);
  height: 2.5rem;
  justify-content: center;
  width: 2.5rem;
}

/* Personenfarben als Tokens, damit darkmode.css sie ueberschreiben kann:
   vorher leuchteten die sechs hellen Flaechen als weisse Inseln aus der
   dunklen Oberflaeche (14,7:1 gegen den Seitenhintergrund). */
/* Mit Bild: der Kreis ist der Rahmen, das Bild füllt ihn ganz aus. `overflow`
   und `object-fit` zusammen — ohne das zweite würde ein nicht ganz quadratisches
   Bild verzerrt statt beschnitten. */
.person-avatar.has-image { overflow: hidden; padding: 0; }
.person-avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }
.person-avatar-gross { width: 3.5rem; height: 3.5rem; font-size: var(--text-lg); }

/* `.settings-row` verteilt seine zwei Kinder auf die Ränder. Die Profilzeile hat
   drei — ohne diese Regel stünde der Name mittig zwischen Bild und Aktion. */
.profile-row { align-items: center; gap: var(--space-3); }
.profile-row > span:nth-of-type(2) { flex: 1 1 auto; min-width: 0; }
.profile-row > span:last-child { flex: 0 0 auto; }
.profile-row strong { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.profile-row p { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.profile-edit { display: flex; align-items: center; gap: var(--space-4); flex-wrap: wrap; }
.profile-edit-actions { display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-2); min-width: 0; flex: 1 1 12rem; }
/* Das <label> vertritt das versteckte Dateifeld und muss deshalb aussehen und
   sich anfassen lassen wie ein Knopf — inklusive Mindesthöhe für den Finger. */
.profile-edit-actions label { cursor: pointer; display: inline-flex; align-items: center; min-height: 40px; }
.profile-edit-actions .field-hint { margin: 0; }

/* Ein gesperrtes Feld muss auch so aussehen. Ohne das steht die E-Mail-Adresse
   im Profil wie ein Eingabefeld da, und man tippt vergeblich hinein. Nicht über
   `opacity`: das nähme dem Text auch den Kontrast. */
.form-field input:disabled,
.form-field select:disabled,
.form-field textarea:disabled {
  /* Kurzform vermieden, siehe oben: sie setzt die Pfeilregeln zurück. */
  background-color: var(--surface-muted);
  color: var(--text-muted);
  cursor: not-allowed;
  border-style: dashed;
}

.color-choice { border: 0; margin: 0; padding: 0; }
.color-choice legend { font-size: var(--text-sm); font-weight: var(--weight-medium); padding: 0; margin-bottom: var(--space-2); }
.color-options { display: flex; gap: var(--space-2); flex-wrap: wrap; }
.color-option { position: relative; cursor: pointer; display: inline-flex; }
/* Das Eingabefeld liegt unsichtbar über dem Kreis: so bleibt es fokussierbar
   und mit der Tastatur bedienbar, statt mit `display:none` aus dem Baum zu
   fallen. Der Ring unten zeigt beides an — Auswahl und Fokus. */
.color-option input { position: absolute; inset: 0; opacity: 0; margin: 0; cursor: pointer; }
.color-option .person-avatar { width: 2.25rem; height: 2.25rem; box-shadow: 0 0 0 2px transparent; transition: box-shadow .15s ease; }
.color-option input:checked + .person-avatar { box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px var(--accent-strong); }
.color-option input:focus-visible + .person-avatar { box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px var(--accent-strong); outline: 2px solid var(--accent-strong); outline-offset: 4px; }

.person-blue { background: var(--person-blue-surface); color: var(--person-blue-text); }
.person-green { background: var(--person-green-surface); color: var(--person-green-text); }
.person-amber { background: var(--person-amber-surface); color: var(--person-amber-text); }
.person-violet { background: var(--person-violet-surface); color: var(--person-violet-text); }
.person-rose { background: var(--person-rose-surface); color: var(--person-rose-text); }
.person-slate { background: var(--person-slate-surface); color: var(--person-slate-text); }

.compact-field {
  margin-top: var(--space-4);
  max-width: 20rem;
}

.asset-plan {
  display: grid;
  gap: var(--space-2);
  margin: var(--space-3) 0;
}

.asset-plan div {
  display: flex;
  justify-content: space-between;
  gap: var(--space-3);
}

.asset-plan dt { color: var(--text-muted); }
.asset-plan dd { font-weight: var(--weight-bold); text-align: right; }
.data-row:last-child { border-bottom: 0; }
/* Die Zeile hat zwei Spalten; ohne eigene Regel rutschten die Aktionen in eine
   implizite dritte Zeile und klebten linksbuendig unter dem Avatar. Volle
   Breite, rechts ausgerichtet — bei jeder Fensterbreite gleich. */
.data-row .row-actions { grid-column: 1 / -1; justify-content: flex-end; }
/* overflow-wrap wie bei h1/h2/h3/p: die erste Spalte der Zeile ist `auto` und
   wurde sonst von einem einzigen langen Kategorienamen aufgezogen. */
.data-row strong, .data-row small { display: block; overflow-wrap: anywhere; }
.data-row small { margin-top: var(--space-1); color: var(--text-muted); font-size: var(--text-xs); }
.data-icon { display: grid; width: 38px; height: 38px; place-items: center; border-radius: var(--radius-md); background: var(--surface-muted); color: var(--text-muted); }
.data-icon svg { width: 18px; height: 18px; }
.chip-list { display: flex; flex-wrap: wrap; min-width: 0; gap: var(--space-2); }
/* Pille: ein Bauteil fuer alle fuenf Auspraegungen. Vorher fuenf Innenabstaende
   und zwei Schriftgroessen fuer dieselbe Form. Die Varianten setzen nur noch
   Flaeche und Schriftfarbe; .sync-badge bleibt zusaetzlich ein 44px-Touchziel. */
.badge,
.category-chip,
.budget-status,
.goal-percent,
.sync-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-1) var(--space-2);
  border-radius: var(--radius-pill);
  background: var(--surface-muted);
  color: var(--text-muted);
  font-size: var(--text-xs);
  font-weight: var(--weight-bold);
  white-space: nowrap;
}
/* Ohne min-width/max-width war die min-content-Breite eines Chips 326px — er
   sprengte 320px und die schmale Spalte von .content-grid bei 1024px. Gekuerzt
   wird nur der Text; die Aktionsknoepfe bleiben ungeschrumpft. */
.category-chip { min-width: 0; max-width: 100%; color: var(--text); }
.category-chip > span:first-child {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.category-chip .row-actions { flex: 0 0 auto; flex-wrap: nowrap; }
/* Name und Kostenart stehen im selben <span>, der Chip-gap greift dort nicht —
   ohne diesen Abstand las sich der Chip als "LebensmittelVariable Kosten". */
.category-chip small { margin-left: var(--space-1); color: var(--text-muted); font-size: var(--text-xs); font-weight: var(--weight-regular); }
/* Werkzeugleiste: dieselbe Zeile mit Titel links und Bedienelementen rechts.
   Zwei Ausfuehrungen wurden zu einer Basis mit zwei Fuellungen. */
.expenses-toolbar,
.cashflow-toolbar {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--space-5);
  padding: var(--space-5);
  border-radius: var(--radius-lg);
}
.expenses-toolbar { background: linear-gradient(135deg, var(--surface) 0%, var(--surface-muted) 100%); box-shadow: var(--shadow-sm); }
/* min-width: 0 — sonst zieht die Textspalte die Werkzeugleiste auf ihre
   min-content-Breite auf. Gemessen: bei 768px lief die Seite dadurch 6px über
   den Bildschirm, seit dort neben dem Filter zwei Knöpfe stehen. */
.expenses-toolbar > div:first-child { min-width: 0; }
.expenses-toolbar h1 { margin: var(--space-1) 0 var(--space-1); font-size: clamp(var(--text-xl), 4vw, var(--text-2xl)); letter-spacing: var(--tracking-tighter); }
.expenses-actions { display: flex; min-width: 0; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: var(--space-2); }
.expenses-actions select { min-width: 0; min-height: 44px; padding: 0 var(--space-3); border: 1px solid var(--border-control); border-radius: var(--radius-md); background-color: var(--surface); color: var(--text); }
.expense-month-section { margin-top: var(--space-5); }
.expense-month-heading { display: flex; align-items: baseline; justify-content: space-between; margin: 0 var(--space-1) var(--space-2); }
.expense-month-heading h2 { margin: 0; font-size: var(--text-md); text-transform: capitalize; }
.expense-month-heading span { color: var(--text-muted); font-size: var(--text-xs); }
.feed-sentinel { height: 28px; margin-top: var(--space-4); }
/* Kein overflow-x mehr: mit drei Reitern passt die Leiste auch bei 320px
   nebeneinander — gemessen 93,3px je Reiter bei 75,4px längster Textbreite,
   scrollWidth = clientWidth = 296px. Vorher lagen vier von sieben Reitern hinter
   einem Wischen, mit per CSS ausgeblendetem Scrollbalken. */
/* Reiter und Segmente sind dasselbe Bauteil: eine Spur mit Schaltern darin.
   Die Semantik bleibt getrennt — .household-tabs ist ein `tablist` und meldet
   `aria-selected`, .segmented-control meldet `aria-pressed`. Gemeinsam ist das
   Aussehen, nicht die Rolle. */
.household-tabs,
.segmented-control {
  display: flex;
  gap: var(--space-1);
  padding: var(--space-1);
  border-radius: var(--radius-md);
  background: var(--surface-muted);
}
.household-tabs button,
.segmented-control button {
  min-height: 44px;
  padding: 0 var(--space-3);
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-muted);
  cursor: pointer;
  font-size: var(--text-sm);
  font-weight: var(--weight-bold);
}
/* Die Reiterleiste teilt die volle Breite gleichmaessig auf, das Segment waechst
   mit seiner Beschriftung. Das ist der einzige Unterschied. */
.household-tabs button { flex: 1 1 0; min-width: 0; }
.household-tabs button[aria-selected="true"],
.segmented-control button[aria-pressed="true"] { background: var(--surface); color: var(--text); box-shadow: var(--shadow-raised); }
.transaction-list { display: grid; }
.transaction-row { display: grid; grid-template-columns: 58px minmax(0, 1fr) auto auto; }
.transaction-row:last-child { border-bottom: 0; }
.transaction-date { color: var(--text-muted); font-size: var(--text-xs); font-weight: var(--weight-bold); text-transform: uppercase; }
.transaction-copy strong, .transaction-copy small { display: block; }
.transaction-copy small { margin-top: var(--space-1); color: var(--text-muted); font-size: var(--text-xs); }
/* Einnahmen und Ausgaben stehen seit Release 10 in einer Liste. Farbe allein
   traegt die Unterscheidung nicht: Vorzeichen und Ziffern liegen in einer festen
   Spalte (tabular-nums), rechtsbuendig — "+" und "−" stehen dadurch
   untereinander und sind auch ohne Farbwahrnehmung abtastbar. */
.transaction-amount {
  display: inline-flex;
  justify-self: end;
  align-items: baseline;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
  text-align: right;
}
.amount-sign { display: inline-block; width: .62em; text-align: center; font-weight: var(--weight-bold); }
.transaction-amount.positive { color: var(--positive); }
.transaction-amount.negative { color: var(--negative); }
/* Nur fuer Screenreader: "Einnahme"/"Ausgabe" als Wort, weil das Vorzeichen
   vorgelesen entfallen wuerde. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
.positive { color: var(--positive); }
.negative { color: var(--negative); }
/* strong.warning aus dashboard.js/analysis.js hatte gar keine Regel: die
   dreistufige Budgetampel rendert sonst gruen / unformatiert / rot. */
.warning { color: var(--warning); }
/* 8px statt 5px: zwei 44-px-Ziele mit 5px Abstand trifft der Daumen daneben. */
.row-actions { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.icon-button.compact { width: 44px; min-height: 44px; font-size: var(--text-md); }
.icon-button.danger:hover { background: color-mix(in srgb, var(--negative) 12%, var(--surface)); color: var(--negative); }
/* .primary-button steht spaeter in der Datei und gewinnt bei gleicher
   Spezifitaet — der Loeschknopf war deshalb blau statt rot. */
.primary-button.danger-button { background: var(--negative); color: var(--on-negative); }
/* Abmelden stand bis R4.4 als vierte Zeile in „Haushalt & Mitglieder", zwischen
   Einladen und den Aufgaben. Eine Zeile, die einen abmeldet, sieht dort aus wie
   eine, die einen Dialog oeffnet. Jetzt steht sie unten, allein und rot. */
.sign-out-zone { display: flex; flex-direction: column; align-items: center; gap: var(--space-2); padding: var(--space-5) 0 var(--space-3); }
.sign-out-button { width: 100%; max-width: 320px; }
.sign-out-zone .field-hint { text-align: center; }
.check-row { display: flex; min-height: 44px; align-items: center; gap: var(--space-2); font-size: var(--text-sm); font-weight: var(--weight-bold); cursor: pointer; }
.check-row input { width: 18px; height: 18px; accent-color: var(--accent); }
[hidden] { display: none !important; }
.cashflow-toolbar { background: var(--surface); border: 1px solid var(--border); }
.cashflow-toolbar h2 { text-transform: capitalize; }
.toolbar-heading { min-width: 0; }
/* Filterzeilen: zwei gleich breite Spalten, volle Breite.
   Vorher war `.cashflow-filters` gar nicht gestylt — die beiden Felder standen
   untereinander und hörten wegen `max-width: 220px` mitten in der Karte auf:
   links bündig, rechts 104px Luft. Gemessen bei 390px. */
.filter-row,
.cashflow-filters {
  display: grid;
  /* 12rem = 192px je Spalte, zwei davon plus Abstand brauchen 400px Behaelter.
     Der Werkzeugleisten-Behaelter hat hier 448px (28rem), also stehen sie am
     Desktop nebeneinander. Auf dem Telefon entscheidet nicht diese Schwelle,
     sondern `mobile.css`: dort steht bis 720px fest eine Spalte, weil die
     Mindestbreite des nativen Monatsfeldes von der Plattform abhaengt und in
     Chrome nicht messbar ist. Zweimal geraten, zweimal daneben — siehe dort. */
  grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
  gap: var(--space-3);
  width: 100%;
  /* Am Desktop nimmt die Werkzeugleiste den ganzen Rest der Zeile ein; zwei
     Filter darin wären sonst 320px breit für ein Wort. Gemessen: 656,8px. */
  max-width: 28rem;
  margin-left: auto;
}
/* Innerhalb einer Filterzeile bestimmt das Raster die Breite, nicht das Feld. */
.filter-row .month-field,
.cashflow-filters .month-field { max-width: none; width: auto; }
/* Und das Bedienelement selbst muss schrumpfen dürfen. `min-width: 0` am
   Umschlag genügt nicht: die Mindestbreite steckt im nativen Feld, und ohne
   diese Regel wächst es über seine Rasterspalte hinaus und legt sich über das
   Feld daneben — auf dem iPhone gemessen, weil iOS „August 2026" schreibt, wo
   Chrome „2026-08" schreibt. */
.filter-row input,
.filter-row select,
.cashflow-filters input,
.cashflow-filters select { min-width: 0; width: 100%; }
/* Vorher `width: 180px`. In der Zeile daneben steht auf der Haushaltsseite ein
   zweites Feld; bei 390px blieb fuer dessen Beschriftung so wenig, dass
   „Gesamter Haushalt" als „Gesamter Hau" ankam. */
.month-field { flex: 1 1 auto; min-width: 0; max-width: 220px; }
/* `.month-field` traegt seit R4.1 ein gewoehnliches Auswahlfeld. Die Regeln,
   die hier standen, galten dem nativen `input[type=month]`: Textausrichtung,
   weil iOS den Wert zentrierte, und die Einfaerbung seines Kalenderknopfes.
   Beides ist mit dem Bedienelement entfallen — ein `select` richtet links aus
   und zeichnet den Pfeil, den die Datei weiter unten fuer alle setzt. */
/* Ziehgriff der Übersichtskacheln.
   `touch-action: none` steht nur hier und nicht auf der Kachel: sonst liesse
   sich die Seite mit dem Finger nirgends mehr scrollen, wo eine Kachel liegt. */
.widget-grip {
  display: grid;
  width: 44px;
  height: 44px;
  place-items: center;
  border-radius: var(--radius-md);
  color: var(--text-muted);
  cursor: grab;
  touch-action: none;
}
.widget-grip:active { cursor: grabbing; }
.widget-grip:hover { background: var(--surface-muted); color: var(--text); }
/* Das Symbol darf kein eigenes Berührungsziel sein: `touch-action` vererbt
   sich nicht, eine Berührung auf dem SVG galt dem Browser deshalb als Scrollen
   und brach das Ziehen nach dem ersten Zeigerereignis mit pointercancel ab. */
.widget-grip svg { fill: currentColor; pointer-events: none; touch-action: none; }
.dashboard-widget.dragging {
  opacity: .6;
  /* Durchlässig, damit elementFromPoint die Kachel darunter findet. */
  pointer-events: none;
  box-shadow: var(--shadow);
}
.dashboard-widget-grid.is-dragging { cursor: grabbing; user-select: none; }

/* Eine Zahl trägt die Karte: sie ist die Antwort, alles darunter ist Beleg. */
.headline-metric { display: grid; gap: var(--space-1); margin-bottom: var(--space-5); }
.headline-label { margin: 0; color: var(--text-muted); font-size: var(--text-sm); }
.headline-value { margin: 0; font-size: var(--text-2xl); font-weight: var(--weight-bold); letter-spacing: var(--tracking-tighter); font-variant-numeric: tabular-nums; }
.headline-meta { margin: 0; color: var(--text-muted); font-size: var(--text-sm); }

/* Wer schuldet wem — der offene Stand über alle Monate, ganz oben in der
   Abrechnung. Der Satz trägt die Aussage, die Liste darunter den Beleg. */
.balance-card { display: grid; gap: var(--space-2); }
.balance-card .eyebrow { margin: 0; }
.balance-headline { margin: 0; font-size: var(--text-lg); font-weight: var(--weight-bold); line-height: var(--leading-snug); }
.balance-card .muted { margin: 0; font-size: var(--text-sm); }
.balance-card .data-list { margin-top: var(--space-2); }

/* Wiederkehrende Posten: dieselbe Zeile wie überall, plus eine Zeile Hinweis,
   wenn der Monat fällig und noch nicht gebucht ist. */
/* `.data-row` hat `auto minmax(0,1fr)`: dort steht links ein Symbol und rechts
   der Text. Hier ist es umgekehrt — der Text traegt die Zeile, der Betrag muss
   in einer Zeile bleiben. Ohne eigene Regel wurde er auf ein Zeichen je Zeile
   zusammengequetscht. */
.recurring-row { grid-template-columns: minmax(0, 1fr) auto; }
.recurring-row > strong { white-space: nowrap; }
.recurring-row .recurring-due { display: block; color: var(--warning); }
.recurring-row .row-actions { flex-wrap: wrap; justify-content: flex-end; }

/* Prognose und Warnung im Formular: Farbe allein trägt die Aussage nicht —
   der Satz nennt sie ebenfalls. */
.plan-forecast { margin: var(--space-2) 0 0; font-size: var(--text-sm); }
.plan-forecast.positive { color: var(--positive); }
.plan-forecast.warning { color: var(--warning); }
.plan-forecast.negative { color: var(--negative); }
.field-hint.field-warning { color: var(--warning); }

/* Das Angebot, einen abgeschlossenen Monat zu öffnen, steht IM Formular —
   ein zweiter Dialog hätte das Formular ersetzt und alles Getippte verworfen. */
.reopen-notice {
  display: grid;
  gap: var(--space-2);
  justify-items: start;
  padding: var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface-muted);
}
.reopen-notice p { margin: 0; font-size: var(--text-sm); }

/* Haushaltsrechnung ---------------------------------------------------------
   Eine Spalte aus Zeilen: Beschriftung links, Betrag rechts. Zeilen mit
   Posten sind <details> und klappen auf; sie müssen deshalb dieselbe Höhe und
   dasselbe Raster haben wie eine Zeile ohne Posten, sonst springt die Liste. */
.budget-plan { display: grid; gap: var(--space-5); }
.plan-rows { display: grid; margin: 0; padding: 0; list-style: none; }
.plan-line {
  display: flex;
  min-height: 44px;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-2) 0;
}
.plan-label { display: grid; gap: var(--space-1); min-width: 0; color: var(--text); font-size: var(--text-sm); }
.plan-label small { color: var(--text-muted); font-size: var(--text-xs); }
.plan-amount { font-size: var(--text-md); font-weight: var(--weight-bold); font-variant-numeric: tabular-nums; }
.plan-row + .plan-row .plan-line { border-top: 1px solid var(--border); }
/* Zwischen- und Endsumme heben sich durch eine kräftigere Linie und Schrift ab,
   nicht durch Farbe — der Vorzeichenwechsel trägt die Aussage schon. */
.plan-row.subtotal .plan-line, .plan-row.total .plan-line { border-top: 2px solid var(--border-control); }
.plan-row.subtotal .plan-label, .plan-row.total .plan-label { font-weight: var(--weight-bold); }
.plan-row.total .plan-line { padding-top: var(--space-3); }
.plan-row.total .plan-label { font-size: var(--text-md); }
.plan-row.total .plan-amount { font-size: var(--text-lg); }
.plan-details > summary { cursor: pointer; list-style: none; }
.plan-details > summary::-webkit-details-marker { display: none; }
/* Der Hinweis, dass sich die Zeile öffnen lässt, hängt am Label statt an einem
   eigenen Symbol rechts — dort steht schon der Betrag. */
.plan-details > summary .plan-name::after {
  content: "▾";
  margin-left: var(--space-2);
  color: var(--text-muted);
  font-size: var(--text-xs);
}
.plan-details[open] > summary .plan-name::after { content: "▴"; }
.plan-details > summary:focus-visible { border-radius: var(--radius-sm); }
.plan-items { display: grid; gap: var(--space-2); margin: 0 0 var(--space-3); padding: 0 0 0 var(--space-4); list-style: none; }
.plan-items li { display: flex; justify-content: space-between; gap: var(--space-4); color: var(--text-muted); font-size: var(--text-sm); }
.plan-items span:last-child { font-variant-numeric: tabular-nums; }
.plan-progress { display: grid; gap: var(--space-2); }
.plan-progress-note { margin: 0; color: var(--text-muted); font-size: var(--text-sm); }

.cashflow-summary { display: grid; }
.cashflow-summary > div { display: flex; justify-content: space-between; }
.cashflow-summary > div:last-child { border-bottom: 0; }
.cashflow-summary span { color: var(--text-muted); font-size: var(--text-sm); }
.asset-total-card { background: linear-gradient(135deg, var(--accent-soft), var(--surface)); }
.asset-total { margin: var(--space-1) 0 var(--space-1); font-size: clamp(var(--text-2xl), 6vw, var(--text-3xl)); font-weight: var(--weight-bold); letter-spacing: var(--tracking-tighter); }
.asset-total-card p:last-child { margin: 0; color: var(--text-muted); font-size: var(--text-xs); }
.asset-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-3); }
.asset-card { display: grid; min-height: 210px; align-content: space-between; gap: var(--space-4); }
.asset-card > div:first-child { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); color: var(--text-muted); font-size: var(--text-xs); }
.asset-card h3 { font-size: var(--text-sm); }
.asset-balance { margin: 0; font-size: var(--text-xl); font-weight: var(--weight-bold); letter-spacing: var(--tracking-tighter); }
.history-row { display: flex; justify-content: space-between; }
.history-row:last-child { border-bottom: 0; }
.history-row span strong, .history-row span small { display: block; }
.history-row span small { margin-top: var(--space-1); color: var(--text-muted); font-size: var(--text-xs); }
.budget-list { display: grid; gap: var(--space-5); }
.budget-row { display: grid; gap: var(--space-2); }
.budget-heading { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-4); }
.budget-heading strong, .budget-heading small { display: block; }
.budget-heading small { margin-top: var(--space-1); color: var(--text-muted); font-size: var(--text-xs); }

.budget-status.warning { background: var(--tone-warning-surface); color: var(--warning); }
.budget-status.exceeded { background: var(--tone-negative-surface); color: var(--negative); }
.progress-track { height: 8px; overflow: hidden; border-radius: var(--radius-pill); background: var(--surface-muted); }
.progress-track span { display: block; height: 100%; border-radius: inherit; background: var(--accent-strong); }
.progress-track span.warning { background: var(--warning); }
.progress-track span.exceeded { background: var(--negative); }
.budget-row > p { margin: 0; color: var(--text-muted); font-size: var(--text-xs); }
.closing-summary { display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); }
.closing-summary span { color: var(--text-muted); font-size: var(--text-xs); }

.form-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-3); }


.section-heading {
  display: flex;
  /* Umbruchfest: passen Titel und Handlung nicht nebeneinander, rutscht die
     Handlung in die naechste Zeile. Vorher brach stattdessen die Ueberschrift
     mitten im Wort um — `overflow-wrap: anywhere` gilt global fuer h2. */
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-4);
  margin-bottom: var(--space-5);
}
/* Der Textblock gibt zuerst nach, die Handlung behaelt ihre Breite. */
.section-heading > :first-child { min-width: 0; flex: 1 1 12rem; }
.section-heading p { margin: var(--space-1) 0 0; color: var(--text-muted); font-size: var(--text-sm); }

/* Leerzustand und Ladeflaeche: eine mittige Meldung in einer Flaeche. Sechs
   Muster mit vier Mindesthoehen und drei Schriftgroessen sind zu einer Basis
   geworden; einzige Variante ist die grosse Seitenflaeche mit 200px. */
.empty-state,
.inline-empty,
.search-placeholder,
.modal-loading,
.route-loader,
.route-error {
  display: grid;
  min-height: 160px;
  place-items: center;
  padding: var(--space-5);
  color: var(--text-muted);
  font-size: var(--text-sm);
  text-align: center;
}
.empty-state {
  min-height: 200px;
  border: 1px dashed var(--border);
  border-radius: var(--radius-lg);
  background: color-mix(in srgb, var(--surface-muted) 55%, transparent);
}
.empty-state.compact { min-height: 160px; }
.empty-icon {
  display: grid;
  width: 44px;
  height: 44px;
  margin: 0 auto var(--space-4);
  place-items: center;
  border-radius: var(--radius-md);
  background: var(--accent-soft);
  color: var(--accent-ink);
}
.empty-state h3 { margin-bottom: var(--space-2); }
.empty-state p { max-width: 390px; margin: 0; color: var(--text-muted); font-size: var(--text-sm); line-height: var(--leading-relaxed); }
/* Leerzustaende hatten die Aufforderung nur im Fliesstext ("Erfasse deine erste
   Ausgabe.") und nichts zum Draufdruecken. */
.empty-state-action { margin-top: var(--space-4); }
.empty-state-action .primary-button, .empty-state-action .secondary-button { min-height: 44px; }

/* Feldfehler statt Toast: der Toast verschwand nach 3,2 s und nannte das Feld
   nicht. Das Symbol traegt die Information zusaetzlich zur Farbe. */
.field-error {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  margin: var(--space-2) 0 0;
  color: var(--negative);
  font-size: var(--text-xs);
  font-weight: var(--weight-bold);
  line-height: var(--leading-snug);
}
.field-error-icon {
  display: grid;
  width: 16px;
  height: 16px;
  flex: 0 0 auto;
  /* Ausnahme von der Abstandsskala: 1px optischer Ausgleich. Das runde Symbol
     ist 16px hoch, die Fehlerzeile 1,4 · 12,5px — ohne den Versatz steht der
     Kreis eine Haarlinie über der ersten Textzeile. */
  margin-top: 1px;
  place-items: center;
  border-radius: var(--radius-round);
  background: var(--negative);
  color: var(--on-accent);
  font-size: var(--text-xs);
  font-weight: var(--weight-bold);
}
[aria-invalid="true"] { border-color: var(--negative) !important; box-shadow: 0 0 0 1px var(--negative); }

.primary-button, .secondary-button, .icon-button, .icon-text-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-height: 44px;
  border: 0;
  border-radius: var(--radius-md);
  cursor: pointer;
  font-weight: var(--weight-bold);
}
.primary-button { padding: 0 var(--space-4); background: var(--accent); color: var(--on-accent); }
/* Jeder Knopf ist eine FLÄCHE. Gleiche Höhe, gleicher Radius, gleiches
   Gewicht — der Unterschied liegt allein in der Farbe der Fläche, nicht in der
   Bauform.
   Zwei Anläufe davor waren falsch: erst ein Rand in --border auf --surface
   (im Dunkelmodus 1,3:1 zur Karte, also praktisch unsichtbar — „Abbrechen" sah
   aus wie Text, der zufällig dort steht), dann ein Rand im Akzent ohne Füllung.
   Auch der war noch ein Umriss unter lauter Flächen. */
.secondary-button {
  padding: 0 var(--space-4);
  border: 1px solid var(--border);
  background: var(--surface-muted);
  color: var(--text);
}
.secondary-button:hover { background: var(--accent-soft); color: var(--accent-ink); border-color: var(--accent-soft); }
/* Auf einer Akzentfläche wäre die gedämpfte Fläche wirkungslos. */
.secondary-button.on-accent { background: var(--on-accent); color: var(--accent); border-color: var(--on-accent); }
/* Touch-Ziel: 44px in beide Richtungen, sonst trifft der Daumen daneben. */
/* flex-shrink: 0 — in .topbar-actions wurde der Knopf sonst auf 30,6px gequetscht. */
.icon-button { width: 44px; flex-shrink: 0; background: var(--surface-muted); }
.icon-text-button { width: 100%; justify-content: flex-start; padding: 0 var(--space-3); background: transparent; color: var(--text-muted); }
.primary-button:hover { filter: brightness(.96); }
.secondary-button:hover, .icon-button:hover, .icon-text-button:hover { background: var(--surface-muted); color: var(--text); }

.segmented-control { display: inline-flex; }
/* Auf der Buchungsseite steht die Steuerung in einer eigenen Zeile und teilt
   die volle Breite gleichmaessig auf die drei Segmente. */
.segmented-control.entry-filter { display: flex; width: 100%; }
.segmented-control.entry-filter button { flex: 1 1 0; min-width: 0; }
/* Aufteilung im Ausgabendialog: drei gleich breite Segmente statt einer
   Auswahlliste mit vier Eintraegen. */
.segmented-control.split-control { display: flex; width: 100%; }
.segmented-control.split-control button { flex: 1 1 0; min-width: 0; }

/* --- Ausgabendialog ---------------------------------------------------------
   Der Betrag steht zuerst und gross: er ist die Zahl, die beim Oeffnen im Kopf
   ist. Alles Weitere liegt hinter „Mehr Details". */
.amount-input { position: relative; display: grid; }
.expense-form .amount-field input {
  min-height: 64px;
  padding-right: var(--space-7);
  font-size: var(--text-2xl);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-tight);
}
/* Die Pfeile des Zahlenfelds halbieren die nutzbare Breite des grossen Feldes. */
.expense-form .amount-field input::-webkit-outer-spin-button,
.expense-form .amount-field input::-webkit-inner-spin-button { margin: 0; appearance: none; }
.expense-form .amount-field input { appearance: textfield; }
.amount-currency {
  position: absolute;
  top: 50%;
  right: var(--space-4);
  color: var(--text-muted);
  font-size: var(--text-lg);
  font-weight: var(--weight-bold);
  transform: translateY(-50%);
}
.field-label { font-size: var(--text-sm); font-weight: var(--weight-bold); }
/* Die Anteilsfelder erben aus .compact-field einen Aussenabstand und eine
   Maximalbreite von 20rem — im Raster des Dialogs standen sie dadurch schmaler
   als jedes andere Feld. */
#expense-splits .compact-field { max-width: none; margin-top: 0; }
.more-details { border-top: 1px solid var(--border); }
.more-details summary {
  display: flex;
  min-height: 44px;
  align-items: center;
  gap: var(--space-2);
  color: var(--text-muted);
  cursor: pointer;
  font-size: var(--text-sm);
  font-weight: var(--weight-bold);
}
/* display: flex loescht den Standardmarker; ohne Ersatz sieht die Klappe wie
   eine Ueberschrift aus und niemand tippt darauf. */
.more-details summary::-webkit-details-marker,
.chart-table summary::-webkit-details-marker { display: none; }
.more-details summary::before,
.chart-table summary::before { content: "›"; display: inline-block; font-size: var(--text-md); transition: transform .15s ease; }
.more-details[open] summary::before,
.chart-table[open] summary::before { transform: rotate(90deg); }
.more-details > .form-stack { padding-bottom: var(--space-2); }

/* Sichtbarer Synchronisierungsstatus, seit Release 9 die einzige Netzanzeige:
   #network-status meldete denselben Offline-Zustand ein zweites Mal und in
   derselben Farbe, war aber nicht anklickbar. Rahmen und Pfeil zeigen, dass
   hier getippt werden kann; die Erklaerung kommt beim Tippen als Toast, weil
   ein title-Attribut auf Touch unsichtbar bleibt. */
.sync-badge {
  min-width: 0;
  /* Gekuerzt wird erst ab 34vw — darunter bleibt vom Status nur ein Fleck. */
  max-width: 34vw;
  /* Touch-Ziel, kein Formparameter: die Pille ist anklickbar. */
  min-height: 44px;
  padding-inline: var(--space-3);
  border: 1px solid currentColor;
  cursor: pointer;
  overflow: hidden;
}
.sync-badge > span { overflow: hidden; text-overflow: ellipsis; }
.sync-badge::after { content: "↻"; flex: 0 0 auto; font-size: var(--text-md); }
.sync-badge[data-tone="ok"] { background: var(--tone-positive-surface); color: var(--positive); }
.sync-badge[data-tone="busy"] { background: var(--tone-neutral-surface); color: var(--text-muted); }
.sync-badge[data-tone="warn"] { background: var(--tone-warning-surface); color: var(--warning); }
.sync-badge[data-tone="error"] { background: var(--tone-negative-surface); color: var(--negative); }

.toast-region { position: fixed; z-index: 80; right: 20px; bottom: 20px; display: grid; gap: var(--space-2); }
.toast { padding: var(--space-3) var(--space-4); border-radius: var(--radius-md); background: var(--text); color: var(--surface); box-shadow: var(--shadow); font-size: var(--text-sm); }
.update-prompt {
  position: fixed;
  z-index: 85;
  right: 20px;
  bottom: 20px;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3);
  border: 1px solid color-mix(in srgb, var(--accent-strong) 24%, var(--border));
  border-radius: var(--radius-lg);
  background: color-mix(in srgb, var(--surface) 94%, transparent);
  box-shadow: var(--shadow);
  backdrop-filter: blur(18px);
}
.update-prompt-copy { min-width: 0; display: grid; gap: var(--space-1); }
.update-prompt-copy strong { font-size: var(--text-sm); font-weight: var(--weight-bold); }
.update-prompt-copy small { color: var(--text-muted); font-size: var(--text-xs); font-weight: var(--weight-bold); }
.update-prompt-copy ul { margin: var(--space-1) 0 0; padding-left: var(--space-4); color: var(--text-muted); font-size: var(--text-xs); line-height: var(--leading-snug); }
.compact-action { min-height: 44px; padding: 0 var(--space-4); }

.form-stack { display: grid; gap: var(--space-4); }
.form-field { display: grid; gap: var(--space-2); }
.form-field label { font-size: var(--text-sm); font-weight: var(--weight-bold); }
.form-field input,
.form-field select,
.form-field textarea {
  width: 100%;
  /* DER eigentliche Hebel. `.form-field` ist ein Gitter, und ein Gitterelement
     hat `min-width: auto` — es weigert sich, unter seine INHALTSBREITE zu
     schrumpfen. `width: 100%` verliert gegen diese Grenze, ohne dass irgendwo
     etwas ueberlaeuft, das man im Aufbau sehen koennte.
     Bei Textfeldern faellt das nie auf: ihre Inhaltsbreite ist klein. Bei
     einem nativen Datumsfeld ist sie das, was die Plattform fuer „TT.MM.JJJJ
     plus Kalendersymbol" fuer noetig haelt — in Safari deutlich mehr als der
     Platz im Dialog. Genau dieselbe Mechanik hat schon das Monatsfeld dreimal
     ueberstehen lassen. */
  min-width: 0;
  max-width: 100%;
  min-height: 46px;
  padding: 0 var(--space-3);
  /* --text-md sind 16px. Darunter zoomt Safari auf dem iPhone beim Antippen
     die ganze Seite; danach steht die Ansicht schief. Die Groesse steht hier
     deshalb ausdruecklich und nicht als Erbe. */
  font-size: var(--text-md);
  /* Kein `outline: none` mehr: die Regel stand über dem globalen
     :focus-visible und löschte den Fokusring an genau den Feldern, an denen
     der bloße Rahmenfarbwechsel nur 1,2:1 erreicht. */
  border: 1px solid var(--border-control);
  border-radius: var(--radius-md);
  /* `background-color`, NICHT die Kurzform `background`. Die Kurzform setzt
     `background-repeat`, `-position` und `-size` auf ihre Anfangswerte zurück —
     und weil `.form-field select` spezifischer ist als `select`, hat sie genau
     die drei Regeln überschrieben, mit denen der Pfeil an seiner Stelle bleibt.
     Folge: der Pfeil kachelte über das ganze Feld, in jedem Formular der App,
     seit es das Pfeil-Token gibt. In Grau sah es aus wie eine Struktur im
     Hintergrund; in Mint fiel es sofort auf. */
  background-color: var(--surface);
  color: var(--text);
}
.form-field textarea { min-height: 100px; padding: var(--space-3) var(--space-3); resize: vertical; }

/* Auswahlfelder zeichnen ihren Pfeil selbst — und iOS legte ihn in dieselben
   12px Innenabstand, in denen der Text steht. Bei „Gesamter Haushalt" lief die
   Beschriftung darunter durch. Eigener Pfeil, eigener Platz dafuer.
   `background-image` mit `currentColor` geht in CSS nicht; die Farbe steckt
   deshalb im SVG und wird fuer den Dunkelmodus eigens gesetzt. */
select {
  appearance: none;
  -webkit-appearance: none;
  /* Links 12px, rechts 12px bis zum Pfeil, dann 12px bis zum Rand: der Text
     endet bei 32px, weil dort der 12px breite Pfeil steht. Damit sind die
     sichtbaren Ränder gleich — nicht die Zahlen im Regelsatz. */
  padding-right: var(--space-6);
  background-image: var(--select-arrow);
  background-repeat: no-repeat;
  background-position: right var(--space-3) center;
  background-size: 12px 8px;
  /* Ein langer Eintrag zog das Feld auf, statt gekuerzt zu werden. */
  text-overflow: ellipsis;
}
.form-field select { padding-right: var(--space-6); }

/* NATIVE DATUMS- UND ZEITFELDER
 *
 * `appearance: none` stand bis R4.4 ausschliesslich auf `select`. Ohne diese
 * Zeile behaelt ein natives Datumsfeld in Safari seine eigene, aus dem Inhalt
 * abgeleitete Breite und ignoriert `width: 100%` — dieselbe Ursache, an der das
 * Monatsfeld dreimal vorbeigelaufen ist. Damals war die Loesung, das Feld durch
 * ein Auswahlfeld zu ersetzen. Hier geht das nicht: ein Datum ist tagesgenau
 * und braucht einen echten Kalender.
 *
 * Also die Ursache selbst, und zwar an allen vier Typen — nicht nur an dem, der
 * gerade auffaellt. */
input[type="date"],
input[type="time"],
input[type="datetime-local"],
input[type="month"] {
  appearance: none;
  -webkit-appearance: none;
  /* Safari misst sonst weiter am Inhalt, auch ohne die native Zeichnung. */
  min-width: 0;
  max-width: 100%;
  /* Ein inline-Element mit intrinsischer Breite laesst sich schlechter
     beschneiden als ein Blockelement. */
  display: block;
}

/* Die Innereien des Feldes tragen in WebKit eigene Abstaende und eine
   Rechtsbuendigkeit, die zusaetzliche Breite verlangt. Beides hier weg —
   in anderen Browsern greifen die Regeln schlicht nicht. */
input[type="date"]::-webkit-date-and-time-value,
input[type="time"]::-webkit-date-and-time-value,
input[type="datetime-local"]::-webkit-date-and-time-value,
input[type="month"]::-webkit-date-and-time-value {
  text-align: left;
  margin: 0;
  min-width: 0;
}
input[type="date"]::-webkit-datetime-edit,
input[type="time"]::-webkit-datetime-edit,
input[type="datetime-local"]::-webkit-datetime-edit,
input[type="month"]::-webkit-datetime-edit { padding: 0; }

/* Das Kalendersymbol bleibt — es ist der Griff zum Kalender. Es soll nur nicht
   mitwachsen und nicht mit dem Text kollidieren. */
input[type="date"]::-webkit-calendar-picker-indicator,
input[type="datetime-local"]::-webkit-calendar-picker-indicator,
input[type="month"]::-webkit-calendar-picker-indicator {
  flex: none;
  margin-inline-start: auto;
  padding: 0;
  opacity: .6;
  cursor: pointer;
}
input[type="date"]::-webkit-calendar-picker-indicator:hover { opacity: 1; }

/* Und die Container, in denen sie stehen. Eine Gitterspalte ohne `minmax(0, …)`
   hat dieselbe Weigerung zu schrumpfen wie das Feld selbst — die Grenze wandert
   dann nur eine Ebene nach aussen. */
.form-stack, .form-field, .form-grid > * { min-width: 0; }

/* Felder, aus denen man WÄHLT, tragen den Akzent als Rand — Textfelder, in die
   man tippt, nicht. Damit sieht man einem Feld an, was es von einem will.
   Nicht --accent (das CD-Mint): das hat auf hellem Grund 1,5:1 und verfehlt die
   3:1, die ein Bedienelementrahmen braucht. --accent-strong ist dieselbe Hue,
   nur so weit abgedunkelt, dass die Grenze steht; im Dunkelmodus IST es das
   CD-Mint. */
.form-field select,
.expenses-actions select {
  border-color: var(--accent-strong);
}
/* Der Pfeil ebenso: er ist der Hinweis „hier lässt sich etwas aufklappen" und
   soll nicht wie Fließtext aussehen. */
.form-field select,
.expenses-actions select { background-image: var(--select-arrow-accent); }
/* Der Monatswert ist eine Zahlenreihe wie „August 2026" und soll in einer
   Filterzeile nicht umbrechen, wenn daneben ein langer Personenname steht. */
.month-field select { text-overflow: ellipsis; }
.form-field input:focus-visible,
.form-field select:focus-visible,
.form-field textarea:focus-visible { border-color: var(--accent-strong); }
.field-hint { margin: 0; color: var(--text-muted); font-size: var(--text-xs); line-height: var(--leading-snug); }
.backup-progress { color: var(--text); font-weight: var(--weight-bold); }
.backup-progress:empty { display: none; }

.settings-list { display: grid; gap: var(--space-1); }
/* Eine Zeile, die etwas ANZEIGT statt etwas zu tun. Sie sieht aus wie die
   Knoepfe daneben, reagiert aber nicht — deshalb kein Zeiger und kein
   Tastaturfokus, sonst verspricht sie eine Handlung, die es nicht gibt. */
.settings-row.static { cursor: default; }
.settings-row.static:hover { background: var(--surface); }
.settings-row { display: flex; justify-content: space-between; background: transparent; color: var(--text); text-align: left; }
button.settings-row { width: 100%; cursor: pointer; }
button.settings-row:hover { color: var(--accent-ink); }
.settings-row:last-child { border-bottom: 0; }
.settings-row p { margin: var(--space-1) 0 0; color: var(--text-muted); font-size: var(--text-xs); }

.modal-backdrop {
  position: fixed;
  z-index: 90;
  inset: 0;
  display: grid;
  place-items: center;
  padding: var(--space-5);
  background: var(--scrim);
  backdrop-filter: blur(5px);
}
.modal {
  width: min(100%, 520px);
  max-height: min(760px, calc(100vh - 40px));
  overflow: auto;
  padding: var(--space-5);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface);
  box-shadow: var(--shadow);
}
.modal-header { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-4); margin-bottom: var(--space-5); }
.modal-header p { margin: var(--space-1) 0 0; color: var(--text-muted); font-size: var(--text-sm); }
.modal-actions { display: flex; justify-content: flex-end; gap: var(--space-2); margin-top: var(--space-5); }

.spinner {
  width: 24px;
  height: 24px;
  border: 3px solid var(--border);
  border-top-color: var(--accent-strong);
  border-radius: var(--radius-round);
  animation: spin .75s linear infinite;
}
.loading-row { display: inline-flex; align-items: center; gap: var(--space-2); color: var(--text-muted); font-size: var(--text-sm); }
/* Platzhalter-Dialog waehrend eines dynamischen Imports. Er erscheint verzoegert
   (Animation mit 140 ms Vorlauf), damit ein bereits geladenes Modul nicht fuer
   einen Bildaufbau aufblitzt — sichtbar wird er nur, wenn es wirklich dauert. */

.modal-backdrop-loading { animation: loader-in .01s 140ms both; }

@keyframes spin { to { transform: rotate(360deg); } }
@keyframes loader-in { from { opacity: 0; } to { opacity: 1; } }

@media (prefers-reduced-motion: no-preference) {
  .workspace, .card, button, a { transition: background-color .18s ease, color .18s ease, border-color .18s ease, transform .18s ease, box-shadow .18s ease; }
  .page-enter { animation: page-in .25s ease both; }
  .dashboard-widget { animation: widget-in .38s cubic-bezier(.2,.75,.25,1) both; animation-delay: calc(var(--widget-index, 0) * 45ms); }
  .primary-button:active, .secondary-button:active, .icon-button:active { transform: scale(.97); }
  /* Kein Strichmuster mehr: mit vector-effect: non-scaling-stroke rechnet der
     Browser stroke-dasharray in Bildschirmpixeln, nicht in viewBox-Einheiten.
     Die feste 300 zerschnitt die Linie auf einer 920px breiten Karte in drei
     Stuecke mit zwei Luecken. Die Linie blendet jetzt ein, statt sich zu zeichnen. */
  .chart-line { animation: chart-draw .8s ease forwards; }
  .donut-graphic { animation: donut-in .55s cubic-bezier(.2,.75,.25,1) both; }
  @keyframes page-in { from { opacity: 0; transform: translateY(4px); } }
  @keyframes widget-in { from { opacity: 0; transform: translateY(10px); } }
  @keyframes chart-draw { from { opacity: 0; } to { opacity: 1; } }
  @keyframes donut-in { from { opacity: 0; transform: scale(.92) rotate(-10deg); } }
}

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

.input-suffix { position: relative; }
.input-suffix input { padding-right: var(--space-6); }
.input-suffix span { position: absolute; top: 50%; right: var(--space-3); color: var(--text-muted); transform: translateY(-50%); }

.goal-list { display: grid; gap: var(--space-3); margin-top: var(--space-4); }

.goal-card { display: grid; gap: var(--space-3); padding: var(--space-4); border-radius: var(--radius-lg); background: var(--surface-muted); }
.goal-card small, .goal-card dt, .goal-card .muted { color: var(--text-muted); font-size: var(--text-xs); }
.goal-card-header { display: flex; align-items: start; justify-content: space-between; gap: var(--space-4); }
.goal-card-header strong { display: block; font-size: var(--text-md); }
.goal-percent { background: var(--surface); color: var(--accent-ink); }
.goal-card dl { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-2); margin: 0; }
.goal-card dd { margin: var(--space-1) 0 0; font-weight: var(--weight-bold); }
.checkbox-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-2); }
.check-option { display: flex; gap: var(--space-2); align-items: flex-start; padding: var(--space-3); border-radius: var(--radius-md); background: var(--surface-muted); cursor: pointer; }
.check-option span { display: grid; gap: var(--space-1); font-size: var(--text-sm); font-weight: var(--weight-bold); }
.check-option small { color: var(--text-muted); font-size: var(--text-xs); font-weight: var(--weight-medium); }

.inline-empty { margin-top: var(--space-5); border-radius: var(--radius-md); background: var(--surface-muted); }

.planner-task-list { display: grid; }
.planner-task { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; }
.planner-task:last-child { border-bottom: 0; }
.task-check { width: 44px; height: 44px; border: 1.5px solid var(--border); border-radius: var(--radius-md); background: var(--surface); color: var(--accent-ink); cursor: pointer; font-weight: var(--weight-bold); }
.task-check:hover { border-color: var(--accent-strong); }
.planner-task-copy { min-width: 0; }
.planner-task-copy > div { display: flex; align-items: center; gap: var(--space-2); margin-bottom: var(--space-2); }
.planner-task-copy small { color: var(--text-muted); font-size: var(--text-xs); }
.planner-task-copy strong { display: block; }
.planner-task-copy p { margin: var(--space-1) 0 0; color: var(--text-muted); font-size: var(--text-xs); line-height: var(--leading-snug); }
.planner-task.completed .planner-task-copy { opacity: .6; }
.planner-task.completed .planner-task-copy strong { text-decoration: line-through; }
.text-button { min-height: 44px; padding: var(--space-2) var(--space-2); border: 0; background: transparent; color: var(--text-muted); cursor: pointer; font-size: var(--text-xs); font-weight: var(--weight-bold); }
.text-button:hover { color: var(--accent-ink); }
.text-button.danger:hover { color: var(--negative); }
.completed-tasks { margin-top: var(--space-4); border-top: 1px solid var(--border); }
.completed-tasks summary { min-height: 44px; padding: var(--space-4) 0 var(--space-2); color: var(--text-muted); cursor: pointer; font-size: var(--text-xs); font-weight: var(--weight-bold); }
.dashboard-toolbar { display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); }
.dashboard-toolbar p { margin: 0; color: var(--text-muted); font-size: var(--text-xs); }
.dashboard-widget-grid { display: grid; gap: var(--space-4); }
/* Erstzustand: der Weg zur ersten Buchung als Fortschritt. Keine Knoepfe —
   die Willkommenskarte traegt die einzige Handlung. */
.onboarding-steps { display: grid; margin: 0; padding: 0; gap: var(--space-1); list-style: none; }
.onboarding-steps li { display: grid; min-height: 44px; grid-template-columns: 28px minmax(0, 1fr) auto; align-items: center; gap: var(--space-3); color: var(--text-muted); font-size: var(--text-sm); }
.onboarding-steps li.current { color: var(--text); font-weight: var(--weight-bold); }
.onboarding-marker { display: grid; width: 28px; height: 28px; place-items: center; border: 1px solid var(--border-control); border-radius: var(--radius-round); font-size: var(--text-xs); font-weight: var(--weight-bold); }
.onboarding-steps li.current .onboarding-marker { border-color: var(--accent); background: var(--accent); color: var(--on-accent); }
.onboarding-steps li.done .onboarding-marker { border-color: var(--positive); color: var(--positive); }
.onboarding-state { color: var(--text-muted); font-size: var(--text-xs); font-weight: var(--weight-medium); }
.dashboard-widget { display: grid; min-width: 0; gap: var(--space-2); cursor: grab; }
.dashboard-widget:active { cursor: grabbing; }
/* Tastaturweg zum Umsortieren. Eigene Zeile ueber der Kachel statt schwebend
   in der Ecke: die Kacheln haben keine gemeinsame Kopfzeile — ueber dem
   Kennzahlenraster laegen die Knoepfe sonst auf der vierten Karte. */
.widget-reorder { display: flex; justify-self: end; gap: var(--space-1); }
/* Groesse bleibt bei den 44px aus .icon-button.compact — das ist die
   Touch-Zielgroesse, die die App ueberall haelt. */
.widget-reorder .icon-button { background: var(--surface-muted); }
.widget-reorder .icon-button[disabled] { color: var(--text-muted); cursor: not-allowed; opacity: .45; }

.widget-picker { display: grid; gap: var(--space-1); }
.widget-picker-row { display: flex; justify-content: space-between; }
.widget-picker-row label { display: flex; min-height: 44px; align-items: center; gap: var(--space-2); font-size: var(--text-sm); font-weight: var(--weight-bold); }
.widget-picker-row input { width: 18px; height: 18px; accent-color: var(--accent); }
/* --- Diagramme -------------------------------------------------------------
   Duenne Marken: 2px Linien, 8px Punkte, 4px gerundete Datenenden, 2px Fuge.
   Achsen und Gitternetz liegen im --border-Ton, damit die Daten vorn stehen.
   Jede Zahl im Diagramm traegt eine Textfarbe, nie die Reihenfarbe. */
.chart { display: grid; margin: 0; gap: var(--space-3); }
.line-chart { margin: 0; }
.chart-plot { position: relative; }
.line-chart svg { display: block; width: 100%; height: 220px; overflow: visible; }
.chart-line { fill: none; stroke: var(--accent-ink); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; vector-effect: non-scaling-stroke; }
/* Punkte als Pfad mit Nulllaengen-Segmenten: mit runder Kappe und
   non-scaling-stroke bleibt der Durchmesser exakt 8px, waehrend ein <circle>
   im verzerrt skalierten viewBox zur Ellipse wuerde. */
.chart-points { fill: none; stroke: var(--accent-ink); stroke-width: 8; stroke-linecap: round; vector-effect: non-scaling-stroke; }
.chart-grid line, .chart-axis { stroke: var(--border); stroke-width: 1; vector-effect: non-scaling-stroke; }
.chart-zero { stroke: var(--border); stroke-width: 1; stroke-dasharray: 3 3; vector-effect: non-scaling-stroke; }
.chart-annotations { position: absolute; inset: 0; pointer-events: none; }
.chart-annotation {
  position: absolute;
  padding: 0 var(--space-1);
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--surface) 88%, transparent);
  color: var(--text);
  font-size: var(--text-xs);
  font-weight: var(--weight-bold);
  white-space: nowrap;
  transform: translate(-50%, -140%);
}
.chart-annotation.start { transform: translate(0, -140%); }
.chart-annotation.end { transform: translate(-100%, -140%); }
.chart-legend { display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-4); margin: 0; padding: 0; list-style: none; }
.chart-legend li { display: flex; align-items: center; gap: var(--space-2); color: var(--text); font-size: var(--text-xs); font-weight: var(--weight-bold); }
.chart-legend i { width: 9px; height: 9px; border-radius: var(--radius-round); background: var(--legend); }
.line-chart figcaption { display: flex; justify-content: space-between; gap: var(--space-1); margin-top: var(--space-2); color: var(--text-muted); font-size: var(--text-xs); text-transform: uppercase; }
/* height: auto — die globale svg-Regel (20px) schlaegt sonst das
   height-Attribut und alle Balken ab dem ersten laegen ausserhalb des
   Layoutkastens ueber der Tabellenansicht. */
.bar-chart svg { display: block; width: 100%; height: auto; overflow: visible; }
/* Die globale svg-Regel setzt stroke: currentColor und stroke-width: 1.8.
   Geerbt bekaeme jeder Balken eine Kontur und jede Zahl eine zweite Strichbreite. */
.bar-chart rect, .bar-chart text { stroke: none; }
.chart-bar-name { fill: var(--text); font-size: var(--text-sm); }
.chart-bar-value { fill: var(--text); font-size: var(--text-sm); font-weight: var(--weight-bold); }
/* Tabellenansicht: gleiche Zahlen, ohne jede Farbunterscheidung. */
.chart-table { margin-top: var(--space-2); border-top: 1px solid var(--border); }
.chart-table summary { min-height: 44px; display: flex; align-items: center; gap: var(--space-2); color: var(--text-muted); cursor: pointer; font-size: var(--text-xs); font-weight: var(--weight-bold); }
.chart-table table { width: 100%; border-collapse: collapse; }
.chart-table th, .chart-table td { padding: var(--space-2) 0; border-bottom: 1px solid var(--border); color: var(--text); font-size: var(--text-xs); text-align: left; }
.chart-table thead th { color: var(--text-muted); font-weight: var(--weight-medium); }
.chart-table .numeric { text-align: right; font-variant-numeric: tabular-nums; }
.donut-chart { display: grid; grid-template-columns: minmax(160px, 220px) minmax(0, 1fr); align-items: center; gap: var(--space-6); margin: 0; }
.donut-chart .chart-table { grid-column: 1 / -1; }
.donut-graphic { display: grid; width: min(100%, 220px); aspect-ratio: 1; place-items: center; border-radius: var(--radius-round); background: conic-gradient(var(--donut)); }
.donut-graphic::before { width: 67%; aspect-ratio: 1; border-radius: var(--radius-round); background: var(--surface); content: ""; grid-area: 1 / 1; }
.donut-graphic span { z-index: 1; grid-area: 1 / 1; font-size: var(--text-md); font-weight: var(--weight-bold); letter-spacing: var(--tracking-tight); }
.donut-chart figcaption { display: grid; gap: var(--space-3); }
.donut-chart figcaption span { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: center; gap: var(--space-2); color: var(--text-muted); font-size: var(--text-xs); }
.donut-chart figcaption i { width: 9px; height: 9px; border-radius: var(--radius-round); background: var(--legend); }
.donut-chart figcaption strong { color: var(--text); font-size: var(--text-xs); }
.global-search { display: grid; gap: var(--space-4); }
.search-input { display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: center; gap: var(--space-2); padding: 0 var(--space-3); border: 1px solid var(--border-control); border-radius: var(--radius-md); background: var(--surface-muted); }
.search-input:focus-within { border-color: var(--accent-strong); }
.search-input svg { width: 20px; height: 20px; color: var(--text-muted); }
/* `outline: 0` entfernt: der Rahmenwechsel am Elternteil ersetzt den Fokusring nicht. */
.search-input input { min-height: 48px; min-width: 0; border: 0; background: transparent; color: var(--text); font-size: var(--text-md); }

.global-search-results { display: grid; max-height: min(52vh, 460px); overflow-y: auto; }
.global-search-result { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; }
.global-search-result:last-child { border-bottom: 0; }
.global-search-result > span:nth-child(2) { display: grid; gap: var(--space-1); min-width: 0; }
.global-search-result strong, .global-search-result small { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.global-search-result small { color: var(--text-muted); font-size: var(--text-xs); }
.global-search-result svg { width: 18px; height: 18px; color: var(--text-muted); }
.confirmation-check { display: flex; align-items: flex-start; gap: var(--space-2); color: var(--text-muted); font-size: var(--text-xs); line-height: var(--leading-snug); }
.confirmation-check input { width: 18px; height: 18px; flex: 0 0 auto; accent-color: var(--accent); }
button:disabled { cursor: wait; opacity: .65; }
/* Der Ladehinweis stellt Kreisel und Text nebeneinander, die Fehlermeldung
   Text und Knopf untereinander. */
.route-loader { grid-auto-flow: column; justify-content: center; gap: var(--space-3); }
.route-error { gap: var(--space-3); }

::view-transition-old(root) { animation: .16s ease both fade-out; }
::view-transition-new(root) { animation: .24s ease both fade-in; }
@keyframes fade-out { to { opacity: 0; transform: translateY(-3px); } }
@keyframes fade-in { from { opacity: 0; transform: translateY(4px); } }
@media (prefers-reduced-motion: reduce) {
  ::view-transition-old(root), ::view-transition-new(root) { animation: none; }
}

/* Kennzeichnet die Person, die zum angemeldeten Konto gehört. Sie beantwortet
   „wer bin ich" — ohne sie kann der Ausgabendialog nichts vorbelegen. */
.person-badge {
  display: inline-block;
  margin-left: var(--space-2);
  padding: 0 var(--space-2);
  border-radius: var(--radius-pill);
  background: var(--accent-soft);
  color: var(--accent-ink);
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  vertical-align: middle;
}

/* Filter der Buchungsliste. Als aufklappbarer Block, damit sie sichtbar
   bleiben, während man die Liste darunter liest — ein Dialog verdeckte genau
   das, was man gerade einstellt. */
.expenses-filters {
  padding: var(--space-4);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface);
}
.expenses-filters summary {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-height: 44px;
  color: var(--accent-ink);
  cursor: pointer;
  font-weight: var(--weight-bold);
}
.expenses-filters[open] summary { margin-bottom: var(--space-3); }
/* Volle Breite der Karte, aber jede Spalte hoert bei 20rem auf. Ohne die
   Obergrenze bekam „Person" am 1280px-Bildschirm ein 600px breites Auswahlfeld
   fuer das Wort „Alle". */
.expenses-filters .filter-row {
  margin-bottom: var(--space-3);
  margin-left: 0;
  max-width: none;
  grid-template-columns: repeat(auto-fit, minmax(12rem, 20rem));
}
.expenses-filters .filter-row:last-of-type { margin-bottom: 0; }

/* Ein Monat, der noch nicht angebrochen ist. Die Buchungen darin sind erfasst,
   aber noch nicht geschehen — der linke Streifen sagt das, ohne dass die Zeilen
   anders aussehen müssten. */
.expense-month-section.is-future .transaction-list {
  border-left: 3px solid var(--accent-strong);
  padding-left: var(--space-3);
}
.expense-month-heading .badge { margin-left: var(--space-2); }

/* Ziel eines Suchtreffers. Kurz hervorgehoben, dann wieder normal: es ist ein
   Hinweis, kein Zustand. */
.transaction-row.is-treffer {
  border-radius: var(--radius-md);
  box-shadow: 0 0 0 2px var(--accent-strong);
}

/* --- Prognose ---------------------------------------------------------------
   Zwoelf Monate mal sechs Spalten passen auf kein Telefon. Statt die Tabelle
   umzubauen bekommt sie einen eigenen Rollbereich: eine Tabelle bleibt eine
   Tabelle, und der Monat bleibt beim Rollen stehen. */
.table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; margin-inline: calc(var(--space-4) * -1); padding-inline: var(--space-4); }
.forecast-table { width: 100%; min-width: 34rem; border-collapse: collapse; font-size: var(--text-sm); }
.forecast-table th, .forecast-table td { padding: var(--space-2) var(--space-3); text-align: left; white-space: nowrap; }
.forecast-table thead th { font-size: var(--text-xs); font-weight: var(--weight-bold); color: var(--text-muted); border-bottom: 1px solid var(--border); }
.forecast-table td.num, .forecast-table thead th:not(:first-child) { text-align: right; }
.forecast-table tbody tr + tr th, .forecast-table tbody tr + tr td { border-top: 1px solid var(--border); }
/* Die erste Spalte bleibt beim seitlichen Rollen stehen — ohne sie weiss man
   nach zwei Spalten nicht mehr, welcher Monat gemeint ist. */
.forecast-table tbody th { position: sticky; left: 0; background: var(--surface); font-weight: var(--weight-bold); }
.forecast-source { display: inline-block; margin-left: var(--space-1); padding: 0 var(--space-1); border-radius: var(--radius-sm); background: var(--surface-muted); color: var(--text-muted); font-size: var(--text-xs); }
.forecast-note { display: block; font-size: var(--text-xs); color: var(--text-muted); white-space: nowrap; }
/* Ein Monat, den die Ruecklage nicht traegt, ist der einzige, der eine Farbe
   im Hintergrund bekommt. Bekaeme ihn auch der gedeckte, waere die Tabelle
   bunt und die Warnung nichts wert. */
.forecast-row.forecast-eng th, .forecast-row.forecast-eng td { background: color-mix(in srgb, var(--negative) 8%, var(--surface)); }
.forecast-warning { margin: 0 0 var(--space-3); padding: var(--space-3); border-radius: var(--radius-md); background: color-mix(in srgb, var(--negative) 10%, var(--surface)); color: var(--text); font-weight: var(--weight-bold); }

/* Unter 720px wird aus der Tabelle eine Karte je Monat.
   Seitliches Rollen war die erste Fassung, und sie war falsch: die wichtigste
   Spalte — der Cashflow — lag ausserhalb des Bildes, und die Zeile, auf die es
   ankommt, war die einzige, die man nicht sah. Die Auszeichnung bleibt eine
   Tabelle (Kopfzeilen, Zeilenbezug, Vorlesbarkeit); nur die Darstellung
   wechselt. Die Beschriftung je Zelle kommt aus `data-label`. */
@media (max-width: 720px) {
  .table-scroll { overflow-x: visible; margin-inline: 0; padding-inline: 0; }
  .forecast-table { min-width: 0; display: block; }
  .forecast-table thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
  .forecast-table tbody { display: grid; gap: var(--space-3); }
  .forecast-table tbody tr {
    display: grid;
    grid-template-columns: 1fr auto;
    gap: var(--space-1) var(--space-3);
    padding: var(--space-3);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    background: var(--surface);
  }
  .forecast-table tbody tr + tr th, .forecast-table tbody tr + tr td { border-top: 0; }
  .forecast-table tbody th {
    position: static;
    grid-column: 1 / -1;
    padding: 0 0 var(--space-1);
    font-size: var(--text-md);
    border-bottom: 1px solid var(--border);
  }
  /* Wert und Kennzeichen stehen in EINEM Kind, sonst legt das Gitter das
     Kennzeichen in eine eigene Zeile und es sieht aus wie ein Balken. */
  .forecast-table tbody td { display: grid; grid-column: 1 / -1; grid-template-columns: 1fr auto; align-items: baseline; gap: var(--space-3); padding: 0; text-align: right; }
  .forecast-table tbody td .forecast-value { display: inline; }
  .forecast-table tbody td::before { content: attr(data-label); color: var(--text-muted); font-size: var(--text-sm); text-align: left; }
  /* Der Cashflow ist die Zeile, wegen der man hinsieht. */
  .forecast-table tbody td.forecast-cashflow { margin-top: var(--space-1); padding-top: var(--space-2); border-top: 1px solid var(--border); font-size: var(--text-md); }
  .forecast-note { grid-column: 1 / -1; text-align: left; white-space: normal; }
}

/* --- Gruppenabrechnung -------------------------------------------------------
   Je Gruppe eine Karte, nicht eine Zeile: eine Gruppe traegt eine Summe, einen
   Ausgleich und eine Aktion — das sind drei Dinge, und drei Dinge passen nicht
   in eine Datenzeile, ohne dass eines davon zum Anhaengsel wird. */
.group-list { display: grid; gap: var(--space-4); }
.group-card { display: grid; gap: var(--space-3); padding: var(--space-4); }
.group-head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-3); flex-wrap: wrap; }
.group-head h4 { margin: 0; font-size: var(--text-lg); }
.group-head p { margin: var(--space-1) 0 0; font-size: var(--text-sm); }
/* Die Gesamtsumme ist die Zahl, wegen der man auf die Karte sieht. */
.group-total { margin: 0; font-size: var(--text-xl); font-weight: var(--weight-bold); white-space: nowrap; }
.group-state { margin: 0; font-size: var(--text-sm); }
/* NICHT `.data-row` wiederverwendet. Dort steht `.row-actions` per Regel in
   einer eigenen Gitterzeile ueber die volle Breite — in der Monatsabrechnung
   richtig, hier nicht: Name und Betrag standen dadurch mit achtzig Pixeln
   Leerraum dazwischen, und die Karte sah aus wie ein Fehler. */
.group-transfers { display: grid; }
.group-transfer { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); flex-wrap: wrap; padding: var(--space-2) 0; border-bottom: 1px solid var(--border); }
.group-transfer:last-child { border-bottom: 0; }
.group-transfer .row-actions { align-items: center; }
.secondary-button.compact { height: 44px; padding: 0 var(--space-3); font-size: var(--text-sm); }
.group-payments summary { cursor: pointer; font-size: var(--text-sm); color: var(--text-muted); min-height: 44px; display: flex; align-items: center; }
.group-payments[open] summary { margin-bottom: var(--space-2); }
