// Field / list components used by the questionnaire
const { useState, useEffect, useRef, useMemo } = React;

// --- Basic inputs ---
function Label({ field, id, error }) {
 return (
 <label htmlFor={id} className="field-label">
 <span className="field-label-main">
 {field.label}
 {field.required && <span className="req-star" aria-label="Pflichtfeld">*</span>}
 </span>
 {field.hint && <span className="field-hint">{field.hint}</span>}
 {error && <span className="field-error">{error}</span>}
 </label>
 );
}

function TextInput({ field, value, onChange, error }) {
 const id = "f_" + field.id;
 const [touched, setTouched] = useState(false);
 const formatError = (touched && field.validate)
 ? window.validateField(field, value)
 : null;
 const showError = error || formatError;
 return (
 <div className={`field ${showError ? "has-error" : ""}`}>
 <Label field={field} id={id} error={showError} />
 <input
 id={id}
 type={field.type === "email" ? "email" : field.type === "date" ? "date" : "text"}
 className="input"
 value={value || ""}
 placeholder={field.placeholder || ""}
 onChange={(e) => onChange(e.target.value)}
 onBlur={() => setTouched(true)}
 inputMode={field.validate === "phone" ? "tel" : field.validate === "email" ? "email" : undefined}
 autoComplete={field.validate === "email" ? "email" : field.validate === "phone" ? "tel" : undefined}
 />
 </div>
 );
}

function TextareaInput({ field, value, onChange, error }) {
 const id = "f_" + field.id;
 return (
 <div className={`field ${error ? "has-error" : ""}`}>
 <Label field={field} id={id} error={error} />
 <textarea
 id={id}
 className="input textarea"
 rows={field.rows || 3}
 value={value || ""}
 placeholder={field.placeholder || ""}
 onChange={(e) => onChange(e.target.value)}
 />
 </div>
 );
}

function SelectInput({ field, value, onChange, error }) {
 const id = "f_" + field.id;
 return (
 <div className={`field ${error ? "has-error" : ""}`}>
 <Label field={field} id={id} error={error} />
 <select id={id} className="input select" value={value || ""} onChange={(e) => onChange(e.target.value)}>
 <option value="">Bitte wählen</option>
 {field.options.map((o) => <option key={o} value={o}>{o}</option>)}
 </select>
 </div>
 );
}

function RadioInput({ field, value, onChange, error }) {
 return (
 <div className={`field ${error ? "has-error" : ""}`}>
 <Label field={field} error={error} />
 <div className="radio-group">
 {field.options.map((o) => (
 <label key={o} className={`radio-card ${value === o ? "is-active" : ""}`}>
 <input type="radio" name={field.id} value={o} checked={value === o} onChange={() => onChange(o)} />
 <span className="radio-dot" aria-hidden="true" />
 <span className="radio-text">{o}</span>
 </label>
 ))}
 </div>
 </div>
 );
}

function YesNoInput({ field, value, onChange, error }) {
 return (
 <div className={`field ${error ? "has-error" : ""}`}>
 <Label field={field} error={error} />
 <div className="yesno-group">
 {["Ja", "Nein"].map((o) => (
 <button
 key={o}
 type="button"
 className={`yesno-btn ${value === o ? "is-active" : ""} ${o === "Ja" ? "yes" : "no"}`}
 onClick={() => onChange(o)}
 >
 {o}
 </button>
 ))}
 </div>
 </div>
 );
}

function Field({ field, data, onChange, errors, state, wizard, sectionId, setUnsure, isUnsure }) {
 if (field.showIf && !field.showIf(data)) return null;

  // Hide advanced fields in Wizard-Modus (unless they already have a value).
 if (wizard && window.EASY_MODE && window.EASY_MODE.isFieldAdvanced(sectionId, field.id) && !data[field.id]) {
 return null;
 }

 const value = data[field.id];
 const set = (v) => onChange(field.id, v);
 const unsure = isUnsure ? isUnsure(field.id) : false;
  // "Weiß nicht" gilt als beantwortet: dann keine Pflichtfeld-Meldung mehr zeigen
 const error = unsure ? null : errors[field.id];

 let inner;
 switch (field.type) {
 case "textarea": inner = <TextareaInput field={field} value={value} onChange={set} error={error} />; break;
 case "select": inner = <SelectInput field={field} value={value} onChange={set} error={error} />; break;
 case "radio": inner = <RadioInput field={field} value={value} onChange={set} error={error} />; break;
 case "yesno": inner = <YesNoInput field={field} value={value} onChange={set} error={error} />; break;
 case "info": return <InfoField field={field} />;
 case "entity": inner = <EntityPicker field={field} value={value} onChange={set} error={error} state={state} />; break;
 case "entity-multi": inner = <EntityPicker field={field} value={value} onChange={set} error={error} state={state} multi />; break;
 case "clip-map": inner = <ClipMap field={field} value={value} onChange={set} error={error} state={state} />; break;
 case "shortcut-map": inner = <ShortcutMap field={field} value={value} onChange={set} error={error} state={state} />; break;
 case "ext-shortcut-map": inner = <ExtShortcutMap field={field} value={value} onChange={set} error={error} />; break;
 case "dept-hours": inner = <DeptHoursEditor field={field} value={value} onChange={set} error={error} state={state} />; break;
 default: inner = <TextInput field={field} value={value} onChange={set} error={error} />;
 }

  // Wrap with "Weiß nicht / X-sec entscheidet"-affordance when allowed.
  // Skip for kontakt-section required text fields and the notruf field.
 const skipUnsure = field.id === "firma"|| field.id === "ansprechpartner"|| field.id === "kontakt_email"|| field.id === "notruf_adresse"|| field.type === "info";
 if (skipUnsure || !setUnsure) return inner;

 return (
 <div className={`field-wrap ${unsure ? "is-unsure" : ""}`}>
 {inner}
 <div className="field-unsure-row">
 {unsure ? (
 <span className="unsure-pill">
 <span className="unsure-pill-icon" aria-hidden="true"><XsecIcon name="check" size={14} /></span>
 X-sec entscheidet das für Sie
 <button type="button" className="unsure-pill-undo" onClick={() => setUnsure(field.id, false)}>doch selbst angeben</button>
 </span>
 ) : (
 <button type="button" className="unsure-btn" onClick={() => { set(""); setUnsure(field.id, true); }} title="Wir kümmern uns darum. Sie müssen das nicht entscheiden.">
 <span aria-hidden="true"><XsecIcon name="circle-help" size={14} /></span> Weiß nicht, X-sec entscheidet
 </button>
 )}
 </div>
 </div>
 );
}

function InfoField({ field }) {
 return (
 <div className="info-callout">
 <span className="info-callout-icon" aria-hidden="true"><XsecIcon name="info" /></span>
 <span className="info-callout-text">{field.text}</span>
 </div>
 );
}

// --- Entity picker: pick from previously-entered users/departments/queues/etc.
// Field can declare:
//   entityTypes: ["users","departments","queues","voicemail","ivr","special"]
//   allowCustom: true   -> show a text input at the bottom for free-form fallback
function buildEntityOptions(state, types) {
 const groups = [];
 const safeTypes = types || ["users", "departments"];
 if (safeTypes.includes("users")) {
 const users = (state.users || []).filter(u => (u.name || "").trim());
 if (users.length) groups.push({
 label: "Nutzer",
 items: users.map(u => ({ value: "user:" + u.name, display: u.name + (u.durchwahl ? " (" + u.durchwahl + ")" : "") }))
 });
 }
 if (safeTypes.includes("departments")) {
 const depts = (state.departments || []).filter(d => (d.name || "").trim());
 if (depts.length) groups.push({
 label: "Abteilungen",
 items: depts.map(d => ({ value: "dept:" + d.name, display: d.name }))
 });
 }
 if (safeTypes.includes("queues")) {
 const wsText = (state.data && state.data.ws_abteilungen) || "";
 const queueNames = new Set();
 wsText.split(/[,;\n]/).map(s => s.trim()).filter(Boolean).forEach(n => queueNames.add(n));
    // Also suggest a queue per department
 (state.departments || []).forEach(d => { if (d.name) queueNames.add(d.name); });
 const items = Array.from(queueNames).map(n => ({ value: "queue:" + n, display: "Warteschlange " + n }));
 if (items.length) groups.push({ label: "Warteschlangen", items });
 }
 if (safeTypes.includes("ivr")) {
 groups.push({ label: "Sprachmenü", items: [{ value:"ivr:main", display:"IVR / Sprachmenü Hauptnummer" }] });
 }
 if (safeTypes.includes("voicemail")) {
 groups.push({ label: "Voicemail", items: [
 { value: "vm:main", display:"Voicemail Hauptnummer" },
 { value: "vm:personal", display:"Persönliche Voicemail" }
 ]});
 }
 if (safeTypes.includes("special")) {
 groups.push({ label: "Sonstige", items: [
 { value: "special:hangup", display:"Auflegen" },
 { value: "special:ext-input", display:"Durchwahl eingeben (DDI)" },
 { value: "special:external", display:"Externe Rufnummer (Freitext)" }
 ]});
 }
 return groups;
}

function displayForValue(val, groups) {
 for (const g of groups) {
 for (const it of g.items) if (it.value === val) return it.display;
 }
  // Free-form fallback stored as "free:..."
 if (val && val.startsWith("free:")) return val.slice(5);
 return val || "";
}

function EntityPicker({ field, value, onChange, error, state, multi }) {
 const id = "f_" + field.id;
 const [open, setOpen] = useState(false);
 const [customText, setCustomText] = useState("");
 const ref = useRef(null);
 useEffect(() => {
 const h = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
 document.addEventListener("mousedown", h);
 return () => document.removeEventListener("mousedown", h);
 }, []);
 const groups = useMemo(() => buildEntityOptions(state || {}, field.entityTypes), [state, field.entityTypes]);
 const flatCount = groups.reduce((a, g) => a + g.items.length, 0);
 const selected = multi ? (Array.isArray(value) ? value : []) : (value ? [value] : []);

 const toggle = (v) => {
 if (multi) {
 if (selected.includes(v)) onChange(selected.filter(x => x !== v));
 else onChange([...selected, v]);
 } else {
 onChange(v);
 setOpen(false);
 }
 };
 const remove = (v) => {
 if (multi) onChange(selected.filter(x => x !== v));
 else onChange(null);
 };
 const addCustom = () => {
 const t = customText.trim();
 if (!t) return;
 const v = "free:" + t;
 if (multi) onChange([...selected, v]);
 else { onChange(v); setOpen(false); }
 setCustomText("");
 };

 return (
 <div className={`field ${error ? "has-error" : ""}`}>
 <Label field={field} id={id} error={error} />
 <div className="entity-picker" ref={ref}>
 <div
 role="button"
 tabIndex={0}
 className="entity-trigger"
 onClick={() => setOpen(v => !v)}
 onKeyDown={(e) => { if (e.key === "Enter" || e.key === " ") { e.preventDefault(); setOpen(v => !v); } }}
 id={id}
 >
 {selected.length === 0
 ? <span className="muted-text">{field.placeholder || (multi ? "Ein oder mehrere auswählen" : "Auswählen")}</span>
 : <span className="chiprow">
 {selected.map(v => (
 <span key={v} className="chip entity-chip">
 <span className="chip-label">{displayForValue(v, groups)}</span>
 <span
 role="button"
 tabIndex={0}
 className="chip-x"
 onClick={(e) => { e.stopPropagation(); remove(v); }}
 onKeyDown={(e) => { if (e.key === "Enter" || e.key === " ") { e.stopPropagation(); e.preventDefault(); remove(v); } }}
 aria-label="entfernen"
 ><XsecIcon name="x" size={12} /></span>
 </span>
 ))}
 </span>
 }
 <span className="caret" aria-hidden="true"><XsecIcon name="chevron-down" size={14} /></span>
 </div>
 {open && (
 <div className="entity-menu">
 {flatCount === 0 && !field.allowCustom && (
 <div className="entity-empty">Noch keine Einträge in den vorherigen Abschnitten.</div>
 )}
 {groups.map(g => (
 <div key={g.label} className="entity-group">
 <div className="entity-group-label">{g.label}</div>
 {g.items.map(it => (
 <label key={it.value} className={`entity-item ${selected.includes(it.value) ? "is-on" : ""}`}>
 <input type={multi ? "checkbox" : "radio"} checked={selected.includes(it.value)} onChange={() => toggle(it.value)} />
 <span>{it.display}</span>
 </label>
 ))}
 </div>
 ))}
 {field.allowCustom && (
 <div className="entity-custom">
 <div className="entity-group-label">Freitext / externe Angabe</div>
 <div className="entity-custom-row">
 <input className="input" placeholder={field.customPlaceholder || "z. B. externe Mobilnummer"} value={customText} onChange={e => setCustomText(e.target.value)} onKeyDown={e => { if (e.key === "Enter") { e.preventDefault(); addCustom(); } }} />
 <button type="button" className="btn btn-ghost" onClick={addCustom}>Hinzufügen</button>
 </div>
 </div>
 )}
 </div>
 )}
 </div>
 </div>
 );
}

// --- CLIP map: per-department outbound number ---
function ClipMap({ field, value, onChange, error, state }) {
 const depts = (state.departments || []).filter(d => (d.name || "").trim());
 const map = value && typeof value === "object" ? value : {};
 const set = (name, v) => {
 const next = { ...map };
 if (v) next[name] = v; else delete next[name];
 onChange(next);
 };
 const trunkNumbers = [];
 (state.trunks || []).forEach(t => (t.nummern || []).forEach(n => {
 if (n.base) trunkNumbers.push(formatNumber ? formatNumber(n) : n.base);
 }));
 return (
 <div className={`field ${error ? "has-error" : ""}`}>
 <Label field={field} id={"f_" + field.id} error={error} />
 {depts.length === 0 ? (
 <div className="warn-box">Noch keine Abteilungen angelegt.</div>
 ) : (
 <div className="clip-map">
 {depts.map(d => (
 <div key={d.name} className="clip-map-row">
 <div className="clip-map-key">{d.name}</div>
 <input
 className="input"
 placeholder="Rufnummer, die hier als CLIP gezeigt werden soll"
 value={map[d.name] || ""}
 onChange={e => set(d.name, e.target.value)}
 list={trunkNumbers.length ? "clip-trunk-numbers" : undefined}
 />
 </div>
 ))}
 {trunkNumbers.length > 0 && (
 <datalist id="clip-trunk-numbers">
 {trunkNumbers.map((n, i) => <option key={i} value={n} />)}
 </datalist>
 )}
 </div>
 )}
 </div>
 );
}

// --- Shortcut map: short code -> user (mit Anzeigename) ---
function ShortcutMap({ field, value, onChange, error, state }) {
 const rows = Array.isArray(value) ? value : [];
 const users = (state.users || []).filter(u => (u.name || "").trim()).map(u => u.name);
 const depts = (state.departments || []).filter(d => (d.name || "").trim()).map(d => d.name);
 const options = [...users.map(n => ({ v: "user:"+ n, d: n })), ...depts.map(n => ({ v:"dept:"+ n, d: n +" (Abteilung)" }))];
 const add = () => onChange([...rows, { name: "", code:"", target:"" }]);
 const update = (i, k, v) => onChange(rows.map((r, idx) => idx === i ? { ...r, [k] : v } : r));
 const remove = (i) => onChange(rows.filter((_, idx) => idx !== i));
 return (
 <div className={`field ${error ? "has-error" : ""}`}>
 <Label field={field} id={"f_" + field.id} error={error} />
 <div className="shortcut-map">
 {rows.length > 0 && (
 <div className="shortcut-head">
 <span>Anzeigename</span>
 <span>Taste(n)</span>
 <span>Ziel</span>
 <span aria-hidden="true" />
 </div>
 )}
 {rows.length === 0 && <div className="muted-text small">Noch keine Kurzwahl definiert.</div>}
 {rows.map((r, i) => (
 <div key={i} className="shortcut-row">
 <input className="input sc-name" placeholder="z. B. Geschäftsführung" value={r.name || ""} onChange={e => update(i, "name", e.target.value)} />
 <input className="input sc-code mono" placeholder="*1" value={r.code || ""} onChange={e => update(i, "code", e.target.value)} />
 <select className="input sc-target" value={r.target || ""} onChange={e => update(i, "target", e.target.value)}>
 <option value="">Ziel wählen</option>
 {options.map(o => <option key={o.v} value={o.v}>{o.d}</option>)}
 </select>
 <button type="button" className="icon-btn danger" onClick={() => remove(i)} title="Entfernen" aria-label="Entfernen"><XsecIcon name="x" size={14} /></button>
 </div>
 ))}
 <div className="list-footer">
 <button type="button" className="btn btn-ghost" onClick={add}>+ Kurzwahl hinzufügen</button>
 </div>
 </div>
 </div>
 );
}

// --- Externe Kurzwahl: Anzeigename + Rufnummer + optional Tastenkombi ---
function ExtShortcutMap({ field, value, onChange, error }) {
 const rows = Array.isArray(value) ? value : [];
 const add = () => onChange([...rows, { name: "", number:"", code:"" }]);
 const update = (i, k, v) => onChange(rows.map((r, idx) => idx === i ? { ...r, [k] : v } : r));
 const remove = (i) => onChange(rows.filter((_, idx) => idx !== i));
 return (
 <div className={`field ${error ? "has-error" : ""}`}>
 <Label field={field} id={"f_" + field.id} error={error} />
 <div className="shortcut-map ext-shortcut">
 {rows.length > 0 && (
 <div className="shortcut-head ext">
 <span>Anzeigename</span>
 <span>Rufnummer</span>
 <span>Taste(n) (optional)</span>
 <span aria-hidden="true" />
 </div>
 )}
 {rows.length === 0 && <div className="muted-text small">Noch keine externe Kurzwahl definiert.</div>}
 {rows.map((r, i) => (
 <div key={i} className="shortcut-row ext">
 <input className="input sc-name" placeholder="z. B. Steuerbüro Meier" value={r.name || ""} onChange={e => update(i, "name", e.target.value)} />
 <input className="input sc-number mono" placeholder="0211 1234567" value={r.number || ""} onChange={e => update(i, "number", e.target.value)} />
 <input className="input sc-code mono" placeholder="*41" value={r.code || ""} onChange={e => update(i, "code", e.target.value)} />
 <button type="button" className="icon-btn danger" onClick={() => remove(i)} title="Entfernen" aria-label="Entfernen"><XsecIcon name="x" size={14} /></button>
 </div>
 ))}
 <div className="list-footer">
 <button type="button" className="btn btn-ghost" onClick={add}>+ Externe Kurzwahl hinzufügen</button>
 </div>
 </div>
 </div>
 );
}

// --- Multi-select chips for department assignment ---
function MultiChipSelect({ value, options, onChange, placeholder }) {
 const [open, setOpen] = useState(false);
 const selected = Array.isArray(value) ? value : [];
 const toggle = (opt) => {
 if (selected.includes(opt)) onChange(selected.filter(x => x !== opt));
 else onChange([...selected, opt]);
 };
 const ref = useRef(null);
 useEffect(() => {
 const handler = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
 document.addEventListener("mousedown", handler);
 return () => document.removeEventListener("mousedown", handler);
 }, []);
 return (
 <div className="multichip" ref={ref}>
 <button type="button" className="multichip-trigger" onClick={() => setOpen(v => !v)}>
 {selected.length === 0
 ? <span className="muted-text">{placeholder || "Auswählen"}</span>
 : <span className="chiprow">{selected.map(s => <span key={s} className="chip">{s}</span>)}</span>
 }
 <span className="caret" aria-hidden="true"><XsecIcon name="chevron-down" size={14} /></span>
 </button>
 {open && (
 <div className="multichip-menu">
 {options.length === 0 && <div className="multichip-empty">Zuerst Abteilungen anlegen.</div>}
 {options.map(o => (
 <label key={o} className={`multichip-item ${selected.includes(o) ? "is-on" : ""}`}>
 <input type="checkbox" checked={selected.includes(o)} onChange={() => toggle(o)} />
 <span>{o}</span>
 </label>
 ))}
 </div>
 )}
 </div>
 );
}

// --- Dynamic: Users table ---
function UsersList({ users, onChange, departmentOptions }) {
 const addRow = () => onChange([...users, blankUser()]);
 const update = (idx, k, v) => onChange(users.map((u, i) => i === idx ? { ...u, [k] : v } : u));
 const remove = (idx) => onChange(users.filter((_, i) => i !== idx));
 const [touchedEmail, setTouchedEmail] = useState({});
  // Support both flat string[] and grouped [{group, items:string[]}] structures for SUPPORTED_PHONES
 const phoneSource = window.SUPPORTED_PHONES || [];
 const phoneGroups = Array.isArray(phoneSource) && phoneSource.length && typeof phoneSource[0] === "object" && phoneSource[0].items
 ? phoneSource
 : [{ group: "", items: phoneSource }];

 return (
 <div className="users-list">
 <div className="users-head">
 <span>Name + E-Mail</span>
 <span>Durchwahl</span>
 <span>Tischtelefon</span>
 <span>3CX-Apps</span>
 <span>Abteilung(en)</span>
 <span>Sonderrufe</span>
 <span className="tight"></span>
 </div>
 {users.length === 0 && (
 <div className="empty">Noch keine Nutzer eingetragen. Klicken Sie unten auf „+ Nutzer hinzufügen“.</div>
 )}
 {users.map((u, i) => (
 <div className="users-row" key={i}>
 <div className="users-cell stack">
 <input className="input-bare" placeholder="Anna Mustermann" value={u.name || ""} onChange={(e) => update(i, "name", e.target.value)} />
 {(() => {
 const isInvalid = touchedEmail[i] && u.email && !window.isValidEmail(u.email);
 return (
 <>
 <input
 className={`input-bare muted ${isInvalid ? "is-invalid" : ""}`}
 type="email"
 inputMode="email"
 autoComplete="email"
 placeholder="a.mustermann@firma.de"
 value={u.email || ""}
 onChange={(e) => update(i, "email", e.target.value)}
 onBlur={() => setTouchedEmail(t => ({ ...t, [i]: true }))}
 />
 {isInvalid && <span className="cell-error">Ungültige E-Mail</span>}
 </>
 );
 })()}
 </div>
 <div className="users-cell">
 <input className="input-bare" placeholder="auto" value={u.durchwahl || ""} onChange={(e) => update(i, "durchwahl", e.target.value)} />
 </div>
 <div className="users-cell">
 <select className="input-bare phone-select" value={u.phone || ""} onChange={(e) => update(i, "phone", e.target.value)}>
 <option value="">Wählen</option>
 {phoneGroups.map((g, gi) => (
 g.group ? (
 <optgroup key={gi} label={g.group}>
 {g.items.map(p => <option key={p} value={p}>{p}</option>)}
 </optgroup>
 ) : (
 g.items.map(p => <option key={p} value={p}>{p}</option>)
 )
 ))}
 </select>
 </div>
 <div className="users-cell">
 <select className="input-bare" value={u.apps || ""} onChange={(e) => update(i, "apps", e.target.value)}>
 <option value=""></option>
 <option>Beide</option>
 <option>Desktop</option>
 <option>Mobile</option>
 <option>Keine</option>
 </select>
 </div>
 <div className="users-cell">
 <MultiChipSelect
 value={u.abteilungen || []}
 options={departmentOptions}
 onChange={(v) => update(i, "abteilungen", v)}
 placeholder="Abteilungen"
 />
 </div>
 <div className="users-cell">
 <select className="input-bare" value={u.sonderrufe || ""} onChange={(e) => update(i, "sonderrufe", e.target.value)}>
 <option value=""></option>
 <option>Ja</option>
 <option>Nein</option>
 <option>Nur EU</option>
 </select>
 </div>
 <div className="users-cell actions">
 <button type="button" className="icon-btn danger" title="Entfernen" aria-label="Entfernen" onClick={() => remove(i)}><XsecIcon name="x" size={14} /></button>
 </div>
 </div>
 ))}
 <div className="list-footer">
 <button type="button" className="btn btn-ghost" onClick={addRow}>+ Nutzer hinzufügen</button>
 <span className="count">{users.length} Nutzer</span>
 </div>
 </div>
 );
}

function blankUser() { return { name: "", email:"", durchwahl:"", phone:"", apps:"", abteilungen: [], sonderrufe:"" }; }

// --- Dynamic: Departments ---
function DepartmentsList({ items, onChange }) {
 const addRow = () => onChange([...items, { name: "", members:"", owner:"" }]);
 const update = (idx, k, v) => onChange(items.map((u, i) => i === idx ? { ...u, [k] : v } : u));
 const remove = (idx) => onChange(items.filter((_, i) => i !== idx));
 return (
 <div className="dept-list">
 {items.length === 0 && <div className="empty">Noch keine Abteilungen.</div>}
 {items.map((d, i) => (
 <div key={i} className="dept-card">
 <div className="dept-grid">
 <div className="field inline">
 <label className="field-label-main small">Abteilung / Team</label>
 <input className="input" placeholder="z. B. Vertrieb" value={d.name || ""} onChange={(e) => update(i, "name", e.target.value)} />
 </div>
 <div className="field inline">
 <label className="field-label-main small">Beschreibung (optional)</label>
 <input className="input" placeholder="z. B. Neukunden-Akquise" value={d.members || ""} onChange={(e) => update(i, "members", e.target.value)} />
 </div>
 <div className="field inline">
 <label className="field-label-main small">Hauptverantwortlich</label>
 <input className="input" placeholder="Name" value={d.owner || ""} onChange={(e) => update(i, "owner", e.target.value)} />
 </div>
 </div>
 <button type="button" className="icon-btn danger dept-remove" title="Entfernen" aria-label="Entfernen" onClick={() => remove(i)}><XsecIcon name="x" size={14} /></button>
 </div>
 ))}
 <div className="list-footer">
 <button type="button" className="btn btn-ghost" onClick={addRow}>+ Abteilung hinzufügen</button>
 <span className="count">{items.length} Abteilungen</span>
 </div>
 </div>
 );
}

// --- Dynamic: SIP-Trunks with structured number blocks ---
function blankTrunk() {
 return {
 name: "",
 anbieter: "",
 typ: "Anlagenanschluss",
 unsure: false,
 nummern: [blankNumber()],
 notiz: "",
 path: "", //"credentials" | "contract" | "port" | "unknown"
 credentials: { server: "", port:"", registrar:"", username:"", password:"", outbound:"" },
 portierung: { aktueller_anbieter: "", kuendigung_bis:"", wunschtermin:"", rechnung_beigefuegt: false },
 docs: [], // [{name, kind, dataUrl}]
 kontakt_gewuenscht: false
 };
}
function blankNumber() {
 return { kind: "einzel", country: "+49", area:"", base:"", dw_from:"", dw_to:"", label:"" };
}

function TrunksList({ trunks, onChange }) {
 const addTrunk = () => onChange([...trunks, blankTrunk()]);
 const updateTrunk = (idx, k, v) => onChange(trunks.map((t, i) => i === idx ? { ...t, [k] : v } : t));
 const removeTrunk = (idx) => onChange(trunks.filter((_, i) => i !== idx));

 return (
 <div className="trunks">
 {trunks.length === 0 && <div className="empty">Noch kein SIP-Trunk. Klicken Sie unten auf „+ SIP-Trunk hinzufügen“.</div>}
 {trunks.map((t, i) => (
 <TrunkCard
 key={i}
 trunk={t}
 index={i}
 onChange={(k, v) => updateTrunk(i, k, v)}
 onRemove={() => removeTrunk(i)}
 />
 ))}
 <div className="list-footer">
 <button type="button" className="btn btn-ghost" onClick={addTrunk}>+ SIP-Trunk hinzufügen</button>
 <span className="count">{trunks.length} Trunk{trunks.length === 1 ? "" : "s"}</span>
 </div>
 </div>
 );
}

const PROVIDER_GUIDES = {
 "Telekom DeutschlandLAN SIP-Trunk": {
 where: "Geschäftskunden-Portal › Meine Produkte › Ihr SIP-Trunk.",
 fields: "SIP-Benutzername (Anschlusskennung), SIP-Passwort, SIP-Server (meist tel.t-online.de)."
 },
 "Telekom CompanyFlex": {
 where: "CompanyFlex-Portal › Zugangsdaten SIP-Trunk.",
 fields: "Registrar, SIP-Benutzername, Passwort; beachten Sie Realm und Outbound-Proxy."
 },
 "Vodafone IP Anlagenanschluss": {
 where: "Vodafone Business-Portal › Meine Anschlüsse › Zugangsdaten.",
 fields: "Registrar (sip.arcor.de o. ä.), Benutzer, Passwort."
 },
 "1&1 Versatel": {
 where: "Service-Portal › SIP-Trunk-Zugangsdaten.",
 fields: "Rufnummer, Passwort, Registrar."
 },
 "Placetel": {
 where: "Placetel Admin › Telefonanlage › SIP-Trunk › Zugangsdaten.",
 fields: "Benutzername (Rufnummer), Passwort, SIP-Server (sipgate.placetel.de)."
 },
 "Sipgate Trunking": {
 where: "Sipgate-Konto › Trunks › Zugangsdaten anzeigen.",
 fields: "SIP-ID, Passwort, Registrar (sipgate.de)."
 },
 "easybell": {
 where: "easybell-Portal › Meine Anschlüsse › SIP-Zugangsdaten.",
 fields: "SIP-Benutzer, SIP-Passwort, Registrar (sip.easybell.de)."
 },
 "NFON": {
 where: "NFON-Admin › Trunks › Download Konfigurationsdaten.",
 fields: "Registrar, Proxy, Benutzer, Passwort."
 },
 "Toplink": {
 where: "Toplink-Kundenportal › SIP-Trunk › Zugangsdaten.",
 fields: "Registrar, Benutzername, Passwort."
 }
};

const TRUNK_PATHS = [
 { id: "credentials", label:"Ich habe Zugangsdaten", sub:"SIP-Benutzer, Passwort und Server liegen mir vor", icon:"key-round" },
 { id: "contract", label:"Ich habe den Vertrag / Übernahmeschein", sub:"Ich sehe meine Rufnummern auf Papier oder PDF", icon:"file-text" },
 { id: "port", label:"Ich möchte portieren", sub:"Rufnummern sollen von einem anderen Anbieter mitgenommen werden", icon:"arrow-right-left" },
 { id: "unknown", label: "Ich weiß es nicht", sub: "Bitte rufen Sie mich an und klären das mit mir", icon: "circle-help" }
];

function TrunkCard({ trunk, index, onChange, onRemove }) {
 const updateNumber = (ni, k, v) => {
 const next = trunk.nummern.map((n, i) => i === ni ? { ...n, [k] : v } : n);
 onChange("nummern", next);
 };
 const addNumber = (kind) => onChange("nummern", [...trunk.nummern, { ...blankNumber(), kind }]);
 const removeNumber = (ni) => onChange("nummern", trunk.nummern.filter((_, i) => i !== ni));

 const path = trunk.path || "";
 const creds = trunk.credentials || {};
 const port = trunk.portierung || {};
 const docs = trunk.docs || [];

 const setCred = (k, v) => onChange("credentials", { ...creds, [k]: v });
 const setPort = (k, v) => onChange("portierung", { ...port, [k]: v });

 const handleFile = async (e, kind) => {
 const files = Array.from(e.target.files || []);
 if (!files.length) return;
 // Vorprüfung wie auf dem Server (dort maßgeblich): PDF/JPEG/PNG/HEIC, max. 5 Dateien, zusammen max. 10 MB
 const allowed = (f) => f.type ? /^(application\/pdf|image\/(jpeg|png|heic|heif))$/.test(f.type) : /\.(pdf|jpe?g|png|heic|heif)$/i.test(f.name);
 let total = docs.reduce((sum, d) => sum + (d.size || 0), 0);
 const accepted = [];
 for (const f of files) {
 if (!allowed(f) || docs.length + accepted.length >= 5 || total + f.size > 10 * 1024 * 1024) continue;
 accepted.push(f);
 total += f.size;
 }
 const readAs = (f) => new Promise((res) => {
 const r = new FileReader();
 r.onload = () => res({ name: f.name, size: f.size, type: f.type, kind, dataUrl: r.result });
 r.readAsDataURL(f);
 });
 const parsed = await Promise.all(accepted.map(readAs));
 onChange("docs", [...docs, ...parsed]);
 e.target.value = "";
 if (accepted.length < files.length) alert("Nicht alle Dateien wurden übernommen. Erlaubt sind PDF- oder Bilddateien (JPEG, PNG, HEIC), bis zu 5 Dateien und zusammen max. 10 MB.");
 };
 const removeDoc = (i) => onChange("docs", docs.filter((_, idx) => idx !== i));

 const showNumbers = path === "credentials"|| path === "contract"|| path === "port";
 const showAnbieter = path !== "";
 const guide = PROVIDER_GUIDES[trunk.anbieter];

 return (
 <div className={`trunk-card ${trunk.unsure ? "is-unsure" : ""} path-${path || "none"}`}>
 <div className="trunk-head">
 <div className="trunk-head-title">
 <span className="trunk-badge">Trunk {index + 1}</span>
 <input
 className="trunk-name"
 placeholder={`z. B. Telekom SIP-Trunk ${index + 1}`}
 value={trunk.name}
 onChange={(e) => onChange("name", e.target.value)}
 />
 </div>
 <button type="button" className="icon-btn danger" onClick={onRemove} title="Trunk entfernen" aria-label="Trunk entfernen"><XsecIcon name="x" size={14} /></button>
 </div>

 <div className="path-picker">
 <div className="path-picker-label">Was wissen Sie über diesen Anschluss?</div>
 <div className="path-grid">
 {TRUNK_PATHS.map(p => (
 <button
 key={p.id}
 type="button"
 className={`path-card ${path === p.id ? "is-active" : ""}`}
 onClick={() => onChange("path", p.id)}
 >
 <span className="path-icon" aria-hidden="true"><XsecIcon name={p.icon} /></span>
 <span className="path-label">{p.label}</span>
 <span className="path-sub">{p.sub}</span>
 </button>
 ))}
 </div>
 </div>

 {path === "unknown" && (
 <div className="path-body unknown-body">
 <div className="callout-dark">
 <div className="callout-head">Kein Problem, wir rufen Sie an</div>
 <p>Wir klären die Anschlussart gemeinsam am Telefon. Bitte halten Sie folgendes bereit:</p>
 <ul className="checklist">
 <li>Ihren aktuellen Telefonvertrag oder die letzte Rechnung Ihres Anbieters</li>
 <li>Eine Liste aller Rufnummern, die heute auf dem Anschluss laufen</li>
 <li>Den Namen Ihres Anbieters (steht auf der Rechnung)</li>
 <li>Ihre Kundennummer beim Anbieter</li>
 </ul>
 <label className="consent-row">
 <input type="checkbox" checked={!!trunk.kontakt_gewuenscht} onChange={(e) => onChange("kontakt_gewuenscht", e.target.checked)} />
 <span>Ja, X-sec darf mich für diesen Punkt anrufen.</span>
 </label>
 </div>
 </div>
 )}

 {showAnbieter && (
 <div className="trunk-grid">
 <div className="field inline">
 <label className="field-label-main small">Anbieter</label>
 <select className="input" value={trunk.anbieter} onChange={(e) => onChange("anbieter", e.target.value)}>
 <option value="">Wählen</option>
 <option>Telekom DeutschlandLAN SIP-Trunk</option>
 <option>Telekom CompanyFlex</option>
 <option>Vodafone IP Anlagenanschluss</option>
 <option>1&amp;1 Versatel</option>
 <option>Placetel</option>
 <option>Sipgate Trunking</option>
 <option>easybell</option>
 <option>NFON</option>
 <option>Toplink</option>
 <option>Anderer Anbieter (siehe Notiz)</option>
 </select>
 </div>
 <div className="field inline">
 <label className="field-label-main small">Anschluss-Typ</label>
 <select className="input" value={trunk.typ} onChange={(e) => onChange("typ", e.target.value)}>
 <option>Anlagenanschluss (Durchwahlblock)</option>
 <option>Mehrgeräteanschluss (MSN)</option>
 <option>All-IP / Cloud-PBX-Anschluss</option>
 <option>Weiß ich nicht</option>
 </select>
 </div>
 </div>
 )}

 {guide && (path === "credentials"|| path === "contract"|| path === "port") && (
 <div className="provider-guide">
 <div className="provider-guide-tag">Anleitung {trunk.anbieter}</div>
 <div className="provider-guide-row"><strong>Wo finde ich das?</strong><span>{guide.where}</span></div>
 <div className="provider-guide-row"><strong>Das brauchen wir:</strong><span>{guide.fields}</span></div>
 </div>
 )}

 {path === "credentials" && (
 <div className="path-body creds-body">
 <div className="section-tag">Zugangsdaten</div>
 <div className="creds-grid">
 <div className="nf grow">
 <label>SIP-Server / Registrar</label>
 <input className="input mono" placeholder="tel.t-online.de" value={creds.server || ""} onChange={(e) => setCred("server", e.target.value)} />
 </div>
 <div className="nf sm">
 <label>Port</label>
 <input className="input mono" placeholder="5060" value={creds.port || ""} onChange={(e) => setCred("port", e.target.value)} />
 </div>
 <div className="nf grow">
 <label>Outbound-Proxy (optional)</label>
 <input className="input mono" placeholder="Falls vom Anbieter vorgegeben" value={creds.outbound || ""} onChange={(e) => setCred("outbound", e.target.value)} />
 </div>
 <div className="nf grow">
 <label>Benutzername / Anschlusskennung</label>
 <input className="input mono" value={creds.username || ""} onChange={(e) => setCred("username", e.target.value)} />
 </div>
 <div className="nf grow">
 <label>Passwort</label>
 <input className="input mono" type="password" value={creds.password || ""} onChange={(e) => setCred("password", e.target.value)} />
 </div>
 </div>
 <div className="creds-note">Die Zugangsdaten werden ausschließlich für die Einrichtung der 3CX genutzt und verschlüsselt übertragen.</div>
 </div>
 )}

 {path === "port" && (
 <div className="path-body port-body">
 <div className="section-tag">Portierung</div>
 <div className="port-grid">
 <div className="nf grow">
 <label>Aktueller Anbieter</label>
 <input className="input" placeholder="z. B. 1&1" value={port.aktueller_anbieter || ""} onChange={(e) => setPort("aktueller_anbieter", e.target.value)} />
 </div>
 <div className="nf sm">
 <label>Kündigung möglich bis</label>
 <input className="input mono" type="date" value={port.kuendigung_bis || ""} onChange={(e) => setPort("kuendigung_bis", e.target.value)} />
 </div>
 <div className="nf sm">
 <label>Wunsch-Portierungstermin</label>
 <input className="input mono" type="date" value={port.wunschtermin || ""} onChange={(e) => setPort("wunschtermin", e.target.value)} />
 </div>
 </div>
 <div className="callout-soft">
 <strong>Was wir für die Portierung brauchen:</strong>
 <ul className="checklist tight">
 <li>Letzte Rechnung oder Vertrag des aktuellen Anbieters (bitte unten hochladen)</li>
 <li>Rufnummern-Auflistung aus dem Vertrag (Portierungsvollmacht folgt per DocuSign)</li>
 <li>Gewünschter Portierungstermin (frühestens 5 Werktage nach Beauftragung)</li>
 </ul>
 </div>
 </div>
 )}

 {showNumbers && (
 <>
 <label className="unsure-toggle">
 <input type="checkbox" checked={!!trunk.unsure} onChange={(e) => onChange("unsure", e.target.checked)} />
 <span>Ich bin unsicher, bitte prüfen Sie meine Rufnummern mit meinem Anbieter</span>
 </label>

 <div className="numbers-block">
 <div className="numbers-head">
 <h5>Rufnummern dieses Trunks</h5>
 <div className="numbers-add">
 <button type="button" className="btn btn-ghost sm" onClick={() => addNumber("einzel")}>+ Einzelrufnummer</button>
 <button type="button" className="btn btn-ghost sm" onClick={() => addNumber("block")}>+ Durchwahlblock</button>
 </div>
 </div>

 {trunk.nummern.length === 0 && <div className="empty small">Keine Nummern erfasst.</div>}

 {trunk.nummern.length > 0 && (
 <div className="num-table">
 <div className="num-row num-head">
 <div className="c-kind">Typ</div>
 <div className="c-country">Land</div>
 <div className="c-area">Vorwahl</div>
 <div className="c-base">Basisnummer</div>
 <div className="c-dw">Durchwahl</div>
 <div className="c-label">Bezeichnung</div>
 <div className="c-preview">Vorschau</div>
 <div className="c-remove" aria-hidden="true"></div>
 </div>
 {trunk.nummern.map((n, ni) => (
 <div key={ni} className={`num-row num-body ${n.kind}`}>
 <div className="c-kind">
 <select
 className="input-bare kind-select"
 value={n.kind}
 onChange={(e) => updateNumber(ni, "kind", e.target.value)}
 >
 <option value="einzel">Einzel</option>
 <option value="block">Block</option>
 </select>
 </div>
 <div className="c-country">
 <input className="input mono" placeholder="+49" value={n.country} onChange={(e) => updateNumber(ni, "country", e.target.value)} />
 </div>
 <div className="c-area">
 <input className="input mono" placeholder="211" value={n.area} onChange={(e) => updateNumber(ni, "area", e.target.value.replace(/[^0-9]/g, ""))} />
 </div>
 <div className="c-base">
 <input className="input mono" placeholder="12345" value={n.base} onChange={(e) => updateNumber(ni, "base", e.target.value.replace(/[^0-9]/g, ""))} />
 </div>
 <div className="c-dw">
 {n.kind === "block" ? (
 <div className="dw-range">
 <input className="input mono" placeholder="100" value={n.dw_from} onChange={(e) => updateNumber(ni, "dw_from", e.target.value.replace(/[^0-9]/g, ""))} />
 <span className="dw-sep">bis</span>
 <input className="input mono" placeholder="199" value={n.dw_to} onChange={(e) => updateNumber(ni, "dw_to", e.target.value.replace(/[^0-9]/g, ""))} />
 </div>
 ) : (
 <input className="input mono" placeholder="optional" value={n.dw_from} onChange={(e) => updateNumber(ni, "dw_from", e.target.value.replace(/[^0-9]/g, ""))} />
 )}
 </div>
 <div className="c-label">
 <input className="input" placeholder={n.kind === "block" ? "z. B. Hauptblock" : "z. B. Zentrale"} value={n.label} onChange={(e) => updateNumber(ni, "label", e.target.value)} />
 </div>
 <div className="c-preview mono">{formatNumber(n)}</div>
 <div className="c-remove">
 <button type="button" className="icon-btn danger sm" onClick={() => removeNumber(ni)} title="Entfernen" aria-label="Entfernen"><XsecIcon name="x" size={14} /></button>
 </div>
 </div>
 ))}
 </div>
 )}
 </div>
 </>
 )}

 {path !== "" && (
 <div className="docs-block">
 <div className="section-tag">Dokumente (optional, aber hilfreich)</div>
 <label className="doc-drop">
 <input type="file" accept="application/pdf, image/*" multiple onChange={(e) => handleFile(e, path)} hidden />
 <span className="doc-drop-icon">+</span>
 <span className="doc-drop-text">
 <strong>Vertrag / Übernahmeschein / Rechnung hochladen</strong>
 <small>PDF oder Foto · bis zu 5 Dateien · zusammen max. 10 MB · wird verschlüsselt übertragen</small>
 </span>
 </label>
 {docs.length > 0 && (
 <ul className="doc-list">
 {docs.map((d, i) => (
 <li key={i}>
 <span className="doc-file"><XsecIcon name="paperclip" size={14} /> {d.name}</span>
 <span className="doc-size">{Math.round((d.size || 0) / 1024)} KB</span>
 <button type="button" className="icon-btn danger sm" onClick={() => removeDoc(i)} title="Entfernen" aria-label="Entfernen"><XsecIcon name="x" size={14} /></button>
 </li>
 ))}
 </ul>
 )}
 </div>
 )}

 {path !== "" && (
 <div className="field inline">
 <label className="field-label-main small">Notiz zu diesem Trunk (optional)</label>
 <textarea className="input textarea" rows="2" placeholder="z. B. Portierung läuft, Aktivierung zum 01.06." value={trunk.notiz} onChange={(e) => onChange("notiz", e.target.value)} />
 </div>
 )}
 </div>
 );
}

function formatNumber(n) {
 if (!n.base && !n.area) return "";
 const head = `${n.country || ""} ${n.area || ""}`.trim();
 if (n.kind === "block") {
 if (!n.dw_from || !n.dw_to) return `${head} ${n.base}-???`;
 return `${head} ${n.base}-${n.dw_from}…${n.dw_to}`;
 }
 return n.dw_from ? `${head} ${n.base}-${n.dw_from}` : `${head} ${n.base}`;
}

// --- Dynamic: Week hours ---
const WEEKDAYS = [
 { k: "mo", l:"Montag" },
 { k: "di", l:"Dienstag" },
 { k: "mi", l:"Mittwoch" },
 { k: "do", l:"Donnerstag" },
 { k: "fr", l:"Freitag" },
 { k: "sa", l:"Samstag" },
 { k: "so", l:"Sonntag" }
];

function defaultHours() {
 const o = {};
 WEEKDAYS.forEach(({k}) => {
 const weekday = !["sa", "so"].includes(k);
 o[k] = { open: weekday, from: weekday ? "08:00" : "", to: weekday ? "17:00" : "", pause: false, pause_from: "", pause_to: "" };
 });
 return o;
}

function WeekHours({ hours, onChange }) {
 const h = hours || defaultHours();
 const update = (day, k, v) => onChange({ ...h, [day]: { ...h[day], [k]: v } });
  // Kopiere Zeiten des Quelltages auf alle Werktage Mo-Fr (öffnet sie, falls geschlossen).
  // Samstag/Sonntag werden bewusst NICHT überschrieben, Wochenenden regelt der Nutzer manuell.
 const copyToWeekdays = (day) => {
 const src = h[day] || {};
 const next = { ...h };
 ["mo", "di", "mi", "do", "fr"].forEach(k => {
 next[k] = {
 open: true,
 from: src.from || "",
 to: src.to || "",
 pause: !!src.pause,
 pause_from: src.pause_from || "",
 pause_to: src.pause_to || ""
 };
 });
 onChange(next);
 };
 return (
 <div className="weekhours">
 {WEEKDAYS.map(({ k, l }) => {
 const d = h[k] || { open: false };
 const isWeekday = !["sa", "so"].includes(k);
 return (
 <div key={k} className={`wh-row ${d.open ? "is-open" : "is-closed"}`}>
 <div className="wh-day">
 <label className="wh-toggle">
 <input type="checkbox" checked={!!d.open} onChange={(e) => update(k, "open", e.target.checked)} />
 <span className="wh-day-name">{l}</span>
 </label>
 </div>
 {d.open ? (
 <>
 <div className="wh-times">
 <div className="wh-time">
 <label>Öffnet</label>
 <input type="time" className="input mono" value={d.from || ""} onChange={(e) => update(k, "from", e.target.value)} />
 </div>
 <span className="wh-dash">bis</span>
 <div className="wh-time">
 <label>Schließt</label>
 <input type="time" className="input mono" value={d.to || ""} onChange={(e) => update(k, "to", e.target.value)} />
 </div>
 </div>
 <div className="wh-pause">
 <label className="wh-toggle sm">
 <input type="checkbox" checked={!!d.pause} onChange={(e) => update(k, "pause", e.target.checked)} />
 <span>Pause</span>
 </label>
 {d.pause && (
 <div className="wh-pause-times">
 <input type="time" className="input mono" value={d.pause_from || ""} onChange={(e) => update(k, "pause_from", e.target.value)} />
 <span className="wh-dash">bis</span>
 <input type="time" className="input mono" value={d.pause_to || ""} onChange={(e) => update(k, "pause_to", e.target.value)} />
 </div>
 )}
 </div>
 {isWeekday ? (
 <button
 type="button"
 className="btn sm wh-copy"
 onClick={() => copyToWeekdays(k)}
 title="Diese Zeiten auf alle Wochentage Mo-Fr übertragen (Samstag und Sonntag bleiben unverändert)"
 >
 Auf Mo-Fr übernehmen
 </button>
 ) : <span />}
 </>
 ) : (
 <div className="wh-closed">geschlossen</div>
 )}
 </div>
 );
 })}
 </div>
 );
}

// --- Dept-specific hours editor ---
function DeptHoursEditor({ field, value, onChange, error, state }) {
 const selected = Array.isArray(state.data.team_ausnahmen_depts) ? state.data.team_ausnahmen_depts : [];
 const deptNames = selected.map(v => {
 const s = String(v || "");
 if (s.startsWith("dept:")) return s.slice(5);
 if (s.startsWith("free:")) return s.slice(5);
 return s;
 }).filter(Boolean);
 const map = value && typeof value === "object" && !Array.isArray(value) ? value : {};
 const setFor = (name, hrs) => {
 const next = { ...map };
 next[name] = hrs;
 onChange(next);
 };
 return (
 <div className={`field ${error ? "has-error" : ""}`}>
 <Label field={field} id={"f_" + field.id} error={error} />
 {deptNames.length === 0 ? (
 <div className="warn-box">Wählen Sie im Feld darüber eine oder mehrere Abteilungen aus.</div>
 ) : (
 <div className="dept-hours-stack">
 {deptNames.map(name => (
 <div key={name} className="dept-hours-block">
 <div className="dept-hours-title">{name}</div>
 <WeekHours hours={map[name] || defaultHours()} onChange={(h) => setFor(name, h)} />
 </div>
 ))}
 </div>
 )}
 </div>
 );
}

// --- Dynamic: IVR Tree ---
function IVRTree({ items, onChange, state }) {
 const addRow = () => {
 const nextKey = items.length < 9 ? String(items.length + 1) : "0";
 onChange([...items, { key: nextKey, label: "", target:"" }]);
 };
 const update = (idx, k, v) => onChange(items.map((u, i) => i === idx ? { ...u, [k] : v } : u));
 const remove = (idx) => onChange(items.filter((_, i) => i !== idx));

 const groups = useMemo(() => buildEntityOptions(state || {}, ["users", "departments", "queues", "voicemail", "special"]), [state]);

 return (
 <div className="ivr-tree">
 <div className="ivr-root">
 <div className="ivr-root-icon" aria-hidden="true"><XsecIcon name="phone" size={18} /></div>
 <div>
 <div className="ivr-root-title">Hauptansage</div>
 <div className="ivr-root-sub">„Willkommen bei …“ · Taste drücken für Menü</div>
 </div>
 </div>
 <div className="ivr-branches">
 {items.length === 0 && <div className="empty">Noch keine Menüpunkte.</div>}
 {items.map((m, i) => (
 <div key={i} className="ivr-branch">
 <span className="ivr-connector" aria-hidden="true" />
 <div className="ivr-key">
 <input className="input-bare key-input" maxLength={2} value={m.key || ""} onChange={(e) => update(i, "key", e.target.value.replace(/[^0-9*#]/g, ""))} />
 </div>
 <div className="ivr-card">
 <div className="ivr-card-grid">
 <div>
 <label className="field-label-main small">Bezeichnung (hört der Anrufer)</label>
 <input className="input" placeholder="z. B. Vertrieb" value={m.label || ""} onChange={(e) => update(i, "label", e.target.value)} />
 </div>
 <div>
 <label className="field-label-main small">Ziel</label>
 <select className="input" value={m.target || ""} onChange={(e) => update(i, "target", e.target.value)}>
 <option value="">Ziel auswählen</option>
 {groups.map(g => (
 <optgroup key={g.label} label={g.label}>
 {g.items.map(it => (
 <option key={it.value} value={it.value}>{it.display}</option>
 ))}
 </optgroup>
 ))}
 </select>
 </div>
 </div>
 <button type="button" className="icon-btn danger" onClick={() => remove(i)} title="Entfernen" aria-label="Entfernen"><XsecIcon name="x" size={14} /></button>
 </div>
 </div>
 ))}
 </div>
 <div className="ivr-hint">Die Ziele stammen aus Ihren bereits erfassten Abteilungen, Nutzern und Warteschlangen.</div>
 <div className="list-footer">
 <button type="button" className="btn btn-ghost" onClick={addRow} disabled={items.length >= 10}>+ Menüpunkt hinzufügen</button>
 <span className="count">{items.length} / 10</span>
 </div>
 </div>
 );
}

// --- Interactive flow diagram ---
function FlowDiagram({ state, onJump }) {
 const [selected, setSelected] = useState(null);
 const d = state.data || {};
 const trunks = state.trunks || [];
 const depts = state.departments || [];
 const users = state.users || [];
 const ivr = state.ivr || [];
 const hours = state.hours || {};

 const nodes = useMemo(() => {
 const list = [];
    // Trunks
 trunks.forEach((t, i) => {
 const numbers = (t.nummern || []).map(formatNumber);
 list.push({
 id: "trunk-" + i,
 type: "trunk",
 col: 0, row: i,
 label: t.name || `Trunk ${i + 1}`,
 sub: t.anbieter || "Anbieter offen",
 details: [
 ["Typ", t.typ],
 ["Anbieter", t.anbieter || ""],
 ...numbers.map((n, j) => [`Nummer ${j+1}`, n]),
 t.unsure ? ["Status", "Kunde unsicher, Anbieter bestätigen"] : null,
 t.notiz ? ["Notiz", t.notiz] : null
 ].filter(Boolean),
 jumpTo: "trunks"
 });
 });
 if (trunks.length === 0) {
 list.push({ id: "trunk-none", type:"trunk placeholder", col: 0, row: 0, label:"Kein SIP-Trunk", sub:"noch nicht erfasst", details: [], jumpTo:"trunks" });
 }

    // Hours / entry logic
 const openDays = WEEKDAYS.filter(({k}) => (hours[k] || {}).open).map(({k, l}) => `${l.slice(0,2)} ${hours[k].from} bis ${hours[k].to}`);
 list.push({
 id: "hours",
 type: "hours",
 col: 1, row: 0,
 label: "Geschäftszeiten",
 sub: openDays.length ? `${openDays.length} offene Tage` : "noch nicht gesetzt",
 details: openDays.length ? openDays.map((s, i) => [`Tag ${i+1}`, s]) : [["Hinweis", "Öffnungs­zeiten fehlen"]],
 jumpTo: "zeiten"
 });

    // Main routing (GZ)
 list.push({
 id: "routing-gz",
 type: "routing",
 col: 2, row: 0,
 label: "Während Geschäftszeiten",
 sub: d.verhalten_gz || "",
 details: [
 ["Hauptnummer-Verhalten", d.verhalten_gz || ""],
 ["Erstannahme", resolveEntity(d.erst_annahme, state)],
 ["Fallback", resolveEntity(d.nicht_erreicht_ziel, state)]
 ],
 jumpTo: "eingehend"
 });

    // Outside hours
 list.push({
 id: "routing-aus",
 type: "routing alt",
 col: 2, row: 1,
 label: "Außerhalb Geschäftszeiten",
 sub: d.verhalten_ausser || "",
 details: [
 ["Verhalten", d.verhalten_ausser || ""],
 d.verhalten_ausser_nummer ? ["Notfallnummer", d.verhalten_ausser_nummer] : null,
 ["Feiertage", d.verhalten_feiertag || ""]
 ].filter(Boolean),
 jumpTo: "zeiten"
 });

    // IVR or departments
 if (d.ivr_aktiv === "Ja" && ivr.length > 0) {
 list.push({
 id: "ivr",
 type: "ivr",
 col: 3, row: 0,
 label: "Sprachmenü (IVR)",
 sub: `${ivr.length} Auswahlen`,
 details: ivr.map(m => [`Taste ${m.key || "?"}`, [m.label, "→ " + (m.target ? resolveEntity(m.target, state) : "?")].filter(Boolean).join(" ")]),
 jumpTo: "ivr"
 });
 }

    // Departments
 depts.forEach((dep, i) => {
 const members = users.filter(u => (u.abteilungen || []).includes(dep.name));
 list.push({
 id: "dept-" + i,
 type: "dept",
 col: 4, row: i,
 label: dep.name || `Abteilung ${i+1}`,
 sub: `${members.length} Mitglied${members.length === 1 ? "" : "er"}`,
 details: [
 ["Verantwortlich", dep.owner || ""],
 ...members.map(u => ["Mitglied", `${u.name || "?"}${u.durchwahl ? " · DW " + u.durchwahl : ""}`])
 ],
 jumpTo: "abteilungen"
 });
 });

    // Unassigned users
 const unassigned = users.filter(u => u.name && (!u.abteilungen || u.abteilungen.length === 0));
 if (unassigned.length > 0) {
 list.push({
 id: "unassigned",
 type: "dept placeholder",
 col: 4, row: depts.length,
 label: "Einzelnutzer (ohne Abt.)",
 sub: `${unassigned.length} Person${unassigned.length === 1 ? "" : "en"}`,
 details: unassigned.map(u => ["Nutzer", `${u.name}${u.durchwahl ? " · DW " + u.durchwahl : ""}`]),
 jumpTo: "nutzer"
 });
 }

 return list;
 }, [trunks, depts, users, ivr, hours, d]);

  // Build edges
 const edges = useMemo(() => {
 const e = [];
 const trunkNodes = nodes.filter(n => n.type.startsWith("trunk"));
 const hoursNode = nodes.find(n => n.id === "hours");
 const gzNode = nodes.find(n => n.id === "routing-gz");
 const ausNode = nodes.find(n => n.id === "routing-aus");
 const ivrNode = nodes.find(n => n.id === "ivr");
 const deptNodes = nodes.filter(n => n.type.startsWith("dept"));

 trunkNodes.forEach(t => hoursNode && e.push([t.id, hoursNode.id]));
 if (hoursNode && gzNode) e.push([hoursNode.id, gzNode.id, "tagsüber"]);
 if (hoursNode && ausNode) e.push([hoursNode.id, ausNode.id, "abends/WE"]);

 if (ivrNode && gzNode) {
 e.push([gzNode.id, ivrNode.id]);
 deptNodes.forEach(dn => e.push([ivrNode.id, dn.id]));
 } else if (gzNode) {
 deptNodes.forEach(dn => e.push([gzNode.id, dn.id]));
 }
 return e;
 }, [nodes]);

  // Layout
 const colW = 220;
 const rowH = 100;
 const padX = 30;
 const padY = 30;
 const positions = {};
 nodes.forEach(n => {
 positions[n.id] = { x: padX + n.col * colW, y: padY + n.row * rowH };
 });
 const maxCol = Math.max(0, ...nodes.map(n => n.col));
 const maxRow = Math.max(0, ...nodes.map(n => n.row));
 const width = padX * 2 + (maxCol + 1) * colW + 40;
 const height = padY * 2 + (maxRow + 1) * rowH + 40;
 const nodeW = 190;
 const nodeH = 68;

 const selectedNode = nodes.find(n => n.id === selected);

 return (
 <div className="flow-wrap">
 <div className="flow-legend">
 <span><i className="lgd trunk" /> SIP-Trunk</span>
 <span><i className="lgd hours" /> Geschäftszeiten</span>
 <span><i className="lgd routing" /> Routing</span>
 <span><i className="lgd ivr" /> IVR</span>
 <span><i className="lgd dept" /> Abteilung</span>
 <span className="hint">Knoten anklicken für Details · Doppelklick springt in den Abschnitt</span>
 </div>
 <div className="flow-canvas" style={{ minHeight: height }}>
 <svg className="flow-svg" width={width} height={height}>
 <defs>
 <marker id="arr" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
 <path d="M0,0 L10,5 L0,10 z" fill="var(--line-strong)" />
 </marker>
 </defs>
 {edges.map(([a, b, lbl], i) => {
 const pa = positions[a], pb = positions[b];
 if (!pa || !pb) return null;
 const x1 = pa.x + nodeW;
 const y1 = pa.y + nodeH / 2;
 const x2 = pb.x;
 const y2 = pb.y + nodeH / 2;
 const mx = (x1 + x2) / 2;
 const path = `M${x1},${y1} C${mx},${y1} ${mx},${y2} ${x2},${y2}`;
 return (
 <g key={i}>
 <path d={path} className="flow-edge" markerEnd="url(#arr)" />
 {lbl && (
 <text x={mx} y={(y1 + y2) / 2 - 6} textAnchor="middle" className="flow-edge-label">{lbl}</text>
 )}
 </g>
 );
 })}
 {nodes.map(n => {
 const p = positions[n.id];
 return (
 <g
 key={n.id}
 className={`flow-node ${n.type} ${selected === n.id ? "is-selected" : ""}`}
 transform={`translate(${p.x},${p.y})`}
 onClick={() => setSelected(selected === n.id ? null : n.id)}
 onDoubleClick={() => onJump && onJump(n.jumpTo)}
 >
 <rect width={nodeW} height={nodeH} rx="10" />
 <text x="14" y="26" className="fn-label">{n.label}</text>
 <text x="14" y="48" className="fn-sub">{n.sub}</text>
 </g>
 );
 })}
 </svg>
 </div>
 {selectedNode && (
 <div className="flow-details">
 <div className="flow-details-head">
 <h4>{selectedNode.label}</h4>
 <div className="flow-details-actions">
 <button className="btn sm" onClick={() => onJump && onJump(selectedNode.jumpTo)}>Zum Abschnitt springen <XsecIcon name="arrow-right" size={14} /></button>
 <button className="icon-btn" onClick={() => setSelected(null)} aria-label="Schließen"><XsecIcon name="x" size={14} /></button>
 </div>
 </div>
 {selectedNode.details.length === 0 ? (
 <div className="muted-text">Keine Details erfasst.</div>
 ) : (
 <dl className="flow-details-list">
 {selectedNode.details.map(([k, v], i) => (
 <React.Fragment key={i}><dt>{k}</dt><dd>{v}</dd></React.Fragment>
 ))}
 </dl>
 )}
 </div>
 )}
 </div>
 );
}

Object.assign(window, { Field, UsersList, DepartmentsList, TrunksList, WeekHours, IVRTree, FlowDiagram, EntityPicker, ClipMap, ShortcutMap, MultiChipSelect, blankUser, blankTrunk, defaultHours, formatNumber, WEEKDAYS });

// --- Icons: Inline-SVG mit Pfaden aus Lucide (ISC-Lizenz, siehe assets/LICENSE-lucide.txt) ---
const XSEC_ICONS = {
 "arrow-down": [["path", {"d": "M12 5v14"}], ["path", {"d": "m19 12-7 7-7-7"}]],
 "arrow-left": [["path", {"d": "m12 19-7-7 7-7"}], ["path", {"d": "M19 12H5"}]],
 "arrow-right": [["path", {"d": "M5 12h14"}], ["path", {"d": "m12 5 7 7-7 7"}]],
 "arrow-right-left": [["path", {"d": "m16 3 4 4-4 4"}], ["path", {"d": "M20 7H4"}], ["path", {"d": "m8 21-4-4 4-4"}], ["path", {"d": "M4 17h16"}]],
 "building-2": [["path", {"d": "M6 22V4a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v18Z"}], ["path", {"d": "M6 12H4a2 2 0 0 0-2 2v6a2 2 0 0 0 2 2h2"}], ["path", {"d": "M18 9h2a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2h-2"}], ["path", {"d": "M10 6h4"}], ["path", {"d": "M10 10h4"}], ["path", {"d": "M10 14h4"}], ["path", {"d": "M10 18h4"}]],
 "check": [["path", {"d": "M20 6 9 17l-5-5"}]],
 "chevron-down": [["path", {"d": "m6 9 6 6 6-6"}]],
 "circle-check": [["circle", {"cx": "12", "cy": "12", "r": "10"}], ["path", {"d": "m9 12 2 2 4-4"}]],
 "circle-help": [["circle", {"cx": "12", "cy": "12", "r": "10"}], ["path", {"d": "M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"}], ["path", {"d": "M12 17h.01"}]],
 "corner-down-right": [["polyline", {"points": "15 10 20 15 15 20"}], ["path", {"d": "M4 4v7a4 4 0 0 0 4 4h12"}]],
 "eye-off": [["path", {"d": "M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49"}], ["path", {"d": "M14.084 14.158a3 3 0 0 1-4.242-4.242"}], ["path", {"d": "M17.479 17.499a10.75 10.75 0 0 1-15.417-5.151 1 1 0 0 1 0-.696 10.75 10.75 0 0 1 4.446-5.143"}], ["path", {"d": "m2 2 20 20"}]],
 "file-down": [["path", {"d": "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"}], ["path", {"d": "M14 2v4a2 2 0 0 0 2 2h4"}], ["path", {"d": "M12 18v-6"}], ["path", {"d": "m9 15 3 3 3-3"}]],
 "file-text": [["path", {"d": "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"}], ["path", {"d": "M14 2v4a2 2 0 0 0 2 2h4"}], ["path", {"d": "M10 9H8"}], ["path", {"d": "M16 13H8"}], ["path", {"d": "M16 17H8"}]],
 "grid-3x3": [["rect", {"width": "18", "height": "18", "x": "3", "y": "3", "rx": "2"}], ["path", {"d": "M3 9h18"}], ["path", {"d": "M3 15h18"}], ["path", {"d": "M9 3v18"}], ["path", {"d": "M15 3v18"}]],
 "hourglass": [["path", {"d": "M5 22h14"}], ["path", {"d": "M5 2h14"}], ["path", {"d": "M17 22v-4.172a2 2 0 0 0-.586-1.414L12 12l-4.414 4.414A2 2 0 0 0 7 17.828V22"}], ["path", {"d": "M7 2v4.172a2 2 0 0 0 .586 1.414L12 12l4.414-4.414A2 2 0 0 0 17 6.172V2"}]],
 "info": [["circle", {"cx": "12", "cy": "12", "r": "10"}], ["path", {"d": "M12 16v-4"}], ["path", {"d": "M12 8h.01"}]],
 "key-round": [["path", {"d": "M2.586 17.414A2 2 0 0 0 2 18.828V21a1 1 0 0 0 1 1h3a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h1a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h.172a2 2 0 0 0 1.414-.586l.814-.814a6.5 6.5 0 1 0-4-4z"}], ["circle", {"cx": "16.5", "cy": "7.5", "r": ".5", "fill": "currentColor"}]],
 "mail": [["rect", {"width": "20", "height": "16", "x": "2", "y": "4", "rx": "2"}], ["path", {"d": "m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7"}]],
 "megaphone": [["path", {"d": "m3 11 18-5v12L3 14v-3z"}], ["path", {"d": "M11.6 16.8a3 3 0 1 1-5.8-1.6"}]],
 "moon": [["path", {"d": "M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"}]],
 "monitor-smartphone": [["path", {"d": "M18 8V6a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2v7a2 2 0 0 0 2 2h8"}], ["path", {"d": "M10 19v-3.96 3.15"}], ["path", {"d": "M7 19h5"}], ["rect", {"width": "6", "height": "10", "x": "16", "y": "12", "rx": "2"}]],
 "network": [["rect", {"x": "16", "y": "16", "width": "6", "height": "6", "rx": "1"}], ["rect", {"x": "2", "y": "16", "width": "6", "height": "6", "rx": "1"}], ["rect", {"x": "9", "y": "2", "width": "6", "height": "6", "rx": "1"}], ["path", {"d": "M5 16v-3a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1v3"}], ["path", {"d": "M12 12V8"}]],
 "paperclip": [["path", {"d": "m21.44 11.05-9.19 9.19a6 6 0 0 1-8.49-8.49l8.57-8.57A4 4 0 1 1 18 8.84l-8.59 8.57a2 2 0 0 1-2.83-2.83l8.49-8.48"}]],
 "pause": [["rect", {"x": "14", "y": "4", "width": "4", "height": "16", "rx": "1"}], ["rect", {"x": "6", "y": "4", "width": "4", "height": "16", "rx": "1"}]],
 "phone": [["path", {"d": "M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z"}]],
 "phone-forwarded": [["polyline", {"points": "18 2 22 6 18 10"}], ["line", {"x1": "14", "x2": "22", "y1": "6", "y2": "6"}], ["path", {"d": "M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z"}]],
 "settings": [["path", {"d": "M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z"}], ["circle", {"cx": "12", "cy": "12", "r": "3"}]],
 "smartphone": [["rect", {"width": "14", "height": "20", "x": "5", "y": "2", "rx": "2", "ry": "2"}], ["path", {"d": "M12 18h.01"}]],
 "sparkles": [["path", {"d": "M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z"}], ["path", {"d": "M20 3v4"}], ["path", {"d": "M22 5h-4"}], ["path", {"d": "M4 17v2"}], ["path", {"d": "M5 18H3"}]],
 "sun": [["circle", {"cx": "12", "cy": "12", "r": "4"}], ["path", {"d": "M12 2v2"}], ["path", {"d": "M12 20v2"}], ["path", {"d": "m4.93 4.93 1.41 1.41"}], ["path", {"d": "m17.66 17.66 1.41 1.41"}], ["path", {"d": "M2 12h2"}], ["path", {"d": "M20 12h2"}], ["path", {"d": "m6.34 17.66-1.41 1.41"}], ["path", {"d": "m19.07 4.93-1.41 1.41"}]],
 "user": [["path", {"d": "M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"}], ["circle", {"cx": "12", "cy": "7", "r": "4"}]],
 "users": [["path", {"d": "M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"}], ["circle", {"cx": "9", "cy": "7", "r": "4"}], ["path", {"d": "M22 21v-2a4 4 0 0 0-3-3.87"}], ["path", {"d": "M16 3.13a4 4 0 0 1 0 7.75"}]],
 "voicemail": [["circle", {"cx": "6", "cy": "12", "r": "4"}], ["circle", {"cx": "18", "cy": "12", "r": "4"}], ["line", {"x1": "6", "x2": "18", "y1": "16", "y2": "16"}]],
 "x": [["path", {"d": "M18 6 6 18"}], ["path", {"d": "m6 6 12 12"}]]
};

function XsecIcon({ name, size = 16 }) {
 const nodes = XSEC_ICONS[name];
 if (!nodes) return null;
 return (
 <svg className="xicon" width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" focusable="false">
 {nodes.map(([tag, attrs], i) => React.createElement(tag, { key: i, ...attrs }))}
 </svg>
 );
}
window.XsecIcon = XsecIcon;

// --- Hell/Dunkel: Quelle ist <html data-theme>, gesetzt vom Inline-Script in index.html ---
const XSEC_THEME_KEY = "x-sec-3cx-theme";
const readXsecTheme = () => (document.documentElement.getAttribute("data-theme") === "dark" ? "dark" : "light");
function applyXsecTheme(theme) {
 document.documentElement.setAttribute("data-theme", theme);
 // Browserleiste in der Hintergrundfarbe des Themes
 const meta = document.querySelector('meta[name="theme-color"]');
 if (meta) meta.setAttribute("content", getComputedStyle(document.documentElement).getPropertyValue("--bg").trim());
 try { localStorage.setItem(XSEC_THEME_KEY, theme); } catch (e) {}
}

// --- Hauptnavigation im Stil von 360sec.de: Logo, dunkle Pille, Hotline, runde Icon-Knöpfe ---
const XSEC_NAV_ITEMS = [["start", "Start"], ["story", "Neue Anlage"], ["change", "Änderung"], ["message", "Nachricht"]];

function XsecNav({ active, onNavigate, phone, phoneRaw }) {
 const [open, setOpen] = useState(false);
 const [theme, setTheme] = useState(readXsecTheme);
 const dark = theme === "dark";
 const go = (view) => { setOpen(false); onNavigate(view); };
 const toggleTheme = () => { const next = dark ? "light" : "dark"; applyXsecTheme(next); setTheme(next); };
 return (
 <header className={`xnav ${open ? "is-open" : ""}`}>
 <div className="xnav-inner">
 <a className="xnav-logo" href="./" onClick={(e) => { e.preventDefault(); go("start"); }} aria-label="X-sec, zur Startseite">
 <img className="xnav-logo-base on-dark" src="assets/xsec_logo_white.svg" alt="X-sec" width="122" height="40" />
 <img className="xnav-logo-transform on-dark" src="assets/xsec_logo_white_transform.svg" alt="" width="122" height="40" />
 <img className="xnav-logo-base on-light" src="assets/xsec_logo_black.svg" alt="X-sec" width="122" height="40" />
 <img className="xnav-logo-transform on-light" src="assets/xsec_logo_black_transform.svg" alt="" width="122" height="40" />
 </a>
 <nav id="xnav-menu" className="xnav-menu" aria-label="Bereiche">
 {XSEC_NAV_ITEMS.map(([view, label]) => (
 <button key={view} type="button" className={`xnav-item ${active === view ? "is-active" : ""}`} aria-current={active === view ? "page" : undefined} onClick={() => go(view)}>{label}</button>
 ))}
 </nav>
 <div className="xnav-actions">
 <a className="xnav-phone" href={"tel:" + phoneRaw}>{phone}</a>
 <a className="xnav-icon" href={"tel:" + phoneRaw} aria-label={"X-sec anrufen: " + phone} title={phone}>
 <XsecIcon name="phone" size={20} />
 </a>
 <button type="button" className="xnav-icon xnav-theme" aria-label="Dunkler Modus" aria-pressed={dark} title={dark ? "Hellen Modus einschalten" : "Dunklen Modus einschalten"} onClick={toggleTheme}>
 <XsecIcon name={dark ? "sun" : "moon"} size={20} />
 </button>
 <button type="button" className="xnav-burger" aria-expanded={open} aria-controls="xnav-menu" aria-label="Menü" onClick={() => setOpen(!open)}>
 <span /><span /><span />
 </button>
 </div>
 </div>
 </header>
 );
}
window.XsecNav = XsecNav;
