/* Konfigurator Steps 1, 2, 3 — manual rooms / plan import / modules / summary */

const HOME_ROOM_TYPES = [
  { type:"living",  label:"Wohn/Esszimmer", icon:"🛋", defaultArea:28, defaultWindows:2 },
  { type:"kitchen", label:"Küche",          icon:"🍳", defaultArea:12, defaultWindows:1 },
  { type:"bedroom", label:"Schlafzimmer",   icon:"🛏", defaultArea:16, defaultWindows:1 },
  { type:"room",    label:"Zimmer",         icon:"▢",  defaultArea:14, defaultWindows:1 },
  { type:"bath",    label:"Bad",            icon:"🚿", defaultArea: 8, defaultWindows:1 },
  { type:"wc",      label:"WC",             icon:"⌬",  defaultArea: 2, defaultWindows:0 },
  { type:"hall",    label:"Vorraum/Flur",   icon:"⌷",  defaultArea: 9, defaultWindows:0 },
  { type:"storage", label:"Speis/Abstell",  icon:"▦",  defaultArea: 4, defaultWindows:0 },
  { type:"tech",    label:"Technik/HWR",    icon:"⚙",  defaultArea: 5, defaultWindows:0 },
  { type:"entry",   label:"Eingang",        icon:"⌂",  defaultArea: 4, defaultWindows:0 },
  { type:"terrace", label:"Terrasse",       icon:"☀",  defaultArea:20, defaultWindows:0, outdoor:true },
  { type:"office",  label:"Büro/Arbeit",    icon:"✎",  defaultArea:12, defaultWindows:1 },
];

const GEWERBE_ROOM_TYPES = [
  { type:"living",  label:"Empfang / Lobby",          icon:"⌂", defaultArea:24, defaultWindows:2 },
  { type:"office",  label:"Büro / Arbeitsplatz",      icon:"✎", defaultArea:16, defaultWindows:1 },
  { type:"room",    label:"Meeting / Seminar",        icon:"▢", defaultArea:22, defaultWindows:1 },
  { type:"bedroom", label:"Hotelzimmer / Apartment",  icon:"▣", defaultArea:20, defaultWindows:1 },
  { type:"kitchen", label:"Teeküche / Gastro",        icon:"◫", defaultArea:14, defaultWindows:1 },
  { type:"hall",    label:"Flur / Gang",              icon:"⌁", defaultArea:14, defaultWindows:0 },
  { type:"entry",   label:"Eingang / Zutritt",        icon:"⌂", defaultArea:8,  defaultWindows:0 },
  { type:"tech",    label:"Technik / Server",         icon:"⚙", defaultArea:8,  defaultWindows:0 },
  { type:"storage", label:"Lager / Backoffice",       icon:"▤", defaultArea:12, defaultWindows:0 },
  { type:"wc",      label:"WC / Sanitär",             icon:"⌬", defaultArea:4,  defaultWindows:0 },
  { type:"terrace", label:"Außenbereich",             icon:"☀", defaultArea:24, defaultWindows:0, outdoor:true },
];

const ROOM_TYPES = HOME_ROOM_TYPES;

function getRoomTypes(pathKey) {
  return pathKey === "gewerbe" ? GEWERBE_ROOM_TYPES : HOME_ROOM_TYPES;
}

const FLOORS = ["UG", "EG", "OG", "DG"];

/* ============================================================
   STEP 1 — Manual room entry
============================================================ */
function KfgStep1Manual({ pathKey, rooms, setRooms, activeFloor, setActiveFloor, onNext, onBack }) {
  const floorRooms = rooms.filter(r => r.floor === activeFloor);
  const isGewerbe = pathKey === "gewerbe";
  const roomTypes = getRoomTypes(pathKey);
  const copy = isGewerbe
    ? {
        title: "Bereiche erfassen",
        intro: "Wähle ein Stockwerk und füge Betriebsbereiche aus der Bibliothek hinzu. Größe und Fenster kannst du anpassen.",
        library: "Bereichstypen — klicken zum Hinzufügen",
        panel: "Bereiche",
        empty: "Noch keine Bereiche in diesem Stockwerk.",
        emptyHint: "← Wähle einen Bereichstyp aus der Bibliothek",
        next: "Funktionen wählen →",
        total: "Bereiche",
      }
    : {
        title: "Räume erfassen",
        intro: "Wähle ein Stockwerk und füge Räume aus der Bibliothek hinzu. Größe und Fenster kannst du anpassen.",
        library: "Raumtypen — klicken zum Hinzufügen",
        panel: "Räume",
        empty: "Noch keine Räume in diesem Stockwerk.",
        emptyHint: "← Wähle einen Raumtyp aus der Bibliothek",
        next: "Module wählen →",
        total: "Räume",
      };

  const addRoom = (type) => {
    const def = roomTypes.find(rt => rt.type === type);
    const id = "r" + Date.now() + Math.random().toString(36).slice(2, 6);
    const same = rooms.filter(r => r.type === type).length;
    const name = same > 0 ? `${def.label} ${same+1}` : def.label;
    setRooms([...rooms, {
      id, name, type, floor: activeFloor,
      area: def.defaultArea, windows: def.defaultWindows,
      outdoor: !!def.outdoor,
    }]);
  };

  const updateRoom = (id, patch) => {
    setRooms(rooms.map(r => r.id === id ? { ...r, ...patch } : r));
  };

  const removeRoom = (id) => {
    setRooms(rooms.filter(r => r.id !== id));
  };

  const totalArea = rooms.filter(r => !r.outdoor).reduce((s,r) => s + r.area, 0);

  return (
    <div className="kfg-content">
      <h3 style={{ fontFamily: "var(--font-display, serif)", fontSize: 28, fontWeight: 400, margin: "0 0 6px" }}>{copy.title}</h3>
      <p style={{ color: KFG_PALETTE.muted, fontSize: 14, margin: "0 0 24px" }}>{copy.intro}</p>

      <div className="kfg-floors">
        {FLOORS.map(f => (
          <button key={f} className={`kfg-floor-tab ${activeFloor===f?"active":""}`} onClick={() => setActiveFloor(f)}>
            {f} <span style={{ opacity: 0.5, marginLeft: 4 }}>{rooms.filter(r => r.floor === f).length || ""}</span>
          </button>
        ))}
      </div>

      <div className="kfg-manual-grid">
        <div className="kfg-room-library">
          <h4>{copy.library}</h4>
          <div className="kfg-room-types">
            {roomTypes.map(rt => (
              <button key={rt.type} className="kfg-room-type-btn" onClick={() => addRoom(rt.type)}>
                <div className="ic">{rt.icon}</div>
                <div>
                  <div className="lbl">{rt.label}</div>
                  <div className="sub">~{rt.defaultArea}m² · {rt.defaultWindows} Fenster</div>
                </div>
              </button>
            ))}
          </div>
        </div>

        <div className="kfg-room-list-panel">
          <div className="kfg-room-list-head">
            <h4>{activeFloor} · {copy.panel}</h4>
            <span className="count">{floorRooms.length} · {floorRooms.filter(r=>!r.outdoor).reduce((s,r)=>s+r.area,0).toFixed(1)} m²</span>
          </div>

          {floorRooms.length === 0 ? (
            <div className="kfg-room-empty">
              {copy.empty}<br/>
              <span style={{ opacity: 0.7 }}>{copy.emptyHint}</span>
            </div>
          ) : (
            <ul className="kfg-room-list">
              {floorRooms.map(r => {
                const def = roomTypes.find(rt => rt.type === r.type) || ROOM_TYPES.find(rt => rt.type === r.type);
                return (
                  <li key={r.id} className="kfg-room-item">
                    <span className="ic">{def?.icon || "▢"}</span>
                    <span className="name">
                      <input value={r.name} onChange={e => updateRoom(r.id, { name: e.target.value })}/>
                    </span>
                    <span className="area">
                      <input type="number" value={r.area} step="0.5" min="0" onChange={e => updateRoom(r.id, { area: parseFloat(e.target.value) || 0 })}/> m²
                    </span>
                    <button className="rm" onClick={() => removeRoom(r.id)} title="Entfernen">×</button>
                  </li>
                );
              })}
            </ul>
          )}
        </div>
      </div>

      <div className="kfg-footer-nav">
        <div className="left">
          <button className="kfg-back" onClick={onBack}>← Zurück</button>
        </div>
        <div className="right">
          <span style={{ fontFamily: "var(--font-mono, monospace)", fontSize: 12, color: KFG_PALETTE.muted, marginRight: 16 }}>
            {rooms.length} {copy.total} · {totalArea.toFixed(1)} m²
          </span>
          <button className="kfg-next" onClick={onNext} disabled={rooms.length === 0}>
            {copy.next}
          </button>
        </div>
      </div>
    </div>
  );
}

/* ============================================================
   STEP 1 — Plan import (with animated OCR)
============================================================ */
function KfgStep1Import({ pathKey, onComplete, onBack }) {
  const isGewerbe = pathKey === "gewerbe";
  const [phase, setPhase] = React.useState("drop"); // drop | scanning | review | error
  const [logs, setLogs] = React.useState([]);
  const [progress, setProgress] = React.useState(0);
  const [detected, setDetected] = React.useState([]);
  // Echte Server-Antwort (token + erkannte rooms) wenn nicht Demo:
  const [serverResult, setServerResult] = React.useState(null);
  const [errorMsg, setErrorMsg] = React.useState(null);
  // Lokal eingelesenes File (data: URL) -- wird waehrend des Scannings als
  // Hintergrund angezeigt, damit der User direkt sieht was hochgeladen wurde.
  const [localPreviewUrl, setLocalPreviewUrl] = React.useState(null);
  const [localPreviewKind, setLocalPreviewKind] = React.useState(null); // "image" | "pdf"
  const fileInputRef = React.useRef(null);

  // Demo: hardcoded BASTING_ROOMS (existing path).
  const startDemo = () => {
    setPhase("scanning");
    setLogs([]);
    setProgress(0);
    setDetected([]);
    setServerResult(null);
    setErrorMsg(null);

    const sequence = [
      { t: 200,  log: { txt: "PDF geladen · EFH Basting 06.03.2020.pdf", cls: "" } },
      { t: 600,  log: { txt: "Maßstab erkannt: 1:100", cls: "" } },
      { t: 900,  log: { txt: "Stockwerk identifiziert: Erdgeschoss", cls: "" } },
      { t: 1200, log: { txt: "Wandstärken extrahiert: 25/50/12 cm", cls: "" } },
      { t: 1500, log: { txt: isGewerbe ? "Beginne Bereichserkennung..." : "Beginne Raumerkennung...", cls: "" } },
    ];

    BASTING_ROOMS.forEach((room, i) => {
      sequence.push({
        t: 1800 + i * 280,
        log: { txt: `→ ${room.name} · ${room.area} m²`, cls: "detect" },
        room,
      });
    });

    sequence.push({
      t: 1800 + BASTING_ROOMS.length * 280 + 400,
      log: { txt: `✓ ${BASTING_ROOMS.length} ${isGewerbe ? "Bereiche" : "Räume"} · 138.99 m² ${isGewerbe ? "Nutzfläche" : "Wohnfläche"} erkannt`, cls: "done" },
      finish: true,
    });

    sequence.forEach(s => {
      setTimeout(() => {
        setLogs(prev => [...prev, s.log]);
        setProgress(((sequence.indexOf(s) + 1) / sequence.length) * 100);
        if (s.room) setDetected(prev => [...prev, s.room]);
        if (s.finish) {
          setTimeout(() => setPhase("review"), 800);
        }
      }, s.t);
    });
  };

  // Echter Upload via XHR (Progress-Events).
  const uploadPlan = (file) => {
    if (!file) return;
    setPhase("scanning");
    setLogs([{ txt: `Lade ${file.name} (${(file.size/1024/1024).toFixed(2)} MB) hoch …`, cls: "" }]);
    setProgress(0);
    setDetected([]);
    setServerResult(null);
    setErrorMsg(null);

    // Lokalen Preview als data:URL erstellen, wird sofort sichtbar.
    // Browser rendern PNG/JPG/WebP nativ; AVIF in modernen Browsern auch.
    // Bei PDF zeigen wir nur ein Loading-Icon -- PDFs sind in <img> nicht
    // trivial darstellbar.
    const isImage = /\.(png|jpe?g|webp|avif|bmp|tiff?)$/i.test(file.name)
                    || file.type.startsWith("image/");
    setLocalPreviewKind(isImage ? "image" : "pdf");
    if (isImage) {
      const reader = new FileReader();
      reader.onload = () => setLocalPreviewUrl(String(reader.result));
      reader.onerror = () => setLocalPreviewUrl(null);
      reader.readAsDataURL(file);
    } else {
      setLocalPreviewUrl(null);
    }

    const fd = new FormData();
    fd.append("file", file);

    const xhr = new XMLHttpRequest();
    xhr.open("POST", "/api/floorplan/detect", true);
    xhr.timeout = 90_000;
    xhr.upload.onprogress = (e) => {
      if (e.lengthComputable) {
        const pct = (e.loaded / e.total) * 30;  // Upload nimmt erste 30% der Bar
        setProgress(pct);
      }
    };
    xhr.upload.onload = () => {
      setProgress(35);
      setLogs(prev => [...prev, { txt: "Upload abgeschlossen — Plan wird analysiert …", cls: "" }]);
    };
    xhr.onload = () => {
      let json;
      try { json = JSON.parse(xhr.responseText); }
      catch (e) { json = null; }

      if (xhr.status >= 200 && xhr.status < 300 && json && json.ok && json.token) {
        try { window.localStorage.setItem("sm-floorplan-token", json.token); } catch (e) {}
        setProgress(100);
        const fullRooms = (json.rooms || []);

        // Setze sofort serverResult mit LEEREN rooms -- viewbox+token sind ab
        // jetzt verfuegbar, Vorschau-Komponente kann den lokalen Plan rendern.
        // Dann animieren wir die Raum-Erkennung Stueck fuer Stueck (~240ms),
        // wie in der Demo. So sieht der User: 1. Plan kommt an, 2. Räume
        // werden "erkannt" und tauchen einer nach dem anderen als cyan Box auf.
        setServerResult({ ...json, rooms: [] });

        const sourceLabel =
          json.source === "vector"  ? "Vector-PDF (Text-Layer)" :
          json.source === "ocr"     ? "OCR (Tesseract)" :
          json.source === "gemini"  ? "Vision-LLM (Gemini 2.5 Flash)" :
          json.source === "llm"     ? "Vision-LLM (LM Studio)" :
          json.source;
        setLogs(prev => [...prev, { txt: `Quelle: ${sourceLabel}`, cls: "" }]);

        const STEP_MS = 220;
        fullRooms.forEach((r, i) => {
          setTimeout(() => {
            setServerResult(prev => prev ? { ...prev, rooms: [...prev.rooms, r] } : prev);
            setLogs(prev => [...prev, {
              txt: `→ ${r.label}${r.area_m2 ? ` · ${r.area_m2.toFixed(2)} m²` : ""}`,
              cls: "detect",
            }]);
          }, i * STEP_MS);
        });

        // Nach allen Räumen: Final-Log + Wechsel zu Review.
        const finishAt = fullRooms.length * STEP_MS + 600;
        setTimeout(() => {
          setLogs(prev => [
            ...prev,
            { txt: `✓ ${fullRooms.length} ${isGewerbe ? "Bereiche" : "Räume"} erkannt`, cls: "done" },
            ...(json.note ? [{ txt: `! ${json.note}`, cls: "" }] : []),
          ]);
          setPhase("review");
        }, finishAt);
      } else {
        setErrorMsg(json?.error || `Server-Fehler ${xhr.status}`);
        setPhase("error");
      }
    };
    xhr.onerror = () => { setErrorMsg("Netzwerk-Fehler beim Upload."); setPhase("error"); };
    xhr.ontimeout = () => { setErrorMsg("Upload-Timeout (über 90 Sek.)."); setPhase("error"); };
    xhr.send(fd);
  };

  const onPickFile = (e) => {
    const f = e.target.files && e.target.files[0];
    if (f) uploadPlan(f);
    e.target.value = "";  // erlaubt erneutes Hochladen derselben Datei
  };

  const onDropFile = (e) => {
    e.preventDefault();
    const f = e.dataTransfer.files && e.dataTransfer.files[0];
    if (f) uploadPlan(f);
  };

  // Display-Daten: Demo-Fall vs. echter Upload.
  const isDemo = !serverResult;
  const displayRooms = isDemo ? BASTING_ROOMS : (serverResult.rooms || []);
  const indoor  = isDemo ? BASTING_ROOMS.filter(r => !r.outdoor) : displayRooms;
  const outdoor = isDemo ? BASTING_ROOMS.filter(r =>  r.outdoor) : [];
  const totalArea = isDemo
    ? indoor.reduce((s, r) => s + r.area, 0)
    : displayRooms.reduce((s, r) => s + (r.area_m2 || 0), 0);
  const floors = isDemo
    ? Array.from(new Set(BASTING_ROOMS.map(r => r.floor)))
    : ["EG"];  // Worker erkennt aktuell nur 1 Stockwerk pro Upload

  return (
    <div className="kfg-content">
      <h3 style={{ fontFamily: "var(--font-display, serif)", fontSize: 28, fontWeight: 400, margin: "0 0 6px" }}>
        {phase === "drop" ? "Bauplan importieren"
         : phase === "scanning" ? "Plan wird analysiert..."
         : "Erkannter Plan · zur Kontrolle"}
      </h3>
      <p style={{ color: KFG_PALETTE.muted, fontSize: 14, margin: "0 0 32px" }}>
        {phase === "drop"
          ? (isGewerbe
              ? "PDF, DWG oder Foto von einem Objektplan. Bereiche, Flächen und Fenster werden automatisch erkannt."
              : "PDF, DWG oder Foto von einem Architektenplan. Räume, Flächen und Fenster werden automatisch erkannt.")
          : phase === "scanning"
          ? `Texterkennung läuft. Sobald alle ${isGewerbe ? "Bereiche" : "Räume"} erkannt sind, geht's automatisch weiter.`
          : `Bitte prüfen — passt das Layout? Du kannst ${isGewerbe ? "Bereiche" : "Räume"} noch korrigieren oder direkt weiter zum ${isGewerbe ? "Funktions" : "Modul"}-Schritt.`}
      </p>

      {phase === "drop" && (
        <div className="kfg-import-stage">
          <div className="kfg-import-drop"
               onClick={() => fileInputRef.current && fileInputRef.current.click()}
               onDragOver={(e) => e.preventDefault()}
               onDrop={onDropFile}>
            <svg width="56" height="56" viewBox="0 0 56 56" fill="none" style={{ margin: "0 auto" }}>
              <rect x="14" y="8" width="28" height="36" stroke={KFG_PALETTE.ink} strokeWidth="1.5" fill={KFG_PALETTE.paper}/>
              <line x1="20" y1="20" x2="36" y2="20" stroke={KFG_PALETTE.muted} strokeWidth="1"/>
              <line x1="20" y1="26" x2="36" y2="26" stroke={KFG_PALETTE.muted} strokeWidth="1"/>
              <line x1="20" y1="32" x2="30" y2="32" stroke={KFG_PALETTE.muted} strokeWidth="1"/>
              <circle cx="42" cy="44" r="9" fill={KFG_PALETTE.accent}/>
              <line x1="42" y1="40" x2="42" y2="48" stroke="white" strokeWidth="1.8"/>
              <line x1="38" y1="44" x2="46" y2="44" stroke="white" strokeWidth="1.8"/>
            </svg>
            <h4>Plan hier ablegen</h4>
            <p>oder klicken zum Hochladen</p>
            <input
              ref={fileInputRef}
              type="file"
              accept=".pdf,.png,.jpg,.jpeg,.webp,.avif,.bmp,.tif,.tiff,application/pdf,image/*"
              style={{ display: "none" }}
              onChange={onPickFile}
            />
            <button className="demo-link" onClick={(e) => { e.stopPropagation(); startDemo(); }}>
              ▶ Demo starten · EFH Basting
            </button>
            <div className="kfg-import-formats">PDF · PNG · JPG · WebP · AVIF · max. 20 MB</div>
            <div
              onClick={(e) => e.stopPropagation()}
              style={{
                marginTop: 14, fontSize: 11, lineHeight: 1.5,
                color: KFG_PALETTE.muted, maxWidth: 380, textAlign: "center",
                fontFamily: "'Inter', sans-serif",
              }}>
              Mit dem Hochladen bestätigst du, dass du zur Übermittlung des Plans
              berechtigt bist. Der Plan wird nach 24&nbsp;Stunden automatisch gelöscht.{' '}
              <a href="datenschutz.html" target="_blank" rel="noopener"
                 style={{ color: KFG_PALETTE.accent, textDecoration: "underline" }}>
                Details
              </a>.
            </div>
          </div>
        </div>
      )}

      {phase === "error" && (
        <div className="kfg-import-stage">
          <div className="kfg-import-drop" style={{ borderColor: "#ff8b8b" }}>
            <h4 style={{ color: "#ff8b8b" }}>Upload fehlgeschlagen</h4>
            <p>{errorMsg || "Unbekannter Fehler"}</p>
            <button className="demo-link" onClick={() => setPhase("drop")}>← Erneut versuchen</button>
          </div>
        </div>
      )}

      {phase === "scanning" && (
        <div className="kfg-scan-stage">
          <div className="kfg-scan-doc">
            {localPreviewKind ? (
              // Upload-Pfad. SRC-Logik:
              // - Vor Server-Antwort: lokales File (data: URL) sofort sichtbar
              // - Nach Server-Antwort: server-cropped preview.png, weil die
              //   Room-Bboxes vom Server in cropped-Koords sind und sich
              //   sonst auf dem ungeschnittenen Original verschieben würden.
              <KfgUploadPreview
                 src={serverResult
                       ? `/api/floorplan/${serverResult.token}/preview.png`
                       : localPreviewUrl}
                 fallbackSrc={localPreviewUrl}
                 placeholderKind={localPreviewKind} />
            ) : (
              // Demo-Pfad: hardcoded EFH-Basting Plan + simulierte Erkennung.
              <KfgPlanSVG detected={detected} />
            )}
            <div className="kfg-scan-line" />
          </div>
          <div className="kfg-scan-status">
            <h4>{localPreviewKind ? "Plan-Analyse" : "Texterkennung · OCR"}</h4>
            <div className="kfg-scan-progress"><div className="bar" style={{ width: `${progress}%` }}/></div>
            <ul className="kfg-scan-log">
              {logs.map((l, i) => (
                <li key={i} className={l.cls}>{l.txt}</li>
              ))}
            </ul>
          </div>
        </div>
      )}

      {phase === "review" && (
        <div className="kfg-review-stage">
          <div className="kfg-review-plan">
            {isDemo
              ? <KfgPlanSVG detected={BASTING_ROOMS} />
              : <KfgUploadPreview
                   src={`/api/floorplan/${serverResult.token}/preview.png`}
                   fallbackSrc={localPreviewUrl} />}
            {isDemo
              ? <div className="kfg-review-tag"><span className="dot"/>Erkennung abgeschlossen</div>
              : <SourcePill source={serverResult.source} note={serverResult.note} />}
          </div>
          <div className="kfg-review-summary">
            <div className="kfg-review-stats">
              <div className="stat">
                <div className="num">{displayRooms.length}</div>
                <div className="lbl">{isGewerbe ? "Bereiche" : "Räume"}</div>
              </div>
              <div className="stat">
                <div className="num">{(totalArea || 0).toFixed(1)}<span className="u">m²</span></div>
                <div className="lbl">Fläche</div>
              </div>
              <div className="stat">
                <div className="num">{floors.length}</div>
                <div className="lbl">Stockwerk{floors.length > 1 ? "e" : ""}</div>
              </div>
              <div className="stat">
                <div className="num">{outdoor.length}</div>
                <div className="lbl">Außen</div>
              </div>
            </div>
            <div className="kfg-review-roomlist">
              <div className="rl-head">Erkannte {isGewerbe ? "Bereiche" : "Räume"}</div>
              {isDemo
                ? BASTING_ROOMS.map(r => (
                    <div key={r.id} className={`rl-row ${r.outdoor ? "out" : ""}`}>
                      <span className="rl-name">{r.name}</span>
                      <span className="rl-meta">{r.floor}{r.outdoor ? " · außen" : ""}</span>
                      <span className="rl-area">{r.area} m²</span>
                    </div>
                  ))
                : displayRooms.map((r, i) => (
                    <div key={i} className="rl-row">
                      <span className="rl-name">{r.label || `Raum ${i+1}`}</span>
                      <span className="rl-meta">{Math.round((r.confidence || 0) * 100)}% Konfidenz</span>
                      <span className="rl-area">{r.area_m2 ? r.area_m2.toFixed(2) + " m²" : "–"}</span>
                    </div>
                  ))}
              {!isDemo && serverResult.note && (
                <div className="rl-row" style={{ color: KFG_PALETTE.muted, fontStyle: "italic" }}>
                  {serverResult.note}
                </div>
              )}
            </div>
          </div>
        </div>
      )}

      {(phase === "drop" || phase === "error") && (
        <div className="kfg-footer-nav">
          <div className="left">
            <button className="kfg-back" onClick={onBack}>← Zurück</button>
          </div>
          <div className="right">
            <span style={{ fontFamily: "var(--font-mono, monospace)", fontSize: 12, color: KFG_PALETTE.muted }}>
              Tipp: „Demo starten" zeigt einen echten EFH-Plan
            </span>
          </div>
        </div>
      )}

      {phase === "review" && (
        <div className="kfg-footer-nav">
          <div className="left">
            <button className="kfg-back" onClick={() => setPhase("drop")}>← Anderen Plan laden</button>
          </div>
          <div className="right">
            <button className="kfg-next" onClick={() => onComplete(isDemo ? BASTING_ROOMS : adaptServerRoomsToConfigurator(displayRooms))}>
              Stimmt — weiter zu den Modulen →
            </button>
          </div>
        </div>
      )}
    </div>
  );
}

/* ============================================================
   Status-Pille: zeigt was die Erkennung gemacht hat (welcher LLM-Pfad,
   ob fallback, ob Diag-Bundle gespeichert).
============================================================ */
function SourcePill({ source, note }) {
  // source -> {label, color}
  const map = {
    "vector":     { label: "Plan-Daten direkt gelesen", color: "#5DD3B0", bg: "rgba(93,211,176,0.10)" },
    "ocr":        { label: "OCR (Tesseract)",            color: "#8DDBEA", bg: "rgba(141,219,234,0.10)" },
    "gemini":     { label: "Vision-AI · Gemini Flash-Lite", color: "#3ABDD0", bg: "rgba(58,189,208,0.10)" },
    "openrouter": { label: "Vision-AI · OpenRouter",      color: "#E8C547", bg: "rgba(232,197,71,0.10)" },
    "llm":        { label: "Vision-AI · lokal (LM Studio)", color: "#B47BD9", bg: "rgba(180,123,217,0.10)" },
    "failed":     { label: "Erkennung fehlgeschlagen",    color: "#F2A93B", bg: "rgba(242,169,59,0.10)" },
  };
  const m = map[source] || { label: source || "Unbekannt", color: "#8DDBEA", bg: "rgba(141,219,234,0.10)" };
  // Wenn note vorhanden + es klingt nach Problem (Quota/Fehler), zusaetzliche Warn-Pille.
  const hasIssue = note && /quota|fehlgeschlag|nicht verfueg|nicht verfügbar|Keine Räume/i.test(note);
  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 6, alignItems: "flex-start" }}>
      <div className="kfg-review-tag" style={{
        background: m.bg, borderColor: m.color, color: m.color,
      }}>
        <span className="dot" style={{ background: m.color }}/>
        {m.label}
      </div>
      {hasIssue && (
        <div style={{
          fontFamily: "var(--font-mono, monospace)", fontSize: 11,
          color: "#F2A93B", background: "rgba(242,169,59,0.08)",
          border: "1px solid rgba(242,169,59,0.25)", borderRadius: 4,
          padding: "5px 9px", maxWidth: 360,
        }}>
          ⚠ {note}
        </div>
      )}
    </div>
  );
}

/* ============================================================
   Server-Upload-Vorschau: NUR das Plan-Bild ohne Box-Overlays.

   Vision-LLMs (Gemini/OpenRouter free tier) sind nicht zuverlaessig genug
   fuer Pixel-genaue Box-Positionen, deshalb zeigen wir den Plan unverfaelscht
   und die erkannten Raeume in der Liste rechts (Label + m^2 + Konfidenz).

   Step 2 (Modul-Auswahl) und Step 4 (Grundriss-Explorer) rendern dann
   GENERISCHE Raum-Geometrien aus den zuverlaessigen Daten (m^2, Anzahl
   und Seite der Fenster, Tuerseite).

   src           Primary image URL (Server-Preview-PNG)
   fallbackSrc   Fallback (lokale data:URL aus FileReader) wenn src fehlt/404
   placeholderKind  "image"|"pdf"|null -- nur fuer Scanning-Phase ohne Server-Bild
============================================================ */
function KfgUploadPreview({ src, fallbackSrc, placeholderKind }) {
  const [imgError, setImgError] = React.useState(false);
  const effectiveSrc = (!imgError && src) ? src : fallbackSrc;

  return (
    <div style={{
      position: "relative", width: "100%", height: "100%",
      background: "#fff", overflow: "hidden",
      border: `1px solid ${KFG_PALETTE.line}`, borderRadius: 4,
    }}>
      {effectiveSrc ? (
        <img src={effectiveSrc} alt="Plan-Vorschau"
             onError={() => setImgError(true)}
             style={{ position: "absolute", inset: 0, width: "100%", height: "100%",
                      objectFit: "contain", pointerEvents: "none" }} />
      ) : (
        <div style={{
          position: "absolute", inset: 0, display: "flex",
          alignItems: "center", justifyContent: "center",
          color: KFG_PALETTE.muted, fontFamily: "ui-monospace, monospace",
          fontSize: 13, padding: 24, textAlign: "center",
        }}>
          {placeholderKind === "pdf" ? "PDF wird analysiert …" : "Plan wird hochgeladen …"}
        </div>
      )}
    </div>
  );
}

/* Map server room-shape -> konfigurator-internal room-shape so Step 2 (Module)
   funktioniert wenn der User einen eigenen Plan hochgeladen hat.

   Wir uebernehmen NUR die zuverlaessigen Felder vom LLM:
     label, area_m2, windows count + window_sides Array, door_side.
   Die Pixel-Bbox/Polygon vom Plan ignorieren wir, weil free Vision-LLMs
   raeumlich zu unzuverlaessig sind. Step 2 + Step 4 rendern stattdessen
   generische Raum-Geometrien aus diesen Daten. */
function adaptServerRoomsToConfigurator(serverRooms) {
  const TYPE_BY_LABEL = [
    [/wohn|essen/i,            "living"],
    [/koch|küche|kueche/i,     "kitchen"],
    [/bad/i,                   "bath"],
    [/wc|du(sche)?/i,          "wc"],
    [/schlaf/i,                "bedroom"],
    [/zimmer/i,                "bedroom"],
    [/kinder/i,                "bedroom"],
    [/diele|vorraum|flur|gang|korridor/i, "hall"],
    [/garderobe|abst/i,        "hall"],
    [/technik|wira|ht|hwr/i,   "tech"],
    [/speis|speiß|kammer/i,    "storage"],
    [/stiege|treppe/i,         "stairs"],
    [/galerie/i,               "hall"],
    [/terrass|balkon|loggia/i, "terrace"],
    [/keller|garage|carport/i, "tech"],
  ];
  const inferType = (label) => {
    for (const [re, t] of TYPE_BY_LABEL) if (re.test(label || "")) return t;
    return "room";
  };
  const inferOutdoor = (label) => /terrass|balkon|loggia|garten/i.test(label || "");
  return serverRooms.map((r, i) => {
    // Anzahl der Fenster vertrauen wir dem LLM (das LLM kann zaehlen).
    // Die SEITE pro Fenster und doorSide sind beim LLM ~50% Trefferquote --
    // wir verwerfen sie und benutzen generische Defaults: alle Fenster oben
    // (typische Annahme Aussenwand), Tuer unten (typische Diele-Anbindung).
    // Damit ist der Preview ehrlich generisch statt halbfalsch positioniert.
    const wnRaw = Array.isArray(r.window_sides) ? r.window_sides.length
                : (typeof r.windows === "number" ? r.windows : _heuristicWindows(r.label, r.area_m2 || 0));
    const windowCount = Math.max(0, Math.min(8, wnRaw));
    return {
      id: `u${i+1}`,
      name: r.label || `Raum ${i+1}`,
      area: r.area_m2 || 10,
      type: inferType(r.label),
      floor: "EG",
      windows: windowCount,
      windowSides: Array(windowCount).fill("top"),
      doorSide: "bottom",
      outdoor: inferOutdoor(r.label),
    };
  });
}

function _heuristicWindows(label, area) {
  const t = (label || "").toLowerCase();
  if (/wohn|essen/.test(t)) return area > 25 ? 3 : 2;
  if (/koch|kueche|küche/.test(t)) return area > 8 ? 1 : 0;
  if (/schlaf|kind|zimmer/.test(t)) return area > 10 ? 1 : 0;
  if (/bad|wc/.test(t)) return area > 4 ? 1 : 0;
  if (/diele|vorraum|flur|gang/.test(t)) return 0;  // innenliegend meist
  if (/technik|wira|hwr/.test(t)) return 0;
  return area > 6 ? 1 : 0;
}

/* Pixel-genaue Raum-Geometrie für assets/bauplan_eg.png.
   Generiert aus EFH_Basting_Bauplan.pdf (S.2 EG, M 1:100) via
   PDF-Render @200dpi → Crop → Hough-Wandgrid + per-label walk-walls.
   Window/door overlays: Pixel-Koordinaten aus BASTING_ROOMS.windowSides/doorSide,
   Fenster 120cm = 94px, Tür 80cm = 63px, Wandstärke 24px @200dpi.
   ViewBox: 1614 × 1049 px. */
const BASTING_GEOM = {
  "r1": {
    x:   56, y:  479, w:  612, h:  320, label: "Wohnen/Essen",
    windows: [{ x:  210, y:  787, w:  94, h:  24, side: "bottom" }, { x:  419, y:  787, w:  94, h:  24, side: "bottom" }, { x:  656, y:  592, w:  24, h:  94, side: "right" }],
    door: { x:   44, y:  608, w:  24, h:  63, side: "left" },
  },
  "r2": {
    x:   56, y:  374, w:  309, h:  207, label: "Kochen",
    windows: [{ x:   44, y:  430, w:  24, h:  94, side: "left" }],
    door: { x:  179, y:  569, w:  63, h:  24, side: "bottom" },
  },
  "r3": {
    x:   67, y:  166, w:  294, h:   93, label: "Speiß",
    windows: [],
    door: { x:  183, y:  247, w:  63, h:  24, side: "bottom" },
  },
  "r4": {
    x:  668, y:  154, w:  118, h:  517, label: "VR",
    windows: [],
    door: { x:  774, y:  381, w:  24, h:  63, side: "right" },
  },
  "r5": {
    x:  667, y:  671, w:  141, h:   93, label: "WC",
    windows: [{ x:  796, y:  670, w:  24, h:  94, side: "right" }],
    door: { x:  655, y:  686, w:  24, h:  63, side: "left" },
  },
  "r6": {
    x:  866, y:  166, w:  126, h:  213, label: "WiRa/HT",
    windows: [],
    door: { x:  980, y:  241, w:  24, h:  63, side: "right" },
  },
  "r7": {
    x: 1096, y:  166, w:  181, h:  203, label: "Bad",
    windows: [{ x: 1139, y:  154, w:  94, h:  24, side: "top" }],
    door: { x: 1155, y:  357, w:  63, h:  24, side: "bottom" },
  },
  "r8": {
    x: 1127, y:  484, w:  422, h:  280, label: "Schlafzimmer",
    windows: [{ x: 1291, y:  472, w:  94, h:  24, side: "top" }, { x: 1115, y:  577, w:  24, h:  94, side: "left" }],
    door: { x: 1537, y:  593, w:  24, h:  63, side: "right" },
  },
  "r9": {
    x:  818, y:  484, w:  300, h:  280, label: "K Zimmer 1",
    windows: [{ x:  921, y:  472, w:  94, h:  24, side: "top" }],
    door: { x:  937, y:  752, w:  63, h:  24, side: "bottom" },
  },
  "r10": {
    x: 1286, y:  166, w:  263, h:  307, label: "K Zimmer 2",
    windows: [{ x: 1370, y:  154, w:  94, h:  24, side: "top" }],
    door: { x: 1386, y:  461, w:  63, h:  24, side: "bottom" },
  },
  "r11": {
    x:  371, y:  166, w:  286, h:  203, label: "Zimmer",
    windows: [{ x:  645, y:  220, w:  24, h:  94, side: "right" }],
    door: { x:  359, y:  236, w:  24, h:  63, side: "left" },
  },
  "r12": {
    x:  807, y:  374, w:  320, h:  130, label: "Garderobe",
    windows: [],
    door: { x:  795, y:  408, w:  24, h:  63, side: "left" },
  },
  "r13": {
    x:   31, y:  801, w:  626, h:  248, label: "Terrasse", outdoor: true,
    windows: [],
    door: null,
  },
  "r14": {
    x:  657, y:   24, w:  200, h:  110, label: "Zugang Haupt",
    windows: [],
    door: { x:  726, y:  122, w:  63, h:  24, side: "bottom" },
  },
};
window.BASTING_GEOM = BASTING_GEOM;
window.BASTING_GEOM_VIEWBOX = { w: 1614, h: 1049 };

/* KfgPlanSVG — zeigt das echte EG-PNG als Hintergrund, mit animierten
   Raum-Highlights + Fenster-/Tür-Markierungen aus BASTING_GEOM. */
function KfgPlanSVG({ detected }) {
  const ROOMS_GEOM = BASTING_GEOM;
  const detectedIds = new Set(detected.map(r => r.id));
  const VB = window.BASTING_GEOM_VIEWBOX;

  return (
    <div style={{ position: "relative", width: "100%", aspectRatio: `${VB.w}/${VB.h}`, background: KFG_PALETTE.paper, overflow: "hidden" }}>
      <img
        src="assets/bauplan_eg.png"
        alt="Erdgeschoss-Plan"
        style={{
          position: "absolute", inset: 0,
          width: "100%", height: "100%",
          objectFit: "contain",
          opacity: 0.55,
          filter: "grayscale(1) contrast(1.05)",
          pointerEvents: "none",
        }}
      />
      <svg
        viewBox={`0 0 ${VB.w} ${VB.h}`}
        preserveAspectRatio="xMidYMid meet"
        style={{ position: "absolute", inset: 0, width: "100%", height: "100%" }}
      >
        {Object.entries(ROOMS_GEOM).map(([id, g]) => {
          const isDetected = detectedIds.has(id);
          const windows = (isDetected && g.windows) || [];
          const door = isDetected ? g.door : null;
          return (
            <g key={id}>
              <rect
                x={g.x} y={g.y} width={g.w} height={g.h}
                fill={isDetected ? (g.outdoor ? "rgba(141,219,234,0.20)" : "rgba(58,189,208,0.20)") : "transparent"}
                stroke={isDetected ? KFG_PALETTE.accent : "transparent"}
                strokeWidth={isDetected ? "3" : "0"}
                strokeDasharray={g.outdoor ? "8 5" : ""}
                style={{ transition: "all 0.5s" }}
              />
              {windows.map((w, i) => (
                <rect
                  key={`${id}-w-${i}`}
                  x={w.x} y={w.y} width={w.w} height={w.h}
                  fill="#8DDBEA" opacity="0.9"
                  stroke="#3ABDD0" strokeWidth="1.5"
                  style={{ animation: `kfgFadeIn 0.5s ${0.15 + i * 0.06}s both` }}
                />
              ))}
              {door && (
                <rect
                  x={door.x} y={door.y} width={door.w} height={door.h}
                  fill="#F2A93B" opacity="0.9"
                  stroke="#C77A1A" strokeWidth="1.5"
                  style={{ animation: "kfgFadeIn 0.5s 0.25s both" }}
                />
              )}
              {isDetected && (
                <text
                  x={g.x + g.w/2} y={g.y + g.h/2 + 8}
                  fontFamily="ui-monospace, monospace" fontSize="22" fontWeight="600"
                  fill={KFG_PALETTE.ink} textAnchor="middle"
                  style={{ animation: "kfgFadeIn 0.4s forwards", paintOrder: "stroke", stroke: "rgba(10,27,40,0.85)", strokeWidth: "5" }}
                >{g.label}</text>
              )}
            </g>
          );
        })}
      </svg>
    </div>
  );
}

/* ============================================================
   STEP 2 — Module per room
============================================================ */

/* Visualizes a single room with its features placed inside.
   - Window slots on top edge, door on bottom edge
   - Module dots appear when modules are selected, with positions that make sense
*/
function KfgRoomPreview({ room, activeMods, modules, moduleCounts }) {
  // Aspect-aware rectangle inside 480×280 viewBox
  const VB_W = 480, VB_H = 280, PAD = 36;
  const aw = VB_W - 2*PAD, ah = VB_H - 2*PAD;
  // Aspekt: bevorzugt aus echter Detection-Geometrie (Plan-Upload), sonst
  // heuristisch aus Fläche. Geom-Variante macht den Preview-Raum proportional
  // korrekt -- ein langer Diele-Korridor sieht dann auch wie einer aus.
  const ratio = (room.geom && room.geom.w > 0 && room.geom.h > 0)
    ? Math.min(2.5, Math.max(0.4, room.geom.w / room.geom.h))
    : Math.min(2.0, Math.max(0.7, Math.sqrt(room.area / 16)));
  let rw, rh;
  if (ratio >= 1) { rw = aw; rh = aw / ratio; }
  else            { rh = ah; rw = ah * ratio; }
  if (rh > ah) { rh = ah; rw = ah * ratio; }
  if (rw > aw) { rw = aw; rh = aw / ratio; }
  const rx = (VB_W - rw) / 2;
  const ry = (VB_H - rh) / 2;

  // Window positions: pro Wand verteilt
  // Wenn windowSides nicht gegeben → fallback: alle oben
  const sides = (Array.isArray(room.windowSides) && room.windowSides.length === room.windows)
    ? room.windowSides
    : Array(Math.min(room.windows, 5)).fill("top");
  const sideGroups = { top: [], right: [], bottom: [], left: [] };
  sides.slice(0, 5).forEach((s, i) => { (sideGroups[s] || sideGroups.top).push(i); });

  const windows = [];
  for (const side of ["top","right","bottom","left"]) {
    const idxList = sideGroups[side];
    const n = idxList.length;
    if (n === 0) continue;
    const isHorizontal = (side === "top" || side === "bottom");
    const wallLen = isHorizontal ? rw : rh;
    for (let k = 0; k < n; k++) {
      const t = (k + 1) / (n + 1);
      const winLen = Math.min(60, wallLen / (n + 1) * 0.7);
      if (side === "top")    windows.push({ side, x: rx + rw*t - winLen/2, y: ry,        w: winLen, h: 4 });
      if (side === "bottom") windows.push({ side, x: rx + rw*t - winLen/2, y: ry + rh-4, w: winLen, h: 4 });
      if (side === "left")   windows.push({ side, x: rx,         y: ry + rh*t - winLen/2, w: 4, h: winLen });
      if (side === "right")  windows.push({ side, x: rx + rw-4,  y: ry + rh*t - winLen/2, w: 4, h: winLen });
    }
  }

  // Door — pro Wand
  const doorSide = room.doorSide || "bottom";
  const doorLen = 36;
  // anchor point = wo das Türscharnier in der Wand sitzt; door öffnet ins Rauminnere
  let doorAnchor, doorEnd, doorArcEnd;
  if (doorSide === "bottom") {
    const cx = rx + rw * 0.3;
    doorAnchor = { x: cx, y: ry + rh };
    doorEnd =    { x: cx + doorLen, y: ry + rh };
    doorArcEnd = { x: cx, y: ry + rh - doorLen }; // swing inward (up)
  } else if (doorSide === "top") {
    const cx = rx + rw * 0.3;
    doorAnchor = { x: cx, y: ry };
    doorEnd =    { x: cx + doorLen, y: ry };
    doorArcEnd = { x: cx, y: ry + doorLen };
  } else if (doorSide === "left") {
    const cy = ry + rh * 0.3;
    doorAnchor = { x: rx, y: cy };
    doorEnd =    { x: rx, y: cy + doorLen };
    doorArcEnd = { x: rx + doorLen, y: cy };
  } else { // right
    const cy = ry + rh * 0.3;
    doorAnchor = { x: rx + rw, y: cy };
    doorEnd =    { x: rx + rw, y: cy + doorLen };
    doorArcEnd = { x: rx + rw - doorLen, y: cy };
  }
  // Door rendering uses doorX/doorY/doorW; das SVG ist auf horizontale
  // (top/bottom) Tueren ausgelegt -- fuer left/right fallen Rect+Arc visuell
  // nicht ideal, aber zumindest kein Crash mehr (war: ReferenceError doorX).
  const doorX = Math.min(doorAnchor.x, doorEnd.x);
  const doorY = doorAnchor.y;
  const doorW = doorLen;

  // Module placements
  const isOn = (k) => activeMods.includes(k);

  // Light points: ceiling grid + circuit-Zuordnung
  const lights = [];
  let lightCircuits = 1;
  if (isOn("licht")) {
    const lichtMod = modules.licht;
    lightCircuits = lichtMod ? Math.max(1, window.getModuleCount(lichtMod, room, moduleCounts, room.id, "licht")) : 1;
    const cols = Math.max(2, Math.ceil(rw / 90));
    const rows = Math.max(1, Math.ceil(rh / 90));
    const total = cols * rows;
    let idx = 0;
    for (let i = 0; i < cols; i++) {
      for (let j = 0; j < rows; j++) {
        // Verteile Spots gleichmäßig auf circuits: aufeinanderfolgende Indizes → gleicher Kreis-Block
        const circuit = Math.floor((idx * lightCircuits) / total);
        lights.push({
          x: rx + rw * (i+1) / (cols+1),
          y: ry + rh * (j+1) / (rows+1),
          circuit: Math.min(lightCircuits - 1, circuit),
        });
        idx++;
      }
    }
  }

  // Sockets: along walls, schaltbar
  const sockets = [];
  if (isOn("steckdosen")) {
    const stkMod = modules.steckdosen;
    const count = stkMod ? window.getModuleCount(stkMod, room, moduleCounts, room.id, "steckdosen") : Math.max(2, Math.ceil(room.area / 12));
    for (let i = 0; i < count; i++) {
      const side = i % 4;
      const t = ((Math.floor(i/4) + 1) / (Math.ceil(count/4) + 1));
      let x, y;
      if (side === 0)      { x = rx + rw * t; y = ry + rh - 5; }
      else if (side === 1) { x = rx + rw - 5; y = ry + rh * t; }
      else if (side === 2) { x = rx + rw * t; y = ry + 5; }
      else                 { x = rx + 5;      y = ry + rh * t; }
      sockets.push({ x, y });
    }
  }

  return (
    <div className="kfg-roompreview">
      <svg viewBox={`0 0 ${VB_W} ${VB_H}`}>
        {/* Floor pattern */}
        <defs>
          <pattern id="kfg-floor" width="14" height="14" patternUnits="userSpaceOnUse">
            <path d="M 14 0 L 0 0 0 14" fill="none" stroke={KFG_PALETTE.line} strokeWidth="0.4"/>
          </pattern>
        </defs>

        {/* Room rectangle */}
        <rect x={rx} y={ry} width={rw} height={rh} fill="url(#kfg-floor)" stroke={KFG_PALETTE.muted} strokeWidth="2"/>

        {/* Windows: knock out wall, draw window frame */}
        {windows.map((w, i) => (
          <g key={`w${i}`}>
            <rect x={w.x} y={w.y - 2} width={w.w} height={4} fill={KFG_PALETTE.paper}/>
            <line x1={w.x} y1={w.y} x2={w.x + w.w} y2={w.y} stroke={KFG_PALETTE.accent} strokeWidth="1.5" opacity="0.6"/>
            <line x1={w.x + w.w/2} y1={w.y - 2} x2={w.x + w.w/2} y2={w.y + 2} stroke={KFG_PALETTE.muted} strokeWidth="0.5"/>
            {/* Beschattung indicator */}
            {isOn("beschattung") && (
              <g style={{ animation: "kfgFadeIn 0.4s forwards" }}>
                <rect x={w.x} y={w.y - 7} width={w.w} height={3} fill={KFG_PALETTE.warm} opacity="0.85"/>
                <line x1={w.x + 2} y1={w.y - 5.5} x2={w.x + w.w - 2} y2={w.y - 5.5} stroke={KFG_PALETTE.ink} strokeWidth="0.4" opacity="0.4"/>
              </g>
            )}
          </g>
        ))}

        {/* Door */}
        <g>
          <rect x={doorX} y={doorY - 2} width={doorW} height={4} fill={KFG_PALETTE.paper}/>
          <path d={`M ${doorX} ${doorY} A ${doorW} ${doorW} 0 0 1 ${doorX + doorW} ${doorY}`} fill="none" stroke={KFG_PALETTE.muted} strokeWidth="0.6" strokeDasharray="2 2"/>
          <line x1={doorX} y1={doorY} x2={doorX} y2={doorY - doorW} stroke={KFG_PALETTE.muted} strokeWidth="0.8"/>
          {/* Zutritt */}
          {isOn("zutritt") && (
            <g style={{ animation: "kfgFadeIn 0.4s forwards" }}>
              <circle cx={doorX + doorW + 8} cy={doorY - 4} r="4" fill={KFG_PALETTE.accent}/>
              <text x={doorX + doorW + 8} y={doorY + 14} fontFamily="monospace" fontSize="7" fill={KFG_PALETTE.muted} textAnchor="middle">RFID</text>
            </g>
          )}
        </g>

        {/* Lights — pro Schaltkreis gruppiert, sequenziell animiert */}
        {lights.map((l, i) => {
          // Zykluslänge: 1 Schaltkreis = 2.4s solo; mehrere Kreise = sequenziell + Pause
          const cycle = Math.max(2.4, lightCircuits * 1.2 + 0.6);
          const slot = lightCircuits === 1 ? 0 : (l.circuit / lightCircuits) * cycle;
          return (
            <g key={`l${i}`} style={{
              animation: `kfgFadeIn 0.4s forwards, kfgLightPulse ${cycle}s ${slot}s infinite`,
              transformOrigin: `${l.x}px ${l.y}px`,
            }}>
              <circle cx={l.x} cy={l.y} r="9" fill={KFG_PALETTE.warm} opacity="0" style={{
                animation: `kfgLightGlow ${cycle}s ${slot}s infinite`,
              }}/>
              <circle cx={l.x} cy={l.y} r="6" fill="none" stroke={KFG_PALETTE.warm} strokeWidth="1"/>
              <circle cx={l.x} cy={l.y} r="2.5" fill={KFG_PALETTE.warm}/>
            </g>
          );
        })}

        {/* Heating: floor zones */}
        {isOn("heizung") && (
          <g style={{ animation: "kfgFadeIn 0.4s forwards" }}>
            <rect x={rx + 4} y={ry + 4} width={rw - 8} height={rh - 8} fill="rgba(212,128,90,0.07)" stroke="rgba(212,128,90,0.4)" strokeDasharray="3 4" strokeWidth="0.6"/>
            <text x={rx + rw - 6} y={ry + rh - 6} fontFamily="monospace" fontSize="7" fill="rgba(212,128,90,0.8)" textAnchor="end">FBH 21°C</text>
          </g>
        )}

        {/* Klima: ceiling diffuser */}
        {isOn("klima") && (
          <g style={{ animation: "kfgFadeIn 0.4s forwards" }}>
            <rect x={rx + rw/2 - 12} y={ry + 6} width={24} height={8} fill="none" stroke={KFG_PALETTE.accent} strokeWidth="0.8"/>
            <line x1={rx + rw/2 - 8} y1={ry + 8} x2={rx + rw/2 + 8} y2={ry + 8} stroke={KFG_PALETTE.accent} strokeWidth="0.5"/>
            <line x1={rx + rw/2 - 8} y1={ry + 12} x2={rx + rw/2 + 8} y2={ry + 12} stroke={KFG_PALETTE.accent} strokeWidth="0.5"/>
          </g>
        )}

        {/* Sockets */}
        {/* Sockets — sequenziell aufpulsend */}
        {sockets.map((s, i) => {
          const cycle = Math.max(3, sockets.length * 0.35 + 1.5);
          const slot = sockets.length === 1 ? 0 : (i / sockets.length) * cycle;
          return (
            <g key={`s${i}`} style={{
              animation: "kfgFadeIn 0.4s forwards",
              transformOrigin: `${s.x}px ${s.y}px`,
            }}>
              <circle cx={s.x} cy={s.y} r="5" fill={KFG_PALETTE.accent} opacity="0" style={{
                animation: `kfgSocketGlow ${cycle}s ${slot}s infinite`,
              }}/>
              <circle cx={s.x} cy={s.y} r="3" fill={KFG_PALETTE.card} stroke={KFG_PALETTE.muted} strokeWidth="0.8"/>
              <circle cx={s.x - 0.8} cy={s.y} r="0.6" fill={KFG_PALETTE.muted}/>
              <circle cx={s.x + 0.8} cy={s.y} r="0.6" fill={KFG_PALETTE.muted}/>
            </g>
          );
        })}

        {/* Multimedia: speaker icons */}
        {isOn("multimedia") && (
          <g style={{ animation: "kfgFadeIn 0.4s forwards" }}>
            <rect x={rx + 12} y={ry + 12} width={10} height={14} fill="none" stroke={KFG_PALETTE.accent} strokeWidth="0.8"/>
            <circle cx={rx + 17} cy={ry + 17} r="2" fill="none" stroke={KFG_PALETTE.accent} strokeWidth="0.6"/>
            <circle cx={rx + 17} cy={ry + 22} r="1" fill={KFG_PALETTE.accent}/>
            <rect x={rx + rw - 22} y={ry + 12} width={10} height={14} fill="none" stroke={KFG_PALETTE.accent} strokeWidth="0.8"/>
            <circle cx={rx + rw - 17} cy={ry + 17} r="2" fill="none" stroke={KFG_PALETTE.accent} strokeWidth="0.6"/>
            <circle cx={rx + rw - 17} cy={ry + 22} r="1" fill={KFG_PALETTE.accent}/>
          </g>
        )}

        {/* Sensorik: motion detector in corner */}
        {isOn("sensorik") && (
          <g style={{ animation: "kfgFadeIn 0.4s forwards" }}>
            <circle cx={rx + rw - 14} cy={ry + 14} r="4" fill={KFG_PALETTE.accent} opacity="0.85"/>
            <path d={`M ${rx + rw - 14} ${ry + 14} L ${rx + rw - 30} ${ry + 30} M ${rx + rw - 14} ${ry + 14} L ${rx + rw - 4} ${ry + 30}`} stroke={KFG_PALETTE.accent} strokeWidth="0.5" opacity="0.4" strokeDasharray="2 2"/>
          </g>
        )}

        {/* Energie: flash icon */}
        {isOn("energie") && (
          <g style={{ animation: "kfgFadeIn 0.4s forwards" }}>
            <circle cx={rx + 14} cy={ry + rh - 14} r="6" fill={KFG_PALETTE.warm} opacity="0.2"/>
            <path d={`M ${rx + 13} ${ry + rh - 18} L ${rx + 11} ${ry + rh - 13} L ${rx + 14} ${ry + rh - 13} L ${rx + 12} ${ry + rh - 8} L ${rx + 17} ${ry + rh - 14} L ${rx + 14} ${ry + rh - 14} Z`} fill={KFG_PALETTE.warm}/>
          </g>
        )}

        {/* Alarm: siren in corner */}
        {isOn("alarm") && (
          <g style={{ animation: "kfgFadeIn 0.4s forwards" }}>
            <circle cx={rx + rw - 14} cy={ry + rh - 14} r="5" fill="none" stroke="#E66B5C" strokeWidth="1"/>
            <circle cx={rx + rw - 14} cy={ry + rh - 14} r="2" fill="#E66B5C"/>
          </g>
        )}

        {/* Room label */}
        <text x={rx + rw/2} y={ry - 12} fontFamily="var(--font-display, serif)" fontSize="14" fill={KFG_PALETTE.ink} textAnchor="middle">{room.name}</text>
        <text x={rx + rw/2} y={ry + rh + 22} fontFamily="monospace" fontSize="9" fill={KFG_PALETTE.muted} textAnchor="middle">{room.area} m² · {room.windows} Fenster</text>
      </svg>

      {activeMods.length > 0 && (
        <div className="legend">
          {activeMods.map(k => (
            <div key={k} className="row">
              <span className="sw" style={{ background: legendColor(k) }}/>
              <span>{modules[k]?.label || k}</span>
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

function legendColor(k) {
  switch(k) {
    case "licht":       return KFG_PALETTE.warm;
    case "beschattung": return KFG_PALETTE.warm;
    case "heizung":     return "#D4805A";
    case "klima":       return KFG_PALETTE.accent;
    case "zutritt":     return KFG_PALETTE.accent;
    case "multimedia":  return KFG_PALETTE.accent;
    case "sensorik":    return KFG_PALETTE.accent;
    case "steckdosen":  return KFG_PALETTE.muted;
    case "energie":     return KFG_PALETTE.warm;
    case "alarm":       return "#E66B5C";
    default:            return KFG_PALETTE.accent;
  }
}

/* Generische Sub-Bereich-Namen pro Raumtyp fuer Lichtkreise.
   Beispiel: Gang (hall, 2 Kreise) -> "Licht Gang", "Licht Garderobe".
   Erster Eintrag uebernimmt den Raum-Namen (matcht oft den ersten Sub-
   Bereich, wird dann uebersprungen). Wenn mehr Kreise als Sub-Bereiche
   existieren, fuellen wir mit "Kreis N" auf. */
const KFG_LIGHT_SUBAREAS = {
  living:    ["Wohnen", "Essen", "Sofa", "Vitrine", "Wand"],
  kitchen:   ["Kochen", "Esstisch", "Arbeitsplatte", "Insel"],
  bath:      ["Bad", "Spiegel", "Dusche", "Wanne"],
  wc:        ["WC", "Spiegel"],
  bedroom:   ["Schlafen", "Nachttisch", "Schrank", "Lesen"],
  hall:      ["Gang", "Garderobe", "Eingang", "Treppe"],
  tech:      ["Technik", "Arbeitsfläche"],
  storage:   ["Speis", "Regal"],
  stairs:    ["Treppe", "Geländer"],
  terrace:   ["Terrasse", "Wand"],
  room:      ["Zimmer", "Schreibtisch", "Wand"],
  entry:     ["Eingang", "Außen"],
};

function kfgLichtCircuitNames(room, count) {
  const subs = KFG_LIGHT_SUBAREAS[room.type] || ["Decke", "Wand", "Spot"];
  const out = [room.name];
  for (const sub of subs) {
    if (out.length >= count) break;
    if (sub.toLowerCase() === (room.name || "").toLowerCase()) continue;
    out.push(sub);
  }
  while (out.length < count) out.push(`Kreis ${out.length}`);
  return out.slice(0, count);
}

/* ============================================================
   Live-Dashboard fuer den AKTIVEN Raum -- nachgebaut aus dem
   Smart-Home-Demo (SMDRoomDetail), aber dynamisch: Sektionen
   erscheinen nur wenn das passende Modul aktiv ist.
   - licht       -> Beleuchtung-Card mit Dimmer
   - heizung     -> Raumklima Thermostat + Thermostat-Detail
   - klima       -> Raumklima (alternativ wenn keine Heizung)
   - multimedia  -> Mediaplayer
   - sensorik    -> Praesenz-Tiles + Temperatur-Pill oben
   - steckdosen  -> Hauptschalter-Tiles
   - energie     -> Verbrauchs-Tile
   - beschattung -> Beschattungs-Card
   - zutritt     -> Zutritt-Tile
   - alarm       -> Alarm-Tile
   Szenen rendern wir wenn Licht aktiv ist (Szenen = Lichtszenen).
   Automationen werden aus den aktiven Modulen abgeleitet.
============================================================ */
function KfgRoomDashboard({ room, activeMods, modules, moduleCounts, ausstattungsklasse }) {
  // Demo-Helpers vom Smart-Home-Demo nachladen (ueber window expose).
  const SMD = window.SMD_PALETTE;
  const Icons = window.SMDIcons;
  const Tile = window.SMDTile;

  // dim: pro Lichtkreis ein Wert (per Index). Wir verwenden ein Object
  // damit Schluesselwechsel beim Raum-Switch keinen Stale-State erzeugen.
  const [dimByKey, setDimByKey] = React.useState({});
  const dimOf = (key, fallback = 60) => dimByKey[key] != null ? dimByKey[key] : fallback;
  const setDim = (key, v) => setDimByKey({ ...dimByKey, [key]: v });
  const [temp, setTemp] = React.useState(21.0);
  const [shade, setShade] = React.useState(40);
  const [scene, setScene] = React.useState("hell");

  if (!SMD || !Icons || !Tile) {
    // smart-home-demo.jsx noch nicht geladen -- defensiver Fallback
    return <div className="kfg-live-preview"><div className="kfg-live-empty">Lade Vorschau…</div></div>;
  }

  const has = (k) => activeMods.includes(k);
  const count = (k) => {
    const mod = modules[k];
    if (!mod || !mod.countable || !window.getModuleCount) return 1;
    return window.getModuleCount(mod, room, moduleCounts, room.id, k, ausstattungsklasse);
  };

  // Welche Sektionen werden gerendert? Wenn keine -> Empty-State.
  const showAny = activeMods.length > 0;

  // Section-Header (kompakt fuer ~320px Spalte)
  const Section = ({ title, action, children }) => (
    <div style={{ marginBottom: 14 }}>
      <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 8 }}>
        <h4 style={{ fontSize: 12, fontWeight: 500, color: SMD.text, margin: 0,
                     fontFamily: "Inter, sans-serif", letterSpacing: 0.2 }}>{title}</h4>
        {action}
      </div>
      {children}
    </div>
  );

  const Card = ({ children, style }) => (
    <div style={{
      background: SMD.bgCard, border: `1px solid ${SMD.border}`,
      borderRadius: 10, padding: 12, ...style,
    }}>{children}</div>
  );

  // Bullet (Status-Pille) wie im Demo
  const StatusPill = ({ icon, color, text }) => (
    <span style={{
      fontSize: 10, padding: "2px 8px", borderRadius: 100,
      background: `${color}22`, color: color, fontFamily: "Inter, sans-serif",
      fontWeight: 500, display: "inline-flex", alignItems: "center", gap: 4,
    }}>
      <span style={{ display: "block", width: 9, height: 9 }}>{icon(color)}</span>
      {text}
    </span>
  );

  return (
    <div className="kfg-live-preview" style={{ background: SMD.bgDeep, color: SMD.text }}>
      {/* Header */}
      <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between",
                    paddingBottom: 14, marginBottom: 14, borderBottom: `1px solid ${SMD.border}` }}>
        <div>
          <div style={{ fontSize: 11, color: SMD.accent, fontFamily: "JetBrains Mono, monospace",
                        letterSpacing: "0.12em", textTransform: "uppercase" }}>Live-Vorschau</div>
          <div style={{ fontSize: 18, fontWeight: 500, fontFamily: "Inter, sans-serif", marginTop: 2 }}>
            {room.name}
          </div>
        </div>
        {(has("heizung") || has("klima") || has("sensorik")) && (
          <span style={{ display: "inline-flex", alignItems: "center", gap: 6,
                         padding: "5px 10px", borderRadius: 100,
                         background: SMD.bgCard, border: `1px solid ${SMD.border}`,
                         fontSize: 11, fontFamily: "Inter, sans-serif" }}>
            <span style={{ display: "block", width: 11, height: 11, color: SMD.warm }}>
              {Icons.thermo(SMD.warm)}
            </span>
            21,0 °C
          </span>
        )}
      </div>

      {!showAny && (
        <div className="kfg-live-empty" style={{
          background: "rgba(58,189,208,0.06)", border: `1px dashed ${SMD.border}`,
          color: SMD.textDim, padding: 14, borderRadius: 8, fontSize: 12, lineHeight: 1.55,
        }}>
          Wähle links Module aus — die zugehörigen Steuerungen für <strong style={{ color: SMD.text }}>{room.name}</strong> erscheinen dann hier so wie sie später im Dashboard aussehen.
        </div>
      )}

      {/* Beleuchtung -- ein Card PRO Lichtkreis (N Buttons mit generisch
          sinnvollen Namen, statt einer Karte mit Count). */}
      {has("licht") && (() => {
        const n = Math.max(1, count("licht"));
        const names = kfgLichtCircuitNames(room, n);
        return (
          <Section title="Beleuchtung" action={
            <StatusPill icon={Icons.power} color={SMD.pv} text="Ein" />
          }>
            <div style={{ display: "flex", flexDirection: "column", gap: 6 }}>
              {names.map((sub, i) => {
                const key = `${room.id}-licht-${i}`;
                const v = dimOf(key, 60 - i * 15);
                return (
                  <Card key={i}>
                    <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 8 }}>
                      <div style={{ width: 26, height: 26, borderRadius: 999, background: `${SMD.pv}22`,
                                    display: "flex", alignItems: "center", justifyContent: "center" }}>
                        <span style={{ display: "block", width: 13, height: 13 }}>{Icons.light(SMD.pv)}</span>
                      </div>
                      <div style={{ flex: 1, minWidth: 0 }}>
                        <div style={{ fontSize: 12, fontWeight: 500, fontFamily: "Inter, sans-serif",
                                      whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>
                          Licht {sub}
                        </div>
                        <div style={{ fontSize: 10, color: SMD.textDim, fontFamily: "Inter, sans-serif" }}>
                          {v} %
                        </div>
                      </div>
                    </div>
                    <input type="range" min="0" max="100" value={v}
                           onChange={(e) => setDim(key, parseInt(e.target.value))}
                           style={{ width: "100%", height: 4, accentColor: SMD.pv, cursor: "pointer" }} />
                  </Card>
                );
              })}
            </div>
          </Section>
        );
      })()}

      {/* Beschattung */}
      {has("beschattung") && room.windows > 0 && (
        <Section title="Beschattung">
          <Card>
            <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 10 }}>
              <div style={{ width: 28, height: 28, borderRadius: 999, background: `${SMD.water}22`,
                            display: "flex", alignItems: "center", justifyContent: "center" }}>
                <span style={{ display: "block", width: 14, height: 14 }}>{Icons.grid(SMD.water)}</span>
              </div>
              <div style={{ flex: 1 }}>
                <div style={{ fontSize: 12, fontWeight: 500, fontFamily: "Inter, sans-serif" }}>
                  Raffstore {room.name}
                </div>
                <div style={{ fontSize: 10, color: SMD.textDim, fontFamily: "Inter, sans-serif" }}>
                  {shade} % · {room.windows} Fenster
                </div>
              </div>
            </div>
            <input type="range" min="0" max="100" value={shade}
                   onChange={(e) => setShade(parseInt(e.target.value))}
                   style={{ width: "100%", height: 4, accentColor: SMD.water, cursor: "pointer" }} />
          </Card>
        </Section>
      )}

      {/* Raumklima -- bevorzugt Heizung, sonst Klima */}
      {(has("heizung") || has("klima")) && (
        <Section title="Raumklima">
          <Card>
            <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 10 }}>
              <div style={{ width: 28, height: 28, borderRadius: 999, background: `${SMD.warm}22`,
                            display: "flex", alignItems: "center", justifyContent: "center" }}>
                <span style={{ display: "block", width: 14, height: 14 }}>{Icons.fire(SMD.warm)}</span>
              </div>
              <div style={{ flex: 1 }}>
                <div style={{ fontSize: 12, fontWeight: 500, fontFamily: "Inter, sans-serif" }}>
                  {has("heizung") ? "Thermostat" : "Klima"} {room.name}
                </div>
                <div style={{ fontSize: 10, color: SMD.textDim, fontFamily: "Inter, sans-serif" }}>
                  {has("heizung") ? "Heizbetrieb" : "Auto"}
                </div>
              </div>
            </div>
            <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 6 }}>
              <button onClick={() => setTemp(Math.max(15, temp - 0.5))} style={{
                width: 28, height: 28, borderRadius: 6, border: `1px solid ${SMD.border}`,
                background: SMD.bgDeep, color: SMD.text, cursor: "pointer", fontSize: 14,
              }}>−</button>
              <div style={{ fontSize: 14, fontWeight: 500, fontFamily: "Inter, sans-serif" }}>{temp.toFixed(1)} °C</div>
              <button onClick={() => setTemp(Math.min(28, temp + 0.5))} style={{
                width: 28, height: 28, borderRadius: 6, border: `1px solid ${SMD.border}`,
                background: SMD.bgDeep, color: SMD.text, cursor: "pointer", fontSize: 14,
              }}>+</button>
            </div>
          </Card>
        </Section>
      )}

      {/* Mediaplayer */}
      {has("multimedia") && (
        <Section title="Mediaplayer">
          <Card style={{ display: "flex", alignItems: "center", gap: 10 }}>
            <div style={{ width: 28, height: 28, borderRadius: 6, background: `${SMD.accent}22`,
                          display: "flex", alignItems: "center", justifyContent: "center" }}>
              <span style={{ display: "block", width: 14, height: 14 }}>{Icons.cast(SMD.accent)}</span>
            </div>
            <div>
              <div style={{ fontSize: 12, fontWeight: 500, fontFamily: "Inter, sans-serif" }}>
                Lautsprecher {room.name}
              </div>
              <div style={{ fontSize: 10, color: SMD.textDim, fontFamily: "Inter, sans-serif" }}>Pausiert</div>
            </div>
          </Card>
        </Section>
      )}

      {/* Szenen -- nur wenn Licht aktiv (Szenen = Lichtszenen) */}
      {has("licht") && (
        <Section title="Szenen">
          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 6 }}>
            {[
              { id: "nacht",   name: "Nachtlicht" },
              { id: "hell",    name: "Hell" },
              { id: "gedimmt", name: "Gedimmt" },
              { id: "fokus",   name: "Fokus" },
            ].map(s => {
              const active = scene === s.id;
              return (
                <button key={s.id} onClick={() => setScene(s.id)} style={{
                  background: active ? `${SMD.accent}14` : SMD.bgCard,
                  border: `1px solid ${active ? SMD.borderHi : SMD.border}`,
                  borderRadius: 8, padding: "8px 10px", display: "flex", alignItems: "center", gap: 8,
                  cursor: "pointer", textAlign: "left", color: "inherit",
                  fontFamily: "Inter, sans-serif",
                }}>
                  <span style={{ display: "block", width: 11, height: 11 }}>
                    {Icons.scenes(active ? SMD.accent : SMD.soft)}
                  </span>
                  <div style={{ fontSize: 11, fontWeight: 500 }}>{s.name}</div>
                </button>
              );
            })}
          </div>
        </Section>
      )}

      {/* Hauptschalter (Steckdosen + Energie) */}
      {(has("steckdosen") || has("energie")) && (
        <Section title={`Hauptschalter ${room.name}`}>
          <div style={{ display: "flex", gap: 6 }}>
            {has("energie") && (
              <Tile icon="bolt" name="Verbrauch" state="0 W" on={true} color={SMD.consumption} />
            )}
            {has("steckdosen") && (
              <Tile icon="light" name={`${count("steckdosen")} Steckdosen`} state="Aus" on={true} color={SMD.pv} />
            )}
          </div>
        </Section>
      )}

      {/* Thermostat-Detail */}
      {has("heizung") && (
        <Section title="Thermostat — Detail">
          <div style={{ display: "flex", gap: 6 }}>
            <Tile icon="valve" name="Ventil" state="0,0 %" on={true} color={SMD.water} />
            <Tile icon="thermo" name="Temperatur" state="21,0 °C" on={true} color={SMD.warm} />
          </div>
        </Section>
      )}

      {/* Praesenz / Sensorik */}
      {has("sensorik") && (
        <Section title={`Präsenz ${room.name}`}>
          <div style={{ display: "flex", gap: 6 }}>
            <Tile icon="presence" name="Anwesenheit" state="Anwesend" on={true} color={SMD.battery} />
            <Tile icon="weather" name="Helligkeit" state="380 lx" on={true} color={SMD.pv} />
          </div>
        </Section>
      )}

      {/* Sicherheit */}
      {(has("zutritt") || has("alarm")) && (
        <Section title="Sicherheit">
          <div style={{ display: "flex", gap: 6 }}>
            {has("zutritt") && <Tile icon="lock" name="Zutritt" state="Verriegelt" on={true} color={SMD.warm} />}
            {has("alarm")   && <Tile icon="shield" name="Alarm" state="Bereit" on={true} color={SMD.battery} />}
          </div>
        </Section>
      )}

      {/* Automationen -- aus aktiven Modulen abgeleitet */}
      {showAny && (
        <Section title="Automationen">
          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 6 }}>
            {has("licht") && <Tile icon="flow" name={`Licht ${room.name}`} state="Aktiv" on={true} color={SMD.battery} />}
            {has("heizung") && <Tile icon="flow" name={`Heizung ${room.name}`} state="Aktiv" on={true} color={SMD.battery} />}
            {has("beschattung") && <Tile icon="flow" name="Beschattung" state="Auto" on={true} color={SMD.battery} />}
            {has("sensorik") && <Tile icon="flow" name="Anwesenheit" state="Aktiv" on={true} color={SMD.battery} />}
          </div>
        </Section>
      )}
    </div>
  );
}

function KfgStep2Modules({ pathKey, rooms, modules, assignments, setAssignments, moduleCounts, setModuleCounts, ausstattungsklasse = 1, setAusstattungsklasse, onNext, onBack }) {
  const indoorRooms = rooms.filter(r => !r.outdoor);
  const [activeRoomId, setActiveRoomId] = React.useState(indoorRooms[0]?.id);
  const [previewDevice, setPreviewDevice] = React.useState("tablet"); // "tablet" | "phone"
  const [previewFullscreen, setPreviewFullscreen] = React.useState(false);
  const isGewerbe = pathKey === "gewerbe";
  const unitLabel = isGewerbe ? "Bereiche" : "Räume";
  const moduleLabel = isGewerbe ? "Funktionen" : "Module";

  // ESC schliesst Fullscreen
  React.useEffect(() => {
    if (!previewFullscreen) return;
    const onKey = (e) => { if (e.key === "Escape") setPreviewFullscreen(false); };
    window.addEventListener("keydown", onKey);
    const prevOverflow = document.body.style.overflow;
    document.body.style.overflow = "hidden";
    return () => {
      window.removeEventListener("keydown", onKey);
      document.body.style.overflow = prevOverflow;
    };
  }, [previewFullscreen]);
  const activeRoom = rooms.find(r => r.id === activeRoomId);
  const activeMods = assignments[activeRoomId] || [];

  const toggleModule = (modKey) => {
    const current = assignments[activeRoomId] || [];
    const next = current.includes(modKey)
      ? current.filter(m => m !== modKey)
      : [...current, modKey];
    setAssignments({ ...assignments, [activeRoomId]: next });
  };

  const setCount = (modKey, value) => {
    const room = activeRoomId;
    const next = { ...(moduleCounts || {}) };
    next[room] = { ...(next[room] || {}), [modKey]: Math.max(0, value) };
    setModuleCounts(next);
  };

  if (!activeRoom) return null;

  const totalRoomPrice = activeMods.reduce(
    (s, m) => s + (modules[m] ? window.getModulePrice(modules[m], activeRoom, moduleCounts, activeRoomId, m, ausstattungsklasse) : 0),
    0
  );

  const klasseLabels = (window.KLASSE_LABELS || ["Mindest (DIN)", "Standard", "Komfort"]);

  return (
    <div className="kfg-content">
      {/* RÄUME — kompakte horizontale Tabs (zuoberst, schlanker als Heading-Block) */}
      <div className="kfg-mod-roombar-head">
        <div className="kfg-mod-roombar-eyebrow">{moduleLabel} pro {isGewerbe ? "Bereich" : "Raum"} · {isGewerbe ? "Bereich" : "Raum"} wählen</div>
        <div className="kfg-mod-roombar-line"></div>
        <div className="kfg-mod-roombar-stat">{indoorRooms.length} {unitLabel}</div>
      </div>
      <div className="kfg-mod-roombar">
        {indoorRooms.map(r => {
          const count = (assignments[r.id] || []).length;
          const isActive = r.id === activeRoomId;
          return (
            <button
              key={r.id}
              className={`kfg-mod-roomtab ${isActive ? "active" : ""}`}
              onClick={() => setActiveRoomId(r.id)}
            >
              <span className={`pill ${count === 0 ? "zero" : ""}`}>{count}</span>
              <div className="info">
                <div className="nm">{r.name}</div>
                <div className="meta">{r.area} m²</div>
              </div>
            </button>
          );
        })}
      </div>

      {/* HERO: Live-Vorschau — Header (Text + Toggles) volle Breite,
          darunter Bezel (Tablet ODER Handy) volle Breite. */}
      <div className="kfg-mod-hero">
        <div className="kfg-mod-hero-header">
          <div className="kfg-mod-hero-text">
            <div className="kfg-mod-hero-eyebrow">▶ Live-Vorschau · {activeRoom.name}</div>
            <h4 className="kfg-mod-hero-title">
              {isGewerbe ? <>So sieht <em>{activeRoom.name}</em> später in der Betriebsoberfläche aus.</> : <>So sieht <em>{activeRoom.name}</em> später in deinem Home Assistant aus.</>}
            </h4>
            <p className="kfg-mod-hero-sub">
              {isGewerbe
                ? "Jede Funktion, die du unten aktivierst, erscheint hier sofort als Karte. Eine Oberfläche für Energie, Bereiche, Zutritt und Technik — bedienbar auf:"
                : "Jedes Modul, das du unten aktivierst, erscheint hier sofort als Karte. Eine Visu, ein Login, alle Geräte — bedienbar auf:"}
            </p>
          </div>
          <div className="kfg-mod-hero-controls">
            <div className="kfg-mod-hero-pills" role="tablist" aria-label="Vorschau-Gerät">
              <button
                type="button"
                role="tab"
                aria-selected={previewDevice === "tablet"}
                className={previewDevice === "tablet" ? "on" : ""}
                onClick={() => setPreviewDevice("tablet")}
              >
                Web / Tablet
              </button>
              <button
                type="button"
                role="tab"
                aria-selected={previewDevice === "phone"}
                className={previewDevice === "phone" ? "on" : ""}
                onClick={() => setPreviewDevice("phone")}
              >
                Handy
              </button>
            </div>
            <button
              type="button"
              className="kfg-mod-hero-maximize"
              onClick={() => setPreviewFullscreen(true)}
              aria-label="Vorschau maximieren"
              title="Vorschau maximieren"
            >
              ⤢ Maximieren
            </button>
          </div>
        </div>

        {previewDevice === "tablet" ? (
          <div className="kfg-mod-hero-tablet">
            <div className="kfg-mod-hero-bezel">
              <div className="kfg-mod-hero-screen">
                <KfgRoomDashboard room={activeRoom} activeMods={activeMods} modules={modules}
                                  moduleCounts={moduleCounts} ausstattungsklasse={ausstattungsklasse} />
              </div>
            </div>
            <div className="kfg-mod-hero-cap">▲ Tablet · Wandhalterung oder mobil</div>
          </div>
        ) : (
          <div className="kfg-mod-hero-phone-wrap">
            <div className="kfg-mod-hero-phone">
              <div className="kfg-mod-hero-phone-bezel">
                <div className="kfg-mod-hero-phone-notch"></div>
                <div className="kfg-mod-hero-phone-screen">
                  <KfgRoomDashboard room={activeRoom} activeMods={activeMods} modules={modules}
                                    moduleCounts={moduleCounts} ausstattungsklasse={ausstattungsklasse} />
                </div>
              </div>
              <div className="kfg-mod-hero-cap">▲ Smartphone · iOS / Android</div>
            </div>
          </div>
        )}
      </div>

      {previewFullscreen && (
        <div
          className="kfg-mod-hero-fs-overlay"
          role="dialog"
          aria-modal="true"
          aria-label="Live-Vorschau maximiert"
          onClick={(e) => { if (e.target === e.currentTarget) setPreviewFullscreen(false); }}
        >
          <div className="kfg-mod-hero-fs-frame">
            <button
              type="button"
              className="kfg-mod-hero-fs-close"
              onClick={() => setPreviewFullscreen(false)}
              aria-label="Schliessen"
              title="Schliessen (ESC)"
            >×</button>
            <div className="kfg-mod-hero-fs-screen">
              <KfgRoomDashboard room={activeRoom} activeMods={activeMods} modules={modules}
                                moduleCounts={moduleCounts} ausstattungsklasse={ausstattungsklasse} />
            </div>
            <div className="kfg-mod-hero-fs-cap">
              {previewDevice === "tablet" ? "Tablet · Wandhalterung oder mobil" : "Smartphone · iOS / Android"} · ESC zum Schliessen
            </div>
          </div>
        </div>
      )}

      {/* MODULE — 3-Spalten Cards. Klassen-Toggle (Mindest/Standard/Komfort)
         direkt neben der Modul-Überschrift, weil er die Vorgaben hier steuert. */}
      <div className="kfg-mod-detail-head">
        <div style={{ display: "flex", alignItems: "baseline", flexWrap: "wrap", gap: 14 }}>
          <h3>{moduleLabel} für {activeRoom.name}</h3>
          <span className="meta">{activeRoom.area} m² · {activeRoom.windows} Fenster · €{totalRoomPrice.toLocaleString('de-AT')}</span>
        </div>
        {setAusstattungsklasse && (
          <div style={{ display: "inline-flex", gap: 0,
                        border: `1px solid ${KFG_PALETTE.line}`, borderRadius: 100,
                        padding: 3, background: KFG_PALETTE.paper }}>
            {klasseLabels.map((lbl, i) => {
              const active = i === ausstattungsklasse;
              return (
                <button
                  key={i}
                  type="button"
                  onClick={() => setAusstattungsklasse(i)}
                  style={{
                    padding: "6px 14px", borderRadius: 100, border: 0, cursor: "pointer",
                    background: active ? KFG_PALETTE.accent : "transparent",
                    color: active ? KFG_PALETTE.paper : KFG_PALETTE.muted,
                    fontFamily: "var(--font-mono, monospace)", fontSize: 11,
                    letterSpacing: "0.08em", textTransform: "uppercase",
                    fontWeight: active ? 600 : 400, transition: "all 0.15s",
                  }}
                  title={`Ausstattungsklasse ${i + 1} -- ${lbl}`}
                >
                  {lbl}
                </button>
              );
            })}
          </div>
        )}
      </div>
      <p style={{ color: KFG_PALETTE.muted, fontSize: 14, margin: "0 0 18px", maxWidth: 760 }}>
        {isGewerbe
          ? <>Typische Smart-Building-Funktionen sind je Bereich vorgewählt. Alles an- und abwählbar — Stückzahlen direkt anpassbar. Klassen-Toggle <em style={{ fontStyle: "normal", color: KFG_PALETTE.accent }}>{klasseLabels[ausstattungsklasse]}</em> rechts ändert die Vorgabe für alle Bereiche gleichzeitig.</>
          : <>Standard-Module pro Raumtyp sind nach DIN 18015-2 / RAL-RG 678 vorgewählt. Alles an- und abwählbar — Stückzahlen direkt anpassbar. Klassen-Toggle <em style={{ fontStyle: "normal", color: KFG_PALETTE.accent }}>{klasseLabels[ausstattungsklasse]}</em> rechts ändert die Vorgabe für alle Räume gleichzeitig.</>}
      </p>
      <div className="kfg-mod-grid3">
        {Object.entries(modules).map(([key, mod]) => {
          const isOn = activeMods.includes(key);
          const count = window.getModuleCount(mod, activeRoom, moduleCounts, activeRoomId, key, ausstattungsklasse);
          const price = window.getModulePrice(mod, activeRoom, moduleCounts, activeRoomId, key, ausstattungsklasse);
          return (
            <div
              key={key}
              className={`kfg-mod-card ${isOn ? "on" : ""}`}
              onClick={(e) => {
                if (e.target.closest(".kfg-mod-counter")) return;
                toggleModule(key);
              }}
            >
              <div className="kfg-mod-card-tog"></div>
              <div className="kfg-mod-card-head">
                <div className="ic">{mod.icon}</div>
                <div>
                  <div className="lbl">{mod.label}</div>
                  <div className="desc">{mod.countable ? `${count} ${mod.unit}` : modDescriptor(key, activeRoom)}</div>
                </div>
              </div>
              <div className="kfg-mod-card-foot">
                {mod.countable && isOn ? (
                  <div className="kfg-mod-counter" onClick={(e) => e.stopPropagation()}>
                    <button className="cb" onClick={() => setCount(key, count - 1)} aria-label={`${mod.unit} weniger`}>−</button>
                    <span className="cv">{count}</span>
                    <button className="cb" onClick={() => setCount(key, count + 1)} aria-label={`${mod.unit} mehr`}>+</button>
                  </div>
                ) : <span />}
                <div className="price">€{price.toLocaleString('de-AT')}</div>
              </div>
            </div>
          );
        })}
      </div>

      <div className="kfg-footer-nav">
        <div className="left">
          <button className="kfg-back" onClick={onBack}>← Zurück</button>
        </div>
        <div className="right">
          <button className="kfg-next" onClick={onNext}>Empfehlung ansehen →</button>
        </div>
      </div>
    </div>
  );
}

function modDescriptor(key, room) {
  switch(key) {
    case "licht":       return `~${Math.ceil(room.area/8)} Schaltkreise`;
    case "beschattung": return `${room.windows} Fenster · automatisch`;
    case "heizung":     return `Einzelraum-Regelung`;
    case "klima":       return `Temperatur + Lüftung`;
    case "zutritt":     return `Zugang + Rollen`;
    case "multimedia":  return `Audio · Multiroom-fähig`;
    case "sensorik":    return `Bewegung · Luft · Klima`;
    case "steckdosen":  return `~${Math.max(2, Math.ceil(room.area/12))} schaltbar`;
    case "energie":     return `Verbrauch + Submetering`;
    case "alarm":       return `Status + Eskalation`;
    default: return "";
  }
}

/* ============================================================
   PDF-Generator: baut ein selbst-haltbares druckbares HTML-Dokument
   mit pro-Raum Funktion, Groesse, Modulen, Material, Funktions-
   beschreibung. Wird in einem neuen Browser-Fenster geoeffnet
   damit der User es als PDF speichern kann.
============================================================ */
const TYPE_LABEL = {
  living: "Wohnbereich",
  kitchen: "Küche",
  bath: "Badezimmer",
  wc: "WC",
  bedroom: "Schlafzimmer",
  hall: "Verkehrsfläche",
  tech: "Technikraum",
  storage: "Lager / Speis",
  stairs: "Treppenraum",
  terrace: "Außenbereich",
  room: "Zimmer",
  entry: "Eingangsbereich",
};

const TYPE_LABEL_GEWERBE = {
  living: "Empfang / Lobby",
  kitchen: "Teeküche / Gastro",
  bath: "Sanitärbereich",
  wc: "WC / Sanitär",
  bedroom: "Hotelzimmer / Apartment",
  hall: "Flur / Gang",
  tech: "Technik / Server",
  storage: "Lager / Backoffice",
  stairs: "Treppenraum",
  terrace: "Außenbereich",
  room: "Meeting / Seminar",
  office: "Büro / Arbeitsplatz",
  entry: "Eingang / Zutritt",
};

const MODULE_FUNCTION = {
  licht:       "Schaltbare und dimmbare Beleuchtung pro Lichtkreis. Zentralsteuerung über KNX/DALI, einbindbar in Szenen und Anwesenheits-Automation.",
  steckdosen:  "Schaltbare Steckdosen, fernsteuerbar via App und Sprachassistent. Zeitschaltung und Standby-Killer per Szene möglich.",
  beschattung: "Automatische Steuerung von Raffstores oder Rollläden pro Fenster, abhängig von Tageszeit, Sonneneinstrahlung und Innenraum-Temperatur.",
  heizung:     "Einzelraumregelung mit Stellantrieb am Heizkreisverteiler und Raumthermostat. Heiz-/Absenkprofile pro Raum, Anwesenheits-gesteuert.",
  klima:       "Steuerung von Klimaanlage und kontrollierter Wohnraumlüftung. CO₂-, Feuchte- und Anwesenheits-abhängig.",
  zutritt:     "Türsteuerung, Zugangskontrolle und Status-Anzeige (z.B. Garage, Eingangstür). Anbindung an Anwesenheits-Logik.",
  multimedia:  "Multiroom-Audio-Steuerung. Sonos/HEOS-kompatibel, ein-/ausschaltbar via Szenen.",
  sensorik:    "Bewegungs-, CO₂-, Helligkeits- und Temperatursensorik. Basis für Anwesenheits-Automation und Raumklima-Regelung.",
  energie:     "Verbrauchsmessung pro Stromkreis. Verbindung zur Energiemonitoring-App + PV/Speicher-Auswertung.",
  alarm:       "Einbruch- und Brandmeldeanlage, Panik-Funktion, Anbindung an Sirene und Push-Benachrichtigung.",
};

const MODULE_FUNCTION_GEWERBE = {
  licht:       "Beleuchtung pro Zone oder Lichtkreis, einbindbar in Präsenzlogik, Szenen, Zeitprogramme und zentrale Betriebsoberflächen.",
  steckdosen:  "Schaltbare Steckdosen oder Stromkreise für definierte Verbraucher, Zeitprogramme oder Standby-Abschaltung.",
  beschattung: "Automatische Steuerung von Raffstores oder Beschattung nach Sonne, Uhrzeit, Raumbelegung und Innenraumtemperatur.",
  heizung:     "Einzelraum- oder Zonenregelung für Heizung. Nutzungszeiten, Absenkbetrieb und Präsenz können berücksichtigt werden.",
  klima:       "HLK-Anbindung für Heizung, Kühlung oder Lüftung. CO₂-, Temperatur- und Anwesenheitswerte dienen als Regelbasis.",
  zutritt:     "Zutritt, Türstatus und Rollenlogik für Eingänge, Teamzonen, Lager, Technikräume oder Hotelbereiche.",
  multimedia:  "Medien- oder Audiozonen für Empfang, Besprechung, Gastro oder Allgemeinflächen.",
  sensorik:    "Präsenz-, CO₂-, Helligkeits- und Temperatursensorik als Grundlage für Komfort, Energie und Betriebsalarme.",
  energie:     "Verbrauchsmessung, PV-/Netzbezug und Submetering. Grundlage für Energie-Dashboard, Reports und Schwellwertmeldungen.",
  alarm:       "Status- und Eskalationsmeldungen für betriebliche Übersicht. Zertifizierte Alarm- oder Brandmeldetechnik wird mit Fachpartnern abgegrenzt.",
};

function _esc(s) {
  return String(s == null ? "" : s)
    .replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")
    .replace(/"/g, "&quot;").replace(/'/g, "&#39;");
}

function _eur(n) {
  return Math.round(n).toLocaleString('de-AT');
}

function buildPdfHtml(ctx) {
  const {
    pathKey, pathLabel, klasseLabel,
    indoorRooms, modules, assignments, moduleCounts, ausstattungsklasse,
    totalArea, totalModuleCount,
    material, install_cost, install_hours,
    commissioning_cost, commissioning_hours, total, lower, upper,
    stundensatz, safeAreaForDiv,
  } = ctx;

  const today = new Date().toLocaleDateString('de-AT', { day: '2-digit', month: '2-digit', year: 'numeric' });
  const validUntilDate = new Date(Date.now() + 30 * 24 * 3600 * 1000);
  const validUntil = validUntilDate.toLocaleDateString('de-AT', { day: '2-digit', month: '2-digit', year: 'numeric' });
  const docNummer = `S-${new Date().getFullYear()}-${Math.floor(Date.now() / 1000) % 10000}`;
  const pricePerSqm = Math.round(total / safeAreaForDiv);
  const isGewerbe = pathKey === "gewerbe";
  const areaLabel = isGewerbe ? "Nutzfläche" : "Wohnfläche";
  const unitLabel = isGewerbe ? "Bereiche" : "Räume";
  const typeLabels = isGewerbe ? TYPE_LABEL_GEWERBE : TYPE_LABEL;
  const moduleFunctions = isGewerbe ? MODULE_FUNCTION_GEWERBE : MODULE_FUNCTION;

  const positionRows = indoorRooms.map((r, ri) => {
    const mods = assignments[r.id] || [];
    if (mods.length === 0) return '';
    const typeLbl = typeLabels[r.type] || (isGewerbe ? "Bereich" : "Raum");
    const winLbl = (r.windows || 0) === 0 ? "innenliegend"
                 : (r.windows === 1 ? "1 Fenster" : `${r.windows} Fenster`);
    const catLine = `${_esc(r.name)} · ${typeLbl} · ${r.area} m² · ${winLbl}`;
    const rows = mods.map((modKey, mi) => {
      const mod = modules[modKey];
      if (!mod) return '';
      const count = window.getModuleCount(mod, r, moduleCounts, r.id, modKey, ausstattungsklasse);
      const price = window.getModulePrice(mod, r, moduleCounts, r.id, modKey, ausstattungsklasse);
      const fn = moduleFunctions[modKey] || "";
      const menge = mod.countable ? count : 1;
      const einheit = mod.countable ? (mod.unit || "Stk") : "Set";
      const einzel = mod.countable
        ? (mod.material_unit || (price / Math.max(menge, 1)))
        : (mod.material_base || price);
      return `
        <tr class="ar-pos">
          <td class="ar-c-pos mono">${ri + 1}.${mi + 1}</td>
          <td class="ar-c-desc">
            <div class="ar-pos-title">${_esc(mod.label)}</div>
            <div class="ar-pos-detail">${_esc(fn)}</div>
          </td>
          <td class="ar-c-mng mono">${menge}</td>
          <td class="ar-c-unit">${_esc(einheit)}</td>
          <td class="ar-c-price mono">€&nbsp;${_eur(einzel)}</td>
          <td class="ar-c-sum mono">€&nbsp;${_eur(price)}</td>
        </tr>`;
    }).join('');
    return `
      <tr class="ar-cat"><td colspan="6">${catLine}</td></tr>
      ${rows}`;
  }).join('');

  const installCategory = `
    <tr class="ar-cat"><td colspan="6">Installation &amp; Inbetriebnahme</td></tr>
    <tr class="ar-pos">
      <td class="ar-c-pos mono">${indoorRooms.length + 1}.1</td>
      <td class="ar-c-desc">
        <div class="ar-pos-title">Installation Monteur</div>
        <div class="ar-pos-detail">Verkabelung, Montage Aktoren/Sensoren, Anschluss an KNX-Bus.</div>
      </td>
      <td class="ar-c-mng mono">${install_hours}</td>
      <td class="ar-c-unit">Std.</td>
      <td class="ar-c-price mono">€&nbsp;${stundensatz.monteur.toFixed(2).replace('.', ',')}</td>
      <td class="ar-c-sum mono">€&nbsp;${_eur(install_cost)}</td>
    </tr>
    <tr class="ar-pos">
      <td class="ar-c-pos mono">${indoorRooms.length + 1}.2</td>
      <td class="ar-c-desc">
        <div class="ar-pos-title">Inbetriebnahme Spezial-Monteur</div>
        <div class="ar-pos-detail">Programmierung, Funktionstest, Übergabe und Einweisung.</div>
      </td>
      <td class="ar-c-mng mono">${commissioning_hours}</td>
      <td class="ar-c-unit">Std.</td>
      <td class="ar-c-price mono">€&nbsp;${stundensatz.spezial_monteur.toFixed(2).replace('.', ',')}</td>
      <td class="ar-c-sum mono">€&nbsp;${_eur(commissioning_cost)}</td>
    </tr>`;

  return `<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Smartmacherei — Kostenschätzung</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Outfit:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
<style>
  :root {
    --ar-accent: #3ABDD0;
    --ar-ink: #14202c;
    --ar-ink-soft: #4a5d70;
    --ar-line: #d9dfe6;
    --ar-line-soft: #ecf0f4;
    --ar-page-bg: #ffffff;
    --ar-stage-bg: #e9eef3;
  }
  * { box-sizing: border-box; }
  html, body { margin: 0; padding: 0; }
  body {
    background: var(--ar-stage-bg);
    color: var(--ar-ink);
    font-family: 'Inter', system-ui, sans-serif;
    font-size: 14px; line-height: 1.5;
    -webkit-font-smoothing: antialiased;
  }
  .mono { font-family: 'JetBrains Mono', monospace; }

  .ar-toolbar {
    position: sticky; top: 0; z-index: 50;
    display: flex; justify-content: space-between; align-items: center;
    padding: 14px 28px; background: #ffffffd9; backdrop-filter: blur(10px);
    border-bottom: 1px solid var(--ar-line);
  }
  .ar-toolbar-left { display: flex; align-items: center; gap: 12px;
    font-size: 12px; color: var(--ar-ink-soft); }
  .ar-toolbar-eyebrow { font-family: 'JetBrains Mono', monospace;
    letter-spacing: 0.18em; text-transform: uppercase; color: var(--ar-accent); font-size: 11px; }
  .ar-toolbar-divider { width: 1px; height: 14px; background: var(--ar-line); }
  .ar-print-btn {
    padding: 10px 18px; border-radius: 100px; border: 1px solid var(--ar-accent);
    background: var(--ar-accent); color: white; cursor: pointer;
    font-family: 'Inter', sans-serif; font-size: 13px; font-weight: 500;
    letter-spacing: 0.01em;
  }
  .ar-print-btn:hover { transform: translateY(-1px); box-shadow: 0 6px 18px rgba(58,189,208,0.35); }

  .ar-pages { padding: 32px 0 80px; display: flex; flex-direction: column; gap: 28px; align-items: center; }
  .ar-page {
    width: 210mm; min-height: 297mm; background: var(--ar-page-bg);
    box-shadow: 0 4px 28px rgba(20, 32, 44, 0.08);
    padding: 18mm 18mm 14mm;
    display: flex; flex-direction: column;
    position: relative; overflow: hidden;
  }

  .ar-header {
    display: flex; justify-content: space-between; align-items: flex-start;
    padding-bottom: 14mm; border-bottom: 1px solid var(--ar-line); margin-bottom: 8mm;
  }
  .ar-brand { display: flex; align-items: center; gap: 14px; }
  .ar-brand-name { font-family: 'Outfit', sans-serif; font-size: 20px; font-weight: 600; letter-spacing: -0.01em; }
  .ar-brand-sub { font-family: 'JetBrains Mono', monospace; font-size: 10px;
    color: var(--ar-ink-soft); letter-spacing: 0.14em; text-transform: uppercase; margin-top: 2px; }
  .ar-doc-title { font-family: 'Outfit', sans-serif; font-size: 28px; font-weight: 500;
    letter-spacing: 0.06em; text-align: right; line-height: 1; margin-bottom: 6px; color: var(--ar-accent); }
  .ar-meta-tbl { border-collapse: collapse; margin-left: auto; font-size: 11px; }
  .ar-meta-tbl td { padding: 2px 0; }
  .ar-meta-tbl td:first-child { color: var(--ar-ink-soft); font-family: 'JetBrains Mono', monospace;
    letter-spacing: 0.1em; text-transform: uppercase; padding-right: 14px; text-align: right; }
  .ar-meta-tbl td:last-child { font-weight: 500; text-align: right; font-family: 'JetBrains Mono', monospace; }

  .ar-address { margin-bottom: 8mm; }
  .ar-sender-line {
    font-size: 10px; color: var(--ar-ink-soft);
    border-bottom: 1px solid var(--ar-line-soft);
    padding-bottom: 3px; margin-bottom: 8mm; letter-spacing: 0.02em;
  }
  .ar-empfaenger { max-width: 70mm; }
  .ar-empfaenger-h { font-family: 'JetBrains Mono', monospace; font-size: 10px;
    color: var(--ar-ink-soft); letter-spacing: 0.18em; text-transform: uppercase; margin-bottom: 6px; }
  .ar-empfaenger-body { font-size: 13.5px; line-height: 1.55; }
  .ar-empfaenger-body div:first-child { font-weight: 600; }

  .ar-betreff { margin-bottom: 6mm; }
  .ar-betreff-h { font-family: 'JetBrains Mono', monospace; font-size: 10px;
    color: var(--ar-ink-soft); letter-spacing: 0.18em; text-transform: uppercase; margin-bottom: 4px; }
  .ar-betreff-text { font-family: 'Outfit', sans-serif; font-size: 17px; font-weight: 500;
    letter-spacing: -0.005em; line-height: 1.35; }
  .ar-betreff-meta { margin-top: 6px; display: flex; gap: 18px; flex-wrap: wrap;
    font-size: 11px; color: var(--ar-ink-soft); }
  .ar-betreff-meta b { color: var(--ar-ink); font-weight: 500; }

  .ar-callout {
    border-left: 3px solid var(--ar-accent);
    background: #f7f9fb; padding: 14px 18px; margin-bottom: 6mm;
    border-radius: 0 4px 4px 0;
  }
  .ar-callout-h { font-family: 'JetBrains Mono', monospace; font-size: 10px;
    letter-spacing: 0.18em; text-transform: uppercase;
    margin-bottom: 6px; font-weight: 500; color: var(--ar-accent); }
  .ar-callout p { margin: 0; font-size: 12.5px; color: var(--ar-ink-soft); line-height: 1.55; }
  .ar-callout p b { color: var(--ar-ink); font-weight: 600; }

  .ar-tbl { width: 100%; border-collapse: collapse; margin-bottom: 6mm; font-size: 12px; }
  .ar-tbl thead th {
    text-align: left; font-family: 'JetBrains Mono', monospace;
    font-size: 9.5px; font-weight: 500; color: var(--ar-ink-soft);
    letter-spacing: 0.14em; text-transform: uppercase;
    border-bottom: 1.5px solid var(--ar-ink); padding: 8px;
  }
  .ar-tbl thead th.ar-c-mng,
  .ar-tbl thead th.ar-c-price,
  .ar-tbl thead th.ar-c-sum { text-align: right; }
  .ar-c-pos { width: 14mm; }
  .ar-c-mng { width: 14mm; text-align: right; }
  .ar-c-unit { width: 18mm; }
  .ar-c-price { width: 26mm; text-align: right; }
  .ar-c-sum { width: 26mm; text-align: right; }
  .ar-tbl tbody td { padding: 8px; vertical-align: top; }
  .ar-tbl tr.ar-cat td {
    padding-top: 12px; padding-bottom: 6px;
    font-family: 'JetBrains Mono', monospace; font-size: 10px;
    letter-spacing: 0.16em; text-transform: uppercase; font-weight: 500;
    border-bottom: 1px dashed var(--ar-line); color: var(--ar-accent);
  }
  .ar-tbl tr.ar-pos td { border-bottom: 1px solid var(--ar-line-soft); }
  .ar-pos-title { font-size: 13px; font-weight: 500; margin-bottom: 2px; }
  .ar-pos-detail { font-size: 11px; color: var(--ar-ink-soft); line-height: 1.5; max-width: 80mm; }

  .ar-totals { margin-left: auto; width: 95mm; margin-bottom: 6mm; }
  .ar-totals-rows { display: flex; flex-direction: column; }
  .ar-trow {
    display: flex; justify-content: space-between; align-items: baseline;
    padding: 7px 4px; font-size: 12.5px;
    border-bottom: 1px solid var(--ar-line-soft);
  }
  .ar-trow span:first-child { color: var(--ar-ink-soft); }
  .ar-trow--note span:first-child { font-style: italic; font-size: 11.5px; }
  .ar-trow--brutto {
    margin-top: 6px; padding: 14px 4px;
    font-family: 'Outfit', sans-serif; font-size: 17px; font-weight: 500;
    border-top: 2px solid var(--ar-accent); border-bottom: 2px solid var(--ar-accent);
    align-items: center;
  }
  .ar-trow--brutto span:first-child { color: var(--ar-ink); letter-spacing: -0.01em; }
  .ar-trow--brutto span:last-child { font-size: 19px; font-weight: 500; color: var(--ar-accent); }

  .ar-conditions {
    background: #f7f9fb; padding: 18px 22px; margin-bottom: 6mm; border-radius: 4px;
  }
  .ar-cond-h { font-family: 'JetBrains Mono', monospace; font-size: 10px;
    letter-spacing: 0.18em; text-transform: uppercase;
    margin-bottom: 10px; font-weight: 500; color: var(--ar-accent); }
  .ar-cond-list { margin: 0; display: grid; grid-template-columns: 32mm 1fr;
    gap: 6px 14px; font-size: 12px; line-height: 1.5; }
  .ar-cond-list dt { font-weight: 500; color: var(--ar-ink);
    font-family: 'JetBrains Mono', monospace; font-size: 10.5px;
    letter-spacing: 0.06em; text-transform: uppercase; padding-top: 1px; }
  .ar-cond-list dd { margin: 0; color: var(--ar-ink-soft); }

  .ar-signoff { margin-top: auto; }
  .ar-signoff p { font-size: 12.5px; color: var(--ar-ink-soft);
    line-height: 1.55; margin: 0 0 8mm; max-width: 150mm; }
  .ar-greeting { font-size: 13px; }
  .ar-sig-block { margin-top: 14mm; }
  .ar-sig-line { width: 60mm; height: 1px; background: var(--ar-ink); margin-bottom: 4px; }
  .ar-sig-name { font-size: 13px; font-weight: 500; }
  .ar-sig-role { font-family: 'JetBrains Mono', monospace; font-size: 10px;
    color: var(--ar-ink-soft); letter-spacing: 0.14em; text-transform: uppercase; }

  .ar-footer { margin-top: 8mm; padding-top: 4mm; }
  .ar-footer-line { height: 2px; width: 100%; margin-bottom: 6mm; background: var(--ar-accent); }
  .ar-footer-grid { display: grid; grid-template-columns: repeat(4, 1fr);
    gap: 10mm; font-size: 10.5px; color: var(--ar-ink-soft); line-height: 1.5; }
  .ar-footer-h { font-family: 'JetBrains Mono', monospace; font-size: 9px;
    color: var(--ar-ink); letter-spacing: 0.18em; text-transform: uppercase;
    margin-bottom: 6px; font-weight: 500; }

  [contenteditable="true"]:hover { background: rgba(58,189,208,0.05); outline: 1px dashed var(--ar-accent); outline-offset: 2px; }
  [contenteditable="true"]:focus { background: rgba(58,189,208,0.08); outline: 1px solid var(--ar-accent); outline-offset: 2px; }

  /* Page-Break-Kontrolle: Tabellen-Header wiederholen, Zeilen/Bloecke
     nicht mitten durchbrechen */
  .ar-tbl thead { display: table-header-group; }
  .ar-tbl tr { page-break-inside: avoid; break-inside: avoid; }
  .ar-tbl tr.ar-cat {
    page-break-after: avoid; break-after: avoid;
    page-break-inside: avoid; break-inside: avoid;
  }
  .ar-pos-detail { page-break-inside: avoid; break-inside: avoid; }
  .ar-totals,
  .ar-conditions,
  .ar-callout,
  .ar-betreff,
  .ar-address,
  .ar-header,
  .ar-signoff,
  .ar-sig-block,
  .ar-footer {
    page-break-inside: avoid; break-inside: avoid;
  }

  /* @page margin wirkt auf JEDE seite -- auch beim umbruch */
  @page { size: A4; margin: 18mm 18mm 14mm; }
  @media print {
    html, body { background: white; -webkit-print-color-adjust: exact; print-color-adjust: exact; }
    .no-print { display: none !important; }
    .ar-pages { padding: 0; gap: 0; }
    .ar-page {
      box-shadow: none; page-break-after: always; break-after: page;
      margin: 0; padding: 0; width: auto; min-height: 0;
    }
    .ar-page:last-child { page-break-after: auto; break-after: auto; }
    [contenteditable="true"]:hover, [contenteditable="true"]:focus { background: transparent; outline: none; }
  }
</style>
</head>
<body>
  <div class="ar-toolbar no-print">
    <div class="ar-toolbar-left">
      <span class="ar-toolbar-eyebrow">Smartmacherei · Kostenschätzung</span>
      <span class="ar-toolbar-divider"></span>
      <span>Druck-Vorschau · "Als PDF speichern" wählen</span>
    </div>
    <button class="ar-print-btn" onclick="window.print()">Drucken / Als PDF speichern</button>
  </div>

  <div class="ar-pages">
    <article class="ar-page">

      <div class="ar-header">
        <div class="ar-brand">
          <svg width="42" height="33" viewBox="0 0 100 78" fill="none">
            <g stroke="#3ABDD0" stroke-width="2.4" fill="none" stroke-linejoin="round" stroke-linecap="round">
              <path d="M 22 42.9 L 50 14 L 78 42.9 L 78 68.6 L 22 68.6 Z"/>
              <circle cx="50" cy="27.3" r="2.2" fill="#3ABDD0" stroke="none"/>
            </g>
          </svg>
          <div>
            <div class="ar-brand-name">Smartmacherei</div>
            <div class="ar-brand-sub">Ing. Thomas Basting</div>
          </div>
        </div>
        <div class="ar-doc-meta">
          <div class="ar-doc-title">KOSTENSCHÄTZUNG</div>
          <table class="ar-meta-tbl">
            <tbody>
              <tr><td>Schätzung Nr.</td><td>${docNummer}</td></tr>
              <tr><td>Datum</td><td>${_esc(today)}</td></tr>
            </tbody>
          </table>
        </div>
      </div>

      <div class="ar-address">
        <div class="ar-sender-line">
          Smartmacherei · Ing. Thomas Basting · Gnadlingerweg 11 · 4650 Edt bei Lambach
        </div>
        <div class="ar-empfaenger" contenteditable="true">
          <div class="ar-empfaenger-h">An</div>
          <div class="ar-empfaenger-body">
            <div>Bauherr / Kundin</div>
            <div>Name</div>
            <div>Strasse</div>
            <div>PLZ Ort</div>
          </div>
        </div>
      </div>

      <div class="ar-betreff">
        <div class="ar-betreff-h">Betreff</div>
        <div class="ar-betreff-text" contenteditable="true">
          Unverbindliche Kostenschätzung Smart-Building · ${totalArea.toFixed(0)} m², ${indoorRooms.length} ${unitLabel}, ${totalModuleCount} Module · Pfad ${_esc(pathLabel)} · Ausstattung ${_esc(klasseLabel)}
        </div>
        <div class="ar-betreff-meta">
          <span><b>Pfad</b> ${_esc(pathLabel)}</span>
          <span><b>${areaLabel}</b> ${totalArea.toFixed(0)} m²</span>
          <span><b>${unitLabel}</b> ${indoorRooms.length}</span>
          <span><b>Module</b> ${totalModuleCount}</span>
          <span><b>Ausstattung</b> ${_esc(klasseLabel)}</span>
        </div>
      </div>

      <div class="ar-callout">
        <div class="ar-callout-h">Erste Grobschätzung</div>
        <p>
          Diese Schätzung gibt Ihnen eine erste Hausnummer auf Basis Ihrer Angaben im Online-Konfigurator.
          Sie ist <b>unverbindlich</b> und ersetzt kein Angebot — die finalen Werte ermitteln wir gemeinsam
          bei der Vor-Ort-Begehung.
        </p>
      </div>

      <table class="ar-tbl">
        <thead>
          <tr>
            <th class="ar-c-pos">Pos.</th>
            <th class="ar-c-desc">Beschreibung</th>
            <th class="ar-c-mng">Menge</th>
            <th class="ar-c-unit">Einheit</th>
            <th class="ar-c-price">Einzelpreis</th>
            <th class="ar-c-sum">Gesamt</th>
          </tr>
        </thead>
        <tbody>
          ${positionRows}
          ${installCategory}
        </tbody>
      </table>

      <div class="ar-totals">
        <div class="ar-totals-rows">
          <div class="ar-trow">
            <span>Material</span>
            <span class="mono">€&nbsp;${_eur(material)}</span>
          </div>
          <div class="ar-trow">
            <span>Installation (${install_hours} h)</span>
            <span class="mono">€&nbsp;${_eur(install_cost)}</span>
          </div>
          <div class="ar-trow">
            <span>Inbetriebnahme (${commissioning_hours} h)</span>
            <span class="mono">€&nbsp;${_eur(commissioning_cost)}</span>
          </div>
          <div class="ar-trow ar-trow--note">
            <span>Steuerfrei · Kleinunternehmerregelung § 6 Abs. 1 Z 27 UStG</span>
            <span class="mono">€&nbsp;0,00</span>
          </div>
          <div class="ar-trow">
            <span>Geschätzte Gesamtsumme</span>
            <span class="mono">€&nbsp;${_eur(total)}</span>
          </div>
        </div>
        <div class="ar-trow ar-trow--brutto">
          <span>Bandbreite ±15 %</span>
          <span class="mono">€&nbsp;${_eur(lower)} – €&nbsp;${_eur(upper)}</span>
        </div>
        <div class="ar-trow ar-trow--note" style="justify-content: flex-end; border-bottom: none;">
          <span style="margin-right: 12px;">Preis pro m² (Mittelwert)</span>
          <span class="mono">€&nbsp;${_eur(pricePerSqm)}</span>
        </div>
      </div>

      <div class="ar-conditions">
        <div class="ar-cond-h">Hinweise zur Schätzung</div>
        <dl class="ar-cond-list">
          <dt>Gültigkeit</dt><dd>30 Tage ab Schätzungsdatum, bis ${validUntil}.</dd>
          <dt>Bandbreite</dt><dd>±15 % auf den Endpreis. Ausreisser nach oben durch Sonderwünsche, Ausreisser nach unten durch Eigenleistung möglich.</dd>
          <dt>Material</dt><dd>Listenpreise 2026 ±15 %. KNX/DALI-Komponenten teurer als Funk-Lösungen — wir wählen pro ${isGewerbe ? "Bereich" : "Raum"} die saubere Variante.</dd>
          <dt>Stundensätze</dt><dd>Laut WKO-Regiestundensätze 2026 (Österreich) inkl. Überstundenzuschlag. Monteur €&nbsp;${stundensatz.monteur.toFixed(2).replace('.', ',')}/h, Spezial-Monteur €&nbsp;${stundensatz.spezial_monteur.toFixed(2).replace('.', ',')}/h.</dd>
          <dt>Nächster Schritt</dt><dd>Vor-Ort-Begehung (kostenlos, ca. 60 min) → verbindliches Angebot innerhalb von 5 Werktagen.</dd>
          <dt>Hinweis</dt><dd>Diese Schätzung ist <b>kein Angebot</b> im Sinne des § 861 ABGB und löst keine Vertragsbindung aus.</dd>
        </dl>
      </div>

      <div class="ar-signoff">
        <p>
          Wenn Sie die Zahlen für Ihre Planung passen, vereinbaren wir gerne einen Vor-Ort-Termin.
          Bei Rückfragen melden Sie sich jederzeit — telefonisch oder per E-Mail.
        </p>
        <div class="ar-greeting">
          <div>Mit freundlichen Grüßen</div>
          <div class="ar-sig-block">
            <div class="ar-sig-line"></div>
            <div class="ar-sig-name">Ing. Thomas Basting</div>
            <div class="ar-sig-role">Smartmacherei</div>
          </div>
        </div>
      </div>

      <div class="ar-footer">
        <div class="ar-footer-line"></div>
        <div class="ar-footer-grid">
          <div>
            <div class="ar-footer-h">Smartmacherei</div>
            <div>Ing. Thomas Basting</div>
            <div>Gnadlingerweg 11</div>
            <div>4650 Edt bei Lambach</div>
          </div>
          <div>
            <div class="ar-footer-h">Kontakt</div>
            <div>+43 677 642 654 41</div>
            <div>thobasting@gmail.com</div>
            <div>smartmacherei.at</div>
          </div>
          <div>
            <div class="ar-footer-h">Online-Konfigurator</div>
            <div>${indoorRooms.length} ${unitLabel} · ${totalModuleCount} Module</div>
            <div>Pfad: ${_esc(pathLabel)}</div>
            <div>Ausstattung: ${_esc(klasseLabel)}</div>
          </div>
          <div>
            <div class="ar-footer-h">Steuerlich</div>
            <div>Kleinunternehmer<br>gem. § 6 Abs. 1 Z 27 UStG</div>
          </div>
        </div>
      </div>

    </article>
  </div>

  <script>
    window.onafterprint = function() { /* keep open */ };
  </script>
</body>
</html>`;
}


/* ============================================================
   STEP 3 — Summary + Price
============================================================ */
function KfgStep3Summary({ pathKey, rooms, modules, assignments, moduleCounts, ausstattungsklasse = 1, onBack, onRestart }) {
  const indoorRooms = rooms.filter(r => !r.outdoor);
  const totalModuleCount = Object.values(assignments).flat().length;
  const totalArea = indoorRooms.reduce((s,r) => s + (Number(r.area) || 0), 0);
  const safeAreaForDiv = totalArea > 0 ? totalArea : 1;
  const pathLabel = (pathKey || "").toUpperCase() || "ALLGEMEIN";
  const klasseLabel = (window.KLASSE_LABELS || ["Mindest","Standard","Komfort"])[ausstattungsklasse] || "Standard";
  const stundensatz = (window.WKO_STUNDENSATZ || { monteur: 70.66, spezial_monteur: 91.28 });
  const isGewerbe = pathKey === "gewerbe";
  const areaLabel = isGewerbe ? "Nutzfläche" : "Wohnfläche";
  const unitLabel = isGewerbe ? "Bereiche" : "Räume";

  // Aufschluesselung Material / Installation / Inbetriebnahme.
  const breakdown = window.priceBreakdown
    ? window.priceBreakdown(rooms, assignments, modules, moduleCounts, ausstattungsklasse, pathKey)
    : null;
  const material = breakdown ? breakdown.material : 0;
  const install_cost = breakdown ? breakdown.install_cost : 0;
  const install_hours = breakdown ? breakdown.install_hours : 0;
  const commissioning_cost = breakdown ? breakdown.commissioning_cost : 0;
  const commissioning_hours = breakdown ? breakdown.commissioning_hours : 0;
  const total = breakdown ? breakdown.total : 0;
  const lower = breakdown ? breakdown.range_low : 0;
  const upper = breakdown ? breakdown.range_high : 0;

  // Plain-Text-Summary fuer Mail / Print.
  const buildSummary = () => {
    const lines = [];
    lines.push(`Smartmacherei -- Konfigurator-Empfehlung`);
    lines.push(`Pfad: ${pathLabel}    Ausstattung: ${klasseLabel}`);
    lines.push(`${unitLabel}: ${indoorRooms.length}    Module: ${totalModuleCount}    ${areaLabel}: ${totalArea.toFixed(0)} m²`);
    lines.push(`Schätzung: ab €${lower.toLocaleString('de-AT')}  (Bandbreite €${lower.toLocaleString('de-AT')} – €${upper.toLocaleString('de-AT')})`);
    lines.push(``);
    lines.push(`${unitLabel} im Detail:`);
    for (const r of indoorRooms) {
      const mods = assignments[r.id] || [];
      const modNames = mods.map(m => modules[m]?.label || m).join(", ");
      const roomPrice = mods.reduce((s,m) => s + (modules[m] ? window.getModulePrice(modules[m], r, moduleCounts, r.id, m, ausstattungsklasse) : 0), 0);
      lines.push(`  - ${r.name}  (${r.area} m², ${r.windows ?? 0} Fenster) -- Material €${Math.round(roomPrice).toLocaleString('de-AT')}`);
      if (mods.length) lines.push(`     Module: ${modNames}`);
    }
    lines.push(``);
    lines.push(`Kosten-Aufschluesselung:`);
    lines.push(`  Material (Aktoren, Sensoren, Taster):       €${material.toLocaleString('de-AT')}`);
    lines.push(`  Installation (${install_hours} h × €${stundensatz.monteur.toFixed(2)} Monteur):   €${install_cost.toLocaleString('de-AT')}`);
    lines.push(`  Inbetriebnahme (${commissioning_hours} h × €${stundensatz.spezial_monteur.toFixed(2)} Spezial):  €${commissioning_cost.toLocaleString('de-AT')}`);
    lines.push(`  ─────────────────────────────────────────────`);
    lines.push(`  Geschätzte Gesamtsumme:                     €${total.toLocaleString('de-AT')} (±18%)`);
    lines.push(`  Preis pro m²:                                ~€${Math.round(total/safeAreaForDiv).toLocaleString('de-AT')}`);
    lines.push(``);
    lines.push(`Stundensätze laut WKO Regiestundensätze 2026 (Österreich).`);
    return lines.join("\n");
  };

  const onAppointment = () => {
    // Snapshot in localStorage -> ContactForm liest + prefilled message-Feld.
    try {
      window.localStorage.setItem("sm-kfg-summary", buildSummary());
      window.localStorage.setItem("sm-kfg-path", pathKey || "");
    } catch (e) { /* private mode */ }
    // Sanft zum Kontakt-Anker scrollen.
    if (window.smoothScrollTo) {
      window.smoothScrollTo("kontakt", 1600);
    } else {
      const el = document.getElementById("kontakt");
      if (el) el.scrollIntoView({ behavior: "smooth", block: "start" });
    }
    // ContactForm-Listener kann auf das Storage-Event reagieren wenn schon
    // sichtbar ist. Wir feuern auch ein custom event als Sicherheits-Netz.
    window.dispatchEvent(new CustomEvent("sm-kfg-summary-updated"));
  };

  // PDF-Export: oeffnet ein neues Browser-Fenster mit einem sauberen,
  // druck-optimierten HTML-Dokument. Der User druckt dieses Fenster und
  // waehlt im Druck-Dialog "Als PDF speichern". Vorteile gegenueber dem
  // alten window.print() im Haupt-Fenster: keine CSS-Konflikte mit der
  // Live-Site, sauberes A4-Layout, der User sieht eine echte Vorschau
  // bevor er druckt.
  const onPdfPrint = () => {
    const html = buildPdfHtml({
      pathKey, pathLabel, klasseLabel,
      indoorRooms, modules, assignments, moduleCounts, ausstattungsklasse,
      totalArea, totalModuleCount,
      material, install_cost, install_hours,
      commissioning_cost, commissioning_hours, total, lower, upper,
      stundensatz, safeAreaForDiv,
    });
    const w = window.open("", "_blank", "width=920,height=1200");
    if (!w) {
      alert("Bitte Pop-ups erlauben, um die PDF-Vorschau zu öffnen.");
      return;
    }
    w.document.open();
    w.document.write(html);
    w.document.close();
    // Nach Load auto-print. setTimeout damit die Schriften + SVG-Render
    // fertig sind bevor der Druck-Dialog kommt.
    const tryPrint = () => {
      try { w.focus(); w.print(); } catch (e) { /* User can manuell drucken */ }
    };
    if (w.document.readyState === "complete") setTimeout(tryPrint, 300);
    else w.addEventListener("load", () => setTimeout(tryPrint, 300));
  };

  // ───── Paketempfehlung aus Zustand ableiten ─────
  // Mapping auf die Pakete in pakete-section.jsx (A-I).
  const allAssignedMods = Object.values(assignments).flat();
  const hasEnergyMod = allAssignedMods.includes("energie") || allAssignedMods.includes("klima");
  const hasFullSet = ["licht","beschattung","heizung"].every(m => allAssignedMods.includes(m));

  let recommendedPkg;
  if (pathKey === "gewerbe") {
    const names = indoorRooms.map(r => `${r.name} ${r.type || ""}`.toLowerCase()).join(" ");
    const hasHotelSignal = /hotel|gast|gäste|gaeste|apartment|pension|suite/.test(names);
    const hasOfficeSignal = /büro|buero|office|meeting|besprech|seminar|kanzlei|ordination/.test(names);
    recommendedPkg = hasHotelSignal
      ? { id: "hotel", title: "Hotel-Paket: Zimmer & Betrieb", price: "ab 6.500 €",
          why: "Zimmer-/Gästebereiche erkannt — hier zählen Belegung, Klima, Zutritt und Technikstatus im Betrieb." }
      : hasOfficeSignal
      ? { id: "buero", title: "Büro-Paket: Komfort & Präsenz", price: "ab 4.800 €",
          why: "Büro- oder Meetingbereiche erkannt — Präsenz, Licht, Beschattung, Raumklima und Zutritt sind die wichtigsten Hebel." }
      : { id: "gewerbe", title: "Gewerbe-Starter: Energie & Technik", price: "ab 2.900 €",
          why: hasEnergyMod
            ? "Energiemonitoring und Technikstatus stehen im Vordergrund — ein guter Einstieg für Betriebsgebäude."
            : "Einstieg für Gewerbeobjekte mit Fokus auf Technikstatus, Betriebsdashboard und sauberer Schnittstellenprüfung." };
  } else if (totalModuleCount < 4 || indoorRooms.length < 3) {
    recommendedPkg = { id: "check", title: "Smart-Home-Check vor Ort", price: "ab 349 €",
      why: "Bei geringem Umfang oder noch unklarer Bestandssituation ist ein Vor-Ort-Check der saubere Einstieg." };
  } else if (pathKey === "neubau" && hasFullSet && indoorRooms.length >= 5) {
    recommendedPkg = totalArea > 200
      ? { id: "loxone", title: "Loxone Planung & Integration", price: "ab 2.900 €",
          why: "Größerer Neubau mit Licht, Beschattung und Heizung — robuste lokale Automatisierung empfehlenswert." }
      : { id: "neubau", title: "Neubau Smart-Home-Konzept", price: "ab 1.500 €",
          why: "Neubau mit vielen Räumen — Konzept- und Lastenheft vor der Elektroplanung lohnt sich." };
  } else if (hasEnergyMod) {
    recommendedPkg = { id: "energie", title: "Energiepaket PV / Wärmepumpe / Wallbox", price: "ab 1.900 €",
      why: "Energie-/Klima-Module gewählt — sichtbar machen von PV, Wärmepumpe, Speicher und Wallbox steht im Fokus." };
  } else {
    recommendedPkg = { id: "ha-starter", title: "Home-Assistant-Starter", price: "ab 1.250 €",
      why: "Mehrere Gewerke gewählt — Home Assistant als zentrale Oberfläche ist der naheliegende Einstieg." };
  }

  // Kontakt-CTA mit Paket-Subject vorbefüllen.
  const onPaketAnfrage = () => {
    try {
      window.localStorage.setItem("sm-kfg-summary", buildSummary() +
        `\n\nEmpfohlenes Paket: ${recommendedPkg.title} (${recommendedPkg.price})`);
      window.localStorage.setItem("sm-kfg-path", pathKey || "");
      window.localStorage.setItem("sm-paket-subject",
        `Anfrage: ${recommendedPkg.title}`);
    } catch (e) { /* private */ }
    if (window.smoothScrollTo) window.smoothScrollTo("kontakt", 1600);
    window.dispatchEvent(new CustomEvent("sm-kfg-summary-updated"));
    window.dispatchEvent(new CustomEvent("sm-paket-cta", { detail: { id: recommendedPkg.id, title: recommendedPkg.title } }));
  };

  return (
    <div className="kfg-content">
      <h3 style={{ fontFamily: "var(--font-display, serif)", fontSize: 28, fontWeight: 400, margin: "0 0 6px" }}>Deine Empfehlung</h3>
      <p style={{ color: KFG_PALETTE.muted, fontSize: 14, margin: "0 0 18px" }}>
        Eine erste Schätzung basierend auf {indoorRooms.length} {unitLabel.toLowerCase()} und {totalModuleCount} Modulen. Verbindlich wird's nach dem Vor-Ort-Termin.
      </p>

      {/* Hinweis: Grobeinschätzung — rechtliche Absicherung */}
      <div style={{
        marginBottom: 28, padding: "14px 18px",
        background: "rgba(58,189,208,0.06)",
        border: `1px solid ${KFG_PALETTE.line}`,
        borderLeft: `3px solid ${KFG_PALETTE.accent}`,
        borderRadius: 4,
        fontSize: 13, lineHeight: 1.55,
        color: KFG_PALETTE.muted,
        fontFamily: "Inter, sans-serif",
      }}>
        <strong style={{ color: KFG_PALETTE.ink, fontWeight: 600 }}>Grobeinschätzung, kein verbindliches Angebot.</strong>{" "}
        Der Konfigurator liefert eine erste Orientierung. Das finale Angebot entsteht nach Vor-Ort-Check,
        Schnittstellenprüfung und Abstimmung der Hardware. Elektroinstallationsarbeiten erfolgen
        über konzessionierte Partnerbetriebe.
      </div>

      <div className="kfg-sum-grid">
        <div className="kfg-sum-rooms">
          {indoorRooms.map(r => {
            const mods = assignments[r.id] || [];
            const roomPrice = mods.reduce((s,m) => s + (modules[m] ? window.getModulePrice(modules[m], r, moduleCounts, r.id, m, ausstattungsklasse) : 0), 0);
            return (
              <div key={r.id} className="kfg-sum-room">
                <div>
                  <div className="name">{r.name}</div>
                  <div className="meta">{r.floor} · {r.area} m² · {r.windows} Fenster · {mods.length} Module</div>
                </div>
                <div className="price">€{roomPrice.toLocaleString('de-AT')}</div>
                <div className="mods">
                  {mods.length === 0 ? (
                    <span style={{ fontSize: 11, color: KFG_PALETTE.muted, fontStyle: "italic" }}>Keine Module ausgewählt</span>
                  ) : mods.map(m => modules[m] ? (
                    <span key={m} className="mod-chip">
                      <span className="ic">{modules[m].icon}</span>
                      {modules[m].label}
                    </span>
                  ) : null)}
                </div>
              </div>
            );
          })}
        </div>

        <div className="kfg-sum-card">
          <h4>Grobschätzung · {pathLabel} · {klasseLabel}</h4>
          <div className="kfg-sum-total">
            <span className="from">ab</span>€{lower.toLocaleString('de-AT')}
          </div>
          <div className="kfg-sum-range">Bandbreite: €{lower.toLocaleString('de-AT')} – €{upper.toLocaleString('de-AT')}</div>

          <div className="kfg-sum-breakdown">
            <div className="row">
              <span className="lbl">Material<br/><span style={{ fontSize: 10, opacity: 0.6 }}>Aktoren, Sensoren, Taster</span></span>
              <span className="v">€{material.toLocaleString('de-AT')}</span>
            </div>
            <div className="row">
              <span className="lbl">Installation<br/><span style={{ fontSize: 10, opacity: 0.6 }}>{install_hours} h × €{stundensatz.monteur.toFixed(2)} Monteur</span></span>
              <span className="v">€{install_cost.toLocaleString('de-AT')}</span>
            </div>
            <div className="row">
              <span className="lbl">Inbetriebnahme<br/><span style={{ fontSize: 10, opacity: 0.6 }}>{commissioning_hours} h × €{stundensatz.spezial_monteur.toFixed(2)} Spezial</span></span>
              <span className="v">€{commissioning_cost.toLocaleString('de-AT')}</span>
            </div>
            <div className="row" style={{ paddingTop: 10, borderTop: "1px solid rgba(245,241,232,0.15)" }}>
              <span className="lbl">Geschätzte Gesamtsumme</span>
              <span className="v" style={{ fontWeight: 600 }}>€{total.toLocaleString('de-AT')}</span>
            </div>
            <div className="row">
              <span className="lbl">{areaLabel}</span><span className="v">{totalArea.toFixed(0)} m²</span>
            </div>
            <div className="row">
              <span className="lbl">Preis pro m²</span><span className="v">~€{Math.round(total/safeAreaForDiv).toLocaleString('de-AT')}</span>
            </div>
          </div>

          <div className="kfg-sum-actions">
            <button type="button" className="kfg-btn kfg-btn-primary" onClick={onAppointment}>Termin vereinbaren →</button>
            <button type="button" className="kfg-btn kfg-btn-ghost-light" onClick={onPdfPrint}>Schätzung als PDF</button>
          </div>

          {/* Paketempfehlung — leitet zur passenden Variante in pakete-section */}
          <div style={{
            marginTop: 22, padding: "18px 18px",
            background: "rgba(58,189,208,0.07)",
            border: `1px solid ${KFG_PALETTE.accent}55`,
            borderRadius: 6,
          }}>
            <div style={{
              fontFamily: "var(--font-mono, monospace)", fontSize: 10,
              letterSpacing: "0.18em", textTransform: "uppercase",
              color: KFG_PALETTE.accent, marginBottom: 8,
            }}>
              ▶ Empfohlenes Paket
            </div>
            <div style={{
              fontFamily: "var(--font-display, serif)", fontSize: 18,
              fontWeight: 500, color: KFG_PALETTE.ink, marginBottom: 4,
              lineHeight: 1.25,
            }}>
              {recommendedPkg.title}
            </div>
            <div style={{
              fontFamily: "Inter, sans-serif", fontSize: 13,
              color: KFG_PALETTE.accent, marginBottom: 8, fontWeight: 500,
            }}>
              {recommendedPkg.price}
            </div>
            <div style={{
              fontFamily: "Inter, sans-serif", fontSize: 12.5,
              lineHeight: 1.55, color: KFG_PALETTE.muted, marginBottom: 12,
            }}>
              {recommendedPkg.why}
            </div>
            <button type="button" onClick={onPaketAnfrage}
                    className="kfg-btn kfg-btn-ghost-light"
                    style={{ width: "100%", justifyContent: "center" }}>
              {recommendedPkg.title} anfragen →
            </button>
          </div>

          <p className="kfg-sum-disclaimer">
            Unverbindliche Erstschätzung. Stundensätze laut WKO 2026 (AT). Endpreis hängt von Vor-Ort-Begehung,
            Materialqualität und individuellen Wünschen ab. Bandbreite ±18%.
            Elektroinstallationsarbeiten erfolgen über konzessionierte Partnerbetriebe.
          </p>
        </div>
      </div>

      <div className="kfg-footer-nav">
        <div className="left">
          <button className="kfg-back" onClick={onBack}>← Module bearbeiten</button>
        </div>
        <div className="right">
          <button className="kfg-back" onClick={onRestart}>↻ Neu starten</button>
        </div>
      </div>
    </div>
  );
}

window.KfgStep1Manual = KfgStep1Manual;
window.KfgStep1Import = KfgStep1Import;
window.KfgStep2Modules = KfgStep2Modules;
window.KfgStep3Summary = KfgStep3Summary;
