diff --git a/docs/superpowers/specs/2026-09-28-ausbaustufe-11-design.md b/docs/superpowers/specs/2026-09-28-ausbaustufe-11-design.md
new file mode 100644
index 0000000..1ab0e5e
--- /dev/null
+++ b/docs/superpowers/specs/2026-09-28-ausbaustufe-11-design.md
@@ -0,0 +1,274 @@
+# Design — Ausbaustufe 11: Szenario-Löschen, Grafana-Korrekturen, Tabellensortierung (v0.11.0)
+
+> Status: Design im Chat vom Nutzer freigegeben (2026-09-28), Spec zur
+> Durchsicht. Anlass: vier Nutzerwünsche — (1) Szenarien lassen sich nicht
+> löschen, (2) „Szenario-Vergleich" zeigt vor heute falsche Werte,
+> (3) Planungs-Gegenstück zu „Monat nach Kategorie" fehlt, (4) Tabellen
+> sollen per Spaltenkopf sortierbar sein.
+
+## Nutzerentscheidungen (2026-09-28)
+
+- Szenario-Vergleich vor heute: **Ist-Gesamtsaldo als eigene Serie „Ist"**,
+ Szenario-Kurven erst ab heute.
+- Veraltete Szenarien: **automatisch neu rechnen** beim Seitenaufruf.
+- Planungs-Diagramm: **Einnahmen + Ausgaben mit Vorzeichen** (Semantik wie
+ „Monat nach Kategorie").
+- Kreditraten **aller** Kredite im Planungs-Diagramm als eigene Kategorie
+ **„Kreditraten"**, auch wenn keinem Szenario zugeordnet.
+- Sortierung: **alle** Tabellen (Aufwand moderat, keine Einschränkung nötig).
+
+## Befund (Ist-Zustand)
+
+- `DELETE /api/scenarios/{id}` existiert bereits inkl. expliziter
+ Abräumung von `ProjectionPoint`, `ProjectionResult`, `ScenarioLoan`,
+ `ScenarioModifier`, `ScenarioPlannedItem` (`routers/scenarios.py`). Es
+ fehlt nur das GUI-Element.
+- `projection_points` sind Momentaufnahmen des letzten „Durchrechnen"
+ (Serie ab `start_date + 1` bis `start_date + horizon_days`,
+ `engine/projection.py::project`). Wurde ein Szenario vor Wochen
+ gerechnet, zeigt `v_projection` für die Zeit bis heute eine alte Prognose
+ statt Ist-Werten; auch der Zukunftsteil startet vom damaligen Saldo.
+- „Monat nach Kategorie" (Panel 3) liest `v_monthly_by_category` =
+ bestätigte Buchungen, also nur Vergangenheit.
+- Tabellen: 14 statische `
` in 8 Templates (inkl. htmx-Fragmente
+ `_import_list.html`, `_preview_table.html`), plus der per JS erzeugte
+ Tilgungsplan. `/buchungen` ist serverseitig paginiert (50/Seite,
+ Standard-Reihenfolge `booking_date, id` aufsteigend).
+
+## 1. Szenario löschen
+
+- `szenarien.html`: im Szenario-Kopf (`
`) neben
+ „Bearbeiten" ein Formular
+ `hx-delete="/api/scenarios/{{ sc.id }}" hx-swap="none"`,
+ `hx-confirm="Szenario „{{ sc.name }}“ mit allen Einträgen wirklich löschen?"`,
+ bei Erfolg `window.location.reload()` — Muster wie die bestehenden
+ Einmalzahlungs-Löschbuttons.
+- Keine API-Änderung.
+- Tests: GUI-Seite enthält den Button mit korrektem `hx-delete`-Ziel;
+ (vorhandene API-Löschtests bleiben gültig, ggf. um GUI-Roundtrip
+ ergänzen: nach DELETE ist das Szenario nicht mehr auf `/szenarien`).
+
+## 2. Szenario-Vergleich korrigieren
+
+### 2a. View `v_projection` (`models/views.py`)
+
+Neue Definition, Spalten unverändert (`scenario, day, balance`):
+
+1. **Ist-Serie:** `SELECT 'Ist', day, balance FROM v_balance_total`
+ (reicht bis `CURRENT_DATE`).
+2. **Szenario-Startpunkt:** je Szenario mit mindestens einem
+ Projektionspunkt eine Zeile `(s.name, CURRENT_DATE, )`
+ — Ist-Saldo aus `v_balance_total` am `CURRENT_DATE`. Dadurch beginnt
+ jede Szenario-Kurve lückenlos am Ist-Endpunkt.
+3. **Projektion:** `projection_points` mit `day > CURRENT_DATE`.
+ Veraltete Punkte ≤ heute werden damit nie angezeigt — auch dann nicht,
+ wenn ein Szenario (noch) nicht neu gerechnet wurde.
+
+Kombination per `UNION ALL`. `create_views` behandelt die geänderte
+Definition bereits (CREATE OR REPLACE, Fallback DROP+CREATE). Da `v_projection`
+auf `v_balance_total` aufbaut, muss es in `VIEWS` nach `v_balance_total`
+stehen (ist bereits so; Reihenfolge beibehalten). Achtung: DROP … CASCADE
+von `v_balance_total` im Fallback-Pfad würde `v_projection` mitreißen — da
+`v_projection` danach ohnehin neu angelegt wird, ist das unkritisch.
+
+Bekannte Grenze: ein Szenario namens „Ist" würde mit der Ist-Serie
+verschmelzen. Wird hingenommen (keine Namensvalidierung eingeführt).
+
+### 2b. Automatisches Neurechnen (`services/projection_service.py`)
+
+Neue Funktion `refresh_stale_projections(session, today: date) -> int`
+(Rückgabe: Anzahl neu gerechneter Szenarien):
+
+- Kandidaten: Szenarien mit vorhandenem `ProjectionResult` **und**
+ `min(ProjectionPoint.day) <= today` (d.h. Rechen-Start lag vor heute).
+ Maßstab ist der gespeicherte Projektionsbeginn, nicht `computed_at`
+ (UTC-Zeitstempel, würde um Mitternacht falsch schneiden).
+- Nie gerechnete Szenarien bleiben unberührt („Noch nicht durchgerechnet").
+- Neu gerechnet mit `run_projection(session, scenario,
+ settings.horizon_days, today)` — identisch zum Button „Durchrechnen".
+- Serialisierung: auf Postgres zu Beginn `SELECT pg_advisory_xact_lock()`,
+ danach Staleness neu prüfen (zwei parallele Seitenaufrufe dürfen nicht
+ doppelt Punkte schreiben). Auf SQLite (Tests) entfällt der Lock.
+- Aufruf in `routers/gui.py` beim GET von `/` (Startseite, bettet Grafana
+ ein) und `/szenarien`, **vor** dem Rendern. Fehler im Refresh dürfen die
+ Seite nicht brechen: `try/except Exception` → `session.rollback()`,
+ Log-Warnung, Seite rendert mit dem vorhandenen Stand.
+- „Durchrechnen" bleibt nötig nach Änderungen am selben Tag (ein heute
+ gerechnetes Szenario gilt nicht als veraltet).
+
+Tests (SQLite, `run_projection` real):
+- Szenario mit Start gestern → wird neu gerechnet, erster Punkt = heute+1.
+- Szenario mit Start heute → unverändert (Zähler 0, `computed_at` gleich).
+- Nie gerechnetes Szenario → bleibt ohne Result.
+- GUI: GET `/szenarien` löst den Refresh aus; ein erzwungener Fehler im
+ Refresh ergibt trotzdem 200.
+- View-Semantik (Postgres-only) wird im Fable-Gate in einer Wegwerf-DB
+ geprüft: Ist-Serie endet heute, Szenario-Serien beginnen heute mit dem
+ Ist-Saldo, keine Szenario-Punkte vor heute.
+
+## 3. Neues Diagramm „Planung nach Kategorie"
+
+### 3a. Tabelle `planning_monthly` (`models/tables.py` + Alembic-Migration)
+
+| Spalte | Typ |
+|---|---|
+| `month` | `Date` (Monatserster), PK-Teil |
+| `category` | `String(100)`, PK-Teil |
+| `total` | `MONEY` (`Numeric(12,2)`) |
+
+Composite Primary Key `(month, category)`. Migration mit up + down.
+Abgeleitete Daten (jederzeit neu erzeugbar) — kein Backup-Sonderfall.
+
+### 3b. Befüllung (`services/planning_chart.py`)
+
+`refresh_planning_chart(session, today: date) -> None`:
+
+- Fenster: `window_start` = 1. des laufenden Monats,
+ `window_end` = `today + settings.horizon_days`.
+- **Wiederkehrende Posten** (`RecurringItem`): Termine über
+ `engine/recurrence.py::occurrences(rhythm, due_day, window_start,
+ window_end, start_date, end_date)` — dieselbe Logik wie Projektion und
+ „Nächste anstehende Posten". Betrag je Termin = `amount`.
+- **Einmalposten** (`PlannedItem`): `window_start <= due <= window_end`.
+- Kategorie: `Category.name` bzw. `"unkategorisiert"` bei
+ `category_id IS NULL` (wie `v_monthly_by_category`).
+- **Kreditraten:** für **alle** `Loan` der Tilgungsplan
+ `engine/loans.py::loan_schedule(...)`; jede Rate mit
+ `window_start <= due <= window_end` geht mit `Installment.payment`
+ **unverändert** in Kategorie `"Kreditraten"` ein — `payment` ist bereits
+ negativ (`-(interest + principal_part)`), identisch zur Verwendung in
+ `build_cashflows`. Die Auszahlung (`payout_date`, `principal`) zählt
+ **nicht** (keine Rate).
+- Aggregation in Python mit `Decimal` je (Monatserster, Kategorie);
+ Zeilen mit Summe 0 entfallen.
+- Schreiben: gesamte Tabelle in **einer** Transaktion löschen und neu
+ befüllen; Serialisierung wie 2b (`pg_advisory_xact_lock`, eigene
+ Konstante).
+- Aufruf in `routers/gui.py` beim GET von `/` und `/planung` (jede
+ Planung-Änderung lädt die Seite neu → Diagramm stets aktuell). Fehler
+ → rollback + Log, Seite rendert trotzdem (wie 2b).
+- Szenario-Modifikatoren fließen **nicht** ein (reine Planung).
+
+Tests (Handrechnung, künstliche Werte — keine Echtdaten):
+- monatlicher Posten mit Kategorie über Monatsgrenzen, quartalsweiser und
+ jährlicher Posten mit `start_date` (Phase), Posten mit `end_date`
+ mitten im Fenster, Posten ohne Kategorie → „unkategorisiert".
+- Einmalposten innerhalb/außerhalb des Fensters.
+- Kredit (annuity): Raten negativ unter „Kreditraten", Auszahlung fehlt.
+- Zwei Aufrufe hintereinander → keine Duplikate (Idempotenz).
+- GUI: GET `/planung` befüllt die Tabelle; Refresh-Fehler → trotzdem 200.
+
+### 3c. Grafana-Panel (`grafana/dashboards/finanzen.json`)
+
+- Neues Panel `id: 5`, `type: barchart`, Titel **„Planung nach Kategorie"**,
+ Optionen/`fieldConfig` wie Panel 3 (gestapelt, `currencyEUR`).
+- Query: `SELECT month AS time, category AS metric, total AS value FROM
+ planning_monthly ORDER BY month`.
+- Platzierung: direkt unter „Monat nach Kategorie": `gridPos
+ {h: 8, w: 12, x: 0, y: 16}`.
+- Dashboard-Zeitfenster (`now-1y` … `now+19M`) deckt den Horizont bereits
+ ab (Commit 224baba); keine Änderung.
+- Test: JSON-Strukturtest (Panel vorhanden, Query referenziert
+ `planning_monthly`, keine gridPos-Überlappung).
+
+## 4. Tabellen sortieren
+
+### 4a. Clientseitig: `app/static/sort.js`
+
+Eingebunden in `base.html` (nach `htmx.min.js`). Initialisierung über
+`htmx.onLoad(el => …)` — greift dadurch sowohl beim Seitenaufbau als auch
+bei per htmx nachgeladenen Fragmenten (`_import_list.html`,
+`_preview_table.html`).
+
+- Erfasst werden alle `table` mit `thead`, außer `table[data-sort-mode="server"]`
+ und bereits initialisierte Tabellen (Markierung per Attribut).
+- Jeder `th` mit nicht-leerem Text bekommt einen
+ `