/* finance-stack — styles split (Phase 46 Wave 4)
   Datei: components.css  •  Lade-Position: 3/6 — Cards, Buttons, Forms, Tables, Modals, Filter-Sheet, Inline-Forms
   Lade-Reihenfolge: base → layout-shell → components → pages → charts-hero → polish
*/

/* === Cards === */
.card {
  background: var(--surface);
  border-radius: 12px;
  padding: 1.2rem 1.4rem;
  margin-bottom: 1rem;
  box-shadow: var(--shadow);
  border: 1px solid var(--border-3);
}
.card h2 { margin: 0 0 .6rem; font-size: 1.05rem; font-weight: 600; }
.card h3 { margin: 0 0 .4rem; font-size: .92rem; font-weight: 600; }

/* === Layout primitives === */
.row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 1rem;
  flex-wrap: wrap;
  margin-bottom: .6rem;
}
.row > * { margin: 0; }
.inline { display: inline-flex; gap: .5rem; align-items: center; }
.muted { color: var(--text-dim); }
.small { font-size: .82rem; }
.pos { color: var(--pos); }
.neg { color: var(--danger); }
.num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.num.neg { color: var(--danger); }
.zweck { max-width: 280px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* === Forms === */
form { display: flex; flex-direction: column; gap: .8rem; }
form.inline { flex-direction: row; gap: .4rem; }
.form-inline { flex-direction: row; flex-wrap: wrap; gap: .6rem; align-items: flex-end; }
.form-inline label { flex-direction: column; }
label { display: flex; flex-direction: column; gap: .25rem; font-size: .85rem; }
input, select, textarea {
  padding: .5rem .7rem;
  border: 1px solid var(--border-input);
  border-radius: 7px;
  font-size: .95rem;
  background: var(--surface);
  color: var(--text);
  font-family: inherit;
}
input:focus, select:focus, textarea:focus {
  outline: 2px solid var(--accent);
  outline-offset: -1px;
  border-color: var(--accent);
}

button, .btn-primary {
  padding: .55rem 1rem;
  border: none;
  background: var(--accent);
  color: #fff;
  font-size: .92rem;
  border-radius: 7px;
  cursor: pointer;
  font-family: inherit;
  text-decoration: none;
  display: inline-block;
}
button:hover, .btn-primary:hover { background: var(--accent-h); text-decoration: none; }
button.btn-secondary, a.btn-secondary, .btn-secondary {
  padding: .45rem .85rem;
  background: var(--surface-2);
  color: var(--accent);
  border: 1px solid var(--border-2);
  font-size: .88rem;
  font-weight: 500;
  border-radius: 7px;
  cursor: pointer;
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  gap: .35rem;
  font-family: inherit;
  line-height: 1.2;
  white-space: nowrap;
}
button.btn-secondary:hover, a.btn-secondary:hover, .btn-secondary:hover {
  background: var(--surface-3);
  border-color: var(--accent);
  text-decoration: none;
}
button.link {
  background: none;
  color: var(--accent);
  padding: 0;
  font-size: inherit;
}
button.link:hover { background: none; text-decoration: underline; }
button.link.danger, .danger { color: var(--danger) !important; }
.btn-link {
  color: var(--text-muted);
  padding: .55rem 0;
  text-decoration: none;
  font-size: .9rem;
}

/* === Tabellen === */
table { width: 100%; border-collapse: collapse; font-size: .9rem; }
th, td {
  padding: .5rem .55rem;
  text-align: left;
  border-bottom: 1px solid var(--border-2);
  vertical-align: top;
}
th {
  font-weight: 600;
  color: var(--text-muted);
  font-size: .76rem;
  text-transform: uppercase;
  letter-spacing: .03em;
  border-bottom: 1px solid var(--border);
}
td code { font-family: ui-monospace, Menlo, monospace; font-size: .82rem; color: var(--text-code); }
tr.clickable { cursor: pointer; transition: background .1s; }
tr.clickable:hover { background: var(--surface-4); }
tr.total-row td { border-top: 2px solid var(--border); border-bottom: none; padding-top: .7rem; font-weight: 600; }
tr.archived td { background: var(--surface-3); color: var(--text-muted); }
tr.history-current td { background: var(--accent-bg); font-weight: 500; }

/* === Status === */
.flash {
  background: var(--flash-bg);
  border: 1px solid var(--flash-border);
  color: var(--flash-text);
  padding: .55rem .8rem;
  border-radius: 7px;
  margin-bottom: .9rem;
  font-size: .9rem;
}
.error { background: var(--error-bg); color: var(--error-text); padding: .55rem .8rem; border-radius: 7px; }
/* Error-Variante des Flash (für Server-Side validation/IntegrityError-Toasts).
   Überschreibt `.flash`-Background mit Error-Farben — höhere Spezifität durch
   Doppel-Klassen. */
.flash.flash--error {
  background: var(--error-bg);
  color: var(--error-text);
  border-color: var(--error-text);
}

/* Floating-Toast-Variante: für kurze Status-Meldungen (z.B. Sync-Erfolg).
   Markiert via `.flash--toast` und JS-gesteuerte Lifecycle-Klasse `.is-show`.
   Mobile: top-center sticky band. Desktop: top-right floating card. */
.flash--toast {
  position: fixed;
  top: 1rem;
  left: 50%;
  transform: translate(-50%, -120%);
  z-index: 1200;
  margin: 0;
  padding: .7rem 1.1rem;
  font-size: .92rem;
  font-weight: 500;
  border-radius: 10px;
  box-shadow: 0 8px 24px -8px rgba(0, 0, 0, .25),
              0 2px 6px -2px rgba(0, 0, 0, .15);
  opacity: 0;
  transition: transform .25s cubic-bezier(.2, .8, .2, 1), opacity .2s ease;
  cursor: pointer;
  max-width: calc(100vw - 2rem);
}
.flash--toast.is-show {
  transform: translate(-50%, 0);
  opacity: 1;
}
.flash--toast.is-hide {
  transform: translate(-50%, -120%);
  opacity: 0;
}
@media (min-width: 720px) {
  .flash--toast {
    left: auto;
    right: 1rem;
    transform: translate(120%, 0);
  }
  .flash--toast.is-show { transform: translate(0, 0); }
  .flash--toast.is-hide { transform: translate(120%, 0); }
}

/* === Pager === */
.pager {
  display: flex; justify-content: space-between; align-items: center;
  margin-top: 1rem; padding-top: .8rem; border-top: 1px solid var(--border-2);
}
.pager a { color: var(--accent); text-decoration: none; }

/* === Filters === */
.filters {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: .6rem;
  align-items: end;
}
.filters .grow { grid-column: span 2; }
.filters label { font-size: .76rem; }
.filters input, .filters select { height: 2.35rem; box-sizing: border-box; }

/* Filter panel (collapsible) */
/* === Filter-FAB + Bottom-Sheet auf /transactions === */
.tx-filter-fab {
  position: fixed;
  right: 1.25rem;
  bottom: 1.25rem;
  width: 56px;
  height: 56px;
  border-radius: var(--r-pill);
  border: none;
  background: var(--accent);
  color: #fff;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-shadow: var(--shadow-3);
  cursor: pointer;
  z-index: var(--z-overlay);
  transition: transform var(--duration-fast) var(--ease-out),
              box-shadow var(--duration-fast) var(--ease-out);
}
.tx-filter-fab:hover { transform: translateY(-2px); box-shadow: var(--shadow-4); }
.tx-filter-fab:active { transform: translateY(0); }
.tx-filter-fab:focus-visible { outline: 2px solid var(--accent-2); outline-offset: 3px; }
.tx-filter-fab .icon { width: 24px; height: 24px; }
.tx-filter-fab__badge {
  position: absolute;
  top: -4px;
  right: -4px;
  min-width: 20px;
  height: 20px;
  padding: 0 .35rem;
  border-radius: 999px;
  background: var(--danger);
  color: #fff;
  font-size: .72rem;
  font-weight: 700;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  letter-spacing: .02em;
  border: 2px solid var(--surface);
  line-height: 1;
}

/* „Alle gelesen"-FAB — sitzt über dem Filter-FAB, visuell sekundär (kleiner,
   Surface statt Akzent-Füllung). #1 Gelesen-Status. */
.tx-readall-fab {
  position: fixed;
  right: calc(1.25rem + 4px);
  bottom: calc(1.25rem + 56px + .75rem);
  width: 48px;
  height: 48px;
  border-radius: var(--r-pill);
  border: 1px solid var(--border-2);
  background: var(--surface);
  color: var(--accent);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-shadow: var(--shadow-2);
  cursor: pointer;
  z-index: var(--z-overlay);
  transition: transform var(--duration-fast) var(--ease-out),
              box-shadow var(--duration-fast) var(--ease-out);
}
.tx-readall-fab:hover { transform: translateY(-2px); box-shadow: var(--shadow-3); }
.tx-readall-fab:active { transform: translateY(0); }
.tx-readall-fab:focus-visible { outline: 2px solid var(--accent-2); outline-offset: 3px; }
.tx-readall-fab .icon { width: 22px; height: 22px; }

/* Discover-Chip („X ungelesene Buchungen — Anzeigen") oben in der Liste.
   Erscheint, solange ungelesene existieren und der Filter NICHT aktiv ist.
   Klick → ?unread=1. Subtil, aber klar als interaktive Einladung lesbar. */
.tx-filter-discover-chip {
  display: flex;
  align-items: center;
  gap: .65rem;
  padding: .65rem .9rem;
  margin: 0 0 .85rem;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 10px;
  color: var(--text);
  text-decoration: none;
  font-size: .9rem;
  transition: background var(--duration-fast) var(--ease-out),
              border-color var(--duration-fast) var(--ease-out);
}
.tx-filter-discover-chip:hover {
  background: var(--accent-bg);
  border-color: var(--accent);
}
.tx-filter-discover-chip:focus-visible {
  outline: 2px solid var(--accent-2);
  outline-offset: 2px;
}
.tx-filter-discover-chip__icon {
  display: inline-flex;
  align-items: center;
  color: var(--accent);
}
.tx-filter-discover-chip__icon .icon { width: 18px; height: 18px; }
.tx-filter-discover-chip__label {
  flex: 1;
  font-weight: var(--weight-semi);
}
.tx-filter-discover-chip__cta {
  color: var(--accent);
  font-weight: var(--weight-semi);
  font-size: .85rem;
  white-space: nowrap;
}

/* Active-Chip („Nur ungelesene Buchungen [×]") — Status, dass der Filter
   gerade läuft. X-Button entfernt den Filter ohne den Filter-Sheet zu öffnen. */
.tx-filter-active-chip {
  display: flex;
  align-items: center;
  gap: .65rem;
  padding: .55rem .65rem .55rem .9rem;
  margin: 0 0 .85rem;
  background: var(--accent-bg);
  border: 1px solid var(--accent);
  border-radius: 10px;
  color: var(--text);
  font-size: .9rem;
}
.tx-filter-active-chip__icon {
  display: inline-flex;
  align-items: center;
  color: var(--accent);
}
.tx-filter-active-chip__icon .icon { width: 18px; height: 18px; }
.tx-filter-active-chip__label {
  flex: 1;
  font-weight: var(--weight-semi);
}
.tx-filter-active-chip__clear {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  color: var(--text-muted);
  text-decoration: none;
  transition: background var(--duration-fast) var(--ease-out),
              color var(--duration-fast) var(--ease-out);
}
.tx-filter-active-chip__clear:hover {
  background: var(--surface);
  color: var(--text);
}
.tx-filter-active-chip__clear:focus-visible {
  outline: 2px solid var(--accent-2);
  outline-offset: 2px;
}
.tx-filter-active-chip__clear .icon { width: 16px; height: 16px; }

/* Filter-Sheet-Toggle („Nur ungelesene Buchungen") — Reihen-Layout mit
   Checkbox links, label rechts. Bewusst kein <select>, da Boolean-Filter.
   `flex-direction: row` MUSS explizit gesetzt sein, weil das globale
   `label { flex-direction: column }` (Zeile 42) sonst die Checkbox UNTER
   den Text schiebt. */
.tx-filter-sheet__toggle {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: .65rem;
  padding: .55rem 0;
  font-size: .92rem;
  color: var(--text);
  font-weight: 500;
  cursor: pointer;
}
.tx-filter-sheet__toggle input[type="checkbox"] {
  width: 18px;
  height: 18px;
  accent-color: var(--accent);
  cursor: pointer;
  flex-shrink: 0;
}

/* Ungelesen-Markierung: dezenter Akzent-Punkt vor dem Namen (kein lauter Balken,
   damit die Liste ruhiger wirkt). Name bleibt etwas kräftiger. Der Punkt sitzt
   inline VOR dem Namen — kein vertikaler Layout-Shift beim Markieren-als-gelesen. */
.tx-row--unread > summary .tx-name { font-weight: var(--weight-semi); }
.tx-row--unread > summary .tx-name::before {
  content: '';
  display: inline-block;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--accent);
  margin-right: .45rem;
  vertical-align: 1px;
  flex-shrink: 0;
}

/* Gelesen-Haken: standardmäßig unsichtbar, bei Hover über der Zeile sichtbar
   (Desktop). Absolute Position verhindert, dass der Button die Zeilenhöhe oder
   Spaltenbreite ändert — Layout bleibt konstant, egal ob unread oder read.
   .tx-summary trägt position:relative + padding-right als Slot. Mobil nutzt
   die Wisch-Geste, der Button wird per JS .click() getriggert (auch bei
   display:none). */
.tx-read-btn {
  position: absolute;
  top: 50%;
  right: .5rem;
  transform: translateY(-50%);
  width: 30px;
  height: 30px;
  border-radius: 50%;
  border: 1px solid var(--border-2);
  background: var(--surface-2);
  color: var(--text-dim);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  opacity: 0;
  transition: opacity var(--duration-fast) var(--ease-out),
              color var(--duration-fast) var(--ease-out),
              background var(--duration-fast) var(--ease-out);
}
.tx-read-btn .icon { width: 16px; height: 16px; }
.tx-row:hover .tx-read-btn,
.tx-read-btn:focus-visible { opacity: 1; }
.tx-read-btn:hover { color: #fff; background: var(--accent); border-color: var(--accent); }

/* Wischen → gelesen (Mobil): die Zeile tönt während der Geste Richtung Akzent,
   damit „als gelesen markieren" spürbar wird. transform setzt JS pro Frame. */
.tx-row--swiping > summary {
  background: color-mix(in srgb, var(--accent) 12%, var(--surface));
  transition: background var(--duration-fast) var(--ease-out);
}
.tx-row--swiping { overflow: hidden; }

.tx-filter-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, .35);
  z-index: 250;
  opacity: 0;
  transition: opacity .2s ease;
  -webkit-backdrop-filter: blur(2px);
  backdrop-filter: blur(2px);
}
.tx-filter-overlay.is-open { opacity: 1; }

.tx-filter-sheet {
  position: fixed;
  left: 50%;
  bottom: 0;
  transform: translate(-50%, 100%);
  width: min(520px, 100%);
  max-height: 85vh;
  /* iOS dvh respektiert dynamische URL-/Tab-Bar — vermeidet, dass das
     Sheet hinter die Mobile-Tabbar geschoben wird. */
  max-height: min(85vh, 85dvh);
  background: var(--surface);
  border-radius: 16px 16px 0 0;
  box-shadow: 0 -8px 32px rgba(0, 0, 0, .2);
  z-index: 260;
  display: flex;
  flex-direction: column;
  transition: transform .22s cubic-bezier(.16, .84, .44, 1);
}
.tx-filter-sheet.is-open { transform: translate(-50%, 0); }
.tx-filter-sheet__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: .85rem 1.1rem .6rem;
  border-bottom: 1px solid var(--border-2);
}
.tx-filter-sheet__title {
  font-size: 1rem;
  font-weight: 600;
  margin: 0;
  letter-spacing: .005em;
}
.tx-filter-sheet__close {
  border: none;
  background: transparent;
  color: var(--text-muted);
  padding: .35rem;
  border-radius: 8px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
}
.tx-filter-sheet__close:hover { background: var(--surface-2); color: var(--text); }
.tx-filter-sheet__form {
  display: flex;
  flex-direction: column;
  gap: .75rem;
  /* WICHTIG: padding-bottom == 0 — der sticky-Actions-Bar liefert die untere
     Atemluft. Mit padding-bottom > 0 entstand ein Streifen zwischen Actions-
     Bar und Sheet-Boden, durch den beim Scrollen Inhalte sichtbar durchblitzten
     (Fred-Sichtung Mobile, 2026-05-31). */
  padding: 1rem 1.1rem 0;
  overflow-y: auto;
}
.tx-filter-sheet__field {
  display: flex;
  flex-direction: column;
  gap: .3rem;
  font-size: .82rem;
  color: var(--text-muted);
  font-weight: 500;
}
.tx-filter-sheet__field select,
.tx-filter-sheet__field input {
  font-size: .95rem;
  padding: .55rem .65rem;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text);
  width: 100%;
  /* BL-BUG-07c: iOS Safari gibt input[type=date] eine intrinsische min-width,
     die unsere 1fr-Grid-Spalte aufsprengt → Bis-Feld läuft rechts raus und
     überlagert das Von-Feld. min-width:0 erlaubt Shrink unter min-content. */
  min-width: 0;
  box-sizing: border-box;
}
.tx-filter-sheet__row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: .65rem;
  /* Row selbst min-width:0, sonst sprengt die min-content der Date-Inputs
     auch den Row-Track und schiebt die Felder über den Modal-Rand. */
  min-width: 0;
}
.tx-filter-sheet__row > .tx-filter-sheet__field {
  min-width: 0;
}
/* iOS Safari rendert input[type=date] mit nativem Sprite (Kalender-Icon +
   internem Padding), das width:100% teilweise ignoriert → Input ist breiter
   als sein Container. -webkit-appearance:none deaktiviert die native Hülle,
   die input wird wie ein Text-Input geboxt aber öffnet trotzdem den Picker. */
.tx-filter-sheet__field input[type="date"] {
  -webkit-appearance: none;
  appearance: none;
  max-width: 100%;
}
/* Frueher: @media (max-width: 480px) → 1-Spalte. Entfernt 2026-05-31, weil
   Select+Date-Inputs dank `min-width:0` auch auf engen Mobile-Viewports in
   2 Spalten passen — und Fred 2-spaltige Paare explizit gewünscht hat. */
/* Move 5 (v14.0): Filter-Submit als Primary-Action — sticky am Sheet-Footer,
   safe-area-aware, klare visuelle Trennung über schmalen Top-Border. */
.tx-filter-sheet__actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  margin-top: var(--space-2);
  /* padding-bottom liefert die Atemluft am Sheet-Boden, statt sie ins Form-
     padding zu legen. So bleibt die opake Actions-Bar bündig mit dem unteren
     Sheet-Rand — kein Durchblitz-Streifen unter dem Button. */
  padding: var(--space-3) 0 var(--space-4);
  border-top: 1px solid var(--border-2);
  position: sticky;
  bottom: 0;
  background: var(--surface);
}
.tx-filter-sheet__actions .primary {
  flex: 1 1 auto;
  padding: .85rem 1rem;
  font-size: var(--text-md);
  font-weight: var(--weight-semi);
  border-radius: var(--r-md);
}

@media (max-width: 540px) {
  /* Move 2: Filter-FAB sitzt oberhalb der Tabbar. body:not(.no-tabbar) hält
     den Spezifitäts-Override gegen die 620px-Regel weiter oben aktiv. */
  .tx-filter-fab { right: 1rem; }
  body:not(.no-tabbar) .tx-filter-fab {
    /* +14px synchron zum Tabbar-Padding-Bottom, sonst überlappt der FAB die
       Tabbar-Oberkante (Move 2 Iteration 2, 2026-05-31). */
    bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom) + var(--space-2) + 14px);
  }
  body.no-tabbar .tx-filter-fab { bottom: 1rem; }
  .tx-readall-fab { right: calc(1rem + 4px); }
  body:not(.no-tabbar) .tx-readall-fab {
    bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom) + var(--space-2) + 14px + 56px + .75rem);
  }
  body.no-tabbar .tx-readall-fab { bottom: calc(1rem + 56px + .75rem); }
  .tx-filter-sheet {
    width: 100%;
    border-radius: 18px 18px 0 0;
    /* Sheet ueber der Mobile-Tabbar parken, sonst verdeckt sie die sticky-Actions
       (Anwenden-Button). body.no-tabbar laesst den Default greifen. */
  }
  body:not(.no-tabbar) .tx-filter-sheet {
    /* +14px synchron zum Tabbar-Puffer, sonst sitzt das Sheet auf der Tabbar-
       Oberkante statt sauber darüber. */
    bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom) + 14px);
    max-height: min(85vh, calc(85dvh - var(--tabbar-h) - env(safe-area-inset-bottom) - 14px));
  }
  /* padding-bottom-Override 2026-05-31 entfernt — Bottom-Atemluft kommt jetzt
     komplett aus .tx-filter-sheet__actions, damit die opake Bar bündig sitzt. */
}

/* === Budget-Total-Card v4 (Budgets-Top-Card, 2026-06-07) ===
   Reine Gesamt-Budget-Übersicht: Hauptzahl links, Progress-Bar + Restbudget-%
   rechts. Frei-verfügbar wurde aus dieser Card entfernt (war v3 2026-05-30). */
.budget-history-chart { margin-top: .4rem; }
.budget-chart { width: 100%; max-width: 100%; height: auto; display: block; }
.budget-total-card { padding: 1.5rem 1.4rem; }
.budget-total-card__head { margin-bottom: .55rem; }
.budget-total-card__label { display: block; }
.budget-total-card__body {
  display: grid;
  grid-template-columns: minmax(0, auto) minmax(0, 1fr);
  gap: 1.5rem 2rem;
  align-items: end;
}
.budget-total-card__amount-block { min-width: 0; }
.budget-total-card__amount {
  font-size: 2.1rem;
  font-weight: 650;
  letter-spacing: -.01em;
  line-height: 1.1;
  margin-bottom: .25rem;
}
.budget-total-card__amount.neg { color: var(--danger); }
.budget-total-card__progress-block {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: .4rem;
  align-items: stretch;
  padding-bottom: .15rem;
}
.budget-total-card__progress {
  height: 8px;
  background: var(--surface-2);
  border-radius: 5px;
  overflow: hidden;
}
.budget-total-card__progress-fill {
  height: 100%;
  background: linear-gradient(90deg, hsl(155, 78%, 40%), hsl(142, 65%, 48%));
  border-radius: 5px;
  transition: width .3s;
}
.budget-total-card__progress-fill.is-over { background: hsl(0, 75%, 56%); }
.budget-total-card__progress-label {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* === Rücklagen-Sub-Card (Budgets-Seite, 2026-06-07) ===
   Direkt unter der Top-Card. Optisch verwandter Container für die
   Pseudo-Budgets (Vertragspuffer + Sparen) — konzeptionell von den
   Monats-Budgets getrennt. Block-Wert 50€ (statt 10€), damit große Pools
   auf einer Zeile passen. */
.budget-reserve-card {
  padding: .9rem 1.2rem 1rem;
  margin-top: -.4rem;
}
.budget-reserve-card__head { margin-bottom: .25rem; }
.budget-reserve-list {
  display: flex;
  flex-direction: column;
}

@media (max-width: 580px) {
  .budget-total-card { padding: 1.1rem 1rem 1.2rem; }
  .budget-total-card__body {
    grid-template-columns: 1fr;
    gap: .75rem;
    align-items: stretch;
  }
  .budget-total-card__amount { font-size: 1.85rem; }
  .budget-total-card__progress-block { padding-bottom: 0; }
}

/* === Vertragsseite: Drei-Kachel-Header (Phase 33 CONTRACT-UI-01 + Phase 34 CONTRACT-UI-09) ===
   Outer .card.contracts-header wrapper + .tile-grid → 1:1:2 auf Desktop,
   Mobile-Hybrid bricht in 2-Spalten-Row (Belastung + Puffer) + Chart
   full-width.
   Phase 34: Jedes Tile bekommt LOKAL ein eigenes 3-Row-Grid
   (Label / Hauptzahl / Sub), sodass die drei gleichartigen Slot-Linien
   über alle Tiles hinweg horizontal aligned bleiben — auch wenn ein
   Tile keinen Sub-Text rendert (Variante B aus 34-02-Plan, weniger
   risikoreich als `display: contents` wegen Round-Icon-Btn im
   Pool-Tile-Head). Chart-Tile spannt Label-Row + zwei Body-Rows. */
.contracts-header {
  /* D-07: ~+30% top, ~+10% gesamt-Card-Höhe ggü .card-Default (1.2rem 1.4rem). */
  padding: 1.75rem 1.5rem;
}
.tile-grid {
  display: grid;
  grid-template-columns: 1fr 1fr 2fr;
  /* Row-Höhen werden pro Tile (Variante B) gesetzt, nicht hier auf dem
     Outer-Grid — Pool-Tile-Markup mit Round-Icon-Btn würde `display: contents`
     riskanter machen als per-Tile-Grid. */
  gap: 1.2rem;
  align-items: start;
}
.tile,
.tile-pool {
  display: grid;
  /* Alle Rows `auto` + uniforme row-gap → konstanter Spacing-Rhythmus zwischen
     allen Slots, unabhängig wie viele Sub-Elemente ein Tile hat. min-height
     ist nicht nötig, da alle Slots in beiden Tile-Varianten befüllt sind. */
  grid-template-rows: auto auto auto;
  row-gap: 1rem;
  min-width: 0;
}
.tile > .muted.small:first-child,
.tile-pool > .tile-head,
.tile-chart > .muted.small:first-child {
  grid-row: 1;
  /* Cross-Tile-Label-Sync: Pool-Tile-Head hat einen 2rem-Round-Icon-Btn,
     die anderen Tiles nur einen schlichten Span. Erzwingen identischer
     min-height + vertikales Zentrieren, sodass alle drei Hauptzahl-Tops
     auf gleicher Linie sitzen. */
  min-height: 2rem;
  display: flex;
  align-items: center;
}
.tile > .tile-amount,
.tile-pool > .tile-amount,
.tile-pool > .tile-cta {
  grid-row: 2;
  margin: 0;
}
.tile-pool > .tile-cta {
  justify-self: start;
  align-self: center;
  margin-top: 0;
}
.tile > .muted.small:nth-child(n+3),
.tile-pool > .pill,
.tile-pool > .btn-secondary:not(.tile-cta) {
  grid-row: 3;
  align-self: start;
}
.tile-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .5rem;
  margin-bottom: 0;
  width: 100%;
}
.tile-amount {
  font-size: 2rem;
  font-weight: var(--weight-semi);
  line-height: var(--leading-tight);
  font-variant-numeric: tabular-nums;
  letter-spacing: -.01em;
  color: var(--text);
}
.tile-amount.neg { color: var(--danger); }
.tile-amount.muted { color: var(--text-muted); }
.tile-chart {
  display: grid;
  /* Label-Slot identisch zu den linken Tiles → Label-Höhen-Linie aligned.
     Body spannt die unteren zwei Rows. */
  grid-template-rows: auto 1fr;
  row-gap: .15rem;
  min-width: 0;
}
.tile-chart > .muted.small {
  grid-row: 1;
}
.tile-chart > svg,
.tile-chart > p {
  grid-row: 2;
  align-self: stretch;
}
.tile-chart svg {
  width: 100%;
  height: auto;
  display: block;
  overflow: visible;
}

/* Round-Icon-Button (Pool-Tile-Zahnrad) — kompakter Verwandter von .icon-btn-add */
.round-icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  border-radius: 50%;
  background: var(--surface-2);
  color: var(--text);
  border: 1px solid var(--border);
  cursor: pointer;
  padding: 0;
  transition: background .12s, transform .12s;
}
.round-icon-btn:hover {
  background: var(--accent-bg);
  transform: scale(1.04);
}
.round-icon-btn .icon {
  width: 1.05rem;
  height: 1.05rem;
}

/* Pills — Status-Indikatoren (z.B. Puffer aktiv / inaktiv) */
.pill {
  display: inline-flex;
  align-items: center;
  padding: 2px var(--space-2);
  border-radius: var(--r-pill);
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  white-space: nowrap;
  line-height: var(--leading-snug);
  letter-spacing: .01em;
}
.pill-success {
  background: var(--pill-success-bg);
  color: var(--pill-success-text);
}
.pill-neutral {
  background: var(--pill-neutral-bg);
  color: var(--pill-neutral-text);
}
/* Zweiteilige Pill: links Status-Label (bold), rechts Detail-Wert (regular,
   right-aligned). Eliminiert eine zusätzliche grid-row im Pool-Tile. */
.pill-split {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: .6rem;
  width: 100%;
  padding-right: .55rem;
  padding-left: .55rem;
}
.pill-split > strong {
  font-weight: 600;
}
.pill-split > span {
  font-weight: 400;
  opacity: .85;
  white-space: nowrap;
}

@media (max-width: 720px) {
  /* Mobile bleibt visuell unverändert ggü Phase 33 (Plan 34-02 D-07).
     Padding-Erhöhung der Outer-Card wird zurückgesetzt, Tile-Grid bricht
     in 2 Spalten + Chart full-width. */
  .contracts-header {
    padding: 1.2rem 1.4rem; /* = .card-Default — Mobile unverändert */
  }
  .tile-grid {
    grid-template-columns: 1fr 1fr;
    gap: .8rem;
  }
  .tile-chart {
    grid-column: 1 / -1;
  }
  .tile-amount {
    font-size: 1.5rem;
  }
}

/* === Vertragsseite: Archive-Collapse (Phase 34, Plan 34-01, CONTRACT-UI-08) ===
   Headline ist ein Button (gesamter Header klickbar, a11y via aria-expanded).
   Body wird per `hidden`-Attribut versteckt — `display: none`-Override stellt
   sicher, dass das Attribute auch bei CSS-Frameworks/Resets greift. Chevron
   rotiert via data-archive-open-State. */
.archive-section .archive-toggle {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .6rem;
  width: 100%;
  background: transparent;
  border: 0;
  padding: 0;
  cursor: pointer;
  color: inherit;
  font: inherit;
  text-align: left;
}
.archive-section .archive-headline {
  margin: 0;
}
.archive-section .archive-chevron {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--text-secondary);
  transition: transform .2s ease;
}
.archive-section[data-archive-open="true"] .archive-chevron {
  transform: rotate(180deg);
}
.archive-section .archive-body[hidden] {
  display: none;
}
.archive-section .archive-body {
  margin-top: .8rem;
}

/* === Budgets === */
.budget-list { display: flex; flex-direction: column; gap: .5rem; }
.budget-row {
  display: block; padding: .7rem .95rem;
  border: 1px solid var(--border); border-radius: 9px;
  text-decoration: none; color: inherit; background: var(--surface);
  transition: border-color .12s;
}
.budget-row:hover { border-color: var(--accent); text-decoration: none; }
.budget-head { display: flex; align-items: center; gap: .4rem; margin-bottom: .3rem; font-size: .9rem; }
.progress { height: 5px; background: var(--surface-2); border-radius: 3px; overflow: hidden; }
.progress-fill { height: 100%; background: linear-gradient(90deg, var(--accent), #00c2ff); transition: width .3s; }
.progress-fill.over { background: linear-gradient(90deg, var(--danger), #ff6b6b); }

/* === Budget-Card v2 (2026-05-29, /budgets) ===
   Cards (kein Tabellen-Markup), neutrale Icon-Tile, stacked-Labels unter Zahl,
   Farb-Transition-Progress via --ratio CSS-Var. */
.budget-card {
  display: grid;
  grid-template-columns: auto 1fr auto auto;
  /* Reihenfolge ab 2026-05-29 v2: monthly LEFT, verfuegbar RIGHT — der
     Pool-Stand soll der prominenteste, am weitesten rechts stehende Wert sein. */
  grid-template-areas:
    "icon name monthly verfuegbar"
    "progress progress progress progress";
  gap: .55rem 1.1rem;
  align-items: center;
  padding: .9rem 1.05rem;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--surface);
  text-decoration: none;
  color: inherit;
  transition: border-color .12s, background .12s;
}
.budget-card:hover { border-color: var(--accent); text-decoration: none; }

.budget-card .b-icon {
  grid-area: icon;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.4rem;
  height: 2.4rem;
  border-radius: 9px;
  background: var(--surface-2);
  color: var(--text-muted);
  flex-shrink: 0;
}
.budget-card .b-icon .icon { width: 1.3rem; height: 1.3rem; }

.budget-card .b-name {
  grid-area: name;
  font-weight: 500;
  font-size: 1rem;
  display: inline-flex;
  align-items: center;
  gap: .35rem;
  min-width: 0;
}
.budget-card .b-name .b-rollover-mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--text-muted);
  opacity: .7;
}
.budget-card .b-name .b-rollover-mark .icon { width: .9rem; height: .9rem; }

.budget-card .b-amount {
  grid-area: verfuegbar;
  text-align: right;
  font-variant-numeric: tabular-nums;
  line-height: 1.2;
}
.budget-card .b-amount .v-val {
  display: block;
  font-weight: 600;
  font-size: 1rem;
}
.budget-card .b-amount .v-val.neg { color: var(--danger); }
.budget-card .b-amount .v-label {
  display: block;
  font-size: .72rem;
  color: var(--text-muted);
  margin-top: .15rem;
}
.budget-card .b-amount .v-label.over { color: var(--danger); }

.budget-card .b-monthly {
  grid-area: monthly;
  text-align: right;
  font-variant-numeric: tabular-nums;
  min-width: 4.5rem;
  line-height: 1.2;
}
.budget-card .b-monthly .m-val {
  display: block;
  font-size: .9rem;
  color: var(--text);
  font-weight: 400;
}
.budget-card .b-monthly .m-label {
  display: block;
  font-size: .68rem;
  color: var(--text-muted);
  margin-top: .15rem;
  letter-spacing: .015em;
}

.budget-progress-v2 {
  grid-area: progress;
  height: 6px;
  border-radius: 4px;
  background: var(--surface-2);
  overflow: hidden;
  margin-top: .1rem;
}
.budget-progress-v2-fill {
  height: 100%;
  border-radius: 4px;
  /* 2026-06-07 Polish: Mehrstufiger Verlauf statt linearer 0→142-Hue,
     der zu früh ins Rötliche kippte. Plateau-Grün → Türkis-Übergang →
     Lila-Warnzone → Rot bei Überschreitung. Default-Fallback grün. */
  background: hsl(142, 65%, 48%);
  transition: width .3s, background .3s;
}
.budget-progress-v2-fill[data-state="deep"] {
  background: linear-gradient(90deg, hsl(155, 78%, 40%) 0%, hsl(142, 72%, 48%) 100%);
}
.budget-progress-v2-fill[data-state="normal-hi"] {
  background: hsl(142, 65%, 48%);
}
.budget-progress-v2-fill[data-state="normal-mid"] {
  /* 2026-06-07 Iter 2: Türkis kräftiger (war 60%/48 → jetzt 75%/45),
     Zone wird auch breiter (0.15..0.55 statt 0.25..0.55). */
  background: linear-gradient(90deg, hsl(180, 75%, 45%) 0%, hsl(168, 70%, 45%) 100%);
}
.budget-progress-v2-fill[data-state="normal-lo"] {
  /* 2026-06-07 Iter 2: Lila dezenter (Saturation 55→38, Lightness 55→58)
     und nur noch <0.15 statt <0.25 — kommt später, weniger intense. */
  background: linear-gradient(90deg, hsl(282, 38%, 58%) 0%, hsl(265, 38%, 58%) 100%);
}
.budget-progress-v2-fill[data-state="over"] {
  background: hsl(0, 75%, 56%);
}
.budget-progress-v2-fill[data-state="neutral"] {
  background: var(--surface-2);
}

/* Mobile: 3-col grid mit gestackten Werten unter dem Namen.
   v2-Reihenfolge: monthly LEFT, verfuegbar RIGHT (Pool-Stand prominent rechts). */
@media (max-width: 580px) {
  .budget-card {
    grid-template-columns: auto 1fr auto;
    grid-template-areas:
      "icon name name"
      "icon monthly verfuegbar"
      "progress progress progress";
    gap: .3rem .8rem;
    padding: .8rem .9rem;
  }
  .budget-card .b-icon { width: 2.2rem; height: 2.2rem; align-self: start; margin-top: .15rem; }
  .budget-card .b-monthly { text-align: left; }
  .budget-card .b-amount .v-val { font-size: 1.05rem; }
}

/* === Budget-Card v3 Block-System (2026-06-07) ===
   variant='full' + 'reserve' der /budgets-Liste. Progress-Bar durch eine
   Reihe gleichgrosser Bloecke ersetzt — 1 Block = konstanter Geldwert
   (default 10 EUR, Ruecklagen-Sub-Card 50 EUR). Block-Anzahl ist damit
   direkt zwischen Cards vergleichbar. Farb-Logik (state) wird von der
   v2-Progress-Bar wiederverwendet. */
.budget-card--blocks {
  /* Name+Monthly-Spalte fix breit, damit die Block-Reihe in JEDER Card an
     derselben X-Position startet — unabhängig von der Namens-Länge
     (Fred-Verify 2026-06-07: vorher driftete die Block-X-Position je
     nach Text-Länge daneben). */
  grid-template-columns: auto 13rem minmax(0, 1fr) auto;
  grid-template-areas:
    "icon name blocks verfuegbar"
    "icon monthly blocks verfuegbar";
  gap: .15rem 1.1rem;
  align-items: center;
  padding: .85rem 1.05rem;
}
.budget-card--blocks .b-name {
  grid-area: name;
  align-self: end;
  line-height: 1.15;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.budget-card--blocks .b-monthly {
  grid-area: monthly;
  text-align: left;
  align-self: start;
  min-width: 0;
  font-size: .82rem;
  color: var(--text-muted);
  display: inline-flex;
  align-items: baseline;
  gap: .25rem;
}
.budget-card--blocks .b-monthly .m-val {
  font-size: .82rem;
  font-weight: 400;
  color: var(--text-muted);
}
.budget-card--blocks .b-monthly .m-suffix { font-size: .82rem; }
.budget-card--blocks .b-amount {
  grid-area: verfuegbar;
  align-self: center;
}

/* Block-Reihe selbst — vertikal mittig in der Card (Span beider Grid-Rows).
   align-self: center sichert konsistente Y-Position über alle Cards hinweg
   (Fred-Verify 2026-06-07: Blöcke sollen exakt auf einer Linie über die Liste
   liegen). */
.budget-blocks {
  grid-area: blocks;
  align-self: center;
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 3px;
  height: 14px;
  min-width: 0;
  overflow: hidden;
}
.bb {
  flex: 1 1 0;
  min-width: 4px;
  max-width: 14px;
  height: 100%;
  border-radius: 2px;
  background: var(--surface-2);
  transition: background .2s;
}
.bb--ghost { background: var(--surface-2); opacity: .85; }
/* Trenner-Element entfernt (2026-06-07 Iter 2): Entsättigung der Rollover-
   Excess-Blöcke trennt visuell ausreichend, der zusätzliche Gap wirkte
   unruhig auf einer Liste mit vielen Cards. */

/* Filled-Bloecke: Farbe aus state (analog Progress-Bar v2) */
.budget-blocks[data-state="deep"] .bb--filled { background: hsl(142, 72%, 48%); }
.budget-blocks[data-state="normal-hi"] .bb--filled { background: hsl(142, 65%, 48%); }
.budget-blocks[data-state="normal-mid"] .bb--filled { background: hsl(180, 75%, 45%); }
.budget-blocks[data-state="normal-lo"] .bb--filled { background: hsl(282, 38%, 58%); }

/* Rollover-Excess: gleiche Hue wie filled, aber heller + leichter Glanz
   (Iter 4, 2026-06-07): vorherige Entsaettigung wirkte "abgeklungen" statt
   "ueber voll". Jetzt Lightness hoch, Saettigung gehalten/leicht erhoeht,
   plus 1px Inset-Top-Highlight fuer dezenten Schimmer. */
.budget-blocks[data-rollover-excess="1"][data-state="deep"] .bb--over,
.budget-blocks[data-rollover-excess="1"][data-state="normal-hi"] .bb--over {
  background: hsl(142, 78%, 62%);
  box-shadow: inset 0 1px 0 hsla(142, 90%, 82%, 0.6);
}
.budget-blocks[data-rollover-excess="1"][data-state="normal-mid"] .bb--over {
  background: hsl(180, 78%, 60%);
  box-shadow: inset 0 1px 0 hsla(180, 90%, 82%, 0.6);
}
.budget-blocks[data-rollover-excess="1"][data-state="normal-lo"] .bb--over {
  background: hsl(282, 55%, 72%);
  box-shadow: inset 0 1px 0 hsla(282, 75%, 88%, 0.6);
}

/* Ueberschritten: ausschliesslich rote Bloecke (Brief-Vorgabe Fred 2026-06-07).
   Helper liefert filled=0/ghost=0/over=K — wir faerben .bb--over rot. */
.budget-blocks[data-overspend="1"] .bb--over { background: hsl(0, 75%, 56%); }

/* === Budget-Card variant='reserve' (Ruecklagen-Sub-Card) ===
   Visuell verwandt zu --blocks, aber kompakter (kein Hover-Akzent,
   dezenter Container — gehoert konzeptionell zur Top-Card). */
.budget-card--reserve {
  background: transparent;
  border: none;
  padding: .55rem 0;
  border-radius: 0;
}
.budget-card--reserve + .budget-card--reserve { border-top: 1px solid var(--border); }
.budget-card--reserve:hover { border-color: transparent; }

/* Mobile-Layout fuer Block-Variante: Blocks unten in eigener Zeile
   (wie altes Progress-Layout) — mehr Breite fuer schmale Bloecke. */
@media (max-width: 580px) {
  .budget-card--blocks {
    grid-template-columns: auto 1fr auto;
    grid-template-areas:
      "icon name name"
      "icon monthly verfuegbar"
      "blocks blocks blocks";
    gap: .3rem .8rem;
    padding: .8rem .9rem;
  }
  .budget-card--blocks .b-name { align-self: center; }
  .budget-card--blocks .b-monthly { align-self: center; }
  .budget-card--blocks .b-amount { align-self: center; }
  .budget-blocks { height: 12px; gap: 2px; margin-top: .3rem; }
  .bb { min-width: 3px; max-width: 12px; }
}

/* === Budget-Toolbar (Sort-Icon + Add) ===
   v2 (2026-05-29): Dropdown durch round-icon-btn ersetzt (Settings-Pattern). */
.budgets-toolbar {
  display: inline-flex;
  align-items: center;
  gap: .55rem;
  flex-wrap: wrap;
}

/* === Sort-Optionen-Modal === */
.sort-mode-fieldset {
  border: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: .35rem;
}
.sort-mode-option {
  display: flex;
  align-items: flex-start;
  gap: .6rem;
  padding: .55rem .7rem;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--surface);
  cursor: pointer;
  transition: border-color .12s, background .12s;
}
.sort-mode-option:hover { border-color: var(--accent); }
.sort-mode-option.is-active { border-color: var(--accent); background: var(--accent-bg, var(--surface-2)); }
.sort-mode-option input[type="radio"] {
  margin-top: .15rem;
  flex-shrink: 0;
}
.sort-mode-text { display: flex; flex-direction: column; gap: .12rem; min-width: 0; }
.sort-mode-label { font-size: .92rem; font-weight: 500; }
.sort-mode-hint { font-size: .75rem; }

/* === Sortier-Modus: ↑/↓-Reihenfolge (2026-05-30) ===
   Loest das alte Sortable.js-Drag ab (touch-untauglich). Eintritt via
   Settings-Modal → „Reihenfolge bearbeiten". Markup: _budgets_sort_panel.html. */
.sort-mode-edit-entry {
  margin-top: 1rem;
  padding-top: 1rem;
  border-top: 1px solid var(--border);
}
.btn-block { display: flex; width: 100%; justify-content: center; align-items: center; gap: .4rem; }
.btn-block .icon { width: 1.05rem; height: 1.05rem; }

/* Sortier-Panel: budget-* sind die Original-Klassen; sort-reorder-* generische
   Aliase fuer Konten + Wallets (gleiche Optik, kein CSS-Duplikat). */
.budget-sort-list,
.sort-reorder-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
}
.budget-sort-row,
.sort-reorder-row {
  display: flex;
  align-items: center;
  gap: .75rem;
  padding: .65rem .25rem;
}
.budget-sort-row + .budget-sort-row,
.sort-reorder-row + .sort-reorder-row { border-top: 1px solid var(--border); }
.budget-sort-row .b-icon,
.sort-reorder-row .b-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.2rem;
  height: 2.2rem;
  border-radius: 9px;
  background: var(--surface-2);
  color: var(--text-muted);
  flex-shrink: 0;
}
.budget-sort-row .b-icon .icon,
.sort-reorder-row .b-icon .icon { width: 1.2rem; height: 1.2rem; }
.budget-sort-row__name,
.sort-reorder-row__name {
  flex: 1 1 auto;
  min-width: 0;
  font-weight: 500;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.budget-sort-row__controls,
.sort-reorder-row__controls { display: inline-flex; gap: .4rem; flex-shrink: 0; }
.sort-move-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.5rem;
  height: 2.5rem;
  min-height: 0;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 9px;
  background: var(--surface);
  color: var(--text);
  cursor: pointer;
  transition: border-color .12s, background .12s, opacity .12s;
}
.sort-move-btn:hover:not(:disabled) { border-color: var(--accent); color: var(--accent); }
.sort-move-btn:active:not(:disabled) { background: var(--surface-2); }
.sort-move-btn:disabled { opacity: .3; cursor: default; }
.sort-move-btn .icon { width: 1.15rem; height: 1.15rem; }
.sort-move-btn--down .icon { transform: rotate(180deg); }

/* === Budget-Card Mini-Variante (Dashboard) ===
   2026-05-29 v2: Stacked-Layout statt 3-col-horizontal — vermeidet die
   Text-Ueberlappung bei langen Namen (z.B. "Drogerie & Friseur").

   Layout:
       [icon] Lebensmittel
       [icon] 227,82 €  verfügbar
       [=== progress ===========]

   Icon spannt zwei Inhalt-Reihen (align-self center). Name oben, Betrag+Label
   inline auf zweiter Reihe links-aligned. Progress unter beidem.
*/
.budget-card--mini {
  grid-template-columns: auto 1fr auto;
  grid-template-areas:
    "icon name badge"
    "icon amount amount"
    "progress progress progress";
  align-items: center;
  gap: 0 .7rem;
  padding: .65rem .85rem;
}
.budget-card--mini .b-badge {
  grid-area: badge;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.5rem;
  height: 1.5rem;
  align-self: start;
  margin-top: -.05rem;
  flex-shrink: 0;
}
.budget-card--mini .b-badge .icon { width: 1.15rem; height: 1.15rem; }
.budget-card--mini .b-badge--tight { color: hsl(282, 55%, 60%); }
.budget-card--mini .b-badge--over { color: hsl(0, 75%, 56%); }
.budget-card--mini .b-icon {
  grid-area: icon;
  align-self: center;
  width: 2rem;
  height: 2rem;
}
.budget-card--mini .b-icon .icon { width: 1.1rem; height: 1.1rem; }
.budget-card--mini .b-name {
  grid-area: name;
  align-self: end;
  font-size: .93rem;
  font-weight: 500;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.budget-card--mini .b-amount {
  grid-area: amount;
  align-self: start;
  text-align: left;
  display: inline-flex;
  align-items: baseline;
  gap: .35rem;
  flex-wrap: wrap;
  min-width: 0;
}
.budget-card--mini .b-amount .v-val {
  display: inline;
  font-size: .95rem;
}
.budget-card--mini .b-amount .v-label {
  display: inline;
  font-size: .72rem;
  margin-top: 0;
}
.budget-card--mini .budget-progress-v2 {
  height: 5px;
  margin-top: .4rem;
}

.preview-card {
  background: var(--surface);
  border: 1px solid var(--border);
  box-shadow: none;
  margin-top: 1rem;
}

/* === Icon-Buttons (Add / Edit) === */
.icon-btn-add, .icon-btn-edit {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.2rem;
  height: 2.2rem;
  border-radius: 50%;
  background: var(--accent);
  color: #fff;
  text-decoration: none;
  font-size: 1.3rem;
  font-weight: 400;
  line-height: 1;
  transition: transform .12s, filter .12s;
  border: none;
  cursor: pointer;
}
.icon-btn-add:hover, .icon-btn-edit:hover {
  filter: brightness(1.1);
  transform: scale(1.04);
}
.icon-btn-edit {
  background: var(--surface-2);
  color: var(--text);
  border: 1px solid var(--border);
  font-size: 1rem;
}
.icon-btn-edit:hover { background: var(--surface-4); }

/* === Vertragsseite: Add-Button-Prominenz (Phase 33, CONTRACT-UI-02, D-20) === */
/* Container für die beiden Add-Einstiege (Desktop-Primary + Mobile-Icon).
   CSS regelt Sichtbarkeit per Viewport — KEINE doppelten Einstiege sichtbar. */
.add-contract-actions {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
}
.btn-add-contract {
  /* Background / color / radius erbt von .btn-primary; hier nur Padding + Schrift-
     Gewicht aufdrehen, damit der Button visuell deutlich über dem alten Icon-Button steht. */
  padding: .7rem 1.4rem;
  font-size: 1rem;
  font-weight: 600;
  border-radius: 10px;
  display: inline-flex;
  align-items: center;
  gap: .35rem;
}
/* Desktop: großer Primary-Button sichtbar, Icon-Button versteckt. */
@media (min-width: 721px) {
  .add-contract-actions .icon-btn-add { display: none; }
}
/* Mobile: großer Primary-Button versteckt, bestehender Icon-Button bleibt. */
@media (max-width: 720px) {
  .add-contract-actions .btn-add-contract { display: none; }
}
/* === /Vertragsseite Add-Button === */

.icon-btn-delete {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.2rem;
  height: 2.2rem;
  border-radius: 50%;
  background: transparent;
  color: var(--text-muted);
  border: 1px solid var(--border);
  font-size: 1rem;
  cursor: pointer;
  transition: background .12s, color .12s, border-color .12s;
}
.icon-btn-delete:hover {
  background: var(--danger);
  color: #fff;
  border-color: var(--danger);
}

.tx-pill.split-pill {
  background: var(--surface-2);
  color: var(--text-muted);
  border: 1px solid var(--border);
}

/* X-Achse für Verlaufsdiagramme */
.x-ticks-overlay {
  position: relative;
  height: 1.1rem;
  margin: .35rem 0 .2rem;
  pointer-events: none;
}
.x-tick-label-html {
  position: absolute;
  top: 0;
  transform: translateX(-50%);
  font-size: .68rem;
  color: var(--text-muted);
  white-space: nowrap;
  line-height: 1;
}

/* === Contracts Mobile-Karten === */
.contracts-cards { list-style: none; margin: 0; padding: 0; }
.contract-card {
  display: flex;
  flex-direction: column;
  gap: .3rem;
  padding: .7rem .9rem;
  border: 1px solid var(--border);
  border-radius: 10px;
  margin-bottom: .5rem;
  background: var(--surface);
  cursor: pointer;
  transition: border-color .12s;
}
.contract-card:hover { border-color: var(--accent); }
.contract-card-head {
  display: flex; align-items: center; justify-content: space-between; gap: .6rem;
}
.contract-card-meta { white-space: normal; }

/* === 24-03: Archiv-Karten + Wallet-Karten === */
.contract-card.archived { opacity: .75; }
.contract-card.archived .cat-badge { opacity: .8; }

.wallets-cards { list-style: none; margin: 0; padding: 0; }
.wallet-card {
  display: flex;
  flex-direction: column;
  gap: .35rem;
  padding: .7rem .9rem;
  border: 1px solid var(--border);
  border-radius: 10px;
  margin-bottom: .5rem;
  background: var(--surface);
  cursor: pointer;
  transition: border-color .12s;
}
.wallet-card:hover { border-color: var(--accent); }
.wallet-card-head {
  display: flex; align-items: center; justify-content: space-between; gap: .6rem;
}
.wallet-card-meta { white-space: normal; word-break: break-word; }
.wallet-card-actions {
  display: flex; gap: 1rem; padding-top: .35rem;
  border-top: 1px dashed var(--border);
  align-items: center;
}
.wallet-card-actions .link {
  display: inline-flex; align-items: center; gap: .25rem;
  min-height: 40px;
  padding: .35rem .25rem;
}
.wallet-card-actions .icon { width: 1.1em; height: 1.1em; }

/* Screenreader-Only (visuell unsichtbar, semantisch da) */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0);
  white-space: nowrap; border: 0;
}
/* === /24-03 === */

.desktop-only { display: block; }
.mobile-only { display: none; }
@media (max-width: 720px) {
  .desktop-only { display: none !important; }
  .mobile-only { display: block; }
}

/* === Phase 33 D-19 / CONTRACT-UI-07 — Sort=category Container-Breite-Fix ===
   Wurzelursache: `<table class="contracts-table desktop-only">` erbt von
   `.desktop-only { display: block }` (L1109) statt `display: table`. Auf einer
   block-Element-Tabelle greift der table-layout-Algorithmus nicht durchgängig —
   Browser sizen Spalten pro Block-Container individuell zur Content-Breite. Bei
   sort=category rendert das Template MEHRERE separate `<table>`s (eine pro
   Kategorie), jede mit unterschiedlichem Content → inkonsistente Spaltenbreiten
   zwischen den Kategorien (visuelles Springen der Container-Breite).
   Fix: Auf Desktop `display: table` zurücksetzen + `table-layout: fixed`, damit
   alle Tabellen das gleiche Spaltenraster nutzen (gleicher Header → gleiche
   Spalten-Defaults). Mobile-Verhalten (block + horizontal-scroll via `.card table`)
   bleibt unverändert, weil die `@media (max-width: 720px)`-Regel oben `display:
   none !important` setzt — die desktop-only-Tabelle ist mobil eh unsichtbar. */
.contracts-table.desktop-only {
  display: table;
  width: 100%;
  table-layout: fixed;
}

/* === 2026-06-07 Polish: Vertrags-Listen-Layout an Budget-Cards angeglichen ===
   Eckige Icon-Tile (badge_color als Hintergrund) + Name daneben statt der
   alten Icon-in-Pill. Lange Texte truncaten mit ellipsis, damit sie nicht
   in benachbarte Tabellen-Zellen überlaufen. */
.contract-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  border-radius: 8px;
  flex-shrink: 0;
  color: #fff;
  font-size: 1rem;
  line-height: 1;
}
.contract-icon .icon { width: 1.1rem; height: 1.1rem; }
.contract-row__name-cell {
  display: flex;
  align-items: center;
  gap: .55rem;
  min-width: 0;
}
.contract-row__name-main {
  display: flex;
  flex-direction: column;
  min-width: 0;
  gap: .1rem;
}
.contract-name {
  font-weight: 500;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  display: inline-block;
  max-width: 100%;
}
.contract-row__sub,
.contract-row__cell-truncate {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 100%;
}
/* Mobile-Card: head splittet sich Icon+Name LINKS vs. Betrag RECHTS auf;
   Name muss truncaten, damit die Amount-Spalte rechts stabil bleibt. */
.contract-card-head__main {
  display: flex;
  align-items: center;
  gap: .55rem;
  min-width: 0;
  flex: 1 1 auto;
}
.contract-card-head__amount {
  flex: 0 0 auto;
  white-space: nowrap;
}
/* Dashboard "Nächste Verträge" — due-row matched die neue Optik. */
.due-name {
  display: flex;
  align-items: center;
  gap: .55rem;
  min-width: 0;
}
.due-name__text {
  display: flex;
  flex-direction: column;
  min-width: 0;
  gap: .1rem;
}
.due-name__sub {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* 2026-06-07 Polish: Backend liefert bis zu 6 anstehende Termine, damit die
   Card auf Desktop die Nebenspalten-Höhe besser ausfüllt. Auf Mobile
   würde das die Liste aber unverhältnismäßig lang machen — dort die
   Items ab dem 4. ausblenden. */
@media (max-width: 859px) {
  .dash-vertraege .due-list .due-row:nth-child(n+4) { display: none; }
}

/* === Contract-Detail Grid === */
.detail-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: .8rem 1.2rem;
}
.detail-grid > div { min-width: 0; }
.detail-grid .field-label {
  font-size: .75rem;
  color: var(--text-muted);
  margin-bottom: .15rem;
}

/* === Modal (z.B. Regel-Overlay) === */
#modal-target:empty { display: none; }

/* === 2026-06-06: Dashboard-Page-Footer mit Daten-Stand-Hinweis ===
   Wanderte aus dem aufgelösten Hero-Wrapper hierher. Border-top, dezente
   Optik, immer sichtbar unter dem .dash-grid. */
.dashboard-footer {
  margin-top: 1.25rem;
  padding-top: .5rem;
  border-top: 1px solid var(--border);
  display: flex;
  align-items: center;
  gap: .4rem;
  flex-wrap: wrap;
  font-size: .75rem;
  color: var(--text-muted);
  opacity: .85;
}
.dashboard-footer__pill {
  display: inline-flex;
  align-items: center;
  gap: .25rem;
  padding: .15rem .5rem;
  border-radius: 999px;
  font-size: .72rem;
  font-weight: 500;
  background: color-mix(in srgb, var(--hero-warning, #ca8a04) 18%, transparent);
  color: var(--hero-warning, #ca8a04);
}
.dashboard-footer__pill--warning {
  background: color-mix(in srgb, var(--hero-warning, #ca8a04) 18%, transparent);
  color: var(--hero-warning, #ca8a04);
}
/* 2026-06-06: einheitliches Dashboard-Section-Rhythmus 1.5rem zwischen
   allen Major-Sections (Header → Hero-Cards → Chips → Dash-Grid → Footer).
   Doppelklasse-Selector schlägt die schwächere .hero__header-Regel
   (margin-bottom: 1.25rem in charts-hero.css) — Letztere wird weiter
   von Sparen/Geldfluss/Konten genutzt und bleibt unberührt. */
.hero__header.dashboard-page-header {
  margin-bottom: 1.5rem;
}

/* BL-UX-07: Pull-to-Refresh Indikator (nur Dashboard + Mobile, gesteuert via JS).
   Erscheint UNTER dem sticky app-header — top folgt --header-h.
   2026-06-06: Spinner + Text-Zeile (Daten-Stand bzw. „Synchronisiere…"). */
.pull-refresh {
  position: fixed;
  top: var(--header-h); left: 0; right: 0;
  height: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: .6rem;
  background: linear-gradient(to bottom, var(--surface-3, var(--surface)), transparent);
  color: var(--accent);
  z-index: 999;
  pointer-events: none;
  overflow: hidden;
}
.pull-refresh-text {
  font-size: .8rem;
  color: var(--text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 60vw;
}
.pull-refresh.refreshing .pull-refresh-text { color: var(--accent); }
.pull-refresh:not(.pulling) { transition: height 0.22s ease-out; }
.pull-refresh-spinner {
  width: 24px; height: 24px;
  border: 2.5px solid currentColor;
  border-top: 2.5px solid transparent;
  border-radius: 50%;
  opacity: 0.5;
  /* GPU-Compositing: verhindert Layout-Recalc + ruckelnde Anim */
  transform: translateZ(0);
  will-change: transform, opacity;
  transition: opacity 0.2s ease;
}
/* .armed = Pulse genau einmal beim Überschreiten der Schwelle (vom JS gesetzt).
   pulse → "klickt ein", danach steht der Spinner still, bis touchend kommt. */
.pull-refresh.armed .pull-refresh-spinner {
  opacity: 1;
  animation: pull-refresh-pulse 0.42s ease-out;
}
.pull-refresh.refreshing .pull-refresh-spinner {
  opacity: 1;
  animation: pull-refresh-spin 0.9s linear infinite;
}
@keyframes pull-refresh-pulse {
  0%   { transform: translateZ(0) scale(1); }
  45%  { transform: translateZ(0) scale(1.35); }
  100% { transform: translateZ(0) scale(1); }
}
@keyframes pull-refresh-spin {
  from { transform: translateZ(0) rotate(0deg); }
  to   { transform: translateZ(0) rotate(360deg); }
}

.modal-backdrop {
  position: fixed; inset: 0;
  background: rgba(0,0,0,0.55);
  display: flex; align-items: center; justify-content: center;
  z-index: 1000;
  padding: 1rem;
  overflow-y: auto;
}
/* Iter-5: persistenter #modal-target hat .modal-backdrop + hidden initial.
   Author-`display:flex` würde Browser-Default `[hidden] { display:none }`
   übersteuern (gleiche Spezifität, Author gewinnt) → expliziter Override. */
.modal-backdrop[hidden] { display: none; }
.modal-card {
  background: var(--surface);
  color: var(--text);
  border-radius: 12px;
  padding: 1.2rem 1.3rem;
  width: 100%;
  max-width: 38rem;
  box-shadow: 0 12px 40px rgba(0,0,0,0.35);
  max-height: 92vh;
  overflow-y: auto;
}
.modal-head {
  display: flex; align-items: center; justify-content: space-between;
  margin-bottom: .3rem;
}
.modal-close {
  background: transparent; border: none; color: var(--text-muted);
  font-size: 1.2rem; cursor: pointer; padding: .2rem .5rem; border-radius: 6px;
}
.modal-close:hover { background: var(--surface-2); color: var(--text); }

.rule-fields-grid {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: .55rem .8rem;
  align-items: end;
}
.rule-regex-toggle {
  font-size: .85rem;
  color: var(--text-muted);
  display: flex; align-items: center; gap: .3rem;
  padding-bottom: .5rem;
  white-space: nowrap;
}
@media (max-width: 540px) {
  .rule-fields-grid { grid-template-columns: 1fr; }
}

.rule-overlay-footer {
  display: flex; align-items: center; gap: .8rem;
  margin-top: 1rem; padding-top: .8rem; border-top: 1px solid var(--border);
}
.rule-match-counter {
  display: inline-block;
  padding: .35rem .65rem;
  border-radius: 6px;
  background: var(--surface-2);
  color: var(--text);
  font-size: .85rem;
  font-variant-numeric: tabular-nums;
}


.budget-rollover-mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.05rem;
  height: 1.05rem;
  border-radius: 50%;
  background: var(--surface-2);
  color: var(--text-muted);
  font-size: .72rem;
  font-weight: 700;
  margin-left: .35rem;
  line-height: 1;
}

/* === Period Nav === */
.period-nav { display: flex; align-items: center; gap: .6rem; }
.period-nav a, .period-nav .disabled {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.8rem;
  min-height: 1.8rem;
  padding: .1rem .45rem;
  border-radius: 5px;
  text-decoration: none;
  font-size: 1rem;
  line-height: 1;
}
.period-nav a { color: var(--accent); background: var(--accent-bg); }
.period-nav a:hover { background: var(--accent-bg-h); }
.period-nav .disabled { color: var(--text-disabled); }

/* === Seg-Toggle === */
.seg-toggle {
  display: inline-flex;
  background: var(--surface-2);
  border-radius: 7px;
  padding: 2px;
  font-size: .82rem;
}
.seg-toggle a {
  padding: .25rem .65rem; border-radius: 5px;
  text-decoration: none; color: var(--text-muted);
  transition: background .12s, color .12s;
}
.seg-toggle a.active { background: var(--surface); color: var(--text); box-shadow: 0 1px 2px rgba(0,0,0,.06); }
.seg-toggle a:hover:not(.active) { color: var(--text); }

/* === Kategorien-Badges === */
.cat-badge {
  display: inline-flex; align-items: center; gap: .25rem;
  padding: .2rem .65rem; border-radius: 999px;
  font-size: .8rem; font-weight: 600; color: #fff;
  white-space: nowrap; line-height: 1.35;
}
.cat-badge.lg { font-size: .9rem; padding: .3rem .85rem; }
.cat-badge.sm { font-size: .72rem; padding: .12rem .55rem; }
.cat-badge.sub { opacity: .9; }
.cat-badge.auto { opacity: .55; }
.cat-badge.auto:hover { opacity: .85; }

/* === Kategorien-Liste === */
.cat-group {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 10px;
  margin-bottom: .55rem;
  overflow: hidden;
}
.cat-line {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto;
  gap: 1.5rem;
  align-items: center;
  padding: .6rem 1rem;
}
.cat-line.parent {
  background: var(--surface-3);
  border-bottom: 1px solid var(--border);
}
.cat-line.parent:last-child { border-bottom: none; }
.cat-line.child { padding-left: 2.4rem; font-size: .9rem; }
.cat-line.child:not(:last-child) { border-bottom: 1px solid var(--bg); }
.cat-line.child:hover { background: var(--surface-3); }
.cat-stats {
  color: var(--text-dim);
  font-size: .8rem;
  display: flex;
  gap: 1rem;
  white-space: nowrap;
}
.cat-row-actions {
  display: flex;
  gap: 1rem;
  align-items: center;
  font-size: .82rem;
}

/* === Buchungsliste (Card-Style mit Inline-Expand) ===
   Move 5 (v14.0): Token-driven Polish — bessere Touch-Targets (Icon 36,
   min-height 56), klarere Hierarchie (Name --text-md/medium, Meta dezent),
   ruhigere sticky-Header. */
.tx-day {
  font-size: var(--text-xs);
  font-weight: var(--weight-semi);
  color: var(--text-muted);
  padding: var(--space-3) var(--space-3) var(--space-1);
  text-transform: uppercase;
  letter-spacing: .04em;
  background: var(--surface);
  position: sticky;
  top: var(--header-h);
  z-index: var(--z-sticky);
}
.tx-day:first-child { padding-top: var(--space-2); }
.tx-day--today {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  background: var(--accent);
  color: #fff;
  text-transform: none;
  letter-spacing: 0;
  padding: var(--space-2) var(--space-4);
  font-size: var(--text-sm);
  font-weight: var(--weight-semi);
  /* z-index gleich wie .tx-day — Source-Order entscheidet, welcher sticky-Header
     visuell oben liegt. Mit höherem z-index würde Heute permanent über die
     anderen kleben statt wie bei iOS-Section-Headers vom nächsten Datum
     verdrängt zu werden. */
}
.tx-day--today .tx-day__count {
  color: rgba(255,255,255,.78);
  font-weight: var(--weight-regular);
  font-size: var(--text-xs);
}
.tx-list { display: flex; flex-direction: column; }
details.tx-row { border-bottom: 1px solid var(--border-2); }
details.tx-row:last-child { border-bottom: none; }
details.tx-row[open] { background: var(--surface-4); }
.tx-summary {
  position: relative;
  display: grid;
  grid-template-columns: 36px minmax(0, 1fr) auto;
  gap: var(--space-3);
  align-items: center;
  padding: var(--space-2) var(--space-1);
  /* rechter Slot für den absolut positionierten Read-Button (immer reserviert,
     damit die Zeilenhöhe und der Betrag nicht springen, wenn der Button
     beim Lesen verschwindet) */
  padding-right: 44px;
  min-height: 56px;
  cursor: pointer;
  list-style: none;
  transition: background var(--duration-fast) var(--ease-out);
}
.tx-summary::-webkit-details-marker { display: none; }
.tx-row:hover { background: var(--surface-4); }
.tx-icon {
  width: 36px;
  height: 36px;
  border-radius: var(--r-pill);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: var(--text-base);
  color: #fff;
  font-weight: var(--weight-semi);
  flex-shrink: 0;
}
.tx-icon.empty { background: var(--border); color: var(--text-dim); }
.tx-body { min-width: 0; }
.tx-name {
  font-weight: var(--weight-medium);
  font-size: var(--text-md);
  line-height: var(--leading-snug);
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.tx-meta {
  display: flex;
  gap: var(--space-1);
  align-items: center;
  flex-wrap: nowrap;
  font-size: var(--text-xs);
  color: var(--text-muted);
  margin-top: 2px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.tx-pill {
  display: inline-flex;
  align-items: center;
  gap: .15rem;
  padding: .05rem .45rem;
  border-radius: 5px;
  background: var(--surface-2);
  color: var(--text-code);
  font-size: .72rem;
  flex-shrink: 0;
}
.tx-pill.auto { background: var(--surface-3); color: var(--text-dim); }
.tx-pill.contract { background: var(--accent-bg); color: var(--text-code); }
.tx-pill.forecast-pill { background: var(--surface-3); color: var(--text-dim); font-style: italic; }
.tx-pill.tx-pill--late { background: var(--error-bg); color: var(--error-text); font-style: normal; font-weight: var(--weight-semi); }
/* „angekündigt" — Bank-Vormerkung, noch nicht final gebucht. Kein Alarm: dezente
   Accent-Tönung, damit der Nutzer auf einen Blick Realität von Vormerkung trennt. */
.tx-pill.tx-pill--announced { background: var(--accent-bg); color: var(--accent); font-weight: var(--weight-semi); }
.tx-pill.tx-pill--announced .icon { width: 12px; height: 12px; }
/* 2026-06-07 Polish: nur-Icon-Variante — Pill schrumpft auf das Icon,
   Tooltip transportiert die Semantik. Wird für „Angekündigt" genutzt
   (Text war zu lang); bleibt erweiterbar für weitere kompakte Status-Pills. */
.tx-pill.tx-pill--icon-only {
  padding: .15rem .3rem;
  gap: 0;
}
.tx-pill.tx-pill--icon-only .icon { width: 13px; height: 13px; }
/* Angekündigte Buchungen visuell etwas zurücknehmen, ähnlich wie Forecast-Zeilen,
   damit echte Buchungen klar dominieren. */
.tx-row:has(.tx-pill--announced) > summary .tx-amount,
.tx-row:has(.tx-pill--announced) > summary .tx-name { opacity: .85; }
.tx-row:has(.tx-pill--announced) > summary .tx-name,
.tx-row:has(.tx-pill--announced) > summary .tx-amount { font-style: italic; }
/* „verfallen?" — Vormerkung älter als PENDING_STALE_DAYS (10), nicht gebucht.
   Amber/Peach-Warnton, gleiche Palette wie hero-v3__data-footer-pill--warning,
   damit der User sieht: das ist eine Anomalie zur Prüfung, nicht ein Bug. */
.tx-pill.tx-pill--stale { background: #fef3c7; color: #92400e; font-weight: var(--weight-semi); }
.tx-pill.tx-pill--stale .icon { width: 12px; height: 12px; }
[data-theme="dark"] .tx-pill.tx-pill--stale { background: #422006; color: #fdba74; }
.tx-row:has(.tx-pill--stale) > summary .tx-amount,
.tx-row:has(.tx-pill--stale) > summary .tx-name { opacity: .85; font-style: italic; }

/* Forecast section */
.tx-combined-wrap { display: flex; flex-direction: column; gap: 0; }
.forecast-card { background: var(--surface); border-bottom: none; border-radius: var(--radius) var(--radius) 0 0; }
.forecast-label { padding: .5rem 1rem .25rem; font-size: .75rem; }
.forecast-list { }
.forecast-day { opacity: .7; position: static; background: transparent; }
.forecast-row { border-bottom: 1px solid var(--border-2); opacity: .8; }
.forecast-row:last-child { border-bottom: none; }
.forecast-row .tx-name { font-style: italic; color: var(--text-secondary); }
.forecast-row .tx-amount { opacity: .8; }
#real-transactions { border-radius: 0 0 var(--radius) var(--radius); border-top: none; }
.tx-amount {
  font-size: var(--text-md);
  font-weight: var(--weight-semi);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  letter-spacing: -.005em;
}
.tx-amount.neg { color: var(--danger); }
.tx-amount.pos { color: var(--pos); }

/* Mobile: kein Overflow der Beträge. Bewusst kein overflow:hidden auf .tx-list —
   das würde das sticky-Verhalten der .tx-day-Header brechen (siehe MDN: sticky
   bindet sich an den nächsten Ancestor mit scrolling-mechanism). */
.tx-row { min-width: 0; overflow: hidden; }
.tx-summary > * { min-width: 0; }
@media (max-width: 540px) {
  /* Mobil: kein Read-Button-Slot (Wischen statt Hover-Haken), Button im DOM
     lassen — JS triggert btn.click() bei Swipe auch wenn display:none. */
  .tx-summary { gap: var(--space-2); padding: var(--space-2) var(--space-1); padding-right: var(--space-1); }
  .tx-amount { font-size: var(--text-base); }
  .tx-read-btn { display: none; }
}

/* „Mehr laden"-Buttons */
.tx-load-more {
  display: block;
  width: 100%;
  margin: .4rem 0;
  padding: .75rem 1rem;
  border: 1px dashed var(--border);
  background: transparent;
  color: var(--text-muted);
  border-radius: 8px;
  cursor: pointer;
  font-size: .85rem;
  text-align: center;
  transition: background .15s, border-color .15s, color .15s;
}
.tx-load-more:hover {
  border-color: var(--accent);
  color: var(--text);
  background: var(--surface-2);
}
.tx-load-more.htmx-request {
  opacity: .6;
  cursor: progress;
}
/* Toast (Speichern-Feedback nach Edit-Save) */
.tx-toast {
  position: fixed;
  bottom: 1.5rem;
  right: 1.5rem;
  z-index: 1100;
  background: var(--text);
  color: var(--surface);
  padding: .6rem 1.05rem;
  border-radius: 10px;
  font-size: .9rem;
  font-weight: 500;
  box-shadow: 0 6px 20px rgba(0, 0, 0, .18);
  opacity: 0;
  transform: translateY(12px);
  transition: opacity .18s ease-out, transform .18s ease-out;
  pointer-events: none;
  max-width: calc(100vw - 3rem);
}
.tx-toast::before {
  content: '✓';
  display: inline-block;
  margin-right: .45rem;
  font-weight: 700;
  color: var(--pos, #16a34a);
}
.tx-toast.tx-toast--error {
  background: var(--danger);
  color: #fff;
}
.tx-toast.tx-toast--error::before {
  content: '!';
  color: #fff;
}
.tx-toast.is-visible {
  opacity: 1;
  transform: translateY(0);
}
@media (max-width: 540px) {
  .tx-toast {
    bottom: 1rem;
    right: 1rem;
    left: 1rem;
    text-align: center;
  }
}

/* Manuelle-Kategorie-Indikatoren (Listing-Pill + Edit-Card-Tag + Reset-Link) */
.tx-pill--locked {
  display: inline-flex;
  align-items: center;
  gap: .2rem;
}
.tx-pill--locked .icon {
  width: 10px;
  height: 10px;
  opacity: .55;
}
.tx-cat-manual-tag {
  display: inline-block;
  margin-left: .35rem;
  padding: .04rem .35rem;
  background: var(--surface-3);
  color: var(--text-muted);
  font-size: .62rem;
  font-weight: 500;
  text-transform: none;
  letter-spacing: 0;
  border-radius: 3px;
  vertical-align: middle;
}
.tx-cat-reset-link {
  background: none;
  border: none;
  color: var(--text-muted);
  font-size: .76rem;
  padding: .25rem 0;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: .3rem;
  margin-top: .2rem;
  align-self: flex-start;
  text-decoration: none;
}
.tx-cat-reset-link:hover {
  color: var(--text);
  background: none;
}
.tx-cat-reset-link:hover .icon { opacity: 1; }
.tx-cat-reset-link .icon {
  width: 11px;
  height: 11px;
  opacity: .6;
}

/* === Buchungs-Detail Edit-Form (Card-Layout, Form-Konsolidierung) === */
.tx-edit {
  padding: .85rem 1rem 1rem;
  border-top: 1px solid var(--border-3);
  background: var(--surface-2);
  display: flex;
  flex-direction: column;
  gap: .75rem;
  font-size: .88rem;
}
.tx-edit-row { display: grid; gap: .75rem; }
.tx-edit-row--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.tx-edit-row--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.tx-edit-row--zweck { grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); }

.tx-edit-card {
  background: var(--surface);
  border: 1px solid var(--border-2);
  border-radius: 10px;
  padding: .65rem .8rem;
  display: flex;
  flex-direction: column;
  gap: .5rem;
  min-width: 0;
}
.tx-edit-card--ro {
  flex-direction: row;
  align-items: flex-start;
  gap: .65rem;
}
.tx-edit-card__icon {
  flex-shrink: 0;
  width: 32px;
  height: 32px;
  border-radius: 8px;
  background: var(--surface-3);
  color: var(--text-muted);
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.tx-edit-card__icon .icon { width: 18px; height: 18px; }
.tx-edit-card__main { min-width: 0; flex: 1; }
.tx-edit-card__head {
  display: flex;
  align-items: center;
  gap: .55rem;
  min-width: 0;
}
.tx-edit-card__label {
  font-size: .72rem;
  text-transform: uppercase;
  color: var(--text-muted);
  letter-spacing: .04em;
  font-weight: 500;
  margin-bottom: .1rem;
}
.tx-edit-card__optional {
  text-transform: none;
  letter-spacing: 0;
  color: var(--text-dim);
  font-weight: 400;
  margin-left: .2rem;
}
.tx-edit-card__value {
  font-size: .95rem;
  font-weight: 500;
  color: var(--text);
  word-break: break-word;
}
.tx-edit-card__value--text {
  font-size: .88rem;
  font-weight: 400;
  line-height: 1.35;
  white-space: pre-line;
}
.tx-edit-card__value.neg { color: var(--danger); font-variant-numeric: tabular-nums; }
.tx-edit-card__value.pos { color: var(--pos); font-variant-numeric: tabular-nums; }
.tx-edit-card__sub {
  font-size: .78rem;
  color: var(--text-muted);
  margin-top: .15rem;
  font-variant-numeric: tabular-nums;
  letter-spacing: .02em;
  word-break: break-all;
}
.tx-edit-card__row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: .5rem;
  align-items: center;
}
.tx-edit-card__row--single { grid-template-columns: minmax(0, 1fr); }
.tx-edit-card__select {
  min-width: 0;
  width: 100%;
  font-size: .9rem;
  padding: .4rem .55rem;
}
.tx-edit-card--toggle {
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
  gap: .75rem;
  cursor: pointer;
}
.tx-edit-card--toggle .tx-edit-card__head { flex: 1; min-width: 0; }
.tx-edit-card__splits {
  display: flex;
  flex-direction: column;
  gap: .4rem;
}
.tx-edit-card__split-cta { align-self: flex-start; }

/* Notiz-Feld: Card-Pattern (Icon + Label oben), Textarea + Counter darunter */
.tx-edit-card__note-input {
  width: 100%;
  border: 1px solid var(--border-2);
  border-radius: 8px;
  background: var(--surface-2);
  padding: .55rem .65rem;
  resize: vertical;
  min-height: 2.4rem;
  font-family: inherit;
  font-size: .9rem;
  line-height: 1.45;
  color: var(--text);
  outline: none;
  box-sizing: border-box;
  display: block;
}
.tx-edit-card__note-input::placeholder { color: var(--text-dim); }
.tx-edit-card__note-input:focus {
  border-color: var(--accent);
  background: var(--surface);
}
.tx-edit-card__note-counter {
  text-align: right;
  font-size: .72rem;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
  margin-top: -.2rem;
}

/* Toggle (iOS-Style) */
.tx-toggle {
  position: relative;
  display: inline-block;
  width: 42px;
  height: 24px;
  flex-shrink: 0;
}
.tx-toggle__input {
  position: absolute;
  opacity: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  cursor: pointer;
  z-index: 2;
}
.tx-toggle__slider {
  position: absolute;
  inset: 0;
  background: var(--border);
  border-radius: 999px;
  transition: background .18s;
}
.tx-toggle__slider::before {
  content: '';
  position: absolute;
  top: 2px;
  left: 2px;
  width: 20px;
  height: 20px;
  background: #fff;
  border-radius: 50%;
  transition: transform .18s;
  box-shadow: 0 1px 2px rgba(0, 0, 0, .15);
}
.tx-toggle__input:checked + .tx-toggle__slider { background: var(--accent); }
.tx-toggle__input:checked + .tx-toggle__slider::before { transform: translateX(18px); }
.tx-toggle__input:focus-visible + .tx-toggle__slider {
  box-shadow: 0 0 0 3px var(--accent-bg);
}

/* Footer-Bar mit Collapse-Link + Speichern */
.tx-edit-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin-top: .25rem;
  padding-top: .65rem;
  border-top: 1px solid var(--border-3);
  flex-wrap: wrap;
}
.tx-edit-footer__collapse {
  display: inline-flex;
  align-items: center;
  gap: .35rem;
  background: none;
  border: none;
  color: var(--accent);
  font-size: .88rem;
  padding: .35rem .25rem;
  cursor: pointer;
}
.tx-edit-footer__collapse:hover { text-decoration: underline; }
.tx-edit-footer__collapse .icon { width: 14px; height: 14px; }
.tx-edit-footer__save {
  font-size: .92rem;
  padding: .5rem 1.1rem;
}
.tx-edit-unsplit {
  padding: 0 1rem .65rem;
  background: var(--surface-2);
  border-top: 0;
  text-align: right;
  margin-top: -.5rem;
}

@media (max-width: 720px) {
  .tx-edit-row--3,
  .tx-edit-row--2,
  .tx-edit-row--zweck { grid-template-columns: 1fr; }
  .tx-meta { font-size: .72rem; }
  .tx-edit { padding: .65rem .65rem .8rem; }
  .tx-edit-card { padding: .55rem .65rem; }
}

/* Split UI */
.split-chip { display: flex; align-items: center; gap: .4rem; flex-wrap: wrap; font-size: .85rem; }

/* Split-Children im Buchungs-Listing (Finanzguru-Style):
   Parent bleibt eine collapsable details-Row; die zugehörigen Splits werden
   als eigene, eingerückte Zeilen direkt darunter angezeigt mit
   Tree-Connector-Linie links. */
.tx-row--has-splits { border-bottom: none; }
.tx-split-child {
  display: grid;
  grid-template-columns: 28px 32px minmax(0, 1fr) auto;
  gap: .6rem;
  align-items: center;
  padding: .45rem .3rem .45rem 0;
  position: relative;
  font-size: .92rem;
}
.tx-split-child + .tx-split-child,
.tx-row--has-splits + .tx-split-child { border-top: 1px dashed var(--border-3); }
.tx-split-child--last { border-bottom: 1px solid var(--border-2); }
.tx-split-child .tx-icon { width: 26px; height: 26px; font-size: .78rem; }
.tx-split-child .tx-name { font-size: .9rem; font-weight: 500; color: var(--text-secondary); }
.tx-split-child .tx-amount { font-size: .92rem; opacity: .92; }
.tx-split-connector {
  width: 28px; height: 100%;
  position: relative;
  align-self: stretch;
}
.tx-split-connector::before {
  /* vertikale Linie vom Parent nach unten — endet bei der letzten Child-Row */
  content: '';
  position: absolute;
  left: 14px; top: 0; bottom: 0;
  border-left: 1px solid var(--border-2);
}
.tx-split-child--last .tx-split-connector::before { bottom: 50%; }
.tx-split-connector::after {
  /* horizontaler Stub zur Icon-Spalte */
  content: '';
  position: absolute;
  left: 14px; top: 50%;
  width: 12px;
  border-top: 1px solid var(--border-2);
}
@media (max-width: 540px) {
  .tx-split-child {
    grid-template-columns: 20px 26px minmax(0, 1fr) auto;
    gap: .4rem;
    padding-left: 0;
  }
  .tx-split-connector { width: 20px; }
  .tx-split-connector::before { left: 10px; }
  .tx-split-connector::after { left: 10px; width: 8px; }
  .tx-split-child .tx-icon { width: 22px; height: 22px; font-size: .7rem; }
  .tx-split-child .tx-amount { font-size: .85rem; }
}
.split-row { display: flex; gap: .6rem; align-items: flex-start; flex-wrap: wrap; padding: .6rem 0; border-bottom: 1px solid var(--border); }
.split-row:last-child { border-bottom: none; }
.split-row label { display: flex; flex-direction: column; gap: .2rem; font-size: .85rem; color: var(--text-secondary); }
.split-row input[type=number], .split-row input[type=text] { width: 120px; }
.split-row .cat-select { width: 200px; }
/* Teilbuchungs-Karten (Finanzguru-Style) */
.teilbuchung-card {
  margin-bottom: .8rem;
}
.teilbuchung-card.original {
  background: var(--surface-2);
}
.teilbuchung-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: .4rem;
}
.teilbuchung-title {
  margin: 0;
  font-size: 1rem;
  font-weight: 600;
}
.teilbuchung-card .field {
  display: block;
  margin: .55rem 0 0;
}
.teilbuchung-card .field-label {
  display: block;
  font-size: .78rem;
  color: var(--text-muted);
  margin-bottom: .25rem;
}
.teilbuchung-card .amount-wrap {
  position: relative;
}
.teilbuchung-card .amount-wrap input {
  width: 100%;
  padding-right: 2rem;
}
.teilbuchung-card .amount-suffix {
  position: absolute;
  right: .8rem;
  top: 50%;
  transform: translateY(-50%);
  color: var(--text-muted);
  pointer-events: none;
}
.teilbuchung-card input.amount-display[disabled] {
  background: transparent;
  border: 1px solid transparent;
  font-weight: 600;
  font-size: 1.05rem;
  color: var(--text);
  cursor: default;
}
.teilbuchung-card .locked-cat {
  padding: .55rem .75rem;
  border: 1px solid transparent;
  border-radius: 8px;
  font-size: .95rem;
  color: var(--text);
  background: var(--surface);
}
.teilbuchung-card .cat-select,
.teilbuchung-card input[type="number"],
.teilbuchung-card input[type="text"] {
  width: 100%;
}
.teilbuchung-add {
  display: block;
  width: 100%;
  margin: .4rem 0 .6rem;
  border: 1px dashed var(--border);
  background: transparent;
}
.teilbuchung-add:hover {
  border-color: var(--accent);
}
#original-remainder.rest-neg { color: var(--danger); }

/* === Vertragsdetailseite — Makeover ===================================== */

/* 2-Spalten-Layout: links Detail-Card, rechts Buchungsliste. Mobil gestapelt. */
.contract-detail-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr);
  gap: var(--space-4);
  align-items: start;
}
@media (max-width: 760px) {
  .contract-detail-grid { grid-template-columns: 1fr; }
}
/* Vertragsinfo-Felder immer 2-spaltig (auch Mobil) — sonst viel verschenkter
   Platz. Scoped auf die Vertragsdetail-Card, damit andere .detail-grid (Budget)
   unberuehrt bleiben. */
.contract-detail-grid .detail-grid { grid-template-columns: 1fr 1fr; }

/* Zurueck-Link ueber der Card. */
.contract-back { display: inline-block; margin-bottom: var(--space-3); }

/* Kopf: Icon-Kachel + Titel/Untertitel + Bearbeiten-Button (an Budget-Card .b-icon
   angelehnt, etwas groesser). Trennlinie zur Betrags-Zeile darunter. */
.contract-head {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding-bottom: var(--space-4);
  margin-bottom: var(--space-4);
  border-bottom: 1px solid var(--border-2);
}
.contract-head-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 3rem;
  height: 3rem;
  border-radius: var(--r-md);
  background: var(--surface-2);
  color: var(--text-muted);
  font-size: 1.4rem;
  flex: 0 0 auto;
}
.contract-head-icon .icon { width: 1.6rem; height: 1.6rem; }
.contract-head-text { min-width: 0; flex: 1 1 auto; }
.contract-head-title {
  margin: 0;
  font-size: 1.4rem;
  font-weight: 700;
  line-height: 1.2;
  letter-spacing: -.01em;
}
.contract-head-sub { font-size: .85rem; margin-top: .1rem; }
.contract-head .icon-btn-edit { flex: 0 0 auto; align-self: flex-start; }

/* Hero: grosser Betrag + Label + Status-Pill. Farbe vorzeichen-abhaengig:
   neutral (--text) bei Ausgabe, .pos (gruen) bei Einnahme — nie rot. */
.contract-hero {
  display: flex;
  align-items: baseline;
  gap: .6rem;
  flex-wrap: wrap;
  margin-bottom: var(--space-4);
}
.contract-hero .hero-amount {
  font-size: 1.2rem;
  font-weight: 700;
  line-height: 1.2;
  color: var(--text);
  font-variant-numeric: tabular-nums;
  letter-spacing: -.01em;
}
.contract-hero .hero-amount.pos { color: var(--pos); }
.contract-hero .hero-label { font-size: .8rem; color: var(--text-muted); }
.contract-hero .hero-status { margin-left: auto; }

/* Optionale Sektionen (Kontakt, Dokumente, Verlaengerung) mit dezenter
   Trennlinie zur Detail-Grid darueber. */
.contract-section {
  margin-top: var(--space-4);
  padding-top: var(--space-4);
  border-top: 1px solid var(--border-2);
}
.contract-section > .field-label { margin-bottom: .4rem; }

/* Kontakt-/Dokument-Buttons (an .btn-secondary angelehnt, als <a>). */
.contract-links { display: flex; flex-wrap: wrap; gap: .5rem; }
.contract-link-btn {
  display: inline-flex;
  align-items: center;
  gap: .4rem;
  min-height: 40px;
  padding: .45rem .7rem;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--surface-2);
  color: var(--text);
  font-size: .9rem;
  text-decoration: none;
}
.contract-link-btn:hover { background: var(--accent-bg); border-color: var(--accent); }
.contract-link-btn .icon { width: 1.1em; height: 1.1em; }

/* Toggle-Switch (Reminder). Per HTMX server-getoggelt: das <input> ist
   dekorativ/disabled, der State kommt serverseitig im Partial. Geklickt wird
   das umschliessende <label> mit hx-post. */
.toggle-switch {
  display: inline-flex;
  align-items: center;
  gap: .6rem;
  cursor: pointer;
  background: none;
  padding: 0;
}
.toggle-switch input { position: absolute; opacity: 0; width: 0; height: 0; }
.toggle-track {
  position: relative;
  width: 42px;
  height: 24px;
  flex: 0 0 auto;
  background: var(--surface-2);
  border: 1px solid var(--border-input);
  border-radius: var(--r-pill);
  transition: background var(--duration-fast, 150ms) ease,
              border-color var(--duration-fast, 150ms) ease;
}
.toggle-track::after {
  content: '';
  position: absolute;
  top: 2px;
  left: 2px;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 1px 2px rgba(0, 0, 0, .2);
  transition: transform var(--duration-fast, 150ms) ease;
}
.toggle-switch input:checked + .toggle-track {
  background: var(--pos);
  border-color: var(--pos);
}
.toggle-switch input:checked + .toggle-track::after { transform: translateX(18px); }
.toggle-switch input:focus-visible + .toggle-track {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.toggle-label { font-size: .9rem; color: var(--text); }
.contract-renewal-date { font-size: 1rem; font-weight: 600; margin-bottom: .55rem; }

