/* ============================================================
   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);
  /* Ruhiger Glanz, der alle paar Sekunden schräg über jede Kachel zieht. */
  --heat-shine: rgba(255, 255, 255, 0.42);

  /* 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);
    --heat-shine: rgba(255, 255, 255, 0.13);

    --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);
  --heat-shine: rgba(255, 255, 255, 0.13);

  --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 */
  /* Seitenwechsel: ruhiger als Dialoge und Umbauten (Wunsch Bars 03.11.2026, war zu schnell).
     Die Kurve startet sanfter als --ease-arrive, die den Weg fast ganz im ersten Viertel macht. */
  --dur-page: 500ms;       /* neue Seite rückt auf den Stapel */
  --dur-page-leave: 300ms; /* alte Seite tritt zurück */
  --ease-page: cubic-bezier(.25, 1, .5, 1);
  --ease-page-leave: cubic-bezier(.4, 0, .6, 1);

  /* 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; }
}

/* ---------- Layout-Tokens: Breite und Dichte (unabhängig vom Theme) ----------
   "Breit und kompakt" (Variante B, 11/2026): Datenseiten nutzen bis 106rem
   (rund 1480px auf dem 14px-Desktop; 116rem war Bars zu breit, an den
   Seiten soll Luft bleiben), der Seitenrand wächst mit dem Fenster von
   1.5rem bis 2.5rem. Text- und Formularseiten sitzen mit
   .page-narrow auf einer Lesebreite in der Mitte. Karten, Abstände und
   Seitentitel sind auf dem Desktop dichter; bis 40rem (Phones) gelten die
   alten Werte (Abschnitt "Smartphone und Touch"). Mehrspaltig werden breite
   Seiten erst ab 90rem (1440px), darunter bleibt ihr bisheriger Aufbau. */
:root {
  --page-max: 106rem;
  --page-gutter: clamp(1.5rem, 2.5vw, 2.5rem);
  --page-narrow: 60rem;   /* Text- und Formularseiten */
  --page-medium: 76rem;   /* Gästeseite (.home mit .gx-hero) */
  --page-top: 1.1rem;
  --card-pad: 1rem 1.25rem;
  --card-gap: 0.75rem;    /* zwischen Karten in Rastern */
  --card-stack: 0.75rem;  /* unter einer Karte im Fluss (margin-bottom) */
}

/* ---------- 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.4rem; 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 {
  max-width: var(--page-max);
  margin: 0 auto;
  padding: var(--page-top) var(--page-gutter) 4rem;
}
/* Text- und Formularseiten (Settings, Profil, Postfach, Login, Datenschutz,
   Fehler): der Inhalt bleibt auf Lesebreite und steht in
   der Mitte, statt die volle Breite auszureizen. Steht am Inhalts-Div der Seite
   (layout:fragment="content"). */
.page-narrow { max-width: var(--page-narrow); margin-inline: auto; }

/* ---------- 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) endet bei 106rem. */
  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-dashboard { --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 Dashboard 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: flex;
  align-items: center;
  gap: 0.6rem;
  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); }
/* Icons im Profil-Menü (fragments/account-icons): gedämpft wie die
   Link-Icons der Leiste, beim Überfahren in Tintenfarbe. */
.dropdown-item .topic-icon-sm {
  color: var(--ink-muted);
  transition: color 0.15s ease;
}
.dropdown-item:is(:hover, :focus-visible) .topic-icon-sm { color: var(--ink); }

/* 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,
.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: var(--card-pad);
  box-shadow: var(--shadow);
  margin-bottom: var(--card-stack);
}
.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: var(--card-gap);
}
.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 (Admin-Übersicht, "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 :is(h2, h3) { 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, 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), "Beta" in der Akzentfamilie
   (fragments/topic-badges.html). 0.8rem = 11px auf dem Desktop, die
   Untergrenze fuer UI-Text. */
.topic-dev-badge,
.topic-admin-badge,
.topic-beta-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);
}
/* Beta: helle Akzentfläche, Schrift ein Stück Richtung Tinte gemischt, damit
   sie in beiden Themes mindestens 4.5:1 hält (hell rund 5.4:1, dunkel 5.2:1). */
.topic-beta-badge {
  background: color-mix(in srgb, var(--accent) 12%, var(--surface));
  color: color-mix(in srgb, var(--accent) 82%, var(--ink));
}
/* Kompakt in engen Zeilen (angepinnter Eintrag der Leiste, Startseite):
   gleiche Schrift, weniger Luft. */
.topic-beta-badge.is-compact {
  margin-left: 0.35rem;
  padding: 0 0.4rem;
  letter-spacing: 0.04em;
  vertical-align: 0.1em;
}

.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; }

/* ---------- Zeilen mit Icon-Badge (Connect AI: Reichweite je Bereich) ---------- */

/* Breiter, damit lange Beschreibungen einzeilig bleiben und die Zeilen (samt
   Schaltern) 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 Zeile (Connect AI). 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; }
/* Kategorie-Überschrift. */
.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;
}

.hub-item-actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 0.5rem;
}

/* Abonnieren-Pill (Entdecken auf der Startseite) 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-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); }

/* Topic-Schalter (fragments/topic-placement.html im Topic Hub): vier Stufen
   Aus, Dashboard, Leiste, Beides als Segment-Pille mit echten Radios
   (.segmented-radio wie die Sprachwahl), jede mit Icon vor dem Wort (Beides
   mit zwei Icons). Das Icon folgt der Schriftfarbe: gedämpft, die gewählte
   Stufe in Tinte. Gespeichert
   wird beim Umschalten; solange die Anfrage läuft, sperrt ui.js die Radios
   der Zeile (is-busy) und die Pille dimmt. Ohne JavaScript steht daneben ein
   Speichern-Knopf (noscript). */
.placement-fieldset { border: 0; margin: 0; padding: 0; min-width: 0; }
.placement { flex-wrap: nowrap; }
/* "label > span" statt ".placement span": .segmented-radio span steht weiter
   unten und gewänne sonst bei gleicher Spezifität. Höhe und Polster bleiben
   die der Segment-Pille, nur links etwas weniger, weil das Icon selbst Luft
   mitbringt. */
.placement label > span {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding-left: 0.8rem;
  white-space: nowrap;
}
/* Die Icons stecken in eigenen spans (Fragmente aus topic-icons.html), und
   .segmented-radio span gäbe ihnen Polster, Rundung und die gedämpfte Farbe
   der Pille. Hier zurückgesetzt, die Farbe kommt von der Pille. */
.placement .placement-icon,
.placement .placement-icon-pair {
  display: flex;
  flex: none;
  padding: 0;
  border-radius: 0;
  color: inherit;
}
.placement .placement-icon { width: 1rem; height: 1rem; }
.placement .placement-icon svg { width: 100%; height: 100%; }
.placement .placement-icon-pair { gap: 0.2rem; }
.placement input:disabled + span { cursor: progress; }
.is-busy .placement { opacity: 0.6; transition: opacity var(--dur-feedback) var(--ease-out); }
.topic-placement { display: flex; align-items: center; gap: 0.5rem; }

.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; }
  .placement label > span { display: inline-flex; align-items: center; justify-content: center; 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 rutscht der Schalter
   unter den Text und nimmt die volle Breite. */
@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; }
  .topic-placement,
  .placement-fieldset { flex: 1 1 auto; min-width: 0; }
  .placement { display: flex; width: 100%; }
  /* Breite nach Textlänge plus gleich viel Luft: "Dashboard" bekommt mehr
     Platz als "Off", die Abstände zwischen den Wörtern bleiben gleich. */
  .placement label { flex: 1 1 auto; min-width: 0; }
  /* Vier Wörter plus Icons passen nebeneinander nicht in die Handybreite:
     das Icon rückt über das Wort, die Pille wird dafür höher. */
  .placement label > span {
    flex-direction: column;
    gap: 0.2rem;
    width: 100%;
    justify-content: center;
    text-align: center;
    padding: 0.45rem 0.3rem 0.4rem;
  }
}

/* ---------- 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-Dropdown an der Hub-Pill und die Topics-Gruppe der Schublade: Icons
   im Violett der Hub-Pill, so wie das Admin-Menü rot ist. Das Hub-Icon selbst
   sitzt in der Leiste am Pill-Rand und folgt dort der Linkfarbe. */
.nav-dropdown-hub .topic-icon-sm,
.drawer-group-topics .topic-icon-sm { color: var(--hub); }


/* Hover-Menüs der Leiste (Topic-Dropdown, Admin): 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);
}

/* ---------- Nachrichtentext (Postfach, Vorschau der Ankündigungen) ---------- */

/* Zeilenumbrüche aus dem Text erhalten (Textarea-Inhalt). */
.post-content { margin: 0; white-space: pre-wrap; }

/* Bild einer Ankündigung (V63/V64): groß unter dem Text, ein Einblick ins
   Topic, in Postfach, Feed und Vorschau gleich. Volle Breite bis 42rem, das
   Seitenverhältnis bleibt (width/height am img reservieren den Platz vor dem
   Laden); sehr hohe Bilder enden bei 30rem und zeigen ihren oberen Teil. Mit
   Knopf ist es ein Link zum selben Ziel; der Rahmen wird dann beim Zeigen
   kräftiger, gehoben wird nichts (Karte und Zeile bleiben ruhig). */
.msg-image {
  display: block;
  width: fit-content;
  max-width: min(100%, 42rem);
  margin-top: 0.85rem;
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--surface-2);
}
.msg-image img {
  display: block;
  max-width: 100%;
  height: auto;
  max-height: 30rem;
  object-fit: cover;
  object-position: top center;
}
a.msg-image { transition: border-color var(--dur-state) var(--ease-out); }
a.msg-image:hover { border-color: var(--line-strong); }
a.msg-image:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Im Feed füllt das Bild die Karte; nur das breite Postfach braucht die 42rem-Grenze. */
.feed-item .msg-image { max-width: 100%; }

/* ---------- 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: var(--card-gap);
  margin-bottom: 1.2rem;
}
.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; }

/* Ab 90rem: die Abo-Liste links fängt oben an, das Dashboard steht als
   28rem-Spalte rechts daneben (Panels untereinander) und bleibt beim
   Scrollen langer Listen stehen. */
@media (min-width: 90rem) {
  .sub-layout {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 28rem;
    grid-template-rows: auto 1fr;
    column-gap: 1.25rem;
    align-items: start;
  }
  .sub-layout > .subdash {
    grid-column: 2;
    grid-row: 1 / span 2;
    grid-template-columns: minmax(0, 1fr);
    margin-bottom: 0;
    position: sticky;
    top: 4.5rem;
  }
  .sub-layout > .sub-list-heading { grid-column: 1; grid-row: 1; }
  .sub-layout > .sub-list,
  .sub-layout > .sub-empty { grid-column: 1; grid-row: 2; }
}

@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: Unterseiten eines Topics (Markets News, BTC, Money Math). */
.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);
}
/* ---- Kopf der Übersicht (Markets-Redesign 11/2026) ----
   Briefing-Karte mit Satz, Live-Status und Plus/Minus-Leiste, darunter drei
   Karten: Top-Gewinner, Top-Verlierer, neueste News. */
.mk-top { display: grid; gap: var(--card-gap); margin-bottom: 1rem; }
.mk-brief {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1.2rem 1.8rem;
  padding: 1rem 1.25rem;
  margin-bottom: 0;
}
.mk-brief-text { flex: 999 1 26rem; min-width: 0; display: grid; gap: 0.7rem; }
.mk-brief-lead {
  margin: 0;
  font-family: var(--font-display);
  font-size: 1.15rem;
  font-weight: 600;
  line-height: 1.4;
  letter-spacing: -0.01em;
  text-wrap: pretty;
}
.mk-brief-avg { font-variant-numeric: tabular-nums; }
.mk-brief-avg.is-up { color: var(--success); }
.mk-brief-avg.is-down { color: var(--danger); }
.mk-brief-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem 0.6rem;
  margin: 0;
  font-size: 0.88rem;
  color: var(--ink-muted);
  font-variant-numeric: tabular-nums;
}
.mk-brief-facts { display: inline-flex; flex-wrap: wrap; align-items: baseline; }
.mk-brief-facts > span + span::before { content: "·"; margin: 0 0.45rem; }
.mk-brief-split { flex: 1 1 15rem; display: grid; gap: 0.5rem; }
.mk-split-counts {
  display: flex;
  justify-content: space-between;
  gap: 0.6rem;
  font-size: 0.9rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.mk-split-counts > span { display: inline-flex; align-items: center; gap: 0.3rem; }
.mk-split-counts .is-up { color: var(--success); }
.mk-split-counts .is-down { color: var(--danger); }
.mk-split {
  display: flex;
  gap: 2px;
  height: 0.7rem;
  border-radius: 999px;
  overflow: hidden;
  background: var(--surface-2);
}
.mk-split > span { min-width: 4px; }
.mk-split > .is-up { background: var(--success); }
.mk-split > .is-down { background: var(--danger); }
.mk-split > .is-flat { background: var(--line-strong); }

/* Live-Status: grünes Paar mit pulsierendem Punkt, solange ein Markt der
   Watchlist handelt; sonst ruhig in Raised Mist ("Markets closed"). */
.live-pill {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.12rem 0.65rem;
  border-radius: 999px;
  font-size: 0.8rem;
  font-weight: 600;
  white-space: nowrap;
  color: var(--success);
  background: var(--success-bg);
}
.live-dot { width: 0.45rem; height: 0.45rem; border-radius: 50%; background: currentColor; flex: none; }
.live-pill.is-closed { color: var(--ink-muted); background: var(--surface-2); }

.mk-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr));
  gap: var(--card-gap);
}
.mk-row > .card { margin-bottom: 0; }
.mk-movers { padding: 1.15rem 0.85rem 0.6rem; }
.mk-movers h2, .mk-news h2 { margin: 0; font-size: 1.05rem; }
.mk-movers h2 { padding: 0 0.6rem 0.4rem; }
.mk-movers-empty { margin: 0.3rem 0.6rem 0.7rem; font-size: 0.88rem; }
.mk-mover {
  display: flex;
  align-items: center;
  gap: 0.8rem;
  padding: 0.6rem 0.6rem;
  border-radius: 12px;
  color: inherit;
  text-decoration: none;
  transition: background-color var(--dur-state) ease;
}
.mk-mover:hover { background: var(--surface-2); }
.mk-mover:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.mk-mover-text { flex: 1; min-width: 0; display: grid; }
.mk-mover-name { font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.mk-mover-meta { font-size: 0.85rem; color: var(--ink-muted); font-variant-numeric: tabular-nums; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* Symbol-Badge (Karten, Mover): Logo oder zwei Buchstaben auf einer
   Stack-Blue-Tönung wie die Icon-Badges der Kacheln. */
.market-logo.mk-badge {
  width: 2.55rem;
  height: 2.55rem;
  border-radius: 10px;
}
.market-logo.mk-badge.is-fallback { background: color-mix(in srgb, var(--accent) 14%, var(--surface)); }
.market-logo.mk-badge.is-fallback::before { font-size: 0.85rem; color: var(--accent); }

.mk-news { display: flex; flex-direction: column; gap: 0.15rem; padding: 1.15rem 1.5rem 0.8rem; }
.mk-news-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.8rem;
  margin-bottom: 0.35rem;
}
.mk-news-new {
  display: inline-block;
  margin-left: 0.3rem;
  padding: 0.08rem 0.55rem;
  border-radius: 999px;
  font-family: var(--font-body);
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0;
  vertical-align: 0.1em;
  color: var(--accent);
  background: color-mix(in srgb, var(--accent) 14%, transparent);
}
.mk-news-all { font-size: 0.88rem; font-weight: 600; white-space: nowrap; }
.mk-news-item {
  display: grid;
  gap: 0.25rem;
  padding: 0.65rem 0;
  color: inherit;
  text-decoration: none;
}
.mk-news-item + .mk-news-item { border-top: 1px solid var(--line); }
.mk-news-title { font-weight: 600; line-height: 1.4; }
.mk-news-item:hover .mk-news-title { text-decoration: underline; }
.mk-news-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.35rem;
  font-size: 0.85rem;
  color: var(--ink-muted);
}
.mk-news-meta .quote-change { font-size: 0.8rem; font-weight: 600; padding: 0 0.5rem; }
.mk-news-empty { margin: 0.3rem 0 0.4rem; font-size: 0.88rem; }

/* Watchlist-Kopf: Titel mit Anzahl links, Zeitraum und Ansicht rechts. */
.wl-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0.8rem;
  margin-bottom: 1rem;
}
.wl-title { position: relative; margin: 0; font-size: 1.2rem; }
.wl-count { color: var(--ink-muted); font-weight: 600; font-variant-numeric: tabular-nums; }

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

/* Segment-Umschalter der Markets-Übersicht (Zeitraum, Ansicht, Größe der
   Heatmap): Raised-Mist-Spur, die Wahl als Surface-Pille. Zeitraum und
   Ansicht bekommen von ui.js eine gleitende Pille (.iv-pill, Abschnitt
   "Markets: Intervall-Wechsel"), der Größen-Schalter springt. */
.seg-toggle {
  position: relative;
  isolation: isolate;
  display: inline-flex;
  gap: 2px;
  padding: 3px;
  border-radius: 999px;
  background: var(--surface-2);
}
.seg-toggle button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 2.3rem;
  padding: 0 0.85rem;
  border: 0;
  border-radius: 999px;
  background: none;
  font: inherit;
  font-size: 0.88rem;
  font-weight: 600;
  color: var(--ink-muted);
  cursor: pointer;
  white-space: nowrap;
  transition: color var(--dur-state) ease, background-color var(--dur-state) ease;
}
.seg-toggle button:hover { color: var(--ink); }
.seg-toggle button.active,
.seg-toggle button[aria-pressed="true"] {
  color: var(--ink);
  background: var(--surface);
  box-shadow: var(--shadow-sm);
}
.seg-toggle button:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* 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(16rem, 1fr));
  gap: 1.1rem;
  margin-bottom: 2rem;
}
.market-grid .card { margin-bottom: 0; }
/* Mit eigenen Gruppen: ein Abschnitt je Gruppe mit kleiner Überschrift. */
.wl-cards { display: grid; gap: 1.5rem; margin-bottom: 2rem; }
.wl-cards .market-grid { margin-bottom: 0; }
.wl-section-title { margin: 0 0 0.7rem; font-size: 1.05rem; }

/* 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 (Markets-Redesign 11/2026): Symbol-Badge mit Name, Kürzel
   und Typ; groß der Kurs mit der Pille des gewählten Zeitraums; eine
   30-Tage-Fläche; unten KGV und Börsenwert. Die ganze Karte verlinkt auf die
   Detailseite (Stretched-Link unten). */
.market-card {
  display: flex;
  flex-direction: column;
  gap: 0.85rem;
  padding: 1.3rem 1.45rem;
  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.75rem;
  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);
}
.market-card-ident { flex: 1; min-width: 0; display: grid; gap: 0.1rem; }
.market-card-name {
  min-width: 0;
  font-size: 1rem;
  font-weight: 700;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.market-card-meta {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  min-width: 0;
  font-size: 0.85rem;
  color: var(--ink-muted);
}
.market-card-sym { font-size: 0.82rem; white-space: nowrap; }
.market-card-meta .badge { font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.04em; }

.market-card-price-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5rem;
  min-width: 0;
}
.market-card-price {
  font-family: var(--font-display);
  font-size: 1.55rem;
  font-weight: 700;
  line-height: 1.15;
  letter-spacing: -0.01em;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.market-price-missing { font-size: 0.95rem; font-weight: 500; }

/* Pillen der drei Zeiträume: sichtbar ist die zum data-interval des Rasters
   passende. Vor Börsenöffnung steht "Prev day" klein davor. */
.market-intervals { display: inline-flex; min-width: 0; }
.market-interval {
  display: none;
  align-items: center;
  gap: 0.4rem;
}
.market-grid[data-interval="day"] .iv-day,
.market-grid[data-interval="month"] .iv-month,
.market-grid[data-interval="year"] .iv-year {
  display: inline-flex;
}
.market-prev { font-size: 0.8rem; font-weight: 600; color: var(--ink-muted); white-space: nowrap; }

/* Veränderungs-Pille mit Pfeil (fragments/market-parts.html). */
.chg-pill {
  display: inline-flex;
  align-items: center;
  gap: 0.28rem;
  padding: 0.14rem 0.6rem;
  font-size: 0.85rem;
  font-weight: 600;
}
.chg-dir { width: 0.62em; height: 0.62em; flex: none; fill: currentColor; }
.is-down > .chg-dir, .chg-dir.is-turned { transform: rotate(180deg); }
.is-flat > .chg-dir { display: none; }

/* 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: 3.4rem;
  overflow: visible;
}
.spark-empty {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 3.4rem;
  margin: 0;
  font-size: 0.8rem;
  border: 1px dashed var(--line);
  border-radius: 8px;
}

/* Kennzahlen in einer Zeile unter einer Haarlinie, leise in Muted Slate. */
.market-metrics-row {
  display: flex;
  justify-content: space-between;
  gap: 0.6rem;
  padding-top: 0.75rem;
  border-top: 1px solid var(--line);
  font-size: 0.85rem;
  color: var(--ink-muted);
  font-variant-numeric: tabular-nums;
}
.market-metrics-row b { font-weight: 600; }
.market-metrics-cap { margin-left: auto; }

/* 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); }

/* Unterseiten (News, 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); }

/* ---------- Markets: Detailseite (Redesign 11/2026, Entwurf final-detail) ----------
   Hero-Karte mit Kopf, Kurs, Chart (Lightweight Charts) und Spannen, darunter
   Kennzahlen und Earnings nebeneinander, der KI-Wochenüberblick und die
   News zum Wert. Bewegung im Abschnitt "Markets: Detailseite landet". */
/* minmax(0, 1fr): die Spur darf schmaler werden als der breiteste Inhalt
   (Chart-Leiste), sonst liefe alles am Handy über den Kartenrand. */
.as { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--card-gap); margin-bottom: 1rem; }
.as > .card, .as-side > .card, .as-grid > .card { margin-bottom: 0; }
/* Wrapper der rechten Spalte: erst ab 90rem eine eigene Spalte (unten). */
.as-side { display: contents; }
.as-hero { display: grid; grid-template-columns: minmax(0, 1fr); gap: 0.8rem; padding: 1.1rem 1.3rem 1rem; }
.as-head { display: flex; align-items: flex-start; gap: 0.9rem; flex-wrap: wrap; }
.as-head .back-button { margin-top: 0.2rem; }
/* Logo im Kopf: so hoch wie Eyebrow und Name zusammen. */
.market-logo.mk-badge.as-logo { width: 3.6rem; height: 3.6rem; border-radius: 12px; }
.market-logo.mk-badge.as-logo.is-fallback::before { font-size: 1.05rem; }
/* Darf schrumpfen und umbrechen, damit Logo und Name auch am Handy in einer Zeile bleiben. */
.as-title { flex: 1 1 10rem; min-width: 0; }
.as-title .eyebrow { margin: 0 0 0.2rem; }
.asset-title {
  display: flex;
  align-items: baseline;
  gap: 0.6rem;
  flex-wrap: wrap;
  margin: 0;
  font-size: 1.9rem;
}
.asset-sym { font-size: 1.2rem; font-weight: 600; color: var(--ink-muted); }
/* Live-Region um die Watchlist-Knöpfe: display: contents, damit die
   Formulare Flex-Kinder des Kopfs bleiben. */
.asset-actions { display: contents; }

.as-quote { display: grid; gap: 0.55rem; }
.asset-quote-line {
  display: flex;
  align-items: baseline;
  gap: 0.8rem;
  flex-wrap: wrap;
  margin: 0;
}
/* Ink ausdrücklich: Absätze in Karten sind sonst Muted Slate (Fließtext). */
.asset-price {
  color: var(--ink);
  font-family: var(--font-display);
  font-size: 2.4rem;
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.025em;
  font-variant-numeric: tabular-nums;
}
.asset-quote-line .chg-pill { font-size: 0.92rem; padding: 0.2rem 0.75rem; }
.as-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem 0.6rem;
  margin: 0;
  font-size: 0.88rem;
  color: var(--ink-muted);
  font-variant-numeric: tabular-nums;
}
.as-meta-facts { display: inline-flex; flex-wrap: wrap; align-items: baseline; }
.as-meta-facts > span + span::before { content: "·"; margin: 0 0.45rem; }

/* Interaktiver Chart: in der Hero-Karte ohne eigenen Rahmen, die Leisten
   (Zeitraum bzw. Kerzenlänge, Chartart) als Segment-Umschalter darunter.
   Die Höhe folgt dem Fenster, bleibt aber gedeckelt. Ohne JS oder ohne
   Serie zeigt .asset-spark-card die 30-Tage-Linie. */
.price-chart-wrap { margin: 0; }
.price-chart { height: clamp(260px, 40vh, 440px); width: 100%; }
.chart-toolbar {
  display: flex;
  gap: 0.6rem;
  align-items: center;
  flex-wrap: wrap;
  padding-top: 0.8rem;
}
.chart-toolbar-spacer { flex: 1; }
.chart-chips { display: flex; gap: 0.4rem; flex-wrap: wrap; }
.chart-chips.seg-toggle { gap: 2px; flex-wrap: nowrap; max-width: 100%; overflow-x: auto; scrollbar-width: none; }
.seg-toggle .chip-btn { min-height: 2.1rem; padding: 0 0.75rem; font-size: 0.85rem; }
.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.8rem; margin: -0.4rem 0 0.2rem; }
.chart-attribution a { color: var(--ink-muted); }
.asset-spark-card { display: grid; gap: 0.5rem; }
.asset-spark-card .chart-failed-note { margin: 0; }

/* Spannen unter dem Chart: Tagesspanne (Punkt = jetzt, Strich = Eröffnung),
   52-Wochen-Spanne, daneben Eröffnung, Vortagsschluss und Volumen. Eigene
   Live-Region (#asset-day), tickt mit dem Kurs; ui.js lässt die Marke von
   der alten zur neuen Stelle gleiten (--pos). Keine Statusfarbe: die Pille
   über dem Chart sagt schon, ob es rauf oder runter ging. */
.as-ranges {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
  gap: 1rem 1.8rem;
  padding-top: 1.1rem;
  border-top: 1px solid var(--line);
}
.as-label { margin: 0; font-size: 0.85rem; font-weight: 600; color: var(--ink-muted); }
.as-range { display: grid; gap: 0.5rem; align-content: start; min-width: 0; }
.as-range .day-range-bar { width: auto; min-width: 0; height: 0.55rem; }
.as-range .day-range-dot {
  width: 4px;
  height: 1.15rem;
  margin: -0.575rem 0 0 -2px;
  border-radius: 2px;
  box-shadow: 0 0 0 2px var(--surface);
}
.as-range-ends {
  display: flex;
  justify-content: space-between;
  gap: 0.6rem;
  font-size: 0.85rem;
  color: var(--ink-muted);
  font-variant-numeric: tabular-nums;
}
.as-day-stats {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 0.3rem 1rem;
  align-content: start;
  margin: 0;
  font-size: 0.88rem;
}
.as-day-stats div { display: contents; }
.as-day-stats dt { color: var(--ink-muted); }
.as-day-stats dd { margin: 0; font-weight: 600; text-align: right; font-variant-numeric: tabular-nums; }

/* 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);
}

/* Kennzahlen und Earnings nebeneinander, gleich hoch. */
.as-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr));
  gap: var(--card-gap);
  align-items: stretch;
}
.as-grid > .card { display: flex; flex-direction: column; gap: 0.8rem; padding: var(--card-pad); }
.as-grid h2, .as-news h2 { margin: 0; font-size: 1.2rem; }
.as-note { margin: 0; font-size: 0.8rem; color: var(--ink-muted); }
.as-stats .as-note { margin-top: auto; }
.as-kv { display: grid; margin: 0; }
.as-kv > div {
  display: flex;
  justify-content: space-between;
  gap: 0.8rem;
  padding: 0.6rem 0;
  border-bottom: 1px solid var(--line);
}
.as-kv > div:last-child { border-bottom: 0; }
.as-kv dt { color: var(--ink-muted); }
.as-kv dd { margin: 0; font-weight: 700; font-variant-numeric: tabular-nums; }
.as-kv dd.is-up { color: var(--success); }
.as-kv dd.is-down { color: var(--danger); }

/* Earnings: nächster Termin auf Raised Mist, darunter je Quartal Schätzung
   (Raised Mist mit Haarlinie) und Ist (Stack Blue) als Balkenpaar, die
   Abweichung in Prozent darüber in Status-Farbe. */
.as-next {
  display: grid;
  gap: 0.1rem;
  padding: 0.8rem 1rem;
  border-radius: 10px;
  background: var(--surface-2);
}
.as-next-date { font-weight: 700; font-variant-numeric: tabular-nums; }
.as-next-est { font-size: 0.85rem; color: var(--ink-muted); font-variant-numeric: tabular-nums; }
.as-next-est > span + span::before { content: "·"; margin: 0 0.4rem; }
.as-next-link { justify-self: start; margin-top: 0.3rem; font-size: 0.85rem; font-weight: 600; color: var(--accent); }
.as-eps {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(0, 1fr);
  gap: 0.7rem;
  align-items: end;
  height: 10rem;
}
.as-eps-q {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  gap: 0.3rem;
  height: 100%;
  min-width: 0;
}
.as-eps-pct { font-size: 0.8rem; font-weight: 700; font-variant-numeric: tabular-nums; color: var(--ink-muted); }
.as-eps-pct.is-up { color: var(--success); }
.as-eps-pct.is-down { color: var(--danger); }
.as-eps-bars { display: flex; align-items: flex-end; gap: 3px; height: 6.2rem; }
.as-eps-est, .as-eps-act { width: 0.85rem; border-radius: 3px 3px 0 0; }
.as-eps-est, .as-swatch.is-est { background: var(--surface-2); box-shadow: inset 0 0 0 1px var(--line-strong); }
.as-eps-act, .as-swatch.is-act { background: var(--accent); }
.as-eps-label { font-size: 0.8rem; color: var(--ink-muted); white-space: nowrap; font-variant-numeric: tabular-nums; }
.as-eps-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 1rem;
  margin: 0;
  font-size: 0.8rem;
  color: var(--ink-muted);
}
.as-eps-legend > span { display: inline-flex; align-items: center; gap: 0.35rem; }
.as-swatch { display: inline-block; width: 0.7rem; height: 0.7rem; border-radius: 2px; }
.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; }

/* KI-Wochenüberblick: Harbor-Teal-Funkeln als Anmerkung, Text englisch. */
.as-digest { display: grid; gap: 0.45rem; padding: var(--card-pad); }
.as-digest-head { display: flex; align-items: center; gap: 0.5rem; }
.as-digest-head h2 { margin: 0; font-size: 1.05rem; }
.as-digest-icon { width: 1.15rem; height: 1.15rem; color: var(--accent-2); }
.as-digest-text { margin: 0; color: var(--ink); line-height: 1.6; max-width: 72ch; }

/* News zum Wert: Stimmung, Schlagzeile als Link zur Quelle, Quelle und Zeit. */
.as-news { padding: 0.4rem 1.25rem 0.9rem; }
.as-news h2 { padding: 0.7rem 0 0.2rem; }
.as-news-item {
  display: flex;
  align-items: flex-start;
  gap: 0.9rem;
  padding: 0.85rem 0;
  border-bottom: 1px solid var(--line);
  color: inherit;
  text-decoration: none;
}
.as-news-item .quote-change { flex: none; margin-top: 0.15rem; font-size: 0.8rem; font-weight: 600; }
.as-news-text { display: grid; gap: 0.1rem; min-width: 0; }
.as-news-title { font-weight: 600; line-height: 1.4; }
.as-news-item:hover .as-news-title { text-decoration: underline; }
.as-news-meta { font-size: 0.85rem; color: var(--ink-muted); }
.as-news-foot {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: baseline;
  gap: 0.4rem 1rem;
  margin: 0.8rem 0 0;
}
.as-news-foot a { font-size: 0.88rem; font-weight: 600; }

/* Ab 90rem zweispaltig: links Kopf mit Kurs und Chart, darunter die
   Chart-Quelle; rechts .as-side als 27rem-Spalte mit Kennzahlen,
   Quartalszahlen, KI-Überblick und News untereinander. Die zweite Zeile ist
   1fr: ist die rechte Spalte höher, wächst nur sie, und die Quelle bleibt
   direkt unter dem Chart. */
@media (min-width: 90rem) {
  .as {
    grid-template-columns: minmax(0, 1fr) 27rem;
    grid-template-rows: auto 1fr;
    align-items: start;
  }
  .as > .as-hero { grid-column: 1; grid-row: 1; }
  .as > .chart-attribution { grid-column: 1; grid-row: 2; }
  .as-side {
    display: grid;
    gap: var(--card-gap);
    grid-column: 2;
    grid-row: 1 / span 2;
    min-width: 0;
  }
  .as-side > .as-grid { grid-template-columns: minmax(0, 1fr); }
}

/* ---------- 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; }

/* Die Heatmap ist eine Karte: oben Größen-Schalter, Live-Status und Zähler,
   in der Mitte das Feld, unten Farbskala und Hinweis zur Fläche. */
.hm.card {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  padding: 1.2rem 1.4rem;
  margin-bottom: 2rem;
}
.hm-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.8rem 1.4rem;
  flex-wrap: wrap;
}
.hm-bar-right {
  display: flex;
  align-items: center;
  gap: 0.6rem 0.8rem;
  flex-wrap: wrap;
}
.hm-counts {
  font-size: 0.88rem;
  font-weight: 600;
  color: var(--ink-muted);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.hm-counts .is-up { color: var(--success); }
.hm-counts .is-down { color: var(--danger); }
.hm-size { display: inline-flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; }
.hm-switch-label { font-size: 0.88rem; font-weight: 600; color: var(--ink-muted); }

/* Fuß: neun Stufen mit den Enden des Zeitraums, rechts der Hinweis, wonach
   sich die Fläche richtet (data-size am .hm, ui.js). */
.hm-foot {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.6rem 1.4rem;
}
.hm-legend {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  font-size: 0.8rem;
  color: var(--ink-muted);
}
.hm-legend-scale {
  display: grid;
  grid-template-columns: repeat(9, 1fr);
  gap: 2px;
  width: min(15rem, 46vw);
  height: 0.7rem;
}
.hm-legend-scale > span { background: var(--heat); border-radius: 3px; }
.hm-legend-scale > [data-heat="0"] { box-shadow: inset 0 0 0 1px var(--line); }
.hm-note { margin: 0 0 0 auto; font-size: 0.8rem; color: var(--ink-muted); }
.hm-note [data-size-note] { display: none; }
.hm[data-size="cap"] [data-size-note="cap"],
.hm[data-size="equal"] [data-size-note="equal"],
.hm[data-size="move"] [data-size-note="move"] { display: inline; }

/* Das Feld: die Kacheln liegen absolut darin (ui.js), gruppiert nach Region
   unter kleinen Überschriften ("US · $15.7T"). */
.hm-map {
  position: relative;
  height: clamp(20rem, 36vw, 32rem);
  contain: layout paint;
}
.hm-map[data-count="few"] { height: clamp(20rem, 38vw, 30rem); }
.hm-group-label {
  position: absolute;
  display: flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0 0.15rem;
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  pointer-events: none;
}
.hm-group-label span { font-weight: 600; font-variant-numeric: tabular-nums; }

.hm-tile {
  position: absolute;
  display: block;
  border-radius: 10px;
  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);
  }
}
/* Inhalt oben links: Badge und Kürzel, unten Veränderung und Kurs (wie im
   Entwurf heatmap-a). Kleine Kacheln zentrieren und sparen (siehe unten). */
.hm-tile-in {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  align-items: flex-start;
  gap: 0.2rem;
  padding: clamp(0.35rem, 6cqmin, 0.75rem) clamp(0.4rem, 6cqmin, 0.85rem);
  text-align: left;
  line-height: 1.2;
  overflow: hidden;
}
.hm-t-top { display: flex; align-items: center; gap: 0.5rem; max-width: 100%; min-width: 0; }
.hm-t-bottom { display: flex; flex-direction: column; gap: 0.05rem; max-width: 100%; }
.hm-t-sym {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(0.85rem, 8cqmin, 1.15rem);
  letter-spacing: -0.01em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.hm-sfx { font-weight: 600; opacity: 0.75; }
/* Symbol-Badge der Kachel: Logo oder zwei Buchstaben auf einer Tönung der
   Kachelschrift, so bleibt es auf jeder Stufe sichtbar (auch auf der
   Nullstufe, die dieselbe Farbe hat wie Raised Mist). */
.market-logo.hm-logo {
  width: clamp(1.45rem, 13cqmin, 2.05rem);
  height: clamp(1.45rem, 13cqmin, 2.05rem);
  border-radius: 8px;
  background: color-mix(in srgb, currentColor 16%, transparent);
  box-shadow: 0 0 0 1px color-mix(in srgb, currentColor 14%, transparent);
}
.market-logo.hm-logo.is-fallback::before {
  font-family: var(--font-body);
  font-size: 0.8rem;
  color: inherit;
}
.hm-t-chg {
  font-weight: 600;
  font-size: clamp(0.85rem, 6.5cqmin, 1.2rem);
  font-variant-numeric: tabular-nums;
}
.hm-chg-iv { display: inline-flex; align-items: center; gap: 0.3em; }
.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-size: 0.85rem;
  font-weight: 600;
  opacity: 0.85;
}
/* Verlauf im freien Band zwischen Kopf und Fuß der Kachel, damit er nie
   unter Kürzel, Prozent oder Kurs läuft; nur auf großen Kacheln. */
.hm-t-curve {
  position: absolute;
  left: 0;
  right: 0;
  top: 3.4rem;
  width: 100%;
  /* Explizit: mit height:auto behielte das SVG sein Seitenverhältnis. */
  height: calc(100% - 3.4rem - 5rem);
  display: none;
  pointer-events: none;
  opacity: 0.26;
  overflow: hidden;
}
.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 nur Kürzel und Prozent, zentriert (Börsenkürzel, Pfeil und Badge
   fallen zuerst weg), mittel mit Kurs, groß dazu der Verlauf. */
@container (max-width: 5.6rem) or (max-height: 4.2rem) {
  .hm-tile-in { justify-content: center; align-items: center; text-align: center; }
  .hm-t-bottom { align-items: center; }
  .hm-logo, .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: 6.5rem) and (min-height: 5.4rem) {
  .hm-t-price { display: block; }
}
@container (min-width: 10rem) and (min-height: 11.5rem) {
  .hm-t-curve { display: block; }
}

/* 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.card { padding: 1rem 0.9rem; }
  .hm-map { height: 30rem; }
  .hm-map[data-count="few"] { height: 24rem; }
  .hm-note { margin-left: 0; }
}

/* 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; }

/* Bearbeiten-Popup mit Gruppen (Markets-Redesign 11/2026, Entwurf edit-c):
   Gruppen als Kärtchen (Kopf in Raised Mist mit Griff, Name, Anzahl,
   Umbenennen), darin die Werte mit Griff und Entfernen. Beim Ziehen folgt
   eine leicht gekippte Kopie dem Zeiger, ein gestrichelter Platzhalter zeigt
   das Ziel, die Zielgruppe wird gestrichelt in Stack Blue umrandet. */
dialog.dialog-card.dialog-edit {
  width: min(36rem, calc(100vw - 2rem));
  --dialog-top: clamp(1.5rem, 8vh, 4rem);
  margin-top: var(--dialog-top);
  margin-bottom: auto;
  max-height: calc(100dvh - var(--dialog-top) - 1.5rem);
}
dialog.dialog-edit.is-dragging { user-select: none; -webkit-user-select: none; }
.ed-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 0.8rem;
  margin-bottom: 1rem;
}
.ed-head h2 { margin: 0; }
.ed-lead { margin: 0.25rem 0 0; font-size: 0.88rem; color: var(--ink-muted); }
.ed-head .inline-form { flex: none; }
/* minmax(0, 1fr): lange Namen kürzen sich mit Ellipse, statt die Spalte
   (und am Handy das Bottom Sheet) über den Rand zu drücken. */
.ed-groups { display: grid; grid-template-columns: minmax(0, 1fr); gap: 0.7rem; }
.ed-group {
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--surface);
  transition: border-color var(--dur-state) ease, background-color var(--dur-state) ease;
}
.ed-group.is-target {
  border: 1.5px dashed var(--accent);
  background: color-mix(in srgb, var(--accent) 6%, var(--surface));
}
.ed-group-head {
  position: relative;
  display: flex;
  align-items: center;
  gap: 0.45rem;
  min-height: 3rem;
  padding: 0.3rem 0.5rem 0.3rem 0.3rem;
  border-radius: 11px 11px 0 0;
  background: var(--surface-2);
}
.ed-group.is-target .ed-group-head { background: color-mix(in srgb, var(--accent) 12%, var(--surface-2)); }
.ed-group[data-fixed] .ed-group-head { padding-left: 0.9rem; }
.ed-group-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ed-count { font-size: 0.85rem; font-weight: 600; color: var(--ink-muted); font-variant-numeric: tabular-nums; }
.ed-grip {
  display: grid;
  place-items: center;
  flex: none;
  width: 2.2rem;
  height: 2.5rem;
  border: 0;
  border-radius: 8px;
  background: transparent;
  color: var(--ink-faint);
  cursor: grab;
  touch-action: none;
}
.ed-grip:hover { color: var(--ink); background: color-mix(in srgb, var(--ink) 6%, transparent); }
.ed-grip:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.is-dragging .ed-grip { cursor: grabbing; }
.ed-mini {
  display: grid;
  place-items: center;
  flex: none;
  width: 2.3rem;
  height: 2.3rem;
  border: 0;
  border-radius: 10px;
  background: transparent;
  color: var(--ink-muted);
  cursor: pointer;
  list-style: none;
  transition: background-color var(--dur-state) ease, color var(--dur-state) ease;
}
.ed-mini::-webkit-details-marker { display: none; }
.ed-mini:hover { color: var(--ink); background: var(--surface); }
.ed-mini:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.ed-remove { color: var(--danger); }
.ed-remove:hover { color: var(--danger); background: var(--danger-bg); }
/* Umbenennen und Löschen: kleines Feld unter dem Gruppenkopf (details). */
.ed-rename[open] > .ed-mini { color: var(--accent); background: var(--surface); }
.ed-rename-body {
  position: absolute;
  top: calc(100% + 0.3rem);
  left: 0.5rem;
  right: 0.5rem;
  z-index: 3;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  padding: 0.6rem;
  border: 1px solid var(--line-strong);
  border-radius: 10px;
  background: var(--surface);
  box-shadow: var(--shadow);
}
.ed-rename-form { display: flex; gap: 0.5rem; flex: 1 1 14rem; min-width: 0; }
.ed-rename-form input { flex: 1; min-width: 0; }
.ed-items { display: grid; grid-template-columns: minmax(0, 1fr); }
.ed-item {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  min-height: 2.9rem;
  padding: 0.2rem 0.4rem 0.2rem 0.3rem;
  border-top: 1px solid var(--line);
  background: var(--surface);
}
.ed-item:last-child { border-radius: 0 0 11px 11px; }
.ed-item-text { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ed-sym { font-size: 0.85rem; color: var(--ink-muted); font-variant-numeric: tabular-nums; }
.ed-empty {
  margin: 0.5rem;
  padding: 0.6rem;
  border: 1.5px dashed var(--line-strong);
  border-radius: 10px;
  text-align: center;
  font-size: 0.85rem;
  color: var(--ink-muted);
}
.ed-placeholder {
  margin: 0.3rem 0.5rem;
  border: 1.5px dashed var(--accent);
  border-radius: 10px;
  background: color-mix(in srgb, var(--accent) 6%, transparent);
}
.ed-groups > .ed-placeholder { margin: 0; border-radius: 12px; }
/* Doppelter Selektor: die Kopie ist das letzte Kind des Dialogs, sonst
   gewänne .ed-item:last-child mit seinen nur unten runden Ecken. */
.ed-item.ed-ghost,
.ed-group.ed-ghost {
  position: fixed;
  z-index: 80;
  margin: 0;
  border: 1px solid var(--accent);
  border-radius: 12px;
  background: var(--surface-2);
  box-shadow: var(--shadow);
  pointer-events: none;
  transform: rotate(calc(-1.5deg * var(--motion-travel)));
}
.ed-item.is-picked,
.ed-group.is-picked > .ed-group-head {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
  background: color-mix(in srgb, var(--accent) 10%, var(--surface));
}
.ed-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.8rem;
  margin-top: 1rem;
}
.ed-hint { font-size: 0.85rem; color: var(--ink-muted); }

/* ---------- Quartalszahlen-Kalender von Markets (/tools/markets/earnings, Entwurf topic-earnings) ----------
   Kopf mit Bereich (Segment-Umschalter als Links) und Wochennavigation,
   darunter eine Karte je Wochentag mit den Terminen als Kästchen; Werte der
   eigenen Watchlist in Stack Blue gerahmt und getönt. Unten die letzten
   Ergebnisse mit Beat/Miss-Pille im Erfolgs- bzw. Gefahrenpaar. */
.ec-lead { margin: 0.2rem 0 0; color: var(--ink-muted); }
.ec-controls { display: flex; flex-wrap: wrap; align-items: center; gap: 0.6rem; }
.ec-scope .segmented-option { text-decoration: none; }
.ec-weeknav { display: flex; align-items: center; gap: 0.4rem; }
.ec-step {
  display: grid;
  place-items: center;
  width: 2.6rem;
  height: 2.6rem;
  border: 1px solid var(--line-strong);
  border-radius: 10px;
  background: var(--surface);
  color: var(--ink);
  transition: background-color var(--dur-state) ease, border-color var(--dur-state) ease;
}
.ec-step:hover { background: var(--surface-2); }
.ec-step:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.ec-step .svg-icon { width: 1.15rem; height: 1.15rem; stroke-width: 2; }
.ec-range { min-width: 7.5rem; text-align: center; font-variant-numeric: tabular-nums; }
.ec-hint {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.8rem;
  margin: 1rem 0 0;
}
.ec-hint p { margin: 0; }
.ec-hint-actions { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.ec-days {
  display: grid;
  grid-template-columns: repeat(var(--ec-cols, 5), minmax(0, 1fr));
  gap: 0.8rem;
  margin-top: 1.2rem;
}
.ec-day {
  display: flex;
  flex-direction: column;
  gap: 0.55rem;
  min-height: 16rem;
  margin: 0;
  padding: 0.9rem;
}
.ec-day.is-today { border-color: color-mix(in srgb, var(--accent) 45%, var(--line)); }
.ec-day-head { display: flex; align-items: baseline; justify-content: space-between; gap: 0.5rem; }
.ec-day-name { margin: 0; font-size: 1rem; }
.ec-day-date { font-size: 0.82rem; color: var(--ink-muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
.ec-today { color: var(--accent); font-weight: 600; }
.ec-events { display: grid; gap: 0.45rem; margin: 0; padding: 0; list-style: none; }
.ec-event {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  overflow: hidden;
  padding: 0.6rem 0.7rem;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--paper);
  color: var(--ink);
  text-decoration: none;
  transition: border-color var(--dur-state) ease, transform var(--dur-state) var(--ease-out);
}
.ec-event:hover { border-color: var(--line-strong); transform: translateY(calc(-1px * var(--motion-travel))); }
.ec-event:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.ec-event.is-mine {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 12%, var(--surface));
}
.ec-event-top { display: flex; align-items: baseline; justify-content: space-between; gap: 0.4rem; }
.ec-sym { font-variant-numeric: tabular-nums; }
.ec-when { font-size: 0.76rem; color: var(--ink-muted); white-space: nowrap; }
.ec-name {
  overflow: hidden;
  font-size: 0.84rem;
  color: var(--ink-muted);
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ec-eps {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.35rem;
  font-size: 0.84rem;
  font-variant-numeric: tabular-nums;
}
.ec-result {
  display: inline-flex;
  align-items: center;
  padding: 0.1rem 0.5rem;
  border-radius: 999px;
  font-size: 0.75rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.ec-result.is-up { color: var(--success); background: var(--success-bg); }
.ec-result.is-down { color: var(--danger); background: var(--danger-bg); }
.ec-result.is-flat { color: var(--ink-muted); background: var(--surface-2); }
.ec-none { margin: 0; font-size: 0.85rem; color: var(--ink-muted); }
.ec-day .ec-none { margin: auto 0; text-align: center; }
.ec-legend { display: grid; gap: 0.25rem; margin: 0.8rem 0 1.2rem; font-size: 0.84rem; color: var(--ink-muted); }
.ec-legend p { display: flex; align-items: center; gap: 0.5rem; margin: 0; }
/* Erinnerung am Vortag: Schalter mit Satz, zwischen Legende und letzten Ergebnissen. */
.ec-remind { display: flex; flex-wrap: wrap; align-items: center; gap: 0.6rem; margin: -0.4rem 0 1.2rem; }
.ec-remind .switch-field { color: var(--ink); font-size: 0.9rem; }
.ec-swatch {
  flex: none;
  width: 0.9rem;
  height: 0.9rem;
  border: 1px solid var(--accent);
  border-radius: 4px;
  background: color-mix(in srgb, var(--accent) 12%, var(--surface));
}
.ec-recent { padding: 1.1rem 1.4rem; }
.ec-recent h2 { margin: 0; font-size: 1.15rem; }
.ec-recent-lead { margin: 0.15rem 0 0.6rem; font-size: 0.84rem; color: var(--ink-muted); }
.ec-recent-list { margin: 0; padding: 0; list-style: none; }
.ec-recent-row {
  display: grid;
  grid-template-columns: minmax(0, 2fr) 5.5rem 8rem 8rem auto;
  align-items: center;
  gap: 0.8rem;
  padding: 0.65rem 0;
  border-top: 1px solid var(--line);
  font-variant-numeric: tabular-nums;
}
.ec-recent-asset {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  min-width: 0;
  color: var(--ink);
  text-decoration: none;
}
.ec-recent-asset:hover strong { text-decoration: underline; }
.ec-recent-row.is-mine .ec-recent-asset strong { color: var(--accent); }
.ec-recent-name { overflow: hidden; color: var(--ink-muted); text-overflow: ellipsis; white-space: nowrap; }
.ec-recent-date { color: var(--ink-muted); }
.ec-recent-row .ec-result { justify-self: end; }
@media (max-width: 64rem) {
  .ec-days { grid-template-columns: repeat(auto-fill, minmax(12rem, 1fr)); }
}
@media (max-width: 40rem) {
  .ec-controls, .ec-scope { width: 100%; }
  .ec-scope .segmented-option { flex: 1; text-align: center; }
  .ec-weeknav { width: 100%; }
  .ec-range { flex: 1; }
  .ec-days { grid-template-columns: minmax(0, 1fr); gap: 0.6rem; }
  .ec-day { min-height: 0; }
  .ec-day.is-empty { flex-direction: row; align-items: baseline; justify-content: space-between; }
  .ec-day.is-empty .ec-day-head { gap: 0.6rem; justify-content: flex-start; }
  .ec-day .ec-none { margin: 0; text-align: right; }
  .ec-recent { padding: 1rem; }
  .ec-recent-row { display: flex; flex-wrap: wrap; column-gap: 0.8rem; row-gap: 0.2rem; }
  .ec-recent-asset { flex: 1 1 100%; }
  .ec-recent-row .ec-result { margin-left: auto; }
}

/* Such-Popup (Markets-Redesign 11/2026, Entwurf search-b): Kopf mit
   Schließen-Knopf, Suchfeld als Eingabe-Box, Filter-Chips (Radios),
   Treffer in Gruppen mit Haarlinien, "Add" je Zeile, Bestätigung mit Undo. */
.sd-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.8rem;
  margin-bottom: 0.9rem;
}
.sd-head h2 { margin: 0; }
.sd-close {
  display: grid;
  place-items: center;
  width: 2.6rem;
  height: 2.6rem;
  flex: none;
  border-radius: 10px;
  color: var(--ink-muted);
  transition: background-color var(--dur-state) ease, color var(--dur-state) ease;
}
.sd-close:hover { color: var(--ink); background: var(--surface-2); }
.sd-close:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.sd-close .svg-icon { width: 1.15rem; height: 1.15rem; }
.dialog-search .asset-search { display: grid; gap: 0.75rem; }
.sd-field {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  min-height: 3.2rem;
  padding: 0 0.9rem;
  border: 1px solid var(--line-strong);
  border-radius: 8px;
  background: var(--paper);
  transition: border-color var(--dur-state) ease, box-shadow var(--dur-state) ease;
}
.sd-field:focus-within { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-strong); }
.sd-field .asset-search-icon { position: static; width: 1.1rem; height: 1.1rem; }
.asset-search .sd-field input[type="search"],
.asset-search .sd-field input[type="search"]:focus {
  flex: 1;
  min-width: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  box-shadow: none;
  outline: none;
  font-size: 1.05rem;
}
.sd-chips { display: flex; flex-wrap: wrap; gap: 0.4rem; }
/* Radios unsichtbar, aber fokussierbar: die Chips sind ihre Labels. */
.sd-chip-input { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
.sd-chip {
  display: inline-flex;
  align-items: center;
  min-height: 2.3rem;
  padding: 0 0.9rem;
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  background: var(--surface);
  font-size: 0.88rem;
  font-weight: 600;
  color: var(--ink);
  cursor: pointer;
  white-space: nowrap;
  transition: color var(--dur-state) ease, background-color var(--dur-state) ease, border-color var(--dur-state) ease;
}
.sd-chip:hover { background: var(--surface-2); }
.sd-chip-input:checked + .sd-chip {
  border-color: var(--accent);
  color: var(--accent);
  background: color-mix(in srgb, var(--accent) 12%, transparent);
}
.sd-chip-input:focus-visible + .sd-chip { outline: 2px solid var(--accent); outline-offset: 2px; }
.dialog-search .search-results { gap: 0; margin-top: 0.7rem; }
.sd-group {
  margin: 0.9rem 0 0.15rem;
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--accent-2);
}
.sd-group:first-child { margin-top: 0; }
.sd-row {
  display: flex;
  align-items: center;
  gap: 0.8rem;
  padding: 0.55rem 0.2rem;
  border-bottom: 1px solid var(--line);
}
.sd-hit { flex: 1; min-width: 0; display: grid; gap: 0.1rem; color: inherit; text-decoration: none; }
.sd-hit:hover .sr-title { text-decoration: underline; }
.sd-hit:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 6px; }
.sd-add { flex: none; color: var(--accent); }
.sd-added {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.3rem 0.7rem;
  border-radius: 10px;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--success);
  background: var(--success-bg);
}
.sd-status {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin-top: 0.8rem;
  padding: 0.65rem 0.9rem;
  border-radius: 10px;
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--success);
  background: var(--success-bg);
}
.sd-status .inline-form { margin-left: auto; }
.sd-undo {
  min-height: 2rem;
  padding: 0.2rem 0.3rem;
  border: 0;
  background: none;
  font: inherit;
  font-weight: 600;
  color: var(--accent);
  text-decoration: underline;
  cursor: pointer;
}
.sd-undo:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }
.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: Das Formular ist Anker für Takt und Sofort-
   Nachladen (ui.js) und hat mit JavaScript keinen sichtbaren Inhalt; der
   Knopf steht im noscript und erscheint nur ohne JavaScript. */
.quote-refresh .btn { margin-bottom: 0.9rem; }

/* Hinweis neben dem Watchlist-Label, solange beim Öffnen veraltete Kurse
   nachladen (Formular is-busy, nicht der stille Takt is-quiet). Absolut
   rechts neben dem Label: nimmt keinen Platz weg, die Umschalter daneben
   (am Handy in eigener Zeile) bleiben stehen. Bei reduzierter Bewegung steht
   der Logo-Lader still, der Text sagt trotzdem, was passiert. */
.wl-head .section-label { position: relative; }
.wl-updating {
  position: absolute;
  left: calc(100% + 0.6rem);
  top: 50%;
  transform: translateY(-50%);
  display: none;
  align-items: center;
  gap: 0.4rem;
  white-space: nowrap;
  font-size: 0.8rem;
  font-weight: 500;
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink-muted);
}
.wl-updating .logo-loader { --ll-size: 1rem; }
.wl-head:has(~ #markets-live .quote-refresh.is-busy:not(.is-quiet)) .wl-updating { display: inline-flex; }

.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;
}

/* ---------- Markets: News nach Wert (Redesign 11/2026, Entwurf news-b) ----------
   Links die Leiste mit allen Werten und ihren Artikelzahlen (sticky,
   filtert per ui.js), rechts je Wert ein Kartenstapel mit Kopf (Logo, Name,
   Kurs, Pille) und Artikeln: Punkt in Stimmungsfarbe, Schlagzeile, KI-
   Einordnung, Stimmung als Wort, Quelle und Zeit. Am Handy wird die Leiste
   eine waagerecht wischbare Reihe über den Gruppen. */
.nw-layout {
  display: grid;
  grid-template-columns: minmax(11rem, 14rem) minmax(0, 1fr);
  gap: 1.1rem;
  align-items: start;
}
.nw-side {
  position: sticky;
  top: 5.5rem;
  display: grid;
  gap: 2px;
  margin: 0;
  padding: 0.55rem;
}
.nw-side-link {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 0.6rem;
  min-height: 2.6rem;
  padding: 0.4rem 0.8rem;
  border-radius: 10px;
  color: var(--ink);
  font-weight: 600;
  text-decoration: none;
  transition: background-color var(--dur-state) ease, color var(--dur-state) ease;
}
.nw-side-link > span:first-child { display: inline-flex; align-items: center; gap: 0.4rem; min-width: 0; }
.nw-side-link:hover { background: var(--surface-2); }
.nw-side-link.is-active { color: var(--accent); background: color-mix(in srgb, var(--accent) 14%, transparent); }
.nw-side-link:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.nw-count { color: var(--ink-muted); font-variant-numeric: tabular-nums; }
.nw-side-link.is-active .nw-count { color: inherit; }
/* Neu seit dem letzten Besuch: kleiner Stack-Blue-Punkt neben dem Kürzel. */
.nw-side-new { width: 0.45rem; height: 0.45rem; border-radius: 50%; background: var(--accent); flex: none; }

.nw-groups { display: grid; gap: var(--card-gap); min-width: 0; }
.nw-group { display: grid; gap: 0.1rem; margin: 0; padding: 0.95rem 1.25rem 0.45rem; scroll-margin-top: 5.5rem; align-content: start; }
.nw-group-head {
  display: flex;
  align-items: center;
  gap: 0.8rem;
  padding-bottom: 0.7rem;
  border-bottom: 1px solid var(--line);
}
.nw-group-head h2 { flex: 1; min-width: 0; margin: 0; font-size: 1.05rem; }
.nw-group-head h2 a { color: inherit; text-decoration: none; }
.nw-group-head h2 a:hover { text-decoration: underline; }
.nw-sym { font-size: 0.88rem; font-weight: 600; color: var(--ink-muted); white-space: nowrap; }
.nw-price { font-weight: 700; font-variant-numeric: tabular-nums; white-space: nowrap; }
.nw-item {
  display: flex;
  gap: 0.8rem;
  padding: 0.75rem 0;
  color: inherit;
  text-decoration: none;
}
.nw-item + .nw-item { border-top: 1px solid var(--line); }
.nw-dot {
  flex: none;
  width: 0.6rem;
  height: 0.6rem;
  margin-top: 0.45rem;
  border-radius: 50%;
  background: var(--ink-faint);
}
.nw-dot.is-up { background: var(--success); }
.nw-dot.is-down { background: var(--danger); }
.nw-text { display: grid; gap: 0.15rem; min-width: 0; }
.nw-title { font-weight: 600; line-height: 1.4; color: var(--ink); }
.nw-item:hover .nw-title > span:first-child { text-decoration: underline; }
.nw-new {
  display: inline-block;
  margin-left: 0.45rem;
  padding: 0.02rem 0.5rem;
  border-radius: 999px;
  font-size: 0.8rem;
  font-weight: 600;
  vertical-align: 0.08em;
  color: var(--accent-ink);
  background: var(--accent-fill);
}
.nw-takeaway { font-size: 0.9rem; color: var(--ink-muted); }
.nw-meta { font-size: 0.85rem; color: var(--ink-muted); }
.nw-tone { font-weight: 600; }
.nw-tone.is-up { color: var(--success); }
.nw-tone.is-down { color: var(--danger); }
.nw-groups .news-ai-note, .nw-general + .news-ai-note { margin: 0.2rem 0 0; }
/* Ab 90rem stehen die Wert-Gruppen in zwei Spalten (je rund 40rem, eine
   angenehme Zeilenlänge); zeigt der Filter nur einen Wert, nimmt seine
   Gruppe wieder die ganze Breite, die Texte bleiben auf Lesebreite. */
@media (min-width: 90rem) {
  .nw-groups { grid-template-columns: repeat(2, minmax(0, 1fr)); align-items: start; }
  .nw-groups > .news-ai-note { grid-column: 1 / -1; }
  .nw-groups:has(> .nw-group[hidden]) { grid-template-columns: minmax(0, 1fr); }
  .nw-groups:has(> .nw-group[hidden]) .nw-text { max-width: 80ch; }
}
@media (max-width: 48rem) {
  .nw-layout { grid-template-columns: minmax(0, 1fr); }
  .nw-side {
    position: static;
    display: flex;
    gap: 0.3rem;
    overflow-x: auto;
    scrollbar-width: none;
  }
  .nw-side-link { flex: none; }
  .nw-group { padding: 1rem 1.1rem 0.4rem; }
}

/* ---------- 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; }
}

@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: var(--card-gap);
  align-items: start;
  margin-top: 1rem;
}
.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; }
/* Ab 90rem drei Spalten: Eingaben | Ergebnis und Kennzahlen | Diagramm,
   die Jahresübersicht darunter über beide Ergebnis-Spalten. */
@media (min-width: 90rem) {
  .calc {
    grid-template-columns: minmax(17rem, 22rem) minmax(0, 1fr) minmax(0, 1.25fr);
    grid-template-areas:
      "form hero chart"
      "form details chart"
      "form table table";
    grid-template-rows: auto auto 1fr;
  }
  .calc > .calc-chart-card .calc-chart { height: 17rem; }
}
@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; }
/* Die Gästeseite ist eine Erklärseite mit fester Komposition (Text links,
   Vorschau rechts): sie bleibt auf 76rem, statt die volle Breite der
   Datenseiten auszureizen. */
.home:has(> .gx-hero) { max-width: var(--page-medium); margin-inline: auto; }
/* 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.4rem; 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 1.75rem; }
.day-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15.5rem, 1fr));
  gap: var(--card-gap);
}
.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;
}
/* Markets in "Dein Tag" (Redesign 11/2026, Entwurf home-b): im Kopf
   Live-Pille und Zähler, darunter bis zu zehn Kacheln in zwei Reihen,
   gefärbt nach der Heat-Stufe des Tages (Tokens wie die Heatmap). */
.day-live {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  min-width: 0;
  margin-left: auto;
  font-size: 0.82rem;
  font-weight: 600;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.day-counts .is-up { color: var(--success); }
.day-counts .is-down { color: var(--danger); }
.day-hm {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 4px;
}
.day-hm-tile {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  min-width: 0;
  min-height: 4.2rem;
  padding: 0.5rem 0.55rem;
  border-radius: 8px;
  overflow: hidden;
  isolation: isolate;
  color: var(--heat-text);
  background: var(--heat);
}
.day-hm-tile b { font-size: 0.9rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.day-hm-tile > span { font-size: 0.8rem; font-weight: 600; font-variant-numeric: tabular-nums; 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: 1.75rem;
  align-items: start;
}
/* Ab 90rem drei Spalten: "Dein Tag" links (Karten untereinander), der Feed
   in der Mitte, die Wege rechts. So fängt alles oben an, statt dass der
   Feed unter einer Kartenreihe beginnt. .home-cols löst sich dafür auf
   (display: contents), Gruß und Erste Schritte bleiben volle Breite. Ohne
   "Dein Tag" nimmt der Feed die ersten beiden Spalten. Nur die Seite für
   Eingeloggte: die Gästeseite hat kein .home-cols. */
@media (min-width: 90rem) {
  .home:has(> .home-cols) {
    display: grid;
    grid-template-columns: 24rem minmax(0, 1fr) 22rem;
    column-gap: 1.75rem;
    align-items: start;
  }
  .home:has(> .home-cols) > * { grid-column: 1 / -1; }
  .home > .home-cols { display: contents; }
  .home > .day { grid-column: 1; margin: 0; }
  .home-cols > .feed { grid-column: 2; }
  .home-cols > .rail { grid-column: 3; }
  .home:not(:has(> .day)) .home-cols > .feed { grid-column: 1 / 3; }
  .home > .day .day-grid { grid-template-columns: minmax(0, 1fr); }
}
.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; }

@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; }
  .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); }

/* Empfänger: die Rollen, dann "Topic users" hinter einer Haarlinie, weil es
   die Rollen nicht ergänzt, sondern eingrenzt. */
.announce-recipients {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem 1.4rem;
  margin-top: 0.25rem;
}
.announce-recipients .checkbox-label { margin-top: 0; }
.announce-topic-users {
  padding-left: 1.4rem;
  border-left: 1px solid var(--line-strong);
}
/* Bildfeld: Dateiauswahl im Stil eines Ghost-Knopfs, daneben Entfernen. */
.announce-image {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 0.8rem;
}
.announce-image input[type="file"] {
  max-width: 100%;
  font: inherit;
  font-size: 0.9rem;
  color: var(--ink-muted);
}
.announce-image input[type="file"]::file-selector-button {
  margin-right: 0.7rem;
  padding: 0.42rem 0.8rem;
  border: 1px solid var(--line-strong);
  border-radius: 10px;
  background: transparent;
  color: var(--ink);
  font: inherit;
  font-weight: 600;
  cursor: pointer;
  transition: background-color var(--dur-state) var(--ease-out);
}
.announce-image input[type="file"]::file-selector-button:hover { background: var(--surface-2); }
/* Formular-Knöpfe tragen sonst einen Abstand nach oben. */
.announce-image .btn { margin: 0; }
.announce-image input[type="file"]:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 10px; }

/* 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; }
}

/* ---------- Admin-Seiten breit (Layout "breit und kompakt") ----------
   Nutzerverwaltung, Nutzer-Detail, Topic-Zugriff, Einladungscodes und
   Ankündigungen nutzen die volle Seitenbreite wie die übrigen Datenseiten;
   nur die Einleitungen bleiben auf Lesebreite. Nutzer-Detail und
   Einladungscodes teilen sich schon ab 64rem (1024px) in zwei Spalten, sonst
   stünde die Datenkarte schmal links bzw. das Formularfeld über die ganze
   Breite; die übrigen werden ab 90rem (1440px) mehrspaltig. Steht nach den
   Ankündigungen, damit die Spalten hier deren Grundregel überschreiben. */
.admin-intro { max-width: 72ch; }
/* Topic-Beschreibungen in den Zugriffszeilen umbrechen statt abschneiden
   (die Klasse user-email kürzt sonst mit "..."). */
.user-desc { white-space: normal; }

@media (min-width: 64rem) {
  /* Nutzer-Detail: Daten links, Aktionen bzw. der Hinweis aufs eigene
     Konto als 27rem-Spalte rechts. */
  .ud-layout {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 27rem;
    gap: var(--card-gap);
    align-items: start;
  }
  .ud-layout > .card,
  .ud-layout > .ud-self { margin: 0; }
  .ud-layout > .profile-card { max-width: none; }
  /* Einladungscodes: Formular als Spalte links, Tabelle daneben. */
  .ic-layout {
    display: grid;
    grid-template-columns: 22rem minmax(0, 1fr);
    gap: var(--card-gap);
    align-items: start;
  }
  .ic-layout > .card { margin: 0; }
}

@media (min-width: 90rem) {
  /* Nutzer- und Topic-Listen in zwei Spalten. */
  .user-list {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 0.6rem var(--card-gap);
  }
  /* Nutzer-Detail: die Datenpaare in zwei Spalten. */
  .ud-layout .data-list {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: 2.5rem;
  }
  /* Bei gerader Zeilenzahl endet auch die linke Spalte ohne Linie. */
  .ud-layout .data-row:nth-last-child(2):nth-child(odd) { border-bottom: none; }
  /* Ankündigungen: Formular und Vorschau gleich breit, so zeigt die
     Vorschau Postfach, Feed und Mail in etwa der echten Breite. */
  .announce-layout { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}


/* ---------- Monitoring (Topic MONITORING, templates/monitoring/, monitoring.js) ----------
   Admin-Topic: eine Momentaufnahme mit einem Reiter je Bereich. Nur Tokens,
   keine eigenen Farben. Wiederverwendet: .sub-head, .segmented (Settings-
   Reiter), .day-card (Dein Tag), table, .sub-item (aufklappbare Zeilen),
   .hub-chip (Filter), .role-badge, .user-avatar, .logo-loader. Die Bewegung
   (Reiterwechsel, frische Uhrzeit, Pille) steht unter "Bewegung", das
   Neuzeichnen der Icons unter "Topic-Icons zeichnen sich". */
.mon { padding-top: 0.25rem; }
.mon ::selection { background: var(--accent-strong); color: var(--ink); }
.mon .mono { font-family: var(--font-mono); font-size: 0.92em; }

/* Kopf: Titel links, rechts die Uhrzeit der Momentaufnahme und Refresh. */
.mon-head { flex-wrap: wrap; row-gap: 0.7rem; }
.mon-snapshot {
  display: flex;
  align-items: center;
  gap: 0.8rem;
}
.mon-snapshot-text {
  font-size: 0.85rem;
  color: var(--ink-muted);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.mon-snapshot-text time { color: var(--ink); font-weight: 600; }
.mon-refresh svg { width: 1rem; height: 1rem; flex: none; }
.mon-refresh .logo-loader { --ll-size: 1rem; }

/* Reiterleiste: die Segment-Pille der Settings. Breiter als der Bildschirm
   scrollt die Leiste in sich (wie die Navigationsleiste), sie bricht nie um. */
.mon-tabs {
  display: flex;
  width: max-content;
  max-width: 100%;
  margin: 0.2rem 0 1.6rem;
  overflow-x: auto;
  scrollbar-width: none;
  overscroll-behavior-x: contain;
}
.mon-tabs::-webkit-scrollbar { display: none; }
.mon-tabs .segmented-option {
  flex: none;
  display: inline-flex;
  align-items: center;
  text-decoration: none;
  white-space: nowrap;
}
.mon-tabs .segmented-option:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
/* Zustand eines Bereichs direkt am Reiter: die Form sagt, wie ernst. */
.mon-tab-mark {
  display: inline-flex;
  margin-left: 0.35rem;
}
.mon-tab-mark svg { width: 0.95rem; height: 0.95rem; }
.mon-tab-mark.is-warn { color: var(--warning); }
.mon-tab-mark.is-bad { color: var(--danger); }
.mon-tab-mark.is-wait,
.mon-tab-mark.is-off,
.mon-tab-mark.is-idle { color: var(--ink-muted); }
/* Mehr Reiter als Platz (Handy): der Rand blendet aus, wo noch etwas kommt
   (monitoring.js setzt is-more-left/right). */
.mon-tabs.is-more-right { mask-image: linear-gradient(to right, var(--ink) calc(100% - 1.5rem), transparent); }
.mon-tabs.is-more-left { mask-image: linear-gradient(to left, var(--ink) calc(100% - 1.5rem), transparent); }
.mon-tabs.is-more-left.is-more-right {
  mask-image: linear-gradient(to right, transparent, var(--ink) 1.5rem, var(--ink) calc(100% - 1.5rem), transparent);
}

/* Bereiche: mit JavaScript einer auf einmal (html.mon-js setzt schon das
   Inline-Skript im Template), ohne stehen alle untereinander. */
.mon-pane {
  display: grid;
  gap: 1.1rem;
  margin: 0 0 2.6rem;
}
.mon-pane .card { margin: 0; }
.mon-pane-title { margin: 0.4rem 0 0; }
html.mon-js .mon-pane:not(.is-active) { display: none; }
html.mon-js .mon-pane-title {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}
.mon-pane:focus { outline: none; }

/* Zustands-Pille: immer Wort plus Form-Icon plus Tönung, nie Farbe allein. */
.mon-state {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.14rem 0.6rem 0.14rem 0.42rem;
  border: 1px solid transparent;
  border-radius: 999px;
  font-size: 0.8rem;
  font-weight: 600;
  line-height: 1.35;
  white-space: nowrap;
  background: var(--surface-2);
  color: var(--ink-muted);
}
.mon-state svg { width: 0.95rem; height: 0.95rem; flex: none; }
.mon-state.is-ok { background: var(--success-bg); color: var(--success); }
.mon-state.is-warn { background: var(--warning-bg); color: var(--warning); }
.mon-state.is-bad { background: var(--danger-bg); color: var(--danger); }
.mon-state.is-off { background: transparent; border: 1px dashed var(--line-strong); }

/* Überblick: Statussatz mit Zustands-Icon, darunter sechs Karten. */
.mon-verdict {
  display: flex;
  align-items: flex-start;
  gap: 0.9rem;
  max-width: 54rem;
  margin: 0 0 0.5rem;
}
.mon-verdict-icon {
  flex: none;
  display: grid;
  place-items: center;
  width: 2.5rem;
  height: 2.5rem;
  border-radius: 999px;
  background: var(--success-bg);
  color: var(--success);
}
.mon-verdict-icon svg { width: 1.35rem; height: 1.35rem; }
.mon-verdict.is-warn .mon-verdict-icon { background: var(--warning-bg); color: var(--warning); }
.mon-verdict.is-bad .mon-verdict-icon { background: var(--danger-bg); color: var(--danger); }
.mon-verdict.is-wait .mon-verdict-icon { background: var(--surface-2); color: var(--ink-muted); }
.mon-brief {
  margin: 0.15rem 0 0;
  font-size: 1.08rem;
  line-height: 1.5;
  color: var(--ink);
  text-wrap: pretty;
}
/* Satzteile als Links wie im Tages-Satz der Startseite. */
.mon-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);
}
.mon-brief a:hover { text-decoration-color: var(--accent); }
.mon-brief a:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.mon-cards {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1.1rem;
}
.mon-card .day-card-head { min-width: 0; }
.mon-card .day-card-title { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.mon-card .day-card-head :is(.mon-state, .mon-growth-chip) { margin-left: auto; }
.mon-growth-chip { white-space: nowrap; }
.mon-card .day-label .mon-level { margin-right: 0.45rem; }
.mon-logger { font-family: var(--font-mono); font-size: 0.85rem; }
.mon-val {
  font-size: 0.9rem;
  font-weight: 600;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
/* Wert mit Problem: Farbe zusätzlich zum Wort (der Text sagt es selbst). */
.mon-val.is-warn,
.mon-card :is(.day-amount, .day-when).is-warn { color: var(--warning); }
.mon-val.is-bad,
.mon-card :is(.day-amount, .day-when).is-bad { color: var(--danger); }
.mon-foot-off {
  display: flex;
  align-items: center;
  gap: 0.4rem;
}
.mon-foot-off svg { width: 1rem; height: 1rem; flex: none; }
.mon-card .day-empty { padding: 0.42rem 0; border-top: 1px solid var(--line); }

/* Karten in den Bereichen */
.mon-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1.1rem;
  align-items: start;
}
.mon-grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); align-items: stretch; }
/* Schmale Drittel-Karten: Frage über der Antwort statt daneben. */
.mon-grid-3 .mon-dl > div { grid-template-columns: minmax(0, 1fr); gap: 0.05rem; }
/* Zwei Spalten als Stapel: kurze Karten lassen keine Lücke unter sich. */
.mon-cols {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1.1rem;
  align-items: start;
}
.mon-col { display: grid; gap: 1.1rem; }
.mon-box-head {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.35rem 0.8rem;
  margin: 0 0 0.85rem;
}
.mon-box-head h3 { margin: 0; }
.mon-box-head > :last-child:not(:first-child) { margin-left: auto; }
.mon-box-meta { font-size: 0.82rem; color: var(--ink-muted); }
.mon-note {
  margin: 0.9rem 0 0;
  font-size: 0.85rem;
  color: var(--ink-muted);
  text-wrap: pretty;
}
.mon-box-empty { margin: 0; padding: 0 1.4rem 1.2rem; color: var(--ink-muted); }

/* Schlüssel und Wert: links die Frage, rechts die Antwort, Haarlinien dazwischen. */
.mon-dl { margin: 0; }
.mon-dl > div {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 1rem;
  padding: 0.5rem 0;
  border-top: 1px solid var(--line);
}
.mon-dl > div:first-child { border-top: 0; padding-top: 0.1rem; }
.mon-dl dt { color: var(--ink-muted); font-size: 0.92rem; }
.mon-dl dd {
  margin: 0;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
}
.mon-dd-note {
  display: block;
  margin-top: 0.1rem;
  font-size: 0.82rem;
  font-weight: 400;
  color: var(--ink-muted);
}

/* Kennzahlen in einer Reihe, alle gleich groß (keine Heldenzahl). Jede
   Kennzahl ist eine Spalte, die ihren Inhalt nach unten schiebt: bricht ein
   Label um ("Not found or denied"), stehen die Werte der Reihe trotzdem auf
   einer Linie und jedes Label direkt über seinem Wert. */
.mon-figures {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(6.5rem, 1fr));
  gap: 0.9rem 1rem;
  margin: 0 0 1rem;
}
.mon-figures > div {
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: 0.1rem;
}
.mon-figures dt { font-size: 0.8rem; font-weight: 600; color: var(--ink-muted); }
.mon-figures dd {
  margin: 0;
  font-family: var(--font-display);
  font-size: 1.2rem;
  font-weight: 700;
  letter-spacing: -0.015em;
  font-variant-numeric: tabular-nums;
}
.mon-figures small { font-family: var(--font-body); font-size: 0.8rem; font-weight: 600; color: var(--ink-muted); letter-spacing: 0; }

/* Balken und Meter: Anteile als Daten, ruhige Tinten-Tönung (Stack Blue
   bleibt Handlung); Breite kommt als --w aus dem Template. */
.mon-bars { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.6rem; }
.mon-bars li {
  display: grid;
  grid-template-columns: minmax(7rem, 12.5rem) minmax(0, 1fr) 2.6rem;
  align-items: center;
  gap: 0.8rem;
}
.mon-bar-label { font-size: 0.92rem; }
.mon-bar,
.mon-meter {
  display: block;
  height: 0.55rem;
  border-radius: 999px;
  background: var(--surface-2);
  overflow: hidden;
}
.mon-bar i,
.mon-meter i {
  display: block;
  width: var(--w);
  max-width: 100%;
  height: 100%;
  border-radius: inherit;
  background: color-mix(in srgb, var(--ink) 32%, transparent);
}
.mon-meter.is-warn i { background: var(--warning); }
.mon-meter.is-bad i { background: var(--danger); }
.mon-bar-val { text-align: right; font-weight: 600; font-variant-numeric: tabular-nums; }

.mon-gauge { margin: 0 0 0.9rem; }
.mon-gauge-head {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  margin: 0 0 0.4rem;
  font-size: 0.92rem;
  color: var(--ink-muted);
}
.mon-gauge-val { color: var(--ink); font-weight: 600; font-variant-numeric: tabular-nums; }
.mon-gauge-val.is-warn { color: var(--warning); }
.mon-gauge-val.is-bad { color: var(--danger); }

/* Rollen und Zahlen */
.mon-roles {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.1rem;
  margin: 1rem 0 0;
  padding: 0.9rem 0 0;
  border-top: 1px solid var(--line);
}
.mon-roles div { display: flex; align-items: center; gap: 0.45rem; }
.mon-roles dd { margin: 0; font-weight: 600; font-variant-numeric: tabular-nums; }

/* Neueste Konten */
.mon-people { list-style: none; margin: 0; padding: 0; }
.mon-people li {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.55rem 0;
  border-top: 1px solid var(--line);
}
.mon-people .user-avatar { --av: 2.1rem; font-size: 0.8rem; }
.mon-person { display: grid; min-width: 0; flex: 1; }
.mon-person-name { font-weight: 600; }
.mon-person-meta { font-size: 0.85rem; color: var(--ink-muted); }
.mon-person-date { font-size: 0.85rem; color: var(--ink-muted); white-space: nowrap; font-variant-numeric: tabular-nums; }

/* Rangliste */
.mon-rank { list-style: none; margin: 0; padding: 0; counter-reset: mon-rank; }
.mon-rank li {
  counter-increment: mon-rank;
  display: flex;
  align-items: baseline;
  gap: 0.7rem;
  padding: 0.45rem 0;
  border-top: 1px solid var(--line);
}
.mon-rank li:first-child { border-top: 0; padding-top: 0.1rem; }
.mon-rank li::before {
  content: counter(mon-rank);
  width: 1rem;
  color: var(--ink-faint);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.mon-rank-name { flex: 1; min-width: 0; }
.mon-ticker { margin-left: 0.35rem; font-size: 0.85rem; color: var(--ink-muted); }
.mon-rank-val { font-weight: 600; font-variant-numeric: tabular-nums; }

/* Was die App selbst nicht sieht (Uptime-Check, Backups, Zertifikat) */
.mon-checks { list-style: none; margin: 0; padding: 0; }
.mon-checks li { display: grid; gap: 0.25rem; padding: 0.7rem 0; border-top: 1px solid var(--line); }
.mon-checks li:first-child { border-top: 0; padding-top: 0; }
.mon-check-head { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; }
.mon-check-name { font-weight: 600; }
.mon-check-head .mon-state { margin-left: auto; }
.mon-check-text { font-size: 0.88rem; color: var(--ink-muted); text-wrap: pretty; }

/* Tabellen: die App-Tabelle in einer Karte, Kopf ohne Raised-Mist-Band und
   in normaler Schreibung (0.8rem statt der 0.72rem-Großbuchstaben).
   .card.mon-flush mit zwei Klassen: die spätere Handy-Regel ".card { padding }"
   (Abschnitt "Smartphone und Touch") hätte sonst dieselbe Spezifität und
   gewönne, die Zeilen-Tönung reichte nicht mehr bis an den Kartenrand. */
.card.mon-flush { padding: 0; overflow: hidden; }
.mon-flush > .mon-box-head { padding: 1.15rem 1.4rem 0.15rem; }
.mon-table-scroll { overflow-x: auto; }
.mon-table { font-size: 0.9rem; }
.mon-table thead th {
  background: transparent;
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink-muted);
  white-space: nowrap;
}
.mon-table :is(th, td) { padding: 0.62rem 0.85rem; }
.mon-table :is(th, td):first-child { padding-left: 1.4rem; }
.mon-table :is(th, td):last-child { padding-right: 1.4rem; }
.mon-table tbody th { font-weight: 600; }
.mon-table tbody tr:last-child :is(th, td) { border-bottom: none; }
.mon-table .mon-calls { font-variant-numeric: tabular-nums; }
.mon-table .num { text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; }
.mon-table td.muted { font-size: 0.88rem; }
/* Zeile mit Problem: getönt, der Zustand steht als Pille in der Zeile. */
.mon-table tbody tr.is-warn { background: color-mix(in srgb, var(--warning-bg) 55%, transparent); }
.mon-table tbody tr.is-warn:hover { background: var(--warning-bg); }
.mon-table tbody tr.is-bad { background: color-mix(in srgb, var(--danger-bg) 55%, transparent); }
.mon-table tbody tr.is-bad:hover { background: var(--danger-bg); }
.mon-row-note {
  display: block;
  margin-top: 0.15rem;
  font-size: 0.82rem;
  font-weight: 400;
  color: var(--ink-muted);
  max-width: 24rem;
}
.mon-topic { display: inline-flex; align-items: center; gap: 0.5rem; white-space: nowrap; }
.mon-topic .topic-icon-sm { color: var(--ink-muted); }

/* Kontingent in der Tabelle: Text, Balken, Hochrechnung. */
.mon-table td:has(.mon-meter) { min-width: 13rem; }
.mon-meter-text { display: block; }
.mon-table .mon-meter { width: 100%; max-width: 13rem; margin: 0.35rem 0 0.3rem; }
.mon-meter-note {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  font-size: 0.82rem;
  color: var(--ink-muted);
}
.mon-meter-note svg { width: 0.9rem; height: 0.9rem; }
.mon-meter-note.is-warn { color: var(--warning); font-weight: 600; }
.mon-meter-note.is-bad { color: var(--danger); font-weight: 600; }

/* Kleine Tabellen in einer Karte (langsamste Seiten, größte Tabellen). Lange
   Tabellennamen dürfen überall brechen; Pfad-Muster (.mon-path) brechen nur
   nach "/", dort setzt MonitoringFormat.path unsichtbare Umbruchstellen. */
.mon-table-compact { margin-top: 0.9rem; }
.mon-table-compact caption {
  caption-side: top;
  text-align: left;
  padding: 0 0 0.3rem;
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--ink-muted);
}
.mon-table-compact :is(th, td) { padding: 0.42rem 0.6rem; }
.mon-table-compact :is(th, td):first-child { padding-left: 0; }
.mon-table-compact :is(th, td):last-child { padding-right: 0; }
.mon-table-compact th:not(.mon-path) { overflow-wrap: anywhere; }
.mon-table-compact tbody th { font-weight: 500; }
.mon-table-compact tbody tr:hover { background: none; }
.mon-table-compact tbody tr:last-child :is(th, td) { border-bottom: none; }

/* Fehler: Filter, gruppierte Log-Zeilen (mit Stacktrace aufklappbar, ohne
   eine schlichte Zeile ohne Pfeil), Leerzustand. */
.mon-toolbar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.6rem 1.2rem;
}
.mon-toolbar-note {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  margin: 0;
  font-size: 0.85rem;
  color: var(--ink-muted);
}
.mon-chip-count {
  margin-left: 0.4rem;
  color: var(--ink-muted);
  font-variant-numeric: tabular-nums;
}
.mon-log { margin: 0; padding: 0; list-style: none; }
.mon-log > li[hidden] { display: none; }
.mon-log :is(.sub-item > summary, .mon-log-row) {
  display: grid;
  grid-template-columns: 5rem 4.2rem minmax(0, 1fr) auto 0.85rem;
  align-items: start;
  gap: 0.8rem;
}
.mon-log-row { padding: 0.85rem 1.1rem; }
.mon-log-plain:hover { border-color: var(--line); box-shadow: none; }
.mon-log-time {
  font-size: 0.88rem;
  color: var(--ink-muted);
  font-variant-numeric: tabular-nums;
  padding-top: 0.12rem;
}
.mon-level {
  justify-self: start;
  padding: 0.08rem 0.5rem;
  border-radius: 999px;
  font-size: 0.8rem;
  font-weight: 700;
}
.mon-level.is-warn { background: var(--warning-bg); color: var(--warning); }
.mon-level.is-bad { background: var(--danger-bg); color: var(--danger); }
.mon-log-msg { min-width: 0; font-weight: 500; overflow-wrap: anywhere; }
.mon-log-logger {
  display: block;
  margin-top: 0.1rem;
  font-family: var(--font-mono);
  font-size: 0.8rem;
  font-weight: 400;
  color: var(--ink-muted);
}
.mon-log-count {
  text-align: right;
  font-weight: 600;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.mon-log-first {
  display: block;
  font-size: 0.8rem;
  font-weight: 400;
  color: var(--ink-muted);
}
.mon-log .sub-caret { margin-top: 0.3rem; }
.mon-log-trace {
  margin: 0;
  padding: 0.75rem 0.9rem;
  border-radius: 10px;
  background: var(--paper);
  font-family: var(--font-mono);
  font-size: 0.8rem;
  line-height: 1.6;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  color: var(--ink-muted);
}
.mon-empty { margin: 0; color: var(--ink-muted); }
.mon-empty-card { display: grid; justify-items: start; gap: 0.3rem; }
.mon-empty-card h3 { margin: 0.4rem 0 0; }
.mon-empty-card p { margin: 0; max-width: 40rem; }
.mon-empty-icon {
  display: grid;
  place-items: center;
  width: 2.4rem;
  height: 2.4rem;
  border-radius: 999px;
  background: var(--success-bg);
  color: var(--success);
}
.mon-empty-icon svg { width: 1.3rem; height: 1.3rem; }

@media (max-width: 64rem) {
  .mon-cards { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .mon-grid-3 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 48rem) {
  .mon-grid,
  .mon-grid-3,
  .mon-cols { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 40rem) {
  .mon-head .sub-spacer { display: none; }
  .mon-snapshot { flex-basis: 100%; justify-content: space-between; }
  .mon-tabs { margin-bottom: 1.2rem; }
  .mon-cards { grid-template-columns: minmax(0, 1fr); }
  .mon-verdict-icon { width: 2.2rem; height: 2.2rem; }
  .mon-flush > .mon-box-head { padding: 1rem 1.1rem 0.1rem; }
  .mon-bars li { grid-template-columns: minmax(0, 1fr) 2.6rem; row-gap: 0.3rem; }
  .mon-bar { grid-column: 1 / -1; grid-row: 2; }
  .mon-dl > div { grid-template-columns: minmax(0, 1fr); gap: 0.1rem; }

  /* Tabellen werden zu Zeilenblöcken: Name links, Zustand rechts, darunter
     die Werte mit ihrer Spaltenüberschrift (data-label setzt das Template).
     Die Pille bleibt in jeder Zeile rechts neben dem Namen. */
  .mon-flush .mon-table,
  .mon-flush .mon-table tbody { display: block; }
  .mon-flush .mon-table thead { display: none; }
  .mon-flush .mon-table tr {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    gap: 0.3rem 0.8rem;
    padding: 0.75rem 1.1rem;
    border-bottom: 1px solid var(--line);
  }
  .mon-flush .mon-table tr:last-child { border-bottom: 0; }
  .mon-flush .mon-table :is(th, td),
  .mon-flush .mon-table :is(th, td):is(:first-child, :last-child) {
    padding: 0;
    border: 0;
    text-align: left;
  }
  .mon-flush .mon-table tbody th { display: block; grid-column: 1 / -1; }
  .mon-flush .mon-table tbody td {
    grid-column: 1 / -1;
    display: grid;
    grid-template-columns: 6.2rem minmax(0, 1fr);
    gap: 0.8rem;
    font-size: 0.88rem;
  }
  .mon-flush .mon-table td[data-label]::before { content: attr(data-label); color: var(--ink-muted); }
  .mon-flush .mon-table tbody td:has(> .mon-state) {
    display: flex;
    grid-column: 2;
    grid-row: 1;
    align-self: start;
    white-space: nowrap;
  }
  .mon-flush .mon-table td:has(> .mon-state)::before { content: none; }
  .mon-flush .mon-table tr:has(td > .mon-state) th { grid-column: 1; }
  /* Mit Erklärung: die Kopfzelle wird ein Subgrid über zwei Zeilen. Der Name
     (.mon-row-name) bleibt in der linken Spalte neben der Pille, die
     Erklärung läuft darunter über die volle Breite. */
  .mon-flush .mon-table tr:has(td > .mon-state) th:has(.mon-row-note) {
    display: grid;
    grid-column: 1 / -1;
    grid-row: 1 / span 2;
    grid-template-columns: subgrid;
    grid-template-rows: subgrid;
    row-gap: 0;
  }
  .mon-flush .mon-table th > .mon-row-name { grid-column: 1; }
  /* Unten etwas Luft, damit die Erklärung zum Namen gehört und nicht zur ersten Wertzeile. */
  .mon-flush .mon-table th > .mon-row-note { grid-column: 1 / -1; margin: 0 0 0.25rem; max-width: none; }
  .mon-flush .mon-table tbody td:has(.mon-meter) { display: block; min-width: 0; }
  .mon-flush .mon-table td:has(.mon-meter)::before { display: block; margin-bottom: 0.2rem; }
  .mon-flush .mon-table .mon-meter { max-width: none; }
  .mon-box-head > .mon-box-meta:last-child { flex-basis: 100%; margin-left: 0; }
  /* Log-Zeile am Handy: Zeit, Stufe, Anzahl und Pfeil oben, die Meldung darunter. */
  .mon-log :is(.sub-item > summary, .mon-log-row) {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0 0.6rem;
  }
  .mon-log .sub-item > summary::after { content: none; }
  .mon-log :is(.sub-item > summary, .mon-log-row) > .mon-log-time { order: 1; padding-top: 0; }
  .mon-log :is(.sub-item > summary, .mon-log-row) > .mon-level { order: 2; }
  .mon-log :is(.sub-item > summary, .mon-log-row) > .mon-log-count { order: 3; margin-left: auto; }
  .mon-log .sub-item > summary > .sub-caret { order: 4; margin-top: 0; }
  .mon-log :is(.sub-item > summary, .mon-log-row) > .mon-log-msg { order: 5; flex-basis: 100%; margin-top: 0.4rem; }
  .mon-log-first { display: inline; margin-left: 0.3rem; }
}
@media (pointer: coarse) {
  .mon-tabs .segmented-option { min-height: 44px; }
}

/* ---------- Datenschutz-Seite (templates/legal/, DESIGN.md "Legal page") ----------
   Ein Dokument zum Lesen: Kopf auf dem Papier (Titel, Einleitung, Stand,
   Sprachumschalter), links das Inhaltsverzeichnis als ruhige Schiene auf dem
   Papier, rechts EINE Karte mit allen Abschnitten (Haarlinien dazwischen,
   keine Karten in der Karte). Fließtext in Tinte statt gedämpft und mit
   echter Zeilenlänge, weil hier gelesen wird. Die Admin-Vorschau (is-draft)
   legt das rote Admin-Banner oben auf den Stapel und markiert Platzhalter
   (Warn-Bernstein, eckige Klammern im Text) und zu prüfende Sätze (Harbor
   Teal, mit Verweis auf den Prüfpunkt). */
.legal {
  max-width: 58rem;
  margin: 0 auto;
}

.legal-head {
  max-width: 46rem;
  margin: 0 0 1.6rem;
}
.legal-head h1 { margin-bottom: 0.5rem; }
.legal-lead {
  margin: 0 0 1rem;
  font-size: 1.07rem;
  color: var(--ink-muted);
  text-wrap: pretty;
}
.legal-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.6rem 1.2rem;
}
.legal-updated {
  margin: 0;
  font-size: 0.85rem;
  color: var(--ink-muted);
  font-variant-numeric: tabular-nums;
}

/* Sprachumschalter: dieselbe Pillen-Spur wie die Segment-Umschalter, aber
   ohne gleitende Pille (Links laden die Seite neu, ui.js lässt .legal-lang
   ohne .segmented-indicator in Ruhe). Die aktuelle Sprache trägt die
   Karten-Fläche und aria-current. */
.legal-lang {
  display: inline-flex;
  gap: 2px;
  padding: 4px;
  background: var(--surface-2);
  border-radius: 999px;
}
.legal-lang a {
  padding: 0.38rem 0.9rem;
  border-radius: 999px;
  font-size: 0.84rem;
  font-weight: 600;
  color: var(--ink-muted);
  text-decoration: none;
  transition: color var(--dur-state) ease, background var(--dur-state) ease;
}
.legal-lang a:hover:not([aria-current]) {
  color: var(--ink);
  background: color-mix(in srgb, var(--ink) 7%, transparent);
}
.legal-lang a[aria-current] {
  color: var(--ink);
  background: var(--surface);
  box-shadow: var(--shadow-sm);
}
.legal-lang a:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.legal-layout {
  display: grid;
  gap: 1.4rem;
}

/* Inhaltsverzeichnis: auf schmalen Fenstern zwei Spalten über der Karte,
   ab 64rem eine Schiene links, die beim Lesen stehen bleibt. */
.legal-toc h2 {
  margin: 0 0 0.5rem;
  font-size: 0.85rem;
  font-weight: 600;
  letter-spacing: 0;
  color: var(--ink-muted);
}
.legal-toc ol {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(12rem, 1fr));
  gap: 0.1rem 1rem;
  counter-reset: legal-toc;
}
.legal-toc a {
  display: block;
  padding: 0.3rem 0;
  font-size: 0.9rem;
  color: var(--ink-muted);
  text-decoration: none;
  text-wrap: balance;
  transition: color var(--dur-state) ease;
}
.legal-toc a:hover { color: var(--accent); }
.legal-toc a:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 4px;
}

@media (min-width: 64rem) {
  .legal-layout {
    grid-template-columns: 13rem minmax(0, 1fr);
    gap: 2.4rem;
    align-items: start;
  }
  .legal-head { margin-left: 15.4rem; }
  .legal-toc {
    position: sticky;
    top: 5.2rem;
    padding-top: 2rem;
  }
  /* Eine feine Linie links, die Einträge hängen daran wie Register; beim
     Zeigen färbt sich ihr Stück der Linie Stack Blue. */
  .legal-toc ol {
    grid-template-columns: 1fr;
    gap: 0;
    border-left: 1px solid var(--line);
  }
  .legal-toc a {
    margin-left: -1px;
    padding: 0.32rem 0 0.32rem 0.85rem;
    border-left: 1px solid transparent;
    transition: color var(--dur-state) ease, border-color var(--dur-state) ease;
  }
  .legal-toc a:hover { border-left-color: var(--accent); }
  /* Prüfpunkte der Vorschau in zwei Spalten, damit der Text früher beginnt. */
  .legal-draft-checks { columns: 2; column-gap: 2.6rem; }
  .legal-draft-checks li { break-inside: avoid; }
}

/* Die Karte trägt den Text. max() hält 15px auf dem dichten Desktop und
   16px auf dem Phone; 60ch ergeben rund 75 Zeichen je Zeile. */
.card.legal-body {
  padding: 0.4rem 2.4rem 1.6rem;
  margin-bottom: 0;
  font-size: max(1rem, 15px);
  line-height: 1.65;
}
.legal-body p,
.legal-body address {
  max-width: 60ch;
  color: var(--ink);
  text-wrap: pretty;
}
.legal-body p { margin: 0 0 0.85rem; }
.legal-section {
  padding: 1.7rem 0 0.9rem;
  scroll-margin-top: 5rem;
}
.legal-section + .legal-section { border-top: 1px solid var(--line); }
.legal-section h2 {
  position: relative;
  margin: 0 0 0.75rem;
  padding-bottom: 0.2rem;
}
/* Aus dem Inhaltsverzeichnis angesprungen: unter der Überschrift wächst ein
   kurzer Stack-Blue-Strich von links, wie unter dem Link der Leiste. */
.legal-section h2::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: 0;
  width: 2.6rem;
  height: 2px;
  border-radius: 2px;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--dur-focal) var(--ease-arrive);
}
.legal-section:target h2::after { transform: scaleX(1); }
.legal-section h3 {
  margin: 1.3rem 0 0.45rem;
  font-size: 1rem;
}
.legal-section.is-summary p { font-size: 1.07em; }

.legal-address {
  display: grid;
  gap: 0.1rem;
  margin: 0 0 0.9rem;
  padding: 0.85rem 1.1rem;
  font-style: normal;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: 12px;
}
.legal-address strong { font-weight: 600; }
.legal-body a { text-underline-offset: 0.18em; }

/* ---------- Admin-Vorschau der Datenschutz-Seite ---------- */
.legal-draft {
  margin-bottom: 1.8rem;
}
.legal-draft-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.4rem 1rem;
  margin-bottom: 0.4rem;
}
.legal-draft-head h2 { margin: 0; }
.status.is-warn { color: var(--warning); }
.card .legal-draft-lead { color: var(--ink); max-width: 60rem; }
.card .legal-draft-label {
  margin: 0.8rem 0 0.4rem;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--ink-muted);
}
.legal-draft-missing {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
  margin: 0 0 0.9rem;
  padding: 0;
  list-style: none;
}
.legal-draft-missing code {
  display: inline-block;
  padding: 0.12rem 0.55rem;
  border-radius: 999px;
  background: var(--warning-bg);
  color: var(--warning);
  font-size: 0.8rem;
}
.card .legal-draft-legend {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 0.9rem;
  margin: 0 0 1rem;
  font-size: 0.85rem;
}
.legal-draft h3 { margin: 1.2rem 0 0.5rem; }
.legal-draft-checks {
  margin: 0;
  padding-left: 1.6rem;
  font-size: 0.9rem;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.legal-draft-checks li {
  padding: 0.3rem 0.4rem;
  border-radius: 8px;
  scroll-margin-top: 5rem;
  max-width: 72ch;
  transition: background var(--dur-state) ease;
}
.legal-draft-checks li::marker { color: var(--ink-muted); font-weight: 600; }
.legal-draft-checks li:target { background: color-mix(in srgb, var(--accent-2) 14%, transparent); }

/* Fehlende Angabe: Bernstein wie jede Warnung, die eckigen Klammern stehen
   im Text selbst (nie Farbe allein). */
mark.legal-placeholder {
  padding: 0.08em 0.3em;
  border-radius: 4px;
  background: var(--warning-bg);
  color: var(--warning);
  font-weight: 600;
  box-decoration-break: clone;
  -webkit-box-decoration-break: clone;
}
/* Zu prüfender Satz: Anmerkung in Harbor Teal, nicht klickbar; nur der
   Verweis dahinter ist ein Link und bleibt darum Stack Blue. */
mark.legal-check {
  padding: 0.08em 0.15em;
  background: color-mix(in srgb, var(--accent-2) 12%, transparent);
  color: inherit;
  border-bottom: 2px solid var(--accent-2);
  box-decoration-break: clone;
  -webkit-box-decoration-break: clone;
}
.legal-check-ref {
  display: inline-block;
  margin-left: 0.2em;
  padding: 0 0.5em;
  border: 1px solid var(--accent-strong);
  border-radius: 999px;
  font-size: 0.8rem;
  font-weight: 600;
  line-height: 1.5;
  text-decoration: none;
  vertical-align: 0.08em;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.legal-check-ref:hover { background: color-mix(in srgb, var(--accent) 10%, transparent); }

@media (max-width: 40rem) {
  .card.legal-body { padding: 0.2rem 1.15rem 1.2rem; }
  .legal-section { padding-top: 1.35rem; }
  .legal-toc ol { grid-template-columns: repeat(auto-fill, minmax(9.5rem, 1fr)); }
}
@media (pointer: coarse) {
  .legal-lang a { padding-block: 0.62rem; }
  .legal-toc a { padding-block: 0.55rem; }
}
@media (prefers-reduced-motion: reduce) {
  .legal-section h2::after { transition: none; }
}
/* Gedruckt nur das Dokument: ohne Leiste, Verzeichnis und Umschalter. */
@media print {
  body:has(.legal) .navbar,
  .legal-toc,
  .legal-lang { display: none; }
  .legal-head { margin-left: 0; }
  .legal-layout { display: block; }
  .card.legal-body { padding: 0; border: none; box-shadow: none; }
}

/* Link auf die Datenschutzerklärung unter Login und Registrierung (erst nach
   der Freigabe): eine gedämpfte Zeile, die nicht mit dem Formular konkurriert. */
.auth-legal {
  margin: 0.7rem 0 0;
  font-size: 0.82rem;
}
.auth-legal a {
  color: var(--ink-muted);
  text-underline-offset: 0.2em;
}
.auth-legal a:hover { color: var(--accent); }
/* Settings > General: Abschnitt "Datenschutz" unter der Sprachwahl. */
.settings-privacy { margin-top: 1.8rem; }
.settings-privacy h3 { margin-bottom: 0.3rem; }
.settings-privacy .btn { margin-top: 0.1rem; }

/* ---------- 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 106rem 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: var(--page-max);
  margin: 0 auto;
  padding: 1.3rem var(--page-gutter) 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; }
/* Konto-Gruppe (fragments/account-icons): gedämpft wie im Profil-Menü; auf
   der aktuellen Seite folgt das Icon dem Akzent der Zeile. */
.drawer-group-account .topic-icon-sm { color: var(--ink-muted); }
.drawer-group-account .drawer-link[aria-current="page"] .topic-icon-sm { color: inherit; }
.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;
  }
  .seg-toggle button { min-height: 44px; min-width: 44px; }
  .sd-chip, .sd-close, .sd-undo { min-height: 44px; }
  .ed-grip, .ed-mini { min-width: 44px; min-height: 44px; }
  .sd-close { 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; }
  /* "Breit und kompakt" gilt erst ab 40rem: hier die bisherigen Abstände
     und Größen, damit Phones aussehen wie vorher. */
  :root { --card-stack: 1rem; }
  h1 { font-size: 1.6rem; }
  .mk-top { gap: 1.1rem; margin-bottom: 1.6rem; }
  .mk-row, .as, .as-grid { gap: 1.1rem; }
  .as-hero { gap: 1.1rem; }
  .nw-groups { gap: 1rem; }
  .calc { gap: 1.1rem; margin-top: 1.2rem; }
  .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,
  dialog.dialog-card.dialog-edit {
    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-toggles { width: 100%; justify-content: flex-start; }
  .wl-toggles .seg-toggle { flex: 1 1 auto; }
  .wl-toggles .seg-toggle button { flex: 1; padding: 0 0.6rem; }
  .mk-brief { padding: 1.2rem 1.25rem; }
  .mk-brief-lead { font-size: 1.2rem; }
  /* Stand und Börsen untereinander statt mit Punkt am Zeilenanfang. */
  .mk-brief-facts { display: grid; gap: 0.15rem; }
  .mk-brief-facts > span + span::before { content: none; }
  .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(240px, 40vh, 360px); }
  .as-hero { padding: 1.2rem 1.1rem 1rem; }
  .as-grid > .card, .as-digest { padding: 1.1rem 1.15rem; }
  .as-news { padding: 0.4rem 1.15rem 0.9rem; }
  .asset-title { font-size: 1.5rem; }
  .asset-price { font-size: 2.4rem; }
  .market-logo.mk-badge.as-logo { width: 3rem; height: 3rem; }
  .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; }
  .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. */
  .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); die Richtung
   ("back") 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; }
/* 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);
}
/* Bei Seitenwechseln gleiten Unterstrich und Titel im Tempo der Seite. */
html:active-view-transition-type(page)::view-transition-group(*) {
  animation-duration: calc(var(--dur-page) * var(--motion-travel));
  animation-timing-function: var(--ease-page);
}
html:active-view-transition-type(page)::view-transition-old(*),
html:active-view-transition-type(page)::view-transition-new(*) {
  animation-duration: var(--dur-page);
}
/* Zwei ganze Seiten übereinander wirken unruhig: die alte blendet zügig
   aus, die neue setzt einen Hauch später ein und läuft weich aus (--dur-page). */
html:active-view-transition-type(page)::view-transition-old(root) {
  animation: stack-leave var(--dur-page-leave) var(--ease-page-leave) both;
}
html:active-view-transition-type(page)::view-transition-new(root) {
  animation: stack-arrive var(--dur-page) var(--ease-page) calc(var(--dur-page-leave) * 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-page) var(--ease-page) 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; }

/* ---------- Monitoring: Reiterwechsel und frische Momentaufnahme ----------
   Die Monitoring-Reiter wechseln in der Seite (monitoring.js), mit derselben
   Geste wie die Settings: der Inhalt kommt aus der Richtung der Pille
   (--tab-dir setzt das Skript). Bis die Pille beim Laden steht, springt sie
   ohne Animation. Nach Refresh leuchtet die Uhrzeit kurz in Stack Blue auf. */
.mon-pane.is-arriving { animation: tab-arrive var(--dur-layout) var(--ease-arrive) backwards; }
.mon-tabs:not(.is-ready) .segmented-indicator { transition: none; }
.mon-snapshot-text.is-fresh time { animation: mon-fresh var(--dur-focal) var(--ease-out); }
@keyframes mon-fresh {
  from { color: var(--accent); }
}

/* ---------- 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 Topic-Dropdown, 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);
}
/* Monitoring: die Pulslinie zeichnet sich neu (Topic-Icon, auch im
   Admin-Menü und auf der /admin-Kachel). */
:is(a.card-link, a.inbox-drop-item, a.drawer-link, a.nav-link, a.rail-row):is(:hover, :focus-visible) .i-pulse {
  animation: icon-draw var(--dur-layout) var(--ease-out) both;
}
/* Waypoints: die Strecke zwischen Start- und Zielkreis 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-route {
  animation: icon-draw var(--dur-layout) var(--ease-out) both;
}
/* Karten im Monitoring-Überblick (templates/monitoring/icons.html): Pfeil der
   Quellen-Wolke, Haken am Schild, Zeilen im Log. */
a.card-link:is(:hover, :focus-visible) :is(.i-drop, .i-check, .i-log) {
  animation: icon-draw var(--dur-layout) var(--ease-out) both;
}
a.card-link:is(:hover, :focus-visible) .i-drop-head {
  animation: icon-draw var(--dur-state) var(--ease-out) calc(var(--dur-layout) * 0.6) both;
}
a.card-link:is(:hover, :focus-visible) .i-log-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) :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 der Segment-Umschalter (Zeitraum, Ansicht) gleitet als Pille
   zum neuen Knopf: Lage per transform, Breite folgt mit (ui.js setzt
   --pill-x und --pill-w). Auf den Karten tauscht die Pille kurz versetzt
   durch das Raster, nur beim Umschalten (is-switching), nicht beim
   Live-Tausch. */
.seg-toggle .iv-pill {
  position: absolute;
  top: 3px;
  bottom: 3px;
  left: 0;
  width: calc(var(--pill-w, 0) * 1px);
  transform: translateX(var(--pill-x, 0));
  border-radius: 999px;
  background: var(--surface);
  box-shadow: var(--shadow-sm);
  z-index: -1;
  pointer-events: none;
}
.seg-toggle.is-ready .iv-pill {
  transition:
    transform calc(var(--dur-state) * var(--motion-travel)) var(--ease-out),
    width calc(var(--dur-state) * var(--motion-travel)) var(--ease-out);
}
.seg-toggle.has-pill button.active { background: none; box-shadow: none; }
.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: Übersicht landet, Live-Puls, Glanz ----------
   Nur ohne Wunsch nach reduzierter Bewegung (Nutzerwunsch 07.11.2026):
   - Beim Laden setzt ui.js kurz .is-entering an .mk. Nach dem Seitenwechsel
     wächst die Plus/Minus-Leiste von links, Mover-Zeilen und Karten legen
     sich nacheinander auf den Stapel (--mk-i), die 30-Tage-Linien ziehen
     sich von links auf, ihre Fläche blendet nach. Live-Tausche ersetzen nur
     das Innere der Live-Regionen, die Klasse ist dann längst weg.
   - Der Punkt der Live-Pille pulst, solange ein Markt handelt.
   - Heatmap: beim Öffnen landen die Kacheln von links unten nach rechts
     oben (--hm-land, ui.js landHeatmap), und ein ruhiger Glanz zieht alle
     7 Sekunden schräg über jede Kachel, versetzt je Kachel
     (--hm-shine-delay, phasentreu über Live-Tausche hinweg). */
@media (prefers-reduced-motion: no-preference) {
  .mk.is-entering .mk-split > span {
    transform-origin: left center;
    animation: mk-grow 900ms var(--ease-arrive) calc(var(--dur-page) * 0.6) both;
  }
  .mk.is-entering .mk-mover,
  .mk.is-entering .market-grid .market-card {
    animation: stack-arrive var(--dur-layout) var(--ease-arrive) backwards;
    animation-delay: calc(var(--dur-page) * 0.5 + var(--mk-i, 0) * 45ms);
  }
  .mk.is-entering .market-grid .spark-svg {
    animation: mk-reveal 1100ms var(--ease-out) backwards;
    animation-delay: calc(var(--dur-page) * 0.5 + 220ms + var(--mk-i, 0) * 45ms);
  }
  .mk.is-entering .market-grid .spark-svg path {
    animation: fade-in 700ms var(--ease-out) backwards;
    animation-delay: calc(var(--dur-page) * 0.5 + 900ms + var(--mk-i, 0) * 45ms);
  }
  .mk.is-entering .market-grid .spark-dot {
    animation: fade-in var(--dur-state) var(--ease-out) backwards;
    animation-delay: calc(var(--dur-page) * 0.5 + 1200ms + var(--mk-i, 0) * 45ms);
  }
  .live-pill:not(.is-closed) .live-dot { animation: live-pulse 1.6s var(--ease-out) infinite; }
  .hm-map.is-landing .hm-tile {
    animation: stack-arrive var(--dur-layout) var(--ease-arrive) backwards;
    animation-delay: calc(var(--hm-land, 0) * 45ms);
  }
  .hm-glint::after {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(105deg, transparent 35%, var(--heat-shine) 50%, transparent 65%);
    transform: translateX(-120%);
    animation: hm-shine 7s ease-in-out infinite;
    animation-delay: var(--hm-shine-delay, 0ms);
  }
}

/* ---------- Markets: Detailseite landet ----------
   Nur ohne Wunsch nach reduzierter Bewegung: beim Laden (.is-entering an
   .as, ui.js) wachsen nach dem Seitenwechsel die Earnings-Balken von unten,
   eins nach dem anderen (--as-i), danach blenden die Abweichungen ein; die
   Marken der Spannen blenden ein. Der Live-Punkt pulst wie auf der Übersicht. */
@media (prefers-reduced-motion: no-preference) {
  .as.is-entering .as-eps-est,
  .as.is-entering .as-eps-act {
    transform-origin: bottom center;
    animation: ad-grow 700ms var(--ease-arrive) backwards;
    animation-delay: calc(var(--dur-page) * 0.6 + var(--as-i, 0) * 60ms);
  }
  .as.is-entering .as-eps-act { animation-delay: calc(var(--dur-page) * 0.6 + 80ms + var(--as-i, 0) * 60ms); }
  .as.is-entering .as-eps-pct {
    animation: fade-in var(--dur-layout) var(--ease-out) backwards;
    animation-delay: calc(var(--dur-page) * 0.6 + 520ms + var(--as-i, 0) * 60ms);
  }
  .as.is-entering .as-range .day-range-run {
    animation: fade-in var(--dur-layout) var(--ease-out) backwards;
    animation-delay: calc(var(--dur-page) * 0.6 + 200ms);
  }
}
@keyframes ad-grow { from { transform: scaleY(0); } }

/* ---------- Markets: News landen ----------
   Nur ohne Wunsch nach reduzierter Bewegung: nach dem Seitenwechsel legen
   sich die Gruppen nacheinander auf den Stapel (--nw-i aus dem Template),
   danach ploppen die Stimmungspunkte auf. Ein Filter in der Leiste lässt die
   gezeigten Gruppen erneut landen (is-revealed, ui.js). */
@media (prefers-reduced-motion: no-preference) {
  .nw .nw-group {
    animation: stack-arrive var(--dur-layout) var(--ease-arrive) backwards;
    animation-delay: calc(var(--dur-page) * 0.5 + var(--nw-i, 0) * 70ms);
  }
  .nw .nw-dot {
    animation: nw-pop 450ms var(--ease-arrive) backwards;
    animation-delay: calc(var(--dur-page) * 0.5 + 320ms + var(--nw-i, 0) * 70ms);
  }
  .nw .nw-group.is-revealed { animation: nw-reveal var(--dur-layout) var(--ease-arrive) backwards; }
}
/* Mini-Heatmap in "Dein Tag": die Kacheln landen nach dem Seitenwechsel
   nacheinander, danach zieht der ruhige Glanz der Heatmap über sie. */
@media (prefers-reduced-motion: no-preference) {
  .day-hm-tile {
    animation: stack-arrive var(--dur-layout) var(--ease-arrive) backwards;
    animation-delay: calc(var(--dur-page) * 0.5 + var(--i, 0) * 40ms);
  }
  .day-hm-tile::after {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(105deg, transparent 35%, var(--heat-shine) 50%, transparent 65%);
    transform: translateX(-120%);
    animation: hm-shine 7s ease-in-out infinite;
    animation-delay: calc(600ms + var(--i, 0) * 150ms);
    pointer-events: none;
  }
}

/* Bearbeiten-Popup: die gezogene Kopie wippt leicht, der Platzhalter atmet. */
@media (prefers-reduced-motion: no-preference) {
  .ed-ghost { animation: ed-bob 1.8s ease-in-out infinite; }
  .ed-placeholder { animation: ed-breathe 1.4s ease-in-out infinite; }
}
@keyframes ed-bob {
  0%, 100% { transform: rotate(-1.5deg) translateY(0); }
  50% { transform: rotate(-1deg) translateY(-4px); }
}
@keyframes ed-breathe {
  50% { opacity: 0.55; }
}

/* Earnings-Kalender: die Tageskarten landen nacheinander auf dem Stapel,
   die Termine darin kurz danach; über eigene Termine läuft der ruhige
   Lichtstreif der Heatmap. */
@media (prefers-reduced-motion: no-preference) {
  .ec-day { animation: stack-arrive var(--dur-layout) var(--ease-arrive) backwards; animation-delay: calc(var(--ec-i, 0) * 60ms); }
  .ec-events > li {
    animation: stack-arrive var(--dur-state) var(--ease-arrive) backwards;
    animation-delay: calc(240ms + var(--ec-i, 0) * 60ms + var(--ec-j, 0) * 40ms);
  }
  .ec-event.is-mine::after {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(105deg, transparent 35%, var(--heat-shine) 50%, transparent 65%);
    transform: translateX(-120%);
    animation: hm-shine 7s ease-in-out 1.2s infinite;
    pointer-events: none;
  }
}

/* Such-Popup: neue Treffer legen sich kurz versetzt in die Liste, die
   Bestätigung landet, das Etikett der gerade hinzugefügten Zeile ploppt. */
@media (prefers-reduced-motion: no-preference) {
  .sd-row { animation: stack-arrive var(--dur-state) var(--ease-arrive) backwards; animation-delay: calc(var(--sd-i, 0) * 30ms); }
  .sd-status { animation: stack-arrive var(--dur-layout) var(--ease-arrive) backwards; }
  .sd-added.is-fresh { animation: sd-check 450ms var(--ease-arrive) backwards; }
}
@keyframes sd-check {
  0% { transform: scale(0.6); }
  60% { transform: scale(1.12); }
}
/* Wie stack-arrive, eigener Name, damit der Neustart nach dem Filtern greift. */
@keyframes nw-reveal {
  from {
    opacity: 0;
    transform: translate(calc(var(--step) * -1), var(--step)) scale(var(--step-scale));
  }
}
@keyframes nw-pop {
  0% { transform: scale(0); }
  70% { transform: scale(1.4); }
}
@keyframes mk-grow { from { transform: scaleX(0); } }
@keyframes mk-reveal {
  from { clip-path: inset(0 100% 0 0); }
  to { clip-path: inset(0 0 0 0); }
}
@keyframes live-pulse {
  from { box-shadow: 0 0 0 0 color-mix(in srgb, currentColor 60%, transparent); }
  to { box-shadow: 0 0 0 0.5rem transparent; }
}
@keyframes hm-shine {
  0%, 60% { transform: translateX(-120%); }
  100% { transform: translateX(120%); }
}

/* ---------- 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;
}

/* ---------- 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); }
}
