"use client";

import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import {
  BarChart3,
  ChevronDown,
  ChevronRight,
  Copy,
  FileText,
  FileSpreadsheet,
  FolderKanban,
  CalendarClock,
  GitBranch,
  Layers,
  LayoutDashboard,
  Menu,
  Pin,
  PinOff,
  PiggyBank,
  Plus,
  Search,
  SlidersHorizontal,
  Sparkles,
  Trash2,
  X,
} from "lucide-react";
import { PlanView } from "@/components/PlanView";
import { Dashboard } from "@/components/Dashboard";
import { MonteCarloDialog } from "@/components/MonteCarloDialog";
import { SensitivityDialog } from "@/components/SensitivityDialog";
import { LiveSimDialog } from "@/components/LiveSimDialog";
import { ActualsDialog } from "@/components/ActualsDialog";
import { PlanDashboardView, ScenarioListView, AnalysesView } from "@/components/PlanViews";
import { SavedAnalysisView } from "@/components/SavedAnalysisView";
import { ReportsView } from "@/components/ReportsView";
import { buildViews } from "@/lib/dataview";
import type { ActualsSetInput, ElementOrigin } from "@/lib/actuals";
import { VersionHistoryDialog } from "@/components/VersionHistoryDialog";
import { SpecView } from "@/components/SpecView";
import { SystemParametersView } from "@/components/SystemParametersView";
import { PlanTraceDialog } from "@/components/DetailView";
import { computePlan } from "@/lib/calculations";
import { ProfileMenu } from "@/components/ProfileMenu";
import { Tour, TOUR_DONE_KEY } from "@/components/Tour";import { CommandPalette, type PaletteAction } from "@/components/CommandPalette";
import { PlanProfileFields, emptyProfileDraft, type ProfileDraft } from "@/components/PlanProfileFields";
import {
  Button,
  ConfirmProvider,
  EmptyState,
  Modal,
  PlanSkeleton,
  ToastProvider,
  useConfirm,
  useToast,
} from "@/components/ui";

// Angepinnte Menueleiste: eine Arbeitsgewohnheit des Benutzers, keine Eigenschaft des Plans.
const RAIL_PINNED_KEY = "fpt.rail.pinned";
import { api } from "@/lib/api-client";
import { computeScenarioDiff } from "@/lib/diff";
import type { PlanInput, PlanListItem, ScenarioMeta } from "@/lib/types";
import type { PlanComputed } from "@/lib/calculations";

interface ScenarioDetail {
  plan: PlanInput; // das Szenario selbst (berechenbare Einheit)
  computed: PlanComputed;
  base: PlanInput | null; // Eltern-Szenario als Vergleichsbasis
  meta: ScenarioMeta & { planName: string };
  // Effektive Werte des PLANS plus die Element-Herkunft aller Szenarien -- daraus entsteht
  // im Browser der zweite Rechenlauf (siehe lib/dataview.ts).
  actuals: ActualsSetInput[];
  elementOrigins: ElementOrigin[];
}

// Die Provider (Toast, Bestätigung) müssen UM die Shell liegen, damit deren Hooks
// innerhalb funktionieren.
export function AppShell({ username }: { username: string }) {
  return (
    <ToastProvider>
      <ConfirmProvider>
        <AppShellInner username={username} />
      </ConfirmProvider>
    </ToastProvider>
  );
}

function AppShellInner({ username }: { username: string }) {
  const toast = useToast();
  const confirm = useConfirm();

  const [plans, setPlans] = useState<PlanListItem[]>([]);
  const [selectedScenarioId, setSelectedScenarioId] = useState<string | null>(null);
  const [detail, setDetail] = useState<ScenarioDetail | null>(null);
  const [loading, setLoading] = useState(true);
  const [sidebarOpen, setSidebarOpen] = useState(false);
  const [showEmptyPlanDialog, setShowEmptyPlanDialog] = useState(false);
  const [copyFrom, setCopyFrom] = useState<ScenarioMeta | null>(null);
  const [showSpec, setShowSpec] = useState(false);
  const [showCharts, setShowCharts] = useState(false);
  const [showMonteCarlo, setShowMonteCarlo] = useState(false);
  const [showSensitivity, setShowSensitivity] = useState(false);
  const [showLiveSim, setShowLiveSim] = useState(false);
  const [showHistory, setShowHistory] = useState(false);
  const [showActuals, setShowActuals] = useState(false);
  // Plan-Ebene: Dashboard / Szenarien-Liste / Analysen. Null = kein Plan-View aktiv.
  const [planNav, setPlanNav] = useState<{ planId: string; tab: "dashboard" | "scenarios" | "actuals" | "analyses" | "reports" } | null>(null);
  const [savedAnalysisId, setSavedAnalysisId] = useState<string | null>(null);
  const [showSystemParams, setShowSystemParams] = useState(false);
  const [showPlanTraces, setShowPlanTraces] = useState(false);
  const [showPalette, setShowPalette] = useState(false);
  // Sprungmarke in die SPEZIFIKATION, gesetzt aus einem Rechenweg heraus.
  const [specAnchor, setSpecAnchor] = useState<string | null>(null);
  // Erhoehen erzwingt ein Neuladen der Szenario-Liste (bleibt bei einer Loeschung montiert).
  const [scenarioListKey, setScenarioListKey] = useState(0);

  // Menueleiste links: eingeklappt auf eine Icon-Schiene (56 px) statt 240 px.
  //
  // Beim Ueberfahren faehrt sie aus und SCHIEBT den Inhalt -- sie legt sich nicht darueber.
  // Der erste Anlauf (0.42) liess sie als Overlay ausfahren, um im Ruhezustand keinen Platz
  // zu kosten; dabei verschwand aber jedes Mal ein Streifen des Inhalts, und das las sich wie
  // ein Fehler. Aufgeklappt sieht es damit genau so aus wie angepinnt, und das Anpinnen
  // entscheidet nur noch, ob es so BLEIBT. Die Wahl gehoert zum Benutzer und ueberlebt die
  // Sitzung.
  const [railPinned, setRailPinned] = useState(false);
  const [railHover, setRailHover] = useState(false);
  const [planMenuOpen, setPlanMenuOpen] = useState(false);
  useEffect(() => {
    try {
      // eslint-disable-next-line react-hooks/set-state-in-effect -- einmalige Initialisierung
      if (localStorage.getItem(RAIL_PINNED_KEY) === "1") setRailPinned(true);
    } catch {
      /* localStorage nicht verfuegbar */
    }
  }, []);
  function toggleRailPin() {
    setRailPinned((v) => {
      try {
        localStorage.setItem(RAIL_PINNED_KEY, v ? "0" : "1");
      } catch {
        /* ignorieren */
      }
      return !v;
    });
  }
  const railWide = railPinned || railHover;

  // Tour (seit dem Layout-Umbau hier statt in PlanView -- sie liest die data-tour-Ziele im
  // DOM der Szenario-Ansicht). Zwei Auslöser:
  //   * pendingTourRef: nach JEDER Plan-Erstellung genau einmal erzwingen (Roadmap-Feedback F14).
  //   * Erst-Öffnen eines Plans mit Phasen, solange die Tour noch nie beendet wurde.
  const [showTour, setShowTour] = useState(false);
  const pendingTourRef = useRef(false);
  const autoTourCheckedRef = useRef<Set<string>>(new Set());

  useEffect(() => {
    // Bis 0.39 stand hier zusätzlich `plan.phases.length === 0`: Die alte Spotlight-Tour
    // beleuchtete echte DOM-Ziele in der Matrix und hatte ohne Phasen nichts zu zeigen. Die
    // Tour ist seit 0.36 eine Attrappe mit erfundenen Zahlen -- sie braucht nichts, und sie
    // gehört genau an den Anfang, wo man noch gar nicht weiss, wie der Plan aufgebaut ist.
    if (!detail) return;
    const sid = detail.meta.id;
    if (pendingTourRef.current) {
      pendingTourRef.current = false;
      autoTourCheckedRef.current.add(sid);
      setShowTour(true);
      return;
    }
    if (autoTourCheckedRef.current.has(sid)) return; // pro Szenario nur einmal, nicht bei Refresh
    autoTourCheckedRef.current.add(sid);
    try {
      // eslint-disable-next-line react-hooks/set-state-in-effect -- einmalige Initialisierung aus localStorage
      if (!localStorage.getItem(TOUR_DONE_KEY)) setShowTour(true);
    } catch {
      /* localStorage nicht verfügbar */
    }
  }, [detail]);

  function openSpecAt(anchor: string) {
    setSpecAnchor(anchor);
    setShowSpec(true);
    setShowSystemParams(false);
    setSelectedScenarioId(null);
    setPlanNav(null);
  }

  const loadPlans = useCallback(async () => {
    const data = await api.get<{ plans: PlanListItem[] }>("/api/plans");
    setPlans(data.plans);
    return data.plans;
  }, []);

  // silent = Hintergrund-Refresh ohne Loading-Umschaltung: die PlanView bleibt montiert,
  // damit die Scrollposition (z. B. nach dem Schliessen eines Panels) erhalten bleibt.
  const loadDetail = useCallback(async (scenarioId: string, silent = false) => {
    if (!silent) setLoading(true);
    try {
      const d = await api.get<ScenarioDetail>(`/api/scenarios/${scenarioId}`);
      setDetail(d);
      return d;
    } finally {
      if (!silent) setLoading(false);
    }
  }, []);

  useEffect(() => {
    // eslint-disable-next-line react-hooks/set-state-in-effect -- asynchroner Datenabruf beim Mount
    loadPlans().finally(() => setLoading(false));
  }, [loadPlans]);

  useEffect(() => {
    if (selectedScenarioId) {
      // eslint-disable-next-line react-hooks/set-state-in-effect -- asynchroner Datenabruf
      loadDetail(selectedScenarioId);
    }
    // Kein `else setDetail(null)`: In der Plan-Ebene (planNav) wird `detail` fürs Basisszenario
    // geladen, damit die Werkzeuge starten können -- das darf hier nicht weggeräumt werden.
  }, [selectedScenarioId, loadDetail]);

  // Cmd/Ctrl+K öffnet die Befehls-Palette.
  useEffect(() => {
    function onKey(e: KeyboardEvent) {
      if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "k") {
        e.preventDefault();
        setShowPalette((v) => !v);
      }
    }
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, []);

  function refreshCurrent() {
    if (selectedScenarioId) loadDetail(selectedScenarioId, true);
  }

  function openScenario(id: string) {
    setSelectedScenarioId(id);
    setPlanNav(null);
    setShowSpec(false);
    setShowSystemParams(false);
    setSidebarOpen(false);
  }

  // Plan-Ebene öffnen (Dashboard / Szenarien / Analysen). Räumt die Szenario- und Wissens-
  // Ansichten weg -- es kann immer nur eine Hauptansicht aktiv sein.
  function openPlanTab(planId: string, tab: "dashboard" | "scenarios" | "actuals" | "analyses" | "reports") {
    setPlanNav({ planId, tab });
    setSelectedScenarioId(null);
    setShowSpec(false);
    setShowSystemParams(false);
    setSidebarOpen(false);
  }

  // Ein Analysewerkzeug aus der Analysen-Ansicht heraus starten. Die Werkzeuge arbeiten auf
  // einem Szenario -- ohne ausgewähltes nehmen wir das Basisszenario; im Werkzeug selbst
  // lässt sich Szenario/Version weiterhin umstellen.
  async function launchTool(planId: string, kind: "CHART" | "MONTE_CARLO" | "SENSITIVITY" | "LIVESIM") {
    const plan = plans.find((p) => p.id === planId);
    const baseId = plan?.scenarios.find((s) => s.isBase)?.id ?? plan?.scenarios[0]?.id;
    if (!baseId) return;
    if (detail?.meta.id !== baseId) await loadDetail(baseId, true);
    if (kind === "CHART") setShowCharts(true);
    else if (kind === "MONTE_CARLO") setShowMonteCarlo(true);
    else if (kind === "SENSITIVITY") setShowSensitivity(true);
    else setShowLiveSim(true);
  }

  // Effektive-Werte-Ansicht (Plan-Ebene, eingebettet). Lädt das Basisszenario, das der
  // eingebettete Dialog als Ausgangspunkt braucht, und öffnet dann den Tab.
  async function openActualsTab(planId: string) {
    const plan = plans.find((p) => p.id === planId);
    const baseId = plan?.scenarios.find((s) => s.isBase)?.id ?? plan?.scenarios[0]?.id;
    if (baseId && detail?.meta.id !== baseId) await loadDetail(baseId, true);
    openPlanTab(planId, "actuals");
  }

  async function handleDeletePlan(id: string) {
    const plan = plans.find((p) => p.id === id);
    const ok = await confirm({
      title: "Plan löschen?",
      message: `«${plan?.name ?? "Plan"}» wird mit allen Szenarien unwiderruflich gelöscht.`,
      confirmLabel: "Endgültig löschen",
      danger: true,
    });
    if (!ok) return;
    await api.delete(`/api/plans/${id}`);
    toast("success", "Plan gelöscht.");
    const rest = await loadPlans();
    if (!rest.some((p) => p.scenarios.some((s) => s.id === selectedScenarioId))) {
      setSelectedScenarioId(null);
    }
  }

  async function handleDeleteScenario(s: ScenarioMeta) {
    const ok = await confirm({
      title: "Szenario löschen?",
      message: `«${s.name}» wird unwiderruflich gelöscht. Untergeordnete Szenarien bleiben bestehen.`,
      confirmLabel: "Endgültig löschen",
      danger: true,
    });
    if (!ok) return;
    try {
      await api.delete(`/api/scenarios/${s.id}`);
      toast("success", "Szenario gelöscht.");
    } catch (e) {
      toast("error", e instanceof Error ? e.message : "Löschen fehlgeschlagen.");
      return;
    }
    await loadPlans();
    // Die Szenario-Liste bleibt beim Löschen aus der Seitenleiste montiert -- ohne dieses
    // Signal zeigte sie den gelöschten Eintrag weiter.
    setScenarioListKey((k) => k + 1);
    if (selectedScenarioId === s.id) setSelectedScenarioId(null);
  }

  const activePlan =
    plans.find((p) => p.scenarios.some((s) => s.id === selectedScenarioId)) ??
    (planNav ? plans.find((p) => p.id === planNav.planId) ?? null : null);

  // Plan-Sicht und Ist-Sicht in einem Zug. Ohne erfasste Ist-Werte bleibt `actual` null und
  // die Oberflaeche verhaelt sich exakt wie bisher.
  const views = useMemo(
    () =>
      detail ? buildViews(detail.plan, detail.actuals ?? [], detail.elementOrigins ?? []) : null,
    [detail]
  );

  // Aktionen der Befehls-Palette -- kontextabhängig (Analysen nur bei offenem Szenario).
  const paletteActions = useMemo<PaletteAction[]>(() => {
    const base: PaletteAction[] = [
      { id: "a-new", label: "Neuen Plan erstellen", hint: "Aktion", run: () => setShowEmptyPlanDialog(true) },
      { id: "a-home", label: "Übersicht öffnen", hint: "Aktion", run: () => { setSelectedScenarioId(null); setPlanNav(null); setShowSpec(false); setShowSystemParams(false); } },
      { id: "a-spec", label: "So rechnet FPT (Spezifikation)", hint: "Aktion", run: () => { setShowSpec(true); setSpecAnchor(null); setShowSystemParams(false); setSelectedScenarioId(null); setPlanNav(null); } },
      { id: "a-params", label: "Systemparameter", hint: "Aktion", run: () => { setShowSystemParams(true); setShowSpec(false); setSelectedScenarioId(null); setPlanNav(null); } },
    ];
    if (detail && selectedScenarioId) {
      base.unshift(
        { id: "a-charts", label: "Grafiken öffnen", hint: "Aktion", run: () => setShowCharts(true) },
        { id: "a-mc", label: "Monte-Carlo-Simulation", hint: "Aktion", run: () => setShowMonteCarlo(true) },
        { id: "a-sens", label: "Einflussfaktoren berechnen", hint: "Aktion", run: () => setShowSensitivity(true) },
        { id: "a-traces", label: "Rechenwege anzeigen", hint: "Aktion", run: () => setShowPlanTraces(true) },
        { id: "a-copy", label: "Neues Szenario aus diesem", hint: "Aktion", run: () => setCopyFrom(detail.meta) }
      );
    }
    return base;
  }, [detail, selectedScenarioId]);

  const diff = detail ? computeScenarioDiff(detail.plan, detail.base) : null;

  return (
    <div className="flex min-h-screen w-full">
      {/* Linke Leiste. Eingeklappt eine Icon-Schiene; beim Ueberfahren faehrt sie als Overlay
          aus (kostet keinen Platz), angepinnt bleibt sie stehen und schiebt den Inhalt.
          Icon-only braucht zwingend Tooltip UND aria-label -- sonst ist die Schiene fuer
          Screenreader und fuer Neulinge gleichermassen stumm. */}
      {/* Linke Leiste. EINE Komponente in zwei Breiten: Die Symbole bleiben an Ort und
          Stelle, rechts daneben faehrt die Beschriftung aus. Ein Wechsel der Darstellung
          (Icons hier, Baum dort) waere kein Uebergang, sondern ein Sprung -- und genau das
          hat sich falsch angefuehlt. */}
      <div
        className="relative hidden shrink-0 lg:block"
        style={{ width: railWide ? "15rem" : "3.5rem", transition: "width 200ms" }}
        onMouseEnter={() => setRailHover(true)}
        onMouseLeave={() => {
          setRailHover(false);
          setPlanMenuOpen(false);
        }}
      >
        <Rail
          wide={railWide}
          pinned={railPinned}
          onTogglePin={toggleRailPin}
          plans={plans}
          activeScenarioId={selectedScenarioId}
          planNav={planNav}
          atHome={selectedScenarioId === null && !planNav && !showSpec && !showSystemParams}
          showSpec={showSpec}
          showSystemParams={showSystemParams}
          planMenuOpen={planMenuOpen}
          onTogglePlanMenu={() => setPlanMenuOpen((o) => !o)}
          onNewPlan={() => {
            setPlanMenuOpen(false);
            setShowEmptyPlanDialog(true);
          }}
          onDeletePlan={handleDeletePlan}
          onHome={() => {
            setSelectedScenarioId(null);
            setPlanNav(null);
            setShowSpec(false);
            setShowSystemParams(false);
          }}
          onOpenTab={(planId, tab) => {
            setPlanMenuOpen(false);
            if (tab === "actuals") void openActualsTab(planId);
            else openPlanTab(planId, tab);
          }}
          onOpenScenario={openScenario}
          onCopyScenario={setCopyFrom}
          onDeleteScenario={handleDeleteScenario}
          onSpec={() => {
            setShowSpec(true);
            setSpecAnchor(null);
            setShowSystemParams(false);
            setSelectedScenarioId(null);
            setPlanNav(null);
          }}
          onParams={() => {
            setShowSystemParams(true);
            setShowSpec(false);
            setSelectedScenarioId(null);
            setPlanNav(null);
          }}
          onSearch={() => setShowPalette(true)}
        />
      </div>

      {sidebarOpen && (
        <div className="fixed inset-0 z-40 lg:hidden">
          <div className="ui-fade absolute inset-0 bg-black/40" onClick={() => setSidebarOpen(false)} />
          <aside className="ui-slide-in absolute left-0 top-0 h-full w-72 border-r border-border bg-surface shadow-xl">
            <button
              type="button"
              onClick={() => setSidebarOpen(false)}
              aria-label="Menü schliessen"
              className="absolute right-2 top-3 rounded-md p-1.5 text-faint hover:bg-surface-2"
            >
              <X className="h-4 w-4" />
            </button>
            <Rail
              wide
              pinned
              mobile
              onTogglePin={() => setSidebarOpen(false)}
              plans={plans}
              activeScenarioId={selectedScenarioId}
              planNav={planNav}
              atHome={selectedScenarioId === null && !planNav && !showSpec && !showSystemParams}
              showSpec={showSpec}
              showSystemParams={showSystemParams}
              planMenuOpen={planMenuOpen}
              onTogglePlanMenu={() => setPlanMenuOpen((o) => !o)}
              onNewPlan={() => {
                setPlanMenuOpen(false);
                setShowEmptyPlanDialog(true);
              }}
              onDeletePlan={handleDeletePlan}
              onHome={() => {
                setSelectedScenarioId(null);
                setPlanNav(null);
                setShowSpec(false);
                setShowSystemParams(false);
                setSidebarOpen(false);
              }}
              onOpenTab={(planId, tab) => {
                setPlanMenuOpen(false);
                setSidebarOpen(false);
                if (tab === "actuals") void openActualsTab(planId);
                else openPlanTab(planId, tab);
              }}
              onOpenScenario={(id) => {
                openScenario(id);
                setSidebarOpen(false);
              }}
              onCopyScenario={setCopyFrom}
              onDeleteScenario={handleDeleteScenario}
              onSpec={() => {
                setShowSpec(true);
                setSpecAnchor(null);
                setShowSystemParams(false);
                setSelectedScenarioId(null);
                setPlanNav(null);
                setSidebarOpen(false);
              }}
              onParams={() => {
                setShowSystemParams(true);
                setShowSpec(false);
                setSelectedScenarioId(null);
                setPlanNav(null);
                setSidebarOpen(false);
              }}
              onSearch={() => setShowPalette(true)}
            />
          </aside>
        </div>
      )}

      <div className="flex min-w-0 flex-1 flex-col">
        <header className="flex items-center gap-3 border-b border-border bg-surface px-4 py-3">
          <button
            type="button"
            onClick={() => setSidebarOpen(true)}
            aria-label="Menü öffnen"
            className="rounded-lg border border-border p-2 text-muted lg:hidden"
          >
            <Menu className="h-4 w-4" />
          </button>
          <h1 className="min-w-0 flex-1 truncate text-base font-semibold text-fg">
            {showSystemParams
              ? "Systemparameter"
              : showSpec
              ? "So rechnet FPT"
              : planNav
                ? `${activePlan?.name ?? "Plan"} · ${
                    { dashboard: "Dashboard", scenarios: "Szenarien", actuals: "Effektive Werte", analyses: "Analysen", reports: "Berichte" }[
                      planNav.tab
                    ]
                  }`
                : selectedScenarioId && detail
                  ? `${detail.meta.planName} · ${detail.meta.name}`
                  : "Übersicht"}
          </h1>
          <ProfileMenu username={username} />
        </header>

        <main className="flex-1 px-4 py-6 lg:px-8">
          {showSystemParams && <SystemParametersView />}

          {showSpec && <SpecView anchor={specAnchor} />}

          {!showSpec && !showSystemParams && loading && !planNav && <PlanSkeleton />}

          {/* Plan-Ebene: Dashboard / Szenarien / Analysen */}
          {!showSpec && !showSystemParams && planNav?.tab === "dashboard" && (
            <PlanDashboardView
              planId={planNav.planId}
              onNavigate={(tab) => (tab === "actuals" ? void openActualsTab(planNav.planId) : openPlanTab(planNav.planId, tab))}
              onRenamed={() => void loadPlans()}
            />
          )}
          {!showSpec && !showSystemParams && planNav?.tab === "scenarios" && (
            <ScenarioListView
              planId={planNav.planId}
              reloadKey={scenarioListKey}
              onOpenMatrix={openScenario}
              onOpenHistory={(sid) => {
                void loadDetail(sid, true).then(() => setShowHistory(true));
              }}
              onCopyFrom={(sid) => {
                const src = activePlan?.scenarios.find((s) => s.id === sid);
                if (src) setCopyFrom(src);
              }}
              onDelete={(sid) => {
                const s = activePlan?.scenarios.find((x) => x.id === sid);
                if (s) void handleDeleteScenario(s);
              }}
            />
          )}
          {!showSpec && !showSystemParams && planNav?.tab === "actuals" && activePlan && (
            detail && detail.meta.planId === planNav.planId ? (
              <ActualsDialog
                key={planNav.planId}
                embedded
                planId={planNav.planId}
                planName={activePlan.name}
                scenarioMetas={activePlan.scenarios}
                initial={{ id: detail.meta.id, name: detail.meta.name, isBase: detail.meta.isBase, plan: detail.plan }}
                onClose={() => {}}
                onChanged={refreshCurrent}
              />
            ) : (
              <p className="text-sm text-muted">Wird geladen…</p>
            )
          )}

          {!showSpec && !showSystemParams && planNav?.tab === "analyses" && (
            <AnalysesView
              planId={planNav.planId}
              onLaunch={(kind) => void launchTool(planNav.planId, kind)}
              onOpenSaved={(id) => setSavedAnalysisId(id)}
            />
          )}

          {!showSpec && !showSystemParams && planNav?.tab === "reports" && activePlan && (
            <ReportsView
              planId={planNav.planId}
              scenarios={activePlan.scenarios.map((s) => ({ id: s.id, name: s.name, isBase: s.isBase }))}
              hasActuals={(activePlan._count?.actuals ?? 0) > 0}
            />
          )}

          {!showSpec && !showSystemParams && !loading && !planNav && selectedScenarioId === null && (
            <DashboardHome
              username={username}
              plans={plans}
              onOpenPlan={(id) => openPlanTab(id, "dashboard")}
              onCreatePlan={() => setShowEmptyPlanDialog(true)}
              onDelete={handleDeletePlan}
            />
          )}

          {!showSpec && !showSystemParams && !loading && detail && selectedScenarioId && (
            <div className="flex flex-col gap-6">
              {/* Nur noch, was NICHT zum Szenario-Inhalt gehört: das Löschen des Szenarios
                  und der Hinweis auf Abweichungen zur Vorlage. Die inhaltlichen Aktionen
                  (Historie, Tour, Kopie, Rechenwege, Export) sitzen seit 0.36 als Kachel im
                  Szenario-Screen -- dort, wo man sie braucht. */}
              {(!detail.meta.isBase || (diff && detail.base)) && (
                <div className="flex flex-wrap items-center gap-2">
                  {!detail.meta.isBase && (
                    <Button variant="danger" onClick={() => handleDeleteScenario(detail.meta)}>
                      <Trash2 className="h-4 w-4" />
                      Szenario löschen
                    </Button>
                  )}
                  {diff && detail.base && (
                    <span className="ml-auto flex items-center gap-1.5 rounded-lg border border-diff bg-diff-soft px-3 py-1.5 text-xs font-medium text-diff">
                      <GitBranch className="h-3.5 w-3.5" />
                      {diff.total === 0
                        ? "Unverändert gegenüber der Vorlage"
                        : `${diff.total} Abweichung${diff.total === 1 ? "" : "en"} gegenüber der Vorlage`}
                    </span>
                  )}
                </div>
              )}

              <PlanView
                plan={detail.plan}
                computed={detail.computed}
                actualComputed={views?.actual ?? null}
                actualYears={views?.actualYears ?? []}
                diff={diff}
                onChanged={refreshCurrent}
                onOpenSpec={openSpecAt}
                onOpenSensitivity={() => setShowSensitivity(true)}
                onOpenHistory={() => setShowHistory(true)}
                onStartTour={() => setShowTour(true)}
                onCopyScenario={() => setCopyFrom(detail.meta)}
                onOpenTraces={() => setShowPlanTraces(true)}
                exportHref={`/api/scenarios/${detail.meta.id}/export`}
              />
            </div>
          )}
        </main>
      </div>



      {showEmptyPlanDialog && (
        <PlanDialog
          onCreate={async (name, profile) => {
            const { scenario } = await api.post<{ plan: { id: string }; scenario: { id: string } }>(
              "/api/plans",
              { name, ...profile }
            );
            setShowEmptyPlanDialog(false);
            await loadPlans();
            pendingTourRef.current = true; // Tour bei jeder Plan-Erstellung (F14)
            openScenario(scenario.id);
            toast("success", "Plan erstellt – die Tour zeigt dir gleich die wichtigsten Stellen.");
          }}
          onClose={() => setShowEmptyPlanDialog(false)}
        />
      )}

      {showCharts && detail && (
        <ChartsDialog onClose={() => setShowCharts(false)} title={`Grafiken · ${detail.meta.name}`}>
          <Dashboard
            plan={detail.plan}
            planId={detail.meta.planId}
            computed={detail.computed}
            siblings={(activePlan?.scenarios ?? []).filter((s) => s.id !== detail.meta.id)}
            actuals={detail.actuals ?? []}
            origins={detail.elementOrigins ?? []}
          />
        </ChartsDialog>
      )}

      {showMonteCarlo && detail && (
        <MonteCarloDialog
          plan={detail.plan}
          computed={detail.computed}
          meta={detail.meta}
          scenarios={activePlan?.scenarios ?? [detail.meta]}
          actuals={detail.actuals ?? []}
          origins={detail.elementOrigins ?? []}
          onClose={() => setShowMonteCarlo(false)}
        />
      )}

      {showSensitivity && detail && (
        <SensitivityDialog
          plan={detail.plan}
          planId={detail.meta.planId}
          actuals={detail.actuals ?? []}
          origins={detail.elementOrigins ?? []}
          onClose={() => setShowSensitivity(false)}
        />
      )}

      {showLiveSim && detail && (
        <LiveSimDialog
          plan={detail.plan}
          actuals={detail.actuals ?? []}
          origins={detail.elementOrigins ?? []}
          onClose={() => setShowLiveSim(false)}
        />
      )}

      {savedAnalysisId && planNav && (
        <SavedAnalysisView
          planId={planNav.planId}
          analysisId={savedAnalysisId}
          onClose={() => setSavedAnalysisId(null)}
        />
      )}

      {showActuals && detail && activePlan && (
        <ActualsDialog
          planId={detail.meta.planId}
          planName={detail.meta.planName}
          scenarioMetas={activePlan.scenarios}
          initial={{ id: detail.meta.id, name: detail.meta.name, isBase: detail.meta.isBase, plan: detail.plan }}
          onClose={() => setShowActuals(false)}
          onChanged={refreshCurrent}
        />
      )}

      {showHistory && detail && (
        <VersionHistoryDialog
          scenarioId={detail.meta.id}
          scenarioName={detail.meta.name}
          onClose={() => setShowHistory(false)}
          onRestored={refreshCurrent}
        />
      )}

      {showPlanTraces && detail && (
        <PlanTraceDialog
          computed={computePlan(detail.plan, undefined, { explain: true })}
          onClose={() => setShowPlanTraces(false)}
          onOpenSpec={openSpecAt}
        />
      )}

      {copyFrom && (
        <CopyScenarioDialog
          source={copyFrom}
          onClose={() => setCopyFrom(null)}
          onCreate={async (name) => {
            const { scenarioId } = await api.post<{ scenarioId: string }>(
              `/api/scenarios/${copyFrom.id}/copy`,
              { name }
            );
            setCopyFrom(null);
            await loadPlans();
            openScenario(scenarioId);
            toast("success", "Szenario erstellt.");
          }}
        />
      )}

      <CommandPalette
        open={showPalette}
        onClose={() => setShowPalette(false)}
        plans={plans}
        actions={paletteActions}
        onOpenScenario={openScenario}
      />

      {showTour && <Tour onClose={() => setShowTour(false)} />}
    </div>
  );
}

// Breiter Dialog für den Analyse-Bereich (Grafiken).
function ChartsDialog({
  title,
  children,
  onClose,
}: {
  title: string;
  children: React.ReactNode;
  onClose: () => void;
}) {
  return (
    <div className="ui-fade fixed inset-0 z-40 flex items-start justify-center overflow-y-auto bg-black/40 px-4 py-8" onClick={onClose}>
      <div
        onClick={(e) => e.stopPropagation()}
        className="ui-pop flex w-full max-w-5xl flex-col gap-4 rounded-2xl border border-border bg-surface p-6 shadow-xl"
      >
        <div className="flex items-center justify-between">
          <h2 className="flex items-center gap-2 text-base font-semibold text-fg">
            <BarChart3 className="h-5 w-5 text-accent" /> {title}
          </h2>
          <button type="button" onClick={onClose} aria-label="Schliessen" className="rounded-md p-1 text-faint hover:bg-surface-2">
            <X className="h-4 w-4" />
          </button>
        </div>
        {children}
      </div>
    </div>
  );
}

// Rekursiver Szenario-Baum: Kinder werden eingerückt, damit Sub-Szenarien sichtbar sind.
// Die Aktions-Icons sind immer leicht sichtbar (nicht nur bei Hover) -- auf Touch-Geräten
// gibt es kein Hover.
function ScenarioTree({
  scenarios,
  selectedId,
  onSelect,
  onCopy,
  onDelete,
}: {
  scenarios: ScenarioMeta[];
  selectedId: string | null;
  onSelect: (id: string) => void;
  onCopy: (s: ScenarioMeta) => void;
  onDelete: (s: ScenarioMeta) => void;
}) {
  // Echte Verschachtelung: Ein Szenario wird unter seiner Vorlage eingerückt (Tiefe = Länge
  // der Herkunftskette). So ist in der Seitenleiste sofort sichtbar, woraus ein Szenario
  // entstanden ist -- die Szenario-Liste zeigt dasselbe zusätzlich als Spalte «aus …».
  const byId = new Map(scenarios.map((s) => [s.id, s]));
  const depthOf = (s: ScenarioMeta): number => {
    let d = 0;
    let cur = s.parentScenarioId ? byId.get(s.parentScenarioId) : undefined;
    // Deckel gegen eine (theoretisch) zyklische Kette.
    while (cur && d < 8) {
      d += 1;
      cur = cur.parentScenarioId ? byId.get(cur.parentScenarioId) : undefined;
    }
    return d;
  };

  // Vorordnung: Basis zuoberst, Kinder direkt unter ihrer Vorlage.
  const ordered: ScenarioMeta[] = [];
  const visit = (parentId: string | null) => {
    for (const s of scenarios) {
      const pid = s.isBase ? null : s.parentScenarioId;
      if ((pid ?? null) !== parentId) continue;
      ordered.push(s);
      visit(s.id);
    }
  };
  visit(null);
  // Szenarien, deren Vorlage gelöscht wurde, hängen sonst nirgends -- ans Ende.
  for (const s of scenarios) if (!ordered.includes(s)) ordered.push(s);
  if (ordered.length === 0) return null;

  return (
    <>
      {ordered.map((s) => (
        <div
          key={s.id}
          style={{ paddingLeft: `${2.5 + depthOf(s) * 0.85}rem` }}
          className={`group flex items-center gap-1.5 rounded-lg py-1.5 pr-1.5 text-sm transition-colors ${
            selectedId === s.id ? "bg-accent-soft font-medium text-accent-soft-fg" : "text-muted hover:bg-surface-2"
          }`}
        >
          <button type="button" onClick={() => onSelect(s.id)} className="flex min-w-0 flex-1 items-center gap-1.5 text-left">
            {s.isBase ? <PiggyBank className="h-3.5 w-3.5 shrink-0" /> : <GitBranch className="h-3.5 w-3.5 shrink-0" />}
            <span className="min-w-0 flex-1 truncate" title={s.name}>{s.name}</span>
          </button>
          <button
            type="button"
            aria-label="Kopie erstellen"
            title="Neues Szenario aus diesem"
            onClick={() => onCopy(s)}
            className="rounded p-0.5 text-faint opacity-60 transition-opacity hover:bg-accent-soft hover:text-accent group-hover:opacity-100"
          >
            <Copy className="h-3.5 w-3.5" />
          </button>
          {!s.isBase && (
            <button
              type="button"
              aria-label="Szenario löschen"
              onClick={() => onDelete(s)}
              className="rounded p-0.5 text-faint opacity-60 transition-opacity hover:bg-danger-soft hover:text-danger group-hover:opacity-100"
            >
              <Trash2 className="h-3.5 w-3.5" />
            </button>
          )}
        </div>
      ))}
    </>
  );
}

// Startansicht: Begrüssung + Plan-Kacheln (Klick öffnet das Basisszenario).
function DashboardHome({
  username,
  plans,
  onOpenPlan,
  onCreatePlan,
  onDelete,
}: {
  username: string;
  plans: PlanListItem[];
  onOpenPlan: (planId: string) => void;
  onCreatePlan: () => void;
  onDelete: (id: string) => void;
}) {
  return (
    <div className="flex flex-col gap-6">
      <div>
        <h2 className="text-xl font-semibold text-fg">Willkommen, {username}</h2>
        <p className="mt-1 text-sm text-muted">
          Wähle einen Plan oder erstelle einen neuen. Jeder Plan enthält ein Basisszenario und beliebig
          viele Varianten davon.
        </p>
      </div>

      {plans.length === 0 ? (
        /* Genau EIN Weg hinein. Bis 0.35 standen hier drei Knöpfe (geführt / Beispielplan /
           leer) -- eine Wahl, die niemand treffen kann, der das Tool noch nicht kennt. Der
           Assistent führt jetzt IM Plan statt davor; die Tour zeigt vorab den Aufbau. */
        <EmptyState
          icon={<Sparkles className="h-6 w-6" />}
          title="Deine erste Finanzplanung"
          text="Ein paar Angaben zu dir – danach zeigt dir eine kurze Tour, wie FPT aufgebaut ist, und der Assistent führt dich Schritt für Schritt durch die Planung."
        >
          <Button onClick={onCreatePlan}>
            <Plus className="h-4 w-4" /> Meinen ersten Finanzplan anlegen
          </Button>
        </EmptyState>
      ) : (
        <div className="grid grid-cols-1 gap-4 sm:grid-cols-2 xl:grid-cols-3">
          {plans.map((p) => {
            const others = p.scenarios.length - 1;
            const persons = (p.persons ?? [])
              .map((pe) => pe.name || (pe.role === "PERSON_A" ? "Person A" : "Person B"))
              .join(" · ");
            return (
              <div
                key={p.id}
                className="group relative flex cursor-pointer flex-col gap-2 rounded-xl border border-border bg-surface p-4 shadow-sm transition-shadow hover:shadow-md"
                onClick={() => onOpenPlan(p.id)}
              >
                <div className="flex items-center gap-2">
                  <div className="flex h-9 w-9 items-center justify-center rounded-lg bg-accent-soft">
                    <FolderKanban className="h-5 w-5 text-accent-soft-fg" />
                  </div>
                  <div className="min-w-0 flex-1">
                    <div className="truncate font-medium text-fg">{p.name}</div>
                    <div className="text-xs text-muted">
                      {persons || "Basisszenario"}
                      {others > 0 ? ` · +${others} Variante${others === 1 ? "" : "n"}` : ""}
                    </div>
                  </div>
                  <button
                    type="button"
                    aria-label="Plan löschen"
                    onClick={(e) => {
                      e.stopPropagation();
                      onDelete(p.id);
                    }}
                    className="rounded-md p-1.5 text-faint opacity-0 transition-opacity hover:bg-danger-soft hover:text-danger group-hover:opacity-100"
                  >
                    <Trash2 className="h-4 w-4" />
                  </button>
                </div>

                {/* Ein paar Kennzahlen pro Plan direkt in der Übersicht. */}
                <div className="mt-1 flex flex-wrap gap-x-3 gap-y-1 text-[11px] text-muted">
                  <span>{p.scenarios.length} Szenario{p.scenarios.length === 1 ? "" : "en"}</span>
                  {p.startYear && <span>ab {p.startYear}</span>}
                  {p._count && p._count.actuals > 0 && <span>{p._count.actuals} {p._count.actuals === 1 ? "Ist-Datensatz" : "Ist-Datensätze"}</span>}
                  {p._count && p._count.analyses > 0 && <span>{p._count.analyses} Analyse{p._count.analyses === 1 ? "" : "n"}</span>}
                </div>
              </div>
            );
          })}

          <button
            type="button"
            onClick={onCreatePlan}
            className="flex min-h-20 items-center justify-center gap-2 rounded-xl border border-dashed border-accent bg-accent-soft text-sm font-medium text-accent-soft-fg transition-colors hover:bg-accent-soft"
          >
            <Plus className="h-4 w-4" />
            Neuer Plan
          </button>
        </div>
      )}
    </div>
  );
}

function PlanDialog({
  onCreate,
  onClose,
}: {
  onCreate: (name: string, profile: ProfileDraft) => void;
  onClose: () => void;
}) {
  const [name, setName] = useState("Meine Planung");
  const [draft, setDraft] = useState<ProfileDraft>(emptyProfileDraft);
  const [saving, setSaving] = useState(false);

  return (
    <Modal title="Neuen Plan erstellen" onClose={onClose}>
      <p className="text-xs text-muted">
        Es wird automatisch ein <strong className="text-fg">Basisszenario</strong> angelegt. Weitere
        Szenarien entstehen später als Kopien davon.
      </p>
      <div>
        <label className="mb-1 block text-xs font-medium text-muted">Name des Plans</label>
        <input
          autoFocus
          className="w-full rounded-lg border border-border bg-input px-3 py-2 text-sm text-fg focus:border-accent focus:outline-none focus:ring-2 focus:ring-accent/25"
          value={name}
          onChange={(e) => setName(e.target.value)}
          placeholder="Name des Plans"
        />
      </div>
      <PlanProfileFields draft={draft} onChange={setDraft} />
      <div className="flex gap-2">
        <Button
          disabled={saving}
          onClick={() => {
            setSaving(true);
            onCreate(name.trim() || "Plan", draft);
          }}
        >
          {saving ? "…" : "Erstellen"}
        </Button>
        <Button variant="secondary" onClick={onClose}>
          Abbrechen
        </Button>
      </div>
    </Modal>
  );
}

function CopyScenarioDialog({
  source,
  onCreate,
  onClose,
}: {
  source: ScenarioMeta;
  onCreate: (name: string) => void;
  onClose: () => void;
}) {
  const [name, setName] = useState(`${source.name} – Variante`);
  const [saving, setSaving] = useState(false);
  return (
    <Modal title="Neues Szenario" onClose={onClose}>
      <p className="text-xs text-muted">
        Vollständige Kopie von <strong className="text-fg">{source.name}</strong>. Änderungen darin
        werden anschliessend farblich hervorgehoben.
      </p>
      <input
        autoFocus
        className="w-full rounded-lg border border-border bg-input px-3 py-2 text-sm text-fg focus:border-accent focus:outline-none focus:ring-2 focus:ring-accent/25"
        value={name}
        onChange={(e) => setName(e.target.value)}
        placeholder="Name des Szenarios"
      />
      <div className="flex gap-2">
        <Button
          disabled={saving}
          onClick={() => {
            setSaving(true);
            onCreate(name.trim() || "Szenario");
          }}
        >
          {saving ? "…" : "Erstellen"}
        </Button>
        <Button variant="secondary" onClick={onClose}>
          Abbrechen
        </Button>
      </div>
    </Modal>
  );
}

// --- Die linke Leiste ------------------------------------------------------------------------
//
// EINE Komponente in zwei Breiten. Die Symbole stehen immer an derselben Stelle -- in einer
// festen 3.5-rem-Spalte --, rechts daneben faehrt die Beschriftung aus. Bis 0.42 wechselte die
// Leiste beim Ueberfahren die Darstellung: eingeklappt Icons, ausgefahren ein Plan-Baum. Das
// war kein Uebergang, sondern ein Sprung, und man verlor jedes Mal die Orientierung.
//
// Der Preis ist der Verlust des Baums ALLER Plaene. Er wird nicht vermisst: Der Plan wird ueber
// den Waehler oben gewechselt, und die vier Bereiche darunter beziehen sich immer auf den
// AKTIVEN Plan. Statt einer Liste von Listen bleiben vier Knoepfe.
function Rail({
  wide,
  pinned,
  mobile,
  onTogglePin,
  plans,
  activeScenarioId,
  planNav,
  atHome,
  showSpec,
  showSystemParams,
  planMenuOpen,
  onTogglePlanMenu,
  onNewPlan,
  onDeletePlan,
  onHome,
  onOpenTab,
  onOpenScenario,
  onCopyScenario,
  onDeleteScenario,
  onSpec,
  onParams,
  onSearch,
}: {
  wide: boolean;
  pinned: boolean;
  mobile?: boolean;
  onTogglePin: () => void;
  plans: PlanListItem[];
  activeScenarioId: string | null;
  planNav: { planId: string; tab: string } | null;
  atHome: boolean;
  showSpec: boolean;
  showSystemParams: boolean;
  planMenuOpen: boolean;
  onTogglePlanMenu: () => void;
  onNewPlan: () => void;
  onDeletePlan: (planId: string) => void;
  onHome: () => void;
  onOpenTab: (planId: string, tab: "dashboard" | "scenarios" | "actuals" | "analyses" | "reports") => void;
  onOpenScenario: (scenarioId: string) => void;
  onCopyScenario: (meta: ScenarioMeta) => void;
  onDeleteScenario: (s: ScenarioMeta) => void;
  onSpec: () => void;
  onParams: () => void;
  onSearch: () => void;
}) {
  // Der Szenario-Baum haengt unter «Szenarien» und braucht Text -- eingeklappt gibt es ihn
  // nicht. Standard: zu, sonst waere die Leiste beim Ueberfahren sofort lang.
  const [treeOpen, setTreeOpen] = useState(false);

  // Welcher Plan ist im Zugriff? Erst der offene, sonst der zuletzt gewaehlte Bereich, sonst
  // der einzige. Ohne diesen Bezug waeren die vier Knoepfe darunter gegenstandslos.
  const active =
    plans.find((p) => p.scenarios.some((sc) => sc.id === activeScenarioId)) ??
    plans.find((p) => p.id === planNav?.planId) ??
    (plans.length === 1 ? plans[0] : undefined);

  const initials = active
    ? active.name.split(/\s+/).slice(0, 2).map((w) => w[0]).join("").toUpperCase()
    : "–";

  return (
    <aside
      className={`${
        mobile ? "relative h-full w-full" : "absolute inset-y-0 left-0 z-40"
      } flex flex-col overflow-hidden border-r border-border bg-surface transition-[width] duration-200 ${
        mobile ? "" : wide ? "w-60" : "w-14"
      }`}
    >
      {/* Klebt am oberen Bildschirmrand: Die Leiste ist so hoch wie die SEITE, und mit einer
          aufgeklappten Karte wird die sehr lang -- ohne sticky waeren die Knoepfe nach ein
          wenig Scrollen ausserhalb des Blickfelds. */}
      <div className="sticky top-0 flex max-h-screen flex-col gap-0.5 overflow-y-auto py-3">
        <RailRow wide={wide} icon={<PiggyBank className="h-[18px] w-[18px] text-accent-fg" />} iconBox="bg-accent" label="FPT" />

        <div className="my-1 h-px w-6 self-center bg-border" />

        <RailRow wide={wide} active={atHome} label="Übersicht" onClick={onHome} icon={<LayoutDashboard className="h-[18px] w-[18px]" />} />

        {/* Plan-Wähler. Eingeklappt die Initialen -- ohne diesen Bezug wüsste man nach dem
            Umschalten nicht mehr, worin man gerade ist. */}
        <div className="relative">
          <RailRow
            wide={wide}
            label={active?.name ?? "Plan wählen"}
            onClick={onTogglePlanMenu}
            trailing={<ChevronDown className="h-3.5 w-3.5 text-faint" />}
            icon={
              <span
                className={`flex h-[26px] w-[26px] items-center justify-center rounded-md border text-[10px] font-semibold ${
                  active ? "border-border text-fg" : "border-dashed border-border text-faint"
                }`}
              >
                {initials}
              </span>
            }
          />
          {planMenuOpen && (
            <div
              className={`ui-fade absolute z-50 w-56 rounded-xl border border-border bg-surface p-1 shadow-xl ${
                wide ? "left-3 top-full mt-1" : "left-12 top-0"
              }`}
            >
              {plans.length === 0 && <p className="px-2 py-1.5 text-xs text-faint">Noch keine Pläne.</p>}
              {plans.map((p) => (
                <div key={p.id} className="group flex items-center gap-1">
                  <button
                    type="button"
                    onClick={() => onOpenTab(p.id, "dashboard")}
                    className={`flex min-w-0 flex-1 items-center gap-2 rounded-lg px-2 py-1.5 text-left text-xs font-medium transition-colors ${
                      p.id === active?.id ? "bg-accent-soft text-accent-soft-fg" : "text-muted hover:bg-surface-2"
                    }`}
                  >
                    <FolderKanban className="h-3.5 w-3.5 shrink-0" />
                    <span className="min-w-0 truncate">{p.name}</span>
                  </button>
                  <button
                    type="button"
                    aria-label={`${p.name} löschen`}
                    onClick={() => onDeletePlan(p.id)}
                    className="rounded p-1 text-faint opacity-0 transition-opacity hover:bg-danger-soft hover:text-danger group-hover:opacity-100"
                  >
                    <Trash2 className="h-3.5 w-3.5" />
                  </button>
                </div>
              ))}
              <button
                type="button"
                onClick={onNewPlan}
                className="mt-1 flex w-full items-center gap-2 rounded-lg border-t border-border px-2 py-1.5 pt-2 text-left text-xs font-medium text-accent transition-colors hover:bg-accent-soft"
              >
                <Plus className="h-3.5 w-3.5 shrink-0" /> Neuer Plan
              </button>
            </div>
          )}
        </div>

        {/* Die vier Bereiche des aktiven Plans. Mehr braucht es nicht. */}
        <RailRow
          wide={wide}
          disabled={!active}
          active={planNav?.tab === "scenarios" || !!activeScenarioId}
          label="Szenarien"
          onClick={() => active && onOpenTab(active.id, "scenarios")}
          icon={<Layers className="h-[18px] w-[18px]" />}
          trailing={
            wide && active ? (
              <span
                role="button"
                tabIndex={0}
                aria-label={treeOpen ? "Szenarien einklappen" : "Szenarien aufklappen"}
                onClick={(e) => {
                  e.stopPropagation();
                  setTreeOpen((o) => !o);
                }}
                onKeyDown={(e) => {
                  if (e.key === "Enter" || e.key === " ") {
                    e.stopPropagation();
                    setTreeOpen((o) => !o);
                  }
                }}
                className="rounded p-0.5 text-faint hover:text-fg"
              >
                {treeOpen ? <ChevronDown className="h-3.5 w-3.5" /> : <ChevronRight className="h-3.5 w-3.5" />}
              </span>
            ) : undefined
          }
        />
        {wide && treeOpen && active && (
          <div className="pl-3 pr-2">
            <ScenarioTree
              scenarios={active.scenarios}
              selectedId={activeScenarioId}
              onSelect={onOpenScenario}
              onCopy={onCopyScenario}
              onDelete={onDeleteScenario}
            />
          </div>
        )}

        <RailRow
          wide={wide}
          disabled={!active}
          active={planNav?.tab === "actuals"}
          label="Effektive Werte"
          onClick={() => active && onOpenTab(active.id, "actuals")}
          icon={<CalendarClock className="h-[18px] w-[18px]" />}
        />
        <RailRow
          wide={wide}
          disabled={!active}
          active={planNav?.tab === "analyses"}
          label="Analysen"
          onClick={() => active && onOpenTab(active.id, "analyses")}
          icon={<BarChart3 className="h-[18px] w-[18px]" />}
        />
        <RailRow
          wide={wide}
          disabled={!active}
          active={planNav?.tab === "reports"}
          label="Berichte"
          onClick={() => active && onOpenTab(active.id, "reports")}
          icon={<FileSpreadsheet className="h-[18px] w-[18px]" />}
        />

        <div className="my-1 h-px w-6 self-center bg-border" />

        <RailRow wide={wide} active={showSpec} label="So rechnet FPT" onClick={onSpec} icon={<FileText className="h-[18px] w-[18px]" />} />
        <RailRow
          wide={wide}
          active={showSystemParams}
          label="Systemparameter"
          onClick={onParams}
          icon={<SlidersHorizontal className="h-[18px] w-[18px]" />}
        />

        {/* Suchen und Anpinnen stehen DIREKT hier, nicht am unteren Rand. Die Leiste ist so
            hoch wie die Seite -- und mit einer aufgeklappten Karte wird die sehr lang. Am Fuss
            waeren die beiden dann ausserhalb des Blickfelds, obwohl sie zu den am haeufigsten
            gebrauchten Knoepfen gehoeren. */}
        <div className="my-1 h-px w-6 self-center bg-border" />

        <RailRow
          wide={wide}
          label="Suchen und springen (Ctrl+K)"
          shortLabel="Suchen"
          onClick={onSearch}
          icon={<Search className="h-[18px] w-[18px]" />}
        />
        {!mobile && (
          <RailRow
            wide={wide}
            active={pinned}
            label={pinned ? "Menü loslösen" : "Menü anpinnen"}
            onClick={onTogglePin}
            icon={pinned ? <PinOff className="h-[18px] w-[18px]" /> : <Pin className="h-[18px] w-[18px]" />}
          />
        )}
      </div>
    </aside>
  );
}

// Eine Zeile der Leiste. Das Symbol sitzt in einer FESTEN Spalte von 3.5 rem -- dadurch bleibt
// es beim Ausfahren exakt an seinem Platz, und es kommt nur die Beschriftung dazu.
function RailRow({
  wide,
  icon,
  iconBox,
  label,
  shortLabel,
  onClick,
  active,
  disabled,
  trailing,
}: {
  wide: boolean;
  icon: React.ReactNode;
  iconBox?: string;
  label: string;
  shortLabel?: string;
  onClick?: () => void;
  active?: boolean;
  disabled?: boolean;
  trailing?: React.ReactNode;
}) {
  const cls = `flex w-full items-center rounded-lg pr-2 text-left transition-colors ${
    disabled ? "opacity-30" : ""
  } ${active ? "bg-accent-soft text-accent-soft-fg" : onClick ? "text-muted hover:bg-surface-2 hover:text-fg" : "text-fg"}`;

  const inner = (
    <>
      <span className="flex h-9 w-14 shrink-0 items-center justify-center">
        <span className={iconBox ? `flex h-7 w-7 items-center justify-center rounded-lg ${iconBox}` : ""}>{icon}</span>
      </span>
      {wide && (
        <>
          <span className="min-w-0 flex-1 truncate text-sm font-medium">{shortLabel ?? label}</span>
          {trailing}
        </>
      )}
    </>
  );

  if (!onClick) return <div className={cls}>{inner}</div>;
  return (
    <button
      type="button"
      onClick={onClick}
      disabled={disabled}
      aria-label={label}
      title={wide ? undefined : label}
      className={cls}
    >
      {inner}
    </button>
  );
}
