/* ============================================================
   BarsWeb - handgeschriebenes CSS, kein Framework.
   Light/Dark Mode über CSS-Variablen: das data-theme-Attribut auf
   <html> wird vor dem Rendern per Inline-Script im Layout gesetzt.
   Regel: im restlichen CSS NIE feste Farbwerte, immer var(--...).
   ============================================================ */

:root,
:root[data-theme="light"] {
  color-scheme: light;

  --paper: #eef2f3;
  --surface: #ffffff;
  --surface-2: #e5eaec;
  --ink: #16232a;
  --ink-muted: #566169;
  --ink-faint: #8b979d;
  --line: rgba(22, 35, 42, 0.12);
  --line-strong: rgba(22, 35, 42, 0.22);

  --accent: #2f66e0;
  --accent-hover: #2454c8;
  --accent-strong: rgba(47, 102, 224, 0.28);
  --accent-ink: #ffffff;
  /* Fläche gefüllter Primär-Buttons: weiße Schrift braucht 4.5:1. Im Hellen
     reicht --accent, im Dunkeln ist --accent für Flächen zu hell. */
  --accent-fill: #2f66e0;
  --accent-fill-hover: #2454c8;
  --accent-2: #2f6e68;
  --card-glow: rgba(47, 102, 224, 0.10);
  /* Logo-Karten (fragments/logo.html); die vordere Karte trägt --accent.
     logo-intro.js liest diese Werte auch für das 3D-Intro der Anmeldeseite. */
  --logo-tile: #ffffff;
  --logo-back: #acc2f3;
  --logo-mid: #7297ea;

  --success: #2e7d5b;
  --success-bg: #e3f3ea;
  --danger: #b3452f;
  --danger-bg: #fbeae4;
  --warning: #8a5a00;
  --warning-bg: #fbeed3;

  --role-admin: #6a4bd4;
  --role-admin-bg: #ece7fa;
  --role-friend: #2f6e68;
  --role-friend-bg: #e2f0ee;
  --role-public: #5f6f6f;
  --role-public-bg: #e8edec;
  /* Topic Hub: nur Pill-Rand in der Leiste und Icon in der Schublade. Bewusst
     derselbe Ton wie --role-admin (so gewählt); nie Knöpfe, Schalter oder
     Zustände, die bleiben blau (One Blue Rule in DESIGN.md). */
  --hub: #6a4bd4;

  /* Zuschnitt des Profilfotos: Abdunklung außerhalb des Kreises und dessen
     Rand. Liegen über einem Foto, darum in beiden Themes gleich (die
     Dark-Blöcke überschreiben sie nicht). */
  --crop-scrim: rgba(6, 12, 14, 0.6);
  --crop-ring: rgba(255, 255, 255, 0.85);

  /* Diagramm-Farben (Sub Tracker). Feste Slot-Reihenfolge, CVD-geprüft gegen
     die Kartenfläche (validate_palette: PASS auf #ffffff und #152224).
     Slot 0 = neutrales Grau, reserviert für "Sonstiges". */
  --chart-0: #7c8a8e;
  --chart-1: #2a78d6;
  --chart-2: #1baf7a;
  --chart-3: #eda100;
  --chart-4: #008300;
  --chart-5: #4a3aa7;
  --chart-6: #e34948;
  --chart-7: #e87ba4;
  --chart-8: #eb6834;

  --shadow: 0 1px 2px rgba(22, 35, 42, 0.06), 0 8px 24px rgba(22, 35, 42, 0.07);
  /* Flache Variante für kleine Flächen (To-Do-Karten, Segment-Pille). */
  --shadow-sm: 0 1px 2px rgba(22, 35, 42, 0.08);
  --radius: 14px;

  --font-display: -apple-system, BlinkMacSystemFont, "SF Pro Display", system-ui, "Segoe UI", Roboto, sans-serif;
  --font-body: -apple-system, BlinkMacSystemFont, "SF Pro Text", system-ui, "Segoe UI", Roboto, sans-serif;
  --font-mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;

  /* Heatmap der Markets-Übersicht: neun Stufen (-4 bis +4) aus den
     Status-Tokens gemischt. Stufe 1 bis 3 bleibt hell genug für Tinte,
     Stufe 4 ist tief und trägt weiße Schrift (gemessen mindestens 5:1).
     --heat-glint ist der Lichtstreif, der bei einem Tick über die Kachel
     läuft (wie der Lichtstreif der Primärknöpfe). */
  --heat-flat: var(--surface-2);
  --heat-up-1: color-mix(in oklab, var(--success) 16%, var(--surface-2));
  --heat-up-2: color-mix(in oklab, var(--success) 34%, var(--surface-2));
  --heat-up-3: color-mix(in oklab, var(--success) 56%, var(--surface-2));
  --heat-up-4: color-mix(in oklab, var(--success) 82%, var(--ink));
  --heat-down-1: color-mix(in oklab, var(--danger) 14%, var(--surface-2));
  --heat-down-2: color-mix(in oklab, var(--danger) 30%, var(--surface-2));
  --heat-down-3: color-mix(in oklab, var(--danger) 52%, var(--surface-2));
  --heat-down-4: color-mix(in oklab, var(--danger) 84%, var(--ink));
  --heat-ink: var(--ink);
  --heat-ink-strong: #ffffff;
  --heat-glint: rgba(255, 255, 255, 0.6);

  /* Budget Flow (Sankey): Bänder mischen die Slot-Farbe der Kategorie zu
     --bf-mix (gezeigt --bf-mix-on) in die Fläche. Im Hellen die Farbe pur
     (--bf-tint 100 %), im Dunkeln erst mit heller Tinte aufgehellt: nur zur
     Fläche hin gemischt zögen die Bänder ins Trübe (Khaki statt Bernstein,
     Weinrot statt Rosa). --bf-income ist der Schieferton der Einnahmen,
     --bf-shine der Lichtstreif entlang einer Spur, --bf-drop der Schatten des
     gezeigten Knotens. */
  --bf-tint: 100%;
  --bf-mix: 34%;
  --bf-mix-on: 72%;
  --bf-income: color-mix(in srgb, var(--ink) 11%, transparent);
  --bf-shine: var(--surface);
  --bf-shine-opacity: 0.7;
  --bf-drop: color-mix(in srgb, var(--ink) 26%, transparent);
}

@media (prefers-color-scheme: dark) {
  :root {
    color-scheme: dark;

    --paper: #0d1618;
    --surface: #152224;
    --surface-2: #1c2c2e;
    --ink: #e9f0f0;
    --ink-muted: #93a3a5;
    --ink-faint: #6f8082;
    --line: rgba(233, 240, 240, 0.12);
    --line-strong: rgba(233, 240, 240, 0.2);

    --accent: #5b8cf5;
    --accent-hover: #6f9bff;
    --accent-strong: rgba(91, 140, 245, 0.32);
    --accent-ink: #ffffff;
    --accent-fill: #3a6ce0;       /* weiß darauf 4.8:1 */
    --accent-fill-hover: #3363d4; /* weiß darauf 5.4:1 */
    --accent-2: #57b0a6;
    --card-glow: rgba(150, 180, 255, 0.08);
    --logo-tile: #16232a;
    --logo-back: #3a4d7b;
    --logo-mid: #456ab2;

    --success: #57c08a;
    --success-bg: #163329;
    --danger: #e38468;
    --danger-bg: #3a2018;
    --warning: #f0c05a;
    --warning-bg: #3b2d12;

    --role-admin: #a892e8;
    --role-admin-bg: #231f36;
    --role-friend: #57b0a6;
    --role-friend-bg: #16302d;
    --role-public: #9bacac;
    --role-public-bg: #1c2a2b;
    --hub: #a892e8;

    --shadow: 0 1px 2px rgba(0, 0, 0, 0.3), 0 12px 28px rgba(0, 0, 0, 0.35);
    --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3);

    /* Heatmap im Dunkeln: Stufe 1 bis 3 dunkel getönt mit heller Tinte,
       Stufe 4 hell mit Papier-Schrift. */
    --heat-up-1: color-mix(in oklab, var(--success) 18%, var(--surface-2));
    --heat-up-2: color-mix(in oklab, var(--success) 32%, var(--surface-2));
    --heat-up-3: color-mix(in oklab, var(--success) 48%, var(--surface-2));
    --heat-up-4: color-mix(in oklab, var(--success) 88%, var(--surface-2));
    --heat-down-1: color-mix(in oklab, var(--danger) 18%, var(--surface-2));
    --heat-down-2: color-mix(in oklab, var(--danger) 32%, var(--surface-2));
    --heat-down-3: color-mix(in oklab, var(--danger) 48%, var(--surface-2));
    --heat-down-4: color-mix(in oklab, var(--danger) 88%, var(--surface-2));
    --heat-ink-strong: #0d1618;
    --heat-glint: rgba(255, 255, 255, 0.2);

    --bf-tint: 64%;
    --bf-mix: 74%;
    --bf-mix-on: 96%;
    --bf-income: color-mix(in srgb, var(--ink) 13%, transparent);
    --bf-shine: var(--ink);
    --bf-shine-opacity: 0.32;
    --bf-drop: color-mix(in srgb, var(--paper) 90%, transparent);
  }
}

:root[data-theme="dark"] {
  color-scheme: dark;

  --paper: #0d1618;
  --surface: #152224;
  --surface-2: #1c2c2e;
  --ink: #e9f0f0;
  --ink-muted: #93a3a5;
  --ink-faint: #6f8082;
  --line: rgba(233, 240, 240, 0.12);
  --line-strong: rgba(233, 240, 240, 0.2);

  --accent: #5b8cf5;
  --accent-hover: #6f9bff;
  --accent-strong: rgba(91, 140, 245, 0.32);
  --accent-ink: #ffffff;
  --accent-fill: #3a6ce0;
  --accent-fill-hover: #3363d4;
  --accent-2: #57b0a6;
  --card-glow: rgba(150, 180, 255, 0.08);
  --logo-tile: #16232a;
  --logo-back: #3a4d7b;
  --logo-mid: #456ab2;

  --success: #57c08a;
  --success-bg: #163329;
  --danger: #e38468;
  --danger-bg: #3a2018;
  --warning: #f0c05a;
  --warning-bg: #3b2d12;

  --role-admin: #a892e8;
  --role-admin-bg: #231f36;
  --role-friend: #57b0a6;
  --role-friend-bg: #16302d;
  --role-public: #9bacac;
  --role-public-bg: #1c2a2b;
  --hub: #a892e8;

  /* Diagramm-Farben: gleiche Hues, für die dunkle Fläche gestuft (validiert). */
  --chart-0: #6d7a7d;
  --chart-1: #3987e5;
  --chart-2: #199e70;
  --chart-3: #c98500;
  --chart-4: #008300;
  --chart-5: #9085e9;
  --chart-6: #e66767;
  --chart-7: #d55181;
  --chart-8: #d95926;

  --shadow: 0 1px 2px rgba(0, 0, 0, 0.3), 0 12px 28px rgba(0, 0, 0, 0.35);
  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3);

  --heat-up-1: color-mix(in oklab, var(--success) 18%, var(--surface-2));
  --heat-up-2: color-mix(in oklab, var(--success) 32%, var(--surface-2));
  --heat-up-3: color-mix(in oklab, var(--success) 48%, var(--surface-2));
  --heat-up-4: color-mix(in oklab, var(--success) 88%, var(--surface-2));
  --heat-down-1: color-mix(in oklab, var(--danger) 18%, var(--surface-2));
  --heat-down-2: color-mix(in oklab, var(--danger) 32%, var(--surface-2));
  --heat-down-3: color-mix(in oklab, var(--danger) 48%, var(--surface-2));
  --heat-down-4: color-mix(in oklab, var(--danger) 88%, var(--surface-2));
  --heat-ink-strong: #0d1618;
  --heat-glint: rgba(255, 255, 255, 0.2);

  --bf-tint: 64%;
  --bf-mix: 74%;
  --bf-mix-on: 96%;
  --bf-income: color-mix(in srgb, var(--ink) 13%, transparent);
  --bf-shine: var(--ink);
  --bf-shine-opacity: 0.32;
  --bf-drop: color-mix(in srgb, var(--paper) 90%, transparent);
}

/* ---------- Bewegungs-Tokens (unabhängig vom Theme) ----------
   Grundidee "Kartenstapel": Die Bildmarke sind drei Karten, jede einen
   Schritt nach rechts oben hinter der vorderen. Neues rückt vorne auf den
   Stapel (kommt von links unten), Altes tritt einen Schritt nach rechts oben
   zurück. Die Regeln dazu stehen im Abschnitt "Bewegung" am Ende der Datei.
   Reduzierte Bewegung: alle Wege multiplizieren mit --motion-travel (0 bei
   prefers-reduced-motion); Blenden, Farben und Zahlen bleiben. ui.js liest
   Dauern und --motion-travel hier aus, statt sie zu wiederholen. */
:root {
  /* Hauskurve: steht an vielen älteren Stellen noch als Literal. */
  --ease-out: cubic-bezier(.2, .8, .2, 1);
  /* Ankommen in den Glanzmomenten: kräftig abbremsen, ruhig landen. */
  --ease-arrive: cubic-bezier(.16, 1, .3, 1);
  /* Gehen beschleunigt und ist kürzer als Kommen. */
  --ease-leave: cubic-bezier(.4, 0, 1, 1);
  /* Umbauten (Karten zu Tabelle, Ring, Ziffern): symmetrisch. */
  --ease-swap: cubic-bezier(.65, 0, .35, 1);

  --dur-feedback: 120ms;  /* Druck, Häkchen, kleine Bestätigung */
  --dur-state: 220ms;     /* Zustandswechsel, Abgänge */
  --dur-layout: 340ms;    /* Seiten, Dialoge, Umbauten */
  --dur-focal: 620ms;     /* die Glanzmomente */

  /* Ein Stapelschritt der Bildmarke (8 von 64 Einheiten) als Weg. */
  --stack-step: 10px;
  --motion-travel: 1;
  --step: calc(var(--stack-step) * var(--motion-travel));
  --step-scale: calc(1 - 0.03 * var(--motion-travel));
}
@media (prefers-reduced-motion: reduce) {
  :root { --motion-travel: 0; }
}

/* ---------- Basis ---------- */

* { box-sizing: border-box; }

/* hidden-Attribut gewinnt auch gegen display:grid/flex aus Klassenregeln
   (Karten/Tabellen-Umschalter der Markets-Seite). */
[hidden] { display: none !important; }

html, body { height: 100%; }

/* Globale Dichte: kleinere Wurzelgröße skaliert alle rem-Maße gleichmäßig
   herunter, damit mehr Inhalt auf die Seiten passt (14px statt 16px). */
html { font-size: 87.5%; }

body {
  margin: 0;
  font-family: var(--font-body);
  font-size: 1rem;
  line-height: 1.55;
  background: var(--paper);
  color: var(--ink);
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3 {
  font-family: var(--font-display);
  text-wrap: balance;
  letter-spacing: -0.015em;
  margin: 0 0 0.6rem;
}
h1 { font-size: 1.6rem; font-weight: 700; }
h2 { font-size: 1.2rem; font-weight: 700; }
h3 { font-size: 1.05rem; font-weight: 700; }

a { color: var(--accent); }

code, .mono {
  font-family: var(--font-mono);
  font-size: 0.92em;
}

.muted { color: var(--ink-muted); }

/* Nur für Screenreader: unsichtbar, aber vorgelesen (z. B. "2 subscriptions"
   hinter einer sichtbaren nackten Zahl). */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

.eyebrow {
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--accent-2);
  margin: 0 0 0.6rem;
}

.container {
  /* 76rem bei kleinerer Wurzelgröße entspricht der alten physischen Breite. */
  max-width: 76rem;
  margin: 0 auto;
  padding: 2rem 1.5rem 4rem;
}

/* ---------- Navigation ---------- */

.navbar {
  background: var(--surface);
  border-bottom: 1px solid var(--line);
  position: sticky;
  top: 0;
  z-index: 10;
}
.navbar-inner {
  /* Bewusst NICHT auf die Inhaltsbreite begrenzt: die Leiste nutzt die volle
     Fensterbreite - Brand ganz links, Buttons ganz rechts, maximaler Platz
     für Topics dazwischen. Der Seiteninhalt (.container) bleibt bei 68rem. */
  max-width: none;
  padding: 0.7rem 1.5rem;
  display: flex;
  align-items: center;
  gap: 1.5rem;
  /* Alles bleibt in EINER Zeile; bei Platzmangel scrollen die Links
     horizontal statt über die rechten Buttons umzubrechen. */
  flex-wrap: nowrap;
}

/* Scroll-Zone um die Links (Lösung A): Fades und Pfeile erscheinen nur,
   wenn wirklich etwas verdeckt ist (Klassen can-left/can-right aus ui.js).
   Ohne JS bleiben sie unsichtbar und die Liste scrollt nativ. */
.nav-scroll {
  position: relative;
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
}
.nav-scroll-fade {
  position: absolute;
  top: 0; bottom: 0;
  width: 2.2rem;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.2s ease;
  z-index: 1;
}
.nav-scroll-fade.left { left: 0; background: linear-gradient(90deg, var(--surface), transparent); }
.nav-scroll-fade.right { right: 0; background: linear-gradient(270deg, var(--surface), transparent); }
.nav-scroll.can-left .nav-scroll-fade.left,
.nav-scroll.can-right .nav-scroll-fade.right { opacity: 1; }
.nav-scroll-arrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 1.5rem; height: 1.5rem;
  padding: 0;
  border-radius: 999px;
  border: 1px solid var(--line-strong);
  background: var(--surface);
  color: var(--ink);
  display: grid;
  place-items: center;
  font-size: 0.72rem;
  line-height: 1;
  cursor: pointer;
  opacity: 0;
  /* visibility zusaetzlich zu opacity: ein unsichtbarer Pfeil darf nicht in
     der Tab-Reihenfolge stehen. */
  visibility: hidden;
  pointer-events: none;
  transition: opacity 0.2s ease, background 0.2s ease, visibility 0.2s;
  z-index: 2;
}
.nav-scroll-arrow:hover { background: var(--surface-2); }
.nav-scroll-arrow.left { left: 0; }
.nav-scroll-arrow.right { right: 0; }
.nav-scroll.can-left .nav-scroll-arrow.left,
.nav-scroll.can-right .nav-scroll-arrow.right { opacity: 1; visibility: visible; pointer-events: auto; }
.brand {
  flex: none;
  display: flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.15rem;
  letter-spacing: -0.02em;
  text-decoration: none;
  color: var(--ink);
}

/* Logo-Kachel links neben dem Schriftzug (gleiche Grafik wie favicon.svg,
   inline eingebettet in fragments/nav.html). Kachelgrund folgt dem Theme,
   die vordere Karte nutzt die Akzentfarbe; die hinteren Karten sind
   vorberechnete Mischungen aus Akzent und Kachelgrund, damit Überlappungen
   keine Säume zeigen (Werte synchron zu favicon.svg halten). */
.brand-mark {
  width: 26px;
  height: 26px;
  display: block;
}

.logo-tile { fill: var(--logo-tile); }
.logo-card-back { fill: var(--logo-back); }
.logo-card-mid { fill: var(--logo-mid); }
.logo-card-front { fill: var(--accent); }
.nav-links {
  display: flex;
  align-items: center;
  gap: 1.4rem;
  list-style: none;
  /* Vertikale Luft INNERHALB des Scrollers: overflow-x schneidet sonst die
     Pill-Umrandungen (nav-link-admin) oben/unten ab. Die negative Margin
     gleicht die Höhe außen wieder aus - die Leiste bleibt gleich hoch. */
  margin: -0.3rem 0;
  padding: 0.3rem 0;
  flex: 1;
  min-width: 0;
  overflow-x: auto;
  scrollbar-width: none;
}
.nav-links::-webkit-scrollbar { display: none; }
.nav-links li { flex: none; }
/* Flex statt inline: Icon (topic-icon-sm) und Text stehen mittig auf einer
   Linie, egal ob das Icon ein SVG oder die BTC-Glyphe ist. */
.nav-link {
  position: relative;
  display: flex;
  align-items: center;
  gap: 0.4rem;
  text-decoration: none;
  font-size: 0.92rem;
  font-weight: 500;
  color: var(--ink-muted);
  padding: 0.3rem 0.05rem;
  white-space: nowrap;
  transition: color 0.2s ease;
}
/* Icons in der Leiste folgen der Linkfarbe (gedämpft, beim Hover hell), auch
   in den Pills; Farbe tragen dort nur die Rahmen. Im Topics- und Admin-Menü
   darunter bleiben sie blau bzw. rot. */
.nav-link .topic-icon-sm { color: inherit; }
.nav-link::after {
  content: "";
  position: absolute;
  left: 0; right: 100%;
  bottom: -1px;
  height: 2px;
  background: var(--accent);
  border-radius: 2px;
  transition: right 0.28s cubic-bezier(.2, .8, .2, 1);
}
.nav-link:hover { color: var(--ink); }
.nav-link:hover::after { right: 0; }
/* Aktuelle Seite (aria-current setzt ui.js): der Unterstrich bleibt stehen. */
.nav-link[aria-current="page"] { color: var(--ink); }
.nav-link[aria-current="page"]::after { right: 0; }

/* Pill-Einträge der Leiste: Topics blau, Topic Hub violett (--hub), Admin rot
   (passend zu .card-admin auf der Admin-Übersicht). Die Farbe kommt aus
   --nav-pill und sitzt nur am Rand. Feste Höhe wie die runden Knöpfe rechts
   (2.1rem), damit die Leiste nicht höher wird. */
.nav-link-pill {
  --nav-pill: var(--accent);
  height: 2.1rem;
  border: 1px solid color-mix(in srgb, var(--nav-pill) 75%, transparent);
  border-radius: 999px;
  padding: 0 0.75rem 0 0.6rem;
  transition: color 0.2s ease, border-color 0.2s ease;
}
.nav-link-topics { --nav-pill: var(--accent); }
.nav-link-hub { --nav-pill: var(--hub); }
.nav-link-admin { --nav-pill: var(--danger); }
.nav-link-pill:hover,
.nav-link-pill[aria-current="page"] { border-color: var(--nav-pill); }
.nav-link-pill[aria-current="page"] { color: var(--ink); }
/* Der Unterstrich-Effekt der normalen Links passt nicht in eine Pill. Folge:
   zwischen Topics-Seite und Topic gleitet der Unterstrich (View Transition
   nav-current) nicht mehr, er blendet ein und aus. */
.nav-link-pill::after { display: none; }

/* Angepinnte Einträge kommen ohne Neuladen dazu oder gehen (Topic Hub, ui.js
   syncNavBar): neu legt sich der Eintrag wie eine Karte auf den Stapel, ein
   weggefallener tritt einen Schritt nach rechts oben zurück und wird danach
   entfernt. Die Wege rechnen die Keyframes über --step, bei reduzierter
   Bewegung bleibt nur das Blenden. */
.nav-links > li.is-arriving {
  animation: stack-arrive var(--dur-layout) var(--ease-arrive) backwards;
}
.nav-links > li.is-leaving {
  animation: stack-leave var(--dur-state) var(--ease-leave) forwards;
  pointer-events: none;
}

.nav-right {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  margin-left: auto;
  /* Rechte Buttons (Theme, Glocke, Profil, Abmelden) geben nie Platz ab -
     bei Enge scrollen stattdessen die Links. */
  flex: none;
}
.user-menu { position: relative; }
/* Profil-Pill: Avatar-Kreis (1.6rem) links bündig, dann Name und Pfeil.
   Feste Höhe wie die runden Knöpfe daneben; das linke Padding zentriert den
   Kreis senkrecht wie waagerecht im Rund der Pill. */
.user-pill {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  height: 2.1rem;
  font-family: var(--font-body);
  font-size: 0.85rem;
  color: var(--ink-muted);
  padding: 0 0.7rem 0 calc((2.1rem - 2px - 1.6rem) / 2);
  border-radius: 999px;
  background: var(--surface-2);
  border: 1px solid transparent;
  cursor: pointer;
  transition: color 0.2s ease, border-color 0.2s ease;
}
.user-pill-name {
  max-width: 10rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.user-menu:hover .user-pill,
.user-menu:focus-within .user-pill {
  color: var(--ink);
  border-color: var(--line);
}
.user-caret { width: 0.7rem; height: 0.7rem; transition: transform 0.25s ease; }
.user-menu:hover .user-caret,
.user-menu:focus-within .user-caret { transform: rotate(180deg); }

.user-dropdown {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  min-width: 10rem;
  padding: 0.35rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 12px;
  box-shadow: var(--shadow);
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  opacity: 0;
  visibility: hidden;
  transform: translateY(-6px);
  transition: opacity 0.2s ease, transform 0.2s ease, visibility 0.2s;
  z-index: 20;
}
/* Unsichtbare Brücke über die 6px-Lücke, damit der Hover nicht abreißt. */
.user-dropdown::before {
  content: "";
  position: absolute;
  top: -6px; left: 0; right: 0;
  height: 6px;
}
.user-menu:hover .user-dropdown,
.user-menu:focus-within .user-dropdown {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}
.dropdown-item {
  display: block;
  padding: 0.5rem 0.7rem;
  border-radius: 8px;
  font-size: 0.9rem;
  color: var(--ink);
  text-decoration: none;
  transition: background 0.15s ease;
}
.dropdown-item:hover { background: var(--surface-2); }

/* Sichtbarer Tastaturfokus fuer die Bedienung von Leiste, Schublade und
   Seitenkopf: derselbe Ring wie bei .btn:focus-visible. */
.nav-link:focus-visible,
.user-pill:focus-visible,
.dropdown-item:focus-visible,
.inbox-drop-item:focus-visible,
.theme-toggle:focus-visible,
.inbox-bell:focus-visible,
.nav-scroll-arrow:focus-visible,
.back-button:focus-visible,
.nav-menu-toggle:focus-visible,
.drawer-close:focus-visible,
.drawer-link:focus-visible,
.view-toggle button:focus-visible,
.chip-btn:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* Runde Icon-Buttons in der Leiste: Theme-Umschalter und Postfach-Glocke. */
.theme-toggle,
.inbox-bell {
  width: 2.1rem; height: 2.1rem;
  flex: none;
  display: grid;
  place-items: center;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--ink);
  cursor: pointer;
  transition: background 0.2s ease, transform 0.2s ease, border-color 0.2s ease;
}
.theme-toggle:hover,
.inbox-bell:hover { background: var(--surface-2); transform: rotate(-8deg); }
.theme-toggle svg,
.inbox-bell svg { width: 1.05rem; height: 1.05rem; }

.inbox-bell { position: relative; text-decoration: none; }
/* Ungelesen-Zähler an der Glocke; der Ring in Leistenfarbe hebt ihn vom
   Button ab. --paper als Textfarbe hat in beiden Themes genug Kontrast. */
.inbox-badge {
  position: absolute;
  top: -0.35rem; right: -0.35rem;
  min-width: 1.05rem; height: 1.05rem;
  padding: 0 0.25rem;
  border-radius: 999px;
  background: var(--danger);
  color: var(--paper);
  font-size: 0.66rem;
  font-weight: 700;
  line-height: 1;
  display: grid;
  place-items: center;
  border: 2px solid var(--surface);
}

/* ---------- Buttons ---------- */

.btn {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 0.92rem;
  line-height: 1.2;
  border-radius: 10px;
  padding: 0.62rem 1.15rem;
  border: 1px solid transparent;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  text-decoration: none;
  transition: transform 0.18s cubic-bezier(.2, .8, .2, 1), box-shadow 0.25s ease,
              background 0.2s ease, border-color 0.2s ease, color 0.2s ease;
}
.btn:active { transform: translateY(1px) scale(0.98); }
.btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Gesperrt (z. B. solange eine Aktion unterwegs ist): sichtbar inaktiv, ohne Hover-Hub. */
.btn:disabled { opacity: 0.55; cursor: default; pointer-events: none; }

.btn-primary {
  background: var(--accent-fill);
  color: var(--accent-ink);
  box-shadow: 0 1px 2px rgba(22, 35, 42, 0.08);
}
.btn-primary:hover {
  background: var(--accent-fill-hover);
  transform: translateY(-2px);
  box-shadow: 0 10px 22px -6px var(--accent-strong);
}
/* Leichter Lichtstreif-Sweep beim Hover (nur Primär-Buttons). */
.btn-primary::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(115deg, transparent 35%, rgba(255, 255, 255, 0.22) 50%, transparent 65%);
  transform: translateX(-120%);
  pointer-events: none;
  z-index: 1;
  transition: transform 0s;
}
.btn-primary:hover::after {
  transform: translateX(120%);
  transition: transform 0.65s cubic-bezier(.2, .8, .2, 1);
}

.btn-ghost {
  background: transparent;
  color: var(--ink);
  border-color: var(--line-strong);
}
.btn-ghost:hover {
  background: var(--surface-2);
  transform: translateY(-1px);
}

.btn-danger {
  background: transparent;
  color: var(--danger);
  border-color: color-mix(in srgb, var(--danger) 45%, transparent);
}
.btn-danger:hover {
  background: var(--danger-bg);
  transform: translateY(-2px);
  box-shadow: 0 10px 20px -8px color-mix(in srgb, var(--danger) 40%, transparent);
}

.btn-sm { padding: 0.42rem 0.8rem; font-size: 0.82rem; }
.btn-block { width: 100%; }

/* ---------- Formulare ---------- */

label {
  display: block;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--ink-muted);
  margin: 0.7rem 0 0.3rem;
}

input[type="text"],
input[type="email"],
input[type="password"],
input[type="number"],
input[type="date"],
textarea,
select {
  font-family: var(--font-body);
  font-size: 0.95rem;
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--line-strong);
  border-radius: 8px;
  padding: 0.55rem 0.7rem;
  transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
input[type="text"],
input[type="email"],
input[type="password"],
input[type="number"],
input[type="date"],
textarea {
  width: 100%;
}
textarea { resize: vertical; }
input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-strong);
}

.checkbox-label {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  font-size: 0.9rem;
  font-weight: 500;
  color: var(--ink);
  margin-top: 0.9rem;
  cursor: pointer;
}
.checkbox-label input[type="checkbox"] {
  width: 1.1rem;
  height: 1.1rem;
  accent-color: var(--accent);
  cursor: pointer;
}

.field-error {
  color: var(--danger);
  font-size: 0.85rem;
  margin: 0.3rem 0 0;
}

/* ---------- Karten ---------- */

.card {
  position: relative;
  overflow: hidden;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1.3rem 1.4rem;
  box-shadow: var(--shadow);
  margin-bottom: 1rem;
}
.card-muted { opacity: 0.75; box-shadow: none; }

/* Nur-Admin-Karten: rote Umrandung als Erinnerung, dass andere Rollen
   diese Karte nicht sehen. */
.card-admin { border-color: color-mix(in srgb, var(--danger) 75%, transparent); }
.card-admin[data-glow]:hover { border-color: var(--danger); }

.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr));
  gap: 1.1rem;
}
.card-grid .card { margin-bottom: 0; }

/* Karten mit Cursor-Beleuchtung (data-glow, dezentes Licht folgt der Maus). */
.card[data-glow]::before {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(300px circle at var(--x, 50%) var(--y, 0%), var(--card-glow), transparent 60%);
  opacity: 0;
  transition: opacity 0.35s ease;
  pointer-events: none;
}
.card[data-glow] {
  transition: transform 0.25s cubic-bezier(.2, .8, .2, 1), border-color 0.25s ease, box-shadow 0.25s ease;
}
.card[data-glow]:hover {
  transform: translateY(-3px);
  border-color: var(--line-strong);
}
.card[data-glow]:hover::before { opacity: 1; }
.card > * { position: relative; z-index: 1; }
.card p { color: var(--ink-muted); }
.card .btn { margin-top: 0.9rem; }

/* Komplett klickbare Kacheln (Topics-Seite, "Dein Tag" der Startseite): die
   Karte selbst ist der Link. */
a.card-link {
  display: block;
  color: inherit;
  text-decoration: none;
}
a.card-link:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
/* Platz für das Icon-Badge, damit der Titel nicht drunterläuft. */
.card-link h2 { padding-right: 3.2rem; }

/* Icon-Badge oben rechts. Steht nach `.card > *`, damit position:absolute
   die relative-Regel überschreibt. */
.card-icon {
  position: absolute;
  top: 1.1rem;
  right: 1.2rem;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2.4rem;
  height: 2.4rem;
  border-radius: 10px;
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  color: var(--accent);
}
.card-icon svg { width: 1.25rem; height: 1.25rem; }
/* Badge mit Schriftzeichen statt Strichgrafik (BTC-Kachel). */
.card-icon-glyph { font-size: 1.35rem; font-weight: 700; line-height: 1; }
.card-admin .card-icon {
  background: color-mix(in srgb, var(--danger) 14%, transparent);
  color: var(--danger);
}

/* ---------- Hero und Knopfreihe (Fehlerseite, Gästeseite) ---------- */

.hero { padding: 1.5rem 0 1rem; }
.hero h1 { font-size: clamp(1.8rem, 3vw, 2.4rem); }
.hero p { color: var(--ink-muted); max-width: 34rem; }
.hero-actions {
  display: flex;
  gap: 0.75rem;
  flex-wrap: wrap;
  margin-top: 1.5rem;
}

/* ---------- Auth-Box ---------- */

.auth-box {
  max-width: 26rem;
  margin: 1.5rem auto;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1.75rem;
  box-shadow: var(--shadow);
}
.auth-box form { display: flex; flex-direction: column; }
.auth-box .btn-block { margin-top: 1.2rem; }

/* 3D-Intro des Logos auf der Anmeldeseite (logo-intro.js). Ohne JS/WebGL
   bleibt das SVG aus fragments/logo.html stehen; sobald das Modul startet,
   versteckt es das SVG (is-loading) und hängt danach das Canvas ein (is-3d).
   Schlägt etwas fehl, nimmt das Modul die Klassen wieder weg. */
.auth-logo {
  position: relative;
  width: 200px;
  height: 200px;
  margin: -0.25rem auto 0.5rem;
}
.auth-logo .brand-mark { width: 100%; height: 100%; }
/* Unter dem zentrierten Logo sitzt auch die Überschrift mittig. */
.auth-logo + h1 { text-align: center; }
.auth-logo.is-loading .brand-mark,
.auth-logo.is-3d .brand-mark { visibility: hidden; }
.auth-logo canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
}
@media (max-width: 480px) {
  .auth-logo { width: 160px; height: 160px; }
}
.auth-alt {
  margin-top: 1.25rem;
  font-size: 0.9rem;
  color: var(--ink-muted);
}

/* ---------- Nutzer-Liste (Admin) ---------- */

.user-list { display: flex; flex-direction: column; gap: 0.6rem; }
.user-item {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 0.8rem 1rem;
  transition: transform 0.2s cubic-bezier(.2, .8, .2, 1), border-color 0.2s ease, box-shadow 0.2s ease;
}
.user-item:hover {
  transform: translateY(-1px);
  border-color: var(--line-strong);
  box-shadow: var(--shadow);
}
/* Nutzerzeilen sind jetzt Links auf die Detailseite (Aktionen wohnen dort). */
a.user-item {
  color: inherit;
  text-decoration: none;
  cursor: pointer;
}
a.user-item:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* Suchleiste der Nutzerverwaltung: gleiche Optik wie die Asset-Suche,
   nur mit begrenzter Breite und Abstand zur Live-Region darunter. */
.user-search { max-width: 34rem; margin-bottom: 1.2rem; }

/* Aktionen auf der Account-Detailseite: Formulare nebeneinander,
   auf schmalen Screens umbrechend. */
.user-actions {
  display: flex;
  align-items: center;
  gap: 0.8rem;
  flex-wrap: wrap;
  margin-top: 0.9rem;
}
/* Avatar-Kreis (fragments/avatar.html): Initialen in Rollenfarben, ein
   Profilfoto liegt darüber. Größe über --av; die Schrift wächst mit. */
.user-avatar {
  --av: 2.5rem;
  position: relative;
  overflow: hidden;
  flex: none;
  width: var(--av); height: var(--av);
  border-radius: 999px;
  display: grid;
  place-items: center;
  font-size: calc(var(--av) * 0.33);
  font-weight: 700;
  line-height: 1;
}
.user-avatar-xs { --av: 1.6rem; font-size: 0.64rem; }
.user-avatar-lg { --av: 3.6rem; font-size: 1.15rem; }
.user-avatar > img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* Auf --surface-2 der Pill verschwinden die Rollen-Hintergründe fast ganz;
   ein feiner Ring in Schriftfarbe hält den Kreis sichtbar. */
.user-pill .user-avatar { box-shadow: inset 0 0 0 1px color-mix(in srgb, currentColor 25%, transparent); }
.user-avatar.role-admin { color: var(--role-admin); background: var(--role-admin-bg); }
.user-avatar.role-friend { color: var(--role-friend); background: var(--role-friend-bg); }
.user-avatar.role-public { color: var(--role-public); background: var(--role-public-bg); }

.user-meta { flex: 1; min-width: 0; }
.user-name { font-weight: 600; font-size: 0.95rem; }
.user-email {
  font-size: 0.82rem;
  color: var(--ink-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.user-right {
  display: flex;
  align-items: center;
  gap: 0.8rem;
  flex-wrap: wrap;
  justify-content: flex-end;
}

/* ---------- Badges & Status ---------- */

.role-badge, .badge {
  display: inline-block;
  font-size: 0.72rem;
  font-weight: 700;
  padding: 0.18rem 0.65rem;
  border-radius: 999px;
  background: var(--surface-2);
  color: var(--ink-muted);
  white-space: nowrap;
}
.role-admin { color: var(--role-admin); background: var(--role-admin-bg); }
.role-friend { color: var(--role-friend); background: var(--role-friend-bg); }
.role-public { color: var(--role-public); background: var(--role-public-bg); }
.badge-ok { color: var(--success); background: var(--success-bg); }
/* "Ungelesen" (Postfach-Zeile, Schublade): dasselbe Blau wie Punkt und Kante
   der ungelesenen Zeile, als Fläche mit weißer Schrift (AA in beiden Themes). */
.badge-new { color: var(--accent-ink); background: var(--accent-fill); }
.badge-blocked { color: var(--danger); background: var(--danger-bg); }

/* Kleines "Beta"-Etikett neben der Markets-Kennung (Titel bzw. Eyebrow). */
.beta-badge {
  display: inline-block;
  vertical-align: middle;
  font-size: 0.8rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 0.15rem 0.5rem;
  border-radius: 999px;
  color: var(--role-friend);
  background: var(--role-friend-bg);
  margin-left: 0.5rem;
  position: relative;
  top: -0.08em;
}
/* In der Eyebrow-Zeile (News, Publications, Detailseite) nicht groesser als
   der Eyebrow-Text selbst. */
.eyebrow .beta-badge { font-size: 0.72rem; }

/* Kachel-Kennzeichen: "Admin" im Violett der Admin-Rolle (wie in der
   Nutzerverwaltung), "Dev" in den Warn-Tokens (Amber, in beiden Themes
   mindestens 4.5:1 auf der Pille). 0.8rem = 11px auf dem Desktop, die
   Untergrenze fuer UI-Text. */
.topic-dev-badge,
.topic-admin-badge {
  display: inline-block;
  margin-left: 0.45rem;
  padding: 0.1rem 0.5rem;
  border-radius: 999px;
  font-size: 0.8rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  vertical-align: 0.15em;
}
.topic-admin-badge {
  background: var(--role-admin-bg);
  color: var(--role-admin);
}
.topic-dev-badge {
  background: var(--warning-bg);
  color: var(--warning);
}

.status {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.82rem;
  font-weight: 500;
  color: var(--ink-muted);
}
.status-dot {
  display: inline-block;
  width: 0.5rem; height: 0.5rem;
  border-radius: 999px;
  background: currentColor;
}
.status.is-ok { color: var(--success); }
.status.is-blocked { color: var(--danger); }

/* ---------- Tabelle (z. B. Einladungscodes) ---------- */

.table-wrap {
  overflow-x: auto;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
}
table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.88rem;
  font-variant-numeric: tabular-nums;
}
th, td {
  text-align: left;
  padding: 0.65rem 0.85rem;
  border-bottom: 1px solid var(--line);
  vertical-align: middle;
}
thead th {
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--ink-muted);
  font-weight: 600;
  background: var(--surface-2);
}
tbody tr { transition: background 0.18s ease; }
tbody tr:hover { background: var(--surface-2); }
tbody tr:last-child td { border-bottom: none; }

.actions {
  display: flex;
  gap: 0.5rem;
  flex-wrap: wrap;
}
.inline-form {
  display: inline-flex;
  gap: 0.4rem;
  align-items: center;
  margin: 0;
}

/* ---------- Meldungen ---------- */

.alert {
  padding: 0.7rem 1rem;
  border-radius: 10px;
  margin-bottom: 1.25rem;
  border: 1px solid transparent;
  font-size: 0.92rem;
}
.alert-success {
  background: var(--success-bg);
  color: var(--success);
  border-color: color-mix(in srgb, var(--success) 30%, transparent);
}
.alert-error {
  background: var(--danger-bg);
  color: var(--danger);
  border-color: color-mix(in srgb, var(--danger) 30%, transparent);
}

/* ---------- Toasts (kurze Popups, verschieben den Inhalt nicht) ---------- */

.toast-container {
  position: fixed;
  top: 4.5rem;
  right: 1rem;
  z-index: 100;
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  pointer-events: none;
}
/* Ein Toast legt sich von rechts auf, zeigt die Restzeit als dünne Linie
   und tritt beim Gehen einen Stapelschritt zurück (stack-leave, Abschnitt
   "Bewegung"). Hover oder Fokus hält die Zeit an: die Linie IST der Timer,
   ui.js (initToast) hört auf ihr animationend. Fehler bleiben länger. */
.toast {
  --toast-dwell: 4.5s;
  position: relative;
  overflow: hidden;
  pointer-events: auto;
  max-width: 22rem;
  padding: 0.7rem 1rem;
  border-radius: 10px;
  font-size: 0.9rem;
  font-weight: 500;
  border: 1px solid transparent;
  box-shadow: var(--shadow);
  cursor: pointer;
  animation: toast-arrive var(--dur-layout) var(--ease-arrive) both;
}
.toast.is-leaving { animation: stack-leave var(--dur-state) var(--ease-leave) forwards; }
.toast-success {
  background: var(--success-bg);
  color: var(--success);
  border-color: color-mix(in srgb, var(--success) 32%, transparent);
}
.toast-error {
  --toast-dwell: 7s;
  background: var(--danger-bg);
  color: var(--danger);
  border-color: color-mix(in srgb, var(--danger) 32%, transparent);
}
@keyframes toast-arrive {
  from {
    opacity: 0;
    transform: translateX(calc(var(--step) * 2)) scale(var(--step-scale));
  }
}
.toast-timer {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 2px;
  background: currentColor;
  opacity: 0.35;
  transform-origin: left center;
  animation: toast-timer var(--toast-dwell) linear forwards;
}
.toast:focus-visible { outline: 2px solid currentColor; outline-offset: 2px; }
.toast:hover .toast-timer,
.toast:focus-within .toast-timer { animation-play-state: paused; }
@keyframes toast-timer { to { transform: scaleX(0); } }

/* ---------- Profil-Seite ---------- */

.profile-card { max-width: 34rem; }
.profile-head {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding-bottom: 1.1rem;
  margin-bottom: 1.1rem;
  border-bottom: 1px solid var(--line);
}
.profile-name { font-weight: 700; font-size: 1.15rem; margin-bottom: 0.3rem; }

.data-list { display: flex; flex-direction: column; gap: 0.1rem; margin: 0; }
.data-row {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.6rem 0;
  border-bottom: 1px solid var(--line);
}
.data-row:last-child { border-bottom: none; }

/* ---------- Profilfoto ----------
   Upload-Zeile unter den Profildaten (account/profile.html). Ohne JS sieht
   man Dateiauswahl und "Upload photo"; mit JS versteckt avatar-crop.js beides
   und zeigt "Change photo"/"Upload photo", das den Zuschnitt öffnet. */
.avatar-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 0.75rem;
  margin-top: 1.1rem;
  padding-top: 1.1rem;
  border-top: 1px solid var(--line);
}
.avatar-actions form { margin: 0; }
.avatar-form {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 0.75rem;
}
.avatar-file label { margin: 0 0 0.35rem; }

/* Zuschnitt-Dialog: quadratische Bühne, darüber ein fester Kreis. Die
   Abdunklung außerhalb ist ein riesiger Schatten des Kreises, den die Bühne
   (overflow: hidden) abschneidet. touch-action: none, damit Ziehen und
   Zwei-Finger-Zoom beim Zuschnitt landen statt beim Scrollen der Seite. */
dialog.dialog-card.avatar-dialog { width: min(26rem, calc(100vw - 2rem)); }
.crop-stage {
  position: relative;
  width: min(100%, 20rem, calc(100dvh - 19rem));
  min-width: 10rem;
  aspect-ratio: 1;
  margin: 0 auto 1rem;
  overflow: hidden;
  border-radius: 12px;
  background: var(--surface-2);
  cursor: grab;
  touch-action: none;
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
}
.crop-stage.is-dragging { cursor: grabbing; }
.crop-stage:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.crop-image {
  position: absolute;
  left: 0;
  top: 0;
  max-width: none;
  pointer-events: none;
  -webkit-user-drag: none;
}
.crop-image:not([src]) { display: none; }
.crop-mask {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  box-shadow: inset 0 0 0 2px var(--crop-ring), 0 0 0 100vmax var(--crop-scrim);
  pointer-events: none;
}
.crop-zoom {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin-bottom: 1rem;
}
.crop-zoom label { margin: 0; font-size: 0.9rem; color: var(--ink-muted); }
.crop-zoom input[type="range"] { flex: 1; min-width: 0; accent-color: var(--accent); }
.avatar-dialog .field-error { margin: -0.4rem 0 1rem; }
.data-row dt { color: var(--ink-muted); font-size: 0.9rem; }
.data-row dd { margin: 0; font-weight: 500; text-align: right; }

/* ---------- Settings-/Topic-Liste ---------- */

.settings-list { display: flex; flex-direction: column; gap: 0.6rem; max-width: 38rem; }
.settings-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 0.9rem 1.1rem;
  transition: border-color 0.2s ease, box-shadow 0.2s ease, transform 0.2s ease;
}
.settings-item:hover {
  border-color: var(--line-strong);
  box-shadow: var(--shadow);
  transform: translateY(-1px);
}
.settings-title { font-weight: 600; }

/* ---------- Topic-Steuerung (zwei Umschalter pro Topic) ---------- */

.topic-controls {
  display: flex;
  gap: 1.4rem;
  align-items: center;
  flex-wrap: nowrap;
  flex: none;
}
/* Manage Topics: breiter, damit lange Beschreibungen einzeilig bleiben und die
   Zeilen (samt Umschaltern) bei langem Text nicht höher werden. */
.settings-list.topic-list { max-width: 54rem; }
/* first-of-type statt first-child: vor der Textspalte steht jetzt das Icon-Badge. */
.settings-list.topic-list .settings-item > div:first-of-type {
  flex: 1;
  min-width: 0;
}

/* Icon-Badge links in der Manage-Topics-Zeile. Eigene Klasse statt .card-icon:
   das Karten-Badge ist absolut positioniert, hier fließt es in der Zeile mit. */
.topic-manage-icon {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2.4rem;
  height: 2.4rem;
  border-radius: 10px;
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  color: var(--accent);
}
.topic-manage-icon svg { width: 1.25rem; height: 1.25rem; }
/* Badge mit Schriftzeichen statt Strichgrafik (BTC). */
.topic-manage-icon-glyph { font-size: 1.35rem; font-weight: 700; line-height: 1; }
.switch-field {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  cursor: pointer;
  font-size: 0.85rem;
  color: var(--ink-muted);
}

/* ---------- Toggle-Switch (an/aus) ---------- */

.switch { position: relative; display: inline-flex; flex: none; cursor: pointer; }
.switch input { position: absolute; opacity: 0; width: 0; height: 0; }
.switch-track {
  width: 2.4rem; height: 1.4rem;
  border-radius: 999px;
  background: var(--surface-2);
  border: 1px solid var(--line-strong);
  position: relative;
  transition: background 0.2s ease, border-color 0.2s ease;
}
.switch-track::after {
  content: "";
  position: absolute;
  top: 50%; left: 0.15rem;
  width: 1rem; height: 1rem;
  border-radius: 999px;
  background: var(--surface);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
  transform: translateY(-50%);
  transition: transform 0.22s cubic-bezier(.2, .8, .2, 1);
}
.switch input:checked + .switch-track {
  background: var(--accent);
  border-color: var(--accent);
}
.switch input:checked + .switch-track::after { transform: translate(1rem, -50%); }
.switch input:disabled + .switch-track { opacity: 0.7; cursor: default; }

/* ---------- Topic Hub (topic/hub.html, topic-hub.js) ----------
   Alle zugänglichen Topics nach Kategorie, wie ein kleiner App Store: oben
   Suche und Kategorie-Chips, darunter je Kategorie EINE Karte, die Topics
   als Zeilen darin (keine Karten in der Karte). Klickbar sind nur der Titel
   und die beiden Umschalter, darum hebt sich die Zeile nicht an
   (Lift-On-Intent). Die Karte hat kein overflow: hidden, sonst würden die
   Fokusringe der Knöpfe am Rand abgeschnitten. Eigene Media Queries am Ende
   des Abschnitts. */

.hub-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem 1rem;
  margin: 1.25rem 0 1.75rem;
}
.hub-search { flex: 1 1 18rem; max-width: 26rem; }
.hub-chips { display: flex; flex-wrap: wrap; gap: 0.4rem; }
/* Die Chips sind Labels einer Radiogruppe: die Grundregel für Feld-Labels
   (Block, Abstand, 0.85rem fett) gilt hier nicht. */
.hub-chip {
  position: relative;
  display: inline-flex;
  margin: 0;
  font-size: 0.85rem;
  font-weight: 500;
  color: var(--ink-muted);
}
.hub-chip input { position: absolute; opacity: 0; width: 0; height: 0; margin: 0; }
/* Optik der Filter-Chips (.chip-btn): transparente Pill mit Hairline, gewählt
   mit Ink-Text, stärkerem Rand und 8 % Ink-Tönung. Höhe wie die Pills der Leiste.
   Nur direkte Kinder: Spans darin (Zähler im Postfach-Chip der Startseite)
   bleiben unberührt. */
.hub-chip > span {
  display: inline-flex;
  align-items: center;
  min-height: 2.1rem;
  padding: 0 0.85rem;
  border: 1px solid var(--line);
  border-radius: 999px;
  white-space: nowrap;
  cursor: pointer;
  transition:
    color var(--dur-state) var(--ease-out),
    border-color var(--dur-state) var(--ease-out),
    background-color var(--dur-state) var(--ease-out);
}
.hub-chip:hover > span { color: var(--ink); border-color: var(--line-strong); }
.hub-chip input:checked + span {
  color: var(--ink);
  border-color: var(--line-strong);
  background: color-mix(in srgb, var(--ink) 8%, transparent);
}
.hub-chip input:focus-visible + span { outline: 2px solid var(--accent); outline-offset: 2px; }
.hub-submit { flex: none; }

/* Liste und Leer-Zustand bleiben lesbar schmal, damit Text und Knöpfe einer
   Zeile auf großen Bildschirmen nicht weit auseinanderliegen. */
.hub-group,
.hub-empty { max-width: 54rem; }
.hub-group { margin: 0 0 1.75rem; }
.hub-group-title {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  margin: 0 0 0.6rem;
}
/* Anzahl sichtbarer Topics: klein und neutral; ink-muted statt ink-faint,
   weil Faint Slate auf Paper unter 3:1 bleibt. */
.hub-group-count {
  font-size: 0.85rem;
  font-weight: 500;
  letter-spacing: 0;
  color: var(--ink-muted);
  font-variant-numeric: tabular-nums;
}
.hub-list {
  list-style: none;
  margin: 0;
  padding: 0;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
}
.hub-item {
  display: grid;
  grid-template-columns: 2.4rem minmax(0, 1fr) auto;
  column-gap: 1rem;
  align-items: center;
  padding: 0.95rem 1.2rem;
}
/* Trennlinie nur zwischen SICHTBAREN Zeilen: mit "+" bekäme die erste
   sichtbare Zeile hinter einer weggefilterten eine Linie direkt unter dem
   Kartenrand. */
.hub-item:not([hidden]) ~ .hub-item:not([hidden]) { border-top: 1px solid var(--line); }
/* Ersatzziel für den Fokus nach dem Tausch (tabindex="-1"): Ring nach innen,
   weil die Nachbarzeilen direkt anschließen. */
.hub-item:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
  border-radius: var(--radius);
}

/* Icon-Badge wie auf den Kacheln, solange nicht abonniert aber neutral: so
   sieht man in der Liste auf einen Blick, was schon unter "Topics" steht. */
.hub-item-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2.4rem;
  height: 2.4rem;
  border-radius: 10px;
  background: var(--surface-2);
  color: var(--ink-muted);
  transition:
    background-color var(--dur-state) var(--ease-out),
    color var(--dur-state) var(--ease-out);
}
.hub-item-icon svg { width: 1.25rem; height: 1.25rem; }
/* Badge mit Schriftzeichen statt Strichgrafik (BTC). */
.hub-item-icon-glyph { font-size: 1.35rem; font-weight: 700; line-height: 1; }
.hub-item.is-subscribed .hub-item-icon {
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  color: var(--accent);
}

.hub-item-text { min-width: 0; }
.hub-item-title { margin: 0; font-size: 1.05rem; overflow-wrap: anywhere; }
.hub-item-title a {
  color: var(--ink);
  text-decoration: underline;
  text-decoration-color: transparent;
  text-decoration-thickness: 2px;
  text-underline-offset: 0.2em;
  border-radius: 4px;
  transition: text-decoration-color var(--dur-state) var(--ease-out);
}
.hub-item-title a:hover,
.hub-item-title a:focus-visible { text-decoration-color: var(--accent); }
.hub-item-title a:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.hub-item-desc {
  margin: 0.15rem 0 0;
  font-size: 0.92rem;
  color: var(--ink-muted);
  text-wrap: pretty;
}

/* Umschalter Abonnieren / Zur Leiste: Pills mit aria-pressed. Das aria-label
   bleibt gleich, der Zustand steckt in aria-pressed, Text und Icon zeigen ihn
   sichtbar. Fill-Versus-Line: weiße Schrift nur auf --accent-fill. */
.hub-item-actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 0.5rem;
}
.hub-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 2.1rem;
  padding: 0 0.85rem 0 0.7rem;
  border: 1px solid transparent;
  border-radius: 999px;
  background: transparent;
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 0.85rem;
  font-weight: 600;
  line-height: 1.2;
  white-space: nowrap;
  cursor: pointer;
  transition:
    transform var(--dur-feedback) var(--ease-out),
    background-color var(--dur-state) var(--ease-out),
    border-color var(--dur-state) var(--ease-out),
    color var(--dur-state) var(--ease-out),
    opacity var(--dur-feedback) var(--ease-out);
}
/* Beide Zustände (Icon und Text) liegen im selben Rasterfeld übereinander,
   sichtbar ist der zu aria-pressed passende. So ist der Knopf immer so breit
   wie sein längerer Text: er springt beim Umschalten nicht, und die Knöpfe
   stehen über alle Zeilen bündig. */
.hub-toggle-face { display: grid; }
.hub-label-off,
.hub-label-on {
  grid-area: 1 / 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.35rem;
}
.hub-toggle[aria-pressed="true"] .hub-label-off,
.hub-toggle[aria-pressed="false"] .hub-label-on { visibility: hidden; }
.hub-toggle svg { flex: none; width: 1rem; height: 1rem; }
.hub-toggle:active {
  transform: translateY(calc(1px * var(--motion-travel))) scale(calc(1 - 0.02 * var(--motion-travel)));
}
.hub-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.hub-subscribe[aria-pressed="false"] {
  background: var(--accent-fill);
  border-color: var(--accent-fill);
  color: var(--accent-ink);
  box-shadow: var(--shadow-sm);
}
.hub-subscribe[aria-pressed="false"]:hover:not(:disabled) {
  background: var(--accent-fill-hover);
  border-color: var(--accent-fill-hover);
}
.hub-subscribe[aria-pressed="false"]:hover:not(:active, :disabled) {
  transform: translateY(calc(-1px * var(--motion-travel)));
}
.hub-subscribe[aria-pressed="true"] { border-color: var(--line-strong); }
.hub-subscribe[aria-pressed="true"] svg { color: var(--accent); }
.hub-subscribe[aria-pressed="true"]:hover:not(:disabled) { background: var(--surface-2); }

.hub-bar[aria-pressed="false"] { border-color: var(--line-strong); color: var(--ink-muted); }
.hub-bar[aria-pressed="false"]:hover:not(:disabled) { color: var(--ink); background: var(--surface-2); }
.hub-bar[aria-pressed="true"] {
  border-color: color-mix(in srgb, var(--accent) 75%, transparent);
  background: color-mix(in srgb, var(--accent) 8%, transparent);
}
.hub-bar[aria-pressed="true"] svg { color: var(--accent); }
.hub-bar[aria-pressed="true"]:hover:not(:disabled) { border-color: var(--accent); }

/* Solange die Anfrage läuft, sperrt ui.js beide Knöpfe der Zeile (is-busy). */
.hub-item.is-busy .hub-toggle { opacity: 0.6; cursor: progress; }

.hub-empty { margin-top: 0.6rem; }

/* Beim Filtern wieder auftauchende Zeilen und Gruppen blenden kurz ein
   (topic-hub.js, nicht bei reduzierter Bewegung): nur Deckkraft, kein Weg. */
.hub-group.is-revealed,
.hub-item.is-revealed,
.hub-empty.is-revealed { animation: fade-in var(--dur-state) var(--ease-out); }

@media (pointer: coarse) {
  .hub-chip > span { min-height: 44px; }
  .hub-toggle { height: auto; min-height: 44px; }
  /* Die Hover-Anhebung bliebe nach dem Antippen hängen. */
  .hub-subscribe[aria-pressed="false"]:hover:not(:active, :disabled) { transform: none; }
}

/* Phone: Suche über die volle Breite, die Chips darunter in einer Zeile zum
   Wischen (nie umbrechend über den Rand). In der Zeile rutschen die Knöpfe
   unter den Text und teilen sich die Breite; passt beides nicht nebeneinander
   (sehr schmal, deutsche Texte), steht jeder Knopf in eigener Zeile. */
@media (max-width: 40rem) {
  .hub-toolbar {
    flex-direction: column;
    align-items: stretch;
    gap: 0.75rem;
    margin-top: 1rem;
  }
  .hub-search { flex: none; max-width: none; }
  .hub-chips {
    flex-wrap: nowrap;
    overflow-x: auto;
    scrollbar-width: none;
    /* Wischen bis an die Fensterkanten; die Luft oben und unten hält die
       Fokusringe im Scroller sichtbar. */
    margin: -4px -1rem;
    padding: 4px 1rem;
  }
  .hub-chips::-webkit-scrollbar { display: none; }
  .hub-chip { flex: none; }
  .hub-submit { align-self: flex-start; }

  .hub-item {
    grid-template-columns: 2.4rem minmax(0, 1fr);
    column-gap: 0.85rem;
    row-gap: 0.8rem;
    align-items: start;
    padding: 0.9rem 1rem;
  }
  .hub-item-actions { grid-column: 1 / -1; flex-wrap: wrap; }
  .hub-item-actions .inline-form { flex: 1 1 0; min-width: max-content; }
  /* Inhalt mittig, darum gleiches Padding links und rechts; knapper als am
     Desktop, damit "Abonnieren" und "In der Leiste" auch bei 320px noch
     nebeneinander passen. */
  .hub-toggle { width: 100%; padding: 0 0.6rem; }
}

/* ---------- Kontakt ---------- */

.contact-email {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  margin-top: 1.3rem;
  padding: 0.9rem 1rem;
  border-radius: 10px;
  background: var(--surface-2);
}
.contact-email .muted { font-size: 0.8rem; }

/* ---------- Settings-Reiter und Connect AI ---------- */

/* Reiterleiste der Settings (fragments/settings-tabs.html): die .segmented-
   Komponente mit Links statt Buttons; das "beta"-Etikett am dritten Reiter. */
.settings-tabs { margin: 0.2rem 0 1.6rem; }
.settings-tabs .segmented-option { text-decoration: none; }
/* Beim Laden springt die Pille ohne Animation unter den aktiven Reiter (ui.js
   setzt is-ready nach dem ersten Positionieren). Nach einem Reiterwechsel
   startet sie unter dem vorigen Reiter und gleitet herüber (page-transitions.js,
   Abschnitt "Settings-Reiter" unter Bewegung). */
.settings-tabs:not(.is-ready) .segmented-indicator { transition: none; }
.tab-note {
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--accent-2);
  margin-left: 0.25rem;
  vertical-align: 0.1em;
}
.settings-lead { max-width: 40rem; margin: 0 0 1.1rem; text-wrap: pretty; }
/* Sprachwahl in Settings > General: Legende wie ein Feld-Label, darunter die
   Segment-Pillen mit echten Radios (wie die Priorität im To Do). */
.settings-fieldset { border: none; padding: 0; margin: 1.2rem 0 0; min-width: 0; }
.settings-fieldset legend {
  padding: 0;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--ink-muted);
  margin: 0 0 0.45rem;
}
.settings-hint { margin-top: 0.6rem; font-size: 0.85rem; }
.settings-pane h2 { margin-top: 2rem; }
.settings-pane h2:first-of-type { margin-top: 1.6rem; }

/* Statuszeile: verbunden oder nicht, welche Apps zuletzt. */
.ai-status { max-width: 54rem; }
.ai-status .settings-item { gap: 1.2rem; }
.ai-status-text .status { margin-bottom: 0.15rem; }
.ai-status .btn { flex: none; }

/* MCP-Adresse als Feld mit Copy-Knopf (data-copy, ui.js). */
.ai-address {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  max-width: 100%;
  padding: 0.45rem 0.45rem 0.45rem 0.9rem;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--surface);
  box-shadow: var(--shadow-sm);
  margin: 0 0 0.6rem;
}
.ai-address-value {
  font-size: 1.05rem;
  letter-spacing: -0.01em;
  user-select: all;
  overflow-wrap: anywhere;
}
[data-copy] svg { width: 1rem; height: 1rem; }
[data-copy].is-done { color: var(--success); border-color: color-mix(in srgb, var(--success) 45%, transparent); }

/* Schalterliste "What Claude may reach": die ganze Zeile ist das Label. */
.ai-reach { max-width: 54rem; }
.ai-row { cursor: pointer; }
.ai-row > div:first-of-type { flex: 1; min-width: 0; }
.ai-row .settings-title { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
.ai-row .muted { text-wrap: pretty; }
/* Read/Write als eckiges Etikett statt Pille: ruhiger neben dem Titel. */
.ai-level {
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  line-height: 1;
  padding: 0.22rem 0.4rem;
  border-radius: 4px;
  background: var(--surface-2);
  color: var(--ink-muted);
}
.ai-level.is-write {
  background: color-mix(in srgb, var(--accent) 13%, transparent);
  color: var(--accent);
}
.ai-scope {
  flex: none;
  font-size: 0.78rem;
  color: var(--ink-faint);
  margin-right: 0.4rem;
}
/* Ausgeschaltete Zeilen treten zurueck, ohne zu verschwinden. */
.ai-row:has(input:not(:checked)) .settings-title,
.ai-row:has(input:not(:checked)) .muted { color: var(--ink-faint); }
.ai-row:has(input:not(:checked)) .topic-manage-icon {
  background: var(--surface-2);
  color: var(--ink-faint);
}
.ai-row:has(input:not(:checked)) .ai-level { opacity: 0.6; }
.ai-row:has(input:focus-visible) { outline: 2px solid var(--accent); outline-offset: 2px; }

.ai-form-actions {
  display: flex;
  align-items: center;
  gap: 1rem;
  margin-top: 1.1rem;
  max-width: 54rem;
}
.ai-save-hint { font-size: 0.85rem; text-wrap: pretty; }

/* Verbundene Apps: Initial-Kachel, Metadaten mit Mittelpunkten getrennt. */
.ai-apps { max-width: 54rem; }
.ai-apps .settings-item > div:first-of-type { flex: 1; min-width: 0; }
.ai-app-mark {
  flex: none;
  display: grid;
  place-items: center;
  width: 2.4rem;
  height: 2.4rem;
  border-radius: 10px;
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  color: var(--accent);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.05rem;
}
.ai-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 0.15rem 0.35rem;
  font-size: 0.85rem;
  font-variant-numeric: tabular-nums;
}
.ai-meta span + span::before { content: "\00b7"; margin-right: 0.35rem; color: var(--ink-faint); }
/* Claude-Kennzeichen (Strahlenkranz in Terrakotta, Fragment claude in mcp/reach-icons.html)
   und das Terminal-Kuerzel fuer Claude Code; feste Markenfarbe in beiden Themes. */
.ai-claude-mark { background: #d97757; color: #ffffff; }
.ai-claude-mark svg { width: 1.3rem; height: 1.3rem; }
.ai-code-mark { font-family: var(--font-mono); font-weight: 700; font-size: 0.85rem; letter-spacing: -0.05em; }

/* Anleitung: Schritte und Befehl mit Copy-Knopf. */
.ai-howto { max-width: 54rem; }
.ai-howto-item { align-items: flex-start; }
.ai-howto-item > div { flex: 1; min-width: 0; }
.ai-steps { margin: 0.35rem 0 0.35rem; padding-left: 1.15rem; }
.ai-steps li { margin: 0.1rem 0; }
.ai-fine { font-size: 0.8rem; }
.ai-command {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex-wrap: wrap;
  margin: 0.45rem 0;
}
.ai-command code {
  padding: 0.45rem 0.7rem;
  border-radius: 8px;
  background: var(--surface-2);
  font-size: 0.85rem;
  overflow-wrap: anywhere;
}

@media (max-width: 40rem) {
  .ai-scope { display: none; }
  .ai-form-actions { flex-direction: column; align-items: flex-start; }
  .ai-status .settings-item { flex-wrap: wrap; }
  .ai-meta { flex-direction: column; gap: 0; }
  .ai-meta span + span::before { content: none; margin: 0; }
}

/* ---------- Segment-Umschalter (wiederverwendbare Komponente) ---------- */

.segmented {
  position: relative;
  display: inline-flex;
  padding: 4px;
  background: var(--surface-2);
  border-radius: 999px;
  gap: 2px;
}
.segmented-indicator {
  position: absolute;
  top: 4px; bottom: 4px; left: 4px;
  width: var(--seg-w, 5rem);
  transform: translateX(var(--seg-x, 0px));
  background: var(--surface);
  border-radius: 999px;
  box-shadow: var(--shadow);
  transition: transform 0.32s cubic-bezier(.2, .85, .25, 1), width 0.32s cubic-bezier(.2, .85, .25, 1);
}
.segmented-option {
  position: relative;
  z-index: 1;
  border: none;
  background: none;
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 0.84rem;
  color: var(--ink-muted);
  padding: 0.5rem 1rem;
  border-radius: 999px;
  cursor: pointer;
  transition: color 0.25s ease, background 0.2s ease;
}
.segmented-option:hover:not(.is-active) {
  color: var(--ink);
  background: color-mix(in srgb, var(--ink) 7%, transparent);
}
.segmented-option.is-active { color: var(--ink); }

/* ---------- Postfach ---------- */

/* Beim ersten Öffnen noch als "neu" markiert (Akzent-Kante + Badge). */
.sub-item.is-unread { border-left: 3px solid var(--accent); }

/* Hover-Dropdown an der Glocke: der Container ist bewusst UNSICHTBAR
   (kein Panel), nur die einzelnen Nachrichten-Blöcke haben Fläche. */
.inbox-menu { position: relative; }
.inbox-dropdown {
  position: absolute;
  top: calc(100% + 6px);
  /* Zentriert unter der Glocke (nicht rechtsbündig). */
  left: 50%;
  width: 17rem;
  display: flex;
  flex-direction: column;
  gap: 0.45rem;
  opacity: 0;
  visibility: hidden;
  transform: translate(-50%, -6px);
  transition: opacity 0.2s ease, transform 0.2s ease, visibility 0.2s;
  z-index: 20;
}
/* Unsichtbare Brücke über die 6px-Lücke, damit der Hover nicht abreißt. */
.inbox-dropdown::before {
  content: "";
  position: absolute;
  top: -6px; left: 0; right: 0;
  height: 6px;
}
.inbox-menu:hover .inbox-dropdown,
.inbox-menu:focus-within .inbox-dropdown {
  opacity: 1;
  visibility: visible;
  transform: translate(-50%, 0);
}
/* Touch ohne Hover (Tablets über der Phone-Breite): der erste Tipp würde sonst
   nur das Dropdown öffnen statt zur Inbox zu führen. */
@media (hover: none) {
  .inbox-dropdown { display: none; }
}
.inbox-drop-item {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.45rem 0.7rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 10px;
  box-shadow: var(--shadow);
  font-size: 0.82rem;
  color: var(--ink);
  text-decoration: none;
  transition: border-color 0.15s ease, transform 0.15s ease;
}
a.inbox-drop-item:hover {
  border-color: var(--line-strong);
  transform: translateY(-1px);
}
.inbox-drop-dot {
  flex: none;
  width: 0.45rem; height: 0.45rem;
  border-radius: 999px;
  background: var(--accent);
}
.inbox-drop-title {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-weight: 500;
}
.inbox-drop-date {
  flex: none;
  color: var(--ink-muted);
  font-size: 0.74rem;
  font-variant-numeric: tabular-nums;
}
.inbox-drop-empty { color: var(--ink-muted); justify-content: center; }
/* Topic-Icon am rechten Rand der Topics-Liste. Eigene Klasse statt .card-icon:
   das Icon der Kacheln ist absolut positioniert und dreimal so groß. */
.topic-icon-sm {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 1.05rem;
  height: 1.05rem;
  color: var(--accent);
}
.topic-icon-sm svg { width: 100%; height: 100%; }
.topic-icon-sm-glyph { font-size: 0.95rem; font-weight: 700; line-height: 1; }
/* Admin-Menü in Leiste und Schublade: Icons rot wie auf den Admin-Kacheln
   (.card-admin .card-icon). */
.nav-dropdown-admin .topic-icon-sm,
.drawer-group-admin .topic-icon-sm { color: var(--danger); }
/* Topic Hub in der Schublade: dort trägt das Icon das Violett der Hub-Pill
   (in der Leiste sitzt es am Pill-Rand, das Icon folgt der Linkfarbe). */
.drawer-link-hub .topic-icon-sm { color: var(--hub); }


/* Topics-Hover-Menü: fixed statt absolute (Position setzt ui.js), sonst wie
   das Glocken-Dropdown ein unsichtbarer Container mit einzelnen Blöcken. */
.nav-dropdown {
  position: fixed;
  width: 13rem;
  display: flex;
  flex-direction: column;
  gap: 0.45rem;
  opacity: 0;
  visibility: hidden;
  transform: translate(-50%, -6px);
  transition: opacity 0.2s ease, transform 0.2s ease, visibility 0.2s;
  z-index: 30;
}
.nav-dropdown::before {
  content: "";
  position: absolute;
  top: -6px; left: 0; right: 0;
  height: 6px;
}
.nav-menu.is-open .nav-dropdown {
  opacity: 1;
  visibility: visible;
  transform: translate(-50%, 0);
}

/* ---------- Markets-Einträge ---------- */

.post { max-width: 44rem; }
.post-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
}
.post-head h2 { margin-bottom: 0.4rem; }
.post-date { flex: none; font-size: 0.82rem; font-variant-numeric: tabular-nums; }
/* Zeilenumbrüche aus dem Eintrag erhalten (Textarea-Inhalt). */
.post-content { margin: 0; white-space: pre-wrap; }

/* ---------- Sub Tracker ---------- */

.add-sub-trigger { margin: 0.5rem 0 1.4rem; }

/* Popup zentriert im Fenster. position/inset/margin zentrieren auch den
   No-JS-Fall (open-Attribut ohne showModal); mit showModal kommt der
   Backdrop dazu. */
dialog.dialog-card {
  position: fixed;
  inset: 0;
  margin: auto;
  height: fit-content;
  width: min(34rem, calc(100vw - 2rem));
  max-height: calc(100dvh - 3rem);
  overflow: auto;
  background: var(--surface);
  color: var(--ink);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1.5rem 1.6rem;
  box-shadow: var(--shadow);
  z-index: 60;
}
dialog.dialog-card::backdrop {
  background: rgba(6, 12, 14, 0.5);
  backdrop-filter: blur(2px);
}
/* Öffnen und Schließen: das Popup legt sich wie eine Karte auf (von links
   unten) und tritt beim Schließen nach rechts oben zurück, der Hintergrund
   blendet mit. allow-discrete hält es sichtbar, bis die Ausblendung fertig
   ist (Chrome 117+, Safari 17.5+, Firefox 129+; ältere schließen sofort).
   Der Smartphone-Block weiter unten tauscht nur die Richtung (Bottom Sheet). */
dialog.dialog-card,
dialog.dialog-card::backdrop {
  transition:
    opacity var(--dur-state) var(--ease-leave),
    transform var(--dur-state) var(--ease-leave),
    overlay var(--dur-state) var(--ease-leave) allow-discrete,
    display var(--dur-state) var(--ease-leave) allow-discrete;
}
dialog.dialog-card {
  opacity: 0;
  transform: translate(var(--step), calc(var(--step) * -1)) scale(var(--step-scale));
}
dialog.dialog-card::backdrop { opacity: 0; }
dialog.dialog-card[open],
dialog.dialog-card[open]::backdrop {
  opacity: 1;
  transition-duration: var(--dur-layout);
  transition-timing-function: var(--ease-arrive);
}
dialog.dialog-card[open] { transform: none; }
@starting-style {
  dialog.dialog-card[open] {
    opacity: 0;
    transform: translate(calc(var(--step) * -1), var(--step)) scale(var(--step-scale));
  }
  dialog.dialog-card[open]::backdrop { opacity: 0; }
}

/* Bestätigungsdialog (data-confirm, ui.js): kompakte Variante des Popups,
   ersetzt das native confirm(). */
dialog.dialog-card.confirm-dialog { width: min(24rem, calc(100vw - 2rem)); }
.confirm-dialog h2 { margin-bottom: 0.45rem; }
.confirm-message { margin: 0 0 1.25rem; color: var(--ink-muted); }
.confirm-actions { display: flex; gap: 0.6rem; }

.sub-form {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: 1.2rem;
}
.sub-form-wide { grid-column: 1 / -1; }
.sub-form-actions { margin-top: 1.1rem; }

/* Dashboard: Diagramm | zentrale Zusammenfassung | Chronologie */
.subdash {
  display: grid;
  /* Diagramm-Karte breiter: Ring und Legende brauchen den Platz, die
     Summen-Karte trägt nur eine Zahl. */
  grid-template-columns: 1.5fr 0.85fr 1fr;
  gap: 1.1rem;
  margin-bottom: 1.6rem;
}
.subdash .card { margin-bottom: 0; }

.subdash-total { display: grid; place-items: center; }
.stat-hero { text-align: center; padding: 0.5rem 0; }
.stat-label {
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-muted);
  margin-bottom: 0.4rem;
}
.stat-value {
  font-family: var(--font-display);
  font-size: clamp(1.9rem, 3.4vw, 2.5rem);
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.1;
}
.stat-sub { margin-top: 0.45rem; font-size: 0.875rem; color: var(--ink-muted); }
/* Kennzahl-Label im Sub-Tracker-Dashboard: nicht unter 11px (Desktop-Root 14px). */
.subdash .stat-label { font-size: 0.8rem; }

/* Kuchendiagramm (Donut) + Legende. Identität hängt nie an der Farbe allein:
   die Legende trägt Label, Betrag, Anteil und Anteilsbalken, die Segmente
   sind durch Lücken getrennt. Der Ring hat eine feste Größe: Hervorheben
   ändert nur Strichstärken, Deckkraft und Transforms, nie das Layout. */
.donut-wrap { display: flex; align-items: center; gap: 1.1rem; }
/* Eigener Rahmen um das SVG: Bezugspunkt für die absolut gesetzte Mitte. */
.donut-ring {
  position: relative;
  flex: none;
  width: 8.5rem;
  height: 8.5rem;
}
/* overflow: visible, sonst schneidet das SVG den unscharfen Schein ab. */
.donut { display: block; width: 100%; height: 100%; overflow: visible; }
/* Grundring des Donuts. Als Klasse statt als SVG-Attribut, weil
   CSS-Variablen nur in Regeln greifen, nicht in Presentation Attributes. */
.donut-track { stroke: var(--line-strong); transition: opacity 0.4s ease; }
/* Weicher Schein hinter den Segmenten, im Ruhezustand unsichtbar. */
.donut-halo {
  stroke: var(--accent);
  stroke-width: 6;
  opacity: 0;
  filter: blur(2.5px);
  transition: stroke-width 0.6s cubic-bezier(.2, .8, .2, 1), opacity 0.6s ease;
}
.donut-seg {
  stroke-width: 6;
  transform-origin: 21px 21px;
  transform: translate(var(--seg-dx, 0), var(--seg-dy, 0));
  transition: stroke-width 0.35s ease, opacity 0.3s ease,
              transform 0.45s cubic-bezier(.2, .8, .2, 1);
}
/* Nur unsichtbares Hover-Ziel, breiter als der sichtbare Strich. */
.donut-hit { stroke: transparent; stroke-width: 11; pointer-events: stroke; }

/* Mittige Anzeige, immer sichtbar: in Ruhe "Monthly" + Summe, beim
   Hervorheben Kategorie (Punkt in ihrer Farbe), Betrag und Anteil. Die
   Anteilszeile hängt absolut unter dem Betrag, damit Label und Betrag beim
   Umschalten nicht springen. pointer-events: none, sonst nimmt die Mitte
   den Segmenten darunter den Hover weg. */
.donut-center {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  text-align: center;
  pointer-events: none;
}
.donut-center > div { position: relative; }
.donut-center-label {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.3rem;
  max-width: 5.6rem;
  margin: 0 auto;
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--ink-muted);
}
.donut-center-label-text { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.donut-center-dot { display: none; width: 0.45rem; height: 0.45rem; border-radius: 50%; flex: none; }
.donut-wrap.has-active .donut-center-dot { display: block; }
.donut-center-value {
  font-family: var(--font-display);
  font-size: 1.15rem;
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.15;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}
/* Vierstellige Summe ("€1,274.05"): eine Stufe kleiner, damit sie in den Ring passt. */
.donut-wrap.has-long-total .donut-center-value { font-size: 0.92rem; }
.donut-center-sub {
  position: absolute;
  top: 100%;
  left: 50%;
  transform: translateX(-50%);
  font-size: 0.7rem;
  color: var(--ink-muted);
  white-space: nowrap;
}

.donut-legend {
  list-style: none;
  margin: 0;
  padding: 0;
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  font-size: 0.84rem;
}
.donut-legend li { min-width: 0; border-radius: 6px; transition: opacity 0.3s ease; }
/* Tastatur: ein Tab-Stopp, Pfeiltasten wandern durch die Kategorien (ui.js). */
.donut-legend li:focus { outline: none; }
.donut-legend li:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
/* Reicht die Breite nicht für Label und Betrag, rutscht der Betrag in eine
   eigene Zeile (rechtsbündig), statt das Wort mitten drin zu brechen. */
.legend-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  column-gap: 0.5rem;
  row-gap: 0.1rem;
  min-width: 0;
}
.legend-dot {
  width: 0.65rem;
  height: 0.65rem;
  border-radius: 999px;
  flex: none;
  transition: box-shadow 0.3s ease;
}
/* Labels dürfen umbrechen - abgeschnittene Kategorienamen helfen niemandem.
   break-word greift nur noch, wenn ein Wort allein breiter ist als die Zeile. */
.legend-label { flex: 1 1 auto; min-width: 0; overflow-wrap: break-word; }
.legend-value {
  margin-left: auto;
  color: var(--ink-muted);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
/* Anteilsbalken unter der Zeile: Daten, darum immer sichtbar und statisch. */
.legend-bar {
  margin: 0.25rem 0 0 1.15rem;
  height: 3px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--ink) 8%, transparent);
  overflow: hidden;
}
.legend-bar-fill {
  display: block;
  height: 100%;
  width: var(--bar-width, 0%);
  border-radius: 999px;
}

/* --- Hervorheben (Klassen kommen aus ui.js) --------------------------------
   .is-hover  = Zeiger über Ring/Legende oder Fokus in der Legende
   .has-active = ein einzelnes Segment ist ausgewählt, .is-active markiert es
   Die Staffelung steht nur hier: beim Verlassen gilt wieder die Grundregel
   ohne Verzögerung, alles läuft also gleichzeitig zurück. */
.donut-wrap.is-hover .donut-track { opacity: 0.35; }
.donut-wrap.is-hover .donut-halo { stroke-width: 13; opacity: 0.2; }
.donut-wrap.is-hover .donut-seg { stroke-width: 7.1; }
.donut-wrap.is-hover .donut-seg { transition-delay: var(--seg-delay, 0ms), 0s, 0s; }

.donut-wrap.is-hover .donut-seg.is-active { stroke-width: 8.6; }
.donut-wrap.has-active .donut-seg:not(.is-active) { opacity: 0.22; }
.donut-wrap.has-active .donut-legend li:not(.is-active) { opacity: 0.45; }
.donut-wrap.has-active .donut-legend li.is-active .legend-dot {
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--ink) 14%, transparent);
}

/* Bei reduzierter Bewegung bleiben nur die Überblendungen: kein
   Herausschieben des Segments. Die Zähl-Animation der Mitte überspringt
   ui.js zusätzlich. */
@media (prefers-reduced-motion: reduce) {
  .donut-seg { transform: none; }
}

/* Chronologie der nächsten Abbuchungen */
.upcoming-list { list-style: none; margin: 0; padding: 0; }
.upcoming-item {
  display: flex;
  align-items: baseline;
  gap: 0.6rem;
  padding: 0.45rem 0;
  border-bottom: 1px solid var(--line);
  font-size: 0.88rem;
}
.upcoming-item:last-child { border-bottom: none; }
.upcoming-date { color: var(--ink-muted); font-variant-numeric: tabular-nums; flex: none; }
.upcoming-name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-weight: 500;
}
.upcoming-days { color: var(--ink-muted); font-size: 0.8rem; white-space: nowrap; }
.upcoming-cost { font-variant-numeric: tabular-nums; white-space: nowrap; }

/* Aufklappbare Zeilen-Liste (Sub Tracker, Postfach, Admin-Markets) */
.sub-list-heading { margin-top: 0.4rem; }
.sub-list-heading:focus { outline: none; }
.sub-list { display: flex; flex-direction: column; gap: 0.6rem; margin-bottom: 1.5rem; }
.sub-item {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 12px;
  transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
.sub-item:hover { border-color: var(--line-strong); box-shadow: var(--shadow); }
.sub-item > summary {
  list-style: none;
  /* Feste Spalten statt Flex: Badges und Werte stehen damit über alle
     Zeilen bündig (mittig) untereinander statt zackig eingerückt. */
  display: grid;
  grid-template-columns: minmax(0, 1fr) 11rem 6rem 9.5rem 0.85rem;
  align-items: center;
  gap: 0.8rem;
  padding: 0.85rem 1.1rem;
  cursor: pointer;
  border-radius: 11px;
}
.sub-item > summary > .badge { justify-self: center; }
.sub-item > summary > .sub-cost { justify-self: center; }
.sub-item > summary::-webkit-details-marker { display: none; }
/* Eigener Fokusring statt Browser-Default: derselbe wie bei .btn. */
.sub-item > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.sub-name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-weight: 600;
  font-size: 0.95rem;
}
.sub-cost {
  font-variant-numeric: tabular-nums;
  font-size: 0.9rem;
  color: var(--ink-muted);
  white-space: nowrap;
}
.category-dot {
  width: 0.55rem;
  height: 0.55rem;
  border-radius: 50%;
  flex: none;
}
.sub-caret {
  width: 0.85rem;
  height: 0.85rem;
  flex: none;
  color: var(--ink-muted);
  transition: transform 0.25s ease;
}
.sub-item[open] .sub-caret { transform: rotate(180deg); }
.sub-item[open] > summary { border-bottom: 1px solid var(--line); border-radius: 11px 11px 0 0; }
.sub-details { padding: 0.7rem 1.1rem 1rem; }
.sub-meta { margin: 0 0 0.2rem; font-size: 0.85rem; }
.sub-meta strong { font-weight: 600; color: var(--ink); }
.sub-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.6rem;
  margin-top: 1.1rem;
}

/* ---------- Postfach: eigenes Zeilenraster ----------
   Titel | "new" | Zeit | Pfeil. Jedes Kind hat eine feste Spalte, damit Zeit
   und Pfeil in jeder Zeile gleich stehen, ob mit oder ohne "new"-Badge (das
   5-Spalten-Raster des Sub Trackers verschob sie je nach Zeile). Rechts
   stehen nur feste oder inhaltsgroße Spalten, der Titel nimmt den Rest. */
.sub-list-inbox .sub-item > summary {
  grid-template-columns: minmax(0, 1fr) auto 6.5rem 0.85rem;
}
/* grid-row: 1 für alle, damit die order-Werte der Phone-Regel unten die
   Zeile nie umbrechen. */
.sub-list-inbox .sub-item > summary > * { grid-row: 1; }
.sub-list-inbox .sub-item > summary > .sub-name { grid-column: 1; }
.sub-list-inbox .sub-item > summary > .badge { grid-column: 2; }
.sub-list-inbox .sub-item > summary > .sub-cost { grid-column: 3; justify-self: end; }
.sub-list-inbox .sub-item > summary > .sub-caret { grid-column: 4; }
/* Aufgeklappt: der ganze Titel statt der abgeschnittenen Zeile. */
.sub-list-inbox .sub-item[open] .sub-name { white-space: normal; overflow-wrap: anywhere; }
.sub-list-inbox .sub-details > .sub-meta { margin-bottom: 0.5rem; }

/* ---------- Sub Tracker: gruppierte Abo-Liste ----------
   Gruppen je Kategorie, "Paused" ganz unten. Jede Zeile zeigt nur, was der
   Gruppenkopf nicht schon sagt: links Name + nächste Abbuchung, rechts
   Kosten + (bei nicht monatlichen Abos) das Monatsäquivalent, das die
   Gruppensumme addiert. Kopf und Zeilen teilen ein Raster, damit die
   Summe rechtsbündig über der Kosten-Spalte steht. */
.sub-list-grouped { gap: 1.5rem; }
.sub-group { display: flex; flex-direction: column; gap: 0.6rem; }

.sub-list-grouped .sub-item > .sub-row,
.sub-group-head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto 0.85rem;
  column-gap: 1rem;
}
.sub-list-grouped .sub-item > .sub-row {
  grid-template-rows: auto auto;
  row-gap: 0.15rem;
  align-items: baseline;
  padding: 0.75rem 1.1rem;
}
/* Platzierung im Raster. Mit .sub-list-grouped davor, damit die Regeln die
   allgemeinen ".sub-item > summary > .sub-cost" (zentriert) sicher schlagen. */
.sub-list-grouped .sub-row > .sub-name { grid-column: 1; grid-row: 1; }
.sub-list-grouped .sub-row > .sub-sub { grid-column: 1; grid-row: 2; }
.sub-list-grouped .sub-row > .sub-cost { grid-column: 2; grid-row: 1; justify-self: end; }
.sub-list-grouped .sub-row > .sub-cost-equiv { grid-column: 2; grid-row: 2; justify-self: end; }
.sub-list-grouped .sub-row > .sub-caret { grid-column: 3; grid-row: 1 / 3; align-self: center; }

/* Zweite Zeile links: nächste Abbuchung (reiner Text, Block mit Ellipse) bzw.
   pausiert Kategorie + "paused since" (Flex: Punkt, Label, Datum; das
   Kategorie-Label kürzt zuerst, das Datum bleibt ganz). */
.sub-sub {
  display: block;
  min-width: 0;
  font-size: 0.8rem;
  color: var(--ink-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.sub-sub.is-paused-line { display: flex; align-items: center; gap: 0.4rem; }
.sub-sub.is-paused-line > :last-child { flex: none; }
/* Abbuchung heute bis übermorgen: kräftiger, damit sie beim Überfliegen auffällt. */
.sub-sub.is-soon { color: var(--ink); font-weight: 600; }
.sub-sub .category-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.sub-list-grouped .sub-cost {
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--ink);
}
.sub-cost-equiv {
  font-size: 0.8rem;
  color: var(--ink-muted);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* Gruppenkopf */
.sub-group-head {
  align-items: center;
  /* Zeilen-Padding plus Rahmen: Kopf und Zeileninhalt fluchten. */
  padding: 0 calc(1.1rem + 1px);
  margin: 0;
  font-family: inherit;
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-muted);
}
.sub-group-title {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  min-width: 0;
}
.sub-group-label {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--ink);
}
.sub-group-count {
  flex: none;
  padding: 0.15rem 0.5rem;
  line-height: 1.2;
  border-radius: 999px;
  background: var(--surface-2);
  letter-spacing: 0;
  font-variant-numeric: tabular-nums;
}
.sub-group-note {
  flex: none;
  font-weight: 500;
  letter-spacing: 0;
  text-transform: none;
}
.sub-group-total {
  grid-column: 2;
  justify-self: end;
  font-size: 0.85rem;
  font-weight: 600;
  letter-spacing: 0;
  text-transform: none;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
/* "Paused" hat ein gezeichnetes Pause-Symbol statt eines Punkts, damit es
   nicht wie eine weitere Kategoriefarbe aussieht. */
.sub-group-pause-icon {
  width: 0.7rem;
  height: 0.7rem;
  flex: none;
  color: var(--ink-muted);
}

/* Pausierte Abos: gestrichelter Rahmen, Name und Betrag in --ink-muted
   (per Farbe statt Opacity, damit der Kontrast bei 4.5:1 bleibt). Die Zeile
   bleibt voll bedienbar (Aufklappen, Bearbeiten, Fortsetzen). */
.sub-item.is-paused { border-style: dashed; }
.sub-list-grouped .sub-item.is-paused .sub-name,
.sub-list-grouped .sub-item.is-paused .sub-cost { color: var(--ink-muted); }
.sub-list-grouped .sub-item.is-paused .sub-cost { font-weight: 500; }
.sub-group.is-paused .sub-group-label { color: var(--ink-muted); }

/* Nach einer Aktion, die die Zeile in eine andere Gruppe verschiebt, holt
   ui.js sie ins Bild und markiert sie kurz (.is-flash). */
.sub-item.is-flash { animation: sub-flash 1.6s cubic-bezier(.2, .8, .2, 1); }
@keyframes sub-flash {
  from { box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 55%, transparent); }
  to { box-shadow: 0 0 0 3px transparent; }
}

/* Aktion unterwegs (ui.js setzt .is-busy/aria-busy auf die Zeile): Knöpfe
   gesperrt, kleiner Spinner rechts daneben. */
.sub-actions .spinner { display: none; }
.sub-item.is-busy .sub-actions .spinner { display: inline-block; }
.sub-item.is-busy > summary { cursor: progress; }

/* Leerer Zustand: gedämpft per Farbe, nicht per Opacity (Kontrast). */
.sub-empty { box-shadow: none; color: var(--ink-muted); }
.sub-empty p { margin: 0; }

@media (max-width: 56rem) {
  .subdash { grid-template-columns: 1fr; }
  /* Die zentrale Zusammenfassung zuerst, wenn nichts mehr "zentral" sein kann. */
  .subdash-total { order: -1; }
  /* Weniger Platz: Spalten bleiben bündig, werden aber schmaler. */
  .sub-item > summary { grid-template-columns: minmax(0, 1fr) 8rem 5.5rem 8.5rem 0.85rem; }
}

/* Schmale Screens: eine Zeile reicht nicht für alle Felder. Umbau auf zwei
   Zeilen pro Eintrag - Name und Kosten oben, die Badges darunter. Das
   ::after mit voller Breite erzwingt den Umbruch (sonst würde Flex erst den
   Namen zusammenquetschen, bevor es wrappt). Die gruppierte Sub-Tracker-
   Liste ist schon zweizeilig und behält ihr Raster. */
@media (max-width: 34rem) {
  .sub-item > summary {
    display: flex;
    flex-wrap: wrap;
    /* Kein row-gap: die Umbruch-Zeile aus dem ::after würde ihn doppelt
       zählen; den Abstand macht das margin-top der Badges. */
    gap: 0 0.8rem;
  }
  .sub-item > summary::after {
    content: "";
    flex-basis: 100%;
    order: 4;
  }
  .sub-item > summary > .sub-cost { order: 2; }
  .sub-item > summary > .sub-caret { order: 3; }
  .sub-item > summary > .badge { order: 6; margin-top: 0.45rem; }
  .sub-list-grouped .sub-item > .sub-row { display: grid; column-gap: 0.8rem; }
  .sub-list-grouped .sub-item > .sub-row::after { content: none; }
  /* Postfach: bleibt einzeilig im eigenen Raster (relative Zeiten sind kurz). */
  .sub-list-inbox .sub-item > summary {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto 5.5rem 0.85rem;
    gap: 0.6rem;
  }
  .sub-list-inbox .sub-item > summary::after { content: none; }
  .sub-list-inbox .sub-item > summary > .badge { order: 0; margin-top: 0; }
  .sub-group-head { column-gap: 0.8rem; }
}

/* ---------- Markets (Watchlist, Kurse, News) ---------- */

/* Inline-Icons (Lupe, Stift, Pfeile): monochrome Linien statt Emojis. */
.svg-icon {
  width: 1.05em;
  height: 1.05em;
  flex: none;
  stroke: currentColor;
  fill: none;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* Kopfzeile der Übersicht: Zurück-Pfeil und Titel links, Such- und
   Bearbeiten-Knopf rechts. Aufbau wie auf den Unterseiten (.sub-head), hier
   kommt nur das Umbrechen der Knöpfe auf schmalen Schirmen dazu. */
.market-head { flex-wrap: wrap; }
.market-head-actions {
  display: flex;
  gap: 0.55rem;
  flex: none;
}
.market-head-actions .btn,
.sub-head .btn {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
}

/* Bereichs-Kacheln (News, Publications) über der Watchlist. */
.section-tiles {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(19rem, 1fr));
  gap: 1rem;
  margin-top: 1.2rem;
}
.section-tiles .card { margin-bottom: 0; }
.section-tile {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  padding: 0.7rem 0.9rem;
  text-decoration: none;
  color: inherit;
  transition: transform 0.25s cubic-bezier(.2, .8, .2, 1), border-color 0.25s ease;
}
.section-tile:hover {
  transform: translateY(-2px);
  border-color: var(--line-strong);
}
.tile-icon {
  flex: none;
  width: 30px;
  height: 30px;
  border-radius: 8px;
  background: var(--surface-2);
  color: var(--ink-muted);
  display: grid;
  place-items: center;
}
.tile-text h2 { margin: 0; font-size: 0.95rem; }
.tile-text p { margin: 0.1rem 0 0; font-size: 0.78rem; color: var(--ink-muted); }

/* Subtile Trennung zwischen Bereichs-Kacheln und Watchlist. */
.section-divider {
  border: 0;
  border-top: 1px solid var(--line);
  margin: 1.4rem 0 1rem;
}
.section-label {
  margin: 0 0 0.7rem;
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.wl-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.8rem;
}
.wl-head .section-label { margin-bottom: 0.5rem; }

/* Umschalter rechts im Watchlist-Kopf: Intervall-Leiste + Kartenansicht. */
.wl-toggles {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 0.6rem;
  flex-wrap: wrap;
}

/* Intervall-Leiste: welcher Veränderungs-Block auf allen Karten steht. */
.interval-toggle {
  display: flex;
  border: 1px solid var(--line-strong);
  border-radius: 8px;
  overflow: hidden;
  margin-bottom: 0.5rem;
}
.interval-toggle button {
  border: 0;
  background: none;
  padding: 0.3rem 0.6rem;
  font-size: 0.72rem;
  font-weight: 600;
  color: var(--ink-muted);
  cursor: pointer;
  white-space: nowrap;
}
.interval-toggle button:hover { color: var(--ink); }
.interval-toggle button.active {
  background: var(--surface-2);
  color: var(--ink);
}
.interval-toggle button:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}

/* Karten/Tabellen-Umschalter. */
.view-toggle {
  display: flex;
  border: 1px solid var(--line-strong);
  border-radius: 8px;
  overflow: hidden;
  margin-bottom: 0.5rem;
}
.view-toggle button {
  display: grid;
  place-items: center;
  width: 30px;
  height: 26px;
  border: 0;
  background: none;
  color: var(--ink-muted);
  cursor: pointer;
}
.view-toggle button.active {
  background: var(--surface-2);
  color: var(--ink);
}

/* Terminal-Ansicht: dieselben Watchlist-Daten als dichte, sortierbare
   Tabelle mit Mini-Sparklines. */
.wl-table-wrap {
  padding: 0;
  overflow-x: auto;
  margin-bottom: 2rem;
}
/* Toolbar über der Terminal-Tabelle; min-height verhindert Springen, wenn der
   "Custom order"-Button (erst nach einer Spalten-Sortierung) erscheint. */
.wl-table-toolbar {
  display: flex;
  justify-content: flex-end;
  padding: 0.5rem 0.6rem 0;
  min-height: 2.2rem;
}
table.wl-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.82rem;
}
.wl-table th {
  text-align: right;
  font-size: 0.62rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-muted);
  padding: 0.5rem 0.7rem;
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
}
.wl-table th:first-child { text-align: left; padding-left: 0.9rem; }
.wl-table th[data-sort] { cursor: pointer; user-select: none; }
.wl-table th[data-sort]:hover { color: var(--ink); }
.wl-table th[data-dir="asc"]::after { content: " ↑"; }
.wl-table th[data-dir="desc"]::after { content: " ↓"; }
.wl-table td {
  padding: 0.42rem 0.7rem;
  border-bottom: 1px solid var(--line);
  text-align: right;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.wl-table td:first-child { text-align: left; padding-left: 0.9rem; }
.wl-table tbody tr { cursor: pointer; }
.wl-table tbody tr:hover { background: var(--surface-2); }
.wl-table tbody tr:last-child td { border-bottom: none; }
.t-ident {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  min-width: 0;
}
.t-name { font-weight: 600; }
.t-sym { font-size: 0.72rem; color: var(--ink-muted); }
.t-spark svg {
  display: block;
  width: 64px;
  height: 18px;
  margin-left: auto;
  overflow: visible;
}
.t-spark.is-up { color: var(--success); }
.t-spark.is-down { color: var(--danger); }
.t-spark.is-flat { color: var(--ink-muted); }
.pct.is-up { color: var(--success); }
.pct.is-down { color: var(--danger); }
.pct.is-flat { color: var(--ink-muted); }
/* Verlustfirmen: "Loss" statt KGV, rot in Karte und Tabelle.
   Kontext-Selektor, weil .market-metrics-row b sonst mit var(--ink) gewinnt. */
.market-metrics-row b.pe-loss,
.wl-table td.pe-loss { color: var(--danger); }

.market-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(19rem, 1fr));
  gap: 1rem;
  margin-bottom: 2rem;
}
.market-grid .card { margin-bottom: 0; }

/* Drag-and-Drop-Sortierung (ui.js, [data-sortable]): gezogenes Element
   halbtransparent, bis es fallen gelassen wird. */
[data-sortable] [data-sort-key] { cursor: grab; }
[data-sortable] [data-sort-key].dragging { opacity: 0.35; cursor: grabbing; }

/* Watchlist-Karte (Design "Terminal-Karte"): dichte Karte mit Logo, Preis,
   Sparkline, Intervall-Chips und Kennzahlen. Die ganze Karte verlinkt auf
   die Detailseite (Stretched-Link unten). */
.market-card {
  padding: 0.6rem 0.8rem 0.65rem;
  transition: transform 0.25s cubic-bezier(.2, .8, .2, 1), border-color 0.25s ease;
}
.market-card:hover {
  transform: translateY(-2px);
  border-color: var(--line-strong);
}
/* Unsichtbare Klickfläche über der ganzen Karte (letztes Kind, damit sie
   über den Geschwistern liegt); überschreibt das position:relative aus
   ".card > *". */
.market-card-link {
  position: absolute;
  inset: 0;
  z-index: 1;
  border-radius: inherit;
}
.market-card-link:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}

.market-card-head {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  min-width: 0;
}
.market-logo {
  position: relative;
  flex: none;
  width: 22px;
  height: 22px;
  border-radius: 6px;
  background: var(--surface-2);
  display: grid;
  place-items: center;
  overflow: hidden;
}
.market-logo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.market-logo.is-fallback::before {
  content: attr(data-initials);
  font-size: 0.64rem;
  font-weight: 700;
  color: var(--ink-muted);
}
/* Name im Fokus, Kürzel klein dahinter. */
.market-card-name {
  flex: 1;
  min-width: 0;
  font-size: 0.9rem;
  font-weight: 700;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.market-card-sym {
  flex: none;
  font-size: 0.72rem;
  color: var(--ink-muted);
}

.market-card-price-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5rem;
  margin-top: 0.3rem;
}
.market-card-price {
  font-size: 1.15rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.01em;
}
.market-stand { font-size: 0.64rem; white-space: nowrap; }

.spark-label {
  margin: 0.45rem 0 0.05rem;
  font-size: 0.56rem;
  color: var(--ink-faint);
}
/* Sparkline: rein dekorativ, der Karten-Link darüber führt zur Stock-Seite.
   Linienfarbe über currentColor, gesetzt durch is-up/is-down. */
.spark-frame { display: block; width: 100%; }
.spark-frame.is-up, .asset-spark-chart.is-up { color: var(--success); }
.spark-frame.is-down, .asset-spark-chart.is-down { color: var(--danger); }
.spark-frame.is-flat, .asset-spark-chart.is-flat { color: var(--ink-muted); }
.spark-svg {
  display: block;
  width: 100%;
  height: 28px;
  overflow: visible;
}
.spark-empty {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 28px;
  margin: 0;
  font-size: 0.72rem;
  border: 1px dashed var(--line);
  border-radius: 6px;
}

/* Kurs-Intervalle der Watchlist-Karte: drei Bloecke (1D/1M/1Y), sichtbar ist
   der zum data-interval der Karte passende. */
.market-intervals {
  margin-top: 0.55rem;
  padding-top: 0.45rem;
  border-top: 1px solid var(--line);
}
.market-interval {
  display: none;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  gap: 3px;
  min-height: 2.9rem;
}
.market-grid[data-interval="day"] .iv-day,
.market-grid[data-interval="month"] .iv-month,
.market-grid[data-interval="year"] .iv-year {
  display: flex;
}
.market-interval-label {
  font-size: 0.56rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-muted);
}
.market-intervals .quote-change {
  font-size: 0.8rem;
  padding: 0.08rem 0.5rem;
}
/* Kennzahlen in einer Zeile, das spart Kartenhöhe. */
.market-metrics-row {
  display: flex;
  justify-content: space-between;
  gap: 0.6rem;
  margin-top: 0.5rem;
  padding-top: 0.5rem;
  border-top: 1px solid var(--line);
  font-size: 0.68rem;
}
.market-metrics-row b {
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}

/* Große Sparkline der Detailseite (Fallback ohne TradingView-Mapping). */
.asset-spark-chart {
  margin-top: 1rem;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 10px 12px;
}
.asset-spark-chart svg {
  display: block;
  width: 100%;
  height: 12.5rem;
  overflow: visible;
}

/* Prozent-Chip: grün rauf, rot runter (Farben aus den Design-Tokens). */
.quote-change {
  display: inline-block;
  font-size: 0.8rem;
  font-variant-numeric: tabular-nums;
  padding: 0.1rem 0.55rem;
  border-radius: 999px;
  white-space: nowrap;
}
.quote-change.is-up { color: var(--success); background: var(--success-bg); }
.quote-change.is-down { color: var(--danger); background: var(--danger-bg); }
.quote-change.is-flat { color: var(--ink-muted); background: var(--surface-2); }

.market-price-missing { font-size: 0.95rem; font-weight: 500; }

/* Unterseiten (News, Publications, Detailseite): Zurück-Pfeil links vom
   Titel, rechts optional eine Aktion (z.B. Add to watchlist). */
.sub-head {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  margin: 0 0 1.1rem;
}
.sub-head h1 { margin: 0; }
.sub-head .eyebrow { margin: 0 0 0.1rem; }
.sub-spacer { flex: 1; }
.back-button {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border-radius: 999px;
  border: 1px solid var(--line-strong);
  background: var(--surface);
  color: var(--ink);
  box-shadow: var(--shadow);
}
.back-button:hover { border-color: var(--accent); color: var(--accent); }

.asset-title {
  display: flex;
  align-items: baseline;
  gap: 0.55rem;
  flex-wrap: wrap;
}
.asset-sym { font-size: 0.9rem; color: var(--ink-muted); font-weight: 400; }

/* Interaktiver Chart der Detailseite (Lightweight Charts): Höhe folgt dem
   Fenster, bleibt aber gedeckelt, damit die Key-stats-Karte darunter ohne
   langes Scrollen sichtbar ist. Fallback-Karte (.asset-spark-card) zeigt
   die Sparkline, wenn JS fehlt oder die Serie nicht lädt. */
.price-chart-wrap {
  border: 1px solid var(--line);
  border-radius: 12px;
  overflow: hidden;
  margin-bottom: 0.4rem;
}
.price-chart { height: clamp(420px, 55vh, 600px); width: 100%; }
.chart-toolbar {
  display: flex;
  gap: 0.4rem;
  align-items: center;
  padding: 0.55rem 0.75rem;
  border-bottom: 1px solid var(--line);
  flex-wrap: wrap;
}
.chart-toolbar-spacer { flex: 1; }
.chart-chips { display: flex; gap: 0.4rem; flex-wrap: wrap; }
.chip-btn {
  font: inherit;
  font-size: 0.8rem;
  padding: 0.25rem 0.65rem;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: transparent;
  color: var(--ink-muted);
  cursor: pointer;
}
.chip-btn.active {
  color: var(--ink);
  border-color: var(--line-strong);
  background: color-mix(in srgb, var(--ink) 8%, transparent);
}
.chart-attribution { font-size: 0.75rem; margin: 0 0 2rem; }
.chart-attribution a { color: var(--ink-muted); }
.asset-spark-card { margin-bottom: 2rem; }
.asset-spark-card .asset-spark-chart { margin-top: 0.6rem; }

/* Live-Region um die Watchlist-Buttons im sub-head: display: contents,
   damit die Formulare Flex-Kinder des sub-head bleiben. */
.asset-actions { display: contents; }

/* Key-stats-Karte der Detailseite: Kennzahlen-Kacheln im Raster. Eigenes
   ks-Präfix, weil .stat-label/.stat-value schon dem Sub Tracker gehören. */
.stats-card { margin-bottom: 2rem; }
.ks-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 0.9rem 1.4rem;
  margin-top: 0.9rem;
}
.ks-label {
  font-size: 0.78rem;
  color: var(--ink-muted);
  margin: 0 0 0.15rem;
}
.ks-value {
  font-family: var(--font-mono);
  font-size: 1.05rem;
  margin: 0;
}
.ks-value.is-up { color: var(--success); }
.ks-value.is-down { color: var(--danger); }
.ks-note { margin-top: 0.9rem; font-size: 0.8rem; }

/* Earnings-Karte der Detailseite: nächster Termin oben, Quartals-Tabelle
   darunter. Surprise nutzt die vorhandenen is-up/is-down/is-flat-Farben. */
.earnings-card { margin-bottom: 2rem; }
.earnings-next {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.5rem 0.9rem;
  padding-bottom: 0.8rem;
  margin-bottom: 0.8rem;
  border-bottom: 1px solid var(--line);
}
.earnings-next-label {
  font-size: 0.7rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-muted);
}
.earnings-next-date { font-weight: 700; font-size: 1.05rem; }
.earnings-table { width: 100%; border-collapse: collapse; font-size: 0.85rem; }
.earnings-table th {
  text-align: right;
  font-size: 0.62rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-muted);
  padding: 0.4rem 0.6rem;
  border-bottom: 1px solid var(--line);
}
.earnings-table th:first-child { text-align: left; padding-left: 0; }
.earnings-table td {
  text-align: right;
  padding: 0.4rem 0.6rem;
  border-bottom: 1px solid var(--line);
  font-variant-numeric: tabular-nums;
}
.earnings-table td:first-child { text-align: left; padding-left: 0; }
.earnings-table tr:last-child td { border-bottom: none; }
.asset-quote-line {
  display: flex;
  align-items: baseline;
  gap: 0.7rem;
  margin: 0 0 1.2rem;
  flex-wrap: wrap;
}
.asset-price {
  font-size: 1.5rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.asset-quote-line:has(+ .asset-day) { margin-bottom: 0.45rem; }

/* Tageswerte der Detailseite (Minutentakt): die Spanne als Balken, Punkt =
   jetzt, Strich = Eröffnung; daneben Eröffnung, Vortagsschluss, Volumen.
   Liegt in der Live-Region der Kurszeile und tickt mit ihr; ui.js lässt
   den Punkt beim Tick von der alten zur neuen Stelle gleiten (--pos). */
.asset-day {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.6rem 1.8rem;
  margin: 0 0 1.2rem;
  font-size: 0.85rem;
}
/* Basis = eigene Breite: passt die Zeile nicht, brechen die Werte daneben um,
   statt den Balken zu stauchen; erst auf dem Handy schrumpft der Balken. */
.day-range {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex: 0 1 auto;
  min-width: 0;
}
.day-range-label {
  color: var(--ink-muted);
  font-weight: 600;
  white-space: nowrap;
}
.day-range-end {
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
/* Feste Breite statt flex-basis: nur so zählt sie für die Inhaltsbreite der
   Zeile mit (ein leeres Flex-Kind trüge sonst nur seine Mindestbreite bei). */
.day-range-bar {
  position: relative;
  flex: 0 1 auto;
  width: 9rem;
  min-width: 4rem;
  height: 0.35rem;
  border-radius: 999px;
  background: var(--surface-2);
}
.day-range-fill {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: color-mix(in srgb, var(--ink) 32%, transparent);
  clip-path: inset(0 calc(100% - var(--pos, 50%)) 0 0 round 999px);
}
.day-range-open {
  position: absolute;
  top: -0.2rem;
  bottom: -0.2rem;
  width: 2px;
  margin-left: -1px;
  border-radius: 1px;
  background: var(--ink-muted);
}
/* Läufer über die volle Breite, um --pos der eigenen Breite verschoben:
   so bewegt sich der Punkt per transform statt über left. */
.day-range-run {
  position: absolute;
  inset: 0;
  transform: translateX(var(--pos, 50%));
  pointer-events: none;
}
.day-range-dot {
  position: absolute;
  top: 50%;
  left: 0;
  width: 0.7rem;
  height: 0.7rem;
  margin: -0.35rem 0 0 -0.35rem;
  border-radius: 50%;
  background: var(--ink);
  box-shadow: 0 0 0 2px var(--paper);
}
.asset-day-stats {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem 1.2rem;
  margin: 0;
}
.asset-day-stats div { display: flex; gap: 0.35rem; }
.asset-day-stats dt { color: var(--ink-muted); }
.asset-day-stats dd { margin: 0; font-variant-numeric: tabular-nums; }

/* ---------- Markets: Heatmap (Kachelfeld) ----------
   Dritte Ansicht der Watchlist neben Karten und Tabelle: eine Treemap,
   Fläche nach Börsenwert, gleich oder nach Bewegung, Farbe nach der
   Veränderung im gewählten Zeitraum. Lage und Größe der Kacheln setzt
   ui.js (layoutHeatmap); die Bewegung steht im Abschnitt "Bewegung". */

/* Stufe -> Fläche; nur Stufe 4 wechselt die Schrift. */
[data-heat="0"] { --heat: var(--heat-flat); }
[data-heat="1"] { --heat: var(--heat-up-1); }
[data-heat="2"] { --heat: var(--heat-up-2); }
[data-heat="3"] { --heat: var(--heat-up-3); }
[data-heat="4"] { --heat: var(--heat-up-4); }
[data-heat="-1"] { --heat: var(--heat-down-1); }
[data-heat="-2"] { --heat: var(--heat-down-2); }
[data-heat="-3"] { --heat: var(--heat-down-3); }
[data-heat="-4"] { --heat: var(--heat-down-4); }
[data-heat] { --heat-text: var(--heat-ink); }
[data-heat="4"], [data-heat="-4"] { --heat-text: var(--heat-ink-strong); }

/* Was nur für einen Zeitraum gilt (data-iv), steht nur in dem gewählten. */
.hm[data-interval="day"] [data-iv]:not([data-iv="day"]),
.hm[data-interval="month"] [data-iv]:not([data-iv="month"]),
.hm[data-interval="year"] [data-iv]:not([data-iv="year"]) { display: none; }

.hm .hm-num { font-variant-numeric: tabular-nums; }

/* Kopfzeile: Legende links, Zähler und Größen-Schalter rechts. */
.hm-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.8rem 1.4rem;
  flex-wrap: wrap;
  margin: 0 0 0.7rem;
}
.hm-legend {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  font-size: 0.8rem;
  color: var(--ink-muted);
}
.hm-legend-label { font-weight: 600; white-space: nowrap; }
.hm-legend-body { display: grid; gap: 0.15rem; }
.hm-legend-scale {
  display: grid;
  grid-template-columns: repeat(9, 1fr);
  width: 11.5rem;
  height: 0.7rem;
  border-radius: 999px;
  overflow: hidden;
  box-shadow: inset 0 0 0 1px var(--line);
}
.hm-legend-scale > span { background: var(--heat); }
.hm-legend-ends {
  display: flex;
  justify-content: space-between;
  width: 11.5rem;
}
.hm-bar-right {
  display: flex;
  align-items: center;
  gap: 0.6rem 1.2rem;
  flex-wrap: wrap;
}
.hm-counts {
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--ink);
  white-space: nowrap;
}
.hm-size { display: inline-flex; align-items: center; gap: 0.45rem; }
.hm-switch-label { font-size: 0.8rem; color: var(--ink-muted); }
/* Wie die Intervall-Leiste: Hairline-Rahmen, Wahl in Raised Mist. */
.hm-switch {
  display: inline-flex;
  border: 1px solid var(--line-strong);
  border-radius: 8px;
  overflow: hidden;
}
.hm-switch button {
  border: 0;
  background: none;
  padding: 0.3rem 0.65rem;
  font: inherit;
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--ink-muted);
  cursor: pointer;
  white-space: nowrap;
  transition: color var(--dur-state) ease, background-color var(--dur-state) ease;
}
.hm-switch button:hover { color: var(--ink); }
.hm-switch button[aria-pressed="true"] { background: var(--surface-2); color: var(--ink); }
.hm-switch button:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* Das Feld: eine Karte, die Kacheln liegen absolut darin (ui.js). */
.hm-map.card {
  position: relative;
  height: clamp(22rem, 46vw, 34rem);
  padding: 0;
  contain: layout paint;
}
.hm-map[data-count="few"] { height: clamp(20rem, 38vw, 28rem); }
.hm-group-label {
  position: absolute;
  display: flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0 0.5rem;
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--ink-muted);
  white-space: nowrap;
  overflow: hidden;
  pointer-events: none;
}
.hm-group-label span { font-weight: 500; font-variant-numeric: tabular-nums; }

.hm-tile {
  position: absolute;
  display: block;
  border-radius: 6px;
  color: var(--heat-text, var(--ink));
  text-decoration: none;
  container-type: size;
  isolation: isolate;
  -webkit-tap-highlight-color: transparent;
}
/* Bis ui.js die Lage gesetzt hat, bleiben die Kacheln unsichtbar. */
.hm-map:not(.is-laid-out) .hm-tile { visibility: hidden; }
.hm-tile:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  z-index: 3;
}
/* Die Farbe liegt auf einer eigenen Ebene: so kann der Vortag-Zustand sie
   dämpfen, ohne die Schrift mitzunehmen. */
.hm-paint {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: var(--heat);
  z-index: -1;
  transition: background-color 700ms var(--ease-out), filter 620ms var(--ease-out), opacity 620ms var(--ease-out);
}
@media (hover: hover) {
  .hm-tile:hover { z-index: 2; }
  .hm-tile:hover .hm-paint {
    box-shadow: var(--shadow), inset 0 0 0 1px color-mix(in srgb, currentColor 35%, transparent);
  }
}
.hm-tile-in {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  gap: 0.1rem;
  padding: 0.3rem 0.4rem;
  text-align: center;
  line-height: 1.15;
  overflow: hidden;
}
.hm-t-sym {
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: clamp(0.8rem, 9cqmin, 1.05rem);
  white-space: nowrap;
}
.hm-t-name {
  display: none;
  font-weight: 600;
  font-size: 0.82rem;
  max-width: 100%;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.hm-t-chg {
  font-weight: 700;
  font-size: clamp(0.8rem, 11cqmin, 1.75rem);
  letter-spacing: -0.01em;
}
.hm-chg-iv { display: inline-flex; align-items: center; gap: 0.25em; }
.hm-dir { width: 0.62em; height: 0.62em; flex: none; fill: currentColor; }
.hm-chg-iv.is-down .hm-dir { transform: rotate(180deg); }
.hm-chg-iv.is-flat .hm-dir { display: none; }
.hm-t-price {
  display: none;
  font-family: var(--font-mono);
  font-size: 0.8rem;
}
.hm-t-curve {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  width: 100%;
  height: 42%;
  display: none;
  pointer-events: none;
  opacity: 0.32;
  overflow: visible;
}
.hm-t-curve polyline {
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linejoin: round;
  stroke-linecap: round;
  vector-effect: non-scaling-stroke;
}
.hm-t-curve line {
  stroke: currentColor;
  stroke-width: 1;
  stroke-dasharray: 3 3;
  vector-effect: non-scaling-stroke;
  opacity: 0.7;
}
/* Vortag-Zustand: vor dem ersten Kurs des Tages (und am Wochenende) die
   Veränderung des letzten Handelstags wie "Prev day" auf den Karten, leicht
   gedämpft; die Farbe bleibt lesbar, das Etikett sagt, woher sie stammt. */
.hm-prev-tag { display: none; font-size: 0.8rem; font-weight: 600; }
.hm-tile[data-state="prev"] .hm-paint { filter: saturate(0.72); }
.hm[data-interval="day"] .hm-tile[data-state="prev"] .hm-prev-tag { display: inline; }

/* Inhalt wächst mit der Kachel (Container-Abfragen statt Breakpoints):
   klein Kürzel und Prozent, mittel mit Name und Kurs, groß dazu der Verlauf. */
.hm-t-top { display: contents; }
@container (max-width: 5.6rem) {
  .hm-sfx { display: none; }
  .hm-t-chg .hm-dir { display: none; }
}
@container (max-width: 3.4rem) or (max-height: 2.6rem) {
  .hm-t-chg { display: none; }
}
@container (min-width: 8.5rem) and (min-height: 5.6rem) {
  .hm-t-name, .hm-t-price { display: block; }
}
@container (min-width: 10rem) and (min-height: 7.5rem) {
  .hm-t-curve { display: block; }
  .hm-tile-in { justify-content: flex-start; align-items: flex-start; text-align: left; padding: 0.6rem 0.7rem; }
  .hm-t-top { display: flex; align-items: baseline; gap: 0.4rem; max-width: 100%; }
}

/* Popover: Tagesverlauf und Tagesspanne beim Zeigen auf eine Kachel. */
.hm-pop-src { display: none; }
.hm-pop {
  position: fixed;
  z-index: 60;
  width: min(21rem, calc(100vw - 2rem));
  padding: 0.95rem 1rem 0.9rem;
  background: var(--surface);
  color: var(--ink);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  pointer-events: none;
  font-size: 0.85rem;
}
.hm-pop-head { display: flex; align-items: center; gap: 0.45rem; min-width: 0; }
.hm-pop-head b { font-size: 1rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.hm-pop-head .mono { color: var(--ink-muted); }
.hm-pop-head .badge { margin-left: auto; }
.hm-pop-price { display: flex; align-items: baseline; gap: 0.6rem; margin: 0.45rem 0 0.25rem; }
.hm-pop-price .mono { font-size: 1.35rem; font-weight: 700; font-variant-numeric: tabular-nums; }
.hm-pop-chart { display: block; position: relative; margin-bottom: 0.6rem; }
.hm-pop-svg { display: block; width: 100%; height: 7rem; overflow: visible; }
.hm-pop-line {
  fill: none;
  stroke: var(--ink);
  stroke-width: 1.6;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
}
.hm-pop-base {
  stroke: var(--ink-faint);
  stroke-width: 1;
  stroke-dasharray: 3 3;
  vector-effect: non-scaling-stroke;
}
.hm-pop-up { fill: var(--success); opacity: 0.16; }
.hm-pop-down { fill: var(--danger); opacity: 0.16; }
.hm-pop-dot {
  position: absolute;
  width: 0.55rem;
  height: 0.55rem;
  margin: -0.275rem 0 0 -0.275rem;
  border-radius: 50%;
  background: var(--ink);
  box-shadow: 0 0 0 2px var(--surface);
}
.hm-pop-axis {
  display: flex;
  justify-content: space-between;
  font-family: var(--font-mono);
  font-size: 0.8rem;
  color: var(--ink-muted);
  margin-top: 0.15rem;
}
.hm-pop-range { display: flex; align-items: center; gap: 0.5rem; }
.hm-pop-range .day-range-bar { flex: 1 1 auto; width: auto; }
.hm-pop .day-range-dot { box-shadow: 0 0 0 2px var(--surface); }
.hm-pop-meta {
  display: grid;
  grid-template-columns: repeat(3, auto);
  justify-content: space-between;
  gap: 0.35rem 0.8rem;
  margin-top: 0.7rem;
  padding-top: 0.6rem;
  border-top: 1px solid var(--line);
  font-size: 0.8rem;
}
.hm-pop-meta > span { display: grid; }
.hm-pop-meta > span > span { color: var(--ink-muted); }
.hm-pop-meta b { font-weight: 600; font-variant-numeric: tabular-nums; }
.hm-pop-meta b.is-up { color: var(--success); }
.hm-pop-meta b.is-down { color: var(--danger); }
@media (max-width: 40rem) {
  .hm-map.card { height: 30rem; }
  .hm-map[data-count="few"] { height: 24rem; }
  .hm-legend-scale, .hm-legend-ends { width: 9rem; }
}

/* News-Feed: Symbol-Spalte in den aufklappbaren Zeilen. */
.news-symbol { color: var(--accent); font-size: 0.9rem; }

/* Such- und Bearbeiten-Popup der Übersicht. Oben verankert statt vertikal
   zentriert: der Dialog wächst nur nach unten, das Suchfeld bleibt beim
   Erscheinen der Treffer an Ort und Stelle. Selektor mit dialog.dialog-card
   davor, sonst gewinnt dessen margin: auto (höhere Specificity). */
dialog.dialog-card.dialog-search {
  width: min(34rem, calc(100vw - 2rem));
  --dialog-top: clamp(1.5rem, 10vh, 5rem);
  margin-top: var(--dialog-top);
  margin-bottom: auto;
  max-height: calc(100dvh - var(--dialog-top) - 1.5rem);
}
.dialog-sub { margin: -0.4rem 0 1rem; font-size: 0.88rem; }
.dialog-hint { margin: 0.8rem 0 0; font-size: 0.75rem; color: var(--ink-faint); }
.search-result-link {
  text-decoration: none;
  color: inherit;
  cursor: pointer;
}
.on-list {
  flex: none;
  font-size: 0.66rem;
  font-weight: 600;
  color: var(--accent-2);
  white-space: nowrap;
}

.asset-search {
  position: relative;
  display: flex;
  gap: 0.5rem;
  align-items: center;
}
.asset-search-icon {
  position: absolute;
  left: 0.85rem;
  width: 1rem;
  height: 1rem;
  color: var(--ink-muted);
  pointer-events: none;
}
.asset-search input[type="search"] {
  flex: 1;
  min-width: 0;
  -webkit-appearance: none;
  appearance: none;
  font-family: var(--font-body);
  font-size: 0.95rem;
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  padding: 0.6rem 1rem 0.6rem 2.5rem;
  transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
.asset-search input[type="search"]:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-strong);
}
.asset-search input[type="search"]::-webkit-search-decoration { display: none; }
/* Spinner nur, solange die Live-Suche unterwegs ist (is-busy setzt ui.js). */
.asset-search .spinner { display: none; }
.asset-search.is-busy .spinner { display: inline-block; }
.asset-search-hint { margin: 0.6rem 0 0; font-size: 0.82rem; }

.search-results {
  margin-top: 1rem;
  max-height: 21rem;
  overflow-y: auto;
  scrollbar-width: thin;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}
.search-group-label { margin: 0.5rem 0 0.1rem; }
.search-group-label:first-child { margin-top: 0; }
.search-result {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.7rem;
  padding: 0.45rem 0.6rem;
  border: 1px solid var(--line);
  border-radius: 10px;
  font-size: 0.88rem;
}
.search-result:hover { border-color: var(--line-strong); background: var(--surface-2); }
.search-result-name {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  min-width: 0;
  flex-wrap: wrap;
}
.search-result-name .mono { color: var(--accent); font-size: 0.85rem; }
.search-result-name .muted { font-size: 0.78rem; }

/* Suchtreffer gestapelt: Name über Kürzel + Börsenort. Eigene Klassen statt
   .search-result-name, damit der Edit-Dialog (nutzt dieselbe Klasse) unberührt
   bleibt. min-height hält die Balken gleich hoch, auch wenn Zeile 2 (US ohne
   Börsenort) nur das Kürzel zeigt. */
.dialog-search .search-result { min-height: 3.05rem; }
.sr-ident {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  flex: 1 1 auto;
  min-width: 0;
}
.sr-title {
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.sr-meta {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  min-width: 0;
  font-size: 0.78rem;
  color: var(--ink-muted);
}
.sr-meta .sr-sym { color: var(--accent); font-size: 0.8rem; }
.sr-exchange { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* Auto-Refresh der Kurse: Button für No-JS, Spinner + Text während des
   fetch (is-busy setzt ui.js). Bei reduzierter Bewegung steht der Spinner
   still, der Text daneben sagt trotzdem, was passiert. */
.quote-refresh {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  margin-bottom: 0.9rem;
  min-height: 2rem;
}
.quote-refresh .spinner,
.quote-refresh .quote-refresh-text { display: none; }
.quote-refresh.is-busy button { display: none; }
.quote-refresh.is-busy .spinner { display: inline-block; }
.quote-refresh.is-busy .quote-refresh-text { display: inline; font-size: 0.85rem; }

.spinner {
  width: 0.9rem;
  height: 0.9rem;
  flex: none;
  border: 2px solid var(--line);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
}
@keyframes spin {
  to { transform: rotate(360deg); }
}

/* Störungs-Hinweis: eine Quelle liefert seit längerem nur Fehler (kaputter
   Key oder Ausfall). Dezent, aber mit rotem Punkt als Signal. */
.source-status {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  font-size: 0.85rem;
  color: var(--ink-muted);
  margin: 0 0 0.9rem;
  max-width: 44rem;
}
.source-status::before {
  content: "";
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  background: var(--danger);
  flex: none;
  align-self: center;
}
dialog .source-status { margin: 0.6rem 0 0; }

/* News-Karten: eine Karte je Artikel, gestapelt mit normalem Seiten-Scroll.
   Kopfzeile (Etikett, Symbol, Kurs-Chip, Zeit), Schlagzeile als Link zur
   Quelle, darunter die KI-Einordnung. Farben kommen von den vorhandenen
   Trend-Klassen is-up/is-down/is-flat, damit News und Watchlist dieselbe
   Sprache sprechen. */
.news-intro { max-width: 44rem; }

/* Kennzeichnung der KI-Anteile über der Liste. Bewusst leise (gedämpft, kein
   Kasten): sie soll auffindbar sein, nicht die Nachrichten überstrahlen. */
.news-ai-note {
  display: flex;
  align-items: flex-start;
  gap: 0.45rem;
  margin: 0 0 0.9rem;
  max-width: 44rem;
  font-size: 0.8rem;
  color: var(--ink-muted);
}
.news-ai-note .svg-icon {
  width: 0.95rem;
  height: 0.95rem;
  flex: none;
  margin-top: 0.1rem;
  fill: currentColor;
  stroke: none;
}
.news-section .news-ai-note { margin-bottom: 0.2rem; }

.news-list { display: flex; flex-direction: column; gap: 0.7rem; }
.news-card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 0.9rem 1.1rem;
  transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
.news-card:hover { border-color: var(--line-strong); box-shadow: var(--shadow); }
/* Seit dem letzten Besuch dazugekommen (Muster aus dem Postfach). */
.news-card.is-unread { border-left: 3px solid var(--accent); }

.news-card-head {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: 0.45rem;
}
.news-symbol { font-size: 0.85rem; font-weight: 600; }
/* Die Zeit steht rechts, alles davor bleibt links zusammen. */
.news-time { margin-left: auto; font-size: 0.8rem; white-space: nowrap; }

.news-headline { margin: 0; font-size: 1rem; line-height: 1.4; font-weight: 600; }
.news-headline a { color: var(--ink); text-decoration: none; }
.news-headline a:hover { text-decoration: underline; }

.news-takeaway { margin: 0.45rem 0 0; font-size: 0.9rem; color: var(--ink-muted); }

.news-card-foot {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin-top: 0.6rem;
  font-size: 0.78rem;
}
.news-tone {
  font-weight: 600;
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
}
/* Punkt in der Stimmungsfarbe statt einer weiteren farbigen Pille neben dem
   Kurs-Chip - sonst kippt die Kopfzeile ins Bunte. */
.news-tone::before {
  content: "";
  width: 0.45rem;
  height: 0.45rem;
  border-radius: 50%;
  background: currentColor;
}
.news-tone.is-up { color: var(--success); }
.news-tone.is-down { color: var(--danger); }
.news-tone.is-flat { color: var(--ink-muted); }

/* News-Abschnitt auf der Detailseite: die Karten sitzen dort in einer Karte,
   brauchen also keine zweite Umrandung. */
.news-section .news-card { border: 0; border-radius: 0; padding: 0.9rem 0; }
.news-section .news-card:hover { box-shadow: none; }
.news-section .news-list { gap: 0; }
.news-section .news-card + .news-card { border-top: 1px solid var(--line); }
.news-section-more { margin: 0.8rem 0 0; font-size: 0.85rem; }

/* ---------- Responsiv ---------- */

@media (max-width: 34rem) {
  .user-item { flex-wrap: wrap; }
  .user-right { width: 100%; justify-content: flex-start; padding-left: 3.4rem; }
  .sub-form { grid-template-columns: 1fr; }
  /* Kopfzeile der News-Karte umbrechen lassen; die Zeit rutscht dann in die
     zweite Zeile statt Symbol und Kurs-Chip zu quetschen. */
  .news-card-head { flex-wrap: wrap; }
  .news-time { margin-left: 0; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    transition-duration: 0.001ms !important;
    animation-duration: 0.001ms !important;
  }
}

/* ---------- BTC-Topic (Whitepaper-Karte) ---------- */

/* Zweispaltig: Seiten-Mockup links, Text rechts. */
.doc-card { max-width: 56rem; margin-top: 1.5rem; }
.doc-card-body {
  display: grid;
  grid-template-columns: 10rem 1fr;
  gap: 1.5rem;
}
/* Etikett der Karte: gleiche Pille wie .beta-badge, steht hier aber allein
   statt neben einer Überschrift - darum ohne Einrückung und etwas größer. */
.doc-card .beta-badge {
  margin-left: 0;
  top: 0;
  padding: 0.2rem 0.6rem;
}
.doc-preview {
  aspect-ratio: 1 / 1.414;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: repeating-linear-gradient(180deg, var(--paper) 0 5px, var(--surface) 5px 10px);
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  padding: 1rem 0.9rem;
}
.doc-preview-lines { display: flex; flex-direction: column; gap: 0.35rem; }
.doc-preview-lines span {
  height: 6px;
  border-radius: 2px;
  background: var(--line-strong);
}
.doc-preview-lines span:nth-child(1) { width: 88%; }
.doc-preview-lines span:nth-child(2) { width: 60%; }
.doc-preview-lines span:nth-child(3) { width: 44%; height: 4px; background: var(--line); margin-top: 0.3rem; }
.doc-preview-page {
  font-family: var(--font-mono);
  font-size: 0.65rem;
  color: var(--ink-faint);
}
.doc-meta {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 0.7rem;
  margin: 0 0 1.1rem;
}
.doc-meta dt { font-size: 0.78rem; color: var(--ink-muted); }
.doc-meta dd { margin: 0.15rem 0 0; font-family: var(--font-mono); font-size: 1rem; }
.doc-actions { display: flex; gap: 0.7rem; flex-wrap: wrap; }
.doc-card .doc-actions .btn { margin-top: 0; }
/* Akzent-Variante der bestehenden Bereichs-Kachel (Einstieg in BTC Market). */
.tile-icon-accent {
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  color: var(--accent);
}

@media (max-width: 40rem) {
  .doc-card-body { grid-template-columns: 1fr; }
  .doc-preview { max-width: 10rem; }
  .doc-meta { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* Glossar zum Whitepaper: zwei Spalten, Begriff mit Abschnittsnummer, die
   Erklärung darunter. Reihenfolge folgt dem Paper, nicht dem Alphabet. */
.glossary-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr));
  gap: 1.1rem 2rem;
  margin: 0;
}
.glossary-item dt { font-size: 0.95rem; font-weight: 700; }
.glossary-item dd {
  margin: 0.2rem 0 0;
  font-size: 0.85rem;
  color: var(--ink-muted);
}
.glossary-ref {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  font-weight: 400;
  color: var(--ink-faint);
  margin-left: 0.35rem;
}

/* ---------- To Do ---------- */
/* Wurzelgröße ist 87,5 % (= 14px), die rem-Werte hier entsprechen also den
   Pixelmaßen aus dem Design-Handoff. Farben, Radien und Schatten kommen
   ausschließlich aus den Tokens oben, damit das Board in beiden Themes sitzt. */

.todo-intro {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 1.15rem;
  flex-wrap: wrap;
  margin-bottom: 1.6rem;
}
.todo-intro p { max-width: 34rem; margin: 0; }

/* Fortschrittsleiste: Ring | erledigt heute | Trenner | Anteil je Liste.
   Bewusst flach gehalten - der Fokus der Seite liegt auf dem Board darunter. */
.todo-progress {
  display: flex;
  align-items: center;
  gap: 1.3rem;
  flex-wrap: wrap;
  padding: 0.55rem 1rem;
  margin-bottom: 1.15rem;
}
.todo-ring { position: relative; width: 3rem; height: 3rem; flex: none; }
.todo-ring svg { display: block; width: 100%; height: 100%; overflow: visible; }
.todo-ring-track { stroke: var(--line-strong); }
/* Farbe folgt dem Anteil: viel geschafft = grün, wenig = rot, dazwischen amber. */
.todo-ring-value { stroke: var(--chart-3); transition: stroke 0.3s ease; }
.todo-ring.is-good .todo-ring-value { stroke: var(--success); }
.todo-ring.is-poor .todo-ring-value { stroke: var(--danger); }
.todo-ring-label {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  font-family: var(--font-display);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: -0.02em;
}
/* Deutsch "100 %": das geschützte Leerzeichen nur schmal setzen, sonst ragt
   die Zahl weiter in den Ring als das englische "100%". */
.todo-ring-label:lang(de) { word-spacing: -0.12em; }

.todo-progress-stat { flex: none; }
.todo-progress .stat-label { font-size: 0.64rem; }
.todo-progress-value {
  font-family: var(--font-display);
  font-size: 1.07rem;
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.15;
}
.todo-progress-overdue {
  margin-top: 0.07rem;
  font-size: 0.79rem;
  font-weight: 600;
  color: var(--danger);
}
.todo-progress-divider { width: 1px; align-self: stretch; background: var(--line); flex: none; }
.todo-progress-lists { display: flex; gap: 1.15rem; flex-wrap: wrap; flex: 1; }
/* Name und Zähler einer Liste in einer Zeile, damit die Leiste flach bleibt. */
.todo-progress-lists > div {
  display: flex;
  align-items: baseline;
  gap: 0.36rem;
}
.todo-progress-list-name {
  display: flex;
  align-items: center;
  gap: 0.36rem;
  font-size: 0.86rem;
  font-weight: 600;
}
.todo-progress-list-count { font-size: 0.79rem; color: var(--ink-muted); }

.todo-dot { width: 0.64rem; height: 0.64rem; border-radius: 999px; flex: none; display: inline-block; }
.todo-progress .todo-dot { width: 0.5rem; height: 0.5rem; align-self: center; }

/* Board: die Pfeile stehen neben dem Scrollbereich, nicht darüber - so
   verdecken sie nie eine Spalte. Der Scrollbalken selbst bleibt verborgen. */
.todo-board { display: flex; align-items: center; gap: 0.6rem; }
.todo-scroll-arrow {
  flex: none;
  width: 1.86rem;
  height: 1.86rem;
  border-radius: 999px;
  border: 1px solid var(--line-strong);
  background: var(--surface);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 0.93rem;
  display: grid;
  place-items: center;
  cursor: pointer;
  transition: background 0.2s ease, border-color 0.2s ease, opacity 0.2s ease;
}
.todo-scroll-arrow:hover:not(:disabled) { background: var(--surface-2); border-color: var(--accent); }
.todo-scroll-arrow:disabled { opacity: 0.35; cursor: default; }

.todo-board-scroll {
  overflow-x: auto;
  flex: 1;
  min-width: 0;
  scrollbar-width: none;
  /* overflow-x macht auch die Senkrechte zur Schnittkante: damit das ⋮-Menü
     einer niedrigen (z. B. leeren) Spalte unten nicht abgeschnitten wird,
     bekommt der Scrollbereich unsichtbaren Auslauf nach unten; der negative
     Margin nimmt ihn aus dem Layout wieder heraus. */
  padding-bottom: 8rem;
  margin-bottom: -8rem;
}
.todo-board-scroll::-webkit-scrollbar { display: none; }
.todo-columns { display: flex; gap: 1rem; align-items: flex-start; padding-bottom: 0.2rem; }

.todo-column {
  width: 15.71rem;
  flex: none;
  background: var(--surface-2);
  border-radius: var(--radius);
  padding: 0.86rem;
  transition: box-shadow 0.18s ease;
}
/* Spalte, über der die gezogene Karte gerade schwebt. */
.todo-column.is-drop-target { box-shadow: inset 0 0 0 2px var(--accent); }
.todo-column-head {
  display: flex;
  align-items: center;
  gap: 0.57rem;
  margin-bottom: 0.7rem;
  padding: 0 0.15rem;
  cursor: grab; /* der Kopf ist der Drag-Griff der ganzen Spalte */
}
.todo-column.is-column-dragging { opacity: 0.4; }
.todo-column-name {
  flex: 1;
  min-width: 0;
  font-size: 0.93rem;
  font-weight: 700;
  overflow-wrap: break-word;
  /* Der Name gehört zum Drag-Griff; eine Textauswahl käme dem Ziehen dazwischen. */
  user-select: none;
  -webkit-user-select: none;
}
.todo-column-count { flex: none; font-size: 0.72rem; font-weight: 700; color: var(--ink-muted); }

/* Umbenennen: das Formular ersetzt Name und Zähler im Kopf. */
.todo-rename-form {
  display: none;
  flex: 1;
  min-width: 0;
  align-items: center;
  gap: 0.29rem;
}
.todo-column-head.is-renaming { cursor: default; }
.todo-column-head.is-renaming .todo-column-name,
.todo-column-head.is-renaming .todo-column-count,
.todo-column-head.is-renaming .todo-menu { display: none; }
.todo-column-head.is-renaming .todo-rename-form {
  display: flex;
  animation: todo-fade-slide-in 0.18s ease;
}
.todo-rename-form input[type="text"] {
  flex: 1;
  min-width: 0;
  width: auto;
  font-size: 0.86rem;
  font-weight: 600;
  padding: 0.21rem 0.43rem;
  background: var(--surface);
  border-color: var(--line-strong);
}
.todo-rename-save,
.todo-rename-cancel {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 1.57rem;
  height: 1.57rem;
  padding: 0;
  border: none;
  border-radius: 6px;
  background: none;
  color: var(--ink-faint);
  font-size: 1rem;
  line-height: 1;
  text-decoration: none;
  cursor: pointer;
  transition: color 0.15s ease, background 0.15s ease;
}
.todo-rename-save:hover { color: var(--success); background: var(--surface); }
.todo-rename-cancel:hover { color: var(--danger); background: var(--surface); }
.todo-rename-save .svg-icon { width: 0.86rem; height: 0.86rem; stroke-width: 2.4; }

/* Spalten-Menü (⋮): kleines Dropdown unterm Kopf, rechtsbündig. */
.todo-menu { position: relative; flex: none; }
.todo-menu summary {
  list-style: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 1.57rem;
  height: 1.57rem;
  border-radius: 6px;
  color: var(--ink-faint);
  cursor: pointer;
  transition: color 0.15s ease, background 0.15s ease;
}
.todo-menu summary::-webkit-details-marker { display: none; }
.todo-menu summary:hover,
.todo-menu[open] summary { color: var(--ink); background: var(--surface); }
.todo-menu summary .svg-icon { width: 1rem; height: 1rem; fill: currentColor; stroke-width: 1.2; }
.todo-menu-list {
  position: absolute;
  top: calc(100% + 0.29rem);
  right: 0;
  z-index: 20;
  min-width: 8.6rem;
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  padding: 0.29rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 10px;
  box-shadow: var(--shadow);
  animation: todo-fade-slide-in 0.15s ease;
}
.todo-menu-list form { display: contents; }
.todo-menu-list a,
.todo-menu-list button {
  display: block;
  width: 100%;
  padding: 0.36rem 0.57rem;
  border: none;
  border-radius: 7px;
  background: none;
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 0.86rem;
  font-weight: 500;
  text-align: left;
  text-decoration: none;
  cursor: pointer;
  transition: background 0.15s ease;
}
.todo-menu-list a:hover,
.todo-menu-list button:hover { background: var(--surface-2); }
.todo-menu-list .todo-menu-danger { color: var(--danger); }

.todo-card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 0.5rem 0.72rem;
  margin-bottom: 0.43rem;
  cursor: grab;
  box-shadow: var(--shadow-sm);
  transition: opacity 0.15s ease, transform 0.15s ease, box-shadow 0.15s ease;
}
.todo-card.is-overdue { border-left: 3px solid var(--danger); }
/* Zustand während des Ziehens: die Karte tritt zurück, die Zielspalte leuchtet. */
.todo-card.is-dragging { opacity: 0.35; transform: scale(0.96); box-shadow: none; cursor: grabbing; }
/* Frisch angelegte Karten gleiten nach dem Live-Tausch kurz ein. */
.todo-card.is-entering { animation: todo-fade-slide-in 0.22s ease; }
@keyframes todo-fade-slide-in {
  from { opacity: 0; transform: translateY(-4px); }
  to { opacity: 1; transform: none; }
}
.todo-card-row { display: flex; align-items: center; gap: 0.57rem; }
.todo-card-title { flex: 1; min-width: 0; font-size: 0.93rem; font-weight: 600; overflow-wrap: break-word; }
/* Der Titel ist ein Link auf das "Edit task"-Popup, sieht aber aus wie Text. */
a.todo-card-title {
  color: inherit;
  text-decoration: none;
  transition: color 0.15s ease;
}
a.todo-card-title:hover { color: var(--accent); }
.todo-card.is-done .todo-card-title { text-decoration: line-through; color: var(--ink-faint); }

.todo-check-form { display: flex; flex: none; }
.todo-check {
  width: 1.14rem;
  height: 1.14rem;
  padding: 0;
  border-radius: 999px;
  border: 2px solid var(--line-strong);
  background: none;
  cursor: pointer;
  flex: none;
  display: grid;
  place-items: center;
  transition: background 0.18s ease, border-color 0.18s ease;
}
.todo-check:hover { border-color: var(--success); }
.todo-check:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.todo-check.is-done { background: var(--success); border-color: var(--success); }
.todo-check-icon {
  width: 0.64rem;
  height: 0.64rem;
  fill: none;
  stroke: var(--accent-ink);
  stroke-width: 3;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* Metazeile: Fähnchen (Priorität) und Wiederholung links, Frist rechts. */
.todo-card-meta {
  display: flex;
  align-items: center;
  gap: 0.43rem;
  margin-top: 0.43rem;
  padding-left: 1.71rem;
}
.todo-card-meta svg {
  width: 0.86rem;
  height: 0.86rem;
  flex: none;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.todo-flag { color: var(--ink-muted); }
.todo-flag.is-medium { color: var(--chart-3); }
.todo-flag.is-high { color: var(--danger); }
.todo-repeat { color: var(--ink-faint); }
.todo-card-label { margin-left: auto; font-size: 0.79rem; font-weight: 600; color: var(--ink-muted); }
.todo-card-label.is-overdue { color: var(--danger); }
.todo-card-label.is-today { color: var(--accent); }

.todo-done-divider {
  display: flex;
  align-items: center;
  gap: 0.43rem;
  margin: 0.15rem 0 0.43rem;
  padding: 0 0.15rem;
}
.todo-done-line { flex: 1; height: 1px; background: var(--line); }
.todo-done-label {
  font-size: 0.64rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

/* Schnelleingabe: die Pille blendet bei Hover ODER Fokus in das Eingabefeld
   über. Reines CSS, damit das auch per Tastatur (Tab ins Feld) funktioniert. */
.todo-quick { position: relative; height: 1.86rem; margin-top: 0.57rem; }
.todo-quick-pill {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 1;
  cursor: pointer;
  transition: opacity 0.15s ease;
}
.todo-quick-pill span {
  padding: 0.28rem 0.64rem;
  border-radius: 7px;
  background: var(--surface);
  color: var(--ink-faint);
  font-size: 0.72rem;
}
.todo-quick-form {
  position: absolute;
  inset: 0;
  display: flex;
  gap: 0.43rem;
  opacity: 0;
  pointer-events: none;
  transform: translateY(-3px);
  transition: opacity 0.15s ease, transform 0.15s ease;
}
.todo-quick:hover .todo-quick-pill,
.todo-quick:focus-within .todo-quick-pill { opacity: 0; pointer-events: none; }
.todo-quick:hover .todo-quick-form,
.todo-quick:focus-within .todo-quick-form { opacity: 1; pointer-events: auto; transform: translateY(0); }
.todo-quick-form input[type="text"] {
  flex: 1;
  min-width: 0;
  width: auto;
  font-size: 0.86rem;
  padding: 0.28rem 0.57rem;
  background: var(--surface);
  border-color: var(--line-strong);
}
.todo-quick-add {
  flex: none;
  font-family: var(--font-body);
  font-size: 0.79rem;
  font-weight: 400;
  color: var(--ink-faint);
  background: none;
  border: none;
  cursor: pointer;
  padding: 0.28rem;
  transition: color 0.2s ease;
}
.todo-quick-add:hover { color: var(--ink-muted); }

/* Geisterspalte am Ende; verschwindet ab der Höchstzahl an Listen. */
.todo-new-list { flex: none; margin: 0; }
.todo-new-list-btn {
  width: 12.14rem;
  min-height: 5rem;
  border: 1.5px dashed var(--line-strong);
  border-radius: var(--radius);
  background: none;
  color: var(--ink-faint);
  font-family: var(--font-body);
  font-size: 0.93rem;
  font-weight: 600;
  cursor: pointer;
  transition: border-color 0.2s ease, color 0.2s ease;
}
.todo-new-list-btn:hover { border-color: var(--accent); color: var(--accent); }

/* Prioritätsauswahl im Popup: echte Radios in der Optik der Segment-Pille.
   Ohne .segmented-indicator lässt der Umschalter aus ui.js sie in Ruhe. */
.todo-fieldset { border: none; padding: 0; margin: 0; min-width: 0; }
.todo-fieldset legend {
  padding: 0;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--ink-muted);
  margin: 0.7rem 0 0.35rem;
}
.segmented-radio label { display: inline-flex; align-items: center; margin: 0; }
.segmented-radio input { position: absolute; opacity: 0; width: 0; height: 0; }
.segmented-radio span {
  display: inline-block;
  font-size: 0.84rem;
  font-weight: 600;
  color: var(--ink-muted);
  padding: 0.5rem 1rem;
  border-radius: 999px;
  cursor: pointer;
  transition: color 0.25s ease, background 0.2s ease, box-shadow 0.25s ease;
}
.segmented-radio label:hover input:not(:checked) + span {
  color: var(--ink);
  background: color-mix(in srgb, var(--ink) 7%, transparent);
}
.segmented-radio input:checked + span {
  color: var(--ink);
  background: var(--surface);
  box-shadow: var(--shadow-sm);
}
.segmented-radio input:focus-visible + span { outline: 2px solid var(--accent); outline-offset: 2px; }

@media (max-width: 48rem) {
  .todo-progress { gap: 1.2rem; }
  .todo-progress-divider { display: none; }
}
@media (max-width: 34rem) {
  .todo-intro { flex-direction: column; align-items: stretch; }
  .todo-intro .btn { align-self: flex-start; }
}

/* ---------- Money Math (Topic CALCULATORS) ---------- */
/* Übersicht: Kacheln je Gruppe. Die älteren Kachel-Texte liegen unter der
   0.8rem-Untergrenze (DESIGN.md); hier lokal angehoben, ohne Markets/BTC
   anzufassen. */
.calc-lede { max-width: 40rem; }
.calc-group { margin-top: 1.6rem; }
.calc-group .section-tiles { margin-top: 0; }
.calc-index .section-label { font-size: 0.8rem; }
.calc-index .tile-text p { font-size: 0.8rem; }
.calc-empty { margin-top: 1.6rem; }

/* Rechnerseite: Eingaben links, Ergebnisse rechts; schmal eine Spalte mit dem
   Ergebnis ganz oben (wie die Summe im Sub-Tracker-Dashboard). */
.calc {
  display: grid;
  grid-template-columns: minmax(17rem, 22rem) minmax(0, 1fr);
  grid-template-areas:
    "form hero"
    "form details"
    "form chart"
    "form table";
  grid-template-rows: auto auto auto 1fr;
  gap: 1.1rem;
  align-items: start;
  margin-top: 1.2rem;
}
.calc > .card { margin-bottom: 0; }
.calc-form { grid-area: form; }
.calc-hero { grid-area: hero; }
.calc-details { grid-area: details; }
.calc-chart-card { grid-area: chart; }
.calc-schedule { grid-area: table; }
@media (max-width: 48rem) {
  .calc {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas: "hero" "form" "details" "chart" "table";
    grid-template-rows: none;
  }
}
.calc-card-title { margin: 0; font-size: 1.05rem; }

/* Ergebnis-Held: die eine Zahl, um die es geht. */
.calc-hero-label {
  margin: 0;
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-muted);
}
/* .calc-hero davor: schlägt die globale Regel .card p (gedämpfte Schrift). */
.calc-hero .calc-hero-value {
  margin: 0.35rem 0 0;
  font-family: var(--font-display);
  font-size: clamp(1.9rem, 3.4vw, 2.5rem);
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.1;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.calc-hero-sub { margin: 0.45rem 0 0; font-size: 0.875rem; color: var(--ink-muted); }
.calc-hero .calc-message {
  margin: 0.8rem 0 0;
  padding: 0.7rem 1rem;
  border-radius: 10px;
  background: var(--warning-bg);
  color: var(--warning);
  border: 1px solid color-mix(in srgb, var(--warning) 30%, transparent);
  font-size: 0.9rem;
}
/* Die Meldung steht allein: auch die Überschrift (sie gehört zum Ergebnis,
   das es gerade nicht gibt, und könnte noch vom vorigen Modus stammen). */
.calc.has-message .calc-hero-label,
.calc.has-message .calc-hero-value,
.calc.has-message .calc-hero-sub { display: none; }
.calc.has-message .calc-hero .calc-message { margin-top: 0; }

/* Veraltete Ergebnisse (Eingabe fehlerhaft) treten zurück, statt zu verschwinden. */
.calc :is(.calc-hero-value, .calc-hero-sub, .calc-details, .calc-chart-card, .calc-schedule) {
  transition: opacity var(--dur-state) var(--ease-out);
}
.calc.is-stale :is(.calc-hero-value, .calc-hero-sub, .calc-details, .calc-chart-card, .calc-schedule) {
  opacity: 0.5;
}

/* Eingabefeld mit Einheit: Rahmen und Fokus liegen auf der Hülle, das Feld
   selbst ist rahmenlos (gleiche Optik wie die globalen Eingabefelder). */
.calc-form > :first-child { margin-top: 0; }
.calc-field > label { margin-top: 0.9rem; }
.calc-form > .calc-field:first-child > label { margin-top: 0; }
.calc-input {
  display: flex;
  align-items: center;
  background: var(--paper);
  border: 1px solid var(--line-strong);
  border-radius: 8px;
  transition: border-color var(--dur-state) var(--ease-out), box-shadow var(--dur-state) var(--ease-out);
}
.calc-input:focus-within {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-strong);
}
.calc-input:has(input[aria-invalid="true"]) { border-color: var(--danger); }
.calc-input:has(input[aria-invalid="true"]):focus-within {
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--danger) 25%, transparent);
}
.calc-input input[type="text"] {
  flex: 1;
  min-width: 0;
  border: 0;
  background: transparent;
  box-shadow: none;
  font-variant-numeric: tabular-nums;
}
.calc-input input[type="text"]:focus { box-shadow: none; }
.calc-affix {
  flex: none;
  padding: 0 0.7rem;
  font-size: 0.9rem;
  color: var(--ink-faint);
  user-select: none;
}
.calc-affix:first-child { padding-right: 0; }
.calc-affix:last-child { padding-left: 0; }
.calc-hint { margin: 0.3rem 0 0; font-size: 0.8rem; color: var(--ink-muted); }
.calc-note { margin: 0.3rem 0 0; font-size: 0.85rem; }
.calc-note[data-type="error"] { color: var(--danger); }
.calc-note[data-type="warning"] { color: var(--warning); }
[data-show-when][hidden] { display: none; }

.calc-fieldset { border: none; padding: 0; margin: 0; min-width: 0; }
.calc-fieldset legend {
  padding: 0;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--ink-muted);
  margin: 0.9rem 0 0.35rem;
}
.calc-form > .calc-fieldset:first-child legend { margin-top: 0; }
/* Auswahl im Rechner: alle Optionen in einer Zeile, gleich breit; lange
   Beschriftungen brechen in ihrer Pille um statt die Leiste zu sprengen. */
.calc .segmented-radio { display: flex; width: 100%; }
.calc .segmented-radio label { flex: 1 1 0; min-width: 0; }
.calc .segmented-radio span {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  padding: 0.45rem 0.55rem;
  text-align: center;
  line-height: 1.25;
}
.calc .calc-table-toggle { width: auto; display: inline-flex; }
.calc .calc-table-toggle label { flex: none; }
.calc .calc-table-toggle span { padding: 0.45rem 1rem; }
.calc select { width: 100%; }

.calc-advanced { margin-top: 1rem; border-top: 1px solid var(--line); padding-top: 0.8rem; }
.calc-advanced > summary {
  cursor: pointer;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--accent);
  width: fit-content;
}
.calc-advanced[open] > summary { margin-bottom: 0.2rem; }
.calc-reset { margin-top: 1.1rem; }

/* Kennzahlen: Zahlen rechts, in festen Ziffernbreiten. */
.calc-details .data-row dd { font-variant-numeric: tabular-nums; }

/* Diagramm: SVG mit gedehnter viewBox; Linien bleiben dank
   non-scaling-stroke gleich dick, Achsen sind HTML. */
.calc-chart {
  position: relative;
  height: clamp(12rem, 28vw, 16rem);
  margin-top: 0.9rem;
  touch-action: pan-y;
}
.calc-plot { position: absolute; left: 3.6rem; right: 0.6rem; top: 0.5rem; bottom: 1.7rem; }
.calc-svg { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }
.calc-grid { stroke: var(--line); stroke-width: 1; vector-effect: non-scaling-stroke; }
.calc-grid.is-zero { stroke: var(--line-strong); }
.calc-area { fill: color-mix(in srgb, var(--c) 18%, transparent); stroke: none; }
.calc-line {
  fill: none;
  stroke: var(--c);
  stroke-width: 2;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
}
.calc-line.is-dashed { stroke-dasharray: 6 5; }
.calc-marker { stroke: var(--line-strong); stroke-width: 1.5; stroke-dasharray: 4 4; vector-effect: non-scaling-stroke; }
.calc-marker-label {
  position: absolute;
  top: -0.2rem;
  transform: translateX(-50%);
  z-index: 1;
  padding: 0.1rem 0.45rem;
  border-radius: 999px;
  background: var(--surface-2);
  font-size: 0.8rem;
  color: var(--ink-muted);
  white-space: nowrap;
}
/* Nahe am Rand bündig statt mittig, damit das Etikett im Diagramm bleibt. */
.calc-marker-label.is-start { transform: none; }
.calc-marker-label.is-end { transform: translateX(-100%); }
.calc-yaxis { position: absolute; left: 0; top: 0.5rem; bottom: 1.7rem; width: 3.2rem; }
.calc-yaxis span,
.calc-xaxis span {
  position: absolute;
  font-size: 0.8rem;
  color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.calc-yaxis span { right: 0; transform: translateY(-50%); }
.calc-xaxis { position: absolute; left: 3.6rem; right: 0.6rem; bottom: 0; height: 1.2rem; }
.calc-xaxis span { transform: translateX(-50%); }
.calc-xaxis span:first-child { transform: none; }
.calc-xaxis span:last-child { transform: translateX(-100%); }
/* Deutsch: Achsenwerte wie "750.000 €" oder "1,2 Mio. €" sind breiter als
   "€750K", lange Wörter in schmalen Pillen ("Endwert") dürfen trennen. */
.calc-chart:lang(de) .calc-yaxis { width: 4.4rem; }
.calc-chart:lang(de) :is(.calc-plot, .calc-xaxis) { left: 4.8rem; }
.calc:lang(de) .segmented-radio span { hyphens: auto; }
.calc-guide {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 1px;
  background: var(--line-strong);
  pointer-events: none;
}
.calc-readout {
  position: absolute;
  top: 0.4rem;
  transform: translateX(0.6rem);
  display: grid;
  gap: 0.15rem;
  min-width: 10rem;
  padding: 0.5rem 0.7rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 10px;
  box-shadow: var(--shadow);
  font-size: 0.8rem;
  font-variant-numeric: tabular-nums;
  pointer-events: none;
  z-index: 1;
}
.calc-readout.is-left { transform: translateX(calc(-100% - 0.6rem)); }
.calc-readout span { display: flex; align-items: center; gap: 0.4rem; color: var(--ink-muted); }
.calc-readout b { margin-left: auto; padding-left: 0.8rem; color: var(--ink); font-weight: 600; }
.calc-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 1.1rem;
  margin: 0.8rem 0 0;
  padding: 0;
  list-style: none;
  font-size: 0.85rem;
  color: var(--ink-muted);
}
.calc-legend li { display: inline-flex; align-items: center; gap: 0.45rem; }
.calc-swatch {
  flex: none;
  display: inline-block;
  width: 0.85rem;
  height: 0.85rem;
  border-radius: 4px;
  background: color-mix(in srgb, var(--c) 18%, transparent);
  border: 2px solid var(--c);
}
.calc-swatch.is-dashed { height: 0; border-width: 2px 0 0; border-style: dashed; border-radius: 0; background: none; }

/* Tabelle: aufklappbar, eigener Scrollbereich mit festem Kopf; auf dem
   Telefon bleibt die erste Spalte stehen. */
.calc-schedule > summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.6rem;
  cursor: pointer;
  list-style: none;
  font-weight: 700;
  font-size: 1.05rem;
}
.calc-schedule > summary::-webkit-details-marker { display: none; }
.calc-caret {
  width: 1.1rem;
  height: 1.1rem;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  color: var(--ink-muted);
  transition: transform var(--dur-state) var(--ease-out);
}
.calc-schedule[open] .calc-caret { transform: rotate(180deg); }
.calc-table-toggle { margin-top: 0.9rem; }
.calc-table-wrap {
  margin-top: 0.9rem;
  max-height: 28rem;
  overflow: auto;
  border: 1px solid var(--line);
  border-radius: 12px;
}
.calc-table-wrap:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.calc-table-wrap table { font-size: 0.85rem; }
.calc-table-wrap thead th { position: sticky; top: 0; z-index: 1; font-size: 0.8rem; }
.calc-table-wrap th, .calc-table-wrap td { padding: 0.5rem 0.8rem; white-space: nowrap; }
.calc-table-wrap tbody th { font-weight: 600; }
.calc-table-wrap .num { text-align: right; }
.calc-table-wrap tr.is-mark > * {
  background: color-mix(in srgb, var(--accent) 9%, var(--surface));
  font-weight: 600;
}
@media (max-width: 40rem) {
  .calc-table-wrap tbody th,
  .calc-table-wrap thead th:first-child {
    position: sticky;
    left: 0;
    background: var(--surface);
  }
  .calc-table-wrap thead th:first-child { z-index: 2; background: var(--surface-2); }
}

.calc-disclaimer { max-width: 46rem; margin: 1.6rem 0 0; font-size: 0.8rem; color: var(--ink-muted); }

/* Touch: Segment-Pillen erreichen 44px wie alle anderen Ziele. */
@media (pointer: coarse) {
  .calc .segmented-radio span { min-height: 44px; }
}

/* ---------- Budget Flow (Topic BUDGET_FLOW) ----------
   Der Monatsplan als Sankey: Quellen | Einnahmen-Stapel | Kategorien |
   Einträge. Bänder und Knoten zeichnet budget-flow.js ins SVG, die
   Beschriftungen liegen als HTML darüber. Farben nur über Tokens: die
   Kategorien behalten ihren Slot (--chart-N), die Mischung je Theme steht
   in --bf-* (Theme-Blöcke oben). Bewegung unter "Budget Flow: das Geld
   fließt ein" im Abschnitt Bewegung. */

.bf-head .bf-add { flex: none; }

/* Ein Satz mit den vier Summen statt Kennzahl-Kacheln. Zahlen kräftig und in
   festen Ziffernbreiten, damit das Hochzählen nicht wackelt. */
.bf-summary {
  max-width: 50rem;
  margin: 0 0 1.3rem;
  font-size: 1.05rem;
  line-height: 1.6;
  color: var(--ink-muted);
  text-wrap: pretty;
}
.bf-summary b {
  color: var(--ink);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
/* Fehlbetrag: dasselbe Danger-Paar wie das Band, das ihn trägt, mit Symbol
   und Worten statt Farbe allein. */
.bf-summary.is-short {
  display: flex;
  align-items: flex-start;
  gap: 0.6rem;
  padding: 0.7rem 1rem;
  border-radius: 10px;
  background: var(--danger-bg);
  color: var(--danger);
  border: 1px solid color-mix(in srgb, var(--danger) 30%, transparent);
  font-size: 0.95rem;
}
.bf-summary.is-short b { color: inherit; }
.bf-summary-icon { flex: none; width: 1.1rem; height: 1.1rem; margin-top: 0.2rem; }
/* Die Zahlen im Satz zeigen beim Überfahren ihre Spur im Diagramm. */
.bf-summary b[data-trace] {
  border-radius: 4px;
  transition: background-color var(--dur-state) var(--ease-out);
}
.bf-summary b[data-trace]:hover { background: color-mix(in srgb, var(--ink) 8%, transparent); }

/* Diagramm-Karte */
.bf-flow-card { padding: 1.3rem 1.4rem 1.2rem; }
.bf-flow-head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: 0.8rem 1.2rem;
  margin-bottom: 1.1rem;
}
.bf-flow-head h2 { margin: 0; }
.bf-flow-sub { margin: 0.2rem 0 0; font-size: 0.85rem; color: var(--ink-muted); }
.bf-flow-controls { display: flex; flex-wrap: wrap; gap: 0.6rem; }
.bf-switch { border: 0; padding: 0; margin: 0; min-width: 0; }
.bf-seg span { padding: 0.4rem 0.95rem; font-variant-numeric: tabular-nums; }

/* Fläche des Diagramms. Die Höhe setzt budget-flow.js aus dem Layout (wächst
   mit der Zahl der Beschriftungen), die Bänder bleiben wertgetreu. */
.bf-sankey {
  position: relative;
  min-height: 12rem;
  touch-action: pan-y;
  border-radius: 10px;
}
.bf-sankey:focus { outline: none; }
.bf-sankey:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.bf-svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
}
.bf-noscript { margin: 0; padding: 4.5rem 1rem; text-align: center; color: var(--ink-muted); }

/* Bänder: die Slot-Farbe, im Dunkeln erst aufgehellt (--bf-tint), dann gemischt. */
.bf-link {
  fill: color-mix(in srgb, color-mix(in srgb, var(--c) var(--bf-tint), var(--ink)) var(--bf-mix), transparent);
  transition: opacity var(--dur-state) var(--ease-out), fill var(--dur-state) var(--ease-out);
}
/* Einnahmen: ruhiger Schieferton, die Farbe gehört dem, wohin es fließt. */
.bf-link.is-income { fill: var(--bf-income); }
/* Noch frei: kaum getönt, gestrichelte Haarlinie (noch keine Aufgabe). */
.bf-link.is-left {
  fill: color-mix(in srgb, var(--ink) 4%, transparent);
  stroke: var(--line-strong);
  stroke-width: 1;
  stroke-dasharray: 5 4;
}
.bf-link.is-short { fill: color-mix(in srgb, var(--danger) 22%, transparent); }

/* Hervorheben: der Rest tritt zurück, die Spur liegt kräftig darüber. */
.bf-sankey.is-tracing .bf-link { opacity: 0.3; }
.bf-sankey.is-tracing .bf-link.is-on { opacity: 1; }
.bf-sankey.is-tracing .bf-link.is-on:not(.is-income):not(.is-left):not(.is-short),
.bf-trace {
  fill: color-mix(in srgb, color-mix(in srgb, var(--c) var(--bf-tint), var(--ink)) var(--bf-mix-on), transparent);
}
.bf-sankey.is-tracing .bf-link.is-income.is-on { fill: color-mix(in srgb, var(--ink) 20%, transparent); }
.bf-trace { pointer-events: none; }

/* Lichtstreif wie beim Primärknopf: läuft einmal die Spur entlang. Die
   Füllung (Verlauf #bf-sweep) setzt budget-flow.js als Attribut: jeder
   url-Verweis in style.css, auch in Kommentaren, lässt Springs
   CssLinkResourceTransformer die Datei umschreiben, und sie verlöre ihren
   Ein-Jahres-Cache (StaticResourceConfigTest). */
.bf-sweep-path { pointer-events: none; }
.bf-sweep-edge { stop-color: var(--bf-shine); stop-opacity: 0; }
.bf-sweep-core { stop-color: var(--bf-shine); stop-opacity: var(--bf-shine-opacity); }

/* Knoten */
.bf-node { fill: var(--c); transition: opacity var(--dur-state) var(--ease-out); }
.bf-node.is-income { fill: var(--ink-muted); }
.bf-node.is-short { fill: var(--danger); }
.bf-node.is-left {
  fill: var(--surface);
  stroke: var(--ink-faint);
  stroke-width: 1.5;
  stroke-dasharray: 3 3;
}
/* Der Einnahmen-Knoten ist die Bildmarke: drei Karten, jede einen Schritt
   nach rechts oben. Hier landet das Geld, von hier wird es ausgeteilt; die
   einzige Datenmarke in Stack Blue, weil sie das Logo ist. */
.bf-hub-back { fill: var(--logo-back); }
.bf-hub-mid { fill: var(--logo-mid); }
.bf-hub-front { fill: var(--accent); }
.bf-hub-short { fill: var(--danger); }
/* Ein Spalt in Flächenfarbe trennt die Karten wie die Lücken im Logo;
   dieselbe Linie teilt den Stapel schmal in seine Quellen. */
.bf-hub-card,
.bf-hub-short { stroke: var(--surface); stroke-width: 2; paint-order: stroke; }
.bf-hub-cut { stroke: var(--surface); stroke-width: 2; }
/* Der gezeigte Knoten hebt sich ab (Schatten nur bei Absicht). */
.bf-node-g.is-hl { filter: drop-shadow(0 2px 5px var(--bf-drop)); }
.bf-sankey.is-tracing .bf-node:not(.is-on),
.bf-sankey.is-tracing .bf-hub:not(.is-on) { opacity: 0.4; }
.bf-node-g { transform-box: fill-box; }

/* Trefferflächen: unsichtbar und breiter als dünne Bänder. */
.bf-hit { fill: none; stroke: transparent; pointer-events: stroke; }
.bf-hit-node { fill: transparent; stroke: none; pointer-events: all; }
.bf-hit.is-action,
.bf-hit-node.is-action { cursor: pointer; }
.bf-focus-ring { fill: none; stroke: var(--accent); stroke-width: 2; pointer-events: none; }

/* Beschriftungen: Chips auf der Fläche, Text in Tinte (nie in der
   Slot-Farbe). translate richtet aus, transform bleibt frei fürs Ankommen. */
.bf-labels { position: absolute; inset: 0; pointer-events: none; }
.bf-label {
  position: absolute;
  left: 0;
  top: 0;
  display: inline-flex;
  align-items: baseline;
  gap: 0.4em;
  padding: 0.1rem 0.45rem;
  border-radius: 6px;
  background: color-mix(in srgb, var(--surface) 90%, transparent);
  box-shadow: var(--shadow-sm);
  font-size: 0.8rem;
  line-height: 1.35;
  white-space: nowrap;
  color: var(--ink-muted);
  pointer-events: auto;
  transition: opacity var(--dur-state) var(--ease-out);
}
.bf-label.is-left-aligned { translate: 0 -50%; }
.bf-label.is-right-aligned { translate: -100% -50%; }
.bf-label-row { display: inline-flex; align-items: baseline; gap: 0.3em; }
/* Zweizeilig, wenn Name und Betrag nebeneinander nicht passen: der Betrag
   fällt nie weg (budget-flow.js plant den Platz dafür ein). */
.bf-label.is-stacked { flex-direction: column; gap: 0; line-height: 1.3; }
.bf-label.is-stacked.is-right-aligned { align-items: flex-end; }
.bf-label.is-stacked.is-left-aligned { align-items: flex-start; }
/* Schmal tragen die Kategorien statt eines Chips einen Hof in Flächenfarbe
   wie Ortsnamen auf einer Karte: lesbar, die Bänder bleiben sichtbar. */
.bf-label.is-halo {
  background: none;
  box-shadow: none;
  text-shadow: 0 0 2px var(--surface), 0 0 3px var(--surface), 0 0 6px var(--surface), 0 0 8px var(--surface);
}
.bf-label b { color: var(--ink); font-weight: 600; font-variant-numeric: tabular-nums; }
.bf-label.is-cat { font-size: 0.85rem; }
.bf-label.is-cat .bf-label-name { color: var(--ink); font-weight: 600; }
.bf-label.is-action { cursor: pointer; }
.bf-label-icon { width: 0.85rem; height: 0.85rem; align-self: center; color: var(--ink-muted); }
/* Überschrift über dem Stapel: wie eine Kennzahl (.stat-label), ohne Chip. */
.bf-label.is-hub {
  translate: -50% -100%;
  flex-direction: column;
  align-items: center;
  gap: 0;
  padding: 0;
  background: none;
  box-shadow: none;
  line-height: 1.2;
}
.bf-label.is-hub.is-start { translate: 0 -100%; align-items: flex-start; }
.bf-label.is-hub .stat-label { margin: 0; font-size: 0.8rem; }
.bf-label.is-hub b { font-size: 1rem; font-weight: 700; }
.bf-sankey.is-tracing .bf-label:not(.is-on) { opacity: 0.35; }

/* Lesefenster beim Zeigen: Linien-Schlüssel, Wert, Anteile, Hinweis. */
.bf-readout {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 3;
  display: grid;
  gap: 0.2rem;
  min-width: 11rem;
  max-width: 17rem;
  padding: 0.55rem 0.75rem 0.6rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 10px;
  box-shadow: var(--shadow);
  font-size: 0.8rem;
  color: var(--ink-muted);
  pointer-events: none;
}
.bf-ro-name { display: flex; align-items: center; gap: 0.45rem; min-width: 0; color: var(--ink); font-weight: 600; }
.bf-ro-name span:last-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Linien-Schlüssel statt Kästchen: ein kurzer Strich in der Farbe des Bands. */
.bf-key { flex: none; width: 0.9rem; height: 3px; border-radius: 999px; background: var(--c); }
.bf-key.is-income { background: var(--ink-muted); }
.bf-key.is-left { background: none; border-top: 2px dashed var(--ink-faint); height: 0; }
.bf-key.is-short { background: var(--danger); }
.bf-ro-value { font-size: 1.05rem; font-weight: 700; color: var(--ink); font-variant-numeric: tabular-nums; }
.bf-ro-hint { margin-top: 0.15rem; color: var(--ink-faint); }

/* Legende und Quellen-Chips: Optik der Filter-Chips (.chip-btn), dazu ein
   Farbfeld. Zeigen oder Fokus hebt die Spur hervor, Klick hält sie fest. */
.bf-legend,
.bf-sources {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  padding: 0;
  list-style: none;
}
.bf-legend { margin: 1rem 0 0; }
.bf-sources { margin: -0.2rem 0 1rem; }
.bf-legend button,
.bf-sources button {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.25rem 0.65rem;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: transparent;
  color: var(--ink-muted);
  font: inherit;
  font-size: 0.8rem;
  cursor: pointer;
  transition:
    color var(--dur-state) var(--ease-out),
    border-color var(--dur-state) var(--ease-out),
    background-color var(--dur-state) var(--ease-out);
}
.bf-legend button:hover,
.bf-legend button.is-on,
.bf-sources button:hover,
.bf-sources button:focus-visible {
  color: var(--ink);
  border-color: var(--line-strong);
  background: color-mix(in srgb, var(--ink) 8%, transparent);
}
.bf-legend button:focus-visible,
.bf-sources button:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.bf-sources b { color: var(--ink); font-weight: 600; font-variant-numeric: tabular-nums; }
.bf-swatch {
  flex: none;
  width: 0.75rem;
  height: 0.75rem;
  border-radius: 4px;
  background: color-mix(in srgb, var(--c) 34%, transparent);
  border: 2px solid var(--c);
}
/* Einnahmen gefüllt in Schiefer wie ihre Knoten: ein leeres Quadrat läse sich als Checkbox. */
.bf-swatch.is-income { --c: var(--ink-muted); background: var(--ink-muted); }
.bf-swatch.is-left { --c: var(--ink-faint); background: none; border-style: dashed; border-width: 1.5px; }
.bf-swatch.is-short { --c: var(--danger); }
/* "+ Kategorie" am Ende der Legende: eine Handlung, darum Stack Blue,
   gestrichelt wie etwas, das es noch nicht gibt. */
.bf-legend .bf-legend-add {
  color: var(--accent);
  border-style: dashed;
  border-color: color-mix(in srgb, var(--accent) 45%, transparent);
}
.bf-legend .bf-legend-add:hover {
  color: var(--accent);
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 8%, transparent);
}

/* Leerer Plan: gestrichelter Umriss eines Sankeys über Satz und Handlung. */
.bf-empty {
  display: grid;
  justify-items: center;
  text-align: center;
  gap: 0.2rem;
  padding: 2.4rem 1rem 2.6rem;
}
.bf-empty-art { width: min(20rem, 80%); height: auto; margin-bottom: 1rem; }
.bf-empty-art path {
  fill: color-mix(in srgb, var(--ink) 5%, transparent);
  stroke: var(--line-strong);
  stroke-dasharray: 4 4;
}
.bf-empty h3 { margin: 0; }
.bf-empty p { max-width: 30rem; margin: 0.2rem 0 0.4rem; color: var(--ink-muted); }

/* Plan-Listen unter dem Diagramm: Einnahmen | Ausgaben und Sparen. */
.bf-lower {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.55fr);
  gap: 1.8rem;
  align-items: start;
  margin-top: 2rem;
}
.bf-col-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 0.8rem;
}
.bf-col-head h2 { margin: 0; }
.bf-col-total {
  font-size: 0.9rem;
  color: var(--ink-muted);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.bf-list { margin-bottom: 0.9rem; }
/* Je Gruppe eine Karte, die Einträge darin als Zeilen mit Haarlinien (wie
   die Kategorie-Listen im Topic Hub), nie Karten in der Karte. */
.bf-group-card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  overflow: hidden;
}
.bf-group-card > .sub-item { border: 0; border-radius: 0; background: transparent; box-shadow: none; }
.bf-group-card > .sub-item + .sub-item,
.bf-group-card > .bf-empty-row + .sub-item { border-top: 1px solid var(--line); }
.bf-group-card > .sub-item:hover {
  border-color: var(--line);
  box-shadow: none;
  background: color-mix(in srgb, var(--ink) 3%, transparent);
}
.bf-group-card > .sub-item.bf-readonly:hover { background: transparent; }
.bf-group-card > .sub-item > summary,
.bf-group-card > .sub-item[open] > summary { border-radius: 0; }
.bf-group-card > .sub-item > summary:focus-visible { outline-offset: -2px; }
.bf-list-add { margin-top: 0; }
.bf-list-actions { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.bf-group-dot { width: 0.55rem; height: 0.55rem; border-radius: 50%; flex: none; background: var(--c); }
.bf-income-empty { margin: 0; }
/* Link zum Sub Tracker im Kopf der Abo-Gruppe. */
.bf-group-link {
  grid-column: 2;
  justify-self: end;
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0;
  text-transform: none;
  color: var(--accent);
  text-decoration: none;
  white-space: nowrap;
}
.bf-group-link:hover { text-decoration: underline; text-underline-offset: 3px; }
.sub-group-head:has(.bf-group-link) { grid-template-columns: minmax(0, 1fr) auto auto 0.85rem; }
.sub-group-head:has(.bf-group-link) .sub-group-total { grid-column: 3; }
/* Stift neben dem Gruppennamen: öffnet die Kategorie zum Anpassen. */
.bf-head-edit {
  flex: none;
  display: inline-grid;
  place-items: center;
  width: 1.6rem;
  height: 1.6rem;
  margin-left: 0.1rem;
  border-radius: 999px;
  color: var(--ink-faint);
  transition: color var(--dur-state) var(--ease-out), background-color var(--dur-state) var(--ease-out);
}
.bf-head-edit svg { width: 0.9rem; height: 0.9rem; }
.bf-head-edit:hover { color: var(--accent); background: color-mix(in srgb, var(--accent) 10%, transparent); }
.bf-head-edit:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Leere eigene Kategorie: wartet auf ihren ersten Eintrag. */
.bf-empty-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.6rem;
  padding: 0.75rem 1.1rem;
  color: var(--ink-muted);
  font-size: 0.9rem;
}
.bf-empty-row .btn { margin-top: 0; }

/* Formular in der aufgeklappten Zeile und im Popup: zwei Spalten wie im
   Sub Tracker. Art als Segment-Pille über die volle Breite. */
.bf-form select { width: 100%; }
.bf-form .segmented-radio { display: flex; width: 100%; }
.bf-form .segmented-radio label { flex: 1 1 0; min-width: 0; }
.bf-form .segmented-radio span { display: block; width: 100%; text-align: center; }
.bf-kind { border: 0; padding: 0; margin: 0; min-width: 0; }
.bf-kind legend {
  padding: 0;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--ink-muted);
  margin: 0 0 0.35rem;
}
/* Kategorie nur bei Ausgaben, das Feld für eine neue Kategorie nur bei
   "+ Neue Kategorie …". Ohne :has bleibt beides sichtbar. */
.bf-form:has(input[type="radio"][name="kind"]:checked:not([value="COST"])) .bf-cat-field { display: none; }
.bf-form:not(:has(select[name="category"] option[value="new"]:checked)) .bf-new-cat { display: none; }
.bf-field-hint { margin: 0.3rem 0 0; font-size: 0.8rem; color: var(--ink-muted); }
.bf-new-cat > label { margin-top: 0.6rem; }
.bf-form-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.6rem;
  margin-top: 1.2rem;
}
.bf-form-actions .btn { margin-top: 0; }

/* Kachel "Abos kommen aus dem Sub Tracker" */
.bf-subcard { margin-top: 1.4rem; }
.bf-subcard h2 { font-size: 1.05rem; margin-bottom: 0.35rem; }
.bf-subcard p { margin: 0 0 0.6rem; font-size: 0.9rem; }
.bf-subcard-link { color: var(--accent); font-weight: 600; font-size: 0.9rem; }
.bf-disclaimer { margin-top: 2rem; }

/* Popup "Kategorien": eine Zeile je Kategorie (aufklappbar zum Anpassen),
   Abos und Sparen fest am Ende, darunter "Neue Kategorie". */
.bf-cats-lead { margin: 0 0 1rem; color: var(--ink-muted); font-size: 0.92rem; }
.bf-cat-list { list-style: none; margin: 0; padding: 0; }
.bf-cat-list > li + li { border-top: 1px solid var(--line); }
.bf-cat-row {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 0.7rem;
  padding: 0.65rem 0.9rem;
}
.bf-cat > summary { list-style: none; cursor: pointer; }
.bf-cat > summary::-webkit-details-marker { display: none; }
.bf-cat > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.bf-cat[open] > summary,
.bf-cat-body { background: color-mix(in srgb, var(--ink) 3%, transparent); }
.bf-cat-body { padding: 0.2rem 0.9rem 0.9rem; }
.bf-cat-text { display: grid; min-width: 0; }
.bf-cat-name { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bf-cat-meta { font-size: 0.8rem; color: var(--ink-muted); font-variant-numeric: tabular-nums; }
.bf-cat-fixed { justify-self: end; }
.bf-cat-edit > div > label:first-child { margin-top: 0; }
/* Löschen abgesetzt unter dem Formular, mit dem Ziel für die Einträge. */
.bf-cat-delete {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 0.6rem;
  margin-top: 1rem;
  padding-top: 0.9rem;
  border-top: 1px dashed var(--line-strong);
}
.bf-cat-delete label { flex-basis: 100%; margin: 0; }
.bf-cat-delete select { flex: 1 1 12rem; width: auto; }
.bf-cat-delete .btn { margin-top: 0; }
/* Runder Symbol-Knopf (Stift) in der Zeile; die ganze Zeile klappt auf. */
.bf-icon-btn {
  display: inline-grid;
  place-items: center;
  width: 2.1rem;
  height: 2.1rem;
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  color: var(--ink-muted);
  transition:
    color var(--dur-state) var(--ease-out),
    background-color var(--dur-state) var(--ease-out),
    transform var(--dur-feedback) var(--ease-out);
}
.bf-icon-btn svg { width: 1rem; height: 1rem; }
.bf-cat > summary:hover .bf-icon-btn,
.bf-cat[open] > summary .bf-icon-btn { color: var(--ink); background: var(--surface-2); }
.bf-cat > summary:active .bf-icon-btn { transform: scale(0.94); }
/* Farbwahl: die Diagramm-Farben als runde Felder, vergebene gesperrt. */
.bf-colors { border: 0; padding: 0; margin: 0.8rem 0 0; min-width: 0; }
.bf-colors legend {
  padding: 0;
  margin: 0 0 0.4rem;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--ink-muted);
}
.bf-color-row { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.bf-color { position: relative; display: inline-grid; margin: 0; cursor: pointer; }
.bf-color input { position: absolute; opacity: 0; width: 0; height: 0; }
.bf-color-dot {
  display: block;
  width: 1.6rem;
  height: 1.6rem;
  border-radius: 999px;
  background: var(--c);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--ink) 14%, transparent);
  transition:
    box-shadow var(--dur-state) var(--ease-out),
    transform var(--dur-feedback) var(--ease-out),
    opacity var(--dur-state) var(--ease-out);
}
.bf-color:hover input:not(:disabled) + .bf-color-dot { transform: scale(1.08); }
.bf-color input:checked + .bf-color-dot { box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px var(--c); }
.bf-color input:focus-visible + .bf-color-dot { outline: 2px solid var(--accent); outline-offset: 5px; }
/* Vergeben: blass und durchgestrichen, nicht Farbe allein. */
.bf-color input:disabled + .bf-color-dot {
  opacity: 0.3;
  background: linear-gradient(135deg, transparent 45%, var(--surface) 45%, var(--surface) 55%, transparent 55%), var(--c);
}
.bf-color:has(input:disabled) { cursor: not-allowed; }
.bf-cat-new { margin-top: 1.2rem; padding-top: 1rem; border-top: 1px solid var(--line); }
.bf-cat-new h3 { margin: 0; }
.bf-cat-new > div > label { margin-top: 0.6rem; }
.bf-cats-foot { justify-content: flex-end; }

@media (max-width: 56rem) {
  .bf-lower { grid-template-columns: minmax(0, 1fr); gap: 2rem; }
}
/* Zweispaltig bleibt die Einnahmen-Spalte beim Scrollen stehen. */
@media (min-width: 56.0625rem) {
  .bf-lower > .bf-col:first-child { position: sticky; top: 4.5rem; }
}
@media (max-width: 40rem) {
  .bf-flow-card { padding: 1.1rem 1rem 1rem; }
  .bf-summary { font-size: 1rem; }
  /* Kategorie-Chips so klein wie die Einträge, sonst passen die Namen nicht
     zwischen Stapel und Kategorie-Spalte (budget-flow.js misst mit). */
  .bf-label.is-cat { font-size: 0.8rem; }
  /* Der Link zum Sub Tracker bekommt eine eigene Zeile unter dem Kopf. */
  .sub-group-head:has(.bf-group-link) { grid-template-columns: minmax(0, 1fr) auto 0.85rem; row-gap: 0.3rem; }
  .sub-group-head:has(.bf-group-link) .sub-group-total { grid-column: 2; grid-row: 1; }
  .sub-group-head .bf-group-link { grid-column: 1 / -1; grid-row: 2; justify-self: start; }
}
@media (pointer: coarse) {
  .bf-legend button,
  .bf-sources button { min-height: 44px; }
  .bf-seg span { min-height: 40px; display: inline-flex; align-items: center; }
  .bf-head-edit { width: 2.2rem; height: 2.2rem; }
  .bf-icon-btn { width: 44px; height: 44px; }
  .bf-color-dot { width: 2rem; height: 2rem; }
}

/* ---------- Startseite (templates/home/, home.js) ----------
   Eingeloggt "Briefing und Feed": Gruß und Tages-Satz, "Dein Tag" (oder die
   Ersten Schritte), links der Feed aus Neuigkeiten und Postfach, rechts die
   Wege. Gäste bekommen die Erklärseite mit Vorschauen aus echten
   App-Komponenten. Die Bewegung steht im Abschnitt "Bewegung"
   ("Startseite: Neues landet auf dem Stapel"). */

.home { padding-top: 0.25rem; }
/* Textauswahl in der Akzentfarbe statt Browser-Blau. */
.home ::selection { background: var(--accent-strong); color: var(--ink); }

/* Gemeinsamer Abschnittskopf: Titel links, eine Aktion rechts. */
.home-section-head {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.5rem 0.9rem;
  margin: 0 0 0.85rem;
}
.home-section-head h2 { margin: 0; }
.home-section-head > :last-child:not(:first-child) { margin-left: auto; }

/* Kleiner Textlink (Alle Topics, Kontakt, Postfach öffnen). */
.home-link {
  font-size: 0.88rem;
  font-weight: 600;
  color: var(--accent);
  text-decoration: underline;
  text-decoration-color: transparent;
  text-decoration-thickness: 2px;
  text-underline-offset: 0.22em;
  border-radius: 4px;
  transition: text-decoration-color var(--dur-state) var(--ease-out);
}
.home-link:hover,
.home-link:focus-visible { text-decoration-color: currentColor; }
.home-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Ausblenden-Knopf: Icon vor dem Text, ruhiger als ein normaler Ghost. */
.home-hide { color: var(--ink-muted); }
.home-hide:hover { color: var(--ink); }
.home-hide svg { width: 1rem; height: 1rem; flex: none; }

/* Kopf: Gruß und Tages-Satz. */
.home-head { margin: 0 0 1.9rem; max-width: 48rem; }
.home-greeting {
  font-size: clamp(1.8rem, 3vw, 2.4rem);
  line-height: 1.15;
  margin: 0 0 0.5rem;
}
.home-brief {
  margin: 0;
  font-size: 1.08rem;
  color: var(--ink-muted);
  text-wrap: pretty;
}
.home-date { color: var(--ink); font-weight: 500; }
/* Satzteile als Links: Ink-Text mit blauer Unterstreichung, die beim Hover
   satt wird. So bleibt der Satz lesbar und zeigt trotzdem, was klickbar ist. */
.home-brief a {
  color: var(--ink);
  font-weight: 600;
  text-decoration: underline;
  text-decoration-color: var(--accent-strong);
  text-decoration-thickness: 2px;
  text-underline-offset: 0.24em;
  border-radius: 4px;
  transition: text-decoration-color var(--dur-state) var(--ease-out), color var(--dur-state) var(--ease-out);
}
.home-brief a:hover { text-decoration-color: var(--accent); }
.home-brief a:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Dein Tag: je abonniertem Topic eine Karte, die ganze Karte ist der Link. */
.day { margin: 0 0 2.4rem; }
.day-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15.5rem, 1fr));
  gap: 1.1rem;
}
.day-card.card-link {
  display: flex;
  flex-direction: column;
  margin: 0;
  padding: 1rem 1.15rem 1.05rem;
}
.day-card-head {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin-bottom: 0.55rem;
}
.day-icon {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  border-radius: 10px;
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  color: var(--accent);
}
.day-icon svg { width: 1.05rem; height: 1.05rem; }
.day-card-title {
  font-family: var(--font-display);
  font-size: 1.05rem;
  font-weight: 700;
  letter-spacing: -0.015em;
  white-space: nowrap;
}
.day-card-meta {
  min-width: 0;
  margin-left: auto;
  overflow: hidden;
  text-overflow: ellipsis;
  font-size: 0.82rem;
  font-weight: 500;
  color: var(--ink-muted);
  white-space: nowrap;
}
.day-rows { display: block; }
.day-row {
  display: flex;
  align-items: baseline;
  gap: 0.7rem;
  padding: 0.42rem 0;
  border-top: 1px solid var(--line);
  font-size: 0.92rem;
}
.day-label {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--ink);
}
.day-sym { margin-left: 0.25rem; font-size: 0.82rem; color: var(--ink-muted); }
.day-when {
  font-size: 0.85rem;
  color: var(--ink-muted);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.day-amount {
  min-width: 4.6rem;
  text-align: right;
  font-weight: 600;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.day-due { font-size: 0.85rem; font-weight: 600; color: var(--ink-muted); white-space: nowrap; }
.day-due.is-today { color: var(--ink); }
/* Überfällig: Wort plus Farbe, nie Farbe allein. */
.day-due.is-overdue { color: var(--danger); }
.day-foot {
  margin-top: auto;
  padding-top: 0.55rem;
  font-size: 0.85rem;
  color: var(--ink-muted);
  font-variant-numeric: tabular-nums;
}
/* Leere Karte (neuer Nutzer): ein Satz und der Weg zum ersten Eintrag. */
.day-empty { color: var(--ink-muted); font-size: 0.95rem; text-wrap: pretty; }
.day-card .day-cta {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding-top: 0.85rem;
  font-weight: 600;
  color: var(--accent);
}
.day-cta svg {
  width: 1rem;
  height: 1rem;
  transition: transform var(--dur-state) var(--ease-out);
}
.day-card:hover .day-cta svg { transform: translateX(calc(3px * var(--motion-travel))); }

/* Erste Schritte (neue Konten): vier nummerierte Schritte nebeneinander. */
.setup { margin: 0 0 2.4rem; padding: 1.15rem 1.4rem 1.3rem; }
.setup-progress {
  margin: 0;
  font-size: 0.88rem;
  color: var(--ink-muted);
  font-variant-numeric: tabular-nums;
}
.setup .home-section-head { margin-bottom: 1rem; }
.setup .home-section-head .setup-progress { margin-left: 0; }
.setup-steps {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
}
.setup-step {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  padding: 0.15rem 1.2rem 0;
  border-left: 1px solid var(--line);
}
.setup-step:first-child { padding-left: 0; border-left: 0; }
.setup-step:last-child { padding-right: 0; }
.setup-mark {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.9rem;
  height: 1.9rem;
  margin-bottom: 0.65rem;
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  color: var(--ink-muted);
  font-weight: 700;
  font-size: 0.9rem;
  font-variant-numeric: tabular-nums;
}
.setup-check { position: absolute; inset: 0; display: none; align-items: center; justify-content: center; }
.setup-check svg { width: 1rem; height: 1rem; }
/* Erledigt: Statusfarbe als Paar (Text plus Tönung), wie die Häkchen im To Do. */
.setup-step.is-done .setup-mark {
  border-color: transparent;
  background: var(--success-bg);
  color: var(--success);
}
.setup-step.is-done .setup-num { visibility: hidden; }
.setup-step.is-done .setup-check { display: flex; }
.setup-step h3 { margin: 0 0 0.25rem; font-size: 1rem; }
.setup-step p { margin: 0 0 0.85rem; font-size: 0.92rem; color: var(--ink-muted); text-wrap: pretty; }
.setup-step .btn { margin-top: auto; }
.setup-step.is-done h3 { color: var(--ink-muted); }
.setup-done-label {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  margin-top: auto;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--success);
}
.setup-done-label svg { width: 0.95rem; height: 0.95rem; }
.setup.is-complete .setup-progress { color: var(--success); font-weight: 600; }

/* Zwei Spalten: Feed links, Wege rechts. */
.home-cols {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 22rem;
  gap: 2.25rem;
  align-items: start;
}
.rail { display: grid; gap: 1.1rem; }
.rail-card { margin: 0; padding: 1.1rem 1.15rem 1rem; }
.rail-card .home-section-head { margin-bottom: 0.55rem; }
.rail-list { list-style: none; margin: 0 -0.55rem; padding: 0; }
.rail-row {
  display: grid;
  grid-template-columns: 2.1rem minmax(0, 1fr) auto;
  align-items: center;
  column-gap: 0.75rem;
  padding: 0.5rem 0.55rem;
  border-radius: 10px;
  color: var(--ink);
  text-decoration: none;
}
a.rail-row { transition: background-color var(--dur-state) var(--ease-out); }
a.rail-row:hover { background: var(--surface-2); }
a.rail-row:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.rail-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2.1rem;
  height: 2.1rem;
  border-radius: 10px;
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  color: var(--accent);
}
.rail-icon svg { width: 1.1rem; height: 1.1rem; }
.rail-icon-glyph { font-size: 1.2rem; font-weight: 700; line-height: 1; }
.rail-text { min-width: 0; }
.rail-title { display: block; font-weight: 600; font-size: 0.95rem; line-height: 1.3; }
.rail-sub {
  display: block;
  margin-top: 0.05rem;
  font-size: 0.85rem;
  color: var(--ink-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.rail-sub-wrap { white-space: normal; text-wrap: pretty; }
/* Warum dieser Vorschlag: Anmerkung in Harbor Teal, nie klickbar. */
.rail-reason {
  display: block;
  margin: 0.1rem 0 0.15rem;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--accent-2);
}
.rail-chevron { display: flex; color: var(--ink-faint); }
.rail-chevron svg {
  width: 1rem;
  height: 1rem;
  transition: transform var(--dur-state) var(--ease-out);
}
a.rail-row:hover .rail-chevron { color: var(--ink-muted); }
a.rail-row:hover .rail-chevron svg { transform: translateX(calc(2px * var(--motion-travel))); }
.rail-note { margin: 0.55rem 0 0.1rem; font-size: 0.85rem; }
/* Entdecken: noch nicht abonniert, darum neutral wie im Topic Hub. Der Knopf
   steht unter dem Text, damit die Beschreibung die volle Breite der
   schmalen Spalte bekommt. */
.discover-row { grid-template-columns: 2.1rem minmax(0, 1fr); align-items: start; row-gap: 0.55rem; }
.discover-row .rail-icon { background: var(--surface-2); color: var(--ink-muted); }
.discover-row form { grid-column: 2; justify-self: start; }
.discover-row form.is-busy .hub-toggle { opacity: 0.6; cursor: progress; }

/* Feed: Ankündigungen und Postfach in einem Strom. */
.feed-write .feed-write-icon { display: flex; color: var(--danger); }
.feed-write svg { width: 1rem; height: 1rem; }
.feed-filter {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  min-inline-size: 0;
  margin: 0 0 1rem;
  padding: 0;
  border: 0;
}
/* Zähler im Postfach-Chip (sitzt im Chip-Span, die Chip-Optik gilt nur
   direkten Kindern von .hub-chip). */
.chip-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.25rem;
  height: 1.25rem;
  margin-left: 0.4rem;
  padding: 0 0.35rem;
  border-radius: 999px;
  background: var(--accent-fill);
  color: var(--accent-ink);
  font-size: 0.8rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
/* Einträge als Stapel gerundeter Zeilen, keine Karten in Karten. Nur Titel
   und Aktion sind klickbar, darum hebt sich die Zeile nicht an. */
.feed-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}
.feed-item {
  padding: 1rem 1.2rem 1.05rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 12px;
  box-shadow: var(--shadow-sm);
}
.feed-meta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.35rem 0.55rem;
  margin: 0 0 0.45rem;
  font-size: 0.85rem;
  color: var(--ink-muted);
}
.feed-source {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-weight: 600;
  color: var(--ink);
}
.feed-source .brand-mark { width: 1.15rem; height: 1.15rem; flex: none; }
.feed-source-inbox svg { width: 1.05rem; height: 1.05rem; flex: none; color: var(--accent); }
.feed-time {
  margin-left: auto;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.feed-title { margin: 0 0 0.3rem; font-size: 1.05rem; }
/* Reiner Text wie im Postfach: Zeilenumbrüche der Nachricht bleiben. */
.feed-text { margin: 0; color: var(--ink-muted); max-width: 64ch; white-space: pre-wrap; text-wrap: pretty; }
.feed-action {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  margin-top: 0.75rem;
  font-size: 0.92rem;
  font-weight: 600;
  color: var(--accent);
  text-decoration: underline;
  text-decoration-color: transparent;
  text-decoration-thickness: 2px;
  text-underline-offset: 0.22em;
  border-radius: 4px;
  transition: text-decoration-color var(--dur-state) var(--ease-out), color var(--dur-state) var(--ease-out);
}
.feed-action svg {
  width: 1rem;
  height: 1rem;
  flex: none;
  transition: transform var(--dur-state) var(--ease-out);
}
.feed-action:hover { color: var(--accent-hover); text-decoration-color: currentColor; }
.feed-action:hover svg { transform: translateX(calc(3px * var(--motion-travel))); }
.feed-action:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Linie "Dein letzter Besuch": darüber liegt, was seitdem dazukam. */
.feed-divider {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin: 0.35rem 0;
  font-size: 0.82rem;
  font-weight: 500;
  color: var(--ink-muted);
  font-variant-numeric: tabular-nums;
}
.feed-divider::before,
.feed-divider::after {
  content: "";
  height: 1px;
  background: var(--line-strong);
}
.feed-divider::before { flex: 0 0 1rem; }
.feed-divider::after { flex: 1; }
.feed-divider span {
  padding: 0.15rem 0.7rem;
  border: 1px solid var(--line);
  border-radius: 999px;
  white-space: nowrap;
}
.feed-empty { margin: 0; }
.feed-foot {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.75rem 1.25rem;
  margin-top: 1rem;
}

/* Hilfe (FAQ), auch auf der Gästeseite. */
.faq { margin: 0 -0.55rem; }
.faq-item + .faq-item { border-top: 1px solid var(--line); }
.faq-item summary {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 0.6rem;
  padding: 0.6rem 0.55rem;
  border-radius: 8px;
  font-size: 0.95rem;
  font-weight: 600;
  list-style: none;
  cursor: pointer;
  transition: background-color var(--dur-state) var(--ease-out);
}
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item summary:hover { background: var(--surface-2); }
.faq-item summary:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.faq-caret { display: flex; color: var(--ink-muted); }
.faq-caret svg {
  width: 1rem;
  height: 1rem;
  transition: transform var(--dur-state) var(--ease-out);
}
.faq-item[open] .faq-caret svg { transform: rotate(180deg); }
.faq-item p {
  margin: 0;
  padding: 0 0.55rem 0.8rem;
  font-size: 0.92rem;
  color: var(--ink-muted);
  text-wrap: pretty;
}
/* Aufklappen gleitet, wo der Browser es kann (Chrome/Edge 131+); sonst
   springt es wie bisher. */
@supports selector(::details-content) {
  .faq-item { interpolate-size: allow-keywords; }
  .faq-item::details-content {
    height: 0;
    overflow: clip;
    transition: height var(--dur-layout) var(--ease-out), content-visibility var(--dur-layout) allow-discrete;
  }
  .faq-item[open]::details-content { height: auto; }
}

/* Gäste: von oben nach unten erzählt. Kopf mit Kartenstapel, die Topics mit
   echten App-Komponenten als Vorschau (inert, Beispieldaten aus
   GuestShowcase), drei Schritte, Fragen, Abschluss. */
.gx-hero {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 3rem;
  align-items: center;
  margin: 0.5rem 0 5rem;
}
/* Display-Stufe für die Gästeseite: eine Stufe größer als der Gruß der
   eingeloggten Startseite, weil sie Fremde abholen muss. */
.gx-title {
  max-width: 16ch;
  margin-bottom: 1rem;
  font-size: clamp(2.1rem, 4vw, 2.9rem);
  line-height: 1.08;
  letter-spacing: -0.02em;
}
.gx-lead {
  max-width: 36rem;
  margin: 0;
  font-size: 1.12rem;
  color: var(--ink-muted);
  text-wrap: pretty;
}
.gx-hero .hero-actions { margin-top: 1.7rem; }
/* Der Stapel: drei gleich große Karten, jede einen Schritt nach rechts oben
   hinter der vorderen, wie die Bildmarke. --i: 0 vorne, 2 hinten. */
.gx-stack {
  --card-w: 19.5rem;
  --card-h: 15rem;
  --offset: 2.9rem;
  position: relative;
  display: block;
  margin: 0;
  width: calc(var(--card-w) + 2 * var(--offset));
  height: calc(var(--card-h) + 2 * var(--offset));
  pointer-events: none;
  user-select: none;
}
/* Zwei Klassen: die mobile Breite der To-Do-Spalten (Abschnitt
   "Smartphone und Touch", weiter unten) gilt im Stapel nicht. */
.gx-stack .gx-stack-card {
  position: absolute;
  left: calc(var(--i) * var(--offset));
  top: calc((2 - var(--i)) * var(--offset));
  z-index: calc(3 - var(--i));
  width: var(--card-w);
  height: var(--card-h);
  margin: 0;
  overflow: hidden;
  box-shadow: var(--shadow);
}
.gx-stack-card.todo-column { border: 1px solid var(--line); }
/* Hintere Karten treten zurück wie bei der Bildmarke: ihre Köpfe sind
   nicht größer als der Spaltenkopf im To Do, die vordere Karte führt. */
.gx-stack-card.subdash-panel h2 { margin-bottom: 0.7rem; font-size: 0.93rem; }
.gx-stack-note {
  position: absolute;
  right: 0;
  bottom: -1.6rem;
  margin: 0;
  font-size: 0.8rem;
  color: var(--ink-muted);
}
/* Die hinteren Karten zeigen nur Kopf und eine leere Kante: der Inhalt
   hält Abstand vom sichtbaren rechten Streifen. */
.gx-stack-card .todo-cards,
.gx-stack-card .upcoming-list { margin-right: var(--offset); }

.gx-section-head { max-width: 42rem; margin: 0 0 1.5rem; }
.gx-section-head h2,
.gx-faq-intro h2,
.gx-close h2 { margin: 0 0 0.45rem; font-size: 1.6rem; }
.gx-section-head p { margin: 0; font-size: 1.05rem; color: var(--ink-muted); text-wrap: pretty; }

.gx-features { margin: 0 0 4.5rem; }
.gx-feature {
  display: grid;
  grid-template-columns: minmax(0, 0.82fr) minmax(0, 1.18fr);
  gap: 3rem;
  align-items: center;
  padding: 2.6rem 0;
}
.gx-feature + .gx-feature { border-top: 1px solid var(--line); }
/* Im Wechsel: Text links, dann rechts; die Vorschau behält die breite Spalte. */
.gx-feature:nth-of-type(even) { grid-template-columns: minmax(0, 1.18fr) minmax(0, 0.82fr); }
.gx-feature:nth-of-type(even) .gx-feature-text { order: 2; }
.gx-feature-name {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.35rem 0.7rem;
  margin-bottom: 0.9rem;
}
.gx-feature-name h3 { margin: 0; font-size: 1.2rem; }
.gx-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2.4rem;
  height: 2.4rem;
  border-radius: 10px;
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  color: var(--accent);
}
.gx-icon svg { width: 1.25rem; height: 1.25rem; }
/* Kategorie als Anmerkung in Harbor Teal (nie klickbar). */
.gx-cat { font-size: 0.85rem; font-weight: 600; color: var(--accent-2); }
.gx-claim {
  margin: 0 0 1.1rem;
  font-family: var(--font-display);
  font-size: 1.45rem;
  font-weight: 700;
  line-height: 1.3;
  letter-spacing: -0.015em;
  color: var(--ink);
  text-wrap: balance;
}
.gx-points { display: grid; gap: 0.6rem; margin: 0; padding: 0; list-style: none; }
.gx-points li {
  display: grid;
  grid-template-columns: 1.1rem minmax(0, 1fr);
  column-gap: 0.6rem;
  color: var(--ink-muted);
  text-wrap: pretty;
}
.gx-points svg { width: 1.05rem; height: 1.05rem; margin-top: 0.22rem; color: var(--accent-2); }

/* Rahmen um die Vorschau: ein Ausschnitt der App auf Raised Mist, damit
   die weißen Karten darin stehen wie auf der echten Seite. */
.gx-frame {
  position: relative;
  padding: 1.2rem;
  overflow: hidden;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: 18px;
  pointer-events: none;
  user-select: none;
}
.gx-frame .card { margin: 0; }
.gx-market-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1rem; margin: 0; }
/* Abo-Dashboard: Diagramm-Karte oben, Abbuchungen darunter, beide in voller
   Breite, damit Namen und Beträge nicht abgeschnitten werden. */
.gx-subs { display: grid; gap: 1rem; }
/* Board: Spalten laufen rechts aus dem Rahmen wie auf der echten Seite,
   eine Blende zeigt, dass es weitergeht. */
.gx-frame-board { padding-right: 0; }
.gx-frame-board .todo-columns { width: max-content; }
.gx-frame-board::after {
  content: "";
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  width: 3.5rem;
  background: linear-gradient(to right, transparent, var(--surface-2));
}
.gx-frame-board .todo-column { background: var(--paper); }
.gx-calc { display: grid; gap: 1rem; }
/* Die Money-Math-Karten tragen auf ihrer Seite Rasterbereiche (hero, chart),
   die es hier nicht gibt; ohne das lägen beide in derselben Zelle. */
.gx-calc > * { grid-area: auto; }

.gx-start { margin: 0 0 4.5rem; }
.gx-steps {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 2rem;
  margin: 0;
  padding: 0;
  list-style: none;
}
.gx-steps li { position: relative; }
/* Haarlinie von Nummer zu Nummer: die Schritte bilden einen Weg. */
.gx-steps li:not(:last-child)::after {
  content: "";
  position: absolute;
  top: 0.95rem;
  left: 2.6rem;
  right: -1.4rem;
  height: 1px;
  background: var(--line-strong);
}
.gx-steps .step-num { margin-bottom: 0.85rem; }
.gx-steps h3 { margin: 0 0 0.3rem; font-size: 1.05rem; }
.gx-steps p { margin: 0; color: var(--ink-muted); text-wrap: pretty; }
.step-num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.9rem;
  height: 1.9rem;
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  background: var(--surface);
  color: var(--ink-muted);
  font-weight: 700;
  font-size: 0.9rem;
  font-variant-numeric: tabular-nums;
}

.gx-faq {
  display: grid;
  grid-template-columns: minmax(0, 0.82fr) minmax(0, 1.18fr);
  gap: 3rem;
  align-items: start;
  margin: 0 0 4rem;
}
.gx-faq-intro p { margin: 0; color: var(--ink-muted); }
.gx-faq-card { margin: 0; padding: 0.55rem 1.15rem; }

.gx-close {
  display: grid;
  justify-items: center;
  margin: 0;
  padding: 2.3rem 2rem 2.4rem;
  text-align: center;
}
.gx-close p { margin: 0; }
.gx-close .hero-actions { justify-content: center; margin-top: 1.3rem; }

/* Topics-Seite (/topics, die Kachel-Übersicht hinter der Pill). */
.topics-lead { margin: -0.2rem 0 1.4rem; max-width: 40rem; }

@media (max-width: 60rem) {
  .home-cols { grid-template-columns: minmax(0, 1fr); gap: 2rem; }
  .rail { grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr)); align-items: start; }
  .gx-hero { grid-template-columns: minmax(0, 1fr); gap: 2.5rem; margin-bottom: 4rem; }
  .gx-stack { justify-self: center; }
  .gx-feature,
  .gx-feature:nth-of-type(even),
  .gx-faq { grid-template-columns: minmax(0, 1fr); gap: 1.6rem; }
  .gx-feature:nth-of-type(even) .gx-feature-text { order: 0; }
  .setup-steps { grid-template-columns: repeat(2, minmax(0, 1fr)); row-gap: 1.1rem; }
  .setup-step:nth-child(3) { padding-left: 0; border-left: 0; }
  .setup-step:nth-child(2) { padding-right: 0; }
}

@media (max-width: 40rem) {
  .home-head { margin-bottom: 1.4rem; }
  .home-brief { font-size: 1rem; }
  .day { margin-bottom: 2rem; }
  /* Phone: die Karten von "Dein Tag" liegen nebeneinander zum Wischen,
     die nächste schaut am Rand hervor. */
  .day-grid {
    grid-template-columns: none;
    grid-auto-flow: column;
    grid-auto-columns: min(21rem, calc(100vw - 4.5rem));
    gap: 0.75rem;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scroll-snap-type: x mandatory;
    scrollbar-width: none;
    margin: -0.5rem -1rem;
    padding: 0.5rem 1rem 0.75rem;
  }
  .day-grid::-webkit-scrollbar { display: none; }
  .day-card.card-link { scroll-snap-align: start; scroll-margin-left: 1rem; }
  .setup { padding: 1.05rem 1rem 1.1rem; }
  /* Kopf: Titel und Ausblenden in einer Zeile, der Fortschritt darunter. */
  .setup .home-section-head .setup-progress { order: 2; flex-basis: 100%; margin-top: -0.35rem; }
  /* Entdecken: die Vollbreite der Umschalter aus dem Topic Hub gilt hier nicht. */
  .discover-row .hub-toggle { width: auto; padding: 0 0.85rem 0 0.7rem; }
  .setup-steps { grid-template-columns: minmax(0, 1fr); row-gap: 0; }
  .setup-step,
  .setup-step:nth-child(n) {
    display: grid;
    grid-template-columns: 1.9rem minmax(0, 1fr);
    column-gap: 0.8rem;
    padding: 0.85rem 0 0.9rem;
    border-left: 0;
    border-top: 1px solid var(--line);
  }
  .setup-step:first-child { border-top: 0; padding-top: 0.2rem; }
  .setup-mark { grid-row: 1 / span 3; margin: 0; }
  .setup-step h3, .setup-step p, .setup-step .btn, .setup-done-label { grid-column: 2; justify-self: start; }
  .setup-step p { margin-bottom: 0.6rem; }
  .feed-item { padding: 0.9rem 1rem 0.95rem; }
  .rail { grid-template-columns: minmax(0, 1fr); }
  .rail-card { padding: 1rem 1rem 0.9rem; }
  .gx-hero { gap: 2rem; margin: 0 0 3.25rem; }
  .gx-title { max-width: none; }
  /* Der Stapel schrumpft mit, bleibt aber ein Stapel. */
  .gx-stack { --card-w: min(16rem, calc(100vw - 7.75rem)); --card-h: 14.75rem; }
  .gx-feature { padding: 2rem 0; }
  .gx-claim { font-size: 1.25rem; }
  .gx-frame { padding: 0.85rem; border-radius: 14px; }
  .gx-market-grid { grid-template-columns: minmax(0, 1fr); }
  .gx-market-grid .market-card + .market-card { display: none; }
  .gx-subs .donut-wrap { flex-direction: column; align-items: stretch; }
  .gx-subs .donut-ring { align-self: center; }
  .gx-steps { grid-template-columns: minmax(0, 1fr); gap: 1.4rem; }
  .gx-steps li { display: grid; grid-template-columns: 1.9rem minmax(0, 1fr); column-gap: 0.85rem; }
  .gx-steps .step-num { grid-row: 1 / span 2; margin: 0; }
  .gx-steps li:not(:last-child)::after { top: 2.2rem; bottom: -1.2rem; left: calc(0.95rem - 0.5px); right: auto; width: 1px; height: auto; }
  .gx-close { padding: 1.8rem 1.1rem 1.9rem; }
}

@media (pointer: coarse) {
  .faq-item summary { min-height: 44px; }
  a.rail-row { min-height: 44px; }
}

/* ---------- Ankündigungen (Admin): Kanäle und Vorschau ----------
   Drei Kanäle statt "Also send by email": Postfach, Startseite, E-Mail,
   frei kombinierbar; die ganze Zeile ist das Label. Daneben die Vorschau
   (announcement-preview.js) mit denselben Bausteinen wie im Postfach und im
   Feed, auf einer vertieften Bühne in Paper (keine Karte in der Karte). */
.announce-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 30rem);
  gap: 1.5rem;
  align-items: start;
}
.announce-layout > .card { margin: 0; }
.announce-channels {
  min-width: 0;
  margin: 1.1rem 0 0;
  padding: 0;
  border: 0;
}
.announce-channels legend {
  padding: 0;
  margin: 0 0 0.35rem;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--ink-muted);
}
.announce-channel {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  column-gap: 0.7rem;
  align-items: start;
  margin: 0;
  padding: 0.6rem 0.75rem;
  border: 1px solid var(--line);
  border-radius: 10px;
  cursor: pointer;
  font-size: 0.95rem;
  font-weight: 400;
  color: var(--ink);
  transition: border-color var(--dur-state) var(--ease-out), background-color var(--dur-state) var(--ease-out);
}
.announce-channel + .announce-channel { margin-top: 0.45rem; }
.announce-channel:hover { border-color: var(--line-strong); }
.announce-channel:has(input:checked) {
  border-color: color-mix(in srgb, var(--accent) 55%, transparent);
  background: color-mix(in srgb, var(--accent) 6%, transparent);
}
.announce-channel input { margin: 0.2rem 0 0; accent-color: var(--accent); }
.announce-channel-name { display: block; font-weight: 600; }
.announce-channel-hint { display: block; font-size: 0.85rem; color: var(--ink-muted); }
.announce-channel-note { margin: 0.5rem 0 0; font-size: 0.85rem; color: var(--ink-muted); }

/* Die Vorschau bleibt beim Scrollen durchs lange Formular im Blick. */
.announce-preview {
  position: sticky;
  top: 5rem;
  padding: 1.1rem 1.15rem 1.2rem;
}
.announce-preview-head {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.5rem 0.9rem;
  margin-bottom: 0.8rem;
}
.announce-preview-head h2 { margin: 0; }
.announce-preview-head .segmented { margin-left: auto; }
.announce-preview-tabs { display: flex; }
.announce-preview-tabs label { flex: 1; }
.announce-preview-tabs span { flex: 1; text-align: center; }
.announce-preview-status { margin: 0.75rem 0 0; font-size: 0.85rem; font-weight: 600; color: var(--warning); }
.announce-preview-pane {
  margin-top: 0.8rem;
  padding: 0.9rem;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: 12px;
  transition: opacity var(--dur-state) var(--ease-out);
}
/* Kanal nicht gewählt: die Vorschau tritt zurück, der Satz darüber sagt warum. */
.announce-preview-pane.is-off { opacity: 0.5; border-style: dashed; }
.announce-preview-pane .sub-list,
.announce-preview-pane .sub-item { margin: 0; }
.announce-mail-head {
  display: grid;
  gap: 0.2rem;
  margin: 0 0 0.7rem;
  font-size: 0.88rem;
}
.announce-mail-head div { display: grid; grid-template-columns: 4.2rem minmax(0, 1fr); gap: 0.6rem; }
.announce-mail-head dt { color: var(--ink-muted); }
.announce-mail-head dd { margin: 0; font-weight: 600; overflow-wrap: anywhere; }
/* Mails sind für helle Mailprogramme gebaut: der Rahmen bleibt hell, auch
   im dunklen Theme. Das iframe rendert in Mailbreite (640px) und wird über
   --mail-scale auf die Spalte verkleinert (announcement-preview.js). */
.announce-mail-viewport {
  position: relative;
  height: 26rem;
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: 10px;
  color-scheme: light;
  background: Canvas;
}
.announce-mail-frame {
  position: absolute;
  top: 0;
  left: 0;
  width: 640px;
  height: calc(26rem / var(--mail-scale, 1));
  border: 0;
  transform: scale(var(--mail-scale, 1));
  transform-origin: 0 0;
}
.announce-mail-empty { margin: 0; padding: 2rem 1rem; text-align: center; font-size: 0.9rem; color: var(--ink-muted); }

@media (max-width: 64rem) {
  .announce-layout { grid-template-columns: minmax(0, 1fr); }
  .announce-preview { position: static; }
}


/* ---------- Footer (Prototyp) ---------- */
/* Bewusst flach: eine Zeile Navigation plus eine Bodenleiste. Alles, was
   Inhalt traegt (Anbieterkennung, Verantwortlicher, Streitbeilegung), liegt
   auf den verlinkten Unterseiten. § 5 DDG ist mit dem Link erfuellt, die
   sichtbare Adresse im Footer war reine Konvention. */

/* Der Footer soll NIE ohne Scrollen sichtbar sein, auch nicht auf kurzen
   Seiten wie dem Login. Deshalb ist der Inhaltsbereich mindestens eine
   Fensterhoehe hoch: der Footer beginnt damit immer unterhalb der Kante und
   man muss ein Stueck scrollen, um ihn zu sehen.
   Bewusst KEIN Sticky-Footer (body als Flex-Spalte): der wuerde ihn auf kurzen
   Seiten genau ans Fensterende heften und damit sofort sichtbar machen.
   Nebeneffekt: main bleibt ein normaler Block und braucht kein width:100%,
   um die 76rem aus .container zu behalten.
   100vh statt 100dvh ist Absicht: auf Mobilgeraeten mit ein- und
   ausblendender Browserleiste bleibt die Zusage "liegt unter der Kante"
   so in jeder Stellung erhalten. */
main.container { min-height: 100vh; }

.site-footer {
  background: var(--surface);
  border-top: 1px solid var(--line);
  /* Kein margin-top: .container bringt schon 4rem Bodenabstand mit. */
}
.footer-inner {
  max-width: 76rem;
  margin: 0 auto;
  padding: 1.3rem 1.5rem 1.1rem;
}

.footer-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.7rem 2rem;
}

/* Optik wie .brand in der Navbar, nur eine Spur kleiner. */
.footer-brand {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1rem;
  letter-spacing: -0.02em;
  text-decoration: none;
  color: var(--ink);
}

.footer-links-inline {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem 1.1rem;
}
.footer-links-inline a {
  color: var(--ink-muted);
  text-decoration: none;
  font-size: 0.9rem;
}
.footer-links-inline a:hover { color: var(--accent); }

/* Cookie-Steuerung ganz nach rechts, sie ist ein Bedienelement und keine
   Navigation (§ 25 TDDDG). */
.footer-cookie { margin-left: auto; }

.footer-bottom {
  border-top: 1px solid var(--line);
  margin-top: 1rem;
  padding-top: 0.8rem;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 1.4rem;
  font-size: 0.82rem;
  color: var(--ink-faint);
}
.footer-copy { margin: 0; }
.footer-top {
  margin-left: auto;
  color: var(--ink-faint);
  text-decoration: none;
}
.footer-top:hover { color: var(--accent); }

/* ---------- Prototyp-Markierung ---------- */
/* Nur fuer die Auswahlphase, faellt beim Livegang zusammen mit der
   Vorschauseite weg. Rot: hier muss Inhalt rein oder geprueft werden.
   Grau gestrichelt: Pflicht haengt an der Unternehmensgroesse. */

.legal-req {
  outline: 1px solid var(--danger);
  outline-offset: 4px;
  border-radius: 6px;
}
.legal-cond {
  outline: 1px dashed var(--ink-faint);
  outline-offset: 4px;
  border-radius: 6px;
}
.footer-links-inline li.legal-req-link a {
  color: var(--danger);
  font-weight: 600;
}
.footer-links-inline li.legal-cond-link a {
  color: var(--ink-muted);
  text-decoration: underline dashed;
  text-underline-offset: 3px;
}

/* Legende der Vorschauseite. */
.legal-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.2rem;
  align-items: center;
  margin: 0 0 1rem;
  font-size: 0.86rem;
  color: var(--ink-muted);
}
.legal-legend span {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
}
.legal-legend span::before {
  content: "";
  width: 1.1rem;
  height: 1.1rem;
  border-radius: 5px;
}
.legal-legend .is-req::before { border: 1px solid var(--danger); }
.legal-legend .is-cond::before { border: 1px dashed var(--ink-faint); }

/* Spalte "Essential" in der Checkliste. */
.req-yes { color: var(--danger); font-weight: 600; }
.req-dep { color: var(--ink-muted); }
.req-no { color: var(--ink-faint); }

@media (max-width: 48rem) {
  .footer-cookie,
  .footer-top { margin-left: 0; }
}
@media (max-width: 34rem) {
  .footer-inner { padding: 1.2rem 1.2rem 1rem; }
}

/* ---------- Smartphone und Touch ----------
   Zwei getrennte Ebenen, weil Bildschirmbreite und Eingabeart nichts
   miteinander zu tun haben (Tablet mit Tastatur, Laptop mit Touchscreen):
   1. (pointer: coarse) = Touch-Bedienung, unabhaengig von der Breite:
      Ziele mindestens 44px, 16px in Eingabefeldern (sonst zoomt iOS beim
      Fokus hinein), Wischen statt Pfeilknoepfen.
   2. (max-width: 40rem) = schmale Fenster, also Phones im Hochformat:
      Wurzelgroesse zurueck auf 16px, einspaltig, die Leiste wird zur
      Schublade, Popups werden zu Bottom Sheets.
   Media Queries rechnen rem IMMER mit 16px, unabhaengig von html { font-size }:
   40rem = 640px, 22.5rem = 360px. */

/* Menue-Knopf der Leiste: nur auf schmalen Fenstern sichtbar (siehe unten). */
.nav-menu-toggle { display: none; }

/* Schublade (dialog#nav-drawer): rechts angeschlagen, volle Hoehe. Mit JS ein
   echtes Modal (showModal: Backdrop, Fokusfalle, Escape); ohne JS oeffnet der
   Link auf #nav-drawer sie ueber :target, und "×" (href="#") schliesst sie. */
dialog.nav-drawer {
  position: fixed;
  inset: 0 0 0 auto;
  margin: 0;
  width: min(20rem, 88vw);
  max-width: none;
  height: 100dvh;
  max-height: none;
  padding: 0;
  border: 0;
  border-left: 1px solid var(--line);
  border-radius: 0;
  background: var(--surface);
  color: var(--ink);
  box-shadow: var(--shadow);
  overflow-y: auto;
  overscroll-behavior: contain;
  z-index: 60;
}
dialog.nav-drawer:target { display: block; }
dialog.nav-drawer::backdrop {
  background: rgba(6, 12, 14, 0.5);
  backdrop-filter: blur(2px);
}
/* Schublade gleitet von rechts herein und wieder hinaus. allow-discrete
   hält sie sichtbar, bis die Ausblendung fertig ist (ältere Browser
   schließen sofort). Ohne JS (:target) steht sie einfach offen da. */
dialog.nav-drawer,
dialog.nav-drawer::backdrop {
  transition:
    opacity var(--dur-state) var(--ease-leave),
    transform var(--dur-state) var(--ease-leave),
    overlay var(--dur-state) allow-discrete,
    display var(--dur-state) allow-discrete;
}
dialog.nav-drawer {
  opacity: 0;
  transform: translateX(calc(1.5rem * var(--motion-travel)));
}
dialog.nav-drawer::backdrop { opacity: 0; }
dialog.nav-drawer[open],
dialog.nav-drawer[open]::backdrop,
dialog.nav-drawer:target {
  opacity: 1;
  transition-duration: var(--dur-layout);
  transition-timing-function: var(--ease-arrive);
}
dialog.nav-drawer[open],
dialog.nav-drawer:target { transform: none; }
@starting-style {
  dialog.nav-drawer[open] { opacity: 0; transform: translateX(calc(1.5rem * var(--motion-travel))); }
  dialog.nav-drawer[open]::backdrop { opacity: 0; }
}
.drawer-head {
  position: sticky;
  top: 0;
  z-index: 1;
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: calc(0.85rem + env(safe-area-inset-top)) 0.75rem 0.85rem 1rem;
  background: var(--surface);
  border-bottom: 1px solid var(--line);
}
.drawer-who { flex: 1; min-width: 0; }
.drawer-name {
  font-weight: 700;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.drawer-role { font-size: 0.8rem; color: var(--ink-muted); }
.drawer-close {
  flex: none;
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--ink);
  text-decoration: none;
  cursor: pointer;
}
.drawer-close svg { width: 1.15rem; height: 1.15rem; }
.drawer-group {
  padding: 0.6rem 0.5rem;
  border-bottom: 1px solid var(--line);
}
.drawer-label {
  margin: 0.25rem 0.6rem 0.3rem;
  font-family: var(--font-body);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-muted);
}
.drawer-label-admin { color: var(--danger); }
.drawer-link {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  min-height: 48px;
  padding: 0.5rem 0.6rem;
  border-radius: 10px;
  font-weight: 500;
  color: var(--ink);
  text-decoration: none;
  transition: background 0.15s ease;
}
.drawer-link:hover,
.drawer-link:active { background: var(--surface-2); }
.drawer-link[aria-current="page"] {
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  color: var(--accent);
}
.drawer-link .topic-icon-sm { width: 1.25rem; height: 1.25rem; }
.drawer-link .badge { margin-left: auto; }
.drawer-empty { margin: 0.2rem 0.6rem 0.4rem; font-size: 0.9rem; }
.drawer-foot { padding: 0.9rem 1rem calc(0.9rem + env(safe-area-inset-bottom)); }
.drawer-foot form { margin: 0; }
.drawer-foot .btn { width: 100%; }

/* Profil-Menue auch per Klick/Tipp (ui.js setzt is-open und aria-expanded):
   Touch kennt kein Hover, und iOS gibt Buttons beim Tippen keinen Fokus,
   :focus-within greift dort also nie. */
.user-menu.is-open .user-pill { color: var(--ink); border-color: var(--line); }
.user-menu.is-open .user-caret { transform: rotate(180deg); }
.user-menu.is-open .user-dropdown { opacity: 1; visibility: visible; transform: translateY(0); }

/* Textarea fuer das Mail-HTML (Announcements): vorher Inline-Style; als
   Klasse laesst sich die Touch-Schriftgroesse unten sauber ueberschreiben. */
.mail-html-field {
  font-family: var(--font-mono);
  font-size: 0.85rem;
  line-height: 1.5;
}

@media (pointer: coarse) {
  /* Eingabefelder: 16px verhindern den Auto-Zoom von iOS beim Fokus. */
  input[type="text"], input[type="email"], input[type="password"],
  input[type="number"], input[type="date"], input[type="search"],
  select, textarea,
  .asset-search input[type="search"],
  .todo-quick-form input[type="text"],
  .todo-rename-form input[type="text"],
  .mail-html-field { font-size: 16px; }
  input[type="text"], input[type="email"], input[type="password"],
  input[type="number"], input[type="date"], input[type="search"],
  select { min-height: 44px; }
  .todo-quick-form input[type="text"],
  .todo-rename-form input[type="text"] { min-height: 40px; }

  /* Knoepfe und Zeilen: Mindesthoehe 44px, die Optik bleibt. */
  .btn, .chip-btn, .search-result-link, .inbox-drop-item,
  .switch-field { min-height: 44px; }
  .checkbox-label { min-height: 44px; margin-top: 0.4rem; }
  .checkbox-label input[type="checkbox"] { width: 1.35rem; height: 1.35rem; }
  .dropdown-item { display: flex; align-items: center; min-height: 44px; }
  .btn-sm { padding-top: 0.5rem; padding-bottom: 0.5rem; }
  .nav-link { padding: 0.6rem 0.2rem; }
  .nav-link-pill { height: auto; min-height: 40px; padding: 0 0.85rem 0 0.7rem; }
  .user-pill { height: auto; min-height: 40px; padding-left: 5px; }
  .user-pill .user-avatar-xs { --av: 28px; }
  .theme-toggle, .inbox-bell, .back-button { position: relative; width: 40px; height: 40px; }
  .theme-toggle::after, .inbox-bell::after, .back-button::after {
    content: "";
    position: absolute;
    inset: -2px;
  }
  .interval-toggle button, .view-toggle button { min-height: 44px; min-width: 44px; }
  .view-toggle button { width: 44px; }
  /* Unsichtbar vergroesserte Trefferflaeche: 44px hoch, nach rechts nur bis
     in die Luecke vor dem Titel, damit ein Tipp auf den Titel nicht abhakt. */
  .todo-check { position: relative; }
  .todo-check::after {
    content: "";
    position: absolute;
    inset: -13px -8px -13px -13px;
  }
  .todo-menu summary { position: relative; width: 2rem; height: 2rem; }
  .todo-menu summary::after { content: ""; position: absolute; inset: -6px; }
  .todo-rename-save, .todo-rename-cancel { width: 2.5rem; height: 2.5rem; }
  .todo-quick { height: 44px; }
  .todo-quick-pill span { padding: 0.5rem 0.9rem; }
  .sub-item > summary { min-height: 52px; }
  .search-result { min-height: 52px; }
  .legend-row { min-height: 32px; }
  /* Pfeilknoepfe der Scroller weg: gewischt wird direkt. */
  .todo-scroll-arrow, .nav-scroll-arrow { display: none; }
  /* Hover-Anhebungen bleiben auf Touch nach dem Loslassen haengen. */
  .card[data-glow]:hover,
  .btn-primary:hover, .btn-ghost:hover, .btn-danger:hover,
  .theme-toggle:hover, .inbox-bell:hover,
  .section-tile:hover, .market-card:hover, .user-item:hover,
  .settings-item:hover { transform: none; }
}

@media (max-width: 40rem) {
  /* 16px Grundschrift: 14px sind auf Armlaenge zu klein, und alle rem-Masse
     (Abstaende, Knoepfe, Felder) wachsen gleich mit. */
  html { font-size: 100%; }
  .container {
    padding: 1.25rem max(1rem, env(safe-area-inset-right)) 3rem max(1rem, env(safe-area-inset-left));
  }
  .card { padding: 1.1rem 1rem; }
  .card-grid, .section-tiles, .market-grid { gap: 0.75rem; }
  .hero { padding: 1rem 0 0.5rem; }
  .hero-actions .btn, .doc-actions .btn { flex: 1 1 auto; }

  /* Kopfleiste: Marke links, rechts Theme, Glocke, Menue. Link-Leiste,
     Profil-Menue und Abmelden ziehen in die Schublade um. */
  .navbar { padding-top: env(safe-area-inset-top); }
  .navbar-inner {
    padding: 0.5rem max(1rem, env(safe-area-inset-right)) 0.5rem max(1rem, env(safe-area-inset-left));
    gap: 0.75rem;
  }
  .nav-scroll, .nav-right .user-menu, .nav-right .inline-form { display: none; }
  /* Die Hover-Menues gibt es hier nicht; unsichtbar nehmen sie trotzdem Platz
     ein und schieben auf 320px-Phones die Seite seitlich (17rem-Dropdown). */
  .inbox-dropdown, .user-dropdown, .nav-dropdown { display: none; }
  .nav-right { gap: 0.5rem; }
  .nav-menu-toggle {
    display: grid;
    place-items: center;
    width: 44px;
    height: 44px;
    border-radius: 999px;
    border: 1px solid var(--line);
    background: var(--surface);
    color: var(--ink);
    cursor: pointer;
    text-decoration: none;
  }
  .nav-menu-toggle svg { width: 1.2rem; height: 1.2rem; }
  .theme-toggle, .inbox-bell { width: 40px; height: 40px; }

  /* Toasts ueber die volle Breite unter der Leiste. */
  .toast-container { left: 1rem; right: 1rem; top: calc(4.25rem + env(safe-area-inset-top)); }
  .toast { max-width: none; }

  /* Solange ein Modal (Schublade, Bottom Sheet, Bestaetigung) offen ist,
     scrollt die Seite dahinter nicht mit dem Finger weiter. */
  html:has(dialog[open]) { overflow: hidden; }

  /* Popups als Bottom Sheet: unten angeschlagen, volle Breite, Griff oben.
     Die Suche bleibt oben verankert, damit die Bildschirmtastatur sie nicht
     aus dem Bild schiebt. Jede Variante mit eigener Breite (dialog.dialog-card.x)
     muss hier mit in die Liste, sonst behält sie ihre Desktop-Breite. */
  dialog.dialog-card,
  dialog.dialog-card.confirm-dialog,
  dialog.dialog-card.avatar-dialog {
    inset: auto 0 0 0;
    margin: 0;
    width: 100%;
    max-width: none;
    max-height: calc(100dvh - 2.5rem);
    border-bottom: 0;
    border-radius: 18px 18px 0 0;
    padding: 0.9rem 1rem calc(1.1rem + env(safe-area-inset-bottom));
  }
  dialog.dialog-card::before {
    content: "";
    display: block;
    width: 2.5rem;
    height: 4px;
    margin: 0 auto 0.9rem;
    border-radius: 999px;
    background: var(--line-strong);
  }
  /* Bottom Sheets gleiten von unten herein und nach unten wieder hinaus
     (die Ausblendung selbst kommt aus der Dialog-Regel oben). */
  dialog.dialog-card { transform: translateY(calc(2rem * var(--motion-travel))); }
  dialog.dialog-card[open] { transform: none; }
  @starting-style {
    dialog.dialog-card[open] { opacity: 0; transform: translateY(calc(2.5rem * var(--motion-travel))); }
  }
  dialog.dialog-card.dialog-search {
    inset: 0 0 auto 0;
    margin: 0;
    width: 100%;
    max-width: none;
    max-height: calc(100dvh - 1rem);
    border-top: 0;
    border-bottom: 1px solid var(--line);
    border-radius: 0 0 18px 18px;
    padding-top: 1.1rem;
  }
  dialog.dialog-card.dialog-search::before { display: none; }
  /* Die oben verankerte Suche kommt von oben und geht nach oben. */
  dialog.dialog-card.dialog-search { transform: translateY(calc(-1.5rem * var(--motion-travel))); }
  dialog.dialog-card.dialog-search[open] { transform: none; }
  @starting-style {
    dialog.dialog-card.dialog-search[open] { opacity: 0; transform: translateY(calc(-1.5rem * var(--motion-travel))); }
  }
  .sub-form-actions, .confirm-actions { display: flex; gap: 0.6rem; }
  .sub-form-actions .btn, .confirm-actions .btn { flex: 1; }

  /* Titelzeilen: Aktionen rutschen unter den Titel und fuellen die Breite. */
  .sub-head { flex-wrap: wrap; gap: 0.75rem; }
  .market-head-actions { flex: 1 1 100%; flex-wrap: wrap; gap: 0.6rem; }
  .market-head-actions .btn { flex: 1 1 14rem; }
  .sub-head .inline-form { flex: 1 1 100%; }
  .sub-head .inline-form .btn { width: 100%; }
  .add-sub-trigger { width: 100%; }
  .todo-intro { flex-direction: column; align-items: stretch; }
  .todo-intro .btn { align-self: stretch; }

  /* Markets: Umschalter in einer eigenen Zeile, Tabellen behalten die erste
     Spalte beim Wischen im Bild. */
  .wl-head { flex-wrap: wrap; }
  .wl-toggles { width: 100%; justify-content: flex-start; }
  .interval-toggle { flex: 1; }
  .interval-toggle button { flex: 1; }
  .wl-table th:first-child,
  .wl-table td:first-child {
    position: sticky;
    left: 0;
    z-index: 1;
    background: var(--surface);
  }
  .t-ident { max-width: calc(46vw - 1.4rem); }
  .t-name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .t-ident .badge, .t-ident .t-sym, .t-ident .market-logo { flex: none; }
  .price-chart { height: clamp(260px, 48vh, 420px); }
  .chart-toolbar { padding: 0.5rem; }

  /* Sub Tracker */
  .subdash { gap: 0.75rem; margin-bottom: 1.2rem; }
  .donut-wrap { gap: 0.75rem; }

  /* To Do: Spalten fast bildschirmbreit mit Einrasten, das Board laeuft bis
     an die Fensterkanten. Karten zwischen Listen verschieben geht auf Touch
     ueber das Aufgaben-Popup (Feld "List"), nicht per Ziehen. */
  .todo-board { gap: 0; }
  .todo-board-scroll {
    margin-inline: -1rem;
    padding-inline: 1rem;
    scroll-snap-type: x mandatory;
    scroll-padding-inline: 1rem;
  }
  .todo-columns { gap: 0.75rem; }
  .todo-column, .todo-new-list { scroll-snap-align: start; }
  .todo-column { width: min(20rem, calc(100vw - 4rem)); }
  .todo-new-list-btn { width: 10rem; }
  .todo-progress { padding: 0.75rem 1rem; gap: 0.9rem; }

  /* Listenzeilen mit Bedienelementen: Steuerung unter den Text. */
  .settings-item { flex-wrap: wrap; }
  .topic-controls { flex: 1 1 100%; justify-content: flex-start; gap: 1.75rem; padding-left: 3.4rem; }
  .settings-item:not(:has(.topic-manage-icon)) .topic-controls { padding-left: 0; }
  .user-item:not(:has(.user-avatar)) .user-right { padding-left: 0; }
  .user-right .inline-form:has(select),
  .user-actions .inline-form:has(select) { flex: 1 1 100%; }
  .user-right .inline-form select,
  .user-actions .inline-form select { flex: 1; min-width: 0; }

  /* Datenzeilen (Profil, Account-Details): Bezeichnung ueber dem Wert. */
  .data-row { flex-direction: column; gap: 0.1rem; }
  .data-row dd { text-align: left; overflow-wrap: anywhere; }

  /* Anmelden und Registrieren */
  .auth-box { margin: 0.5rem auto; padding: 1.4rem 1.1rem; }

  /* Tabellen mit data-label (Einladungscodes): eine Karte je Zeile statt
     seitlichem Scrollen. */
  .table-stack thead { display: none; }
  .table-stack, .table-stack tbody, .table-stack tr { display: block; }
  .table-stack tr { padding: 0.6rem 1rem; border-bottom: 1px solid var(--line); }
  .table-stack tr:last-child { border-bottom: 0; }
  .table-stack td {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding: 0.3rem 0;
    border: 0;
    text-align: right;
  }
  .table-stack td[data-label]::before {
    content: attr(data-label);
    flex: none;
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--ink-muted);
    text-align: left;
  }
  .table-stack td.actions { justify-content: flex-end; padding-top: 0.5rem; }
  .table-stack td[colspan] { display: block; text-align: left; }

  /* Kleinstschriften: Untergrenze 11px. Auf dem Schreibtisch tragen die
     Etiketten bei 8 bis 10px noch, auf dem Handy nicht mehr. */
  .spark-label,
  .market-interval-label, .market-stand, .donut-center-label,
  .donut-center-sub, .section-label, .doc-preview-page,
  .todo-progress .stat-label, .todo-done-label, .wl-table th,
  .earnings-table th, .on-list, .market-logo.is-fallback::before { font-size: 0.7rem; }
  .market-metrics-row { font-size: 0.76rem; }
  /* Vier Spalten in der Kartenbreite, auch mit deutschen Spaltenköpfen. */
  .earnings-table th, .earnings-table td { padding-left: 0.4rem; padding-right: 0.4rem; }
  .earnings-table th:first-child, .earnings-table td:first-child { padding-left: 0; }
  .earnings-table th:last-child, .earnings-table td:last-child { padding-right: 0; }
}

/* Deutsch: "Anmelden" und "Registrieren" sind breiter, der Schriftzug fällt
   abgemeldet schon unter 25rem weg (sonst 15px Überlauf bei 375px). */
@media (max-width: 25rem) {
  html[lang="de"] .navbar-inner:has(.nav-right > .btn) { gap: 0.5rem; }
  html[lang="de"] .navbar-inner:has(.nav-right > .btn) .brand-text { display: none; }
}

/* Sehr schmale Phones (320 bis 360px): abgemeldet stehen zwei Knoepfe rechts,
   dann faellt der Schriftzug neben dem Logo weg. */
@media (max-width: 22.5rem) {
  .navbar-inner { gap: 0.5rem; }
  .navbar-inner:has(.nav-right > .btn) .brand-text { display: none; }
  .nav-right .btn-sm { padding-left: 0.65rem; padding-right: 0.65rem; }
  /* Quartalszahlen: vier Spalten passen auch hier in die Karte. */
  .earnings-table th, .earnings-table td { padding-left: 0.3rem; padding-right: 0.3rem; }
}

/* ============================================================
   Bewegung ("Kartenstapel")
   Tokens stehen oben unter "Bewegungs-Tokens". Neues rückt vorne auf den
   Stapel (stack-arrive, von links unten), Altes tritt einen Schritt nach
   rechts oben zurück (stack-leave). Toasts und Dialoge stehen bei ihren
   Abschnitten, hier der Rest. Das Verhalten dazu liegt in ui.js
   (Abschnitt "Bewegung"), theme-toggle.js und page-transitions.js.
   ============================================================ */

@keyframes stack-arrive {
  from {
    opacity: 0;
    transform: translate(calc(var(--step) * -1), var(--step)) scale(var(--step-scale));
  }
}
@keyframes stack-leave {
  to {
    opacity: 0;
    transform: translate(var(--step), calc(var(--step) * -1)) scale(var(--step-scale));
  }
}
/* Rückwärts: die oberste Karte wird weggenommen, die darunter rückt vor. */
@keyframes stack-take {
  to {
    opacity: 0;
    transform: translate(calc(var(--step) * -1), var(--step)) scale(var(--step-scale));
  }
}
@keyframes stack-forward {
  from {
    opacity: 0;
    transform: translate(var(--step), calc(var(--step) * -1)) scale(var(--step-scale));
  }
}
@keyframes fade-in { from { opacity: 0; } }
@keyframes fade-out { to { opacity: 0; } }

/* Der globale Schalter für reduzierte Bewegung (Abschnitt "Responsiv")
   kürzt alle Animationen auf 0.001 ms. Zwei Stellen sind keine Bewegung
   und würden daran kaputtgehen: der Toast-Timer ist Lesezeit (der Toast
   ginge sofort), eine Endlos-Animation mit 0.001 ms flackert (Logo-Lader,
   siehe unten). */
@media (prefers-reduced-motion: reduce) {
  .toast-timer { animation-duration: var(--toast-dwell) !important; }
}

/* ---------- Seitenwechsel: die Seite rückt auf den Stapel ----------
   Seitenübergreifende View Transitions (Chrome/Edge 126+, Safari 18.2+),
   andere Browser navigieren wie bisher. Typ "page" trennt Seitenwechsel
   von Übergängen innerhalb einer Seite (Theme, Karten/Tabelle); Richtung
   ("back") und das gleitende Titel-Paar setzt page-transitions.js. */
@view-transition {
  navigation: auto;
  types: page;
}
/* Die Leiste bleibt stehen, der Inhalt darunter wechselt; der Unterstrich
   der aktuellen Seite gleitet zum neuen Eintrag. */
.navbar { view-transition-name: navbar; }
.nav-link[aria-current="page"]::after { view-transition-name: nav-current; }
/* Ausnahmen setzt page-transitions.js: ein Unterstrich außerhalb des
   sichtbaren Teils der scrollenden Leiste (Gruppen werden nicht vom
   Scroller beschnitten) und ein offenes Modal, über das die Leiste sonst als
   eigene Gruppe scharf hinweg gezeichnet würde. */
.nav-link.vt-clipped::after { view-transition-name: none; }
.vt-under-modal .navbar,
.vt-under-modal .nav-link::after { view-transition-name: none; }
::view-transition-group(navbar) { z-index: 2; }
::view-transition-group(nav-current) { z-index: 3; }
/* Titel der geklickten Topic-Kachel gleitet in die h1 der Topic-Seite.
   Skaliert wird über die Höhe: Kachel-Titel und h1 sind verschieden breite
   Boxen, über die Breite gerechnet würde der Text kurz riesig. */
::view-transition-group(topic-title) { z-index: 1; }
::view-transition-old(topic-title),
::view-transition-new(topic-title) {
  height: 100%;
  width: auto;
}
/* Fehlt der Partner auf der anderen Seite (z. B. ein angepinntes Topic, das
   auf der Topics-Seite nicht abonniert ist), bewegt sich ein einzelner Titel wie
   seine Seite, statt stehenzubleiben und über der neuen Seite zu schweben. */
html:active-view-transition-type(page)::view-transition-old(topic-title):only-child {
  animation: stack-leave var(--dur-state) var(--ease-out) both;
}
html:active-view-transition-type(page)::view-transition-new(topic-title):only-child {
  animation: stack-arrive var(--dur-layout) var(--ease-arrive) calc(var(--dur-state) * 0.35) both;
}
html:active-view-transition-type(page):active-view-transition-type(back)::view-transition-old(topic-title):only-child {
  animation-name: stack-take;
}
html:active-view-transition-type(page):active-view-transition-type(back)::view-transition-new(topic-title):only-child {
  animation-name: stack-forward;
}
/* Benannte Teile wandern gleich schnell; bei reduzierter Bewegung springen
   sie an ihr Ziel und blenden nur über. */
::view-transition-group(*) {
  animation-duration: calc(var(--dur-layout) * var(--motion-travel));
  animation-timing-function: var(--ease-arrive);
}
::view-transition-old(*),
::view-transition-new(*) {
  animation-duration: var(--dur-layout);
}
/* Zwei ganze Seiten übereinander wirken unruhig: die alte blendet schnell
   aus (ease-out), die neue setzt einen Hauch später ein. */
html:active-view-transition-type(page)::view-transition-old(root) {
  animation: stack-leave var(--dur-state) var(--ease-out) both;
}
html:active-view-transition-type(page)::view-transition-new(root) {
  animation: stack-arrive var(--dur-layout) var(--ease-arrive) calc(var(--dur-state) * 0.35) both;
}
html:active-view-transition-type(page):active-view-transition-type(back)::view-transition-old(root) {
  animation-name: stack-take;
}
html:active-view-transition-type(page):active-view-transition-type(back)::view-transition-new(root) {
  animation-name: stack-forward;
}
/* Ersatz für Browser ohne seitenübergreifende View Transitions (Firefox,
   ältere Safari): page-transitions.js setzt die Klassen vor dem ersten
   Zeichnen, der Inhalt unter der Leiste rückt mit derselben Geste herein.
   "backwards", nicht "both": nach dem Ende bleibt kein Transform hängen. */
html.pt-arrive #main-content {
  animation: stack-arrive var(--dur-layout) var(--ease-arrive) backwards;
}
html.pt-arrive.pt-back #main-content { animation-name: stack-forward; }

/* ---------- Settings-Reiter: der Inhalt folgt der Pille ----------
   Die Reiter sind eigene Seiten, sollen sich aber wie ein Segment-Umschalter
   anfühlen statt wie ein Seitenwechsel. page-transitions.js überspringt dafür
   die View Transition: Kopf und Reiterleiste stehen sofort, die Pille gleitet
   per eigener Transition vom vorigen Reiter herüber, und nur der neue Inhalt
   schiebt sich aus derselben Richtung herein (pt-tab, pt-tab-back = von
   links). Seitwärts ist die Richtung der Pille, nicht die des Stapels;
   Kurve und Dauer bleiben die des Systems, bei reduzierter Bewegung bleibt
   nur das Einblenden. */
html.pt-tab-back { --tab-dir: -1; }
@keyframes tab-arrive {
  from {
    opacity: 0;
    transform: translateX(calc(var(--tab-dir, 1) * var(--step) * 3));
  }
}
html.pt-tab .settings-pane { animation: tab-arrive var(--dur-layout) var(--ease-arrive) backwards; }

/* ---------- Theme-Wechsel ----------
   Statt eines harten Helligkeitssprungs wächst das neue Theme als Kreis aus
   dem Umschalter (theme-toggle.js setzt --wipe-x/-y/-r); bei reduzierter
   Bewegung eine ruhige Überblendung. Für die Dauer des Wechsels gibt es
   keine benannten Teile, damit die Seite als Ganzes wechselt. */
html:active-view-transition-type(theme) *,
html:active-view-transition-type(theme) *::after {
  view-transition-name: none !important;
}
html:active-view-transition-type(theme)::view-transition-old(root),
html:active-view-transition-type(theme)::view-transition-new(root) {
  animation: none;
  mix-blend-mode: normal;
}
html:active-view-transition-type(theme)::view-transition-new(root) {
  animation: theme-wipe var(--dur-focal) var(--ease-swap) both;
}
@keyframes theme-wipe {
  from {
    clip-path: circle(calc(var(--wipe-r, 150vmax) * (1 - var(--motion-travel))) at var(--wipe-x, 100%) var(--wipe-y, 0%));
    opacity: var(--motion-travel);
  }
  to {
    clip-path: circle(var(--wipe-r, 150vmax) at var(--wipe-x, 100%) var(--wipe-y, 0%));
    opacity: 1;
  }
}

/* ---------- Postfach-Glocke und Leisten-Menüs ----------
   Neue Nachricht seit dem letzten Seitenaufruf: die Glocke schwingt einmal
   aus, der Zähler springt auf (ui.js, ringBell). Die Menüs der Leiste sind
   einzelne Blöcke ohne Panel: sie werden kurz versetzt ausgeteilt. */
.inbox-bell.is-ringing svg {
  transform-origin: 50% 12%;
  animation: bell-swing 900ms var(--ease-out);
}
.inbox-bell.is-ringing svg path:last-child {
  transform-box: fill-box;
  transform-origin: 50% 0;
  animation: bell-clapper 900ms var(--ease-out) 40ms;
}
@keyframes bell-swing {
  15% { transform: rotate(calc(16deg * var(--motion-travel))); }
  32% { transform: rotate(calc(-12deg * var(--motion-travel))); }
  50% { transform: rotate(calc(8deg * var(--motion-travel))); }
  68% { transform: rotate(calc(-4deg * var(--motion-travel))); }
  84% { transform: rotate(calc(1.5deg * var(--motion-travel))); }
}
@keyframes bell-clapper {
  20% { transform: translateX(calc(-1.6px * var(--motion-travel))); }
  40% { transform: translateX(calc(1.4px * var(--motion-travel))); }
  60% { transform: translateX(calc(-0.8px * var(--motion-travel))); }
}
.inbox-badge.is-new { animation: badge-pop var(--dur-layout) var(--ease-arrive) both; }
@keyframes badge-pop {
  from { opacity: 0; transform: scale(calc(1 - 0.55 * var(--motion-travel))); }
}
/* Container bleibt ruhig (nur Sichtbarkeit), die Blöcke bewegen sich. */
.inbox-dropdown,
.nav-dropdown { transform: translate(-50%, 0); }
.inbox-dropdown > .inbox-drop-item,
.nav-dropdown > .inbox-drop-item {
  opacity: 0;
  transform: translate(calc(var(--step) * 0.8), calc(var(--step) * -0.8)) scale(var(--step-scale));
  transition:
    opacity var(--dur-feedback) var(--ease-leave),
    transform var(--dur-feedback) var(--ease-leave),
    border-color 0.15s ease;
}
.inbox-menu:hover .inbox-drop-item,
.inbox-menu:focus-within .inbox-drop-item,
.nav-menu.is-open .inbox-drop-item {
  opacity: 1;
  transform: none;
  transition-duration: var(--dur-state), var(--dur-state), 0.15s;
  transition-timing-function: var(--ease-arrive), var(--ease-arrive), ease;
  transition-delay: calc(var(--deal, 0) * 35ms), calc(var(--deal, 0) * 35ms), 0s;
}
.inbox-drop-item:nth-child(2) { --deal: 1; }
.inbox-drop-item:nth-child(3) { --deal: 2; }
.inbox-drop-item:nth-child(4) { --deal: 3; }
.inbox-drop-item:nth-child(n + 5) { --deal: 4; }
/* Hover auf einem einzelnen Block: sofort reagieren, ohne Staffel-Verzug. */
.inbox-menu:hover a.inbox-drop-item:hover,
.nav-menu.is-open a.inbox-drop-item:hover {
  transform: translateY(-1px);
  transition-delay: 0s;
}

/* ---------- Topic-Icons zeichnen sich ----------
   Hover oder Fokus auf einer Kachel (auch im Topics-Menü, in der
   Schublade, an angepinnten Links der Leiste und auf der Startseite in
   "Dein Tag" und "Deine Topics") zeichnet das Icon neu. Greift nur an Linien mit
   pathLength="1" und den i-*-Klassen aus fragments/topic-icons.html. */
.card-icon [pathLength="1"],
.topic-icon-sm [pathLength="1"],
.day-icon [pathLength="1"],
.rail-icon [pathLength="1"] { stroke-dasharray: 1 1; }
@keyframes icon-draw { from { stroke-dashoffset: 1; } to { stroke-dashoffset: 0; } }
@keyframes icon-grow { from { transform: scaleX(0); } }
@keyframes icon-tilt {
  45% { transform: rotate(calc(-9deg * var(--motion-travel))) translateY(calc(-1.5px * var(--motion-travel))); }
}
@keyframes icon-coin {
  from { transform: perspective(120px) rotateY(0deg); }
  to { transform: perspective(120px) rotateY(calc(360deg * var(--motion-travel))); }
}
:is(a.card-link, a.inbox-drop-item, a.drawer-link, a.nav-link, a.rail-row):is(:hover, :focus-visible) .i-trend {
  animation: icon-draw var(--dur-layout) var(--ease-out) both;
}
:is(a.card-link, a.inbox-drop-item, a.drawer-link, a.nav-link, a.rail-row):is(:hover, :focus-visible) .i-trend-head {
  animation: icon-draw var(--dur-state) var(--ease-out) calc(var(--dur-layout) * 0.8) both;
}
.i-card { transform-origin: 50% 60%; }
:is(a.card-link, a.inbox-drop-item, a.drawer-link, a.nav-link, a.rail-row):is(:hover, :focus-visible) .i-card {
  animation: icon-tilt var(--dur-layout) var(--ease-out);
}
:is(a.card-link, a.inbox-drop-item, a.drawer-link, a.nav-link, a.rail-row):is(:hover, :focus-visible) .i-card-stripe {
  animation: icon-draw var(--dur-layout) var(--ease-out) var(--dur-feedback) both;
}
.i-row { transform-box: fill-box; transform-origin: left center; }
:is(a.card-link, a.inbox-drop-item, a.drawer-link, a.nav-link, a.rail-row):is(:hover, :focus-visible) .i-tick {
  animation: icon-draw var(--dur-state) var(--ease-out) both;
}
:is(a.card-link, a.inbox-drop-item, a.drawer-link, a.nav-link, a.rail-row):is(:hover, :focus-visible) .i-tick-2 {
  animation-delay: var(--dur-feedback);
}
:is(a.card-link, a.inbox-drop-item, a.drawer-link, a.nav-link, a.rail-row):is(:hover, :focus-visible) .i-row {
  animation: icon-grow var(--dur-state) var(--ease-out) calc(var(--dur-feedback) * 0.6) both;
}
:is(a.card-link, a.inbox-drop-item, a.drawer-link, a.nav-link, a.rail-row):is(:hover, :focus-visible) .i-row-2 {
  animation-delay: calc(var(--dur-feedback) * 1.6);
}
:is(a.card-link, a.inbox-drop-item, a.drawer-link, a.nav-link, a.rail-row):is(:hover, :focus-visible) .i-calc-display {
  animation: icon-draw var(--dur-layout) var(--ease-out) both;
}
/* Topic Hub (Schaufenster): die Tür zeichnet sich neu. */
:is(a.card-link, a.inbox-drop-item, a.drawer-link, a.nav-link, a.rail-row):is(:hover, :focus-visible) .i-door {
  animation: icon-draw var(--dur-layout) var(--ease-out) both;
}
/* Budget Flow: die drei Äste zeichnen sich nacheinander neu. */
:is(a.card-link, a.inbox-drop-item, a.drawer-link, a.nav-link, a.rail-row):is(:hover, :focus-visible) .i-flow {
  animation: icon-draw var(--dur-layout) var(--ease-out) both;
}
:is(a.card-link, a.inbox-drop-item, a.drawer-link, a.nav-link, a.rail-row):is(:hover, :focus-visible) .i-flow-2 {
  animation-delay: calc(var(--dur-feedback) * 0.6);
}
:is(a.card-link, a.inbox-drop-item, a.drawer-link, a.nav-link, a.rail-row):is(:hover, :focus-visible) .i-flow-3 {
  animation-delay: var(--dur-feedback);
}
:is(a.card-link, a.inbox-drop-item, a.drawer-link, a.nav-link, a.rail-row):is(:hover, :focus-visible) :is(.card-icon-glyph, .topic-icon-sm-glyph, .rail-icon-glyph) {
  animation: icon-coin var(--dur-focal) var(--ease-swap);
}

/* ---------- Die Marke fächert auf ----------
   Hover über "BarsWeb" in der Leiste: die drei Karten rücken einen halben
   Schritt auseinander, die Kachel bleibt stehen. Der Buchstabe steckt in
   einem eigenen <g class="logo-letter"> (fragments/logo.html), weil ein
   CSS-transform sonst das transform-Attribut des inneren <g> ersetzen würde. */
.brand .logo-card-back,
.brand .logo-card-mid,
.brand .logo-card-front,
.brand .logo-letter {
  transition: transform var(--dur-state) var(--ease-out);
}
.brand:hover .logo-card-back { transform: translate(calc(3.5px * var(--motion-travel)), calc(-3.5px * var(--motion-travel))); }
.brand:hover .logo-card-mid { transform: translate(calc(1.5px * var(--motion-travel)), calc(-1.5px * var(--motion-travel))); }
.brand:hover .logo-card-front,
.brand:hover .logo-letter { transform: translate(calc(-1px * var(--motion-travel)), calc(1px * var(--motion-travel))); }

/* ---------- Markets: Live-Kurs-Tick ----------
   Nach einem Kurs-Refresh rollen nur die geänderten Ziffern, in
   Kursrichtung (hoch = von unten, runter = von oben). Der Preis färbt sich
   kurz in der Trendfarbe, der Chip leuchtet auf, der Punkt "jetzt" am Ende
   der Sparkline pulst einmal. Die Spans baut ui.js (rollText) und räumt sie
   danach wieder weg, im DOM steht dann wieder reiner Text. */
.tick-digit {
  display: inline-grid;
  /* clip-path statt overflow: hidden, damit die Grundlinie stimmt. */
  clip-path: inset(0 -0.1em);
}
.tick-digit > span { grid-area: 1 / 1; }
.tick-digit > .tick-was {
  animation: tick-out-up var(--dur-layout) var(--ease-swap) calc(var(--tick-i, 0) * 28ms) forwards;
}
.tick-digit > .tick-now {
  animation: tick-in-up var(--dur-layout) var(--ease-swap) calc(var(--tick-i, 0) * 28ms) both;
}
.tick-digit[data-dir="down"] > .tick-was { animation-name: tick-out-down; }
.tick-digit[data-dir="down"] > .tick-now { animation-name: tick-in-down; }
@keyframes tick-out-up { to { opacity: 0; transform: translateY(calc(-100% * var(--motion-travel))); } }
@keyframes tick-in-up { from { opacity: 0; transform: translateY(calc(100% * var(--motion-travel))); } }
@keyframes tick-out-down { to { opacity: 0; transform: translateY(calc(100% * var(--motion-travel))); } }
@keyframes tick-in-down { from { opacity: 0; transform: translateY(calc(-100% * var(--motion-travel))); } }
.tick-up { animation: tick-tint-up 1400ms var(--ease-out); }
.tick-down { animation: tick-tint-down 1400ms var(--ease-out); }
@keyframes tick-tint-up { from { color: var(--success); } to { color: var(--ink); } }
@keyframes tick-tint-down { from { color: var(--danger); } to { color: var(--ink); } }
.quote-change { transition: color var(--dur-layout) ease, background-color var(--dur-layout) ease; }
.quote-change.tick-flash { animation: chip-flash 1400ms var(--ease-out); }
@keyframes chip-flash {
  from { box-shadow: 0 0 0 3px color-mix(in srgb, currentColor 30%, transparent); }
  to { box-shadow: 0 0 0 3px transparent; }
}
.tick-fade { animation: fade-in var(--dur-layout) var(--ease-out); }
/* Punkt "jetzt" am Ende der Sparkline: HTML statt SVG-Kreis, weil das SVG
   mit preserveAspectRatio="none" gestreckt wird (ein Kreis würde oval). */
.spark-frame { position: relative; }
.spark-dot {
  position: absolute;
  width: 5px;
  height: 5px;
  margin: -2.5px 0 0 -2.5px;
  border-radius: 50%;
  background: currentColor;
  box-shadow: 0 0 0 2px var(--surface);
  pointer-events: none;
}
.spark-dot.is-ping::after {
  content: "";
  position: absolute;
  inset: -1px;
  border-radius: 50%;
  border: 1.5px solid currentColor;
  animation: spark-ping 1100ms var(--ease-out) forwards;
}
@keyframes spark-ping {
  from { opacity: 0.7; transform: scale(1); }
  to { opacity: 0; transform: scale(calc(1 + 2.6 * var(--motion-travel))); }
}

/* ---------- Markets: Karten und Tabelle ----------
   Beim Umschalten gleiten Name, Logo und Preis jedes Werts von der Karte an
   ihren Platz in der Tabelle (und zurück); der Rest blendet über. Die Namen
   vergibt ui.js (switchView) nur für die Dauer des Wechsels. */
html:active-view-transition-type(layout)::view-transition-group(*) {
  animation-duration: calc(var(--dur-layout) * 1.3 * var(--motion-travel));
  animation-timing-function: var(--ease-swap);
}
html:active-view-transition-type(layout)::view-transition-old(root) {
  animation: fade-out var(--dur-state) var(--ease-leave) both;
}
html:active-view-transition-type(layout)::view-transition-new(root) {
  animation: fade-in var(--dur-layout) var(--ease-out) calc(var(--dur-state) * 0.5) both;
}

/* ---------- Markets: Intervall-Wechsel ----------
   Die Auswahl gleitet als Pille zum neuen Intervall. Nur transform: die
   Pille ist 1px breit und wird auf die Breite des Knopfs skaliert (ohne
   Radius verzerrt nichts, die Ecken schneidet der Rahmen der Leiste ab).
   Auf den Karten tauscht der Veränderungs-Block kurz versetzt durch das
   Raster, nur beim Umschalten (is-switching), nicht beim Live-Tausch. */
.interval-toggle { position: relative; isolation: isolate; }
.interval-toggle .iv-pill {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  width: 1px;
  transform-origin: left center;
  transform: translateX(var(--pill-x, 0)) scaleX(var(--pill-w, 0));
  background: var(--surface-2);
  z-index: -1;
  pointer-events: none;
}
.interval-toggle.is-ready .iv-pill {
  transition: transform calc(var(--dur-state) * var(--motion-travel)) var(--ease-out);
}
.interval-toggle.has-pill button.active { background: none; }
.interval-toggle button { transition: color var(--dur-state) ease; }
.market-grid.is-switching .market-interval {
  animation: interval-in var(--dur-state) var(--ease-out) calc(var(--card-i, 0) * 24ms) both;
}
@keyframes interval-in {
  from { opacity: 0; transform: translateY(calc(5px * var(--motion-travel))); }
}

/* ---------- Markets: Heatmap im Minutentakt ----------
   Jeder Tick läuft als Welle von links unten nach rechts oben über das Feld
   (Stapel-Richtung, Versatz setzt ui.js): die Farbe gleitet zur neuen
   Stufe (Übergang an .hm-paint), die Ziffern rollen, und ein Lichtstreif
   läuft in Kursrichtung über die Kachel, umso heller, je größer der
   Schritt (--glint). Ein Sprung von 1 % in einer Minute leuchtet zusätzlich
   auf (hm-flare). Beim ersten Kurs des Tages verlässt die Kachel den
   gedämpften Vortag-Zustand mit einem kräftigen Streif (hm-opening). */
.hm-glint {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  overflow: hidden;
  pointer-events: none;
}
.hm-glint::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 100%;
  height: 70%;
  background: linear-gradient(to top, transparent, var(--heat-glint) 55%, transparent);
  opacity: 0;
}
.hm-glint.is-up::before { animation: glint-up 760ms var(--ease-out); }
.hm-glint.is-down::before { animation: glint-down 760ms var(--ease-out); }
@keyframes glint-up {
  0% { top: 100%; opacity: var(--glint, 1); }
  100% { top: -70%; opacity: 0; }
}
@keyframes glint-down {
  0% { top: -70%; opacity: var(--glint, 1); }
  100% { top: 100%; opacity: 0; }
}
.hm-tile.hm-flare .hm-paint { animation: heat-flare 1400ms var(--ease-out); }
@keyframes heat-flare {
  20% {
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--heat) 90%, transparent),
                0 10px 30px -4px color-mix(in srgb, var(--heat) 80%, transparent);
  }
}
/* Während eines Umbaus ohne Bewegung (Zeitraum nach dem Live-Tausch,
   alter Stand vor dem Abspielen) springt die Farbe sofort. */
.hm-map.is-instant .hm-paint { transition: none; }
/* Popover: legt sich wie eine Karte auf den Stapel. */
.hm-pop.is-entering { animation: hm-pop-in var(--dur-state) var(--ease-arrive) both; }
@keyframes hm-pop-in {
  from {
    opacity: 0;
    transform: translate(calc(-0.6 * var(--step)), calc(0.6 * var(--step))) scale(var(--step-scale));
  }
}

/* ---------- Sub Tracker: der Ring nimmt das Abo auf ----------
   Nach Anlegen, Pausieren, Fortsetzen oder Löschen wandern die Segmente
   aus der alten Geometrie in die neue (ui.js, playDonut): ein neues wächst
   aus seinem Startpunkt, ein wegfallendes schrumpft als Geist auf null, die
   übrigen rücken nach. Mitte und Monatssumme zählen mit. */
.donut-wrap.is-reflowing .donut-seg,
.donut-wrap.is-reflowing .donut-hit {
  transition:
    stroke-dasharray var(--dur-focal) var(--ease-swap),
    stroke-dashoffset var(--dur-focal) var(--ease-swap),
    stroke var(--dur-focal) var(--ease-swap),
    stroke-width 0.35s ease,
    opacity 0.3s ease;
}
.donut-seg.is-ghost { pointer-events: none; }
/* Anteilsbalken per scaleX vom alten auf den neuen Anteil (FLIP), die
   Breite selbst springt sofort. */
.legend-bar-fill { transform-origin: left center; }
.donut-wrap.is-reflowing .legend-bar-fill {
  transition: transform var(--dur-focal) var(--ease-swap);
}
.donut-legend li.is-new { animation: stack-arrive var(--dur-layout) var(--ease-arrive) both; }
.sub-item.is-arriving {
  animation:
    stack-arrive var(--dur-layout) var(--ease-arrive) both,
    sub-flash 1.6s var(--ease-out) var(--dur-layout);
}

/* ---------- Budget Flow: das Geld fließt ein ----------
   Erstes Zeichnen: das Geld fließt von links nach rechts ein (budget-flow.js
   führt einen Clip mit, --ease-arrive). Die einzige seitliche Enthüllung der
   App, weil sie dem Geld folgt. Knoten legen sich mit stack-arrive auf, nach
   Spalten gestaffelt (--d), Beschriftungen blenden hinterher. Ändert sich ein
   Eintrag, gleiten die Bänder mit --ease-swap in ihre neue Lage (JS). */
.bf-node-g.is-arriving {
  animation: stack-arrive var(--dur-layout) var(--ease-arrive) both;
  animation-delay: var(--d, 0ms);
}
.bf-label.is-arriving {
  animation: fade-in var(--dur-state) var(--ease-out) both;
  animation-delay: var(--d, 0ms);
}
.bf-trace { animation: fade-in var(--dur-state) var(--ease-out) both; }
/* Gespeicherte oder angesprungene Zeile: kurz in Stack Blue getönt. In der
   Gruppenkarte statt des Rings von .sub-item.is-flash, den das overflow der
   Karte abschneiden würde. */
.bf-group-card > .sub-item.is-flash,
.bf-cat.is-flash > summary { animation: bf-row-flash 1.6s var(--ease-out); }
@keyframes bf-row-flash { from { background: color-mix(in srgb, var(--accent) 14%, transparent); } }
@media (prefers-reduced-motion: reduce) {
  .bf-sweep-path { display: none; }
}

/* ---------- Aufklappbare Zeilen (Sub Tracker, Postfach, Admin-Markets) ----------
   Die Details öffnen mit weicher Höhe statt Sprung (Chrome/Edge 131+,
   andere Browser klappen wie bisher sofort auf). */
.sub-item { interpolate-size: allow-keywords; }
.sub-item::details-content {
  block-size: 0;
  /* Nur in der Höhe abschneiden (fürs Aufklappen); breiter Inhalt ragt wie
     bisher sichtbar heraus. */
  overflow-x: visible;
  overflow-y: clip;
  opacity: 0;
  transition:
    block-size calc(var(--dur-layout) * var(--motion-travel)) var(--ease-out),
    opacity var(--dur-state) ease,
    content-visibility calc(var(--dur-layout) * var(--motion-travel)) allow-discrete;
}
.sub-item[open]::details-content {
  block-size: auto;
  opacity: 1;
}

/* ---------- To Do: Aufgabe abhaken ----------
   Sofort beim Klick, noch vor der Server-Antwort (ui.js, checkTask): der
   Kreis füllt sich, der Haken zeichnet sich, der Titel wird durchgestrichen.
   Danach gleitet die Karte wie bisher unter "Done" und der Ring füllt sich. */
.todo-check { position: relative; }
.todo-check::before {
  content: "";
  position: absolute;
  inset: -2px;
  border-radius: inherit;
  background: var(--success);
  opacity: 0;
  transform: scale(0.4);
}
.todo-card.is-checking .todo-check { border-color: var(--success); }
.todo-card.is-checking .todo-check::before {
  animation: check-fill var(--dur-state) var(--ease-arrive) forwards;
}
@keyframes check-fill {
  from { opacity: 0; transform: scale(calc(1 - 0.6 * var(--motion-travel))); }
  to { opacity: 1; transform: scale(1); }
}
.todo-card.is-checking .todo-check-icon { position: relative; z-index: 1; }
.todo-card.is-checking .todo-check-icon polyline {
  stroke-dasharray: 1 1;
  animation: icon-draw var(--dur-state) var(--ease-out) calc(var(--dur-feedback) * 0.8) both;
}
/* Durchstreichen als wachsender Hintergrund-Strich: läuft bei umbrechenden
   Titeln Zeile für Zeile durch (box-decoration-break: slice). */
.strike-run {
  background: linear-gradient(currentColor, currentColor) no-repeat 0 58% / 0% 1.5px;
}
.todo-card.is-checking .todo-card-title {
  color: var(--ink-faint);
  transition: color var(--dur-layout) ease var(--dur-feedback);
}
.todo-card.is-checking .strike-run {
  animation: strike var(--dur-layout) var(--ease-out) var(--dur-feedback) forwards;
}
@keyframes strike { to { background-size: 100% 1.5px; } }
/* Schon abgehakt, Antwort steht noch aus, dazwischen kam ein Tausch: den
   Endzustand sofort zeigen, nicht noch einmal abspielen (ui.js). */
.todo-card.is-instant .todo-check::before,
.todo-card.is-instant .todo-check-icon polyline,
.todo-card.is-instant .strike-run {
  animation-duration: 0s;
  animation-delay: 0s;
}
.todo-card.is-instant .todo-card-title { transition: none; }
/* Wegräumen einer erledigten Karte: eine Kopie tritt zurück in den Stapel. */
.todo-ghost {
  position: fixed;
  margin: 0;
  pointer-events: none;
  z-index: 50;
  animation: stack-leave var(--dur-state) var(--ease-leave) forwards;
}
/* Fortschrittsring: füllt sich vom alten auf den neuen Stand; bei 100 %
   einmal ein weicher Schein und ein Häkchen in der Mitte (ui.js räumt es
   nach kurzer Zeit wieder weg, dann steht wieder die Zahl da). */
.todo-ring.is-animating .todo-ring-value {
  transition:
    stroke-dasharray var(--dur-focal) var(--ease-swap),
    stroke var(--dur-layout) ease;
}
.todo-ring-label { transition: opacity var(--dur-state) ease; }
.todo-ring-done {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  color: var(--success);
  pointer-events: none;
}
.todo-ring-done svg {
  width: 1.15rem;
  height: 1.15rem;
  fill: none;
  stroke: currentColor;
  stroke-width: 3;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.todo-ring-done polyline {
  stroke-dasharray: 1 1;
  animation: icon-draw var(--dur-layout) var(--ease-out) calc(var(--dur-focal) * 0.7) both;
}
.todo-ring.is-complete .todo-ring-label { opacity: 0; }
.todo-ring.is-complete::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 50%;
  pointer-events: none;
  animation: ring-glow 1200ms var(--ease-out) calc(var(--dur-focal) * 0.8) both;
}
@keyframes ring-glow {
  from { box-shadow: 0 0 0 0 color-mix(in srgb, var(--success) 45%, transparent); }
  to { box-shadow: 0 0 0 calc(12px * var(--motion-travel) + 2px) transparent; }
}

/* ---------- Startseite: Neues landet auf dem Stapel ----------
   Was seit dem letzten Besuch dazukam, legt sich beim Laden auf den Stapel,
   eins nach dem anderen (--deal, home.js), nach dem Seitenwechsel. "Erste
   Schritte" ausblenden: sie treten nach rechts oben ab, "Dein Tag" legt sich
   an ihre Stelle. Ein unter Entdecken abonniertes Topic legt sich in "Deine
   Topics" auf. Auf der Gästeseite legen sich die drei Karten des Kopfs
   nacheinander auf den Stapel. Ohne JS steht der Feed sofort da. */
.feed-item.is-arriving,
.feed-divider.is-arriving {
  animation: stack-arrive var(--dur-layout) var(--ease-arrive) backwards;
  animation-delay: calc(var(--dur-layout) * 0.55 + var(--deal, 0) * 90ms);
}
.feed-divider.is-arriving { animation-name: fade-in; }
.feed-item.is-revealed { animation: fade-in var(--dur-state) var(--ease-out); }
.home .is-leaving { animation: stack-leave var(--dur-state) var(--ease-leave) forwards; }
.home .is-entering { animation: stack-arrive var(--dur-layout) var(--ease-arrive) backwards; }
.day-card.is-entering:nth-child(2) { animation-delay: 60ms; }
.day-card.is-entering:nth-child(3) { animation-delay: 120ms; }
.gx-stack-card {
  animation: stack-arrive var(--dur-layout) var(--ease-arrive) backwards;
  animation-delay: calc(var(--dur-layout) * 0.5 + (2 - var(--i)) * 120ms);
}

/* ---------- Logo-Lader ----------
   Die drei Karten der Bildmarke mischen sich: die vordere geht nach links
   unten ab und kommt hinten wieder herein, die anderen rücken je einen
   Schritt vor. Für Anmelden und Kurs-Refresh; kleine Knöpfe in Zeilen
   behalten den runden Spinner.
   Markup: <span class="logo-loader" aria-hidden="true"><i></i><i></i><i></i></span> */
.logo-loader {
  --ll-size: 1.25rem;
  --ll-back: var(--logo-back);
  --ll-mid: var(--logo-mid);
  --ll-front: var(--accent);
  --ll-cycle: calc(var(--dur-focal) * 2.4);
  position: relative;
  display: inline-block;
  flex: none;
  width: var(--ll-size);
  height: var(--ll-size);
  vertical-align: middle;
}
.logo-loader i {
  position: absolute;
  /* Maße der Bildmarke: Karte 38/64, Radius 9/38, hintere Karte bei 21/5. */
  left: 32.8%;
  top: 7.8%;
  width: 59.4%;
  height: 59.4%;
  border-radius: 23.7%;
  background: var(--ll-back);
  animation: ll-shuffle var(--ll-cycle) var(--ease-swap) infinite both;
}
.logo-loader i:nth-child(2) { animation-delay: calc(var(--ll-cycle) / -3); }
.logo-loader i:nth-child(3) { animation-delay: calc(var(--ll-cycle) / -1.5); }
/* Ein Schritt = 8/64 der Marke = 21 % der Kartengröße. */
@keyframes ll-shuffle {
  0% { transform: translate(-42%, 42%); background: var(--ll-front); opacity: 1; z-index: 3; }
  7.5% { transform: translate(-63%, 63%); background: var(--ll-front); opacity: 0; z-index: 3; }
  7.6% { transform: translate(21%, -21%); background: var(--ll-back); opacity: 0; z-index: 0; }
  15% { transform: translate(0, 0); background: var(--ll-back); opacity: 1; z-index: 1; }
  33.3% { transform: translate(0, 0); background: var(--ll-back); z-index: 1; }
  48.3% { transform: translate(-21%, 21%); background: var(--ll-mid); z-index: 2; }
  66.6% { transform: translate(-21%, 21%); background: var(--ll-mid); z-index: 2; }
  81.6% { transform: translate(-42%, 42%); background: var(--ll-front); z-index: 3; }
  100% { transform: translate(-42%, 42%); background: var(--ll-front); opacity: 1; z-index: 3; }
}
@keyframes ll-glow { 50% { opacity: 0.45; } }
/* Reduzierte Bewegung: die Karten bleiben in ihrer Logo-Position, ein Licht
   läuft von hinten nach vorn durch den Stapel. Die Dauer mit !important
   gegen den globalen 0.001-ms-Schalter, sonst flackert die Schleife. */
@media (prefers-reduced-motion: reduce) {
  .logo-loader i {
    animation-name: ll-glow;
    animation-duration: var(--ll-cycle) !important;
  }
  .logo-loader i:nth-child(1) { transform: none; }
  .logo-loader i:nth-child(2) { transform: translate(-21%, 21%); background: var(--ll-mid); }
  .logo-loader i:nth-child(3) { transform: translate(-42%, 42%); background: var(--ll-front); }
}
/* Im Primär-Knopf: weiße Karten statt Logo-Farben, die Beschriftung weicht
   (ui.js, bindSignIn setzt .is-loading und hängt den Lader an). */
.btn-primary .logo-loader {
  --ll-back: color-mix(in srgb, var(--accent-ink) 40%, transparent);
  --ll-mid: color-mix(in srgb, var(--accent-ink) 68%, transparent);
  --ll-front: var(--accent-ink);
}
.btn > .btn-label { transition: opacity var(--dur-feedback) ease, transform var(--dur-state) var(--ease-out); }
.btn.is-loading { pointer-events: none; }
.btn.is-loading > .btn-label {
  opacity: 0;
  transform: translateY(calc(-4px * var(--motion-travel)));
}
.btn.is-loading > .logo-loader {
  position: absolute;
  inset: 0;
  margin: auto;
  animation: fade-in var(--dur-state) var(--ease-out) both;
}
/* Kurs-Refresh: der Lader ersetzt den runden Spinner. */
.quote-refresh .logo-loader { display: none; --ll-size: 1.05rem; }
.quote-refresh.is-busy .logo-loader { display: inline-block; }
.quote-refresh.is-busy .spinner { display: none; }

/* ---------- Fehler beim Anmelden und Registrieren ----------
   Eine abgelehnte Eingabe schüttelt das Formular einmal kurz, die Meldung
   legt sich darüber, das betroffene Feld wird markiert. Erfolgsmeldungen
   (abgemeldet, registriert) erscheinen ruhig, ohne Schütteln. */
.auth-box:has(.alert-error, .field-error) form {
  animation: form-nudge 420ms var(--ease-out) both;
}
@keyframes form-nudge {
  18% { transform: translateX(calc(-7px * var(--motion-travel))); }
  36% { transform: translateX(calc(6px * var(--motion-travel))); }
  54% { transform: translateX(calc(-4px * var(--motion-travel))); }
  72% { transform: translateX(calc(2px * var(--motion-travel))); }
}
.auth-box .alert,
.auth-box .field-error {
  animation: alert-in var(--dur-layout) var(--ease-arrive) both;
}
@keyframes alert-in {
  from { opacity: 0; transform: translateY(calc(-4px * var(--motion-travel))); }
}
/* Ohne "to" und ohne fill: der Schein klingt in den normalen Wert aus, also
   auch in den Fokusring, wenn das Feld fokussiert ist. */
.auth-box input:has(+ .field-error) {
  border-color: var(--danger);
  animation: field-flag 1200ms var(--ease-out);
}
.auth-box input:has(+ .field-error):focus { border-color: var(--accent); }
@keyframes field-flag {
  from { box-shadow: 0 0 0 3px color-mix(in srgb, var(--danger) 35%, transparent); }
}
