const { useState, useEffect, useCallback, useRef } = React;

/* ============================ API-Client ============================ */
const api = {
  async req(method, url, body, isForm) {
    const opt = { method, headers: {} };
    if (body && !isForm) { opt.headers["Content-Type"] = "application/json"; opt.body = JSON.stringify(body); }
    if (isForm) opt.body = body;
    const r = await fetch(url, opt);
    if (r.status === 401) { window.__setUnauth && window.__setUnauth(); throw new Error("Nicht angemeldet"); }
    const ct = r.headers.get("content-type") || "";
    const data = ct.includes("json") ? await r.json() : null;
    if (!r.ok) { const e = new Error((data && data.error) || "Fehler"); e.status = r.status; e.data = data; throw e; }
    return data;
  },
  get: (u) => api.req("GET", u),
  post: (u, b) => api.req("POST", u, b),
  put: (u, b) => api.req("PUT", u, b),
  del: (u) => api.req("DELETE", u),
  upload: (u, form) => api.req("POST", u, form, true),
  putForm: (u, form) => api.req("PUT", u, form, true),
};

/* ============================ Konstanten ============================ */
const C = {
  ink:"#0f172a", slate:"#1e293b", steel:"#334155", muted:"#64748b",
  ice:"#38bdf8", iceDeep:"#0ea5e9", iceSoft:"#e0f2fe", line:"#e2e8f0",
  lineSoft:"#eef2f7", paper:"#f8fafc", good:"#059669", goodSoft:"#d1fae5",
  warn:"#b45309", warnSoft:"#fef3c7", danger:"#dc2626",
};
const inputStyle = { padding:"9px 10px", border:`1px solid ${C.line}`, borderRadius:8, fontSize:14, background:"#fff", color:C.ink, width:"100%", boxSizing:"border-box" };
const GRID = { display:"grid", gridTemplateColumns:"repeat(2, minmax(0,1fr))", gap:14 };

const PHOTO_SLOTS = [
  { cat:"mitarbeiterfoto", label:"Mitarbeiterfoto" },
  { cat:"ausweis_vorne", label:"Ausweis · Vorderseite" },
  { cat:"ausweis_hinten", label:"Ausweis · Rückseite" },
  { cat:"bankkarte_vorne", label:"Bankkarte · Vorderseite" },
  { cat:"bankkarte_hinten", label:"Bankkarte · Rückseite" },
  { cat:"kk_vorne", label:"Krankenkassenkarte · Vorderseite" },
  { cat:"kk_hinten", label:"Krankenkassenkarte · Rückseite" },
  { cat:"sv_vorne", label:"Sozialvers.-Ausweis · Vorderseite" },
  { cat:"sv_hinten", label:"Sozialvers.-Ausweis · Rückseite" },
  { cat:"arbeitserlaubnis", label:"Arbeitserlaubnis (optional)" },
  { cat:"schwerbehindertenausweis", label:"Schwerbehindertenausweis (optional)" },
];

/* ============================ Auswahllisten ============================ */
const FAMILIENSTAND = ["ledig", "verheiratet", "geschieden", "verwitwet"];
const STEUERKLASSEN = ["I", "II", "III", "IV", "V", "VI"];
const EINGESTELLT_ALS = ["Reinigungskraft", "inf. Hausmeister", "Springer", "Vorarbeiter", "Glasreiniger", "Sonderreiniger", "Angestellter", "Objektleiter", "Logistikmitarbeiter"];
const EINSATZBEREICHE = ["TH", "Büro", "Arzt-Praxen", "Bremen-Nord"];
const ARBEITSTAGE = ["Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag", "Sonntag"];

const LAENDER = ["Afghanistan","Ägypten","Albanien","Algerien","Andorra","Angola","Antigua und Barbuda","Äquatorialguinea","Argentinien","Armenien","Aserbaidschan","Äthiopien","Australien","Bahamas","Bahrain","Bangladesch","Barbados","Belarus","Belgien","Belize","Benin","Bhutan","Bolivien","Bosnien und Herzegowina","Botswana","Brasilien","Brunei","Bulgarien","Burkina Faso","Burundi","Chile","China","Costa Rica","Dänemark","Deutschland","Dominica","Dominikanische Republik","Dschibuti","Ecuador","El Salvador","Elfenbeinküste","Eritrea","Estland","Eswatini","Fidschi","Finnland","Frankreich","Gabun","Gambia","Georgien","Ghana","Grenada","Griechenland","Guatemala","Guinea","Guinea-Bissau","Guyana","Haiti","Honduras","Indien","Indonesien","Irak","Iran","Irland","Island","Israel","Italien","Jamaika","Japan","Jemen","Jordanien","Kambodscha","Kamerun","Kanada","Kap Verde","Kasachstan","Katar","Kenia","Kirgisistan","Kiribati","Kolumbien","Komoren","Kongo, Demokratische Republik","Kongo, Republik","Kroatien","Kuba","Kuwait","Laos","Lesotho","Lettland","Libanon","Liberia","Libyen","Liechtenstein","Litauen","Luxemburg","Madagaskar","Malawi","Malaysia","Malediven","Mali","Malta","Marokko","Marshallinseln","Mauretanien","Mauritius","Mexiko","Mikronesien","Moldau","Monaco","Mongolei","Montenegro","Mosambik","Myanmar","Namibia","Nauru","Nepal","Neuseeland","Nicaragua","Niederlande","Niger","Nigeria","Nordkorea","Nordmazedonien","Norwegen","Oman","Österreich","Osttimor","Pakistan","Palau","Panama","Papua-Neuguinea","Paraguay","Peru","Philippinen","Polen","Portugal","Ruanda","Rumänien","Russland","Salomonen","Sambia","Samoa","San Marino","São Tomé und Príncipe","Saudi-Arabien","Schweden","Schweiz","Senegal","Serbien","Seychellen","Sierra Leone","Simbabwe","Singapur","Slowakei","Slowenien","Somalia","Spanien","Sri Lanka","St. Kitts und Nevis","St. Lucia","St. Vincent und die Grenadinen","Südafrika","Sudan","Südkorea","Südsudan","Suriname","Syrien","Tadschikistan","Tansania","Thailand","Togo","Tonga","Trinidad und Tobago","Tschad","Tschechien","Tunesien","Türkei","Turkmenistan","Tuvalu","Uganda","Ukraine","Ungarn","Uruguay","Usbekistan","Vanuatu","Vatikanstadt","Venezuela","Vereinigte Arabische Emirate","Vereinigte Staaten","Vereinigtes Königreich","Vietnam","Zentralafrikanische Republik","Zypern"];

const NATIONALITAETEN = ["deutsch","afghanisch","ägyptisch","albanisch","algerisch","andorranisch","angolanisch","argentinisch","armenisch","aserbaidschanisch","äthiopisch","australisch","bahamaisch","bahrainisch","bangladeschisch","barbadisch","belgisch","belizisch","beninisch","bhutanisch","bolivianisch","bosnisch-herzegowinisch","botswanisch","brasilianisch","bruneiisch","bulgarisch","burkinisch","burundisch","chilenisch","chinesisch","costa-ricanisch","dänisch","dominicanisch","dominikanisch","dschibutisch","ecuadorianisch","salvadorianisch","ivorisch","eritreisch","estnisch","eswatinisch","fidschianisch","finnisch","französisch","gabunisch","gambisch","georgisch","ghanaisch","grenadisch","griechisch","guatemaltekisch","guineisch","guinea-bissauisch","guyanisch","haitianisch","honduranisch","indisch","indonesisch","irakisch","iranisch","irisch","isländisch","israelisch","italienisch","jamaikanisch","japanisch","jemenitisch","jordanisch","kambodschanisch","kamerunisch","kanadisch","kap-verdisch","kasachisch","katarisch","kenianisch","kirgisisch","kiribatisch","kolumbianisch","komorisch","kongolesisch","kroatisch","kubanisch","kuwaitisch","laotisch","lesothisch","lettisch","libanesisch","liberianisch","libysch","liechtensteinisch","litauisch","luxemburgisch","madagassisch","malawisch","malaysisch","maledivisch","malisch","maltesisch","marokkanisch","marshallisch","mauretanisch","mauritisch","mexikanisch","mikronesisch","moldauisch","monegassisch","mongolisch","montenegrinisch","mosambikanisch","myanmarisch","namibisch","nauruisch","nepalesisch","neuseeländisch","nicaraguanisch","niederländisch","nigrisch","nigerianisch","nordkoreanisch","nordmazedonisch","norwegisch","omanisch","österreichisch","osttimoresisch","pakistanisch","palauisch","panamaisch","papua-neuguineisch","paraguayisch","peruanisch","philippinisch","polnisch","portugiesisch","ruandisch","rumänisch","russisch","salomonisch","sambisch","samoanisch","san-marinesisch","são-toméisch","saudi-arabisch","schwedisch","schweizerisch","senegalesisch","serbisch","seychellisch","sierra-leonisch","simbabwisch","singapurisch","slowakisch","slowenisch","somalisch","spanisch","sri-lankisch","sudanesisch","südafrikanisch","südkoreanisch","südsudanesisch","surinamisch","syrisch","tadschikisch","tansanisch","thailändisch","togoisch","tongaisch","trinidadisch","tschadisch","tschechisch","tunesisch","türkisch","turkmenisch","tuvaluisch","ugandisch","ukrainisch","ungarisch","uruguayisch","usbekisch","vanuatuisch","vatikanisch","venezolanisch","emiratisch","US-amerikanisch","britisch","vietnamesisch","zentralafrikanisch","zyprisch"];

const KRANKENKASSEN = ["Techniker Krankenkasse (TK)","BARMER","DAK-Gesundheit","AOK Baden-Württemberg","AOK Bayern","AOK Bremen/Bremerhaven","AOK Hessen","AOK Niedersachsen","AOK Nordost","AOK NordWest","AOK PLUS","AOK Rheinland/Hamburg","AOK Rheinland-Pfalz/Saarland","AOK Sachsen-Anhalt","KKH Kaufmännische Krankenkasse","hkk Handelskrankenkasse","Knappschaft","HEK – Hanseatische Krankenkasse","BIG direkt gesund","IKK classic","IKK – Die Innovationskasse","IKK Südwest","IKK gesund plus","mkk – meine krankenkasse","Mobil Krankenkasse","SBK Siemens-Betriebskrankenkasse","VIACTIV Krankenkasse","pronova BKK","BKK VBU","BKK firmus","BKK Pfalz","Salus BKK","Bahn-BKK","Debeka BKK","energie-BKK","Novitas BKK","Audi BKK","Continentale BKK","BKK GILDEMEISTER SEIDENSTICKER"];

/* IBAN-Prüfung (Format + Prüfsumme nach Modulo-97). null = leer, true = gültig, false = ungültig */
function ibanValid(raw) {
  const s = String(raw || "").replace(/\s+/g, "").toUpperCase();
  if (!s) return null;
  if (!/^[A-Z]{2}\d{2}[A-Z0-9]{10,30}$/.test(s)) return false;
  const re = s.slice(4) + s.slice(0, 4);
  let rem = 0;
  for (const ch of re) {
    const val = /[A-Z]/.test(ch) ? (ch.charCodeAt(0) - 55).toString() : ch;
    for (const x of val) rem = (rem * 10 + (x.charCodeAt(0) - 48)) % 97;
  }
  return rem === 1;
}

/* ============================ Datenmodell ============================ */
function emptyPerson() {
  return {
    status:"Bewerber",
    bewerbungVom:"", anrede:"", nachname:"", vorname:"", geburtsname:"",
    strasse:"", plz:"", ort:"", telefon:"", email:"", geschlecht:"",
    staatsangehoerigkeit:"", geburtsdatum:"", geburtsort:"", geburtsland:"",
    familienstand:"", konfession:"", aufenthaltserlaubnisBis:"", arbeitserlaubnisBis:"",
    fuehrerschein:false, pkw:false, einsatzort:"", arbeitszeiten:"",
    einsatzbereiche:[], arbeitstage:[], uhrzeit:"",     // <-- neue Auswahlfelder (Bild)
    kinder:false, anzahlKinder:"",              // <-- Kinderabfrage im Bewerberstamm
    personalNr:"", passNr:"", eintrittsdatum:"", beschaeftigungsbeginn:"",
    eingestelltAls:"", taetigkeit:"", objekt:"", vertragsart:"unbefristet", befristetBis:"",
    stundenlohn:"15,00", wochenstunden:"", kreditinstitut:"", iban:"", bic:"", kontoinhaber:"",
    krankenkasse:"", krankenversichert:"ja", versicherungsnummer:"", versicherungsart:"", sozialversicherungNr:"",
    steuerId:"", steuerklasse:"", schwerbehinderung:false, gradMinderung:"",
    statusBeschaeftigung:"Arbeitnehmer(in)", rvBefreiung:false, notizen:"",
  };
}

/* ============================ Merge / Serienbrief ============================ */
function anredeGeehrt(p){
  if (p.anrede==="Herr") return "Sehr geehrter Herr "+(p.nachname||"");
  if (p.anrede==="Frau") return "Sehr geehrte Frau "+(p.nachname||"");
  return "Sehr geehrte/r "+(p.vorname||"")+" "+(p.nachname||"");
}
function fmtDate(iso){ if(!iso) return ""; const s=String(iso).split("-"); return s.length===3?`${s[2]}.${s[1]}.${s[0]}`:iso; }
const yn = (b)=> b?"ja":"nein";
function mergeMap(p,s){
  const h=new Date();
  const heute=`${String(h.getDate()).padStart(2,"0")}.${String(h.getMonth()+1).padStart(2,"0")}.${h.getFullYear()}`;
  const ort=(s.plzOrt||"Bremen").replace(/^\d+\s*/,"");
  return {
    firma:s.firma, firma_strasse:s.strasse, firma_plz_ort:s.plzOrt, firma_telefon:s.telefon, firma_fax:s.fax,
    geschaeftsfuehrer:s.geschaeftsfuehrer, personalleitung:s.personalleitung, ort, datum_heute:heute, ort_datum:ort+", "+heute,
    anrede:p.anrede, anrede_geehrt:anredeGeehrt(p), name_voll:`${p.vorname} ${p.nachname}`.trim(), plz_ort:`${p.plz} ${p.ort}`.trim(),
    nachname:p.nachname, vorname:p.vorname, geburtsname:p.geburtsname, strasse:p.strasse, telefon:p.telefon, email:p.email,
    geschlecht:p.geschlecht, staatsangehoerigkeit:p.staatsangehoerigkeit, geburtsdatum:fmtDate(p.geburtsdatum),
    geburtsort:p.geburtsort, geburtsland:p.geburtsland, familienstand:p.familienstand, konfession:p.konfession,
    aufenthaltserlaubnis_bis:fmtDate(p.aufenthaltserlaubnisBis), arbeitserlaubnis_bis:fmtDate(p.arbeitserlaubnisBis),
    fuehrerschein:yn(p.fuehrerschein), pkw:yn(p.pkw), einsatzort:p.einsatzort, arbeitszeiten:p.arbeitszeiten,
    einsatzbereiche:Array.isArray(p.einsatzbereiche)?p.einsatzbereiche.join(", "):"", arbeitstage:Array.isArray(p.arbeitstage)?p.arbeitstage.join(", "):"", uhrzeit:p.uhrzeit,
    kinder:(p.kinder||Number(p.anzahlKinder)>0)?"ja":"nein", anzahl_kinder:p.anzahlKinder||"0",
    personalnr:p.personalNr, passnr:p.passNr, eintrittsdatum:fmtDate(p.eintrittsdatum), beschaeftigungsbeginn:fmtDate(p.beschaeftigungsbeginn),
    eingestellt_als:p.eingestelltAls, taetigkeit:p.taetigkeit, objekt:p.objekt, vertragsart:p.vertragsart, befristet_bis:fmtDate(p.befristetBis),
    stundenlohn:p.stundenlohn, wochenstunden:p.wochenstunden, kreditinstitut:p.kreditinstitut, iban:p.iban, bic:p.bic,
    kontoinhaber:p.kontoinhaber||`${p.vorname} ${p.nachname}`.trim(), krankenkasse:p.krankenkasse, versicherungsnummer:p.versicherungsnummer, sozialversicherung_nr:p.sozialversicherungNr,
    steuer_id:p.steuerId, steuerklasse:p.steuerklasse, schwerbehinderung:yn(p.schwerbehinderung), grad_minderung:p.gradMinderung,
    status_beschaeftigung:p.statusBeschaeftigung,
  };
}
function fillTemplate(html,p,s){
  const m=mergeMap(p,s);
  return String(html).replace(/\{\{\s*([a-z_]+)\s*\}\}/g,(_,k)=>{
    const v=m[k];
    if (v===undefined) return `<span style="background:#fee2e2;color:#b91c1c;padding:0 3px;border-radius:3px">?${k}?</span>`;
    if (v==="" || v===null) return `<span style="border-bottom:1px solid #94a3b8;color:#94a3b8;padding:0 20px">&nbsp;</span>`;
    return String(v);
  });
}

/* ============================ UI-Bausteine ============================ */
function Btn({children,onClick,kind="primary",size="md",disabled,style,title,type}){
  const base={border:"1px solid transparent",borderRadius:8,cursor:disabled?"not-allowed":"pointer",fontWeight:600,opacity:disabled?0.5:1,whiteSpace:"nowrap"};
  const sizes={sm:{padding:"5px 10px",fontSize:13},md:{padding:"9px 16px",fontSize:14},lg:{padding:"12px 22px",fontSize:15}};
  const kinds={
    primary:{background:C.ice,color:C.ink,borderColor:C.iceDeep},
    dark:{background:C.ink,color:"#fff"},
    ghost:{background:"transparent",color:C.steel,borderColor:C.line},
    danger:{background:"#fff",color:C.danger,borderColor:"#fecaca"},
    good:{background:C.good,color:"#fff"},
  };
  return <button type={type||"button"} title={title} disabled={disabled} onClick={onClick} style={{...base,...sizes[size],...kinds[kind],...style}}>{children}</button>;
}
function Field({label,children,span}){
  return <label style={{display:"flex",flexDirection:"column",gap:4,gridColumn:span?`span ${span}`:undefined}}>
    <span style={{fontSize:12,fontWeight:600,color:C.muted}}>{label}</span>{children}</label>;
}
function TextInput(props){ return <input {...props} style={{...inputStyle,...(props.style||{})}} />; }
function Select({value,onChange,options,disabled}){
  return <select value={value} onChange={onChange} disabled={disabled} style={inputStyle}>
    {options.map(o=><option key={o.value??o} value={o.value??o}>{o.label??o}</option>)}</select>;
}
function Check({label,checked,onChange}){
  return <label style={{display:"flex",alignItems:"center",gap:8,fontSize:14,cursor:"pointer",padding:"6px 0"}}>
    <input type="checkbox" checked={checked} onChange={onChange} style={{width:17,height:17,accentColor:C.iceDeep}} />{label}</label>;
}
function Badge({status}){
  const m=status==="Mitarbeiter";
  return <span style={{fontSize:11,fontWeight:700,padding:"3px 9px",borderRadius:20,background:m?C.goodSoft:C.iceSoft,color:m?"#065f46":"#075985",border:`1px solid ${m?"#a7f3d0":"#bae6fd"}`}}>{status}</span>;
}
function Card({title,sub,children,right}){
  return <div style={{background:"#fff",border:`1px solid ${C.line}`,borderRadius:12,marginBottom:18,overflow:"hidden"}}>
    <div style={{padding:"14px 18px",borderBottom:`1px solid ${C.lineSoft}`,display:"flex",justifyContent:"space-between",alignItems:"center",background:C.paper}}>
      <div><div style={{fontWeight:700,fontSize:15}}>{title}</div>{sub&&<div style={{fontSize:12.5,color:C.muted,marginTop:2}}>{sub}</div>}</div>{right}
    </div>
    <div style={{padding:18}}>{children}</div></div>;
}
function Logo({size=30}){
  return <svg width={size} height={size} viewBox="0 0 48 48" style={{flexShrink:0}}>
    <polygon points="24,3 44,18 24,45 4,18" fill={C.ice}/><polygon points="24,3 44,18 24,18" fill="#7dd3fc"/>
    <polygon points="24,3 4,18 24,18" fill="#bae6fd"/><polygon points="4,18 24,18 24,45" fill={C.iceDeep}/>
    <polygon points="44,18 24,18 24,45" fill="#0284c7"/></svg>;
}

/* ============================ Kamera-Modal ============================ */
function CameraModal({onCapture,onClose}){
  const videoRef=useRef(null); const [err,setErr]=useState("");
  useEffect(()=>{
    let stream;
    if(!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia){
      setErr("Kamera ist hier nicht verfügbar (nur über eine sichere https-Adresse). Bitte „Datei…“ verwenden.");
      return;
    }
    navigator.mediaDevices.getUserMedia({video:{facingMode:"environment"}})
      .then(s=>{stream=s; if(videoRef.current){videoRef.current.srcObject=s; videoRef.current.play();}})
      .catch(()=>setErr("Kein Kamerazugriff möglich. Bitte Kamera erlauben oder „Datei…“ verwenden."));
    return ()=>{ if(stream) stream.getTracks().forEach(t=>t.stop()); };
  },[]);
  const snap=()=>{
    const v=videoRef.current; if(!v) return;
    const cv=document.createElement("canvas"); cv.width=v.videoWidth||640; cv.height=v.videoHeight||480;
    cv.getContext("2d").drawImage(v,0,0,cv.width,cv.height);
    cv.toBlob(b=>{ onCapture(b); onClose(); },"image/jpeg",0.9);
  };
  return <div style={{position:"fixed",inset:0,background:"rgba(0,0,0,.7)",display:"flex",alignItems:"center",justifyContent:"center",zIndex:100}} onClick={onClose}>
    <div style={{background:"#fff",borderRadius:14,padding:16,width:"90%",maxWidth:520}} onClick={e=>e.stopPropagation()}>
      <div style={{fontWeight:700,marginBottom:10}}>Foto aufnehmen</div>
      {err? <div style={{color:C.danger,padding:20}}>{err}</div> :
        <video ref={videoRef} style={{width:"100%",borderRadius:10,background:"#000"}} muted playsInline/>}
      <div style={{display:"flex",gap:8,justifyContent:"flex-end",marginTop:12}}>
        <Btn kind="ghost" onClick={onClose}>Abbrechen</Btn>
        <Btn onClick={snap} disabled={!!err}>Aufnehmen</Btn>
      </div>
    </div></div>;
}

/* ============================ Foto/Dokument-Slot ============================ */
function PhotoSlot({personId,slot,items,onChanged,onBeforeUpload}){
  const [cam,setCam]=useState(false); const [busy,setBusy]=useState(false);
  const fileRef=useRef(null);
  const doUpload=async(file)=>{
    setBusy(true);
    try{ if(onBeforeUpload) await onBeforeUpload();      // Eingaben zuerst sichern -> kein Datenverlust
      const fd=new FormData(); fd.append("category",slot.cat); fd.append("file",file,file.name||"foto.jpg");
      await api.upload(`/api/persons/${personId}/attachments`,fd); await onChanged();
    }catch(e){ alert("Upload fehlgeschlagen: "+e.message); } finally{ setBusy(false); }
  };
  const del=async(id)=>{ if(!confirm("Datei löschen?"))return; await api.del(`/api/attachments/${id}`); await onChanged(); };
  const mine=items.filter(a=>a.category===slot.cat);
  return <div style={{border:`1px solid ${C.line}`,borderRadius:10,padding:12,background:"#fff"}}>
    <div style={{fontSize:12.5,fontWeight:700,color:C.steel,marginBottom:8}}>{slot.label}</div>
    <div style={{display:"flex",flexWrap:"wrap",gap:8,marginBottom:mine.length?10:0}}>
      {mine.map(a=>(
        <div key={a.id} style={{position:"relative"}}>
          <a href={`/api/attachments/${a.id}/file`} target="_blank" rel="noreferrer">
            {String(a.mime).includes("pdf")
              ? <div style={{width:78,height:78,borderRadius:8,border:`1px solid ${C.line}`,display:"flex",alignItems:"center",justifyContent:"center",fontSize:11,color:C.muted}}>PDF</div>
              : <img src={`/api/attachments/${a.id}/file`} alt="" style={{width:78,height:78,objectFit:"cover",borderRadius:8,border:`1px solid ${C.line}`}}/>}
          </a>
          <button onClick={()=>del(a.id)} title="Löschen" style={{position:"absolute",top:-7,right:-7,width:20,height:20,borderRadius:10,border:"none",background:C.danger,color:"#fff",cursor:"pointer",fontSize:12,lineHeight:"20px",padding:0}}>×</button>
        </div>
      ))}
    </div>
    <div style={{display:"flex",gap:6}}>
      <Btn size="sm" kind="ghost" disabled={busy} onClick={()=>setCam(true)}>📷 Aufnehmen</Btn>
      <Btn size="sm" kind="ghost" disabled={busy} onClick={()=>fileRef.current.click()}>Datei…</Btn>
      <input ref={fileRef} type="file" accept="image/*,application/pdf" style={{display:"none"}}
        onChange={e=>{ const f=e.target.files[0]; if(f) doUpload(f); e.target.value=""; }}/>
    </div>
    {cam && <CameraModal onCapture={b=>doUpload(new File([b],"foto.jpg",{type:"image/jpeg"}))} onClose={()=>setCam(false)}/>}
  </div>;
}

function PhotosSection({personId,attachments,reload,onBeforeUpload}){
  const fileRef=useRef(null); const [busy,setBusy]=useState(false);
  const uploadOther=async(file)=>{ setBusy(true);
    try{ if(onBeforeUpload) await onBeforeUpload();
      const fd=new FormData(); fd.append("category","sonstiges"); fd.append("file",file,file.name||"dokument"); await api.upload(`/api/persons/${personId}/attachments`,fd); await reload(); }
    catch(e){ alert("Upload fehlgeschlagen: "+e.message); } finally{ setBusy(false); } };
  const del=async(id)=>{ if(!confirm("Datei löschen?"))return; await api.del(`/api/attachments/${id}`); await reload(); };
  const others=attachments.filter(a=>a.category==="sonstiges");
  return <Card title="Fotos & Dokumente" sub="Aufnehmen (Kamera) oder als Datei hochladen. Speicherung erfolgt in der Datenbank/auf dem Server.">
    <div style={{display:"grid",gridTemplateColumns:"repeat(auto-fill,minmax(220px,1fr))",gap:12}}>
      {PHOTO_SLOTS.map(s=><PhotoSlot key={s.cat} personId={personId} slot={s} items={attachments} onChanged={reload} onBeforeUpload={onBeforeUpload}/>)}
    </div>
    <div style={{marginTop:16,borderTop:`1px dashed ${C.line}`,paddingTop:14}}>
      <div style={{display:"flex",justifyContent:"space-between",alignItems:"center",marginBottom:10}}>
        <div style={{fontSize:12.5,fontWeight:700,color:C.steel}}>Weitere Dokumente</div>
        <Btn size="sm" kind="ghost" disabled={busy} onClick={()=>fileRef.current.click()}>+ Datei hinzufügen</Btn>
        <input ref={fileRef} type="file" accept="image/*,application/pdf" style={{display:"none"}}
          onChange={e=>{ const f=e.target.files[0]; if(f) uploadOther(f); e.target.value=""; }}/>
      </div>
      {others.length===0? <div style={{fontSize:13,color:C.muted}}>Keine weiteren Dokumente.</div> :
        <div style={{display:"flex",flexWrap:"wrap",gap:8}}>
          {others.map(a=><div key={a.id} style={{position:"relative"}}>
            <a href={`/api/attachments/${a.id}/file`} target="_blank" rel="noreferrer">
              {String(a.mime).includes("pdf")? <div style={{width:90,height:90,borderRadius:8,border:`1px solid ${C.line}`,display:"flex",alignItems:"center",justifyContent:"center",fontSize:11,color:C.muted,textAlign:"center",padding:4}}>{a.original_name||"PDF"}</div>
                : <img src={`/api/attachments/${a.id}/file`} alt="" style={{width:90,height:90,objectFit:"cover",borderRadius:8,border:`1px solid ${C.line}`}}/>}
            </a>
            <button onClick={()=>del(a.id)} style={{position:"absolute",top:-7,right:-7,width:20,height:20,borderRadius:10,border:"none",background:C.danger,color:"#fff",cursor:"pointer",fontSize:12,padding:0}}>×</button>
          </div>)}
        </div>}
    </div>
  </Card>;
}

/* ============================ Unterschrift ============================ */
function SignatureSection({personId,attachments,reload,onBeforeUpload,flash,ctlRef}){
  const sig=attachments.find(a=>a.category==="unterschrift");
  const canvasRef=useRef(null);
  const drawing=useRef(false); const last=useRef(null); const used=useRef(false);
  const [busy,setBusy]=useState(false); const [redo,setRedo]=useState(false);
  const showPad = !sig || redo;

  useEffect(()=>{
    if(!showPad) return;
    const c=canvasRef.current; if(!c) return;
    const g=c.getContext("2d");
    g.clearRect(0,0,c.width,c.height);
    g.strokeStyle="#0b1f3a"; g.lineWidth=2.6; g.lineJoin="round"; g.lineCap="round";
    used.current=false;
  },[showPad]);

  const posOf=(e)=>{ const c=canvasRef.current; const r=c.getBoundingClientRect();
    return { x:(e.clientX-r.left)*(c.width/r.width), y:(e.clientY-r.top)*(c.height/r.height) }; };
  const down=(e)=>{ e.preventDefault(); const c=canvasRef.current; try{c.setPointerCapture(e.pointerId);}catch{}
    drawing.current=true; last.current=posOf(e); };
  const move=(e)=>{ if(!drawing.current)return; e.preventDefault();
    const g=canvasRef.current.getContext("2d"); const p=posOf(e);
    g.beginPath(); g.moveTo(last.current.x,last.current.y); g.lineTo(p.x,p.y); g.stroke();
    last.current=p; used.current=true; };
  const up=()=>{ drawing.current=false; };
  const clearPad=()=>{ const c=canvasRef.current; c.getContext("2d").clearRect(0,0,c.width,c.height); used.current=false; };

  const saveSig=async()=>{
    if(!used.current){ alert("Bitte zuerst im Feld unterschreiben."); return; }
    setBusy(true);
    try{
      if(onBeforeUpload) await onBeforeUpload();               // Formulardaten sichern
      if(sig){ try{ await api.del(`/api/attachments/${sig.id}`);}catch{} }  // alte ersetzen
      const blob=await new Promise(r=>canvasRef.current.toBlob(r,"image/png"));
      const fd=new FormData(); fd.append("category","unterschrift"); fd.append("file",blob,"unterschrift.png");
      await api.upload(`/api/persons/${personId}/attachments`,fd);
      await reload(); setRedo(false); flash&&flash("Unterschrift gespeichert");
    }catch(e){ alert("Konnte Unterschrift nicht speichern: "+e.message); }
    finally{ setBusy(false); }
  };
  const delSig=async()=>{ if(!sig)return; if(!confirm("Unterschrift löschen?"))return;
    if(onBeforeUpload) await onBeforeUpload(); await api.del(`/api/attachments/${sig.id}`); await reload(); setRedo(false); };

  // Steuerung für die Eltern-Maske: „Speichern“ nimmt eine noch nicht gespeicherte Unterschrift automatisch mit
  useEffect(()=>{
    if(!ctlRef) return;
    ctlRef.current={ hasUnsaved:()=> !!(showPad && used.current), flush: async()=>{ if(showPad && used.current) await saveSig(); } };
    return ()=>{ if(ctlRef) ctlRef.current=null; };
  });

  return <Card title="Unterschrift des Mitarbeiters" sub="Der Mitarbeiter unterschreibt direkt im Feld (Maus, Finger oder Stift). Die Unterschrift wird in Dokumente mit dem Platzhalter {{%unterschrift}} eingesetzt – aktuell im DSGVO-Dokument.">
    {sig && !redo &&
      <div style={{display:"flex",alignItems:"center",gap:16,flexWrap:"wrap"}}>
        <img src={`/api/attachments/${sig.id}/file`} alt="Unterschrift"
          style={{maxWidth:320,maxHeight:120,border:`1px solid ${C.line}`,borderRadius:8,background:"#fff",padding:6}}/>
        <div style={{display:"flex",gap:8}}>
          <Btn size="sm" kind="ghost" onClick={()=>setRedo(true)}>Neu unterschreiben</Btn>
          <Btn size="sm" kind="danger" onClick={delSig}>Löschen</Btn>
        </div>
      </div>}

    {showPad &&
      <div>
        <canvas ref={canvasRef} width={520} height={170}
          onPointerDown={down} onPointerMove={move} onPointerUp={up} onPointerLeave={up}
          style={{width:"100%",maxWidth:520,height:"auto",aspectRatio:"520 / 170",
                  border:`1px dashed ${C.steel}`,borderRadius:10,background:"#fff",touchAction:"none",cursor:"crosshair",display:"block"}}/>
        <div style={{display:"flex",gap:8,marginTop:10}}>
          <Btn size="sm" disabled={busy} onClick={saveSig}>Unterschrift speichern</Btn>
          <Btn size="sm" kind="ghost" disabled={busy} onClick={clearPad}>Feld leeren</Btn>
          {sig && <Btn size="sm" kind="ghost" disabled={busy} onClick={()=>setRedo(false)}>Abbrechen</Btn>}
        </div>
      </div>}
  </Card>;
}

/* ============================ Login ============================ */
function Login({onLogin}){
  const [u,setU]=useState(""); const [p,setP]=useState(""); const [err,setErr]=useState(""); const [busy,setBusy]=useState(false);
  const submit=async()=>{ setBusy(true); setErr("");
    try{ const r=await api.post("/api/login",{username:u,password:p}); onLogin(r.user); }
    catch(e){ setErr(e.message); } finally{ setBusy(false); } };
  return <div style={{minHeight:"100vh",display:"flex",alignItems:"center",justifyContent:"center",background:`linear-gradient(135deg, ${C.ink}, ${C.slate})`,padding:20}}>
    <div style={{width:"100%",maxWidth:380,background:"#fff",borderRadius:16,padding:32,boxShadow:"0 20px 60px rgba(0,0,0,.35)"}}>
      <div style={{display:"flex",alignItems:"center",gap:12}}>
        <Logo size={40}/><div>
          <div style={{fontWeight:800,fontSize:22,letterSpacing:"-.01em"}}>Work4Dia</div>
          <div style={{fontSize:11,color:C.muted,letterSpacing:".14em",textTransform:"uppercase"}}>Diamant Gebäudereinigung</div>
        </div></div>
      <p style={{color:C.muted,fontSize:13,margin:"14px 0 22px"}}>Bitte mit Benutzername und Passwort anmelden.</p>
      <div style={{display:"flex",flexDirection:"column",gap:12}}>
        <Field label="Benutzername"><TextInput value={u} autoFocus onChange={e=>setU(e.target.value)} onKeyDown={e=>e.key==="Enter"&&submit()}/></Field>
        <Field label="Passwort"><TextInput type="password" value={p} onChange={e=>setP(e.target.value)} onKeyDown={e=>e.key==="Enter"&&submit()}/></Field>
        {err&&<div style={{color:C.danger,fontSize:13}}>{err}</div>}
        <Btn size="lg" disabled={busy} onClick={submit} style={{marginTop:4}}>{busy?"…":"Anmelden"}</Btn>
      </div>
      <div style={{marginTop:20,fontSize:11.5,color:C.muted,background:C.paper,border:`1px solid ${C.line}`,borderRadius:8,padding:"8px 10px"}}>
        Erststart als Administrator: <b>admin</b> / <b>admin123</b> – danach unter „Benutzer“ ändern.</div>
    </div></div>;
}

/* ============================ Personenliste ============================ */
function PersonList({persons,onOpen,onNew}){
  const [q,setQ]=useState(""); const [f,setF]=useState("alle");
  const [page,setPage]=useState(0);
  const PAGE=25;
  const rows=persons.filter(p=>{
    if(f!=="alle"&&p.status!==f) return false;
    const s=`${p.vorname} ${p.nachname} ${p.ort} ${p.personalNr} ${p.einsatzort}`.toLowerCase();
    return s.includes(q.toLowerCase());
  });
  useEffect(()=>{ setPage(0); },[q,f,persons.length]);
  const pages=Math.max(1,Math.ceil(rows.length/PAGE));
  const cur=Math.min(page,pages-1);
  const shown=rows.slice(cur*PAGE,cur*PAGE+PAGE);
  const from=rows.length?cur*PAGE+1:0, to=cur*PAGE+shown.length;
  return <div>
    <div style={{display:"flex",gap:12,alignItems:"center",marginBottom:18,flexWrap:"wrap"}}>
      <TextInput placeholder="Suche nach Name, Ort, Pers.-Nr., Einsatzort …" value={q} onChange={e=>setQ(e.target.value)} style={{maxWidth:380}}/>
      <div style={{display:"flex",gap:6}}>{["alle","Bewerber","Mitarbeiter"].map(x=><Btn key={x} size="sm" kind={f===x?"dark":"ghost"} onClick={()=>setF(x)}>{x==="alle"?"Alle":x}</Btn>)}</div>
      <div style={{flex:1}}/><Btn onClick={onNew}>+ Neue Person</Btn>
    </div>
    {rows.length===0? <div style={{textAlign:"center",padding:"60px 20px",color:C.muted,border:`1px dashed ${C.line}`,borderRadius:12}}>Noch keine Einträge. Mit „Neue Person“ einen Bewerber anlegen.</div> :
    <div>
    <div style={{background:"#fff",border:`1px solid ${C.line}`,borderRadius:12,overflow:"hidden"}}>
      <table style={{width:"100%",borderCollapse:"collapse",fontSize:14}}>
        <thead><tr style={{background:C.paper,textAlign:"left",color:C.muted,fontSize:12,textTransform:"uppercase"}}>
          <th style={{padding:"11px 16px"}}>Name</th><th style={{padding:"11px 16px"}}>Ort</th><th style={{padding:"11px 16px"}}>Einsatzort</th><th style={{padding:"11px 16px"}}>Pers.-Nr.</th><th style={{padding:"11px 16px"}}>Status</th>
        </tr></thead>
        <tbody>{shown.map(p=>(
          <tr key={p.id} onClick={()=>onOpen(p.id)} style={{borderTop:`1px solid ${C.lineSoft}`,cursor:"pointer"}}
            onMouseEnter={e=>e.currentTarget.style.background=C.paper} onMouseLeave={e=>e.currentTarget.style.background="#fff"}>
            <td style={{padding:"12px 16px",fontWeight:600}}>{p.nachname||"—"}, {p.vorname}</td>
            <td style={{padding:"12px 16px",color:C.steel}}>{p.plz} {p.ort}</td>
            <td style={{padding:"12px 16px",color:C.steel}}>{p.einsatzort||"—"}</td>
            <td style={{padding:"12px 16px",color:C.steel}}>{p.personalNr||"—"}</td>
            <td style={{padding:"12px 16px"}}><Badge status={p.status}/></td>
          </tr>))}</tbody>
      </table></div>
      <div style={{display:"flex",justifyContent:"space-between",alignItems:"center",marginTop:12,flexWrap:"wrap",gap:10}}>
        <div style={{fontSize:13,color:C.muted}}>{from}–{to} von {rows.length}</div>
        {pages>1 && <div style={{display:"flex",alignItems:"center",gap:8}}>
          <Btn size="sm" kind="ghost" disabled={cur<=0} onClick={()=>setPage(cur-1)}>◀ Zurück</Btn>
          <div style={{fontSize:13,color:C.steel,minWidth:96,textAlign:"center"}}>Seite {cur+1} von {pages}</div>
          <Btn size="sm" kind="ghost" disabled={cur>=pages-1} onClick={()=>setPage(cur+1)}>Weiter ▶</Btn>
        </div>}
      </div>
    </div>}
  </div>;
}

/* ============================ Personen-Formular ============================ */
function PersonForm({personId,onBack,onSaved,onDocs,flash,guardRef}){
  const [p,setP]=useState(null); const [atts,setAtts]=useState([]);
  const pRef=useRef(p); pRef.current=p;            // immer aktueller Formularstand (für sicheres Zwischenspeichern)
  const dirtyRef=useRef(false);                    // ungespeicherte Änderungen?
  const sigCtl=useRef(null);                        // Steuerung des Unterschriftsfeldes
  const mark=()=>{ dirtyRef.current=true; };
  const set=(k)=>(e)=>{ mark(); setP({...p,[k]:e.target.value}); };
  const setC=(k)=>(e)=>{ mark(); setP({...p,[k]:e.target.checked}); };
  const setArr=(k,val)=>{ mark(); const arr=Array.isArray(p[k])?p[k]:[]; setP({...p,[k]: arr.includes(val)? arr.filter(x=>x!==val) : [...arr,val]}); };
  const setStatus=(s)=>{ mark(); setP({...p,status:s}); };
  const load=useCallback(async()=>{ const d=await api.get(`/api/persons/${personId}`); const {attachments,...rest}=d; setP(rest); setAtts(attachments||[]); dirtyRef.current=false; },[personId]);
  const loadAtts=useCallback(async()=>{ const d=await api.get(`/api/persons/${personId}`); setAtts(d.attachments||[]); },[personId]);
  // Formulardaten sichern (force: bei Auto-Sicherung vor Uploads -> blockiert keinen Foto-Upload)
  const saveQuiet=useCallback(async()=>{
    if(!pRef.current) return;
    const res=await api.put(`/api/persons/${personId}`,{...pRef.current,force:true});
    if(res&&res.updatedAt){ pRef.current.updatedAt=res.updatedAt; setP(prev=>prev?{...prev,updatedAt:res.updatedAt}:prev); }
  },[personId]);
  useEffect(()=>{ load(); },[load]);
  // Warnung beim Verlassen der Seite (Tab schließen/neu laden) bei ungespeicherten Daten
  useEffect(()=>{
    const h=(e)=>{ if(dirtyRef.current || (sigCtl.current&&sigCtl.current.hasUnsaved())){ e.preventDefault(); e.returnValue=""; } };
    window.addEventListener("beforeunload",h);
    return ()=>window.removeEventListener("beforeunload",h);
  },[]);
  // „Ungespeichert?“ nach oben melden, damit auch Menü-Wechsel/Abmelden warnen
  useEffect(()=>{
    if(guardRef) guardRef.current=()=> !!(dirtyRef.current || (sigCtl.current&&sigCtl.current.hasUnsaved()));
    return ()=>{ if(guardRef) guardRef.current=null; };
  });
  if(!p) return <div style={{color:C.muted}}>Lädt …</div>;
  const isMit=p.status==="Mitarbeiter";
  const ibOk=ibanValid(p.iban);
  const persist=async(force)=>{
    const res=await api.put(`/api/persons/${personId}`, force?{...pRef.current,force:true}:pRef.current);
    if(res&&res.updatedAt){ if(pRef.current) pRef.current.updatedAt=res.updatedAt; setP(prev=>prev?{...prev,updatedAt:res.updatedAt}:prev); }
    return res;
  };
  const save=async(goDocs)=>{
    try{ if(sigCtl.current && sigCtl.current.hasUnsaved()) await sigCtl.current.flush(); }catch(e){}
    try{ await persist(false); }
    catch(e){
      if(e.status===409){
        if(confirm("Achtung: Dieser Datensatz wurde zwischenzeitlich von einer anderen Person geändert.\n\nOK  = Ihre Eingaben speichern und die andere Version überschreiben.\nAbbrechen = nicht speichern (Seite neu laden zeigt die aktuelle Version).")){
          await persist(true);
        } else { return; }
      } else { alert("Speichern fehlgeschlagen: "+(e.message||e)); return; }
    }
    dirtyRef.current=false; flash("Gespeichert");
    if(goDocs) onDocs(pRef.current); else onSaved();
  };
  const del=async()=>{ if(!confirm("Diesen Datensatz und alle zugehörigen Dateien löschen?"))return; await api.del(`/api/persons/${personId}`); flash("Gelöscht"); onSaved(); };
  const guardedBack=()=>{
    if((dirtyRef.current || (sigCtl.current&&sigCtl.current.hasUnsaved())) &&
       !confirm("Es gibt ungespeicherte Änderungen (ggf. auch eine noch nicht gespeicherte Unterschrift). Trotzdem zurück ohne Speichern?")) return;
    onBack();
  };

  return <div>
    <div style={{display:"flex",justifyContent:"space-between",alignItems:"center",marginBottom:18,flexWrap:"wrap",gap:12}}>
      <div style={{display:"flex",alignItems:"center",gap:14}}>
        <Btn kind="ghost" size="sm" onClick={guardedBack}>← Zurück</Btn>
        <div style={{fontWeight:700,fontSize:18}}>{p.vorname||p.nachname?`${p.vorname} ${p.nachname}`:"Neue Person"}</div>
        <Badge status={p.status}/>
      </div>
      <div style={{display:"flex",gap:8}}><Btn kind="danger" size="sm" onClick={del}>Löschen</Btn><Btn kind="good" onClick={()=>save(false)}>Speichern</Btn></div>
    </div>

    <Card title="Status" sub="Bewerberdaten werden gespeichert und vorgehalten. Bei Einstellung auf „Mitarbeiter“ umstellen.">
      <div style={{display:"flex",gap:10,alignItems:"center",flexWrap:"wrap"}}>
        <Btn kind={!isMit?"dark":"ghost"} onClick={()=>setStatus("Bewerber")}>Bewerber</Btn>
        <Btn kind={isMit?"good":"ghost"} onClick={()=>setStatus("Mitarbeiter")}>Mitarbeiter (eingestellt)</Btn>
      </div>
    </Card>

    <Card title="Bewerbungs- & Stammdaten">
      <div style={GRID}>
        <Field label="Bewerbung vom"><TextInput type="date" value={p.bewerbungVom} onChange={set("bewerbungVom")}/></Field>
        <Field label="Anrede"><Select value={p.anrede} onChange={set("anrede")} options={[{value:"",label:"—"},"Herr","Frau","Divers"]}/></Field>
        <Field label="Nachname"><TextInput value={p.nachname} onChange={set("nachname")}/></Field>
        <Field label="Vorname"><TextInput value={p.vorname} onChange={set("vorname")}/></Field>
        <Field label="Geburtsname"><TextInput value={p.geburtsname} onChange={set("geburtsname")}/></Field>
        <Field label="Geschlecht"><Select value={p.geschlecht} onChange={set("geschlecht")} options={[{value:"",label:"—"},"männlich","weiblich","divers"]}/></Field>
        <Field label="Straße"><TextInput value={p.strasse} onChange={set("strasse")}/></Field>
        <div style={{display:"grid",gridTemplateColumns:"90px 1fr",gap:10}}>
          <Field label="PLZ"><TextInput value={p.plz} onChange={set("plz")}/></Field><Field label="Ort"><TextInput value={p.ort} onChange={set("ort")}/></Field></div>
        <Field label="Telefon"><TextInput value={p.telefon} onChange={set("telefon")}/></Field>
        <Field label="E-Mail"><TextInput value={p.email} onChange={set("email")}/></Field>
        <Field label="Geburtsdatum"><TextInput type="date" value={p.geburtsdatum} onChange={set("geburtsdatum")}/></Field>
        <Field label="Geburtsort"><TextInput value={p.geburtsort} onChange={set("geburtsort")}/></Field>
        <Field label="Geburtsland"><Select value={p.geburtsland} onChange={set("geburtsland")} options={[{value:"",label:"—"},...LAENDER]}/></Field>
        <Field label="Staatsangehörigkeit"><Select value={p.staatsangehoerigkeit} onChange={set("staatsangehoerigkeit")} options={[{value:"",label:"—"},...NATIONALITAETEN]}/></Field>
        <Field label="Familienstand"><Select value={p.familienstand} onChange={set("familienstand")} options={[{value:"",label:"—"},...FAMILIENSTAND]}/></Field>
        <Field label="Konfession"><Select value={p.konfession} onChange={set("konfession")} options={[{value:"",label:"—"},"ev","rk","keine","sonstige"]}/></Field>
        <Field label="Aufenthaltserlaubnis bis"><TextInput type="date" value={p.aufenthaltserlaubnisBis} onChange={set("aufenthaltserlaubnisBis")}/></Field>
        <Field label="Arbeitserlaubnis bis"><TextInput type="date" value={p.arbeitserlaubnisBis} onChange={set("arbeitserlaubnisBis")}/></Field>
        <Field label="Möglicher Einsatzort"><TextInput value={p.einsatzort} onChange={set("einsatzort")} placeholder="z. B. Lürssen Werft"/></Field>
        <div style={{display:"flex",gap:24,alignItems:"center"}}>
          <Check label="Führerschein" checked={p.fuehrerschein} onChange={setC("fuehrerschein")}/>
          <Check label="eigener PKW" checked={p.pkw} onChange={setC("pkw")}/></div>
        <div style={{display:"flex",gap:16,alignItems:"end"}}>
          <div style={{flex:"0 0 auto"}}><Check label="Kinder" checked={p.kinder} onChange={setC("kinder")}/></div>
          <Field label="Anzahl Kinder"><TextInput type="number" min="0" value={p.anzahlKinder} onChange={set("anzahlKinder")} disabled={!p.kinder} style={{width:110}}/></Field>
        </div>
        <div style={{gridColumn:"span 2",borderTop:`1px solid ${C.lineSoft}`,paddingTop:12,marginTop:2}}>
          <div style={{fontSize:12,fontWeight:600,color:C.muted,marginBottom:6}}>Mögliche Einsatzbereiche</div>
          <div style={{display:"flex",gap:22,flexWrap:"wrap"}}>
            {EINSATZBEREICHE.map(x=><Check key={x} label={x} checked={(p.einsatzbereiche||[]).includes(x)} onChange={()=>setArr("einsatzbereiche",x)}/>)}
          </div>
        </div>
        <div style={{gridColumn:"span 2"}}>
          <div style={{fontSize:12,fontWeight:600,color:C.muted,marginBottom:6}}>Mögliche Arbeitszeiten</div>
          <div style={{display:"flex",gap:22,flexWrap:"wrap"}}>
            {ARBEITSTAGE.map(x=><Check key={x} label={x} checked={(p.arbeitstage||[]).includes(x)} onChange={()=>setArr("arbeitstage",x)}/>)}
          </div>
        </div>
        <Field label="Uhrzeit"><TextInput value={p.uhrzeit} onChange={set("uhrzeit")} placeholder="z. B. 17:00–20:00 Uhr"/></Field>
      </div>
    </Card>

    <Card title="Einstellung / Personalstammdaten" sub={isMit?"Für die Vertragserstellung relevant.":"Wird bei Einstellung ausgefüllt (Status oben auf „Mitarbeiter“ setzen)."}>
      <div style={{...GRID,opacity:isMit?1:0.6}}>
        <Field label="Personal-Nr."><TextInput value={p.personalNr} onChange={set("personalNr")}/></Field>
        <Field label="Pass-Nr."><TextInput value={p.passNr} onChange={set("passNr")}/></Field>
        <Field label="Eintrittsdatum"><TextInput type="date" value={p.eintrittsdatum} onChange={set("eintrittsdatum")}/></Field>
        <Field label="Beschäftigungsbeginn"><TextInput type="date" value={p.beschaeftigungsbeginn} onChange={set("beschaeftigungsbeginn")}/></Field>
        <Field label="Eingestellt als"><Select value={p.eingestelltAls} onChange={set("eingestelltAls")} options={[{value:"",label:"—"},...EINGESTELLT_ALS]}/></Field>
        <Field label="Tätigkeitsbereich"><TextInput value={p.taetigkeit} onChange={set("taetigkeit")} placeholder="z. B. Unterhaltsreinigung"/></Field>
        <Field label="Objekt"><TextInput value={p.objekt} onChange={set("objekt")} placeholder="z. B. Lürssen Werft, Halle 3"/></Field>
        <Field label="Vertragsart"><Select value={p.vertragsart} onChange={set("vertragsart")} options={[{value:"unbefristet",label:"unbefristet"},{value:"befristet",label:"befristet"}]}/></Field>
        <Field label="Befristet bis"><TextInput type="date" value={p.befristetBis} onChange={set("befristetBis")} disabled={p.vertragsart!=="befristet"}/></Field>
        <Field label="Stundenlohn (€ brutto)"><TextInput value={p.stundenlohn} onChange={set("stundenlohn")} placeholder="z. B. 15,00"/></Field>
        <Field label="Wochenstunden"><TextInput value={p.wochenstunden} onChange={set("wochenstunden")} placeholder="z. B. 20"/></Field>
        <Field label="Kreditinstitut / Geldinstitut"><TextInput value={p.kreditinstitut} onChange={set("kreditinstitut")}/></Field>
        <Field label="Kontoinhaber/in"><TextInput value={p.kontoinhaber} onChange={set("kontoinhaber")} placeholder="leer = Name der Person"/></Field>
        <Field label="IBAN">
          <TextInput value={p.iban} onChange={set("iban")} placeholder="z. B. DE12 3456 7890 1234 5678 90"
            style={ibOk===false?{borderColor:C.danger,background:"#fef2f2"}:ibOk===true?{borderColor:C.good}:{}}/>
          {ibOk===false && <span style={{fontSize:11.5,color:C.danger,marginTop:3}}>Ungültige IBAN – bitte Format und Prüfziffer prüfen.</span>}
          {ibOk===true && <span style={{fontSize:11.5,color:C.good,marginTop:3}}>IBAN gültig ✓</span>}
        </Field>
        <Field label="BIC"><TextInput value={p.bic} onChange={set("bic")}/></Field>
        <Field label="Krankenkasse"><TextInput list="kk-liste" value={p.krankenkasse} onChange={set("krankenkasse")} placeholder="auswählen oder eintippen"/></Field>
        <Field label="Versicherungsnummer"><TextInput value={p.versicherungsnummer} onChange={set("versicherungsnummer")}/></Field>
        <Field label="Sozialversicherungs-Nr."><TextInput value={p.sozialversicherungNr} onChange={set("sozialversicherungNr")}/></Field>
        <Field label="Steuer-ID"><TextInput value={p.steuerId} onChange={set("steuerId")}/></Field>
        <Field label="Steuerklasse"><Select value={p.steuerklasse} onChange={set("steuerklasse")} options={[{value:"",label:"—"},...STEUERKLASSEN]}/></Field>
        <datalist id="kk-liste">{KRANKENKASSEN.map(k=><option key={k} value={k}/>)}</datalist>
        <Field label="Status bei Beginn"><Select value={p.statusBeschaeftigung} onChange={set("statusBeschaeftigung")} options={["Arbeitnehmer(in)","geringfügig Beschäftigte(r)","Schüler(in)/Student(in)","Rentner(in)","Arbeitssuchende(r)","Sonstige"]}/></Field>
        <div style={{display:"flex",gap:20,flexWrap:"wrap",alignItems:"center"}}>
          <Check label="Schwerbehinderung" checked={p.schwerbehinderung} onChange={setC("schwerbehinderung")}/>
          <Check label="Antrag RV-Befreiung (Minijob)" checked={p.rvBefreiung} onChange={setC("rvBefreiung")}/></div>
        <Field label="Grad der Behinderung (%)"><TextInput value={p.gradMinderung} onChange={set("gradMinderung")} disabled={!p.schwerbehinderung}/></Field>
      </div>
    </Card>

    <PhotosSection personId={personId} attachments={atts} reload={loadAtts} onBeforeUpload={saveQuiet}/>

    <SignatureSection personId={personId} attachments={atts} reload={loadAtts} onBeforeUpload={saveQuiet} flash={flash} ctlRef={sigCtl}/>

    <Card title="Notizen"><textarea value={p.notizen} onChange={set("notizen")} rows={3} style={{...inputStyle,resize:"vertical"}} placeholder="Interne Notizen …"/></Card>

    <div style={{display:"flex",justifyContent:"space-between",alignItems:"center",background:"#fff",border:`1px solid ${C.line}`,borderRadius:12,padding:16}}>
      <div style={{fontSize:13.5,color:C.muted}}>{isMit?"Person ist eingestellt – Dokumente können erstellt werden.":"Als Bewerber gespeichert. Für Verträge zuerst auf „Mitarbeiter“ umstellen."}</div>
      <Btn size="lg" disabled={!isMit} onClick={()=>save(true)}>Speichern & Dokumente erstellen →</Btn>
    </div>
  </div>;
}

/* ============================ Dokumente ============================ */
function fmtKB(n){ n=Number(n)||0; return n>=1048576? (n/1048576).toFixed(1)+" MB" : Math.max(1,Math.round(n/1024))+" KB"; }

const DOC_CATS=["Anschreiben","Vertrag","Anlagen","Kunden","Sonstiges"];
function groupDocs(list){
  const norm=(k)=> DOC_CATS.includes(k)?k:"Sonstiges";
  return DOC_CATS.map(c=>[c, list.filter(t=>norm(t.kategorie)===c)]);
}

function DocumentsView({person,settings,onBack,flash}){
  const [docTpls,setDocTpls]=useState([]);
  const [selDoc,setSelDoc]=useState({});     // Original-Word-Vorlagen (docx)
  const [atts,setAtts]=useState(person.attachments||[]);
  const [busy,setBusy]=useState(false);

  useEffect(()=>{ api.get("/api/doctemplates").then(setDocTpls).catch(()=>{}); },[]);
  const reloadAtts=useCallback(async()=>{ const full=await api.get(`/api/persons/${person.id}`); setAtts(full.attachments||[]); },[person.id]);

  const chosenDoc=docTpls.filter(t=>selDoc[t.id]);
  const total=chosenDoc.length;
  const saved=(atts||[]).filter(a=>a.category==="dokumentenpaket").sort((a,b)=>String(b.uploaded_at).localeCompare(String(a.uploaded_at)));

  const generate=async()=>{
    const items=chosenDoc.map(t=>({kind:"docx",id:t.id}));
    if(!items.length) return;
    setBusy(true);
    try{
      const r=await api.post(`/api/persons/${person.id}/generate`,{items});
      await reloadAtts();
      flash&&flash(`PDF erzeugt (${r.count} Dokument${r.count===1?"":"e"}) – gespeichert`);
      window.open(`/api/attachments/${r.id}/file`,"_blank"); // zum Ansehen / Drucken
    }catch(e){ alert(e.message); } finally{ setBusy(false); }
  };
  const delSaved=async(a)=>{ if(!confirm("Dieses gespeicherte PDF löschen?"))return; await api.del(`/api/attachments/${a.id}`); reloadAtts(); };

  const groups=groupDocs(docTpls).filter(([c,a])=>a.length);

  return <div>
    <div style={{display:"flex",justifyContent:"space-between",alignItems:"center",marginBottom:18,gap:12,flexWrap:"wrap"}}>
      <div style={{display:"flex",alignItems:"center",gap:14}}><Btn kind="ghost" size="sm" onClick={onBack}>← Zurück</Btn>
        <div style={{fontWeight:700,fontSize:18}}>Dokumente für {person.vorname} {person.nachname}</div></div>
      <Btn kind="good" onClick={generate} disabled={!total||busy}>{busy?"Erzeuge PDF …":`Als PDF erzeugen & speichern (${total})`}</Btn>
    </div>

    <div style={{display:"grid",gridTemplateColumns:"320px 1fr",gap:18,alignItems:"start"}}>
      <div style={{position:"sticky",top:12}}>
        <div style={{background:"#fff",border:`1px solid ${C.line}`,borderRadius:12,padding:14}}>
          <div style={{fontWeight:700,fontSize:14,marginBottom:2}}>Vorlagen (Word → PDF)</div>
          <div style={{fontSize:11.5,color:C.muted,marginBottom:8}}>1:1 wie in Word, Felder werden eingesetzt.</div>
          {docTpls.length===0
            ? <div style={{fontSize:12.5,color:C.muted,padding:"6px 0"}}>Noch keine Vorlagen. Unter „Vorlagen“ hochladen.</div>
            : groups.map(([cat,items])=><div key={cat} style={{marginBottom:10}}>
                <div style={{fontSize:11,fontWeight:700,color:C.muted,textTransform:"uppercase",letterSpacing:.4,margin:"8px 0 3px"}}>{cat}</div>
                {items.map(t=><Check key={t.id} label={t.name} checked={!!selDoc[t.id]} onChange={e=>setSelDoc({...selDoc,[t.id]:e.target.checked})}/>)}
              </div>)}
        </div>
      </div>

      <div>
        {saved.length>0 && <div style={{background:"#fff",border:`1px solid ${C.line}`,borderRadius:12,marginBottom:18,overflow:"hidden"}}>
          <div style={{padding:"10px 14px",background:C.paper,borderBottom:`1px solid ${C.lineSoft}`,fontWeight:700,fontSize:14}}>Gespeicherte Dokumente (PDF)</div>
          {saved.map(a=><div key={a.id} style={{display:"flex",justifyContent:"space-between",alignItems:"center",padding:"10px 14px",borderTop:`1px solid ${C.lineSoft}`}}>
            <div style={{fontSize:13.5}}>📄 {a.original_name} <span style={{color:C.muted,fontSize:12}}>· {fmtKB(a.size)} · {new Date(a.uploaded_at).toLocaleString("de-DE")}</span></div>
            <div style={{display:"flex",gap:6}}>
              <Btn size="sm" kind="dark" onClick={()=>window.open(`/api/attachments/${a.id}/file`,"_blank")}>Ansehen / Drucken</Btn>
              <Btn size="sm" kind="danger" onClick={()=>delSaved(a)}>Löschen</Btn>
            </div>
          </div>)}
        </div>}

        {total===0&&saved.length===0&&<div style={{color:C.muted,padding:40,textAlign:"center",border:`1px dashed ${C.line}`,borderRadius:12}}>Links Vorlagen auswählen und oben „Als PDF erzeugen & speichern“.</div>}

        {chosenDoc.length>0 && <div style={{background:"#fff",border:`1px solid ${C.line}`,borderRadius:12,marginBottom:18,padding:"14px 16px"}}>
          <div style={{fontWeight:700,fontSize:14,marginBottom:6}}>Ausgewählte Vorlagen</div>
          <div style={{fontSize:13,color:C.steel}}>{chosenDoc.map(t=>t.name).join(", ")}</div>
          <div style={{fontSize:12,color:C.muted,marginTop:6}}>Diese werden originalgetreu befüllt und ins PDF übernommen. Die Vorschau erscheint im erzeugten PDF.</div>
        </div>}
      </div>
    </div>
  </div>;
}

/* ============================ Vorlagen (Admin) ============================ */
const SAMPLE_PERSON={...emptyPerson(),anrede:"Frau",vorname:"Maria",nachname:"Muster",strasse:"Musterweg 1",plz:"28207",ort:"Bremen",personalNr:"3230",eintrittsdatum:"2025-08-01",stundenlohn:"14,25",wochenstunden:"20",taetigkeit:"Unterhaltsreinigung",eingestelltAls:"Reinigungskraft",kinder:true,anzahlKinder:"2",iban:"DE12 3456 7890 1234 5678 90"};
const SAMPLE_SET={firma:"DIAMANT GEBÄUDEREINIGUNGSDIENST GMBH",strasse:"Stresemannstraße 37",plzOrt:"28207 Bremen",telefon:"0421-44 44 28",fax:"0421-44 44 29",geschaeftsfuehrer:"Cem Celik",personalleitung:"Britta Ehlers"};
const FIELD_GROUPS=[
  ["Person",[["Nachname","{{nachname}}"],["Vorname","{{vorname}}"],["Voller Name","{{name_voll}}"],["Anrede","{{anrede}}"],["Anrede (Sehr geehrte…)","{{anrede_geehrt}}"],["Straße","{{strasse}}"],["PLZ + Ort","{{plz_ort}}"],["Telefon","{{telefon}}"],["E-Mail","{{email}}"],["Geburtsdatum","{{geburtsdatum}}"],["Geburtsort","{{geburtsort}}"],["Staatsangeh.","{{staatsangehoerigkeit}}"],["Familienstand","{{familienstand}}"],["Konfession","{{konfession}}"],["Kinder","{{kinder}}"],["Anzahl Kinder","{{anzahl_kinder}}"],["Geburtsland","{{geburtsland}}"],["Einsatzbereiche","{{einsatzbereiche}}"],["Mögl. Arbeitstage","{{arbeitstage}}"],["Uhrzeit","{{uhrzeit}}"]]],
  ["Einstellung",[["Personal-Nr.","{{personalnr}}"],["Eintrittsdatum","{{eintrittsdatum}}"],["Eingestellt als","{{eingestellt_als}}"],["Tätigkeit","{{taetigkeit}}"],["Objekt","{{objekt}}"],["Stundenlohn","{{stundenlohn}}"],["Wochenstunden","{{wochenstunden}}"],["Befristet bis","{{befristet_bis}}"],["IBAN","{{iban}}"],["BIC","{{bic}}"],["Kreditinstitut","{{kreditinstitut}}"],["Kontoinhaber","{{kontoinhaber}}"],["Krankenkasse","{{krankenkasse}}"],["Versicherungsnummer","{{versicherungsnummer}}"],["Sozialvers.-Nr.","{{sozialversicherung_nr}}"],["Steuer-ID","{{steuer_id}}"],["Steuerklasse","{{steuerklasse}}"],["Einsatzort","{{einsatzort}}"]]],
  ["Firma / Datum",[["Firma","{{firma}}"],["Firma Straße","{{firma_strasse}}"],["Firma PLZ/Ort","{{firma_plz_ort}}"],["Firma Telefon","{{firma_telefon}}"],["Geschäftsführer","{{geschaeftsfuehrer}}"],["Personalleitung","{{personalleitung}}"],["Ort + Datum","{{ort_datum}}"],["Datum heute","{{datum_heute}}"]]],
  ["Unterschrift (Bild)",[["Unterschrift Mitarbeiter","{{%unterschrift}}"]]],
];

/* zerlegt einen gespeicherten Vorlagen-Body in Ränder / Kopfzeile / Inhalt */
function parseBody(body){
  try{
    const doc=new DOMParser().parseFromString(body||"","text/html");
    const page=doc.querySelector(".w4d-page");
    if(page){
      const parts=(page.style.padding||"").split(/\s+/).map(x=>parseFloat(x)).filter(x=>!isNaN(x));
      let m={top:25,right:20,bottom:20,left:20};
      if(parts.length===4) m={top:parts[0],right:parts[1],bottom:parts[2],left:parts[3]};
      else if(parts.length===2) m={top:parts[0],right:parts[1],bottom:parts[0],left:parts[1]};
      else if(parts.length===1) m={top:parts[0],right:parts[0],bottom:parts[0],left:parts[0]};
      const hd=page.querySelector(".w4d-header"), ct=page.querySelector(".w4d-content");
      return {margins:m, headerOn:!!hd, header:hd?hd.innerHTML:"", content:ct?ct.innerHTML:page.innerHTML};
    }
  }catch(e){}
  return {margins:{top:25,right:20,bottom:20,left:20}, headerOn:false, header:"", content:body||""};
}

function TemplateEditor({tpl,onBack,onSaved,flash}){
  const init=parseBody(tpl.body);
  const [name,setName]=useState(tpl.name);
  const [kat,setKat]=useState(tpl.kategorie);
  const [mode,setMode]=useState("visual");            // visual | vorschau | html
  const [openGroup,setOpenGroup]=useState(0);
  const [margins,setMargins]=useState(init.margins);
  const [headerOn,setHeaderOn]=useState(init.headerOn);
  const [header,setHeader]=useState(init.header);
  const [content,setContent]=useState(init.content);
  const [raw,setRaw]=useState("");
  const [tabMm,setTabMm]=useState(12);
  const contentRef=useRef(null), headerRef=useRef(null), lastRef=useRef(null);

  // Editoren beim Betreten des Bearbeiten-Modus / bei Kopfzeilenwechsel füllen
  useEffect(()=>{ if(mode==="visual"){ if(contentRef.current) contentRef.current.innerHTML=content; if(headerOn&&headerRef.current) headerRef.current.innerHTML=header; } },[mode,headerOn]); // eslint-disable-line
  const sync=()=>{ if(contentRef.current) setContent(contentRef.current.innerHTML); if(headerRef.current) setHeader(headerRef.current.innerHTML); };
  const activeEd=()=> lastRef.current || contentRef.current;

  const insertNode=(node)=>{ const ed=activeEd(); if(!ed) return; ed.focus();
    const sel=window.getSelection(); let r;
    if(sel&&sel.rangeCount&&ed.contains(sel.anchorNode)){ r=sel.getRangeAt(0); } else { r=document.createRange(); r.selectNodeContents(ed); r.collapse(false); }
    r.deleteContents(); r.insertNode(node); r.setStartAfter(node); r.collapse(true); sel.removeAllRanges(); sel.addRange(r); sync();
  };
  const insertField=(ph)=> insertNode(document.createTextNode(ph+" "));
  const insertTab=(ed)=>{ if(!ed) return; const span=document.createElement("span"); span.style.display="inline-block"; span.style.width=tabMm+"mm"; span.innerHTML="&#8203;";
    const sel=window.getSelection(); let r;
    if(sel&&sel.rangeCount&&ed.contains(sel.anchorNode)){ r=sel.getRangeAt(0);} else { r=document.createRange(); r.selectNodeContents(ed); r.collapse(false);}
    r.deleteContents(); r.insertNode(span); r.setStartAfter(span); r.collapse(true); sel.removeAllRanges(); sel.addRange(r); sync();
  };
  const onKey=(e)=>{ if(e.key==="Tab"){ e.preventDefault(); insertTab(e.currentTarget); } };
  const exec=(cmd)=>{ const ed=activeEd(); if(ed) ed.focus(); document.execCommand(cmd,false,null); sync(); };
  const toggleHeader=()=>{ if(!headerOn){ if(!header) setHeader('<div style="text-align:center"><b>{{firma}}</b><br>{{firma_strasse}}, {{firma_plz_ort}} · Tel. {{firma_telefon}}</div>'); setHeaderOn(true); } else setHeaderOn(false); };

  const composeBody=()=>{
    const c=contentRef.current?contentRef.current.innerHTML:content;
    const h=headerRef.current?headerRef.current.innerHTML:header;
    const pad=`${margins.top}mm ${margins.right}mm ${margins.bottom}mm ${margins.left}mm`;
    const head=headerOn?`<div class="w4d-header" style="border-bottom:1px solid #999;padding-bottom:6px;margin-bottom:14px">${h}</div>`:"";
    return `<div class="w4d-page" style="font-family:Georgia,'Times New Roman',serif;font-size:12pt;line-height:1.45;color:#111;padding:${pad}">${head}<div class="w4d-content">${c}</div></div>`;
  };
  const switchMode=(next)=>{
    if(mode==="visual") sync();
    if(mode==="html"){ const p=parseBody(raw); setMargins(p.margins); setHeaderOn(p.headerOn); setHeader(p.header); setContent(p.content); }
    if(next==="html") setRaw(composeBody());
    setMode(next);
  };
  const save=async()=>{ const body=mode==="html"?raw:composeBody(); await api.put(`/api/templates/${tpl.id}`,{name,kategorie:kat,body,nurMitarbeiter:tpl.nurMitarbeiter}); flash("Vorlage gespeichert"); onSaved(); };

  const tabBtn=(id,label)=><button onClick={()=>switchMode(id)} style={{background:mode===id?C.ink:"transparent",color:mode===id?"#fff":C.steel,border:`1px solid ${mode===id?C.ink:C.line}`,padding:"6px 12px",borderRadius:8,cursor:"pointer",fontSize:13,fontWeight:600}}>{label}</button>;
  const mInput=(k)=><input type="number" min="0" value={margins[k]} onChange={e=>setMargins({...margins,[k]:parseFloat(e.target.value)||0})} style={{width:54,padding:"5px 6px",border:`1px solid ${C.line}`,borderRadius:6,fontSize:13}}/>;
  const tool=(onClick,node,extra)=><button onMouseDown={e=>e.preventDefault()} onClick={onClick} style={{background:"#fff",border:`1px solid ${C.line}`,borderRadius:7,padding:"6px 10px",cursor:"pointer",fontSize:13,color:C.steel,...(extra||{})}}>{node}</button>;
  const edStyle={outline:"none",fontFamily:"Georgia,'Times New Roman',serif",fontSize:14,lineHeight:1.5};

  return <div>
    {/* ===== Fixierte Kopf- und Feldleiste (bleibt beim Scrollen sichtbar) ===== */}
    <div style={{position:"sticky",top:58,zIndex:8,background:C.paper,borderBottom:`1px solid ${C.line}`,paddingBottom:12,marginBottom:16,boxShadow:"0 6px 12px -8px rgba(0,0,0,.15)"}}>
      <div style={{display:"flex",justifyContent:"space-between",alignItems:"center",gap:12,flexWrap:"wrap",paddingTop:8}}>
        <div style={{display:"flex",alignItems:"center",gap:10,flexWrap:"wrap"}}>
          <Btn kind="ghost" size="sm" onClick={onBack}>← Zurück</Btn>
          <TextInput value={name} onChange={e=>setName(e.target.value)} style={{minWidth:200,fontWeight:600}}/>
          <TextInput value={kat} onChange={e=>setKat(e.target.value)} style={{width:140}} placeholder="Kategorie"/>
        </div>
        <div style={{display:"flex",gap:6,alignItems:"center"}}>{tabBtn("visual","Bearbeiten")}{tabBtn("vorschau","Vorschau")}{tabBtn("html","HTML (Profi)")}<Btn kind="good" onClick={save}>Speichern</Btn></div>
      </div>

      {mode==="visual" && <>
        {/* Werkzeugleiste: Format, Tab, Kopfzeile, Ränder */}
        <div style={{display:"flex",gap:8,alignItems:"center",flexWrap:"wrap",marginTop:10}}>
          {tool(()=>exec("bold"),<b>F</b>)}
          {tool(()=>exec("underline"),<span style={{textDecoration:"underline"}}>U</span>)}
          {tool(()=>exec("insertUnorderedList"),"• Liste")}
          {tool(()=>insertTab(activeEd()),"⇥ Tabstopp")}
          <span style={{fontSize:12,color:C.muted}}>Tab-Breite</span>{mInput2(tabMm,setTabMm)}<span style={{fontSize:12,color:C.muted}}>mm</span>
          {tool(toggleHeader, headerOn?"Kopfzeile entfernen":"＋ Kopfzeile", headerOn?{background:C.iceSoft,borderColor:"#bae6fd",color:"#075985"}:null)}
          <span style={{width:1,height:22,background:C.line,margin:"0 2px"}}/>
          <span style={{fontSize:12,color:C.muted,fontWeight:600}}>Ränder (mm):</span>
          <span style={{fontSize:12,color:C.muted}}>oben</span>{mInput("top")}
          <span style={{fontSize:12,color:C.muted}}>rechts</span>{mInput("right")}
          <span style={{fontSize:12,color:C.muted}}>unten</span>{mInput("bottom")}
          <span style={{fontSize:12,color:C.muted}}>links</span>{mInput("left")}
        </div>
        {/* Feld-Auswahl */}
        <div style={{marginTop:10}}>
          <div style={{display:"flex",gap:6,marginBottom:8,flexWrap:"wrap"}}>{FIELD_GROUPS.map((g,i)=>
            <button key={i} onMouseDown={e=>e.preventDefault()} onClick={()=>setOpenGroup(i)} style={{background:openGroup===i?C.ink:"#fff",color:openGroup===i?"#fff":C.steel,border:`1px solid ${openGroup===i?C.ink:C.line}`,padding:"5px 11px",borderRadius:8,cursor:"pointer",fontSize:12.5,fontWeight:600}}>{g[0]}</button>)}</div>
          <div style={{display:"flex",flexWrap:"wrap",gap:6,maxHeight:96,overflow:"auto"}}>{FIELD_GROUPS[openGroup][1].map(([lbl,ph])=>
            <button key={ph} onMouseDown={e=>e.preventDefault()} onClick={()=>insertField(ph)} title={ph} style={{fontSize:12.5,background:"#fff",border:`1px solid ${C.line}`,borderRadius:6,padding:"5px 10px",cursor:"pointer",color:C.ink}}>+ {lbl}</button>)}</div>
        </div>
      </>}
    </div>

    {/* ===== Dokumentbereich (scrollt) ===== */}
    {/* Seite mit sichtbaren Rändern; immer gemountet, damit Inhalt erhalten bleibt */}
    <div style={{display:mode==="visual"?"block":"none",border:`1px solid ${C.line}`,borderRadius:10,background:"#fff",padding:`${margins.top}mm ${margins.right}mm ${margins.bottom}mm ${margins.left}mm`,minHeight:500,boxShadow:"inset 0 0 0 100vmax rgba(0,0,0,0)"}}>
      {headerOn && <div ref={headerRef} contentEditable suppressContentEditableWarning onInput={sync} onKeyDown={onKey} onFocus={()=>lastRef.current=headerRef.current}
        style={{...edStyle,borderBottom:"1px solid #999",paddingBottom:6,marginBottom:14,minHeight:24}}/>}
      <div ref={contentRef} contentEditable suppressContentEditableWarning onInput={sync} onKeyDown={onKey} onFocus={()=>lastRef.current=contentRef.current}
        style={{...edStyle,minHeight:400}}/>
    </div>

    {mode==="vorschau" && <div style={{border:`1px solid ${C.line}`,borderRadius:10,background:"#fff",minHeight:500}} dangerouslySetInnerHTML={{__html:fillTemplate(composeBody(),SAMPLE_PERSON,SAMPLE_SET)}}/>}

    {mode==="html" && <textarea value={raw} onChange={e=>setRaw(e.target.value)} style={{width:"100%",minHeight:500,padding:12,fontFamily:"monospace",fontSize:12.5,border:`1px solid ${C.line}`,borderRadius:10,boxSizing:"border-box",resize:"vertical"}}/>}
  </div>;
}

/* kleines Zahlenfeld für die Tab-Breite */
function mInput2(val,setVal){ return <input type="number" min="1" value={val} onChange={e=>setVal(parseFloat(e.target.value)||0)} style={{width:54,padding:"5px 6px",border:`1px solid ${C.line}`,borderRadius:6,fontSize:13}}/>; }

/* Platzhalter-Spickzettel zum Einfügen in Word */
function PlaceholderHelp(){
  const [open,setOpen]=useState(false);
  const [copied,setCopied]=useState("");
  const copy=(code)=>{ try{ navigator.clipboard&&navigator.clipboard.writeText(code); }catch(e){} setCopied(code); setTimeout(()=>setCopied(""),1200); };
  return <div style={{marginTop:14,border:`1px solid ${C.line}`,borderRadius:10,background:"#fff"}}>
    <button onClick={()=>setOpen(!open)} style={{width:"100%",textAlign:"left",background:C.paper,border:"none",padding:"11px 14px",cursor:"pointer",fontWeight:700,fontSize:13.5,borderRadius:10}}>
      {open?"▾":"▸"} Verfügbare Platzhalter für Word (klicken = kopieren)
    </button>
    {open && <div style={{padding:"6px 14px 14px"}}>
      <div style={{fontSize:12.5,color:C.muted,margin:"6px 0 12px"}}>Schreiben Sie diese Platzhalter genau so – mit doppelten geschweiften Klammern – an die passende Stelle in Ihr Word-Dokument. Beim Erzeugen werden sie durch die Daten der Person ersetzt. Fehlt ein Wert, bleibt die Stelle leer.</div>
      {FIELD_GROUPS.map(([grp,items])=><div key={grp} style={{marginBottom:12}}>
        <div style={{fontSize:11,fontWeight:700,color:C.muted,textTransform:"uppercase",marginBottom:6}}>{grp}</div>
        <div style={{display:"grid",gridTemplateColumns:"repeat(2,minmax(0,1fr))",gap:6}}>
          {items.map(([label,code])=><button key={code} onClick={()=>copy(code)} title="Kopieren"
            style={{display:"flex",justifyContent:"space-between",gap:8,alignItems:"center",border:`1px solid ${copied===code?C.good:C.line}`,borderRadius:7,padding:"6px 9px",background:copied===code?C.goodSoft:"#fff",cursor:"pointer",fontSize:12.5}}>
            <span style={{color:C.steel}}>{label}</span><code style={{color:C.iceDeep,fontSize:12}}>{copied===code?"kopiert!":code}</code></button>)}
        </div>
      </div>)}
    </div>}
  </div>;
}

/* Verwaltung der Original-Word-Vorlagen (.docx, 1:1 -> PDF) */
function reorderDocs(list,dragId,targetId,place,newCat){
  const item=list.find(x=>x.id===dragId); if(!item) return list;
  const moved={...item,kategorie:newCat};
  const rest=list.filter(x=>x.id!==dragId);
  if(targetId){
    const idx=rest.findIndex(x=>x.id===targetId);
    rest.splice(place==="after"?idx+1:idx,0,moved);
  }else{
    let lastIdx=-1; rest.forEach((x,i)=>{ if(x.kategorie===newCat) lastIdx=i; });
    if(lastIdx<0) rest.push(moved); else rest.splice(lastIdx+1,0,moved);
  }
  return rest;
}

function DocTemplatesAdmin({flash}){
  const [list,setList]=useState([]); const [busy,setBusy]=useState(false); const ref=useRef(null);
  const [moveMode,setMoveMode]=useState(false);
  const [dragId,setDragId]=useState(null); const [overZone,setOverZone]=useState(null); const [overRow,setOverRow]=useState(null);
  const load=useCallback(async()=>setList(await api.get("/api/doctemplates")),[]);
  useEffect(()=>{load();},[load]);
  const up=async(file)=>{ setBusy(true); try{ const fd=new FormData(); fd.append("file",file,file.name); fd.append("kategorie","Sonstiges"); await api.upload("/api/doctemplates",fd); await load(); flash("Vorlage hochgeladen (Bereich „Sonstiges“)"); }catch(e){alert(e.message);}finally{setBusy(false);} };
  const rename=async(t)=>{ const n=prompt("Name der Vorlage:",t.name); if(!n||n===t.name)return; const fd=new FormData(); fd.append("name",n); await api.putForm(`/api/doctemplates/${t.id}`,fd); load(); };
  const replace=(t)=>{ const inp=document.createElement("input"); inp.type="file"; inp.accept=".docx"; inp.onchange=async()=>{ if(inp.files[0]){ const fd=new FormData(); fd.append("file",inp.files[0],inp.files[0].name); await api.putForm(`/api/doctemplates/${t.id}`,fd); load(); flash("Vorlage ersetzt"); } }; inp.click(); };
  const del=async(t)=>{ if(confirm("Vorlage löschen?")){ await api.del(`/api/doctemplates/${t.id}`); load(); } };

  const applyOrder=async(newList,msg)=>{
    setList(newList);                                   // sofort anzeigen
    try{ await api.post("/api/doctemplates/reorder",{order:newList.map(t=>({id:t.id,kategorie:t.kategorie}))}); if(msg)flash(msg); }
    catch(e){ alert("Sortieren fehlgeschlagen: "+(e.message||e)); load(); }
  };
  const dropOnRow=(e,target)=>{
    e.preventDefault(); e.stopPropagation();
    if(!moveMode||!dragId||dragId===target.id){ setDragId(null); setOverRow(null); setOverZone(null); return; }
    const r=e.currentTarget.getBoundingClientRect();
    const place=(e.clientY-r.top)>r.height/2?"after":"before";
    const moved=list.find(x=>x.id===dragId);
    applyOrder(reorderDocs(list,dragId,target.id,place,target.kategorie), moved&&moved.kategorie!==target.kategorie?`In „${target.kategorie}“ verschoben`:null);
    setDragId(null); setOverRow(null); setOverZone(null);
  };
  const dropOnZone=(e,cat)=>{
    e.preventDefault();
    if(moveMode&&dragId){ const moved=list.find(x=>x.id===dragId); applyOrder(reorderDocs(list,dragId,null,null,cat), moved&&moved.kategorie!==cat?`In „${cat}“ verschoben`:null); }
    setDragId(null); setOverRow(null); setOverZone(null);
  };

  const groups=groupDocs(list);

  return <div style={{background:"#fff",border:`2px solid ${C.iceDeep}`,borderRadius:12,padding:16,marginBottom:22}}>
    <div style={{display:"flex",justifyContent:"space-between",alignItems:"flex-start",gap:12,flexWrap:"wrap"}}>
      <div style={{maxWidth:600}}>
        <div style={{fontWeight:800,fontSize:15}}>Vorlagen (Word → PDF)</div>
        <div style={{fontSize:13,color:C.muted,marginTop:3}}>Ihr Word-Dokument bleibt 1:1 erhalten. Platzhalter (z. B. <code style={{color:C.iceDeep}}>{"{{nachname}}"}</code>) einmalig setzen und die .docx hochladen. Neue Vorlagen landen im Bereich „Sonstiges“. Zum Sortieren den Schalter „Vorlagen verschieben“ aktivieren und die Vorlagen ziehen – zwischen den Bereichen und innerhalb eines Bereichs.</div>
      </div>
      <div>
        <Btn kind="dark" disabled={busy} onClick={()=>ref.current.click()}>{busy?"Lädt …":"⭱ Word-Vorlage hochladen"}</Btn>
        <input ref={ref} type="file" accept=".docx" style={{display:"none"}} onChange={e=>{const f=e.target.files[0]; if(f)up(f); e.target.value="";}}/>
      </div>
    </div>

    {/* Schalter zum Verschieben/Sortieren */}
    <div style={{display:"flex",alignItems:"center",gap:12,margin:"14px 0 6px",padding:"10px 12px",background:moveMode?C.warnSoft:C.paper,border:`1px solid ${moveMode?C.warn:C.line}`,borderRadius:10}}>
      <button onClick={()=>{setMoveMode(m=>!m); setDragId(null); setOverRow(null); setOverZone(null);}} aria-pressed={moveMode}
        style={{position:"relative",width:46,height:26,borderRadius:20,border:"none",cursor:"pointer",background:moveMode?C.warn:C.line,transition:"background .15s",flexShrink:0}}>
        <span style={{position:"absolute",top:3,left:moveMode?23:3,width:20,height:20,borderRadius:"50%",background:"#fff",transition:"left .15s",boxShadow:"0 1px 3px rgba(0,0,0,.3)"}}/>
      </button>
      <div style={{fontSize:13.5}}>
        <span style={{fontWeight:700}}>Vorlagen verschieben {moveMode?"aktiv":"aus"}</span>
        <span style={{color:C.muted}}> · {moveMode?"Ziehen Sie eine Vorlage an die gewünschte Stelle (anderer Bereich oder andere Position).":"Aktivieren, um Vorlagen per Ziehen zu sortieren."}</span>
      </div>
    </div>

    {list.length>0 && <div style={{marginTop:8}}>
      {groups.map(([cat,items])=><div key={cat}
          onDragOver={(e)=>{ if(moveMode&&dragId){ e.preventDefault(); if(overZone!==cat) setOverZone(cat); } }}
          onDragLeave={()=>{ if(overZone===cat) setOverZone(null); }}
          onDrop={(e)=>dropOnZone(e,cat)}
          style={{marginBottom:12,borderRadius:10,padding:"2px 4px 4px",
                  outline:moveMode&&overZone===cat&&items.length===0?`2px dashed ${C.warn}`:"2px dashed transparent",
                  background:moveMode&&overZone===cat&&items.length===0?C.warnSoft:"transparent"}}>
        <div style={{fontSize:11.5,fontWeight:800,color:C.muted,textTransform:"uppercase",letterSpacing:.5,margin:"6px 2px 6px"}}>{cat} <span style={{color:C.line}}>· {items.length}</span></div>
        <div style={{border:`1px solid ${C.line}`,borderRadius:10,overflow:"hidden"}}>
          {items.length===0
            ? <div style={{padding:"14px",fontSize:12.5,color:C.muted,textAlign:"center"}}>{moveMode?"Vorlage hierher ziehen":"— keine Vorlagen —"}</div>
            : <table style={{width:"100%",borderCollapse:"collapse",fontSize:14}}>
            <tbody>{items.map(t=><tr key={t.id}
                draggable={moveMode}
                onDragStart={(e)=>{ if(moveMode){ setDragId(t.id); e.dataTransfer.effectAllowed="move"; try{e.dataTransfer.setData("text/plain",t.id);}catch{} } }}
                onDragEnd={()=>{ setDragId(null); setOverRow(null); setOverZone(null); }}
                onDragOver={(e)=>{ if(moveMode&&dragId&&dragId!==t.id){ e.preventDefault(); const r=e.currentTarget.getBoundingClientRect(); const place=(e.clientY-r.top)>r.height/2?"after":"before"; if(!overRow||overRow.id!==t.id||overRow.place!==place) setOverRow({id:t.id,place}); } }}
                onDrop={(e)=>dropOnRow(e,t)}
                style={{borderTop:`1px solid ${C.lineSoft}`,cursor:moveMode?"grab":"default",opacity:dragId===t.id?.4:1,
                        background:moveMode?C.paper:"transparent",
                        boxShadow:moveMode&&overRow&&overRow.id===t.id?(overRow.place==="before"?`inset 0 3px 0 ${C.warn}`:`inset 0 -3px 0 ${C.warn}`):"none"}}>
              <td style={{padding:"11px 14px",fontWeight:600,width:24,color:C.muted}}>{moveMode?"⠿":"📄"}</td>
              <td style={{padding:"11px 6px",fontWeight:600}}>{t.name}</td>
              <td style={{padding:"9px 14px",textAlign:"right",whiteSpace:"nowrap"}}>
                <Btn size="sm" kind="ghost" onClick={()=>rename(t)}>Umbenennen</Btn>{" "}
                <Btn size="sm" kind="ghost" onClick={()=>window.open(`/api/doctemplates/${t.id}/download`,"_blank")}>Word laden</Btn>{" "}
                <Btn size="sm" kind="ghost" onClick={()=>replace(t)}>Ersetzen</Btn>{" "}
                <Btn size="sm" kind="danger" onClick={()=>del(t)}>Löschen</Btn>
              </td></tr>)}</tbody>
          </table>}
        </div>
      </div>)}
    </div>}
    <PlaceholderHelp/>
  </div>;
}

function TemplatesAdmin({flash}){
  return <div>
    <DocTemplatesAdmin flash={flash}/>
  </div>;
}

/* ============================ Benutzer (Admin) ============================ */
function UsersAdmin({me,flash}){
  const [list,setList]=useState([]); const [nu,setNu]=useState(""); const [nn,setNn]=useState(""); const [np,setNp]=useState(""); const [nr,setNr]=useState("user");
  const load=useCallback(async()=>setList(await api.get("/api/users")),[]);
  useEffect(()=>{load();},[load]);
  const add=async()=>{ try{ await api.post("/api/users",{username:nu,name:nn,role:nr,password:np}); setNu("");setNn("");setNp("");setNr("user"); flash("Benutzer angelegt"); load(); }catch(e){alert(e.message);} };
  const resetPw=async(u)=>{ const p=prompt("Neues Passwort für "+u+":"); if(!p)return; await api.put(`/api/users/${u}`,{password:p}); flash("Passwort geändert"); };
  const toggle=async(u,role)=>{ await api.put(`/api/users/${u}`,{role:role==="admin"?"user":"admin"}); load(); };
  const del=async(u)=>{ if(confirm("Benutzer löschen?")){ try{ await api.del(`/api/users/${u}`); load(); }catch(e){alert(e.message);} } };
  return <div>
    <Card title="Neuen Benutzer anlegen">
      <div style={{display:"grid",gridTemplateColumns:"1fr 1fr 1fr 140px auto",gap:10,alignItems:"end"}}>
        <Field label="Benutzername"><TextInput value={nu} onChange={e=>setNu(e.target.value)}/></Field>
        <Field label="Anzeigename"><TextInput value={nn} onChange={e=>setNn(e.target.value)}/></Field>
        <Field label="Passwort"><TextInput type="password" value={np} onChange={e=>setNp(e.target.value)}/></Field>
        <Field label="Rolle"><Select value={nr} onChange={e=>setNr(e.target.value)} options={[{value:"user",label:"Benutzer"},{value:"admin",label:"Admin"}]}/></Field>
        <Btn onClick={add}>Anlegen</Btn>
      </div>
    </Card>
    <div style={{background:"#fff",border:`1px solid ${C.line}`,borderRadius:12,overflow:"hidden"}}>
      <table style={{width:"100%",borderCollapse:"collapse",fontSize:14}}>
        <thead><tr style={{background:C.paper,textAlign:"left",color:C.muted,fontSize:12,textTransform:"uppercase"}}><th style={{padding:"11px 16px"}}>Benutzer</th><th style={{padding:"11px 16px"}}>Name</th><th style={{padding:"11px 16px"}}>Rolle</th><th></th></tr></thead>
        <tbody>{list.map(u=><tr key={u.username} style={{borderTop:`1px solid ${C.lineSoft}`}}>
          <td style={{padding:"12px 16px",fontWeight:600}}>{u.username}{u.username===me.username&&<span style={{color:C.muted,fontWeight:400}}> (Sie)</span>}</td>
          <td style={{padding:"12px 16px",color:C.steel}}>{u.name}</td>
          <td style={{padding:"12px 16px"}}><span style={{fontSize:12,fontWeight:700,padding:"3px 9px",borderRadius:20,background:u.role==="admin"?C.warnSoft:C.lineSoft,color:u.role==="admin"?C.warn:C.steel}}>{u.role==="admin"?"Admin":"Benutzer"}</span></td>
          <td style={{padding:"12px 16px",textAlign:"right"}}>
            <Btn size="sm" kind="ghost" onClick={()=>resetPw(u.username)}>Passwort</Btn>{" "}
            <Btn size="sm" kind="ghost" onClick={()=>toggle(u.username,u.role)} disabled={u.username==="admin"}>Rolle wechseln</Btn>{" "}
            <Btn size="sm" kind="danger" onClick={()=>del(u.username)} disabled={u.username==="admin"||u.username===me.username}>Löschen</Btn>
          </td></tr>)}</tbody>
      </table></div>
  </div>;
}

/* ============================ Firmendaten (Admin) ============================ */
function SettingsAdmin({flash,onSaved}){
  const [s,setS]=useState(null);
  useEffect(()=>{ api.get("/api/settings").then(setS); },[]);
  if(!s) return <div style={{color:C.muted}}>Lädt …</div>;
  const set=(k)=>(e)=>setS({...s,[k]:e.target.value});
  const save=async()=>{ const r=await api.put("/api/settings",s); setS(r); flash("Firmendaten gespeichert"); onSaved&&onSaved(r); };
  return <Card title="Firmendaten" sub="Werden über Platzhalter automatisch in alle Vorlagen eingesetzt." right={<Btn kind="good" onClick={save}>Speichern</Btn>}>
    <div style={GRID}>
      <Field label="Firma" span={2}><TextInput value={s.firma||""} onChange={set("firma")}/></Field>
      <Field label="Straße"><TextInput value={s.strasse||""} onChange={set("strasse")}/></Field>
      <Field label="PLZ / Ort"><TextInput value={s.plzOrt||""} onChange={set("plzOrt")}/></Field>
      <Field label="Telefon"><TextInput value={s.telefon||""} onChange={set("telefon")}/></Field>
      <Field label="Fax"><TextInput value={s.fax||""} onChange={set("fax")}/></Field>
      <Field label="Geschäftsführer"><TextInput value={s.geschaeftsfuehrer||""} onChange={set("geschaeftsfuehrer")}/></Field>
      <Field label="Personalleitung"><TextInput value={s.personalleitung||""} onChange={set("personalleitung")}/></Field>
    </div></Card>;
}

/* ============================ Haupt-App ============================ */
function BackupAdmin({flash}){
  const [list,setList]=useState([]); const [busy,setBusy]=useState(false);
  const load=useCallback(async()=>{ try{ setList(await api.get("/api/backup/list")); }catch{} },[]);
  useEffect(()=>{ load(); },[load]);
  const create=async()=>{ setBusy(true); try{ await api.post("/api/backup/create"); await load(); flash("Sicherung erstellt"); }catch(e){ alert("Sicherung fehlgeschlagen: "+(e.message||e)); } finally{ setBusy(false); } };
  const del=async(name)=>{ if(!confirm("Diese Sicherung wirklich löschen?"))return; await api.del(`/api/backup/${encodeURIComponent(name)}`); load(); };
  const fmtSize=(b)=> b>=1048576?(b/1048576).toFixed(1)+" MB":Math.max(1,Math.round(b/1024))+" KB";
  const fmtDate=(iso)=>{ try{ return new Date(iso).toLocaleString("de-DE"); }catch{ return iso; } };
  return <div>
    <Card title="Datensicherung" sub="Erstellt eine vollständige Sicherung aller Daten (Personen-Datenbank, Fotos, Unterschriften und hochgeladene Word-Vorlagen). Zum Herunterladen und sicheren Aufbewahren.">
      <div style={{display:"flex",gap:10,alignItems:"center",flexWrap:"wrap"}}>
        <Btn kind="good" onClick={create} disabled={busy}>{busy?"Sicherung wird erstellt …":"Sicherung jetzt erstellen"}</Btn>
        <span style={{fontSize:13,color:C.muted}}>Die Sicherung wird auf dem Server abgelegt und kann anschließend heruntergeladen werden.</span>
      </div>
    </Card>
    <Card title="Vorhandene Sicherungen" sub="Neueste zuerst. Zum Aufbewahren bitte herunterladen und außerhalb des Servers speichern (z. B. Firmenlaufwerk).">
      {list.length===0? <div style={{color:C.muted}}>Noch keine Sicherungen vorhanden.</div> :
       <table style={{width:"100%",borderCollapse:"collapse"}}>
        <thead><tr style={{textAlign:"left",color:C.muted,fontSize:12.5}}>
          <th style={{padding:"8px 6px"}}>Sicherung</th><th style={{padding:"8px 6px"}}>Erstellt</th><th style={{padding:"8px 6px"}}>Größe</th><th></th></tr></thead>
        <tbody>{list.map(b=><tr key={b.name} style={{borderTop:`1px solid ${C.line}`}}>
          <td style={{padding:"10px 6px",fontFamily:"ui-monospace,Menlo,monospace",fontSize:12.5}}>{b.name}</td>
          <td style={{padding:"10px 6px",fontSize:13}}>{fmtDate(b.created)}</td>
          <td style={{padding:"10px 6px",fontSize:13}}>{fmtSize(b.size)}</td>
          <td style={{padding:"10px 6px",textAlign:"right",whiteSpace:"nowrap"}}>
            <a href={`/api/backup/${encodeURIComponent(b.name)}/download`}
               style={{display:"inline-block",padding:"6px 12px",marginRight:8,borderRadius:8,background:C.ink,color:"#fff",fontSize:13,fontWeight:600,textDecoration:"none"}}>Herunterladen</a>
            <Btn size="sm" kind="danger" onClick={()=>del(b.name)}>Löschen</Btn>
          </td></tr>)}</tbody></table>}
    </Card>
  </div>;
}

function App(){
  const [me,setMe]=useState(undefined); // undefined=lädt, null=nicht angemeldet
  const [ver,setVer]=useState("");
  const [nav,setNav]=useState("personen");
  const [persons,setPersons]=useState([]);
  const [templates,setTemplates]=useState([]);
  const [settings,setSettings]=useState({});
  const [openId,setOpenId]=useState(null);
  const [docPerson,setDocPerson]=useState(null);
  const [toast,setToast]=useState("");
  const guardRef=useRef(null);   // meldet ungespeicherte Änderungen der offenen Maske
  const flash=(m)=>{ setToast(m); setTimeout(()=>setToast(""),2200); };
  const confirmLeave=()=>{ if(openId && guardRef.current && guardRef.current()) return confirm("Es gibt ungespeicherte Änderungen (ggf. auch eine noch nicht gespeicherte Unterschrift). Wenn Sie fortfahren, gehen diese verloren. Trotzdem verlassen?"); return true; };
  useEffect(()=>{ window.__setUnauth=()=>setMe(null); },[]);
  useEffect(()=>{ api.get("/api/me").then(r=>{ setMe(r.user); if(r.version) setVer(r.version); }); },[]);
  const loadData=useCallback(async()=>{
    const [ps,ts,st]=await Promise.all([api.get("/api/persons"),api.get("/api/templates"),api.get("/api/settings")]);
    setPersons(ps); setTemplates(ts); setSettings(st);
  },[]);
  useEffect(()=>{ if(me) loadData(); },[me,loadData]);

  if(me===undefined) return <div className="boot">Lädt …</div>;
  if(!me) return <Login onLogin={(u)=>{setMe(u);setNav("personen");}}/>;
  const isAdmin=me.role==="admin";

  const newPerson=async()=>{ const r=await api.post("/api/persons",emptyPerson()); await loadData(); setOpenId(r.id); };
  const openDocs=async(p)=>{ const full=await api.get(`/api/persons/${p.id}`); setDocPerson(full); setOpenId(null); };
  const backFromForm=async()=>{ setOpenId(null); await loadData(); };

  const navItems=[
    {id:"personen",label:"Bewerber & Mitarbeiter"},
    {id:"vorlagen",label:"Vorlagen",admin:true},
    {id:"benutzer",label:"Benutzer",admin:true},
    {id:"sicherung",label:"Datensicherung",admin:true},
    {id:"einstellungen",label:"Firmendaten",admin:true},
  ].filter(n=>!n.admin||isAdmin);

  let content;
  if(docPerson) content=<DocumentsView person={docPerson} templates={templates} settings={settings} flash={flash} onBack={()=>{setDocPerson(null);loadData();}}/>;
  else if(openId) content=<PersonForm personId={openId} onBack={backFromForm} onSaved={backFromForm} onDocs={openDocs} flash={flash} guardRef={guardRef}/>;
  else if(nav==="personen") content=<PersonList persons={persons} onOpen={setOpenId} onNew={newPerson}/>;
  else if(nav==="vorlagen") content=<TemplatesAdmin flash={flash}/>;
  else if(nav==="benutzer") content=<UsersAdmin me={me} flash={flash}/>;
  else if(nav==="sicherung") content=<BackupAdmin flash={flash}/>;
  else if(nav==="einstellungen") content=<SettingsAdmin flash={flash} onSaved={setSettings}/>;

  return <div style={{minHeight:"100vh"}}>
    <div className="noprint" style={{background:C.ink,color:"#fff",display:"flex",alignItems:"center",padding:"0 20px",height:58,position:"sticky",top:0,zIndex:10}}>
      <div style={{display:"flex",alignItems:"center",gap:11}}><Logo size={28}/>
        <div style={{fontWeight:800}}>Work4Dia {ver&&<span style={{fontWeight:600,color:C.ice,fontSize:12}}>v{ver}</span>} <span style={{fontWeight:400,color:"#94a3b8",fontSize:13}}>· Diamant Gebäudereinigung</span></div></div>
      <nav style={{display:"flex",gap:4,marginLeft:28}}>{navItems.map(n=>
        <button key={n.id} onClick={()=>{ if(!confirmLeave())return; setNav(n.id);setOpenId(null);setDocPerson(null);}}
          style={{background:nav===n.id&&!openId&&!docPerson?C.steel:"transparent",color:"#fff",border:"none",padding:"8px 14px",borderRadius:8,cursor:"pointer",fontSize:13.5,fontWeight:600}}>{n.label}</button>)}
      </nav>
      <div style={{flex:1}}/>
      <div style={{fontSize:13,color:"#cbd5e1",marginRight:14}}>{me.name} {isAdmin&&<span style={{color:C.ice}}>· Admin</span>}</div>
      <Btn size="sm" kind="ghost" style={{color:"#fff",borderColor:C.steel}} onClick={async()=>{ if(!confirmLeave())return; await api.post("/api/logout");setMe(null);setOpenId(null);setDocPerson(null);}}>Abmelden</Btn>
    </div>
    <div style={{maxWidth:1160,margin:"0 auto",padding:"24px 20px 60px"}}>{content}</div>
    <div className="noprint" style={{maxWidth:1160,margin:"0 auto",padding:"0 20px 30px",color:C.muted,fontSize:12,textAlign:"center"}}>Work4Dia{ver?` · Version ${ver}`:""} · Diamant Gebäudereinigungsdienst GmbH</div>
    {toast&&<div style={{position:"fixed",bottom:22,left:"50%",transform:"translateX(-50%)",background:C.ink,color:"#fff",padding:"10px 20px",borderRadius:30,fontSize:14,fontWeight:600,boxShadow:"0 8px 24px rgba(0,0,0,.25)",zIndex:50}}>{toast}</div>}
  </div>;
}

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