/* eslint-disable */
/* Lydtrix Dashboard — React + Babel-Standalone, kein Build-Step.
   Mappt die 9 Konzept-Seiten auf die Server-REST-Endpoints.
*/
(async function bootstrap() {
  const authed = await window.Lydtrix.auth.init();
  if (!authed) return;

  // Welche Anlagen darf der eingeloggte User sehen?
  let sites = [];
  try {
    sites = await window.Lydtrix.api.sites();
  } catch (e) {
    document.getElementById("root").innerHTML =
      '<div style="padding:40px;color:#b91c1c">Fehler beim Laden der Anlagen: ' +
      (e.message || e) + '</div>';
    return;
  }

  // Noch keine Anlage → Onboarding (nur Claim-Formular).
  if (!sites || sites.length === 0) {
    renderOnboarding();
    return;
  }

  // Gespeicherte Auswahl bevorzugen, sonst erste Anlage.
  const stored = localStorage.getItem("lydtrix.siteId");
  const chosen = sites.find(s => s.siteId === stored) || sites[0];
  window.Lydtrix.api.setSite(chosen.siteId);
  localStorage.setItem("lydtrix.siteId", chosen.siteId);
  window.__lydtrixSites  = sites;
  window.__lydtrixSiteId = chosen.siteId;
  startApp();
})();

/* Onboarding-Screen: der User hat noch keine Anlage und kann nur claimen. */
function renderOnboarding() {
  const { useState } = React;
  const api = window.Lydtrix.api;
  const auth = window.Lydtrix.auth;

  function Onboarding() {
    const [token, setToken] = useState("");
    const [msg, setMsg] = useState(null);
    const [busy, setBusy] = useState(false);

    async function claim() {
      setBusy(true); setMsg(null);
      try {
        await api.claimDevice(token.trim());
        setMsg({ ok: true, text: "Gerät verknüpft. Lade Anlage…" });
        setTimeout(() => window.location.reload(), 900);
      } catch (e) {
        setMsg({ ok: false, text: e.message || String(e) });
        setBusy(false);
      }
    }

    return (
      <div style={{minHeight:"100vh", display:"flex", alignItems:"center", justifyContent:"center", padding:24}}>
        <div className="card" style={{maxWidth:460, width:"100%"}}>
          <div className="card-h"><h3>Willkommen bei LYDTRIX</h3></div>
          <div className="card-b">
            <p style={{color:"var(--text-3)", fontSize:13, marginTop:0}}>
              Du hast noch keine Anlage. Verknüpfe dein Energiemanagement mit dem
              Claim-Token aus dem QR-Code des Edge-Geräts.
            </p>
            <div className="v-stack" style={{gap:8}}>
              <label className="muted" style={{fontSize:11}}>Claim-Token</label>
              <input type="text" value={token} onChange={e => setToken(e.target.value)}
                placeholder="Token aus QR-Code…"/>
              <button className="btn" onClick={claim} disabled={!token.trim() || busy} style={{marginTop:8}}>
                {busy && <span className="loading"/>} Anlage verknüpfen
              </button>
            </div>
            {msg && <div style={{marginTop:12, color: msg.ok ? "var(--pv)" : "var(--danger)"}}>{msg.text}</div>}
            <div style={{marginTop:18, textAlign:"right"}}>
              <button className="btn ghost" onClick={() => auth.logout()}>Abmelden</button>
            </div>
          </div>
        </div>
      </div>
    );
  }

  ReactDOM.createRoot(document.getElementById("root")).render(<Onboarding/>);
}

function startApp() {

const { useState, useEffect, useMemo, useRef, useCallback } = React;
const api = window.Lydtrix.api;
const auth = window.Lydtrix.auth;

/* ------------------------------------------------------------------ */
/* Icons                                                              */
/* ------------------------------------------------------------------ */
const Icon = ({ name, size = 18, stroke = 1.6 }) => {
  const p = {
    width: size, height: size, viewBox: "0 0 24 24",
    fill: "none", stroke: "currentColor",
    strokeWidth: stroke, strokeLinecap: "round", strokeLinejoin: "round",
  };
  switch (name) {
    case "home":      return <svg {...p}><path d="M3 11l9-8 9 8"/><path d="M5 9.5V21h14V9.5"/></svg>;
    case "flow":      return <svg {...p}><circle cx="5" cy="12" r="2.2"/><circle cx="19" cy="6" r="2.2"/><circle cx="19" cy="18" r="2.2"/><path d="M7 12l10-5"/><path d="M7 12l10 5"/></svg>;
    case "sun":       return <svg {...p}><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4 12H2M22 12h-2M5 5l1.5 1.5M17.5 17.5L19 19M5 19l1.5-1.5M17.5 6.5L19 5"/></svg>;
    case "plug":      return <svg {...p}><path d="M9 2v4M15 2v4"/><path d="M7 6h10v6a5 5 0 01-10 0V6z"/><path d="M12 17v5"/></svg>;
    case "grid":      return <svg {...p}><path d="M3 3h7v7H3zM14 3h7v7h-7zM3 14h7v7H3zM14 14h7v7h-7z"/></svg>;
    case "history":   return <svg {...p}><path d="M3 12a9 9 0 1 0 3-6.7"/><path d="M3 4v5h5"/><path d="M12 7v5l3 2"/></svg>;
    case "alert":     return <svg {...p}><path d="M12 3l10 17H2L12 3z"/><path d="M12 10v4M12 17v.01"/></svg>;
    case "settings":  return <svg {...p}><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.7 1.7 0 0 0 .3 1.8l.1.1a2 2 0 1 1-2.8 2.8l-.1-.1a1.7 1.7 0 0 0-1.8-.3 1.7 1.7 0 0 0-1 1.5V21a2 2 0 1 1-4 0v-.1a1.7 1.7 0 0 0-1-1.5 1.7 1.7 0 0 0-1.8.3l-.1.1a2 2 0 1 1-2.8-2.8l.1-.1a1.7 1.7 0 0 0 .3-1.8 1.7 1.7 0 0 0-1.5-1H3a2 2 0 1 1 0-4h.1a1.7 1.7 0 0 0 1.5-1 1.7 1.7 0 0 0-.3-1.8l-.1-.1a2 2 0 1 1 2.8-2.8l.1.1a1.7 1.7 0 0 0 1.8.3h0a1.7 1.7 0 0 0 1-1.5V3a2 2 0 1 1 4 0v.1a1.7 1.7 0 0 0 1 1.5 1.7 1.7 0 0 0 1.8-.3l.1-.1a2 2 0 1 1 2.8 2.8l-.1.1a1.7 1.7 0 0 0-.3 1.8v0a1.7 1.7 0 0 0 1.5 1H21a2 2 0 1 1 0 4h-.1a1.7 1.7 0 0 0-1.5 1z"/></svg>;
    case "consumption": return <svg {...p}><path d="M3 21h18"/><path d="M6 21V10M10 21V6M14 21v-9M18 21v-5"/></svg>;
    case "bolt":      return <svg {...p}><path d="M13 2L4 14h7l-1 8 9-12h-7l1-8z"/></svg>;
    case "moon":      return <svg {...p}><path d="M21 12.5A8.5 8.5 0 1 1 11.5 3a7 7 0 0 0 9.5 9.5z"/></svg>;
    case "logout":    return <svg {...p}><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><path d="M16 17l5-5-5-5M21 12H9"/></svg>;
    case "refresh":   return <svg {...p}><path d="M21 12a9 9 0 0 1-15 6.7L3 16"/><path d="M3 12a9 9 0 0 1 15-6.7L21 8"/><path d="M21 3v5h-5M3 21v-5h5"/></svg>;
    case "calendar":  return <svg {...p}><rect x="3" y="5" width="18" height="16" rx="2"/><path d="M8 3v4M16 3v4M3 11h18"/></svg>;
    case "check":     return <svg {...p}><path d="M5 13l4 4L19 7"/></svg>;
    case "x":         return <svg {...p}><path d="M6 6l12 12M6 18L18 6"/></svg>;
    default:          return <svg {...p}/>;
  }
};

/* ------------------------------------------------------------------ */
/* Atomic helpers                                                     */
/* ------------------------------------------------------------------ */
const fmt = {
  num: (v, d = 0) => (v == null || isNaN(v) ? "—" : Number(v).toLocaleString("de-AT", { minimumFractionDigits: d, maximumFractionDigits: d })),
  dt:  (iso) => iso ? new Date(iso).toLocaleString("de-AT") : "—",
  time:(d=new Date()) => d.toLocaleTimeString("de-AT", { hour:"2-digit", minute:"2-digit", second:"2-digit" }),
  date:(d=new Date()) => d.toLocaleDateString("de-AT", { weekday:"long", year:"numeric", month:"long", day:"numeric" }),
};

const Card = ({ title, sub, right, children, flush, tight }) => (
  <div className="card">
    {(title || right) && (
      <div className="card-h">
        {title && <h3>{title}</h3>}
        {sub && <span className="sub">{sub}</span>}
        {right && <div className="right">{right}</div>}
      </div>
    )}
    <div className={`card-b ${flush?"flush":""} ${tight?"tight":""}`}>{children}</div>
  </div>
);

const KPI = ({ icon, color, label, value, unit, foot }) => (
  <div className="kpi">
    <div className="row1">
      <div className="ico-pill" style={{background: color + "22", color}}>
        <Icon name={icon} size={15} stroke={2}/>
      </div>
      <span className="label">{label}</span>
    </div>
    <div className="value">
      <span className="num">{value}</span>
      {unit && <span className="u">{unit}</span>}
    </div>
    {foot && <div className="foot">{foot}</div>}
  </div>
);

const Badge = ({ children, kind="" }) => (
  <span className={`badge ${kind}`}><span className="d"/>{children}</span>
);

const Seg = ({ items, value, onChange }) => (
  <div className="seg">
    {items.map(it => (
      <button key={it.value} className={value === it.value ? "on" : ""}
        onClick={() => onChange(it.value)}>{it.label}</button>
    ))}
  </div>
);

const Loading = () => <div className="empty-state"><span className="loading"/> &nbsp;Laden…</div>;
const ErrorBox = ({ msg }) => <div className="error-box">⚠ {msg}</div>;
const Empty = ({ children }) => <div className="empty-state">{children}</div>;

/* ------------------------------------------------------------------ */
/* Data hooks                                                         */
/* ------------------------------------------------------------------ */
// pollMs darf eine Zahl ODER eine Funktion (data) => ms sein (0 = kein Polling),
// damit das Intervall von den zuletzt geladenen Daten abhängen kann.
function useApi(fn, deps = [], pollMs = 0) {
  const [data, setData] = useState(null);
  const [err, setErr]   = useState(null);
  const [loading, setLoading] = useState(true);
  const reload = useCallback(async () => {
    try {
      setLoading(true);
      const d = await fn();
      setData(d); setErr(null);
    } catch (e) {
      setErr(e.message || String(e));
    } finally {
      setLoading(false);
    }
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, deps);

  const interval = typeof pollMs === "function" ? pollMs(data) : pollMs;

  useEffect(() => { reload(); }, [reload]);

  // Reagiert auf Intervall-Wechsel (z. B. 2s → normal, sobald Live-Daten da sind).
  useEffect(() => {
    if (interval > 0) {
      const id = setInterval(reload, interval);
      return () => clearInterval(id);
    }
  }, [reload, interval]);

  return { data, err, loading, reload };
}

// Poll-Intervalle für den Live-State.
const POLL_FAST_MS = 2000;    // Aufwärmphase nach Öffnen, bis frische Live-Werte da sind
const LIVE_FRESH_MS = 30000;  // Live-Werte gelten als "frisch", wenn jünger (Live-Tick = 10s)

// Sind FRISCHE Live-Werte da? Über den Zeitstempel statt nur über Vorhandensein —
// sonst gelten veraltete Cache-Werte der letzten Session sofort als "da".
function liveFresh(d) {
  const ts = d?.lastLiveValuesUtc ? Date.parse(d.lastLiveValuesUtc) : 0;
  return ts > 0 && Date.now() - ts < LIVE_FRESH_MS;
}

// Aufwärmphase = noch nichts geladen ODER Gerät online, aber noch nicht frisch.
// Offline wärmt NICHT auf → normaler Takt (es kommt ohnehin kein Live-Wert).
function isWarmingUp(d) {
  return !d || (d.connected && !liveFresh(d));
}

// Adaptiver State-Poll: schnell (2s) in der Aufwärmphase, danach normalMs.
const statePoll = (normalMs) => (d) => (isWarmingUp(d) ? POLL_FAST_MS : normalMs);

function useNow(intervalMs = 1000) {
  const [t, setT] = useState(new Date());
  useEffect(() => { const id = setInterval(() => setT(new Date()), intervalMs); return () => clearInterval(id); }, [intervalMs]);
  return t;
}

/* ------------------------------------------------------------------ */
/* OVERVIEW                                                           */
/* ------------------------------------------------------------------ */
function OverviewPage() {
  const state = useApi(api.state, [], statePoll(10000));
  const alarms = useApi(() => api.alarms(new Date(Date.now() - 24*3600*1000).toISOString(), null), [], 30000);
  const devices = useApi(api.devices, [], 30000);

  if (state.loading) return <Loading/>;
  if (state.err) return <ErrorBox msg={state.err}/>;

  const s = state.data || {};
  const dev = (devices.data || [])[0];

  // aggregate live values, if state.stateDevices has alarms data
  let connected = s.connected ? "verbunden" : "offline";
  let lastSeen = s.lastHeartbeatUtc ? new Date(s.lastHeartbeatUtc).toLocaleString("de-AT") : "—";
  let activeAlarms = 0;
  if (s.stateDevices && s.stateDevices.devices) {
    s.stateDevices.devices.forEach(d => activeAlarms += (d.alarms || []).length);
  }

  return (
    <div className="page col">
      <div className="kpi-row">
        <KPI icon="bolt" color="var(--brand)"
          label="Verbindung"
          value={connected}
          foot={<>Last seen: {lastSeen}</>}/>
        <KPI icon="grid" color="var(--grid)"
          label="Geräte registriert"
          value={(devices.data || []).length}
          unit="" foot={dev ? <>Client-ID: <span className="mono">{dev.mqttClientId}</span></> : null}/>
        <KPI icon="alert" color={activeAlarms ? "var(--danger)" : "var(--pv)"}
          label="Aktive Alarme"
          value={activeAlarms}
          foot={activeAlarms === 0 ? "Alles im grünen Bereich." : "Bitte Meldungen prüfen."}/>
        <KPI icon="history" color="var(--brand-2)"
          label="Alarme (24h)"
          value={(alarms.data || []).length}
          foot="aus dem Verlauf der letzten 24 Stunden"/>
      </div>

      <div className="row" style={{alignItems:"stretch"}}>
        <Card title="Geräte-Status" sub="aus retained state/devices" className="grow" style={{flex:2}}>
          {!s.stateDevices && <Empty>Noch kein Snapshot vom Edge empfangen.</Empty>}
          {s.stateDevices && (
            <table className="t">
              <thead><tr>
                <th>Name</th><th>Status</th><th>Connected</th><th>LastCom</th><th>Alarme</th>
              </tr></thead>
              <tbody>
                {(s.stateDevices.devices || []).map((d, i) => (
                  <tr key={i}>
                    <td>{d.deviceName}</td>
                    <td>{d.deviceState}</td>
                    <td>{d.connected ? <Badge kind="ok">online</Badge> : <Badge kind="bad">offline</Badge>}</td>
                    <td className="muted">{d.lastCom}</td>
                    <td>{(d.alarms || []).length}</td>
                  </tr>
                ))}
              </tbody>
            </table>
          )}
        </Card>

        <Card title="Letzte Alarme" right={<a href="#meldungen" style={{color:"var(--brand)"}}>Alle ›</a>} style={{flex:1, minWidth:0}}>
          {alarms.loading && <Loading/>}
          {alarms.err && <ErrorBox msg={alarms.err}/>}
          {alarms.data && alarms.data.length === 0 && <Empty>Keine Alarme in den letzten 24h.</Empty>}
          {alarms.data && alarms.data.slice(0, 6).map((a, i) => (
            <div className="alert-item" key={i} style={{padding:"10px 0", gridTemplateColumns:"auto 1fr"}}>
              <span className="alert-dot" style={{background: a.type === 1 ? "var(--danger)" : "var(--charge)"}}/>
              <div>
                <div style={{fontWeight:500, fontSize:13}}>{a.message}</div>
                <div className="muted" style={{fontSize:11.5}}>
                  {a.deviceName} · {fmt.dt(a.time)}
                </div>
              </div>
            </div>
          ))}
        </Card>
      </div>
    </div>
  );
}

/* ------------------------------------------------------------------ */
/* Device-categorization helpers — used by EnergyFlow & Consumption   */
/* ------------------------------------------------------------------ */
function categorizeDevice(name, group) {
  const n = (name || "").toLowerCase();
  const g = (group || "").toLowerCase();
  if (g.includes("inverter") || g.includes("pv") || n.includes("pv") || n.includes("solar") || n.includes("inverter") || n.includes("wechselrichter"))
    return "pv";
  if (g.includes("battery") || n.includes("battery") || n.includes("batterie") || n.includes("speicher") || n === "bat")
    return "battery";
  if (g.includes("charger") || g.includes("wallbox") || n.includes("wallbox") || n.includes("ladepunkt") || /^lp\d+$/i.test(name) || /^wm\d+$/i.test(name) || /^wb\d+$/i.test(name) || n.includes("charger"))
    return "charge";
  if (g.includes("meter") || n.includes("netz") || n.includes("grid") || n === "rest" || n.endsWith("meter")
      || n === "ez" || n === "energiezähler" || n === "energiezaehler" || /^ez\d*$/i.test(name) || /zähler|zaehler/i.test(n))
    return "grid";
  return "load";
}

const CAT_INFO = {
  pv:      { label: "PV",            color: "var(--pv)",     icon: "sun" },
  grid:    { label: "Netz",          color: "var(--grid)",   icon: "grid" },
  charge:  { label: "Ladeinfra",     color: "var(--charge)", icon: "plug" },
  battery: { label: "Speicher",      color: "var(--brand-2)",icon: "bolt" },
  load:    { label: "Verbrauch",     color: "var(--brand)",  icon: "consumption" },
};

/* Mappt den server-seitigen SingleDeviceDataType (String) auf die Kategorie. */
const TYPE_TO_CAT = {
  grid: "grid", pv: "pv", battery: "battery", charger: "charge", load: "load", building: "load",
};
function typeToCat(type, name) {
  const t = String(type || "").toLowerCase();
  return TYPE_TO_CAT[t] || categorizeDevice(name, "");
}

/* Live-Payload (neu): ein Objekt je Geraet mit name/type/state/power/soc/
   energyImport/energyExport. Rueckgabeform bleibt {device, group, power, cat},
   damit EnergyFlow/Consumption/Production unveraendert weiterlaufen. */
function extractLivePower(liveValues) {
  if (!liveValues || !Array.isArray(liveValues.values)) return [];
  return liveValues.values.map(v => ({
    device: v.name || "",
    group: "",
    power: Number(v.power) || 0,
    soc: v.soc != null ? Number(v.soc) : null,
    energyImport: Number(v.energyImport) || 0,
    energyExport: Number(v.energyExport) || 0,
    state: String(v.state || "").toLowerCase(),
    cat: typeToCat(v.type, v.name),
  }));
}

/* ------------------------------------------------------------------ */
/* ENERGIEFLUSS                                                       */
/* ------------------------------------------------------------------ */
function EnergyFlowPage() {
  const state = useApi(api.state, [], statePoll(5000));

  if (state.loading) return <Loading/>;
  if (state.err) return <ErrorBox msg={state.err}/>;

  const s = state.data || {};
  const livePower = extractLivePower(s.latestLiveValues);
  const devices = (s.stateDevices && s.stateDevices.devices) || [];

  // Sum power per category
  const totals = { pv:0, grid:0, charge:0, battery:0, load:0 };
  livePower.forEach(p => { totals[p.cat] = (totals[p.cat] || 0) + Math.abs(p.power); });
  // For "load" sum we use the absolute building consumption — fallback if no live values
  const hasLive = livePower.length > 0;

  return (
    <div className="page col" style={{gap:14}}>
      <div className="kpi-row">
        <KPI icon="sun" color="var(--pv)"
          label="PV-Erzeugung jetzt"
          value={hasLive ? totals.pv.toFixed(2) : "—"}
          unit="kW"
          foot={livePower.filter(p => p.cat==="pv").length + " Strang/Wechselrichter"}/>
        <KPI icon="grid" color="var(--grid)"
          label="Netz jetzt"
          value={hasLive ? totals.grid.toFixed(2) : "—"}
          unit="kW"
          foot="aus Live-Telemetrie"/>
        <KPI icon="consumption" color="var(--brand)"
          label="Gebäude jetzt"
          value={hasLive ? totals.load.toFixed(2) : "—"}
          unit="kW"
          foot={livePower.filter(p => p.cat==="load").length + " Stromkreise"}/>
        <KPI icon="plug" color="var(--charge)"
          label="Ladeinfrastruktur"
          value={hasLive ? totals.charge.toFixed(2) : "—"}
          unit="kW"
          foot={(s.stateDevices?.devices || []).filter(d => /^lp/i.test(d.deviceName)).length + " Wallboxen"}/>
      </div>

      <Card title="Energiefluss"
        sub={hasLive ? `Live · ${fmt.dt(s.lastLiveValuesUtc)}` : "noch keine Live-Daten — schalte Live-Mode in Einstellungen ein"}>
        {!hasLive && (
          <Empty>
            Sobald Live-Mode aktiv ist und der Edge Power-Werte sendet, erscheint hier
            der animierte Fluss zwischen PV, Netz, Verbrauch und Ladeinfrastruktur.
          </Empty>
        )}
        {hasLive && <FlowDiagram totals={totals}/>}
      </Card>

      <div className="grid-2">
        <Card title="Anlagenstatus" sub={`${devices.length} Komponenten · Heartbeat ${fmt.dt(s.lastHeartbeatUtc)}`}>
          {devices.length === 0 && <Empty>Noch kein State-Snapshot vom Edge.</Empty>}
          {devices.length > 0 && (
            <div style={{display:"grid", gridTemplateColumns:"1fr 1fr", gap:10}}>
              {devices.map((d, i) => {
                const cat = categorizeDevice(d.deviceName, "");
                const info = CAT_INFO[cat];
                return (
                  <div key={i} style={{padding:"10px 12px", border:"1px solid var(--border-2)", borderRadius:8, background:"var(--surface-2)"}}>
                    <div className="h-stack" style={{justifyContent:"space-between", marginBottom:4}}>
                      <span className="h-stack" style={{gap:8}}>
                        <span style={{color: info.color}}><Icon name={info.icon} size={13}/></span>
                        <span style={{fontSize:12.5, fontWeight:600}}>{d.deviceName}</span>
                      </span>
                      {d.connected ? <Badge kind="ok">online</Badge> : <Badge kind="bad">offline</Badge>}
                    </div>
                    <div className="muted" style={{fontSize:11.5}}>
                      {d.deviceState} · {(d.alarms || []).length > 0 ? `${d.alarms.length} Alarme` : "keine Alarme"}
                    </div>
                  </div>
                );
              })}
            </div>
          )}
        </Card>

        <Card title="Aktive Leistung pro Quelle/Senke" sub="aus Live-Telemetrie">
          {!hasLive && <Empty>Wartet auf Live-Daten.</Empty>}
          {hasLive && (
            <div style={{display:"flex", flexDirection:"column", gap:8}}>
              {livePower
                .filter(p => Math.abs(p.power) > 0.01)
                .sort((a,b) => Math.abs(b.power) - Math.abs(a.power))
                .map((p, i) => {
                  const info = CAT_INFO[p.cat];
                  return (
                    <div key={i} style={{display:"grid", gridTemplateColumns:"auto 1fr auto auto", gap:10, alignItems:"center", padding:"8px 0", borderBottom:"1px solid var(--border-2)"}}>
                      <span style={{width:10, height:10, borderRadius:2, background: info.color}}/>
                      <span style={{fontSize:13, fontWeight:500}}>{p.device}</span>
                      <span className="badge">{info.label}</span>
                      <span className="mono" style={{fontWeight:600, textAlign:"right"}}>
                        {p.power.toFixed(2)} kW
                      </span>
                    </div>
                  );
                })}
            </div>
          )}
        </Card>
      </div>
    </div>
  );
}

/* Animated SVG flow — central bus, four ports */
function FlowDiagram({ totals }) {
  const W = 920, H = 380;
  const nodes = {
    pv:     { x: 90,  y: 90,  r: 38, label:"PV",      value:`${totals.pv.toFixed(2)} kW`,     sub:"Erzeugung",  color:"var(--pv)",     icon:"sun" },
    grid:   { x: 90,  y: 290, r: 38, label:"Netz",    value:`${totals.grid.toFixed(2)} kW`,   sub:"Bezug/Einspeisung", color:"var(--grid)",   icon:"grid" },
    bus:    { x: 460, y: 190, r: 50, label:"Hauptknoten", value:`${(totals.pv+totals.grid).toFixed(2)} kW`, sub:"Energiezähler", color:"var(--brand)", icon:"flow" },
    load:   { x: 830, y: 90,  r: 38, label:"Gebäude", value:`${totals.load.toFixed(2)} kW`,   sub:"Verbrauch",  color:"var(--c1)",     icon:"consumption" },
    charge: { x: 830, y: 290, r: 38, label:"Ladeinfra", value:`${totals.charge.toFixed(2)} kW`, sub:"Wallboxen", color:"var(--charge)", icon:"plug" },
  };
  const links = [
    { from:"pv",   to:"bus",    value: totals.pv,     color:"var(--pv)" },
    { from:"grid", to:"bus",    value: totals.grid,   color:"var(--grid)" },
    { from:"bus",  to:"load",   value: totals.load,   color:"var(--brand)" },
    { from:"bus",  to:"charge", value: totals.charge, color:"var(--charge)" },
  ];

  return (
    <svg viewBox={`0 0 ${W} ${H}`} width="100%" style={{display:"block"}}>
      {links.map((l, i) => {
        const a = nodes[l.from], b = nodes[l.to];
        const mx = (a.x + b.x)/2;
        const d = `M ${a.x} ${a.y} C ${mx} ${a.y}, ${mx} ${b.y}, ${b.x} ${b.y}`;
        const kw = l.value;
        const inactive = Math.abs(kw) < 0.05;
        const sw = Math.max(2, Math.min(14, Math.sqrt(Math.abs(kw)) * 2.4));
        return (
          <g key={i}>
            <path d={d} stroke={l.color} strokeOpacity={inactive ? 0.1 : 0.18}
              strokeWidth={sw} fill="none" strokeLinecap="round"/>
            <path d={d} stroke={l.color} strokeOpacity={inactive ? 0.2 : 0.95}
              strokeWidth={Math.max(1.5, sw*0.45)} fill="none" strokeLinecap="round"
              strokeDasharray="4 8"
              style={{ animation: inactive ? "none" : "flow-dash 1.6s linear infinite" }}/>
            {!inactive && Array.from({length: Math.max(2, Math.round(Math.abs(kw)/3))}, (_, p) => (
              <circle key={p} r="3" fill={l.color}>
                <animateMotion dur={`${2.8 - Math.min(1.6, Math.abs(kw)/40)}s`} repeatCount="indefinite"
                  begin={`${p * 0.5}s`} path={d}/>
              </circle>
            ))}
          </g>
        );
      })}

      {Object.entries(nodes).map(([k, n]) => (
        <g key={k} transform={`translate(${n.x}, ${n.y})`}>
          <circle r={n.r + 8} fill={n.color} fillOpacity="0.06"/>
          <circle r={n.r} fill="var(--surface)" stroke={n.color} strokeWidth="2"/>
          <foreignObject x={-14} y={-14} width="28" height="28">
            <div xmlns="http://www.w3.org/1999/xhtml" style={{width:28,height:28,display:"flex",alignItems:"center",justifyContent:"center", color: n.color}}>
              <Icon name={n.icon} size={20} stroke={1.8}/>
            </div>
          </foreignObject>
          <text y={n.r + 20} textAnchor="middle" fontSize="12.5" fontWeight="600" fill="var(--text)">{n.label}</text>
          <text y={n.r + 36} textAnchor="middle" fontSize="13" fontWeight="600" fill={n.color}
            style={{fontVariantNumeric:"tabular-nums"}}>{n.value}</text>
          <text y={n.r + 50} textAnchor="middle" fontSize="11" fill="var(--text-3)">{n.sub}</text>
        </g>
      ))}

      <style>{`@keyframes flow-dash { to { stroke-dashoffset: -24; } }`}</style>
    </svg>
  );
}

/* ------------------------------------------------------------------ */
/* VERBRAUCH (Stacked area + breakdown table + live donut)            */
/* ------------------------------------------------------------------ */
const CIRCUIT_COLORS = ["var(--c1)","var(--c2)","var(--c3)","var(--c4)","var(--c5)","var(--c6)","var(--c7)","var(--c8)","var(--c9)","var(--c10)","var(--c11)","var(--c12)"];

function ConsumptionPage() {
  const [range, setRange] = useState("24h");
  const state = useApi(api.state, [], statePoll(10000));

  const fromIso = useMemo(() => {
    const now = Date.now();
    if (range === "24h") return new Date(now - 24*3600*1000).toISOString();
    if (range === "7d")  return new Date(now -  7*24*3600*1000).toISOString();
    if (range === "30d") return new Date(now - 30*24*3600*1000).toISOString();
    return new Date(now - 24*3600*1000).toISOString();
  }, [range]);

  // Holt EnergyImport_kWh (= absolute Zaehlerwerte) — daraus berechnen wir den Verbrauch
  // pro Geraet als (max - min) im Zeitraum. Faellt der ValueName auf der Anlage
  // anders, fallback auf "Power" und summiere.
  const meters = useApi(() => api.meters(fromIso, null, null), [fromIso]);

  // Per-device-aggregation + per-hour stacking
  const { perDevice, totals, hourly } = useMemo(() => {
    const result = { perDevice: [], totals: 0, hourly: [] };
    if (!meters.data || meters.data.length === 0) return result;

    // Filter auf konsum-relevante value_names. Faengt "Energy Import",
    // "EnergyImport", "Energy_Import", "Verbrauch" etc. ab. NICHT "Energy Export".
    const importRows = meters.data.filter(m => /^energy[\s_-]?import\b/i.test(m.valueName) || /verbrauch|consumption/i.test(m.valueName));
    const rows = importRows.length > 0 ? importRows : meters.data.filter(m => /^power$/i.test(m.valueName));
    if (rows.length === 0) return result;

    // pro Device: min/max bei kWh-Counters → diff; bei Power → mean (W) → kWh per duration
    const byDev = new Map();
    rows.forEach(r => {
      if (!byDev.has(r.deviceName)) byDev.set(r.deviceName, []);
      byDev.get(r.deviceName).push(r);
    });

    const fromMs = Date.parse(fromIso);
    const toMs = Date.now();
    const dur = Math.max(1, (toMs - fromMs) / 3600000); // hours

    const dev = [];
    byDev.forEach((arr, name) => {
      arr.sort((a,b) => Date.parse(a.time) - Date.parse(b.time));
      let kwh = 0;
      if (importRows.length > 0) {
        // Counter-Diff
        const first = arr[0]?.value ?? 0;
        const last = arr[arr.length - 1]?.value ?? first;
        kwh = Math.max(0, last - first);
      } else {
        // Power-Mean * Hours
        const sum = arr.reduce((s, r) => s + Math.abs(Number(r.value) || 0), 0);
        const meanW = sum / arr.length;
        kwh = meanW * dur / 1000;
      }
      dev.push({ name, kwh, samples: arr.length });
    });

    dev.sort((a,b) => b.kwh - a.kwh);
    const total = dev.reduce((s,d) => s + d.kwh, 0);
    dev.forEach((d, i) => { d.pct = total > 0 ? (d.kwh / total) * 100 : 0; d.color = CIRCUIT_COLORS[i % CIRCUIT_COLORS.length]; });

    // Hourly stacked: 24 bins (oder 168 fuer Woche). Wir bauen 24 fix.
    const bins = 24;
    const binMs = (toMs - fromMs) / bins;
    const hourlyTotal = Array.from({length: bins}, () => 0);
    const series = dev.slice(0, 8).map((d, i) => {
      const values = Array(bins).fill(0);
      (byDev.get(d.name) || []).forEach(r => {
        const t = Date.parse(r.time);
        const idx = Math.min(bins - 1, Math.max(0, Math.floor((t - fromMs) / binMs)));
        if (importRows.length > 0) {
          values[idx] += Number(r.value) || 0;
        } else {
          values[idx] += (Math.abs(Number(r.value) || 0)) / 1000;
        }
      });
      // Bei Counter-Quellen: in Diffs umrechnen pro Bin
      if (importRows.length > 0) {
        let prev = values[0];
        for (let i = 1; i < bins; i++) {
          const cur = values[i] || prev;
          values[i] = Math.max(0, cur - prev);
          prev = cur;
        }
        values[0] = 0;
      }
      return { name: d.name, color: d.color, values };
    });

    return { perDevice: dev, totals: total, hourly: series };
  }, [meters.data, fromIso]);

  // Live-Werte fuer Donut-Verteilung
  const livePower = extractLivePower(state.data?.latestLiveValues);
  const liveLoads = livePower.filter(p => p.cat === "load" && p.power > 0);
  const liveTotal = liveLoads.reduce((s,p) => s + p.power, 0);

  const topDevice = perDevice[0];

  return (
    <div className="page col" style={{gap:14}}>
      <div className="row">
        <Seg items={[
          {value:"24h", label:"24 h"},
          {value:"7d",  label:"7 Tage"},
          {value:"30d", label:"30 Tage"},
        ]} value={range} onChange={setRange}/>
        <button className="btn ghost" onClick={meters.reload}><Icon name="refresh" size={14}/> Aktualisieren</button>
      </div>

      <div className="kpi-row">
        <KPI icon="bolt" color="var(--brand)"
          label={`Verbrauch ${range}`}
          value={fmt.num(totals, 1)} unit="kWh"
          foot={meters.data ? `${perDevice.length} Stromkreise` : "—"}/>
        <KPI icon="consumption" color="var(--c1)"
          label="Top-Kreis"
          value={topDevice ? fmt.num(topDevice.kwh, 1) : "—"}
          unit="kWh"
          foot={topDevice ? `${topDevice.name} · ${topDevice.pct.toFixed(0)}%` : "—"}/>
        <KPI icon="grid" color="var(--grid)"
          label="Aktive Kreise"
          value={liveLoads.length || "—"}
          foot="aus Live-Telemetrie"/>
        <KPI icon="alert" color="var(--charge)"
          label="Aktuelle Last"
          value={liveTotal > 0 ? liveTotal.toFixed(2) : "—"}
          unit="kW"
          foot={state.data?.lastLiveValuesUtc ? `seit ${fmt.dt(state.data.lastLiveValuesUtc)}` : "warten auf Live"}/>
      </div>

      <Card title="Verbrauch nach Stromkreis"
        sub={meters.loading ? "lädt…" : `${perDevice.length} Devices, ${meters.data?.length || 0} Datenpunkte`}>
        {meters.loading && <Loading/>}
        {meters.err && <ErrorBox msg={meters.err}/>}
        {meters.data && perDevice.length === 0 && (
          <Empty>
            Keine Verbrauchsdaten im Zeitraum. Sobald der Edge 15-min-Zählerwerte
            (telemetry/meters) sendet, erscheinen sie hier.
          </Empty>
        )}
        {perDevice.length > 0 && (
          <div>
            <StackedAreaChart series={hourly} height={280}/>
            <div className="legend" style={{marginTop:10, justifyContent:"center"}}>
              {hourly.map(s => (
                <span key={s.name} className="it">
                  <span className="sw" style={{background: s.color}}/>{s.name}
                </span>
              ))}
            </div>
          </div>
        )}
      </Card>

      <div style={{display:"grid", gridTemplateColumns:"1.4fr 1fr", gap:14}}>
        <Card title="Tagesverbrauch nach Stromkreis" sub="sortiert nach Energie" flush>
          {perDevice.length === 0 && <Empty>Keine Daten.</Empty>}
          {perDevice.length > 0 && (
            <table className="t">
              <thead><tr>
                <th>Kreis</th>
                <th style={{textAlign:"right"}}>{range}</th>
                <th>Anteil</th>
                <th>Samples</th>
              </tr></thead>
              <tbody>
                {perDevice.map(c => (
                  <tr key={c.name}>
                    <td>
                      <div className="h-stack">
                        <span style={{width:10, height:10, borderRadius:2, background:c.color}}/>
                        <span style={{fontWeight:500}}>{c.name}</span>
                      </div>
                    </td>
                    <td className="mono" style={{textAlign:"right"}}>{fmt.num(c.kwh, 2)} kWh</td>
                    <td>
                      <div style={{display:"grid", gridTemplateColumns:"40px 1fr", gap:8, alignItems:"center"}}>
                        <span className="mono" style={{fontSize:12}}>{c.pct.toFixed(0)}%</span>
                        <div style={{height:4, borderRadius:2, background:"var(--surface-3)", overflow:"hidden"}}>
                          <div style={{height:"100%", width:`${Math.min(100, c.pct * 2)}%`, background:c.color}}/>
                        </div>
                      </div>
                    </td>
                    <td className="muted">{c.samples}</td>
                  </tr>
                ))}
              </tbody>
            </table>
          )}
        </Card>

        <Card title="Verteilung jetzt" sub="aktueller Lastanteil aus Live-Telemetrie">
          {liveLoads.length === 0 && <Empty>Keine Live-Werte.</Empty>}
          {liveLoads.length > 0 && (
            <div>
              <DonutChart data={liveLoads.map((p, i) => ({
                value: Math.abs(p.power),
                color: CIRCUIT_COLORS[i % CIRCUIT_COLORS.length],
                label: p.device,
              }))} centerValue={liveTotal.toFixed(2)} centerSub="kW"/>
              <div style={{display:"flex", flexDirection:"column", gap:6, marginTop:14}}>
                {liveLoads.map((p, i) => (
                  <div key={i} className="h-stack" style={{justifyContent:"space-between"}}>
                    <span className="h-stack">
                      <span style={{width:8, height:8, borderRadius:2, background: CIRCUIT_COLORS[i % CIRCUIT_COLORS.length]}}/>
                      <span style={{fontSize:12.5}}>{p.device}</span>
                    </span>
                    <span className="mono" style={{fontSize:12.5, color:"var(--text-2)"}}>
                      {Math.abs(p.power) < 0.1 ? `${(p.power * 1000).toFixed(0)} W` : `${p.power.toFixed(2)} kW`}
                    </span>
                  </div>
                ))}
              </div>
            </div>
          )}
        </Card>
      </div>
    </div>
  );
}

/* ------------------------------------------------------------------ */
/* PRODUKTION — analog Verbrauch, aber filtert auf "Energy Export"    */
/* ------------------------------------------------------------------ */
function ProductionPage() {
  const [range, setRange] = useState("24h");
  const state = useApi(api.state, [], statePoll(10000));

  const fromIso = useMemo(() => {
    const now = Date.now();
    if (range === "24h") return new Date(now - 24*3600*1000).toISOString();
    if (range === "7d")  return new Date(now -  7*24*3600*1000).toISOString();
    if (range === "30d") return new Date(now - 30*24*3600*1000).toISOString();
    return new Date(now - 24*3600*1000).toISOString();
  }, [range]);

  const meters = useApi(() => api.meters(fromIso, null, null), [fromIso]);

  const { perDevice, totals, hourly } = useMemo(() => {
    const result = { perDevice: [], totals: 0, hourly: [] };
    if (!meters.data || meters.data.length === 0) return result;

    // Energy Export = Einspeisung/Erzeugung. Faengt auch "EnergyExport",
    // "Energy_Export", "Produktion" ab.
    const exportRows = meters.data.filter(m =>
      /^energy[\s_-]?export\b/i.test(m.valueName) || /produktion|production|erzeugung/i.test(m.valueName));
    const rows = exportRows.length > 0
      ? exportRows
      : meters.data.filter(m =>
          /^power$/i.test(m.valueName) &&
          ["pv","solar","inverter","wechselrichter"].some(s => (m.deviceName || "").toLowerCase().includes(s)));
    if (rows.length === 0) return result;

    const byDev = new Map();
    rows.forEach(r => {
      if (!byDev.has(r.deviceName)) byDev.set(r.deviceName, []);
      byDev.get(r.deviceName).push(r);
    });

    const fromMs = Date.parse(fromIso);
    const toMs = Date.now();
    const dur = Math.max(1, (toMs - fromMs) / 3600000);

    const dev = [];
    byDev.forEach((arr, name) => {
      arr.sort((a,b) => Date.parse(a.time) - Date.parse(b.time));
      let kwh = 0;
      if (exportRows.length > 0) {
        const first = arr[0]?.value ?? 0;
        const last  = arr[arr.length - 1]?.value ?? first;
        kwh = Math.max(0, last - first);
      } else {
        const sum = arr.reduce((s, r) => s + Math.abs(Number(r.value) || 0), 0);
        kwh = (sum / arr.length) * dur / 1000;
      }
      dev.push({ name, kwh, samples: arr.length });
    });

    dev.sort((a,b) => b.kwh - a.kwh);
    const total = dev.reduce((s,d) => s + d.kwh, 0);
    dev.forEach((d, i) => {
      d.pct = total > 0 ? (d.kwh / total) * 100 : 0;
      d.color = CIRCUIT_COLORS[i % CIRCUIT_COLORS.length];
    });

    const bins = 24;
    const binMs = (toMs - fromMs) / bins;
    const series = dev.slice(0, 8).map(d => {
      const values = Array(bins).fill(0);
      (byDev.get(d.name) || []).forEach(r => {
        const t = Date.parse(r.time);
        const idx = Math.min(bins - 1, Math.max(0, Math.floor((t - fromMs) / binMs)));
        if (exportRows.length > 0) values[idx] += Number(r.value) || 0;
        else values[idx] += Math.abs(Number(r.value) || 0) / 1000;
      });
      if (exportRows.length > 0) {
        let prev = values[0];
        for (let i = 1; i < bins; i++) {
          const cur = values[i] || prev;
          values[i] = Math.max(0, cur - prev);
          prev = cur;
        }
        values[0] = 0;
      }
      return { name: d.name, color: d.color, values };
    });

    return { perDevice: dev, totals: total, hourly: series };
  }, [meters.data, fromIso]);

  // Live PV-Power aus Live-Telemetrie
  const livePower = extractLivePower(state.data?.latestLiveValues);
  const livePv = livePower.filter(p => p.cat === "pv" && p.power > 0);
  const livePvTotal = livePv.reduce((s, p) => s + p.power, 0);

  const topDevice = perDevice[0];

  return (
    <div className="page col" style={{gap:14}}>
      <div className="row">
        <Seg items={[
          {value:"24h", label:"24 h"},
          {value:"7d",  label:"7 Tage"},
          {value:"30d", label:"30 Tage"},
        ]} value={range} onChange={setRange}/>
        <button className="btn ghost" onClick={meters.reload}><Icon name="refresh" size={14}/> Aktualisieren</button>
      </div>

      <div className="kpi-row">
        <KPI icon="sun" color="var(--pv)"
          label={`Erzeugung ${range}`}
          value={fmt.num(totals, 1)} unit="kWh"
          foot={meters.data ? `${perDevice.length} Quellen` : "—"}/>
        <KPI icon="bolt" color="var(--export)"
          label="Top-Quelle"
          value={topDevice ? fmt.num(topDevice.kwh, 1) : "—"}
          unit="kWh"
          foot={topDevice ? `${topDevice.name} · ${topDevice.pct.toFixed(0)}%` : "—"}/>
        <KPI icon="grid" color="var(--brand-2)"
          label="Live-Leistung"
          value={livePvTotal > 0 ? livePvTotal.toFixed(2) : "—"}
          unit="kW"
          foot={state.data?.lastLiveValuesUtc ? `seit ${fmt.dt(state.data.lastLiveValuesUtc)}` : "warten auf Live"}/>
        <KPI icon="check" color="var(--pv)"
          label="Aktive PV-Stränge"
          value={livePv.length || "—"}
          foot="aus Live-Telemetrie"/>
      </div>

      <Card title="Erzeugung nach Quelle"
        sub={meters.loading ? "lädt…" : `${perDevice.length} Devices, ${meters.data?.length || 0} Datenpunkte`}>
        {meters.loading && <Loading/>}
        {meters.err && <ErrorBox msg={meters.err}/>}
        {meters.data && perDevice.length === 0 && (
          <Empty>
            Keine Erzeugungsdaten im Zeitraum. Sobald PV-Inverter "Energy Export"-Werte
            senden, erscheinen sie hier.
          </Empty>
        )}
        {perDevice.length > 0 && (
          <div>
            <StackedAreaChart series={hourly} height={280}/>
            <div className="legend" style={{marginTop:10, justifyContent:"center"}}>
              {hourly.map(s => (
                <span key={s.name} className="it">
                  <span className="sw" style={{background: s.color}}/>{s.name}
                </span>
              ))}
            </div>
          </div>
        )}
      </Card>

      <div style={{display:"grid", gridTemplateColumns:"1.4fr 1fr", gap:14}}>
        <Card title="Erzeugung nach Quelle" sub="sortiert nach Energie" flush>
          {perDevice.length === 0 && <Empty>Keine Daten.</Empty>}
          {perDevice.length > 0 && (
            <table className="t">
              <thead><tr>
                <th>Quelle</th>
                <th style={{textAlign:"right"}}>{range}</th>
                <th>Anteil</th>
                <th>Samples</th>
              </tr></thead>
              <tbody>
                {perDevice.map(c => (
                  <tr key={c.name}>
                    <td>
                      <div className="h-stack">
                        <span style={{width:10, height:10, borderRadius:2, background:c.color}}/>
                        <span style={{fontWeight:500}}>{c.name}</span>
                      </div>
                    </td>
                    <td className="mono" style={{textAlign:"right"}}>{fmt.num(c.kwh, 2)} kWh</td>
                    <td>
                      <div style={{display:"grid", gridTemplateColumns:"40px 1fr", gap:8, alignItems:"center"}}>
                        <span className="mono" style={{fontSize:12}}>{c.pct.toFixed(0)}%</span>
                        <div style={{height:4, borderRadius:2, background:"var(--surface-3)", overflow:"hidden"}}>
                          <div style={{height:"100%", width:`${Math.min(100, c.pct * 2)}%`, background:c.color}}/>
                        </div>
                      </div>
                    </td>
                    <td className="muted">{c.samples}</td>
                  </tr>
                ))}
              </tbody>
            </table>
          )}
        </Card>

        <Card title="Live-Erzeugung" sub="aktuelle PV-Leistung aus Live-Telemetrie">
          {livePv.length === 0 && <Empty>Keine Live-PV-Werte.</Empty>}
          {livePv.length > 0 && (
            <div>
              <DonutChart data={livePv.map((p, i) => ({
                value: Math.abs(p.power),
                color: CIRCUIT_COLORS[i % CIRCUIT_COLORS.length],
                label: p.device,
              }))} centerValue={livePvTotal.toFixed(2)} centerSub="kW"/>
              <div style={{display:"flex", flexDirection:"column", gap:6, marginTop:14}}>
                {livePv.map((p, i) => (
                  <div key={i} className="h-stack" style={{justifyContent:"space-between"}}>
                    <span className="h-stack">
                      <span style={{width:8, height:8, borderRadius:2, background: CIRCUIT_COLORS[i % CIRCUIT_COLORS.length]}}/>
                      <span style={{fontSize:12.5}}>{p.device}</span>
                    </span>
                    <span className="mono" style={{fontSize:12.5, color:"var(--text-2)"}}>
                      {Math.abs(p.power) < 0.1 ? `${(p.power * 1000).toFixed(0)} W` : `${p.power.toFixed(2)} kW`}
                    </span>
                  </div>
                ))}
              </div>
            </div>
          )}
        </Card>
      </div>
    </div>
  );
}

/* ------------------------------------------------------------------ */
/* Charts                                                             */
/* ------------------------------------------------------------------ */
function StackedAreaChart({ series, height = 260, padTop = 18, padR = 14, padB = 24, padL = 36 }) {
  const w = 960;
  const n = series[0]?.values.length || 0;
  if (n === 0) return null;

  const totals = Array.from({length: n}, (_, i) => series.reduce((s, sv) => s + (sv.values[i] || 0), 0));
  const max = Math.max(...totals, 1);
  const yMax = Math.ceil(max / 5) * 5 || 1;
  const innerW = w - padL - padR;
  const innerH = height - padTop - padB;
  const x = i => padL + (i/(n-1)) * innerW;
  const y = v => padTop + innerH - (v/yMax) * innerH;

  // build stacked top/bot per series
  const acc = Array(n).fill(0);
  const stacks = series.map(s => {
    const top = [], bot = [];
    for (let i = 0; i < n; i++) {
      bot.push(acc[i]);
      acc[i] += s.values[i] || 0;
      top.push(acc[i]);
    }
    return { ...s, top, bot };
  });

  const path = (top, bot) => {
    const a = top.map((v, i) => `${x(i)},${y(v)}`).join(" L ");
    const b = bot.map((v, i) => `${x(i)},${y(v)}`).reverse().join(" L ");
    return `M ${a} L ${b} Z`;
  };

  const yTicks = 4;
  const tickVals = Array.from({length: yTicks + 1}, (_, i) => (yMax/yTicks) * i);

  return (
    <svg viewBox={`0 0 ${w} ${height}`} width="100%" preserveAspectRatio="none" style={{display:"block"}}>
      {tickVals.map((v, i) => (
        <g key={i}>
          <line x1={padL} x2={w - padR} y1={y(v)} y2={y(v)} className="grid-line"/>
          <text x={padL - 6} y={y(v) + 3} textAnchor="end" fontSize="10.5" fill="var(--text-3)"
            style={{fontVariantNumeric:"tabular-nums"}}>{v.toFixed(1)}</text>
        </g>
      ))}
      <text x={padL - 6} y={padTop - 4} textAnchor="end" fontSize="10" fill="var(--text-3)">kWh</text>

      {stacks.map((s, i) => (
        <path key={i} d={path(s.top, s.bot)} fill={s.color} fillOpacity={0.85}
          stroke={s.color} strokeWidth="0.5" strokeOpacity="0.6"/>
      ))}

      {Array.from({length: 8}, (_, k) => {
        const idx = Math.floor((n-1) * k/7);
        return (
          <text key={k} x={x(idx)} y={height - 6} textAnchor="middle" fontSize="10.5" fill="var(--text-3)"
            style={{fontVariantNumeric:"tabular-nums"}}>{Math.floor(idx*24/n).toString().padStart(2,"0")}h</text>
        );
      })}
    </svg>
  );
}

function DonutChart({ data, size = 180, thickness = 22, centerValue, centerSub }) {
  const total = data.reduce((s, d) => s + d.value, 0) || 1;
  const r = size/2;
  const ri = r - thickness;
  let a0 = -Math.PI/2;

  const arc = (start, end) => {
    const sx0 = r + Math.cos(start)*r, sy0 = r + Math.sin(start)*r;
    const sx1 = r + Math.cos(end)*r,   sy1 = r + Math.sin(end)*r;
    const ex1 = r + Math.cos(end)*ri,  ey1 = r + Math.sin(end)*ri;
    const ex0 = r + Math.cos(start)*ri,ey0 = r + Math.sin(start)*ri;
    const large = (end - start) > Math.PI ? 1 : 0;
    return `M ${sx0} ${sy0} A ${r} ${r} 0 ${large} 1 ${sx1} ${sy1}
            L ${ex1} ${ey1} A ${ri} ${ri} 0 ${large} 0 ${ex0} ${ey0} Z`;
  };

  return (
    <div style={{display:"flex", justifyContent:"center"}}>
      <svg viewBox={`0 0 ${size} ${size}`} width={size} height={size}>
        {data.map((d, i) => {
          const slice = (d.value / total) * 2 * Math.PI;
          const a1 = a0 + slice;
          const p = arc(a0, a1);
          a0 = a1;
          return <path key={i} d={p} fill={d.color}/>;
        })}
        <text x={r} y={r - 2} textAnchor="middle" fontSize="22" fontWeight="600"
          style={{fontVariantNumeric:"tabular-nums"}} fill="var(--text)">{centerValue}</text>
        {centerSub && (
          <text x={r} y={r + 18} textAnchor="middle" fontSize="11" fill="var(--text-3)">{centerSub}</text>
        )}
      </svg>
    </div>
  );
}

/* ------------------------------------------------------------------ */
/* MetersListPage (used for Produktion/Netz fallback tables)          */
/* ------------------------------------------------------------------ */
function MetersListPage({ filter, title }) {
  const [range, setRange] = useState("24h");
  const fromIso = useMemo(() => {
    const now = Date.now();
    if (range === "24h") return new Date(now - 24*3600*1000).toISOString();
    if (range === "7d")  return new Date(now -  7*24*3600*1000).toISOString();
    if (range === "30d") return new Date(now - 30*24*3600*1000).toISOString();
    return new Date(now - 24*3600*1000).toISOString();
  }, [range]);

  const meters = useApi(() => api.meters(fromIso, null, filter || null), [fromIso, filter], 0);

  return (
    <div className="page col">
      <div className="row">
        <Seg items={[
          {value:"24h", label:"24 h"},
          {value:"7d",  label:"7 Tage"},
          {value:"30d", label:"30 Tage"},
        ]} value={range} onChange={setRange}/>
        <button className="btn ghost" onClick={meters.reload}><Icon name="refresh" size={14}/> Aktualisieren</button>
      </div>

      <Card title={title} flush>
        {meters.loading && <Loading/>}
        {meters.err && <div style={{padding:16}}><ErrorBox msg={meters.err}/></div>}
        {meters.data && meters.data.length === 0 && <Empty>Keine Datenpunkte im gewählten Zeitraum.</Empty>}
        {meters.data && meters.data.length > 0 && (
          <table className="t">
            <thead><tr>
              <th>Zeit</th><th>Gerät</th><th>Messwert</th><th style={{textAlign:"right"}}>Wert</th>
            </tr></thead>
            <tbody>
              {meters.data.slice(0, 500).map((m, i) => (
                <tr key={i}>
                  <td className="mono">{fmt.dt(m.time)}</td>
                  <td>{m.deviceName}</td>
                  <td className="muted">{m.valueName}</td>
                  <td className="mono" style={{textAlign:"right"}}>{fmt.num(m.value, 3)}</td>
                </tr>
              ))}
            </tbody>
          </table>
        )}
        {meters.data && meters.data.length > 500 && (
          <div className="muted" style={{padding:"10px 18px", fontSize:12}}>
            Anzeige auf 500 Zeilen begrenzt (insgesamt {meters.data.length}).
          </div>
        )}
      </Card>
    </div>
  );
}

/* ------------------------------------------------------------------ */
/* LADEINFRASTRUKTUR                                                  */
/* ------------------------------------------------------------------ */
function ChargingPage() {
  const sessions = useApi(() => api.sessions(new Date(Date.now() - 30*24*3600*1000).toISOString()), [], 0);

  let total = 0;
  let count = (sessions.data || []).length;
  (sessions.data || []).forEach(s => total += s.energyDeliveredKwh || 0);

  return (
    <div className="page col">
      <div className="kpi-row">
        <KPI icon="plug" color="var(--charge)" label="Sessions (30 Tage)" value={count}/>
        <KPI icon="bolt" color="var(--charge)" label="Geladene Energie" value={fmt.num(total, 1)} unit="kWh"/>
        <KPI icon="history" color="var(--brand-2)" label="Letzte Session"
          value={count > 0 ? fmt.dt(sessions.data[0].startTime) : "—"}/>
        <KPI icon="check" color="var(--pv)" label="Ø pro Session"
          value={count > 0 ? fmt.num(total / count, 1) : "0"} unit="kWh"/>
      </div>

      <Card title="Sessions" flush>
        {sessions.loading && <Loading/>}
        {sessions.err && <div style={{padding:16}}><ErrorBox msg={sessions.err}/></div>}
        {sessions.data && sessions.data.length === 0 && <Empty>Keine Ladevorgänge in den letzten 30 Tagen.</Empty>}
        {sessions.data && sessions.data.length > 0 && (
          <table className="t">
            <thead><tr>
              <th>Wallbox</th><th>Start</th><th>Ende</th><th style={{textAlign:"right"}}>Energie</th>
              <th>RFID</th><th>User</th>
            </tr></thead>
            <tbody>
              {sessions.data.map((s, i) => (
                <tr key={i}>
                  <td>{s.chargerName || <span className="muted">—</span>}</td>
                  <td className="mono">{fmt.dt(s.startTime)}</td>
                  <td className="mono">{fmt.dt(s.endTime)}</td>
                  <td className="mono" style={{textAlign:"right"}}>{fmt.num(s.energyDeliveredKwh, 2)} kWh</td>
                  <td className="muted">{s.rfidTag || "—"}</td>
                  <td className="muted">{s.userName || "—"}</td>
                </tr>
              ))}
            </tbody>
          </table>
        )}
      </Card>
    </div>
  );
}

/* ------------------------------------------------------------------ */
/* MELDUNGEN                                                          */
/* ------------------------------------------------------------------ */
function AlertsPage() {
  const [range, setRange] = useState("24h");
  const from = useMemo(() => {
    const now = Date.now();
    if (range === "24h") return new Date(now - 24*3600*1000).toISOString();
    if (range === "7d")  return new Date(now -  7*24*3600*1000).toISOString();
    return new Date(now - 30*24*3600*1000).toISOString();
  }, [range]);

  const alarms = useApi(() => api.alarms(from, null), [from]);

  return (
    <div className="page col">
      <div className="row">
        <Seg items={[
          {value:"24h", label:"24 h"},
          {value:"7d",  label:"7 Tage"},
          {value:"30d", label:"30 Tage"},
        ]} value={range} onChange={setRange}/>
        <button className="btn ghost" onClick={alarms.reload}><Icon name="refresh" size={14}/> Aktualisieren</button>
      </div>

      <Card title="Meldungen" sub={`${(alarms.data || []).length} Einträge`} flush>
        {alarms.loading && <Loading/>}
        {alarms.err && <div style={{padding:16}}><ErrorBox msg={alarms.err}/></div>}
        {alarms.data && alarms.data.length === 0 && <Empty>Keine Meldungen.</Empty>}
        {alarms.data && alarms.data.map((a, i) => (
          <div className="alert-item" key={i}>
            <span className="alert-dot" style={{background: a.type === 1 ? "var(--danger)" : "var(--charge)"}}/>
            <div>
              <div style={{fontWeight:500, fontSize:13.5}}>{a.message}</div>
              <div className="muted" style={{fontSize:11.5, marginTop:2}}>
                {a.deviceName}{a.plc ? ` · ${a.plc}` : ""} · {a.group} · {fmt.dt(a.time)}
              </div>
            </div>
            <div style={{textAlign:"right"}}>
              <div className="mono" style={{fontSize:13}}>{fmt.num(a.value, 2)} {a.unit}</div>
              <Badge kind={a.type === 1 ? "bad" : "warn"}>
                {a.type === 1 ? "Alarm" : "Warnung"}
              </Badge>
            </div>
          </div>
        ))}
      </Card>
    </div>
  );
}

/* ------------------------------------------------------------------ */
/* VERLAUF & BERICHTE                                                 */
/* ------------------------------------------------------------------ */
function HistoryPage() {
  const today = new Date();
  const yesterday = new Date(today.getTime() - 24*3600*1000);
  const [from, setFrom] = useState(yesterday.toISOString().slice(0, 16));
  const [to,   setTo]   = useState(today.toISOString().slice(0, 16));
  const [vn,   setVn]   = useState("");

  const meters = useApi(
    () => api.meters(new Date(from).toISOString(), new Date(to).toISOString(), vn || null),
    [from, to, vn]
  );

  return (
    <div className="page col">
      <Card title="Filter">
        <div className="row" style={{alignItems:"end", gap:14}}>
          <div className="v-stack">
            <label className="muted" style={{fontSize:11}}>Von</label>
            <input type="datetime-local" value={from} onChange={e => setFrom(e.target.value)}/>
          </div>
          <div className="v-stack">
            <label className="muted" style={{fontSize:11}}>Bis</label>
            <input type="datetime-local" value={to} onChange={e => setTo(e.target.value)}/>
          </div>
          <div className="v-stack grow">
            <label className="muted" style={{fontSize:11}}>ValueName (leer = alle)</label>
            <input type="text" placeholder="z.B. EnergyImport_kWh" value={vn} onChange={e => setVn(e.target.value)}/>
          </div>
          <button className="btn" onClick={meters.reload}>Aktualisieren</button>
        </div>
      </Card>

      <Card title="Meter-Werte" sub={`${(meters.data || []).length} Punkte`} flush>
        {meters.loading && <Loading/>}
        {meters.err && <div style={{padding:16}}><ErrorBox msg={meters.err}/></div>}
        {meters.data && meters.data.length === 0 && <Empty>Keine Datenpunkte im gewählten Zeitraum.</Empty>}
        {meters.data && meters.data.length > 0 && (
          <table className="t">
            <thead><tr>
              <th>Zeit</th><th>Gerät</th><th>Messwert</th><th style={{textAlign:"right"}}>Wert</th>
            </tr></thead>
            <tbody>
              {meters.data.slice(0, 1000).map((m, i) => (
                <tr key={i}>
                  <td className="mono">{fmt.dt(m.time)}</td>
                  <td>{m.deviceName}</td>
                  <td className="muted">{m.valueName}</td>
                  <td className="mono" style={{textAlign:"right"}}>{fmt.num(m.value, 3)}</td>
                </tr>
              ))}
            </tbody>
          </table>
        )}
      </Card>
    </div>
  );
}

/* ------------------------------------------------------------------ */
/* EINSTELLUNGEN                                                      */
/* ------------------------------------------------------------------ */
function SettingsPage({ theme, setTheme }) {
  const devices = useApi(api.devices, []);

  const [hubText, setHubText] = useState("{\n  \"nodes\": []\n}");
  const [selectedDevice, setSelectedDevice] = useState(null);
  const [savingHub, setSavingHub] = useState(false);
  const [hubMsg, setHubMsg] = useState(null);

  // Live mode toggles
  const [liveActive, setLiveActive] = useState(false);
  const [liveSec, setLiveSec] = useState(15);
  const [meterMin, setMeterMin] = useState(15);
  const [liveSaving, setLiveSaving] = useState(false);
  const [liveMsg, setLiveMsg] = useState(null);

  // Claim form
  const [claimToken, setClaimToken] = useState("");
  const [claimMsg, setClaimMsg] = useState(null);

  useEffect(() => {
    if (devices.data && devices.data.length > 0 && !selectedDevice) {
      setSelectedDevice(devices.data[0].id);
    }
  }, [devices.data, selectedDevice]);

  async function saveHub() {
    setSavingHub(true); setHubMsg(null);
    try {
      const cfg = JSON.parse(hubText);
      const result = await api.setHubConfig(selectedDevice, cfg);
      setHubMsg({ ok: true, text: `Übernommen — version: ${result.status || "ok"}` });
    } catch (e) {
      setHubMsg({ ok: false, text: e.message });
    } finally {
      setSavingHub(false);
    }
  }

  async function applyLive() {
    setLiveSaving(true); setLiveMsg(null);
    try {
      const r = await api.setLiveMode({
        active: liveActive,
        intervalSeconds: parseInt(liveSec, 10),
        meterIntervalMinutes: parseInt(meterMin, 10),
      });
      setLiveMsg({ ok:true, text:`Übernommen: live=${r.payload.liveActive}, ${r.payload.liveIntervalSeconds}s` });
    } catch (e) {
      setLiveMsg({ ok:false, text: e.message });
    } finally {
      setLiveSaving(false);
    }
  }

  async function doClaim() {
    setClaimMsg(null);
    try {
      const r = await api.claimDevice(claimToken);
      setClaimMsg({ ok:true, text:`Erfolgreich — deviceId: ${r.deviceId}. Lade Anlage…` });
      setClaimToken("");
      // Neues Geraet = neue Site → Seite neu laden, damit /api/sites sie aufnimmt.
      setTimeout(() => window.location.reload(), 900);
    } catch (e) {
      setClaimMsg({ ok:false, text: e.message });
    }
  }

  return (
    <div className="page col">
      <Card title="Darstellung">
        <div className="setting-row">
          <div>
            <div className="t">Theme</div>
            <div className="d">Hell oder dunkel</div>
          </div>
          <Seg items={[{value:"light", label:"Hell"}, {value:"dark", label:"Dunkel"}]} value={theme} onChange={setTheme}/>
        </div>
      </Card>

      <Card title="Neues Gerät claimen" sub="Wert aus dem QR-Code vom Edge-Device">
        <div className="row" style={{alignItems:"end"}}>
          <div className="v-stack grow">
            <label className="muted" style={{fontSize:11}}>Claim-Token</label>
            <input type="text" value={claimToken} onChange={e => setClaimToken(e.target.value)}
              placeholder="Token aus QR-Code…"/>
          </div>
          <button className="btn" onClick={doClaim} disabled={!claimToken}>Gerät verknüpfen</button>
        </div>
        {claimMsg && (
          <div style={{marginTop:12}} className={claimMsg.ok ? "" : "error-box"}>
            {claimMsg.ok ? <Badge kind="ok">{claimMsg.text}</Badge> : claimMsg.text}
          </div>
        )}
      </Card>

      <Card title="Meine Geräte" flush>
        {devices.loading && <Loading/>}
        {devices.err && <ErrorBox msg={devices.err}/>}
        {devices.data && devices.data.length === 0 && <Empty>Noch keine Geräte verknüpft. Nutze die Claim-Funktion oben.</Empty>}
        {devices.data && devices.data.length > 0 && (
          <table className="t">
            <thead><tr>
              <th>Device-ID</th><th>MQTT-Client</th><th>Claimed</th><th>Last seen</th><th>Status</th>
            </tr></thead>
            <tbody>
              {devices.data.map(d => (
                <tr key={d.id} onClick={() => setSelectedDevice(d.id)}
                  style={{cursor:"pointer", background: d.id === selectedDevice ? "var(--surface-2)" : ""}}>
                  <td className="mono" style={{fontSize:11.5}}>{d.id}</td>
                  <td className="mono">{d.mqttClientId}</td>
                  <td className="muted">{fmt.dt(d.claimedAt)}</td>
                  <td className="muted">{fmt.dt(d.lastSeenAt)}</td>
                  <td>{d.lastSeenConnected ? <Badge kind="ok">online</Badge> : <Badge kind="bad">offline</Badge>}</td>
                </tr>
              ))}
            </tbody>
          </table>
        )}
      </Card>

      <Card title="HubConfig pushen" sub={selectedDevice ? `Device ${selectedDevice}` : "Kein Device gewählt"}>
        <div className="v-stack">
          <textarea
            value={hubText}
            onChange={e => setHubText(e.target.value)}
            spellCheck={false}
            rows={14}
            style={{width:"100%", fontFamily:"'Geist Mono', monospace", fontSize:12.5}}/>
          <div className="row" style={{justifyContent:"flex-end", gap:10}}>
            {hubMsg && (
              <span className={hubMsg.ok ? "" : ""} style={{color: hubMsg.ok ? "var(--pv)" : "var(--danger)"}}>
                {hubMsg.text}
              </span>
            )}
            <button className="btn" onClick={saveHub} disabled={!selectedDevice || savingHub}>
              {savingHub && <span className="loading"/>}
              Übernehmen + retained publishen
            </button>
          </div>
        </div>
      </Card>

      <Card title="Live-Mode-Steuerung" sub="setzt retained config/telemetry am Broker">
        <div className="row" style={{alignItems:"end", gap:14}}>
          <div className="v-stack">
            <label className="muted" style={{fontSize:11}}>Live aktiv</label>
            <div className={`switch ${liveActive ? "on" : ""}`} onClick={() => setLiveActive(!liveActive)} role="switch" aria-checked={liveActive}/>
          </div>
          <div className="v-stack">
            <label className="muted" style={{fontSize:11}}>Live-Intervall (s)</label>
            <input type="number" min="5" max="60" value={liveSec} onChange={e => setLiveSec(e.target.value)} style={{width:90}}/>
          </div>
          <div className="v-stack">
            <label className="muted" style={{fontSize:11}}>Meter-Intervall (min)</label>
            <input type="number" min="1" max="60" value={meterMin} onChange={e => setMeterMin(e.target.value)} style={{width:90}}/>
          </div>
          <button className="btn" onClick={applyLive} disabled={liveSaving}>
            {liveSaving && <span className="loading"/>}
            Übernehmen
          </button>
        </div>
        {liveMsg && (
          <div style={{marginTop:12, color: liveMsg.ok ? "var(--pv)" : "var(--danger)"}}>{liveMsg.text}</div>
        )}
      </Card>
    </div>
  );
}

/* ------------------------------------------------------------------ */
/* App shell                                                          */
/* ------------------------------------------------------------------ */
const NAV = [
  { id:"overview",   label:"Übersicht",          icon:"home" },
  { id:"flow",       label:"Energiefluss",       icon:"flow" },
  { id:"verbrauch",  label:"Verbrauch",          icon:"consumption" },
  { id:"produktion", label:"Produktion",         icon:"sun" },
  { id:"laden",      label:"Ladeinfrastruktur",  icon:"plug" },
  { id:"netz",       label:"Netz & Last",        icon:"grid" },
  { id:"verlauf",    label:"Verlauf & Berichte", icon:"history" },
  { id:"meldungen",  label:"Meldungen",          icon:"alert" },
  { id:"settings",   label:"Einstellungen",      icon:"settings" },
];

const PAGE_TITLE = {
  overview:   ["Übersicht",             "Live-Status & Geräte"],
  flow:       ["Energiefluss",          "State-Snapshots des Edge"],
  verbrauch:  ["Verbrauch",             "Power-Werte"],
  produktion: ["Produktion",            "Export-/Erzeugungs-Werte"],
  laden:      ["Ladeinfrastruktur",     "EV-Charging-Sessions"],
  netz:       ["Netz & Lastmanagement", "Grid-Meter-Werte"],
  verlauf:    ["Verlauf & Berichte",    "Historie · Auswertung · Export"],
  meldungen:  ["Meldungen",             "Warnungen & Alarme"],
  settings:   ["Einstellungen",         "Geräte · HubConfig · Live-Mode"],
};

function App() {
  const [page, setPage] = useState(() => window.location.hash.replace("#", "") || "overview");
  const [theme, setTheme] = useState(() => localStorage.getItem("lydtrix.theme") || "light");
  const now = useNow(1000);

  useEffect(() => {
    document.body.dataset.theme = theme;
    localStorage.setItem("lydtrix.theme", theme);
  }, [theme]);

  useEffect(() => {
    const sync = () => {
      const h = window.location.hash.replace("#", "");
      if (PAGE_TITLE[h]) setPage(h);
    };
    sync();
    window.addEventListener("hashchange", sync);
    return () => window.removeEventListener("hashchange", sync);
  }, []);

  const nav = (id) => { setPage(id); window.location.hash = id; };
  const [title, sub] = PAGE_TITLE[page] || PAGE_TITLE.overview;

  let view = null;
  switch (page) {
    case "overview":   view = <OverviewPage/>; break;
    case "flow":       view = <EnergyFlowPage/>; break;
    case "verbrauch":  view = <ConsumptionPage/>; break;
    case "produktion": view = <ProductionPage/>; break;
    case "laden":      view = <ChargingPage/>; break;
    case "netz":       view = <MetersListPage filter={null} title="Alle Meter-Werte"/>; break;
    case "verlauf":    view = <HistoryPage/>; break;
    case "meldungen":  view = <AlertsPage/>; break;
    case "settings":   view = <SettingsPage theme={theme} setTheme={setTheme}/>; break;
    default:           view = <OverviewPage/>;
  }

  const user = auth.user || {};
  const userInitials =
    user.preferred_username
      ? user.preferred_username.substring(0, 2).toUpperCase()
      : (user.given_name && user.family_name)
        ? (user.given_name[0] + user.family_name[0]).toUpperCase()
        : "U";

  return (
    <div className="app">
      <aside className="sidebar">
        <div className="brand">
          <div className="logo">L</div>
          LYDTRIX
        </div>
        <div className="nav-group-label">Dashboard</div>
        {NAV.slice(0, 6).map(n => (
          <div key={n.id} className={`nav-item ${page === n.id ? "active" : ""}`} onClick={() => nav(n.id)}>
            <Icon name={n.icon} size={17} stroke={1.7}/>
            <span>{n.label}</span>
          </div>
        ))}
        <div className="nav-group-label">Auswertungen</div>
        {NAV.slice(6, 8).map(n => (
          <div key={n.id} className={`nav-item ${page === n.id ? "active" : ""}`} onClick={() => nav(n.id)}>
            <Icon name={n.icon} size={17} stroke={1.7}/>
            <span>{n.label}</span>
          </div>
        ))}
        <div className="nav-group-label">System</div>
        {NAV.slice(8).map(n => (
          <div key={n.id} className={`nav-item ${page === n.id ? "active" : ""}`} onClick={() => nav(n.id)}>
            <Icon name={n.icon} size={17} stroke={1.7}/>
            <span>{n.label}</span>
          </div>
        ))}
        <div className="sidebar-foot">
          <div className="avatar">{userInitials}</div>
          <div style={{minWidth:0}}>
            <div style={{fontWeight:600, color:"var(--text)", fontSize:13}}>
              {user.preferred_username || user.email || "User"}
            </div>
            <div style={{color:"var(--text-3)", fontSize:11.5}}>angemeldet</div>
          </div>
          <button className="icon-btn" style={{width:30, height:30, marginLeft:"auto"}}
            title="Abmelden" onClick={() => auth.logout()}>
            <Icon name="logout" size={14}/>
          </button>
        </div>
      </aside>

      <div className="main">
        <header className="topbar">
          <div>
            <div className="crumb">LYDTRIX · Energiemanagement</div>
            <h1>{title} <span style={{color:"var(--text-3)", fontWeight:400, fontSize:13, marginLeft:6}}>{sub}</span></h1>
          </div>
          <div className="topbar-right">
            {(window.__lydtrixSites || []).length > 1 && (
              <select
                value={window.__lydtrixSiteId}
                onChange={e => { localStorage.setItem("lydtrix.siteId", e.target.value); window.location.reload(); }}
                style={{padding:"6px 10px", borderRadius:8, border:"1px solid var(--border-2)", background:"var(--surface)", color:"var(--text)", fontSize:13}}
                title="Anlage wählen">
                {window.__lydtrixSites.map(s => <option key={s.siteId} value={s.siteId}>{s.name}</option>)}
              </select>
            )}
            <span className="chip"><span className="dot"/><span>Live · {fmt.time(now)}</span></span>
            <span className="chip" style={{background:"transparent"}}><Icon name="calendar" size={13}/> {fmt.date(now)}</span>
            <button className="icon-btn" title="Theme wechseln" onClick={() => setTheme(theme === "dark" ? "light" : "dark")}>
              <Icon name={theme === "dark" ? "sun" : "moon"} size={16}/>
            </button>
          </div>
        </header>
        <main className="content">{view}</main>
      </div>
    </div>
  );
}

ReactDOM.createRoot(document.getElementById("root")).render(<App/>);

}
