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

// ─── DATENMODELL ─────────────────────────────────────────────────────────────
// Jede Kalkulation hat: id, nr, datum, titel, kundeId, positionen, _ts
// Jedes Angebot hat:    id, angebotNr, datum, titel, kundeId, kalkulationId, positionen, status ('Entwurf'|'Versendet'), _ts
// Jede Anzahlung hat:   id, rechnungNr, datum, angebotId, kundeId, betragNetto, _ts
// Jede Schlussrechnung: id, rechnungNr, datum, angebotId, kundeId, gesamtNetto, anzahlungNetto, restNetto, _ts

// ─── STATUS BADGE ─────────────────────────────────────────────────────────────
function Badge({s}){
  var cfg={
    Entwurf:{bg:'#F3F4F6',color:'#6B7280'},
    Versendet:{bg:'#EFF6FF',color:'#2563EB'},
    Überarbeitet:{bg:'#F3F4F6',color:'#9CA3AF'},
    Freigegeben:{bg:'#F0FDF4',color:'#16A34A'},
  };
  var c=cfg[s]||{bg:'#F3F4F6',color:'#6B7280'};
  return <span style={{background:c.bg,color:c.color,borderRadius:20,padding:'2px 10px',fontSize:12,fontWeight:600,whiteSpace:'nowrap',border:'1px solid '+c.color+'22'}}>{s}</span>;
}

// ─── NUMMERN-FORMAT-HELPERS (sevDesk RE-YY-NUMBER / AN-YY-NUMBER) ─────────────
// Alte Nummern (<1000) behalten Legacy-Format RE-0001, neue bekommen RE-26-1002
function fmtRechnungNr(nr){
  var n=parseInt(nr)||0;
  if(n<1000) return 'RE-'+String(n).padStart(4,'0');
  var yy=new Date().getFullYear().toString().slice(-2);
  return 'RE-'+yy+'-'+n;
}
function fmtAngebotNr(nr){
  var n=parseInt(nr)||0;
  if(n<1000) return 'AN-'+String(n).padStart(4,'0');
  var yy=new Date().getFullYear().toString().slice(-2);
  return 'AN-'+yy+'-'+n;
}

// ─── NAVIGATION ──────────────────────────────────────────────────────────────
var NAV=[
  {id:'dashboard',   icon:'⊞', label:'Dashboard'},
  {id:'budget',      icon:'⏱', label:'Budget & Zeiten'},
  {id:'kunden',      icon:'◎', label:'Kunden'},
  {id:'kalkulation', icon:'≡', label:'Kalkulation'},
  {id:'reisekosten', icon:'✈', label:'Reisekosten'},
  {id:'privat',      icon:'◈', label:'Private Ausgaben'},
  {id:'einstellungen',icon:'⚙', label:'Einstellungen'},
];
var SYNC_LABEL={idle:{c:'#BBBBBB',t:'● Lokal gespeichert'},syncing:{c:'#D97706',t:'● Sync…'},ok:{c:'#16A34A',t:'● Sync aktiv'},error:{c:'#DC2626',t:'● Sync-Fehler'}};

function Sidebar({page,setPage,syncStatus,syncNow}){
  var sl=SYNC_LABEL[syncStatus]||SYNC_LABEL.idle;
  return(
    <aside className="sk-sidebar" style={{width:220,background:'#fff',borderRight:'1px solid var(--border)',minHeight:'100vh',flexDirection:'column',padding:'0 0 20px',flexShrink:0}}>
      <div style={{padding:'28px 20px 26px'}}>
        <div style={{fontSize:13,fontWeight:600,color:'#111',letterSpacing:2.5,textTransform:'uppercase'}}>Schuster &amp; Krapf</div>
        <div style={{fontSize:9,color:'#BBBBBB',letterSpacing:3,fontWeight:400,textTransform:'uppercase',marginTop:4}}>Kalkulation</div>
      </div>
      <nav style={{flex:1}}>
        {NAV.map(function(n){return(
          <button key={n.id} onClick={function(){setPage(n.id);}} style={{
            display:'flex',alignItems:'center',gap:10,width:'100%',padding:'9px 20px',
            background:page===n.id?'#F5F5F5':'transparent',
            color:page===n.id?'#111':'#999',border:'none',cursor:'pointer',
            borderLeft:page===n.id?'2px solid #111':'2px solid transparent',
            fontSize:13,fontFamily:'inherit',transition:'all .1s',
            fontWeight:page===n.id?600:400,textAlign:'left'
          }}>
            <span style={{fontSize:13,opacity:page===n.id?1:0.6}}>{n.icon}</span>{n.label}
          </button>
        );})}
      </nav>
      <div style={{margin:'0 14px',paddingTop:12,borderTop:'1px solid var(--border)',fontSize:11,color:sl.c}}>{sl.t}</div>
      {syncNow&&(
        <div style={{padding:'8px 14px 0'}}>
          <button onClick={syncNow} style={{width:'100%',padding:'7px 0',background:'transparent',border:'1px solid var(--border)',borderRadius:6,color:'#aaa',fontSize:12,cursor:'pointer',fontFamily:'inherit'}}>↻ Synchronisieren</button>
        </div>
      )}
    </aside>
  );
}

function BottomNav({page,setPage,syncStatus,syncNow}){
  var sl=SYNC_LABEL[syncStatus]||SYNC_LABEL.idle;
  var all=[
    {id:'dashboard',icon:'⊞',label:'Dashboard'},
    {id:'budget',icon:'⏱',label:'Budget'},
    {id:'kunden',icon:'◎',label:'Kunden'},
    {id:'kalkulation',icon:'≡',label:'Kalkulation'},
    {id:'reisekosten',icon:'✈',label:'Reise'},
    {id:'privat',icon:'◈',label:'Privat'},
    {id:'einstellungen',icon:'⚙',label:'Settings'},
  ];
  return(
    <div className="sk-bottom-nav" style={{flexDirection:'column'}}>
      <div style={{display:'flex',alignItems:'center',padding:'6px 0 2px',overflowX:'auto',WebkitOverflowScrolling:'touch',scrollbarWidth:'none',msOverflowStyle:'none'}}>
        {all.map(function(n){return(
          <button key={n.id} onClick={function(){setPage(n.id);}} style={{
            display:'flex',flexDirection:'column',alignItems:'center',gap:2,
            background:'none',border:'none',cursor:'pointer',fontFamily:'inherit',
            color:page===n.id?'#fff':'#666',padding:'6px 10px',flexShrink:0,
            borderBottom:page===n.id?'2px solid #fff':'2px solid transparent'
          }}>
            <span style={{fontSize:18}}>{n.icon}</span>
            <span style={{fontSize:9,whiteSpace:'nowrap'}}>{n.label}</span>
          </button>
        );})}
        {syncNow&&<button onClick={syncNow} style={{display:'flex',flexDirection:'column',alignItems:'center',gap:2,background:'none',border:'none',cursor:'pointer',color:'#888',padding:'6px 10px',flexShrink:0,fontFamily:'inherit',borderBottom:'2px solid transparent'}}>
          <span style={{fontSize:18}}>↻</span><span style={{fontSize:9,whiteSpace:'nowrap'}}>Sync</span>
        </button>}
      </div>
      <div style={{textAlign:'center',fontSize:9,padding:'2px 0 4px'}}><span style={{color:sl.c}}>{sl.t}</span></div>
    </div>
  );
}

// ─── SHARED UI ───────────────────────────────────────────────────────────────
function Card({children,style}){return <div style={Object.assign({background:'#fff',borderRadius:10,border:'1px solid var(--border)',boxShadow:'0 1px 4px rgba(0,0,0,0.05)',padding:20},style||{})}>{children}</div>;}
function Field({label,children,hint}){
  return(
    <div style={{marginBottom:14}}>
      <label style={{display:'block',fontSize:11,fontWeight:600,color:'#999',marginBottom:5,textTransform:'uppercase',letterSpacing:.8}}>{label}</label>
      {children}
      {hint&&<div style={{fontSize:11,color:'#bbb',marginTop:3}}>{hint}</div>}
    </div>
  );
}
function Modal({title,onClose,children,wide}){
  return(
    <div style={{position:'fixed',inset:0,background:'rgba(0,0,0,0.35)',zIndex:1000,display:'flex',alignItems:'center',justifyContent:'center',padding:20,backdropFilter:'blur(2px)'}}>
      <div style={{background:'#fff',borderRadius:12,width:'100%',maxWidth:wide?680:520,maxHeight:'90vh',overflow:'auto',boxShadow:'0 24px 64px rgba(0,0,0,0.18)'}}>
        <div style={{display:'flex',alignItems:'center',justifyContent:'space-between',padding:'18px 22px',borderBottom:'1px solid var(--border)'}}>
          <span style={{fontWeight:700,fontSize:15,letterSpacing:'-0.2px'}}>{title}</span>
          <button onClick={onClose} style={{background:'none',border:'none',cursor:'pointer',fontSize:22,color:'#bbb',lineHeight:1,padding:'0 2px'}}>&times;</button>
        </div>
        <div style={{padding:22}}>{children}</div>
      </div>
    </div>
  );
}
function Pill({active,onClick,children}){
  return(
    <button onClick={onClick} style={{padding:'7px 16px',borderRadius:20,fontSize:13,cursor:'pointer',fontFamily:'inherit',transition:'all .1s',background:active?'#111':'#fff',color:active?'#fff':'#6B6B6B',border:'1px solid '+(active?'#111':'var(--border)'),fontWeight:active?600:400}}>{children}</button>
  );
}

// ─── DASHBOARD ───────────────────────────────────────────────────────────────
function CashflowWidget({settings}){
  var [status,setStatus]=useState('idle'); // idle|loading|ok|error
  var [offenSum,setOffenSum]=useState(null);
  var [faelligSum,setFaelligSum]=useState(null);
  var [count,setCount]=useState(0);
  var hasSevDesk=!!(settings.sevDeskToken||settings.workerUrl);

  function laden(){
    if(!hasSevDesk) return;
    setStatus('loading');
    sdFetch(settings,'/Invoice?status=200&limit=100','GET')
      .then(function(r){return r.json();})
      .then(function(d){
        var invoices=d.objects||[];
        var now=Date.now()/1000;
        var offen=0; var faellig=0; var cnt=0;
        invoices.forEach(function(inv){
          if(inv.status==='200'||inv.status===200){
            var betrag=parseFloat(inv.sumGross||inv.sumNet||0);
            offen+=betrag; cnt++;
            if(inv.timeToPay && (inv.invoiceDate+inv.timeToPay*86400)<now) faellig+=betrag;
          }
        });
        setOffenSum(offen); setFaelligSum(faellig); setCount(cnt); setStatus('ok');
      })
      .catch(function(){setStatus('error');});
  }

  useEffect(function(){laden();},[]);

  if(!hasSevDesk) return(
    <Card style={{marginBottom:24,borderLeft:'3px solid var(--border)',background:'var(--cream)'}}>
      <div style={{display:'flex',alignItems:'center',justifyContent:'space-between'}}>
        <div>
          <div style={{fontWeight:600,fontSize:14,marginBottom:4}}>💶 Cashflow-Monitor</div>
          <div style={{fontSize:13,color:'var(--mid)'}}>Verbinde sevDesk unter <strong>Einstellungen → sevDesk</strong>, um offene Rechnungen und echten Umsatz live abzurufen.</div>
        </div>
      </div>
    </Card>
  );

  return(
    <Card style={{marginBottom:24,borderLeft:'3px solid var(--blue)'}}>
      <div style={{display:'flex',alignItems:'center',justifyContent:'space-between',marginBottom:14}}>
        <div style={{fontWeight:700,fontSize:15}}>💶 Cashflow-Monitor</div>
        <button className="btn-outline btn-sm" onClick={laden} disabled={status==='loading'} style={{display:'flex',alignItems:'center',gap:5}}><span style={{fontSize:13,lineHeight:1}}>{status==='loading'?'⏳':'↻'}</span>{status==='loading'?'Lädt…':'Aktualisieren'}</button>
      </div>
      {status==='idle'&&<div style={{color:'#aaa',fontSize:13}}>Wird geladen…</div>}
      {status==='error'&&<div style={{color:'#C0392B',fontSize:13}}>sevDesk nicht erreichbar. Bitte Worker-URL prüfen.</div>}
      {status==='ok'&&(
        <div style={{display:'flex',gap:20,flexWrap:'wrap'}}>
          <div>
            <div style={{fontSize:11,color:'#999',textTransform:'uppercase',letterSpacing:.8,marginBottom:6}}>Offene Rechnungen ({count})</div>
            <div style={{fontSize:30,fontWeight:700,color:'#111',letterSpacing:'-0.5px',lineHeight:1}}>{offenSum!==null?fmt(offenSum):'–'}</div>
            <div style={{fontSize:11,color:'#bbb',marginTop:4}}>brutto offen gesamt</div>
          </div>
          {faelligSum>0&&(
            <div>
              <div style={{fontSize:11,color:'#999',textTransform:'uppercase',letterSpacing:.8,marginBottom:6}}>davon überfällig</div>
              <div style={{fontSize:30,fontWeight:700,color:'var(--red)',letterSpacing:'-0.5px',lineHeight:1}}>{fmt(faelligSum)}</div>
              <div style={{fontSize:11,color:'var(--red)',marginTop:4,opacity:0.7}}>Zahlungsziel überschritten</div>
            </div>
          )}
        </div>
      )}
    </Card>
  );
}

function KapazitaetsWidget({state,setPage}){
  var angebote=(state.angebote||[]).filter(function(a){
    return !a.storniert&&(a.status==='Entwurf'||a.status==='Versendet');
  });
  var s=state.settings||{};
  if(angebote.length===0) return null;

  var total={planung:0,werkstatt:0,montage:0};
  angebote.forEach(function(a){
    var h=calcAngebotsStunden(a,s);
    total.planung+=h.planung;
    total.werkstatt+=h.werkstatt;
    total.montage+=h.montage;
  });
  var gesamt=total.planung+total.werkstatt+total.montage;
  if(gesamt===0) return null;

  var bars=[
    {label:'Planung',h:total.planung,color:'#3B6FE0'},
    {label:'Werkstatt / Schreinerei',h:total.werkstatt,color:'#1E1E1E'},
    {label:'Montage',h:total.montage,color:'#27AE60'},
  ];

  return(
    <Card style={{marginBottom:24}}>
      <div style={{display:'flex',alignItems:'center',justifyContent:'space-between',marginBottom:14}}>
        <div style={{fontWeight:700,fontSize:15}}>⏱ Kapazitäts-Vorschau</div>
        <span style={{fontSize:12,color:'#888'}}>aus {angebote.length} offenen Angeboten</span>
      </div>
      <div style={{display:'flex',gap:20,marginBottom:16,flexWrap:'wrap'}}>
        {bars.map(function(b){
          if(b.h===0) return null;
          var pct=gesamt>0?b.h/gesamt:0;
          var ampel=b.h>80?'#C0392B':(b.h>50?'#E67E22':b.color);
          return(
            <div key={b.label} style={{flex:'1 1 140px',minWidth:120}}>
              <div style={{display:'flex',justifyContent:'space-between',marginBottom:4}}>
                <span style={{fontSize:12,color:'#555',fontWeight:600}}>{b.label}</span>
                <span style={{fontSize:14,fontWeight:700,color:ampel}}>{b.h.toFixed(0)} h</span>
              </div>
              <div style={{background:'#F0F0F0',borderRadius:4,height:8,overflow:'hidden'}}>
                <div style={{width:Math.round(pct*100)+'%',height:'100%',background:ampel,borderRadius:4,transition:'width .5s'}}/>
              </div>
              <div style={{fontSize:10,color:'#aaa',marginTop:3}}>
                {b.h>80?'⚠ Kapazität prüfen':(b.h>50?'· mittel':'· frei')}
              </div>
            </div>
          );
        })}
      </div>
      <button className="btn-outline btn-sm" onClick={function(){setPage('budget');}}>Details in Budget & Zeiten →</button>
    </Card>
  );
}

function Dashboard({state,setPage}){
  var s=state.settings;
  var angebote=state.angebote||[];
  var anzahlungen=state.anzahlungen||[];
  var schluss=state.schlussrechnungen||[];

  // sevDesk: Daten direkt im Dashboard halten
  var [sdStatus,setSdStatus]=useState('idle');
  var [sdOffen,setSdOffen]=useState(null);
  var [sdCount,setSdCount]=useState(0);
  var hasSevDesk=!!(s.sevDeskToken||s.workerUrl);
  function ladenSevDesk(){
    if(!hasSevDesk) return;
    setSdStatus('loading');
    sdFetch(s,'/Invoice?status=200&limit=100','GET')
      .then(function(r){return r.json();})
      .then(function(d){
        var inv=d.objects||[]; var offen=0; var cnt=0;
        inv.forEach(function(i){if(i.status==='200'||i.status===200){offen+=parseFloat(i.sumGross||i.sumNet||0);cnt++;}});
        setSdOffen(offen);setSdCount(cnt);setSdStatus('ok');
      }).catch(function(){setSdStatus('error');});
  }
  useEffect(function(){ladenSevDesk();},[]);

  // Kennzahlen
  var aktiveAngebote=angebote.filter(function(a){return a.status==='Versendet'||a.status==='Freigegeben';});
  var angebotsvolumen=aktiveAngebote.reduce(function(t,a){return t+calcNetto(a,s);},0);
  var umsatzNetto=schluss.filter(function(r){return !r.storniert;}).reduce(function(t,r){return t+(r.gesamtNetto||0);},0)
    +anzahlungen.filter(function(r){return !r.storniert;}).reduce(function(t,r){return t+(parseFloat(r.betragNetto)||0);},0);
  var sortedAngebote=[].concat(angebote).sort(function(a,b){return b._ts>a._ts?1:-1;}).slice(0,8);

  return(
    <div className="sk-page" style={{flex:1,overflow:'auto'}}>
      <div style={{display:'flex',alignItems:'center',justifyContent:'space-between',marginBottom:28}}>
        <h1 style={{fontSize:24,fontWeight:700,letterSpacing:'-0.3px'}}>Dashboard</h1>
        <button className="btn-dark" onClick={function(){setPage('kalkulation');}} style={{padding:'9px 20px',letterSpacing:'.2px'}}>+ Neue Kalkulation</button>
      </div>

      {/* 3 KPI-Karten */}
      <div className="sk-grid-3" style={{marginBottom:24}}>
        <Card style={{cursor:'pointer'}} onClick={function(){setPage('angebote');}}>
          <div style={{fontSize:11,color:'#999',fontWeight:600,textTransform:'uppercase',letterSpacing:.8,marginBottom:10}}>Angebotsvolumen</div>
          <div style={{fontSize:36,fontWeight:700,letterSpacing:'-0.5px',lineHeight:1,marginBottom:8}}>{fmt(angebotsvolumen)}</div>
          <div style={{fontSize:12,color:'#bbb'}}>{aktiveAngebote.length} Angebot{aktiveAngebote.length!==1?'e':''} · versendet / freigegeben</div>
        </Card>
        <Card style={{cursor:'pointer'}} onClick={function(){setPage('schluss');}}>
          <div style={{fontSize:11,color:'#999',fontWeight:600,textTransform:'uppercase',letterSpacing:.8,marginBottom:10}}>Fakturiert (netto)</div>
          <div style={{fontSize:36,fontWeight:700,letterSpacing:'-0.5px',lineHeight:1,marginBottom:8}}>{fmt(umsatzNetto)}</div>
          <div style={{fontSize:12,color:'#bbb'}}>Schluss + Anzahlungsrechnungen</div>
        </Card>
        <Card>
          <div style={{display:'flex',alignItems:'center',justifyContent:'space-between',marginBottom:10}}>
            <div style={{fontSize:11,color:'#999',fontWeight:600,textTransform:'uppercase',letterSpacing:.8}}>Offen (sevDesk)</div>
            {hasSevDesk&&(
              <button onClick={ladenSevDesk} disabled={sdStatus==='loading'} title="Aktualisieren" style={{background:'none',border:'none',padding:'2px 4px',cursor:'pointer',color:'#ccc',fontSize:15,lineHeight:1,borderRadius:4,transition:'color .15s'}}
                onMouseEnter={function(e){e.currentTarget.style.color='#666';}}
                onMouseLeave={function(e){e.currentTarget.style.color='#ccc';}}>
                {sdStatus==='loading'?'…':'↻'}
              </button>
            )}
          </div>
          {!hasSevDesk?(
            <><div style={{fontSize:22,fontWeight:700,color:'#ddd',marginBottom:8}}>–</div>
            <div style={{fontSize:12,color:'#bbb'}}>Einstellungen → sevDesk verbinden</div></>
          ):sdStatus==='error'?(
            <><div style={{fontSize:15,fontWeight:600,color:'var(--red)',marginBottom:8}}>Verbindungsfehler</div>
            <div style={{fontSize:12,color:'#bbb'}}>Worker-URL in Einstellungen prüfen</div></>
          ):(
            <><div style={{fontSize:36,fontWeight:700,letterSpacing:'-0.5px',lineHeight:1,marginBottom:8,color:sdStatus==='ok'&&sdOffen===0?'var(--green)':'#111'}}>
              {sdOffen!==null?fmt(sdOffen):'–'}
            </div>
            <div style={{fontSize:12,color:'#bbb'}}>
              {sdStatus==='ok'?(sdCount>0?(sdCount+' Rechnung'+(sdCount!==1?'en':'')+' offen'):'Alles bezahlt ✓'):'Wird geladen…'}
            </div></>
          )}
        </Card>
      </div>

      {/* Angebote-Tabelle */}
      {angebote.length>0&&(
        <Card>
          <div style={{display:'flex',alignItems:'center',justifyContent:'space-between',marginBottom:14}}>
            <div style={{fontWeight:700,fontSize:15}}>Letzte Angebote</div>
            <button className="btn-outline btn-sm" onClick={function(){setPage('angebote');}}>Alle anzeigen →</button>
          </div>
          <div className="sk-tbl">
            <table style={{width:'100%',borderCollapse:'collapse',fontSize:14}}>
              <thead><tr style={{borderBottom:'1px solid var(--border)'}}>
                {['AN-Nr.','Datum','Kunde','Projekt','Status','Netto'].map(function(h){return <th key={h} style={{textAlign:'left',padding:'6px 8px',fontSize:12,color:'#888',fontWeight:600}}>{h}</th>;})}
              </tr></thead>
              <tbody>
                {sortedAngebote.map(function(a){
                  var k=(state.kunden||[]).find(function(c){return c.id===a.kundeId;});
                  var n=calcNetto(a,s);
                  return(
                    <tr key={a.id} style={{borderBottom:'1px solid var(--border)',cursor:'pointer'}} onClick={function(){setPage('angebote');}}>
                      <td style={{padding:'10px 8px',color:'#888',fontWeight:600}}>{a.angebotNr?fmtAngebotNr(a.angebotNr):<span style={{color:'#ccc',fontStyle:'italic'}}>Entwurf</span>}</td>
                      <td style={{padding:'10px 8px',color:'#888'}}>{a.datum||'–'}</td>
                      <td style={{padding:'10px 8px',fontWeight:500}}>{k?k.name:'–'}</td>
                      <td style={{padding:'10px 8px',color:'#555'}}>{a.titel||'–'}</td>
                      <td style={{padding:'10px 8px'}}><Badge s={a.status||'Entwurf'}/></td>
                      <td style={{padding:'10px 8px',fontWeight:600,textAlign:'right'}}>{fmt(n)}</td>
                    </tr>
                  );
                })}
              </tbody>
            </table>
          </div>
        </Card>
      )}
    </div>
  );
}

// ─── KUNDEN ──────────────────────────────────────────────────────────────────
function KundenPage({state,dispatch}){
  var [modal,setModal]=useState(false);
  var [form,setForm]=useState({});
  var [bulkStatus,setBulkStatus]=useState('idle');
  var [bulkMsg,setBulkMsg]=useState('');
  var kunden=state.kunden||[];
  var cfg=state.settings;
  var hasSevDesk=!!(cfg.sevDeskToken||cfg.workerUrl);
  var linkedCount=kunden.filter(function(k){return k.sevdeskLinked;}).length;
  function open(k){setForm(k?Object.assign({},k):{id:uid(),name:'',strasse:'',plzOrt:'',tel:'',email:''});setModal(true);}
  function doSave(){dispatch({type:'SAVE_KUNDE',payload:form});setModal(false);}
  function del(id){if(confirm('Kunden löschen?'))dispatch({type:'DEL_KUNDE',payload:id});}
  function syncAlle(){
    if(!hasSevDesk){alert('Kein sevDesk-Token oder Worker-URL hinterlegt (Einstellungen → sevDesk).');return;}
    setBulkStatus('loading');setBulkMsg('');
    var list=kunden.slice();
    var results=[];
    function next(i){
      if(i>=list.length){
        var neu=results.filter(function(r){return r.created;}).length;
        var vor=results.filter(function(r){return !r.created;}).length;
        setBulkStatus('ok');
        setBulkMsg(neu+' neu angelegt, '+vor+' bereits vorhanden.');
        return;
      }
      sdSyncKunde(cfg,list[i]).then(function(res){results.push(res);next(i+1);}).catch(function(e){
        setBulkStatus('error');setBulkMsg('Fehler bei "'+list[i].name+'": '+(e.message||'?'));
      });
    }
    next(0);
  }
  var iw={width:'100%'};
  return(
    <div className="sk-page" style={{flex:1,overflow:'auto'}}>
      <div style={{display:'flex',alignItems:'center',justifyContent:'space-between',marginBottom:24}}>
        <h1 style={{fontSize:22,fontWeight:700}}>Kunden</h1>
        <div style={{display:'flex',gap:8,alignItems:'center'}}>
          {hasSevDesk&&bulkStatus==='idle'&&kunden.length>0&&<button className="btn-outline" onClick={syncAlle} style={{color:'#2D6BE4',borderColor:'#2D6BE4'}}>📒 Alle → sevDesk {linkedCount>0&&<span style={{fontSize:11,background:'#E3F0FF',color:'#2D6BE4',borderRadius:10,padding:'1px 6px',marginLeft:4}}>{linkedCount}/{kunden.length}</span>}</button>}
          {hasSevDesk&&bulkStatus==='loading'&&<span style={{fontSize:13,color:'#2D6BE4'}}>⏳ Wird synchronisiert…</span>}
          {hasSevDesk&&bulkStatus==='ok'&&<span style={{fontSize:13,color:'#27AE60'}}>✓ {bulkMsg} <button onClick={function(){setBulkStatus('idle');}} style={{background:'none',border:'none',cursor:'pointer',fontSize:11,color:'#aaa',textDecoration:'underline'}}>Reset</button></span>}
          {hasSevDesk&&bulkStatus==='error'&&<span style={{fontSize:13,color:'#C0392B'}}>✗ {bulkMsg} <button onClick={function(){setBulkStatus('idle');}} style={{background:'none',border:'none',cursor:'pointer',fontSize:11,color:'#aaa',textDecoration:'underline'}}>Retry</button></span>}
          <button className="btn-dark" onClick={function(){open(null);}}>+ Neuer Kunde</button>
        </div>
      </div>
      <Card>
        {kunden.length===0&&<div style={{color:'#999',padding:'12px 0',fontSize:14}}>Noch keine Kunden angelegt.</div>}
        {kunden.length>0&&(
          <div className="sk-tbl">
          <table style={{width:'100%',borderCollapse:'collapse',fontSize:14}}>
            <thead><tr style={{borderBottom:'1px solid var(--border)'}}>
              {['Name','Adresse','Kontakt','sevDesk',''].map(function(h){return <th key={h} style={{textAlign:'left',padding:'6px 8px',fontSize:12,color:'#888',fontWeight:600}}>{h}</th>;})}
            </tr></thead>
            <tbody>
              {kunden.map(function(k){return(
                <tr key={k.id} style={{borderBottom:'1px solid var(--border)'}}>
                  <td style={{padding:'9px 8px',fontWeight:600}}>{k.name}</td>
                  <td style={{padding:'9px 8px',color:'#666'}}>{[k.strasse,k.plzOrt].filter(Boolean).join(', ')||'–'}</td>
                  <td style={{padding:'9px 8px',color:'#666'}}>{[k.tel,k.email].filter(Boolean).join(' · ')||'–'}</td>
                  <td style={{padding:'9px 8px'}}>
                    {hasSevDesk?<SevDeskKundeButton kunde={k} settings={state.settings} dispatch={dispatch}/>
                      :<span style={{fontSize:12,color:'#ccc'}}>–</span>}
                  </td>
                  <td style={{padding:'9px 8px',textAlign:'right',whiteSpace:'nowrap'}}>
                    <button className="btn-outline btn-sm" onClick={function(){open(k);}} style={{marginRight:6}}>Bearbeiten</button>
                    <button className="btn-danger btn-sm" onClick={function(){del(k.id);}}>Löschen</button>
                  </td>
                </tr>
              );})}
            </tbody>
          </table>
          </div>
        )}
      </Card>
      {modal&&(
        <Modal title={form.name?'Kunde bearbeiten':'Neuer Kunde'} onClose={function(){setModal(false);}}>
          <Field label="Name *"><input value={form.name||''} onChange={function(e){setForm(Object.assign({},form,{name:e.target.value}));}} style={iw}/></Field>
          <Field label="Straße"><input value={form.strasse||''} onChange={function(e){setForm(Object.assign({},form,{strasse:e.target.value}));}} style={iw}/></Field>
          <Field label="PLZ Ort"><input value={form.plzOrt||''} onChange={function(e){setForm(Object.assign({},form,{plzOrt:e.target.value}));}} style={iw}/></Field>
          <div className="sk-grid-2">
            <Field label="Telefon"><input value={form.tel||''} onChange={function(e){setForm(Object.assign({},form,{tel:e.target.value}));}} style={iw}/></Field>
            <Field label="E-Mail"><input value={form.email||''} onChange={function(e){setForm(Object.assign({},form,{email:e.target.value}));}} style={iw}/></Field>
          </div>
          <div style={{display:'flex',gap:10,justifyContent:'flex-end',marginTop:8}}>
            <button className="btn-outline" onClick={function(){setModal(false);}}>Abbrechen</button>
            <button className="btn-dark" onClick={doSave} disabled={!form.name}>Speichern</button>
          </div>
        </Modal>
      )}
    </div>
  );
}

// ─── SEVDESK KALKULATION BUTTON ──────────────────────────────────────────────
function SevDeskKalkButton({kalk,kunde,settings,dispatch}){
  if(!settings.sevDeskToken&&!settings.workerUrl) return null;
  return <SevDeskBtn
    label="📒 → sevDesk"
    linked={!!kalk.sevdeskLinked}
    linkedUrl={kalk.sevdeskUrl||''}
    locked={false}
    onSend={function(){return sendToSevDesk(settings,kalk,kunde);}}
    onSuccess={function(url){
      if(dispatch) dispatch({type:'SAVE_KALKULATION',payload:Object.assign({},kalk,{sevdeskLinked:true,sevdeskUrl:url,_ts:Date.now()})});
    }}
  />;
}

// ─── KALKULATION PAGE ────────────────────────────────────────────────────────
function KalkulationPage({state,dispatch,startNeu,startEdit}){
  var kalks=state.kalkulationen||[];
  var s=state.settings;
  function del(id){if(confirm('Kalkulation löschen?'))dispatch({type:'DEL_KALKULATION',payload:id});}
  function dup(k){
    var maxNr=kalks.reduce(function(m,x){return Math.max(m,x.nr||0);},0);
    var kopie=Object.assign({},k,{id:uid(),nr:maxNr+1,datum:today(),_ts:Date.now(),sevdeskLinked:undefined,sevdeskUrl:undefined});
    dispatch({type:'SAVE_KALKULATION',payload:kopie});
  }
  return(
    <div className="sk-page" style={{flex:1,overflow:'auto'}}>
      <div style={{display:'flex',alignItems:'center',justifyContent:'space-between',marginBottom:24}}>
        <h1 style={{fontSize:22,fontWeight:700}}>Kalkulation</h1>
        <button className="btn-dark" onClick={startNeu} disabled={!(state.kunden&&state.kunden.length>0)} title={!(state.kunden&&state.kunden.length>0)?'Zuerst einen Kunden anlegen':''}>+ Neue Kalkulation</button>
      </div>
      {(state.kunden&&state.kunden.length===0)&&(
        <div style={{background:'#FFF8E1',border:'1px solid #FFE082',borderRadius:8,padding:14,fontSize:13,color:'#555',marginBottom:16}}>
          Bitte zuerst unter <strong>Kunden</strong> einen Kunden anlegen, dann kann eine Kalkulation gestartet werden.
        </div>
      )}
      <Card>
        {kalks.length===0&&<div style={{color:'#999',padding:'12px 0',fontSize:14}}>Noch keine Kalkulationen angelegt.</div>}
        {kalks.length>0&&(
          <div className="sk-tbl">
          <table style={{width:'100%',borderCollapse:'collapse',fontSize:14}}>
            <thead><tr style={{borderBottom:'1px solid var(--border)'}}>
              {['Nr.','Datum','Kunde','Projekt','Netto','Brutto',''].map(function(h){return <th key={h} style={{textAlign:'left',padding:'6px 8px',fontSize:12,color:'#888',fontWeight:600}}>{h}</th>;})}
            </tr></thead>
            <tbody>
              {[].concat(kalks).sort(function(a,b){return b._ts>a._ts?1:-1;}).map(function(k){
                var kd=(state.kunden||[]).find(function(c){return c.id===k.kundeId;});
                var n=calcNetto(k,s);
                return(
                  <tr key={k.id} style={{borderBottom:'1px solid var(--border)'}}>
                    <td style={{padding:'9px 8px',color:'#888',fontWeight:600}}>#{String(k.nr||0).padStart(4,'0')}</td>
                    <td style={{padding:'9px 8px',color:'#888'}}>{k.datum||'–'}</td>
                    <td style={{padding:'9px 8px',fontWeight:500}}>{kd?kd.name:'–'}</td>
                    <td style={{padding:'9px 8px'}}>{k.titel||'–'}</td>
                    <td style={{padding:'9px 8px',textAlign:'right'}}>{fmt(n)}</td>
                    <td style={{padding:'9px 8px',textAlign:'right',fontWeight:600}}>{fmt(n*1.19)}</td>
                    <td style={{padding:'9px 8px',textAlign:'right',whiteSpace:'nowrap'}}>
                      <button className="btn-outline btn-sm" onClick={function(){startEdit(k);}} style={{marginRight:6}}>Bearbeiten</button>
                      <button className="btn-outline btn-sm" onClick={function(){dup(k);}} style={{marginRight:6}} title="Kalkulation duplizieren">⊕ Kopie</button>
                      <button className="btn-outline btn-sm" onClick={function(){generatePDF(k,kd,s);}} style={{marginRight:6}} title="Entwurfs-PDF erstellen">📄 Entwurf PDF</button>
                      <span style={{marginRight:6}}><SevDeskKalkButton kalk={k} kunde={kd} settings={s} dispatch={dispatch}/></span>
                      <button className="btn-danger btn-sm" onClick={function(){del(k.id);}}>&#10005;</button>
                    </td>
                  </tr>
                );
              })}
            </tbody>
          </table>
          </div>
        )}
      </Card>
    </div>
  );
}

// ─── ANGEBOTE PAGE ───────────────────────────────────────────────────────────
function AngebotePage({state,dispatch,setPage}){
  var angebote=state.angebote||[];
  var s=state.settings;
  var [filter,setFilter]=useState('Alle');
  var [rechnungModal,setRechnungModal]=useState(null); // angebot für Rechnungsauswahl
  var list=filter==='Alle'?angebote:angebote.filter(function(a){return a.status===filter;});

  function setStatus(id,status){
    dispatch({type:'SET_ANGEBOT_STATUS',payload:{id:id,status:status}});
  }
  function del(id){
    var a=angebote.find(function(x){return x.id===id;});
    if(a&&a.status==='Versendet'){
      if(!confirm('Dieses Angebot ist bereits versendet und hat eine feste Nummer. Wirklich löschen?\n\nHinweis: Verknüpfte Rechnungen bleiben bestehen.'))return;
    } else {
      if(!confirm('Angebot löschen?'))return;
    }
    dispatch({type:'DEL_ANGEBOT',payload:id});
  }

  function RechnungWaehlen({angebot,onClose}){
    var netto=calcNetto(angebot,s);
    // pct: 40 | 60 | 100 (100 = Vollrechnung ohne Anzahlung)
    var [pct,setPct]=useState(angebot.anzahlungProzent||40);
    var [typ,setTyp]=useState('anzahlung'); // 'anzahlung' | 'schluss', nur relevant wenn pct<100
    var [datum,setDatum]=useState(today());

    var anzNetto=pct===100?0:netto*pct/100;
    var schlussNetto=netto-anzNetto;

    function erstellen(){
      // Rechnungsnummer vor dem Speichern berechnen (Anzahlung: 2001+, Schluss/Voll: 1002+)
      var isAnzahlung=pct<100&&typ==='anzahlung';
      var baseNr=isAnzahlung?(state.nextANr||2001):(state.nextSNr||1002);
      var rNr=String(baseNr).padStart(4,'0');
      var rec={
        id:uid(),rechnungNr:rNr,datum:datum,
        angebotId:angebot.id,kundeId:angebot.kundeId,
        angebotNr:angebot.angebotNr,titel:angebot.titel,
        gesamtNetto:netto,_ts:Date.now()
      };
      if(pct===100){
        rec.betragNetto=netto;rec.anzahlungNetto=0;rec.abzugAnzahlung=0;rec.restNetto=netto;rec.vollrechnung=true;
        dispatch({type:'SAVE_SCHLUSS',payload:rec});setPage('schluss');
      } else if(typ==='anzahlung'){
        rec.betragNetto=anzNetto;rec.prozent=pct;
        dispatch({type:'SAVE_ANZAHLUNG',payload:rec});setPage('anzahlung');
      } else {
        rec.anzahlungNetto=anzNetto;rec.abzugAnzahlung=anzNetto;rec.betragNetto=schlussNetto;rec.restNetto=schlussNetto;
        dispatch({type:'SAVE_SCHLUSS',payload:rec});setPage('schluss');
      }
      onClose();
    }

    var angebotLabel=angebot.angebotNr?fmtAngebotNr(angebot.angebotNr):(angebot.titel||'Entwurf');
    return(
      <Modal title={'Rechnung vorbereiten – '+angebotLabel} onClose={onClose}>
        <div style={{marginBottom:20}}>
          <div style={{fontSize:13,color:'#666',marginBottom:12}}>Projekt: <strong>{angebot.titel}</strong></div>

          <Field label="Anzahlung / Abrechnungsart">
            <div style={{display:'flex',gap:8,marginBottom:16,flexWrap:'wrap'}}>
              <Pill active={pct===40} onClick={function(){setPct(40);setTyp('anzahlung');}}>40 % Anzahlung</Pill>
              <Pill active={pct===60} onClick={function(){setPct(60);setTyp('anzahlung');}}>60 % Anzahlung</Pill>
              <Pill active={pct===100} onClick={function(){setPct(100);}}>100 % Vollrechnung</Pill>
            </div>
          </Field>

          {pct<100&&(
            <div style={{background:'var(--cream)',borderRadius:8,padding:14,marginBottom:16,fontSize:14}}>
              <div style={{display:'flex',justifyContent:'space-between',marginBottom:6}}>
                <span style={{color:'#888'}}>Gesamtbetrag netto</span><strong>{fmt(netto)}</strong>
              </div>
              <div style={{display:'flex',justifyContent:'space-between',marginBottom:6}}>
                <span style={{color:'#888'}}>Anzahlung {pct} % (netto)</span><strong style={{color:'#3B6FE0'}}>{fmt(anzNetto)}</strong>
              </div>
              <div style={{display:'flex',justifyContent:'space-between'}}>
                <span style={{color:'#888'}}>Schlussrechnung {100-pct} % (netto)</span><strong style={{color:'#27AE60'}}>{fmt(schlussNetto)}</strong>
              </div>
            </div>
          )}
          {pct===100&&(
            <div style={{background:'#F0FFF4',border:'1px solid #B2DFDB',borderRadius:8,padding:14,marginBottom:16,fontSize:14}}>
              <div style={{display:'flex',justifyContent:'space-between'}}>
                <span style={{color:'#888'}}>Vollbetrag netto</span><strong style={{color:'#27AE60'}}>{fmt(netto)}</strong>
              </div>
              <div style={{fontSize:12,color:'#888',marginTop:6}}>Eine Rechnung über den gesamten Betrag, keine Anzahlung.</div>
            </div>
          )}

          {pct<100&&(
            <Field label="Welche Rechnung jetzt erstellen?">
              <div style={{display:'flex',gap:10,flexWrap:'wrap'}}>
                <Pill active={typ==='anzahlung'} onClick={function(){setTyp('anzahlung');}}>💶 Anzahlungsrechnung ({pct} %)</Pill>
                <Pill active={typ==='schluss'} onClick={function(){setTyp('schluss');}}>✓ Schlussrechnung ({100-pct} %)</Pill>
              </div>
            </Field>
          )}

          <Field label="Rechnungsdatum">
            <input value={datum} onChange={function(e){setDatum(e.target.value);}} style={{width:180}}/>
          </Field>
          <div style={{background:pct===100||typ==='schluss'?'#EDFAF3':'#EBF1FF',border:'1px solid '+(pct===100||typ==='schluss'?'#B7EFD0':'#C5D8FF'),borderRadius:8,padding:12,fontSize:13}}>
            {pct===100&&<div>Vollrechnung über <strong>{fmt(netto)}</strong> netto ({fmt(netto*1.19)} brutto)</div>}
            {pct<100&&typ==='anzahlung'&&<div>Anzahlungsrechnung über <strong>{fmt(anzNetto)}</strong> netto ({fmt(anzNetto*1.19)} brutto)</div>}
            {pct<100&&typ==='schluss'&&<div>Schlussrechnung: Gesamtbetrag {fmt(netto)} netto, abzgl. Anzahlung {fmt(anzNetto)}, verbleibend <strong>{fmt(schlussNetto)}</strong> netto ({fmt(schlussNetto*1.19)} brutto)</div>}
          </div>
        </div>
        <div style={{display:'flex',gap:10,justifyContent:'flex-end'}}>
          <button className="btn-outline" onClick={onClose}>Abbrechen</button>
          <button className="btn-dark" onClick={erstellen}>Rechnung erstellen →</button>
        </div>
      </Modal>
    );
  }

  return(
    <div className="sk-page" style={{flex:1,overflow:'auto'}}>
      <div style={{display:'flex',alignItems:'center',justifyContent:'space-between',marginBottom:16}}>
        <h1 style={{fontSize:22,fontWeight:700}}>Angebote</h1>
      </div>
      <div style={{display:'flex',gap:8,marginBottom:16,flexWrap:'wrap'}}>
        {['Alle','Entwurf','Versendet','Überarbeitet'].map(function(f){return(
          <button key={f} onClick={function(){setFilter(f);}} style={{padding:'5px 14px',borderRadius:20,fontSize:13,cursor:'pointer',fontFamily:'inherit',background:filter===f?'#1E1E1E':'#fff',color:filter===f?'#fff':'#555',border:'1px solid '+(filter===f?'#1E1E1E':'var(--border)')}}>{f}</button>
        );})}
      </div>
      <Card>
        {list.length===0&&<div style={{color:'#999',padding:'12px 0',fontSize:14}}>Keine Angebote.</div>}
        {list.length>0&&(
          <div className="sk-tbl">
          <table style={{width:'100%',borderCollapse:'collapse',fontSize:14}}>
            <thead><tr style={{borderBottom:'1px solid var(--border)'}}>
              {['AN-Nr.','Datum','Kunde','Projekt','Status','Netto','Brutto','Aktionen'].map(function(h){return <th key={h} style={{textAlign:'left',padding:'6px 8px',fontSize:12,color:'#888',fontWeight:600}}>{h}</th>;})}
            </tr></thead>
            <tbody>
              {[].concat(list).sort(function(a,b){return b._ts>a._ts?1:-1;}).map(function(a){
                var kd=(state.kunden||[]).find(function(c){return c.id===a.kundeId;});
                var n=calcNetto(a,s);
                var isVersendet=a.status==='Versendet';
                var isUeberarbeitet=a.status==='Überarbeitet';
                return(
                  <tr key={a.id} style={{borderBottom:'1px solid var(--border)',opacity:isUeberarbeitet?0.6:1}}>
                    <td style={{padding:'9px 8px',color:'#888',fontWeight:700}}>{a.angebotNr?fmtAngebotNr(a.angebotNr):<span style={{color:'#ccc',fontStyle:'italic'}}>–</span>}</td>
                    <td style={{padding:'9px 8px',color:'#888'}}>{a.datum||'–'}</td>
                    <td style={{padding:'9px 8px',fontWeight:500}}>{kd?kd.name:'–'}</td>
                    <td style={{padding:'9px 8px'}}>{a.titel||'–'}{a.ersetztAngebotId&&<span style={{fontSize:11,color:'#888',marginLeft:6,fontStyle:'italic'}}>(Überarbeitung)</span>}</td>
                    <td style={{padding:'9px 8px'}}><Badge s={a.status||'Entwurf'}/></td>
                    <td style={{padding:'9px 8px',textAlign:'right'}}>{fmt(n)}</td>
                    <td style={{padding:'9px 8px',textAlign:'right',fontWeight:600}}>{fmt(n*1.19)}</td>
                    <td style={{padding:'9px 8px',textAlign:'right',whiteSpace:'nowrap'}}>
                      {a.storniert&&<span style={{fontSize:11,padding:'2px 6px',borderRadius:10,background:'#F8D7DA',color:'#721C24',fontWeight:700,marginRight:6}}>STORNO</span>}
                      {a.locked&&!a.storniert&&<span title="Gesperrt nach sevDesk-Export" style={{fontSize:12,marginRight:4,color:'#888'}}>🔒</span>}
                      {!a.locked&&!isVersendet&&a.status!=='Überarbeitet'&&(
                        <button className="btn-dark btn-sm" onClick={function(){setStatus(a.id,'Versendet');}} style={{marginRight:4,background:'#3B6FE0'}}>→ Versenden</button>
                      )}
                      {!a.locked&&isVersendet&&(
                        <button className="btn-dark btn-sm" onClick={function(){setRechnungModal(a);}} style={{marginRight:4,background:'#27AE60'}}>Rechnung vorbereiten</button>
                      )}
                      {!a.locked&&isVersendet&&!a.storniert&&(
                        <button className="btn-outline btn-sm" onClick={function(){if(confirm('Überarbeitete Version erstellen? Das Original wird als "Überarbeitet" markiert.'))dispatch({type:'UEBERARBEITEN_ANGEBOT',payload:a.id});}} style={{marginRight:4,color:'#B0B0B0',borderColor:'#B0B0B0'}}>Überarbeiten</button>
                      )}
                      {isVersendet&&!a.storniert&&<span style={{marginRight:4}}><SevDeskAngebotButton angebot={a} kunden={state.kunden||[]} settings={s} dispatch={dispatch}/></span>}
                      <button className="btn-outline btn-sm" onClick={function(){generatePDF(a,kd,s);}} style={{marginRight:4}}>PDF</button>
                      {a.locked&&!a.storniert&&<button className="btn-outline btn-sm" onClick={function(){dispatch({type:'DUPLICATE_RECORD',payload:{collection:'angebote',id:a.id}});}} style={{marginRight:4,color:'#2D6BE4',borderColor:'#2D6BE4'}} title="Gesperrten Datensatz als Kopie öffnen">⊕ Kopie</button>}
                      {a.locked&&!a.storniert&&<button className="btn-danger btn-sm" onClick={function(){if(confirm('Angebot '+(a.angebotNr?fmtAngebotNr(a.angebotNr):a.id)+' stornieren?'))dispatch({type:'STORNO_RECORD',payload:{collection:'angebote',id:a.id}});}} style={{marginRight:4}}>Stornieren</button>}
                      {!a.locked&&<button className="btn-danger btn-sm" onClick={function(){del(a.id);}}>&#10005;</button>}
                    </td>
                  </tr>
                );
              })}
            </tbody>
          </table>
          </div>
        )}
      </Card>
      {rechnungModal&&<RechnungWaehlen angebot={rechnungModal} onClose={function(){setRechnungModal(null);}}/>}
    </div>
  );
}

// ─── SEVDESK KUNDE BUTTON ────────────────────────────────────────────────────
function SevDeskKundeButton({kunde,settings,dispatch}){
  // Status aus persistierten Daten initialisieren
  var [status,setStatus]=useState(kunde.sevdeskLinked?'ok':'idle');
  var [msg,setMsg]=useState('');
  var [url,setUrl]=useState(kunde.sevdeskUrl||'');
  var [created,setCreated]=useState(false);
  if(!settings.sevDeskToken&&!settings.workerUrl) return null;
  function send(forceUpdate){
    setStatus('loading');setMsg('');
    // forceUpdate: Contact-ID mitgeben damit sdSyncKunde direkt PUT nutzt
    var kundeForSync=forceUpdate&&kunde.sevdeskContactId?kunde:Object.assign({},kunde,{sevdeskContactId:undefined});
    sdSyncKunde(settings,kundeForSync).then(function(res){
      var contactUrl='https://my.sevdesk.de/#/contacts/detail/'+res.contact.id;
      setStatus('ok');setCreated(res.created);setUrl(contactUrl);
      // Contact-ID + Status persistieren
      if(dispatch) dispatch({type:'SAVE_KUNDE',payload:Object.assign({},kunde,{
        sevdeskLinked:true,sevdeskUrl:contactUrl,sevdeskContactId:String(res.contact.id)
      })});
    }).catch(function(e){setStatus('error');setMsg(e.message||'Fehler');});
  }
  return(
    <span style={{display:'inline-flex',alignItems:'center',gap:4}}>
      {status==='idle'&&<button onClick={function(){send(false);}} className="btn-outline btn-sm" style={{color:'#2D6BE4',borderColor:'#2D6BE4'}}>📒 sevDesk</button>}
      {status==='loading'&&<span style={{fontSize:12,color:'#2D6BE4'}}>⏳…</span>}
      {status==='ok'&&<>
        <a href={url} target="_blank" rel="noopener" style={{fontSize:12,color:'#27AE60',fontWeight:600,textDecoration:'none'}}>✓ sevDesk →</a>
        <button onClick={function(){send(true);}} title="Kontaktdaten in sevDesk überschreiben" style={{background:'none',border:'none',cursor:'pointer',fontSize:12,color:'#888',padding:'0 2px'}}>↻</button>
      </>}
      {status==='error'&&<span style={{fontSize:12,color:'#C0392B'}} title={msg}>✗ {msg.slice(0,40)} <button onClick={function(){setStatus('idle');}} style={{background:'none',border:'none',cursor:'pointer',fontSize:11,color:'#aaa',textDecoration:'underline',padding:0}}>Retry</button></span>}
    </span>
  );
}

// ─── SEVDESK BUTTONS ─────────────────────────────────────────────────────────
function SevDeskBtn({label,onSend,linked,linkedUrl,onSuccess,locked}){
  var [status,setStatus]=useState(linked?'ok':'idle');
  var [msg,setMsg]=useState('');
  var [url,setUrl]=useState(linkedUrl||'');
  function send(){
    setStatus('loading');setMsg('');
    onSend().then(function(link){
      setStatus('ok');setUrl(link);
      if(onSuccess) onSuccess(link);
    }).catch(function(e){setStatus('error');setMsg(e.message||'Fehler');});
  }
  return(
    <span style={{display:'inline-flex',alignItems:'center',gap:4}}>
      {status==='idle'&&<button onClick={send} className="btn-outline btn-sm" style={{color:'#2D6BE4',borderColor:'#2D6BE4'}}>{label}</button>}
      {status==='loading'&&<span style={{fontSize:12,color:'#2D6BE4'}}>⏳…</span>}
      {status==='ok'&&(
        <span style={{display:'inline-flex',alignItems:'center',gap:3}}>
          <a href={url} target="_blank" rel="noopener" style={{fontSize:12,color:'#27AE60',fontWeight:600,textDecoration:'none'}}>✓ sevDesk →</a>
          {!locked&&<button onClick={function(){if(confirm('Nochmal an sevDesk senden? Es wird ein neuer Eintrag erstellt.'))send();}} style={{background:'none',border:'none',cursor:'pointer',fontSize:10,color:'#ccc',padding:'0 2px'}} title="Nochmal senden">↻</button>}
        </span>
      )}
      {status==='error'&&<span style={{fontSize:12,color:'#C0392B'}}>✗ {msg||'Fehler'} <button onClick={function(){setStatus('idle');}} style={{background:'none',border:'none',cursor:'pointer',fontSize:11,color:'#aaa',textDecoration:'underline',padding:0}}>Retry</button></span>}
    </span>
  );
}
function SevDeskAngebotButton({angebot,kunden,settings,dispatch}){
  if(!settings.sevDeskToken&&!settings.workerUrl) return null;
  var kunde=kunden.find(function(k){return k.id===angebot.kundeId;});
  return <SevDeskBtn label="📒 sevDesk" linked={!!angebot.sevdeskLinked} linkedUrl={angebot.sevdeskUrl||''} locked={!!angebot.locked}
    onSend={function(){return sendToSevDesk(settings,angebot,kunde);}}
    onSuccess={function(url){
      if(dispatch){
        dispatch({type:'SAVE_ANGEBOT',payload:Object.assign({},angebot,{sevdeskLinked:true,sevdeskUrl:url,_ts:Date.now()})});
        dispatch({type:'LOCK_RECORD',payload:{collection:'angebote',id:angebot.id,action:'Exportiert nach sevDesk'}});
      }
    }}/>;
}
function SevDeskRechnungButton({rechnung,typ,angebot,kunden,settings,dispatch,saveAction,collection}){
  if(!settings.sevDeskToken&&!settings.workerUrl) return null;
  var kunde=kunden.find(function(k){return k.id===rechnung.kundeId;});
  var col=collection||(saveAction==='SAVE_ANZAHLUNG'?'anzahlungen':'schlussrechnungen');
  return <SevDeskBtn label="📒 sevDesk" linked={!!rechnung.sevdeskLinked} linkedUrl={rechnung.sevdeskUrl||''} locked={!!rechnung.locked}
    onSend={function(){return sdCreateInvoice(settings,rechnung,typ,angebot||null,kunde);}}
    onSuccess={function(url){
      if(dispatch){
        dispatch({type:saveAction||'SAVE_SCHLUSS',payload:Object.assign({},rechnung,{sevdeskLinked:true,sevdeskUrl:url,_ts:Date.now()})});
        dispatch({type:'LOCK_RECORD',payload:{collection:col,id:rechnung.id,action:'Exportiert nach sevDesk'}});
      }
    }}/>;
}

// ─── ANZAHLUNGSRECHNUNGEN ────────────────────────────────────────────────────
function AzahlungPage({state,dispatch,setPage}){
  var list=state.anzahlungen||[];
  var s=state.settings;
  var [schlussModal,setSchlussModal]=useState(null); // {anzahlung, angebot}
  function del(id){if(confirm('Anzahlungsrechnung löschen?'))dispatch({type:'DEL_ANZAHLUNG',payload:id});}
  function storno(collection,id,label){
    var g=window.prompt('Stornierungsgrund für '+label+' (Pflichtfeld – wird im Audit-Log gespeichert):','');
    if(g===null) return; // abgebrochen
    if(!g.trim()){alert('Bitte einen Stornierungsgrund eingeben.');return;}
    dispatch({type:'STORNO_RECORD',payload:{collection:collection,id:id,grund:g.trim()}});
  }

  function openSchluss(anzahlung){
    var angebot=(state.angebote||[]).find(function(a){return a.id===anzahlung.angebotId;});
    setSchlussModal({anzahlung:anzahlung,angebot:angebot||null});
  }

  function createSchluss(modal){
    var ang=modal.angebot;
    var anz=modal.anzahlung;
    var gesamtNetto=ang?calcNetto(ang,s):0;
    var rabattTyp=ang?ang.rabattTyp||'prozent':'prozent';
    var rabattVal=ang?ang.rabatt||0:0;
    var rabattBetrag=rabattTyp==='betrag'?rabattVal:gesamtNetto*rabattVal/100;
    var nettoNachRabatt=gesamtNetto-rabattBetrag;
    var schlussNetto=nettoNachRabatt-(anz.betragNetto||0);
    var rec={
      id:uid(),rechnungNr:null,datum:today(),
      angebotId:ang?ang.id:null,kundeId:anz.kundeId,
      angebotNr:ang?ang.angebotNr:null,titel:anz.titel||'',
      gesamtNetto:nettoNachRabatt,betragNetto:schlussNetto,
      anzahlungNetto:anz.betragNetto||0,abzugAnzahlung:anz.betragNetto||0,
      restNetto:schlussNetto,
      status:'Entwurf',positionen:[],_ts:Date.now()
    };
    dispatch({type:'SAVE_SCHLUSS',payload:rec});
    setSchlussModal(null);
    if(setPage) setPage('schluss');
  }

  return(
    <div className="sk-page" style={{flex:1,overflow:'auto'}}>
      <h1 style={{fontSize:22,fontWeight:700,marginBottom:24}}>Anzahlungsrechnungen</h1>

      {schlussModal&&(
        <Modal title="Schlussrechnung erstellen" onClose={function(){setSchlussModal(null);}}>
          {(function(){
            var ang=schlussModal.angebot;
            var anz=schlussModal.anzahlung;
            var gesamtNetto=ang?calcNetto(ang,s):0;
            var rabattTyp=ang?ang.rabattTyp||'prozent':'prozent';
            var rabattVal=ang?ang.rabatt||0:0;
            var rabattBetrag=rabattTyp==='betrag'?rabattVal:gesamtNetto*rabattVal/100;
            var nettoNachRabatt=gesamtNetto-rabattBetrag;
            var schlussNetto=nettoNachRabatt-(anz.betragNetto||0);
            return(
              <div>
                <div style={{fontSize:13,color:'#555',marginBottom:16}}>
                  Für Projekt: <strong>{anz.titel||'–'}</strong>
                </div>
                <div style={{background:'var(--cream)',borderRadius:8,padding:14,marginBottom:16}}>
                  {ang?(
                    <>
                      <div style={{display:'flex',justifyContent:'space-between',marginBottom:6}}><span style={{color:'#888'}}>Gesamtbetrag Angebot (netto)</span><span>{fmt(nettoNachRabatt)}</span></div>
                      <div style={{display:'flex',justifyContent:'space-between',marginBottom:6}}><span style={{color:'#888'}}>– bereits geleistete Anzahlung</span><span style={{color:'#C0392B'}}>– {fmt(anz.betragNetto||0)}</span></div>
                      <div style={{display:'flex',justifyContent:'space-between',fontWeight:700,fontSize:15,borderTop:'1px solid var(--border)',paddingTop:8}}><span>Restbetrag (netto)</span><span style={{color:'#27AE60'}}>{fmt(schlussNetto)}</span></div>
                      <div style={{display:'flex',justifyContent:'space-between',marginTop:4,color:'#888'}}><span>+ 19% MwSt.</span><span>{fmt(schlussNetto*0.19)}</span></div>
                      <div style={{display:'flex',justifyContent:'space-between',fontWeight:700}}><span>Brutto</span><span>{fmt(schlussNetto*1.19)}</span></div>
                    </>
                  ):(
                    <div style={{color:'#888',fontSize:13}}>Kein Angebot verknüpft – Positionen müssen manuell in der Schlussrechnung ergänzt werden.</div>
                  )}
                </div>
                <div style={{fontSize:12,color:'#888',marginBottom:16}}>Die Schlussrechnung wird als Entwurf gespeichert – bitte vor der Verwendung nochmals prüfen und freigeben.</div>
                <div style={{display:'flex',gap:10}}>
                  <button className="btn-outline" style={{flex:1}} onClick={function(){setSchlussModal(null);}}>Abbrechen</button>
                  <button className="btn-dark" style={{flex:2,background:'#27AE60'}} onClick={function(){createSchluss(schlussModal);}}>✓ Als Entwurf speichern</button>
                </div>
              </div>
            );
          })()}
        </Modal>
      )}

      <Card>
        {list.length===0&&<div style={{color:'#999',padding:'12px 0',fontSize:14}}>Keine Anzahlungsrechnungen vorhanden.<br/><span style={{fontSize:12}}>Diese werden automatisch erstellt wenn du bei einem versendeten Angebot auf "Rechnung vorbereiten" klickst.</span></div>}
        {list.length>0&&(
          <div className="sk-tbl">
          <table style={{width:'100%',borderCollapse:'collapse',fontSize:14}}>
            <thead><tr style={{borderBottom:'1px solid var(--border)'}}>
              {['RE-Nr.','Datum','Angebot','Projekt','Anzahlung netto','+ 19% MwSt','Brutto',''].map(function(h){return <th key={h} style={{textAlign:'left',padding:'6px 8px',fontSize:12,color:'#888',fontWeight:600}}>{h}</th>;})}
            </tr></thead>
            <tbody>
              {[].concat(list).sort(function(a,b){return b._ts>a._ts?1:-1;}).map(function(r){
                var kd=(state.kunden||[]).find(function(c){return c.id===r.kundeId;});
                var angebot=(state.angebote||[]).find(function(a){return a.id===r.angebotId;});
                var mwst=(r.betragNetto||0)*0.19;
                var brutto=(r.betragNetto||0)+mwst;
                // Prüfen ob schon eine Schlussrechnung für dieses Angebot existiert
                var hatSchluss=r.angebotId&&(state.schlussrechnungen||[]).some(function(sc){return sc.angebotId===r.angebotId&&sc.status!=='Entwurf'&&!sc.storniert;});
                return(
                  <tr key={r.id} style={{borderBottom:'1px solid var(--border)'}}>
                    <td style={{padding:'9px 8px',color:'#888',fontWeight:700}}>{fmtRechnungNr(r.rechnungNr)}</td>
                    <td style={{padding:'9px 8px',color:'#888'}}>{r.datum||'–'}</td>
                    <td style={{padding:'9px 8px',color:'#3B6FE0',fontWeight:600}}>{r.angebotNr?'AN-'+String(r.angebotNr).padStart(4,'0'):'–'}</td>
                    <td style={{padding:'9px 8px'}}>{r.titel||'–'}</td>
                    <td style={{padding:'9px 8px',textAlign:'right'}}>{fmt(r.betragNetto||0)}</td>
                    <td style={{padding:'9px 8px',textAlign:'right',color:'#888'}}>{fmt(mwst)}</td>
                    <td style={{padding:'9px 8px',textAlign:'right',fontWeight:700}}>{fmt(brutto)}</td>
                    <td style={{padding:'9px 8px',textAlign:'right',whiteSpace:'nowrap'}}>
                      {r.storniert&&<span title={r.stornoGrund?'Grund: '+r.stornoGrund:''} style={{fontSize:11,padding:'2px 6px',borderRadius:10,background:'#F8D7DA',color:'#721C24',fontWeight:700,marginRight:6,cursor:r.stornoGrund?'help':'default'}}>STORNO</span>}
                      {r.locked&&!r.storniert&&<span title="Gesperrt nach sevDesk-Export" style={{fontSize:12,marginRight:4,color:'#888'}}>🔒</span>}
                      {!r.storniert&&<SevDeskRechnungButton rechnung={r} typ="anzahlung" angebot={angebot||null} kunden={state.kunden||[]} settings={s} dispatch={dispatch} saveAction="SAVE_ANZAHLUNG" collection="anzahlungen"/>}
                      {angebot&&<button className="btn-outline btn-sm" onClick={function(){generateRechnungPDF(r,'anzahlung',angebot,(state.kunden||[]).find(function(c){return c.id===r.kundeId;}),s);}} style={{marginLeft:4,marginRight:4}}>PDF</button>}
                      {!r.storniert&&!hatSchluss&&(
                        <button className="btn-outline btn-sm" onClick={function(){openSchluss(r);}} style={{marginRight:4,color:'#27AE60',borderColor:'#27AE60'}} title="Schlussrechnung erstellen">
                          ✓ Schlussrechnung
                        </button>
                      )}
                      {hatSchluss&&<span style={{fontSize:11,color:'#888',marginRight:4}}>✓ Schluss vorhanden</span>}
                      {r.locked&&!r.storniert&&<button className="btn-outline btn-sm" onClick={function(){dispatch({type:'DUPLICATE_RECORD',payload:{collection:'anzahlungen',id:r.id}});}} style={{marginRight:4,color:'#2D6BE4',borderColor:'#2D6BE4'}} title="Kopie erstellen">⊕ Kopie</button>}
                      {r.rechnungNr&&!r.storniert&&<button className="btn-danger btn-sm" onClick={function(){storno('anzahlungen',r.id,fmtRechnungNr(r.rechnungNr));}} style={{marginRight:4}}>Stornieren</button>}
                      {!r.rechnungNr&&!r.storniert&&<button className="btn-danger btn-sm" onClick={function(){del(r.id);}}>&#10005;</button>}
                    </td>
                  </tr>
                );
              })}
            </tbody>
          </table>
          </div>
        )}
      </Card>
    </div>
  );
}

// ─── SCHLUSSRECHNUNGEN ───────────────────────────────────────────────────────
function SchlussPage({state,dispatch}){
  var list=state.schlussrechnungen||[];
  var s=state.settings;
  var [direktModal,setDirektModal]=useState(false);
  var [dForm,setDForm]=useState({});
  function del(id){if(confirm('Rechnung löschen?'))dispatch({type:'DEL_SCHLUSS',payload:id});}
  function storno(collection,id,label){
    var g=window.prompt('Stornierungsgrund für '+label+' (Pflichtfeld – wird im Audit-Log gespeichert):','');
    if(g===null) return;
    if(!g.trim()){alert('Bitte einen Stornierungsgrund eingeben.');return;}
    dispatch({type:'STORNO_RECORD',payload:{collection:collection,id:id,grund:g.trim()}});
  }

  function openDirekt(){
    setDForm({id:uid(),kundeId:'',titel:'',datum:today(),positionen:[{id:uid(),bezeichnung:'',menge:1,einheit:'pauschal',einzelpreis:0}]});
    setDirektModal(true);
  }
  function saveDirekt(){
    var positionen=(dForm.positionen||[]).filter(function(p){return p.bezeichnung&&(p.einzelpreis||0)>0;});
    var totalNetto=positionen.reduce(function(t,p){return t+(p.menge||1)*(p.einzelpreis||0);},0);
    if(!dForm.kundeId||!dForm.titel||positionen.length===0||totalNetto<=0){alert('Bitte Kunde, Titel und mindestens eine Position mit Betrag ausfüllen.');return;}
    var rec={
      id:dForm.id,rechnungNr:null,datum:dForm.datum||today(),
      angebotId:null,kundeId:dForm.kundeId,angebotNr:null,
      titel:dForm.titel,gesamtNetto:totalNetto,betragNetto:totalNetto,
      anzahlungNetto:0,abzugAnzahlung:0,restNetto:totalNetto,
      positionen:positionen,
      status:'Entwurf',
      vollrechnung:true,direktrechnung:true,_ts:Date.now()
    };
    dispatch({type:'SAVE_SCHLUSS',payload:rec});
    setDirektModal(false);
  }

  return(
    <div className="sk-page" style={{flex:1,overflow:'auto'}}>
      <div style={{display:'flex',alignItems:'center',justifyContent:'space-between',marginBottom:24}}>
        <h1 style={{fontSize:22,fontWeight:700}}>Schlussrechnungen</h1>
        <button className="btn-dark" onClick={openDirekt} style={{background:'#27AE60'}}>🧾 Neue Direktrechnung</button>
      </div>

      {direktModal&&(
        <Modal title="Neue Direktrechnung" onClose={function(){setDirektModal(false);}} wide={true}>
          <div style={{marginBottom:20}}>
            <div style={{fontSize:13,color:'#666',marginBottom:16}}>Für mündlich erteilte Aufträge — direkt ohne Angebot.</div>
            <Field label="Kunde">
              <select value={dForm.kundeId||''} onChange={function(e){setDForm(Object.assign({},dForm,{kundeId:e.target.value}));}} style={{width:'100%'}}>
                <option value="">– Kunden wählen –</option>
                {(state.kunden||[]).map(function(k){return <option key={k.id} value={k.id}>{k.name}</option>;})}
              </select>
            </Field>
            <Field label="Leistungsbeschreibung / Projekttitel">
              <input value={dForm.titel||''} onChange={function(e){setDForm(Object.assign({},dForm,{titel:e.target.value}));}} style={{width:'100%'}} placeholder="z.B. Reparatur Küche, Schreinerstunde"/>
            </Field>
            <Field label="Datum">
              <input value={dForm.datum||today()} onChange={function(e){setDForm(Object.assign({},dForm,{datum:e.target.value}));}} style={{width:'100%'}}/>
            </Field>
            <Field label="Positionen">
              <div>
                <div style={{display:'flex',gap:4,marginBottom:4}}>
                  <span style={{flex:'4 1 0',fontSize:11,color:'#888',fontWeight:600,textTransform:'uppercase'}}>Bezeichnung</span>
                  <span style={{flex:'1 1 40px',fontSize:11,color:'#888',fontWeight:600,textTransform:'uppercase',textAlign:'right'}}>Menge</span>
                  <span style={{flex:'1.5 1 60px',fontSize:11,color:'#888',fontWeight:600,textTransform:'uppercase'}}>Einheit</span>
                  <span style={{flex:'2 1 70px',fontSize:11,color:'#888',fontWeight:600,textTransform:'uppercase',textAlign:'right'}}>Einzelpreis</span>
                  <span style={{flex:'1.5 1 60px',fontSize:11,color:'#888',fontWeight:600,textTransform:'uppercase',textAlign:'right'}}>Gesamt</span>
                  <span style={{width:24}}/>
                </div>
                {(dForm.positionen||[]).map(function(pos){
                  function updDP(field,val){var np=(dForm.positionen||[]).map(function(p){return p.id===pos.id?Object.assign({},p,Object.fromEntries([[field,val]])):p;});setDForm(Object.assign({},dForm,{positionen:np}));}
                  return(
                    <div key={pos.id} style={{display:'flex',gap:4,alignItems:'center',marginBottom:6}}>
                      <input value={pos.bezeichnung||''} onChange={function(e){updDP('bezeichnung',e.target.value);}} style={{flex:'4 1 0',minWidth:0}} placeholder="Leistungsbeschreibung"/>
                      <input type="number" value={pos.menge||1} min="0.01" step="0.5" onChange={function(e){updDP('menge',+e.target.value);}} style={{flex:'1 1 40px',minWidth:0,textAlign:'right'}}/>
                      <select value={pos.einheit||'pauschal'} onChange={function(e){updDP('einheit',e.target.value);}} style={{flex:'1.5 1 60px',minWidth:0,padding:'7px 4px'}}>
                        <option value="pauschal">pauschal</option>
                        <option value="Std.">Std.</option>
                        <option value="Stk.">Stk.</option>
                        <option value="m²">m²</option>
                        <option value="m">m</option>
                      </select>
                      <input type="number" value={pos.einzelpreis||0} min="0" step="10" onChange={function(e){updDP('einzelpreis',+e.target.value);}} style={{flex:'2 1 70px',minWidth:0,textAlign:'right'}} placeholder="0,00"/>
                      <span style={{flex:'1.5 1 60px',minWidth:0,textAlign:'right',fontSize:13,fontWeight:600,color:'#27AE60',whiteSpace:'nowrap'}}>{fmt((pos.menge||1)*(pos.einzelpreis||0))}</span>
                      <button onClick={function(){var np=(dForm.positionen||[]).filter(function(p){return p.id!==pos.id;});setDForm(Object.assign({},dForm,{positionen:np}));}} style={{width:24,background:'none',border:'none',color:'#C0392B',cursor:'pointer',fontSize:16,padding:0,flexShrink:0}}>✕</button>
                    </div>
                  );
                })}
                <button className="btn-outline btn-sm" onClick={function(){var np=(dForm.positionen||[]).concat([{id:uid(),bezeichnung:'',menge:1,einheit:'pauschal',einzelpreis:0}]);setDForm(Object.assign({},dForm,{positionen:np}));}} style={{marginTop:4}}>+ Position hinzufügen</button>
              </div>
            </Field>
            {(function(){var total=(dForm.positionen||[]).reduce(function(t,p){return t+(p.menge||1)*(p.einzelpreis||0);},0);if(total<=0) return null;return(
              <div style={{background:'var(--cream)',borderRadius:8,padding:12,fontSize:14,marginTop:4}}>
                <div style={{display:'flex',justifyContent:'space-between',marginBottom:4}}><span style={{color:'#888'}}>Netto</span><strong>{fmt(total)}</strong></div>
                <div style={{display:'flex',justifyContent:'space-between',marginBottom:4}}><span style={{color:'#888'}}>19 % MwSt.</span><span>{fmt(total*0.19)}</span></div>
                <div style={{display:'flex',justifyContent:'space-between'}}><span style={{fontWeight:700}}>Brutto</span><strong style={{fontSize:16}}>{fmt(total*1.19)}</strong></div>
              </div>
            );})()}
          </div>
          <div style={{display:'flex',gap:10}}>
            <button className="btn-outline" style={{flex:1}} onClick={function(){setDirektModal(false);}}>Abbrechen</button>
            <button className="btn-dark" style={{flex:2,background:'#27AE60'}} onClick={saveDirekt}>🧾 Rechnung erstellen</button>
          </div>
        </Modal>
      )}

      {/* ── Entwürfe ─────────────────────────────────────────────── */}
      {(function(){
        var entwuerfe=list.filter(function(r){return r.status==='Entwurf';});
        if(entwuerfe.length===0) return null;
        return(
          <Card style={{marginBottom:16,border:'2px dashed #E67E22'}}>
            <div style={{display:'flex',alignItems:'center',gap:10,marginBottom:12}}>
              <span style={{fontSize:16}}>📝</span>
              <strong style={{fontSize:15,color:'#E67E22'}}>Rechnungsentwürfe ({entwuerfe.length})</strong>
              <span style={{fontSize:12,color:'#888'}}>Noch keine Rechnungsnummer vergeben – bitte prüfen &amp; freigeben</span>
            </div>
            {entwuerfe.map(function(r){
              var kunde2=(state.kunden||[]).find(function(c){return c.id===r.kundeId;});
              var netto=r.betragNetto||r.restNetto||0;
              return(
                <div key={r.id} style={{display:'flex',alignItems:'center',gap:8,padding:'10px 0',borderBottom:'1px solid #f5e6d0',flexWrap:'wrap'}}>
                  <div style={{flex:'1 1 200px'}}>
                    <div style={{fontWeight:600,fontSize:14}}>{r.titel||'–'}</div>
                    <div style={{fontSize:12,color:'#888'}}>{kunde2?kunde2.name:'Kunde unbekannt'} · {r.datum||'–'} · {r.direktrechnung?'Direktrechnung':'Schlussrechnung'}</div>
                  </div>
                  <div style={{fontWeight:700,fontSize:15,color:'#27AE60',whiteSpace:'nowrap'}}>{fmt(netto)} netto</div>
                  <div style={{display:'flex',gap:6,flexShrink:0}}>
                    <button className="btn-dark" style={{background:'#27AE60',fontSize:12,padding:'5px 12px'}}
                      onClick={function(){if(confirm('Rechnung freigeben? Nächste Rechnungsnummer: '+fmtRechnungNr(String(state.nextSNr||1).padStart(4,'0'))))dispatch({type:'FREIGABE_SCHLUSS',payload:r.id});}}>
                      ✓ Freigeben
                    </button>
                    <button className="btn-danger btn-sm" onClick={function(){if(confirm('Entwurf löschen? (Keine Rechnungsnummer wird vergeben)'))dispatch({type:'DEL_SCHLUSS',payload:r.id});}}>&#10005;</button>
                  </div>
                </div>
              );
            })}
          </Card>
        );
      })()}

      <Card>
        {(function(){
          var freigegebene=list.filter(function(r){return r.status!=='Entwurf';});
          if(freigegebene.length===0) return <div style={{color:'#999',padding:'12px 0',fontSize:14}}>Keine freigegebenen Rechnungen vorhanden.<br/><span style={{fontSize:12}}>Erstellt über "Rechnung vorbereiten" bei Angeboten oder "Neue Direktrechnung" oben.</span></div>;
          return(
          <div className="sk-tbl">
          <table style={{width:'100%',borderCollapse:'collapse',fontSize:14}}>
            <thead><tr style={{borderBottom:'1px solid var(--border)'}}>
              {['RE-Nr.','Datum','Angebot','Projekt','Art','Netto','Brutto',''].map(function(h){return <th key={h} style={{textAlign:'left',padding:'6px 8px',fontSize:12,color:'#888',fontWeight:600}}>{h}</th>;})}
            </tr></thead>
            <tbody>
              {[].concat(freigegebene).sort(function(a,b){return b._ts>a._ts?1:-1;}).map(function(r){
                var angebot=(state.angebote||[]).find(function(a){return a.id===r.angebotId;});
                var isDirekt=!!r.direktrechnung;
                var netto=r.betragNetto||r.restNetto||0;
                var artLabel=isDirekt?'Direktrechnung':(r.vollrechnung?'Vollrechnung':'Schlussrechnung');
                var artBg=isDirekt?'#FFF3E0':r.vollrechnung?'#E8F5E9':'#E3F0FF';
                var artColor=isDirekt?'#E67E22':r.vollrechnung?'#27AE60':'#3B6FE0';
                return(
                  <tr key={r.id} style={{borderBottom:'1px solid var(--border)'}}>
                    <td style={{padding:'9px 8px',color:'#888',fontWeight:700}}>{r.rechnungNr?fmtRechnungNr(r.rechnungNr):'–'}</td>
                    <td style={{padding:'9px 8px',color:'#888'}}>{r.datum||'–'}</td>
                    <td style={{padding:'9px 8px',color:'#3B6FE0',fontWeight:600}}>{r.angebotNr?'AN-'+String(r.angebotNr).padStart(4,'0'):'–'}</td>
                    <td style={{padding:'9px 8px'}}>{r.titel||'–'}</td>
                    <td style={{padding:'9px 8px'}}><span style={{fontSize:12,padding:'2px 7px',borderRadius:10,background:artBg,color:artColor,fontWeight:600}}>{artLabel}</span></td>
                    <td style={{padding:'9px 8px',textAlign:'right',fontWeight:600,color:'#27AE60'}}>{fmt(netto)}</td>
                    <td style={{padding:'9px 8px',textAlign:'right',fontWeight:700}}>{fmt(netto*1.19)}</td>
                    <td style={{padding:'9px 8px',textAlign:'right',whiteSpace:'nowrap'}}>
                      {r.storniert&&<span title={r.stornoGrund?'Grund: '+r.stornoGrund:''} style={{fontSize:11,padding:'2px 6px',borderRadius:10,background:'#F8D7DA',color:'#721C24',fontWeight:700,marginRight:6,cursor:r.stornoGrund?'help':'default'}}>STORNO</span>}
                      {r.locked&&!r.storniert&&<span title="Gesperrt nach sevDesk-Export" style={{fontSize:12,marginRight:4,color:'#888'}}>🔒</span>}
                      {!r.storniert&&<SevDeskRechnungButton rechnung={r} typ="schluss" angebot={angebot||null} kunden={state.kunden||[]} settings={s} dispatch={dispatch} saveAction="SAVE_SCHLUSS" collection="schlussrechnungen"/>}
                      <button className="btn-outline btn-sm" onClick={function(){generateRechnungPDF(r,'schluss',angebot||null,(state.kunden||[]).find(function(c){return c.id===r.kundeId;}),s);}} style={{marginLeft:4,marginRight:4}}>PDF</button>
                      {r.locked&&!r.storniert&&<button className="btn-outline btn-sm" onClick={function(){dispatch({type:'DUPLICATE_RECORD',payload:{collection:'schlussrechnungen',id:r.id}});}} style={{marginRight:4,color:'#2D6BE4',borderColor:'#2D6BE4'}} title="Kopie erstellen">⊕ Kopie</button>}
                      {r.rechnungNr&&!r.storniert&&<button className="btn-danger btn-sm" onClick={function(){storno('schlussrechnungen',r.id,fmtRechnungNr(r.rechnungNr));}} style={{marginRight:4}}>Stornieren</button>}
                      {!r.rechnungNr&&!r.storniert&&<button className="btn-danger btn-sm" onClick={function(){del(r.id);}}>&#10005;</button>}
                    </td>
                  </tr>
                );
              })}
            </tbody>
          </table>
          </div>
          );
        })()}
      </Card>
    </div>
  );
}

// ─── SEVDESK TEST ─────────────────────────────────────────────────────────────
function SevDeskTestButton({token,settings}){
  var cfg=settings||{sevDeskToken:token||''};
  var [status,setStatus]=useState('idle');var [info,setInfo]=useState('');
  if(!cfg.sevDeskToken&&!cfg.workerUrl) return <div style={{fontSize:12,color:'#aaa'}}>Bitte erst Token eintragen (oder Worker-URL für Proxy-Modus) und speichern.</div>;
  function test(){
    setStatus('loading');setInfo('');
    sdGetCurrentUser(cfg).then(function(user){
      if(!user){setStatus('error');setInfo('Kein User gefunden oder Token ungültig');return;}
      setStatus('ok');setInfo('Verbunden als: '+(user.fullname||user.username||user.id));
    }).catch(function(e){setStatus('error');setInfo('Fehler: '+e.message);});
  }
  return(
    <div style={{display:'flex',gap:8,alignItems:'center'}}>
      <button onClick={test} className="btn-outline btn-sm" disabled={status==='loading'}>{status==='loading'?'Teste…':'sevDesk testen'}</button>
      {status==='ok'&&<span style={{color:'#27AE60',fontSize:13}}>✓ {info}</span>}
      {status==='error'&&<span style={{color:'#C0392B',fontSize:13}}>✗ {info}</span>}
    </div>
  );
}

// ─── REISEKOSTEN PAGE ────────────────────────────────────────────────────────
function ReisekostenPage({state,dispatch}){
  var list=state.reisekostenberichte||[];
  var s=state.settings;
  var [modal,setModal]=useState(false);
  var [wForm,setWForm]=useState({});
  var [wStep,setWStep]=useState(0);

  function generateDateRange(vonD,bisD){
    var parts1=vonD.split('.');
    var parts2=bisD.split('.');
    if(parts1.length!==3||parts2.length!==3) return [];
    var start=new Date(parts1[2]+'-'+parts1[1]+'-'+parts1[0]);
    var end=new Date(parts2[2]+'-'+parts2[1]+'-'+parts2[0]);
    if(start>end) return [];
    var tage=[];
    var cur=new Date(start);
    while(cur<=end){
      var d=String(cur.getDate()).padStart(2,'0')+'.'+String(cur.getMonth()+1).padStart(2,'0')+'.'+cur.getFullYear();
      var typ='voll';
      if(d===vonD || d===bisD) typ='anteil';
      tage.push({id:uid(),datum:d,typ:typ,fruehstueck:false,mittagessen:false,abendessen:false});
      cur=new Date(cur.getTime()+86400000);
    }
    return tage;
  }

  function openNeu(){
    setWForm({id:uid(),nr:state.nextReiseNr,datum:today(),mitarbeiter:'',reisezweck:'',land:'Italien',ziel:'',vonDatum:'',bisDatum:'',tage:[],fahrten:[],kosten:[],vorschuss:0});
    setWStep(0);
    setModal(true);
  }
  function openEdit(r){
    setWForm(Object.assign({},r));
    setWStep(0);
    setModal(true);
  }
  function del(id){if(confirm('Reisekostenbericht löschen?'))dispatch({type:'DEL_REISE',payload:id});}

  function doSave(){
    dispatch({type:'SAVE_REISE',payload:Object.assign({},wForm,{_ts:Date.now()})});
    setModal(false);
  }

  var pauschalen=window.AUSLANDSPAUSCHALEN[wForm.land]||{voll:28,anteil:14};
  var sumTage=(wForm.tage||[]).reduce(function(t,tag){return t+calcTagesgeld(tag,pauschalen);},0);
  var sumFahrten=(wForm.fahrten||[]).reduce(function(t,f){return t+(f.km||0)*0.3;},0);
  var sumKosten=(wForm.kosten||[]).reduce(function(t,k){return t+(k.betrag||0);},0);
  var gesamt=sumTage+sumFahrten+sumKosten;
  var auszuzahlen=gesamt-(wForm.vorschuss||0);

  return(
    <div className="sk-page" style={{flex:1,overflow:'auto'}}>
      <div style={{display:'flex',alignItems:'center',justifyContent:'space-between',marginBottom:24}}>
        <h1 style={{fontSize:22,fontWeight:700}}>Reisekostenabrechnung</h1>
        <button className="btn-dark" onClick={openNeu}>+ Neue Reisekostenabrechnung</button>
      </div>
      <Card>
        {list.length===0&&<div style={{color:'#999',padding:'12px 0',fontSize:14}}>Noch keine Reisekostenberichte angelegt.</div>}
        {list.length>0&&(
          <div className="sk-tbl">
          <table style={{width:'100%',borderCollapse:'collapse',fontSize:14}}>
            <thead><tr style={{borderBottom:'1px solid var(--border)'}}>
              {['Nr.','Datum','Mitarbeiter','Ziel','Zeitraum','Gesamt',''].map(function(h){return <th key={h} style={{textAlign:'left',padding:'6px 8px',fontSize:12,color:'#888',fontWeight:600}}>{h}</th>;})}
            </tr></thead>
            <tbody>
              {[].concat(list).sort(function(a,b){return b._ts>a._ts?1:-1;}).map(function(r){
                var sumT=(r.tage||[]).reduce(function(t,tag){var p=window.AUSLANDSPAUSCHALEN[r.land]||{voll:28,anteil:14};return t+calcTagesgeld(tag,p);},0);
                var sumF=(r.fahrten||[]).reduce(function(t,f){return t+(f.km||0)*0.3;},0);
                var sumK=(r.kosten||[]).reduce(function(t,k){return t+(k.betrag||0);},0);
                var g=sumT+sumF+sumK;
                return(
                  <tr key={r.id} style={{borderBottom:'1px solid var(--border)'}}>
                    <td style={{padding:'9px 8px',color:'#888',fontWeight:600}}>#{String(r.nr||0).padStart(4,'0')}</td>
                    <td style={{padding:'9px 8px',color:'#888'}}>{r.datum||'–'}</td>
                    <td style={{padding:'9px 8px',fontWeight:500}}>{r.mitarbeiter||'–'}</td>
                    <td style={{padding:'9px 8px'}}>{r.ziel||'–'} ({r.land||'–'})</td>
                    <td style={{padding:'9px 8px',color:'#666'}}>{r.vonDatum||'–'} – {r.bisDatum||'–'}</td>
                    <td style={{padding:'9px 8px',textAlign:'right',fontWeight:600}}>{fmt(g)}</td>
                    <td style={{padding:'9px 8px',textAlign:'right',whiteSpace:'nowrap'}}>
                      {r.storniert&&<span style={{fontSize:11,padding:'2px 6px',borderRadius:10,background:'#F8D7DA',color:'#721C24',fontWeight:700,marginRight:6}}>STORNO</span>}
                      {r.locked&&!r.storniert&&<span title="Gesperrt nach sevDesk-Export" style={{fontSize:12,marginRight:4,color:'#888'}}>🔒</span>}
                      {r.sevdeskLinked&&r.sevdeskUrl&&<a href={r.sevdeskUrl} target="_blank" rel="noopener" style={{fontSize:12,color:'#27AE60',fontWeight:600,textDecoration:'none',marginRight:8}}>✓ sevDesk →</a>}
                      {!r.locked&&<button className="btn-outline btn-sm" onClick={function(){openEdit(r);}} style={{marginRight:6}}>Bearbeiten</button>}
                      <button className="btn-outline btn-sm" onClick={function(){generateReisekostenPDF(r,s);}} style={{marginRight:6}}>PDF</button>
                      {r.locked&&!r.storniert&&<button className="btn-outline btn-sm" onClick={function(){dispatch({type:'DUPLICATE_RECORD',payload:{collection:'reisekostenberichte',id:r.id}});}} style={{marginRight:6,color:'#2D6BE4',borderColor:'#2D6BE4'}} title="Kopie erstellen">⊕ Kopie</button>}
                      {r.locked&&!r.storniert&&<button className="btn-danger btn-sm" onClick={function(){if(confirm('Reisekostenbericht #'+String(r.nr||r.id)+' stornieren?'))dispatch({type:'STORNO_RECORD',payload:{collection:'reisekostenberichte',id:r.id}});}} style={{marginRight:6}}>Stornieren</button>}
                      {!r.locked&&<button className="btn-danger btn-sm" onClick={function(){del(r.id);}}>Löschen</button>}
                    </td>
                  </tr>
                );
              })}
            </tbody>
          </table>
          </div>
        )}
      </Card>
      {modal&&(
        <Modal title={wForm.id&&list.find(function(r){return r.id===wForm.id;})?'Reisekostenbericht bearbeiten':'Neue Reisekostenabrechnung'} onClose={function(){setModal(false);}} wide={true}>
          <div style={{marginBottom:16}}>
            {wStep===0&&(
              <div>
                <div className="sk-grid-2" style={{marginBottom:16}}>
                  <Field label="Mitarbeiter"><input value={wForm.mitarbeiter||''} onChange={function(e){setWForm(Object.assign({},wForm,{mitarbeiter:e.target.value}));}} style={{width:'100%'}}/></Field>
                  <Field label="Reisezweck"><input value={wForm.reisezweck||''} onChange={function(e){setWForm(Object.assign({},wForm,{reisezweck:e.target.value}));}} style={{width:'100%'}}/></Field>
                </div>
                <div className="sk-grid-2" style={{marginBottom:16}}>
                  <Field label="Land"><select value={wForm.land||'Italien'} onChange={function(e){setWForm(Object.assign({},wForm,{land:e.target.value}));}} style={{width:'100%'}}>
                    {Object.keys(window.AUSLANDSPAUSCHALEN).map(function(k){return <option key={k} value={k}>{k}</option>;})}
                  </select></Field>
                  <Field label="Ziel / Stadt"><input value={wForm.ziel||''} onChange={function(e){setWForm(Object.assign({},wForm,{ziel:e.target.value}));}} style={{width:'100%'}}/></Field>
                </div>
                <div className="sk-grid-2" style={{marginBottom:16}}>
                  <Field label="Von Datum (DD.MM.YYYY)"><input value={wForm.vonDatum||''} onChange={function(e){
                    var von=e.target.value;
                    var form=Object.assign({},wForm,{vonDatum:von});
                    if(form.vonDatum&&form.bisDatum) form.tage=generateDateRange(form.vonDatum,form.bisDatum);
                    setWForm(form);
                  }} style={{width:'100%'}}/></Field>
                  <Field label="Bis Datum (DD.MM.YYYY)"><input value={wForm.bisDatum||''} onChange={function(e){
                    var bis=e.target.value;
                    var form=Object.assign({},wForm,{bisDatum:bis});
                    if(form.vonDatum&&form.bisDatum) form.tage=generateDateRange(form.vonDatum,form.bisDatum);
                    setWForm(form);
                  }} style={{width:'100%'}}/></Field>
                </div>
              </div>
            )}
            {wStep===1&&(
              <div>
                <div style={{fontSize:13,color:'#666',marginBottom:14}}>Konfigurieren Sie die Tagessätze für jeden Tag.</div>
                <div style={{maxHeight:400,overflowY:'auto'}}>
                  {(wForm.tage||[]).map(function(tag){
                    var p=window.AUSLANDSPAUSCHALEN[wForm.land]||{voll:28,anteil:14};
                    var tg=calcTagesgeld(tag,p);
                    return(
                      <div key={tag.id} style={{background:'#f9f8f6',borderRadius:8,padding:12,marginBottom:10,border:'1px solid var(--border)'}}>
                        <div style={{display:'flex',alignItems:'center',justifyContent:'space-between',marginBottom:8}}>
                          <span style={{fontWeight:600,fontSize:14}}>{tag.datum}</span>
                          <span style={{fontSize:13,color:'#27AE60',fontWeight:600}}>{fmt(tg)}</span>
                        </div>
                        <select value={tag.typ||'voll'} onChange={function(e){
                          var neueT=wForm.tage.map(function(t){return t.id===tag.id?Object.assign({},t,{typ:e.target.value}):t;});
                          setWForm(Object.assign({},wForm,{tage:neueT}));
                        }} style={{width:'100%',marginBottom:8}}>
                          <option value="voll">Volltag ({fmt(p.voll)})</option>
                          <option value="anteil">An-/Abreise ({fmt(p.anteil)})</option>
                          <option value="keine">Kein Tagegeld</option>
                        </select>
                        <div style={{display:'flex',gap:10}}>
                          <label><input type="checkbox" checked={tag.fruehstueck||false} onChange={function(e){
                            var neueT=wForm.tage.map(function(t){return t.id===tag.id?Object.assign({},t,{fruehstueck:e.target.checked}):t;});
                            setWForm(Object.assign({},wForm,{tage:neueT}));
                          }}/> <span style={{fontSize:13,marginLeft:4}}>Frühstück</span></label>
                          <label><input type="checkbox" checked={tag.mittagessen||false} onChange={function(e){
                            var neueT=wForm.tage.map(function(t){return t.id===tag.id?Object.assign({},t,{mittagessen:e.target.checked}):t;});
                            setWForm(Object.assign({},wForm,{tage:neueT}));
                          }}/> <span style={{fontSize:13,marginLeft:4}}>Mittagessen</span></label>
                          <label><input type="checkbox" checked={tag.abendessen||false} onChange={function(e){
                            var neueT=wForm.tage.map(function(t){return t.id===tag.id?Object.assign({},t,{abendessen:e.target.checked}):t;});
                            setWForm(Object.assign({},wForm,{tage:neueT}));
                          }}/> <span style={{fontSize:13,marginLeft:4}}>Abendessen</span></label>
                        </div>
                      </div>
                    );
                  })}
                </div>
              </div>
            )}
            {wStep===2&&(
              <div>
                <div style={{fontSize:13,color:'#666',marginBottom:12}}>Km-Entschädigung für Privatwagen – steuerlich erforderliche Angaben. Pauschale: <strong>0,30 €/km</strong></div>
                <div style={{overflowX:'auto',marginBottom:12}}>
                  <table style={{width:'100%',borderCollapse:'collapse',fontSize:13,minWidth:560}}>
                    <thead>
                      <tr style={{background:'#f0ede8'}}>
                        <th style={{padding:'6px 6px',textAlign:'left',fontWeight:600,fontSize:12}}>Datum</th>
                        <th style={{padding:'6px 6px',textAlign:'left',fontWeight:600,fontSize:12}}>Von</th>
                        <th style={{padding:'6px 6px',textAlign:'left',fontWeight:600,fontSize:12}}>Nach</th>
                        <th style={{padding:'6px 6px',textAlign:'left',fontWeight:600,fontSize:12}}>Anlass/Zweck</th>
                        <th style={{padding:'6px 6px',textAlign:'right',fontWeight:600,fontSize:12}}>km</th>
                        <th style={{padding:'6px 6px',textAlign:'right',fontWeight:600,fontSize:12,color:'#27AE60'}}>Betrag</th>
                        <th style={{padding:'6px 6px',width:24}}></th>
                      </tr>
                    </thead>
                    <tbody>
                      {(wForm.fahrten||[]).map(function(f,idx){
                        var betrag=(f.km||0)*0.3;
                        return(
                          <tr key={f.id||idx} style={{borderBottom:'1px solid var(--border)'}}>
                            <td style={{padding:'4px 4px'}}><input value={f.datum||''} onChange={function(e){var nF=wForm.fahrten.map(function(x,i){return i===idx?Object.assign({},x,{datum:e.target.value}):x;});setWForm(Object.assign({},wForm,{fahrten:nF}));}} style={{width:88}} placeholder="DD.MM.YYYY"/></td>
                            <td style={{padding:'4px 4px'}}><input value={f.von||''} onChange={function(e){var nF=wForm.fahrten.map(function(x,i){return i===idx?Object.assign({},x,{von:e.target.value}):x;});setWForm(Object.assign({},wForm,{fahrten:nF}));}} style={{width:76}} placeholder="München"/></td>
                            <td style={{padding:'4px 4px'}}><input value={f.nach||''} onChange={function(e){var nF=wForm.fahrten.map(function(x,i){return i===idx?Object.assign({},x,{nach:e.target.value}):x;});setWForm(Object.assign({},wForm,{fahrten:nF}));}} style={{width:76}} placeholder="Mailand"/></td>
                            <td style={{padding:'4px 4px'}}><input value={f.zweck||''} onChange={function(e){var nF=wForm.fahrten.map(function(x,i){return i===idx?Object.assign({},x,{zweck:e.target.value}):x;});setWForm(Object.assign({},wForm,{fahrten:nF}));}} style={{width:110}} placeholder="Kundentermin"/></td>
                            <td style={{padding:'4px 4px',textAlign:'right'}}><input type="number" value={f.km||''} onChange={function(e){var nF=wForm.fahrten.map(function(x,i){return i===idx?Object.assign({},x,{km:+e.target.value}):x;});setWForm(Object.assign({},wForm,{fahrten:nF}));}} style={{width:56,textAlign:'right'}} min="0" step="1" placeholder="0"/></td>
                            <td style={{padding:'4px 6px',textAlign:'right',color:'#27AE60',fontWeight:600,whiteSpace:'nowrap',fontSize:13}}>{fmt(betrag)}</td>
                            <td style={{padding:'4px 4px',textAlign:'center'}}><button onClick={function(){var nF=wForm.fahrten.filter(function(x,i){return i!==idx;});setWForm(Object.assign({},wForm,{fahrten:nF}));}} style={{background:'none',border:'none',color:'#C0392B',cursor:'pointer',fontSize:14,padding:0,lineHeight:1}}>✕</button></td>
                          </tr>
                        );
                      })}
                    </tbody>
                  </table>
                </div>
                <button className="btn-outline btn-sm" onClick={function(){var nF=(wForm.fahrten||[]).concat([{id:uid(),datum:today(),von:'',nach:'',zweck:'',km:0}]);setWForm(Object.assign({},wForm,{fahrten:nF}));}} style={{width:'100%',marginBottom:12}}>+ Fahrt hinzufügen</button>
                {(wForm.fahrten||[]).length>0&&(
                  <div style={{background:'#f0fdf4',borderRadius:8,padding:'8px 12px',fontSize:13,color:'#166534',fontWeight:600,display:'flex',justifyContent:'space-between'}}>
                    <span>Summe Fahrtkosten</span>
                    <span>{fmt(sumFahrten)} ({(wForm.fahrten||[]).reduce(function(t,f){return t+(f.km||0);},0)} km)</span>
                  </div>
                )}
                {(wForm.fahrten||[]).length===0&&<div style={{color:'#999',fontSize:13,textAlign:'center',padding:'12px 0'}}>Keine Fahrten – weiter wenn nicht zutreffend.</div>}
              </div>
            )}
            {wStep===3&&(
              <div>
                <div style={{fontSize:13,color:'#666',marginBottom:14}}>Sonstige Reisekosten (Hotel, Flug, Bahn, Taxi…)</div>
                <div style={{maxHeight:360,overflowY:'auto',marginBottom:12}}>
                  {(wForm.kosten||[]).map(function(k,idx){
                    var betrag=k.betrag||0;
                    return(
                      <div key={k.id||idx} style={{background:'#f9f8f6',borderRadius:8,padding:12,marginBottom:10,border:'1px solid var(--border)'}}>
                        <div style={{display:'flex',gap:10,marginBottom:8}}>
                          <select value={k.typ||'sonstiges'} onChange={function(e){
                            var neueK=wForm.kosten.map(function(x,i){return i===idx?Object.assign({},x,{typ:e.target.value}):x;});
                            setWForm(Object.assign({},wForm,{kosten:neueK}));
                          }} style={{flex:1}}>
                            <option value="hotel">Hotel</option>
                            <option value="flug">Flug</option>
                            <option value="bahn">Bahn/ÖPNV</option>
                            <option value="taxi">Taxi</option>
                            <option value="sonstiges">Sonstiges</option>
                          </select>
                          <span style={{fontSize:13,fontWeight:600,color:'#27AE60',whiteSpace:'nowrap',paddingTop:5}}>{fmt(betrag)}</span>
                          <button onClick={function(){var neueK=wForm.kosten.filter(function(x,i){return i!==idx;});setWForm(Object.assign({},wForm,{kosten:neueK}));}} style={{background:'none',border:'none',color:'#C0392B',cursor:'pointer',fontSize:16,padding:0}}>✕</button>
                        </div>
                        <input value={k.beschreibung||''} onChange={function(e){
                          var neueK=wForm.kosten.map(function(x,i){return i===idx?Object.assign({},x,{beschreibung:e.target.value}):x;});
                          setWForm(Object.assign({},wForm,{kosten:neueK}));
                        }} style={{width:'100%',marginBottom:8}} placeholder="Beschreibung (z.B. Hotel Mailand 2 Nächte)" />
                        <input type="number" value={k.betrag||0} onChange={function(e){
                          var neueK=wForm.kosten.map(function(x,i){return i===idx?Object.assign({},x,{betrag:+e.target.value}):x;});
                          setWForm(Object.assign({},wForm,{kosten:neueK}));
                        }} style={{width:'100%'}} placeholder="Betrag (€)" min="0" step="1"/>
                      </div>
                    );
                  })}
                </div>
                <button className="btn-outline btn-sm" onClick={function(){var neueK=(wForm.kosten||[]).concat([{id:uid(),typ:'sonstiges',beschreibung:'',betrag:0}]);setWForm(Object.assign({},wForm,{kosten:neueK}));}} style={{width:'100%',marginBottom:16}}>+ Kostenposition hinzufügen</button>
                <Field label="Vorschuss geleistet (€)"><input type="number" value={wForm.vorschuss||0} onChange={function(e){setWForm(Object.assign({},wForm,{vorschuss:+e.target.value}));}} style={{width:'100%'}} min="0" step="10"/></Field>
              </div>
            )}
            {wStep===4&&(
              <div>
                <Card style={{background:'var(--cream)',marginBottom:16}}>
                  <div style={{display:'flex',justifyContent:'space-between',marginBottom:8}}><span>Tagegelder:</span><strong>{fmt(sumTage)}</strong></div>
                  {sumFahrten>0&&<div style={{display:'flex',justifyContent:'space-between',marginBottom:8}}><span>Fahrtkosten:</span><strong>{fmt(sumFahrten)}</strong></div>}
                  {sumKosten>0&&<div style={{display:'flex',justifyContent:'space-between',marginBottom:8}}><span>Sonstige Reisekosten:</span><strong>{fmt(sumKosten)}</strong></div>}
                  <div style={{borderTop:'1px solid rgba(0,0,0,0.1)',paddingTop:8,display:'flex',justifyContent:'space-between',marginBottom:8}}><span>Gesamt:</span><strong style={{fontSize:16}}>{fmt(gesamt)}</strong></div>
                  {(wForm.vorschuss||0)>0&&<div style={{display:'flex',justifyContent:'space-between'}}><span>Abzgl. Vorschuss:</span><strong style={{color:'#C0392B'}}>– {fmt(wForm.vorschuss)}</strong></div>}
                </Card>
                <div style={{background:'#E3F0FF',borderRadius:8,padding:12,marginBottom:14,fontSize:13}}>
                  <div style={{display:'flex',justifyContent:'space-between'}}><span>Auszuzahlender Betrag:</span><strong style={{color:'#3B6FE0',fontSize:15}}>{fmt(auszuzahlen)}</strong></div>
                </div>
                {(s.sevDeskToken||s.workerUrl)&&<div style={{marginBottom:12}}>
                  <SevDeskBtn label="📒 sevDesk"
                    linked={!!wForm.sevdeskLinked} linkedUrl={wForm.sevdeskUrl||''}
                    onSend={function(){return sdCreateVoucher(s,wForm);}}
                    onSuccess={function(url){
                      var updated=Object.assign({},wForm,{sevdeskLinked:true,sevdeskUrl:url});
                      setWForm(updated);
                      dispatch({type:'SAVE_REISE',payload:updated});
                      dispatch({type:'LOCK_RECORD',payload:{collection:'reisekostenberichte',id:wForm.id,action:'Exportiert nach sevDesk'}});
                    }}/>
                </div>}
              </div>
            )}
          </div>
          <div style={{display:'flex',justifyContent:'space-between'}}>
            <div style={{display:'flex',gap:8}}>
              {wStep>0&&<button className="btn-outline" onClick={function(){setWStep(wStep-1);}}>← Zurück</button>}
            </div>
            <div style={{display:'flex',gap:8}}>
              {wStep<4&&<button className="btn-dark" onClick={function(){setWStep(wStep+1);}}>Weiter →</button>}
              {wStep===4&&<button className="btn-dark" onClick={doSave}>Speichern</button>}
            </div>
          </div>
        </Modal>
      )}
    </div>
  );
}

// ─── KALKULATION WIZARD ──────────────────────────────────────────────────────
function KalkWizard({state,dispatch,initial,onClose,onAlsAngebot}){
  var kunden=state.kunden||[];
  var settings=state.settings;
  var nextNr=state.nextKalkulationNr||1;
  var isEdit=!!(initial&&initial.id);
  var initA=initial?migrateAuftrag(initial):{
    id:uid(),nr:nextNr,datum:today(),
    kundeId:'',titel:'',positionen:[],rabatt:0,rabattTyp:'prozent',
    anzahlungProzent:40,
    zahlungsbedingungen:'40 % Anzahlung bei Auftragserteilung, Restsumme nach Lieferung und Montage.'
  };

  var [step,setStep]=useState(0);
  var [a,setA]=useState(initA);
  var [neuerKunde,setNeuerKunde]=useState(false);
  var [nkForm,setNkForm]=useState({name:'',strasse:'',plzOrt:'',tel:'',email:''});
  var [newMoebelName,setNewMoebelName]=useState('');
  var [rechnungGestellt,setRechnungGestellt]=useState(false);
  var [eigenbau,setEigenbau]=useState(null); // {posId, items:[{materialId,menge}], aufschlag}

  var STEPS=['Auftrag','Positionen','Kalkulation','Zusammenfassung'];
  var ZB_PRESETS=[
    {key:'anzahlung40', label:'Anzahlung 40 %', text:'40 % Anzahlung bei Auftragserteilung, Restsumme nach Lieferung und Montage.', pct:40},
    {key:'anzahlung60', label:'Anzahlung 60 %', text:'60 % Anzahlung bei Auftragserteilung, Restsumme nach Lieferung und Montage.', pct:60},
    {key:'nach_lieferung', label:'Zahlung nach Lieferung', text:'Zahlung innerhalb von 14 Tagen nach Lieferung und Montage, netto ohne Abzug.', pct:null},
    {key:'nach_rechnung', label:'Zahlung nach Rechnungsstellung', text:'Zahlung innerhalb von 14 Tagen nach Rechnungsstellung, netto ohne Abzug.', pct:null},
    {key:'individuell', label:'Individuell …', text:'', pct:null},
  ];
  var netto=calcNetto(a,settings);
  var iw={width:'100%'};
  var ORDER_TYP=['planung','moebel','werkstattmiete','montage','anfahrt'];

  function hasTyP(typ){return a.positionen.some(function(p){return p.typ===typ;});}
  function togglePos(typ,template){
    if(hasTyP(typ)){setA(function(prev){return Object.assign({},prev,{positionen:prev.positionen.filter(function(x){return x.typ!==typ;})});});}
    else{
      var newP=Object.assign({id:uid(),typ:typ},template);
      var newOrd=ORDER_TYP.indexOf(typ);
      setA(function(prev){
        var arr=prev.positionen.slice();var idx=arr.length;
        for(var i=0;i<arr.length;i++){if(ORDER_TYP.indexOf(arr[i].typ)>newOrd){idx=i;break;}}
        arr.splice(idx,0,newP);
        return Object.assign({},prev,{positionen:arr});
      });
    }
  }
  function movePos(id,dir){
    setA(function(prev){
      var arr=prev.positionen.slice();var idx=arr.findIndex(function(x){return x.id===id;});
      if(idx<0) return prev;var ni=idx+dir;if(ni<0||ni>=arr.length) return prev;
      var tmp=arr[idx];arr[idx]=arr[ni];arr[ni]=tmp;
      return Object.assign({},prev,{positionen:arr});
    });
  }
  function addMoebel(){
    var name=newMoebelName.trim()||'Möbelstück';
    var newP={id:uid(),typ:'moebel',name:name,stunden:0,stundenSatz:'werkstatt',material:[],beschreibung:''};
    var moebOrd=ORDER_TYP.indexOf('moebel');
    setA(function(prev){
      var arr=prev.positionen.slice();var idx=arr.length;
      for(var i=0;i<arr.length;i++){if(ORDER_TYP.indexOf(arr[i].typ)>moebOrd){idx=i;break;}}
      arr.splice(idx,0,newP);return Object.assign({},prev,{positionen:arr});
    });
    setNewMoebelName('');
  }
  function updPos(id,key,val){setA(function(prev){return Object.assign({},prev,{positionen:prev.positionen.map(function(x){return x.id===id?Object.assign({},x,{[key]:val}):x;})});});}
  function removePos(id){setA(function(prev){return Object.assign({},prev,{positionen:prev.positionen.filter(function(x){return x.id!==id;})});});}
  function addMat(posId){
    var newM={id:uid(),bezeichnung:'',ek:0,aufschlag:settings.aufschlagStd};
    setA(function(prev){return Object.assign({},prev,{positionen:prev.positionen.map(function(x){return x.id===posId?Object.assign({},x,{material:x.material.concat([newM])}):x;})});});
  }
  function updMat(posId,matId,key,val){
    setA(function(prev){return Object.assign({},prev,{positionen:prev.positionen.map(function(x){
      if(x.id!==posId) return x;
      return Object.assign({},x,{material:x.material.map(function(m){return m.id===matId?Object.assign({},m,{[key]:val}):m;})});
    })});});
  }
  function delMat(posId,matId){
    setA(function(prev){return Object.assign({},prev,{positionen:prev.positionen.map(function(x){
      if(x.id!==posId) return x;
      return Object.assign({},x,{material:x.material.filter(function(m){return m.id!==matId;})});
    })});});
  }
  function saveNK(){
    var nk=Object.assign({id:uid()},nkForm);
    dispatch({type:'SAVE_KUNDE',payload:nk});
    setA(function(prev){return Object.assign({},prev,{kundeId:nk.id});});
    setNeuerKunde(false);setNkForm({name:'',strasse:'',plzOrt:'',tel:'',email:''});
  }
  function doSave(){
    dispatch({type:'SAVE_KALKULATION',payload:Object.assign({},a,{_ts:Date.now()})});
    onClose();
  }
  function doSaveUndAngebot(){
    var kalk=Object.assign({},a,{_ts:Date.now()});
    dispatch({type:'SAVE_KALKULATION',payload:kalk});
    var angebot={
      id:uid(),angebotNr:null,datum:today(),
      titel:kalk.titel,kundeId:kalk.kundeId,
      kalkulationId:kalk.id,positionen:kalk.positionen.slice(),
      rabatt:kalk.rabatt||0,rabattTyp:kalk.rabattTyp||'prozent',
      anzahlungProzent:kalk.anzahlungProzent||40,
      zahlungsbedingungen:kalk.zahlungsbedingungen||'',
      status:'Entwurf',_ts:Date.now()
    };
    dispatch({type:'SAVE_ANGEBOT',payload:angebot});
    if(onAlsAngebot) onAlsAngebot();
    onClose();
  }
  function doSaveAlsRechnung(){
    var kalk=Object.assign({},a,{_ts:Date.now()});
    dispatch({type:'SAVE_KALKULATION',payload:kalk});
    var rabattTyp=kalk.rabattTyp||'prozent';
    var rabattVal=kalk.rabatt||0;
    var nettoGesamt=calcNetto(kalk,settings);
    var rabattBetrag=rabattTyp==='betrag'?rabattVal:nettoGesamt*rabattVal/100;
    var nettoNachRabatt=nettoGesamt-rabattBetrag;
    // Positionen aus Kalkulation übernehmen
    var direktPos=(kalk.positionen||[]).filter(function(p){return !p.isAlternativ;}).map(function(pos){
      var bez='',preis=0;
      var desc='';
      if(pos.typ==='planung'){
        if(!pos.berechnen) return null;
        preis=(pos.stunden||0)*settings.planung;
        bez='Planung / Konstruktion';
        desc=pos.beschreibung||'';
      } else if(pos.typ==='moebel'){
        var matT=(pos.material||[]).reduce(function(t,m){return t+(m.ek||0)*(1+(m.aufschlag||0)/100);},0);
        var stdH=pos.stunden!==undefined?pos.stunden:(pos.werkstattStunden||0);
        var stdRate=pos.stundenSatz==='planung'?settings.planung:(pos.stundenSatz==='montage'?settings.montage:(pos.stundenSatz==='individuell'?(pos.stundenSatzIndividuell||0):settings.werkstatt));
        preis=matT+stdH*stdRate+(pos.stunden!==undefined?0:(pos.planungStunden||0)*settings.planung)+(pos.werkstattmieteIntern||0);
        bez=pos.name||'Produktion';
        desc=pos.beschreibung||'';
      } else if(pos.typ==='werkstattmiete'){
        preis=pos.preis||0;
        bez='Werkstattmiete';
        desc=pos.beschreibung||'';
      } else if(pos.typ==='montage'){
        if(pos.gratis) return null;
        preis=(pos.stunden||0)*settings.montage;
        bez='Montage';
        desc=pos.beschreibung||'';
      } else if(pos.typ==='anfahrt'){
        if(pos.gratis) return null;
        preis=(pos.km||0)*0.6+(pos.fahrzeit||0)*55;
        bez='Anfahrt';
        desc=pos.beschreibung||'';
      }
      if(!bez||preis<=0) return null;
      return {id:uid(),bezeichnung:bez,beschreibung:desc,menge:1,einheit:'pauschal',einzelpreis:preis};
    }).filter(Boolean);
    var rec={
      id:uid(),rechnungNr:null,datum:today(),
      angebotId:null,kundeId:kalk.kundeId,
      angebotNr:null,titel:kalk.titel,
      gesamtNetto:nettoNachRabatt,betragNetto:nettoNachRabatt,
      anzahlungNetto:0,abzugAnzahlung:0,restNetto:nettoNachRabatt,
      positionen:direktPos,
      status:'Entwurf',
      vollrechnung:true,direktrechnung:true,
      kalkulationId:kalk.id,_ts:Date.now()
    };
    dispatch({type:'SAVE_SCHLUSS',payload:rec});
    setRechnungGestellt(true);
    setTimeout(function(){if(setPage) setPage('schluss');onClose();},1200);
  }

  function posLabel(pos){
    if(pos.typ==='planung') return 'Planung & Konstruktion';
    if(pos.typ==='moebel') return pos.name||'Möbelstück';
    if(pos.typ==='werkstattmiete') return 'Werkstattmiete';
    if(pos.typ==='montage') return 'Montage';
    if(pos.typ==='anfahrt') return 'Anfahrt';
    return pos.typ;
  }
  function posNetto(pos){
    if(pos.typ==='planung') return pos.berechnen?(pos.stunden||0)*settings.planung:0;
    if(pos.typ==='moebel'){var matT=(pos.material||[]).reduce(function(t,m){return t+(m.ek||0)*(1+(m.aufschlag||0)/100);},0);var stdH=pos.stunden!==undefined?pos.stunden:(pos.werkstattStunden||0);var stdRate=pos.stundenSatz==='planung'?settings.planung:(pos.stundenSatz==='montage'?settings.montage:(pos.stundenSatz==='individuell'?(pos.stundenSatzIndividuell||0):settings.werkstatt));var legPlan=pos.stunden!==undefined?0:(pos.planungStunden||0);return matT+stdH*stdRate+legPlan*settings.planung+(pos.werkstattmieteIntern||0);}
    if(pos.typ==='werkstattmiete') return pos.preis||0;
    if(pos.typ==='montage') return pos.gratis?0:(pos.stunden||0)*settings.montage;
    if(pos.typ==='anfahrt') return pos.gratis?0:(pos.km||0)*0.6+(pos.fahrzeit||0)*55;
    return 0;
  }

  return(
    <div style={{position:'fixed',inset:0,background:'rgba(0,0,0,0.5)',zIndex:1000,display:'flex',alignItems:'center',justifyContent:'center',padding:16}}>
      {eigenbau&&(function(){
        var materialpreise=state.materialpreise||[];
        var ebItems=eigenbau.items||[];
        var aufschlag=eigenbau.aufschlag||30;
        var totalEK=ebItems.reduce(function(t,item){
          var mp=materialpreise.find(function(m){return m.id===item.materialId;});
          return t+(mp?mp.preis*(item.menge||0):0);
        },0);
        var totalVK=totalEK*(1+aufschlag/100);
        function addEbItem(){setEigenbau(Object.assign({},eigenbau,{items:[].concat(ebItems,[{id:uid(),materialId:materialpreise[0]?materialpreise[0].id:'',menge:1}])}));}
        function updEbItem(id,field,val){setEigenbau(Object.assign({},eigenbau,{items:ebItems.map(function(it){return it.id===id?Object.assign({},it,Object.fromEntries([[field,val]])):it;})}));}
        function delEbItem(id){setEigenbau(Object.assign({},eigenbau,{items:ebItems.filter(function(it){return it.id!==id;})}));}
        function applyEigenbau(){
          if(ebItems.length===0){alert('Bitte mindestens ein Material hinzufügen.');return;}
          // Materialien als neue Material-Positionen in die Möbel-Position übernehmen
          var newMats=ebItems.map(function(item){
            var mp=materialpreise.find(function(m){return m.id===item.materialId;});
            return {id:uid(),bezeichnung:(mp?mp.name:'Material')+' '+item.menge+' '+(mp?mp.einheit:''),ek:mp?(mp.preis*item.menge):0,aufschlag:aufschlag};
          });
          setA(function(prev){
            var poss=prev.positionen.map(function(pos){
              if(pos.id!==eigenbau.posId) return pos;
              return Object.assign({},pos,{material:[].concat(pos.material||[],newMats)});
            });
            return Object.assign({},prev,{positionen:poss});
          });
          setEigenbau(null);
        }
        return(
          <div style={{position:'fixed',inset:0,background:'rgba(0,0,0,0.6)',zIndex:1100,display:'flex',alignItems:'center',justifyContent:'center',padding:16}}>
            <div style={{background:'#fff',borderRadius:12,width:'100%',maxWidth:620,maxHeight:'90vh',overflow:'auto',boxShadow:'0 20px 60px rgba(0,0,0,0.4)'}}>
              <div style={{background:'#1E1E1E',padding:'16px 20px',borderRadius:'12px 12px 0 0',display:'flex',alignItems:'center',justifyContent:'space-between'}}>
                <div><div style={{color:'#fff',fontWeight:700,fontSize:15}}>🔨 Eigenbau kalkulieren</div><div style={{color:'#888',fontSize:12}}>Materialien aus Preisdatenbank wählen</div></div>
                <button onClick={function(){setEigenbau(null);}} style={{background:'none',border:'none',color:'#888',fontSize:22,cursor:'pointer'}}>&times;</button>
              </div>
              <div style={{padding:20}}>
                <div style={{display:'flex',gap:8,marginBottom:12,alignItems:'center'}}>
                  <span style={{fontSize:13,color:'#555'}}>Aufschlag auf Materialkosten:</span>
                  <input type="number" value={aufschlag} min="0" max="200" step="5" onChange={function(e){setEigenbau(Object.assign({},eigenbau,{aufschlag:+e.target.value}));}} style={{width:80,padding:'5px 8px',border:'1px solid var(--border)',borderRadius:6}}/>
                  <span style={{fontSize:13,color:'#555'}}>%</span>
                </div>
                <table style={{width:'100%',borderCollapse:'collapse',fontSize:13,marginBottom:8}}>
                  <thead><tr style={{background:'#f5f3ef'}}>
                    <th style={{padding:'6px 8px',textAlign:'left',fontWeight:600,color:'#555',fontSize:11}}>Material</th>
                    <th style={{padding:'6px 8px',textAlign:'right',fontWeight:600,color:'#555',fontSize:11}}>Menge</th>
                    <th style={{padding:'6px 8px',textAlign:'left',fontWeight:600,color:'#555',fontSize:11}}>Einheit</th>
                    <th style={{padding:'6px 8px',textAlign:'right',fontWeight:600,color:'#555',fontSize:11}}>EK/Einh.</th>
                    <th style={{padding:'6px 8px',textAlign:'right',fontWeight:600,color:'#555',fontSize:11}}>Gesamt EK</th>
                    <th style={{width:28}}></th>
                  </tr></thead>
                  <tbody>
                    {ebItems.map(function(item){
                      var mp=materialpreise.find(function(m){return m.id===item.materialId;});
                      var ek=mp?(mp.preis*(item.menge||0)):0;
                      return(
                        <tr key={item.id} style={{borderBottom:'1px solid var(--border)'}}>
                          <td style={{padding:'6px 8px'}}>
                            <select value={item.materialId} onChange={function(e){updEbItem(item.id,'materialId',e.target.value);}} style={{width:'100%',padding:'5px 6px',border:'1px solid var(--border)',borderRadius:5,fontSize:13,fontFamily:'inherit'}}>
                              {materialpreise.map(function(m){return <option key={m.id} value={m.id}>{m.name}</option>;})}
                            </select>
                          </td>
                          <td style={{padding:'6px 8px'}}>
                            <input type="number" value={item.menge||0} min="0" step="0.1" onChange={function(e){updEbItem(item.id,'menge',+e.target.value);}} style={{width:70,padding:'5px 6px',border:'1px solid var(--border)',borderRadius:5,textAlign:'right',fontFamily:'inherit'}}/>
                          </td>
                          <td style={{padding:'6px 8px',color:'#888'}}>{mp?mp.einheit:'–'}</td>
                          <td style={{padding:'6px 8px',textAlign:'right',color:'#888'}}>{mp?fmt(mp.preis):'–'}</td>
                          <td style={{padding:'6px 8px',textAlign:'right',fontWeight:600}}>{fmt(ek)}</td>
                          <td><button onClick={function(){delEbItem(item.id);}} style={{background:'none',border:'none',color:'#C0392B',cursor:'pointer',fontSize:14}}>✕</button></td>
                        </tr>
                      );
                    })}
                  </tbody>
                </table>
                <button className="btn-outline btn-sm" onClick={addEbItem} style={{width:'100%',marginBottom:16}}>+ Material hinzufügen</button>
                {ebItems.length>0&&(
                  <div style={{background:'#f0ede8',borderRadius:8,padding:12,fontSize:13,marginBottom:16}}>
                    <div style={{display:'flex',justifyContent:'space-between',marginBottom:4}}><span style={{color:'#888'}}>Materialkosten EK gesamt</span><strong>{fmt(totalEK)}</strong></div>
                    <div style={{display:'flex',justifyContent:'space-between',marginBottom:4}}><span style={{color:'#888'}}>Aufschlag {aufschlag}%</span><span>+ {fmt(totalEK*aufschlag/100)}</span></div>
                    <div style={{display:'flex',justifyContent:'space-between'}}><span style={{fontWeight:700}}>Materialwert VK netto</span><strong style={{fontSize:15,color:'#27AE60'}}>{fmt(totalVK)}</strong></div>
                  </div>
                )}
                <div style={{display:'flex',gap:10}}>
                  <button className="btn-outline" style={{flex:1}} onClick={function(){setEigenbau(null);}}>Abbrechen</button>
                  <button className="btn-dark" style={{flex:2,background:'#3B6FE0'}} onClick={applyEigenbau}>✓ In Kalkulation übernehmen</button>
                </div>
              </div>
            </div>
          </div>
        );
      })()}
      <div className="sk-wizard-box" style={{background:'#fff',width:'100%',display:'flex',flexDirection:'column',boxShadow:'0 20px 60px rgba(0,0,0,0.3)'}}>
        <div className="sk-wizard-header" style={{background:'#1E1E1E',padding:'18px 24px',display:'flex',alignItems:'center',justifyContent:'space-between',flexShrink:0}}>
          <div>
            <div style={{color:'#fff',fontWeight:700,fontSize:16}}>{isEdit?'Kalkulation bearbeiten':'Neue Kalkulation'}</div>
            <div style={{color:'#888',fontSize:12,marginTop:2}}>{STEPS[step]} · Schritt {step+1}/{STEPS.length}</div>
          </div>
          <div style={{display:'flex',alignItems:'center',gap:16}}>
            <div style={{textAlign:'right'}}>
              <div style={{color:'#aaa',fontSize:10,textTransform:'uppercase',letterSpacing:.5}}>Netto</div>
              <div style={{color:netto>0?'#fff':'#555',fontWeight:700,fontSize:18}}>{fmt(netto)}</div>
              <div style={{color:'#666',fontSize:11}}>{fmt(netto*1.19)} brutto</div>
            </div>
            <button onClick={onClose} style={{background:'none',border:'none',color:'#666',fontSize:22,cursor:'pointer',lineHeight:1}}>&times;</button>
          </div>
        </div>

        <div className="sk-wizard-tabs" style={{display:'flex',padding:'0 24px',background:'#f9f8f6',borderBottom:'1px solid var(--border)',flexShrink:0}}>
          {STEPS.map(function(s,i){return(
            <button key={i} onClick={function(){setStep(i);}} style={{padding:'12px 0',marginRight:24,fontSize:13,background:'none',border:'none',cursor:'pointer',fontFamily:'inherit',color:i===step?'#1E1E1E':'#aaa',fontWeight:i===step?700:400,borderBottom:i===step?'2px solid #1E1E1E':'2px solid transparent'}}>{s}</button>
          );})}
        </div>

        <div style={{padding:24,flex:1,overflowY:'auto'}}>

          {step===0&&(
            <div>
              <div className="sk-grid-2">
                <Field label="Datum"><input value={a.datum} onChange={function(e){setA(function(p){return Object.assign({},p,{datum:e.target.value});});}} style={iw}/></Field>
                <Field label="Kalkulation-Nr.">
                  <div style={{padding:'8px 10px',background:'#f0ede8',borderRadius:6,fontSize:14,fontWeight:600,border:'1px solid var(--border)'}}>#{String(a.nr||nextNr).padStart(4,'0')}</div>
                </Field>
              </div>
              <Field label="Projekttitel">
                <input value={a.titel||''} onChange={function(e){setA(function(p){return Object.assign({},p,{titel:e.target.value});});}} style={iw} placeholder="z.B. Küche Gröbenzell"/>
              </Field>
              <Field label="Kunde auswählen">
                <select value={a.kundeId||''} onChange={function(e){setA(function(p){return Object.assign({},p,{kundeId:e.target.value});});}} style={iw}>
                  <option value="">– Kunden wählen –</option>
                  {kunden.map(function(k){return <option key={k.id} value={k.id}>{k.name}</option>;})}
                </select>
              </Field>
              <Field label="Zahlungsbedingungen">
                {(function(){
                  var zbText=a.zahlungsbedingungen||'';
                  var activePreset=ZB_PRESETS.find(function(p){return p.key!=='individuell'&&p.text===zbText;});
                  var selVal=activePreset?activePreset.key:'individuell';
                  return(
                    <div>
                      <select value={selVal} onChange={function(e){
                        var preset=ZB_PRESETS.find(function(p){return p.key===e.target.value;});
                        if(preset&&preset.key!=='individuell'){
                          setA(function(p){return Object.assign({},p,{zahlungsbedingungen:preset.text,anzahlungProzent:preset.pct||p.anzahlungProzent});});
                        } else {
                          setA(function(p){return Object.assign({},p,{zahlungsbedingungen:'',_zbIndividuell:true});});
                        }
                      }} style={Object.assign({},iw,{marginBottom:selVal==='individuell'?8:0})}>
                        {ZB_PRESETS.map(function(p){return <option key={p.key} value={p.key}>{p.label}</option>;})}
                      </select>
                      {selVal==='individuell'&&(
                        <textarea value={zbText} onChange={function(e){setA(function(p){return Object.assign({},p,{zahlungsbedingungen:e.target.value});});}}
                          style={{width:'100%',height:60,resize:'vertical',fontSize:13,fontFamily:'inherit',border:'1px solid var(--border)',borderRadius:6,padding:'8px 10px'}}
                          placeholder="Zahlungstext für das Angebot …"/>
                      )}
                    </div>
                  );
                })()}
              </Field>
              {!neuerKunde&&<button className="btn-outline btn-sm" onClick={function(){setNeuerKunde(true);}}>+ Neuen Kunden anlegen</button>}
              {neuerKunde&&(
                <Card style={{background:'var(--cream)',marginTop:12}}>
                  <div style={{fontWeight:600,marginBottom:10,fontSize:14}}>Neuer Kunde</div>
                  <Field label="Name *"><input value={nkForm.name} onChange={function(e){setNkForm(Object.assign({},nkForm,{name:e.target.value}));}} style={iw}/></Field>
                  <div className="sk-grid-2">
                    <Field label="Straße"><input value={nkForm.strasse} onChange={function(e){setNkForm(Object.assign({},nkForm,{strasse:e.target.value}));}} style={iw}/></Field>
                    <Field label="PLZ Ort"><input value={nkForm.plzOrt} onChange={function(e){setNkForm(Object.assign({},nkForm,{plzOrt:e.target.value}));}} style={iw}/></Field>
                  </div>
                  <div className="sk-grid-2">
                    <Field label="Tel"><input value={nkForm.tel} onChange={function(e){setNkForm(Object.assign({},nkForm,{tel:e.target.value}));}} style={iw}/></Field>
                    <Field label="E-Mail"><input value={nkForm.email} onChange={function(e){setNkForm(Object.assign({},nkForm,{email:e.target.value}));}} style={iw}/></Field>
                  </div>
                  <div style={{display:'flex',gap:8,marginTop:4}}>
                    <button className="btn-dark btn-sm" onClick={saveNK} disabled={!nkForm.name}>Anlegen &amp; auswählen</button>
                    <button className="btn-outline btn-sm" onClick={function(){setNeuerKunde(false);}}>Abbrechen</button>
                  </div>
                </Card>
              )}
            </div>
          )}

          {step===1&&(
            <div>
              <div style={{fontSize:13,color:'#666',marginBottom:20,lineHeight:1.5}}>Wähle alle Leistungen und füge individuelle Positionen einzeln hinzu.</div>
              <div style={{marginBottom:24}}>
                <div style={{fontSize:12,fontWeight:600,color:'#555',textTransform:'uppercase',letterSpacing:.5,marginBottom:10}}>Leistungen</div>
                <div style={{display:'flex',gap:10,flexWrap:'wrap'}}>
                  <Pill active={hasTyP('planung')} onClick={function(){togglePos('planung',{stunden:0,berechnen:true,beschreibung:''});}}>{hasTyP('planung')?'✓ ':''}Planung &amp; Konstruktion</Pill>
                  <Pill active={hasTyP('montage')} onClick={function(){togglePos('montage',{stunden:0,personen:2,beschreibung:''});}}>{hasTyP('montage')?'✓ ':''}Montage</Pill>
                  <Pill active={hasTyP('anfahrt')} onClick={function(){togglePos('anfahrt',{km:0,beschreibung:''});}}>{hasTyP('anfahrt')?'✓ ':''}Anfahrt</Pill>
                </div>
              </div>
              <div>
                <div style={{fontSize:12,fontWeight:600,color:'#555',textTransform:'uppercase',letterSpacing:.5,marginBottom:10}}>Weitere Leistungen</div>
                {a.positionen.filter(function(p){return p.typ==='moebel';}).map(function(p,i){return(
                  <div key={p.id} style={{display:'flex',alignItems:'center',gap:8,marginBottom:8}}>
                    <div style={{flex:1,background:p.isAlternativ?'#f5f5f5':'var(--cream)',border:p.isAlternativ?'1.5px dashed #bbb':'1px solid var(--border)',borderRadius:8,padding:'10px 14px',fontWeight:500,fontSize:14}}>
                      {i+1}. {p.name}{p.isAlternativ&&<span style={{fontSize:12,color:'#aaa',fontWeight:400,marginLeft:8}}>(Alternativ)</span>}
                    </div>
                    <button onClick={function(){updPos(p.id,'isAlternativ',!p.isAlternativ);}} style={{padding:'5px 9px',fontSize:11,background:'#fff',border:'1px solid #ccc',borderRadius:6,cursor:'pointer',color:'#555',whiteSpace:'nowrap'}}>{p.isAlternativ?'↩ Aktiv':'Alt.'}</button>
                    <button className="btn-danger btn-sm" onClick={function(){removePos(p.id);}}>&#10005;</button>
                  </div>
                );})}
                <div style={{display:'flex',gap:8,marginTop:4}}>
                  <input value={newMoebelName} onChange={function(e){setNewMoebelName(e.target.value);}}
                    onKeyDown={function(e){if(e.key==='Enter'&&newMoebelName.trim())addMoebel();}}
                    style={{flex:1}} placeholder="z.B. Badschrank, Küche, Sideboard …"/>
                  <button className="btn-dark" onClick={addMoebel} disabled={!newMoebelName.trim()}>+ Hinzufügen</button>
                </div>
              </div>
              {a.positionen.length>0&&<div style={{marginTop:16,padding:12,background:'#f0ede8',borderRadius:8,fontSize:13,color:'#666'}}><strong style={{color:'#1E1E1E'}}>Ausgewählt:</strong> {a.positionen.map(function(p){return posLabel(p);}).join(' · ')}</div>}
            </div>
          )}

          {step===2&&(
            <div>
              {a.positionen.length===0&&<div style={{color:'#999',padding:'30px 0',textAlign:'center',fontSize:14}}>Keine Positionen – gehe zurück zu «Positionen».</div>}
              {a.positionen.map(function(pos,idx){
                var pn=posNetto(pos);
                return(
                  <div key={pos.id} style={{marginBottom:16,border:pos.isAlternativ?'1.5px dashed #bbb':'1px solid var(--border)',borderRadius:10,overflow:'hidden',opacity:pos.isAlternativ?0.7:1}}>
                    <div style={{background:pos.isAlternativ?'#555':'#1E1E1E',padding:'10px 16px',display:'flex',alignItems:'center',gap:8}}>
                      <div style={{display:'flex',flexDirection:'column',gap:2}}>
                        <button onClick={function(){movePos(pos.id,-1);}} style={{background:'none',border:'none',color:idx===0?'#444':'#aaa',cursor:idx===0?'default':'pointer',fontSize:10,padding:'1px 4px',lineHeight:1}}>▲</button>
                        <button onClick={function(){movePos(pos.id,1);}} style={{background:'none',border:'none',color:idx===a.positionen.length-1?'#444':'#aaa',cursor:idx===a.positionen.length-1?'default':'pointer',fontSize:10,padding:'1px 4px',lineHeight:1}}>▼</button>
                      </div>
                      <span style={{color:'#666',fontSize:12,minWidth:24}}>{'0'+(idx+1)}</span>
                      <span style={{color:'#fff',fontWeight:600,fontSize:14,flex:1}}>{posLabel(pos)}{pos.isAlternativ&&<span style={{fontSize:11,color:'#aaa',fontWeight:400,marginLeft:6}}>(Alternativ)</span>}</span>
                      <span style={{color:'#aaa',fontSize:13}}>{(pos.typ==='planung'&&!pos.berechnen)||pos.gratis||pos.isAlternativ?'gratis':fmt(pn)}</span>
                    </div>
                    <div style={{padding:16,background:'var(--cream)'}}>
                      {pos.typ==='planung'&&(
                        <div>
                          <div className="sk-grid-3">
                            <Field label={'Stunden ('+settings.planung+' €/h)'}><input type="number" value={pos.stunden||0} onChange={function(e){updPos(pos.id,'stunden',+e.target.value);}} style={iw} min="0" step="0.5"/></Field>
                            <Field label="Berechnen?"><div style={{display:'flex',gap:8,marginTop:2}}><Pill active={!!pos.berechnen} onClick={function(){updPos(pos.id,'berechnen',true);}}>Ja</Pill><Pill active={!pos.berechnen} onClick={function(){updPos(pos.id,'berechnen',false);}}>Gratis</Pill></div></Field>
                            <Field label="Betrag"><div style={{padding:'8px 10px',background:'#eee',borderRadius:6,fontSize:14,fontWeight:600}}>{fmt((pos.stunden||0)*settings.planung)}</div></Field>
                          </div>
                          <Field label="Beschreibung (PDF)"><textarea value={pos.beschreibung||''} onChange={function(e){updPos(pos.id,'beschreibung',e.target.value);}} style={{width:'100%',height:60,resize:'vertical',fontSize:13,fontFamily:'inherit',border:'1px solid var(--border)',borderRadius:6,padding:'8px 10px'}} placeholder="Entwurf, Maßplanung und Konstruktion…"/></Field>
                        </div>
                      )}
                      {pos.typ==='moebel'&&(
                        <div>
                          <div className="sk-grid-2">
                            <Field label="Name"><input value={pos.name||''} onChange={function(e){updPos(pos.id,'name',e.target.value);}} style={iw}/></Field>
                            <Field label="Stunden">
                              <div style={{display:'flex',gap:6,flexWrap:'wrap'}}>
                                <input type="number" value={pos.stunden!==undefined?pos.stunden:(pos.werkstattStunden||0)} onChange={function(e){updPos(pos.id,'stunden',+e.target.value);}} style={{flex:'1 1 70px',minWidth:0}} min="0" step="0.5"/>
                                <select value={pos.stundenSatz||(pos.planungStunden>0?'planung':'werkstatt')} onChange={function(e){updPos(pos.id,'stundenSatz',e.target.value);}} style={{flex:'2 1 140px',minWidth:0}}>
                                  <option value="werkstatt">Werkstatt ({settings.werkstatt} €/h)</option>
                                  <option value="planung">Planung / Konstr. ({settings.planung} €/h)</option>
                                  <option value="montage">Montage ({settings.montage} €/h)</option>
                                  <option value="individuell">Individuell …</option>
                                </select>
                                {(pos.stundenSatz==='individuell')&&(
                                  <input type="number" value={pos.stundenSatzIndividuell||0} onChange={function(e){updPos(pos.id,'stundenSatzIndividuell',+e.target.value);}} style={{flex:'1 1 70px',minWidth:0}} min="0" step="5" placeholder="€/h"/>
                                )}
                              </div>
                            </Field>
                          </div>
                          <div style={{marginBottom:12}}>
                            <div style={{fontSize:12,fontWeight:600,color:'#555',textTransform:'uppercase',letterSpacing:.5,marginBottom:8}}>Material &amp; Zukauf</div>
                            {(pos.material||[]).map(function(m,mi){
                              var mpList=state.materialpreise||[];
                              function applyMP(mpId){
                                var mp=mpList.find(function(x){return x.id===mpId;});
                                if(!mp) return;
                                var menge=parseFloat(window.prompt('Menge in '+mp.einheit+'?','1'))||1;
                                var ekKalkuliert=calcMaterialEK(mp,menge);
                                updMat(pos.id,m.id,'bezeichnung',mp.name+' '+menge+' '+mp.einheit);
                                updMat(pos.id,m.id,'ek',parseFloat(ekKalkuliert.toFixed(2)));
                                updMat(pos.id,m.id,'_mpId',mpId);
                                updMat(pos.id,m.id,'_mpDetail','EK '+fmt(mp.preis)+'/'+mp.einheit+' × '+menge+' '+mp.einheit+' × '+mp.wasteFactor+'(Verschnitt) × '+mp.marketBuffer+'(Puffer)');
                              }
                              return(
                              <div key={m.id} style={{background:'#fff',border:'1px solid var(--border)',borderRadius:8,padding:12,marginBottom:8}}>
                                <div style={{display:'flex',gap:8,alignItems:'flex-end',flexWrap:'wrap'}}>
                                  <div style={{flex:'2 1 160px'}}>
                                    <Field label={'Pos. '+(mi+1)+' – Bezeichnung'}>
                                      <input value={m.bezeichnung} onChange={function(e){updMat(pos.id,m.id,'bezeichnung',e.target.value);}} style={iw} placeholder="z.B. Mdrei, Beschläge …"/>
                                    </Field>
                                    {mpList.length>0&&(
                                      <select value={m._mpId||''} onChange={function(e){if(e.target.value)applyMP(e.target.value);}} style={{width:'100%',marginTop:4,fontSize:12,padding:'4px 6px',border:'1px solid var(--border)',borderRadius:4,background:'#f9f7f4',color:'#555'}}>
                                        <option value="">📦 aus Materialdatenbank wählen …</option>
                                        {mpList.map(function(mp){return <option key={mp.id} value={mp.id}>{mp.name} ({mp.einheit}) – {fmt(mp.preis)}</option>;})}
                                      </select>
                                    )}
                                    {m._mpDetail&&<div style={{fontSize:10,color:'#aaa',marginTop:2}}>{m._mpDetail}</div>}
                                  </div>
                                  <div style={{flex:'1 1 80px'}}><Field label="EK inkl. Verschnitt (€)"><input type="number" value={m.ek} onChange={function(e){updMat(pos.id,m.id,'ek',+e.target.value);}} style={iw} min="0"/></Field></div>
                                  <div style={{flex:'1 1 80px'}}><Field label="Aufschlag (%)"><input type="number" value={m.aufschlag} onChange={function(e){updMat(pos.id,m.id,'aufschlag',+e.target.value);}} style={iw} min="0"/></Field></div>
                                  <div style={{flex:'1 1 80px'}}><Field label="VK netto"><div style={{padding:'8px 10px',background:'#f0ede8',borderRadius:6,fontSize:14,fontWeight:600,border:'1px solid var(--border)'}}>{fmt(m.ek*(1+m.aufschlag/100))}</div></Field></div>
                                  <button className="btn-danger btn-sm" onClick={function(){delMat(pos.id,m.id);}} style={{marginBottom:14}}>&#10005;</button>
                                </div>
                              </div>
                              );
                            })}
                            <div style={{display:'flex',gap:8,marginTop:4}}>
                              <button className="btn-outline btn-sm" onClick={function(){addMat(pos.id);}} style={{flex:1,padding:9}}>+ Material / Zukauf</button>
                              <button className="btn-outline btn-sm" onClick={function(){setEigenbau({posId:pos.id,items:[],aufschlag:30});}} style={{flex:1,padding:9,color:'#3B6FE0',borderColor:'#3B6FE0'}}>🔨 Eigenbau kalkulieren</button>
                            </div>
                          </div>
                          <div style={{marginBottom:12}}>
                            <div style={{fontSize:12,fontWeight:600,color:'#555',textTransform:'uppercase',letterSpacing:.5,marginBottom:8}}>Werkstattmiete</div>
                            {(pos.werkstattmieteIntern||0)>0?(
                              <div style={{display:'flex',gap:8,alignItems:'flex-end'}}>
                                <div style={{flex:1}}><Field label="Betrag (€ netto)"><input type="number" value={pos.werkstattmieteIntern||0} onChange={function(e){updPos(pos.id,'werkstattmieteIntern',+e.target.value);}} style={iw} min="0"/></Field></div>
                                <button className="btn-danger btn-sm" onClick={function(){updPos(pos.id,'werkstattmieteIntern',0);}} style={{marginBottom:14}}>&#10005;</button>
                              </div>
                            ):(
                              <button className="btn-outline btn-sm" onClick={function(){updPos(pos.id,'werkstattmieteIntern',0.01);}} style={{padding:9,width:'100%'}}>+ Werkstattmiete hinzufügen</button>
                            )}
                          </div>
                          {<div style={{marginTop:8,padding:'8px 12px',background:'#e8e4de',borderRadius:6,fontSize:13}}>
              {(pos.material||[]).length>0&&<span>Material: <strong>{fmt((pos.material||[]).reduce(function(t,m){return t+m.ek*(1+m.aufschlag/100);},0))}</strong> · </span>}
              {(pos.werkstattmieteIntern||0)>0&&<span>Werkstattmiete: <strong>{fmt(pos.werkstattmieteIntern||0)}</strong> · </span>}
              {(function(){var h=pos.stunden!==undefined?pos.stunden:(pos.werkstattStunden||0);if(!h)return null;var satz=pos.stundenSatz==='planung'?settings.planung:(pos.stundenSatz==='montage'?settings.montage:(pos.stundenSatz==='individuell'?(pos.stundenSatzIndividuell||0):settings.werkstatt));var lbl=pos.stundenSatz==='planung'?'Planung':(pos.stundenSatz==='montage'?'Montage':(pos.stundenSatz==='individuell'?('Individuell ('+( pos.stundenSatzIndividuell||0)+' €/h)'):'Werkstatt'));return <span>{lbl}: <strong>{fmt(h*satz)}</strong> · </span>;})()}
              Gesamt: <strong style={{color:'#1E1E1E'}}>{fmt(pn)}</strong>
            </div>}
                          <Field label="Beschreibung (PDF)"><textarea value={pos.beschreibung||''} onChange={function(e){updPos(pos.id,'beschreibung',e.target.value);}} style={{width:'100%',height:60,resize:'vertical',fontSize:13,fontFamily:'inherit',border:'1px solid var(--border)',borderRadius:6,padding:'8px 10px'}} placeholder="Herstellung in eigener Werkstatt, inkl. aller Beschläge."/></Field>
                        </div>
                      )}
                      {pos.typ==='werkstattmiete'&&<div className="sk-grid-2"><Field label="Miete (€ netto)"><input type="number" value={pos.preis||0} onChange={function(e){updPos(pos.id,'preis',+e.target.value);}} style={iw} min="0"/></Field><Field label="Beschreibung"><input value={pos.beschreibung||''} onChange={function(e){updPos(pos.id,'beschreibung',e.target.value);}} style={iw} placeholder="z.B. Werkstatt Rosenheim, 3 Tage"/></Field></div>}
                      {pos.typ==='montage'&&(
                        <div>
                          <div className="sk-grid-2">
                            <Field label={'Stunden gesamt ('+settings.montage+' €/h)'}><input type="number" value={pos.stunden||0} onChange={function(e){updPos(pos.id,'stunden',+e.target.value);}} style={iw} min="0" step="0.5"/></Field>
                            <Field label="Berechnen?"><div style={{display:'flex',gap:8,marginTop:2}}><Pill active={!pos.gratis} onClick={function(){updPos(pos.id,'gratis',false);}}>Ja</Pill><Pill active={!!pos.gratis} onClick={function(){updPos(pos.id,'gratis',true);}}>Gratis</Pill></div></Field>
                          </div>
                          <Field label="Beschreibung (PDF)"><textarea value={pos.beschreibung||''} onChange={function(e){updPos(pos.id,'beschreibung',e.target.value);}} style={{width:'100%',height:54,resize:'vertical',fontSize:13,fontFamily:'inherit',border:'1px solid var(--border)',borderRadius:6,padding:'8px 10px'}} placeholder="Fachgerechte Montage vor Ort, inkl. An- und Aufbau."/></Field>
                        </div>
                      )}
                      {pos.typ==='anfahrt'&&(
                        <div>
                          <div className="sk-grid-3">
                            <Field label="km (Hin+Rück)"><input type="number" value={pos.km||0} onChange={function(e){updPos(pos.id,'km',+e.target.value);}} style={iw} min="0"/></Field>
                            <Field label="Fahrzeit (h)"><input type="number" value={pos.fahrzeit||0} onChange={function(e){updPos(pos.id,'fahrzeit',+e.target.value);}} style={iw} min="0" step="0.5"/></Field>
                            <Field label="Betrag (0,60 €/km + 55 €/h)"><div style={{padding:'8px 10px',background:'#eee',borderRadius:6,fontSize:14,fontWeight:600}}>{fmt((pos.km||0)*0.6+(pos.fahrzeit||0)*55)}</div></Field>
                          </div>
                          <div style={{display:'flex',gap:16,alignItems:'flex-end',marginBottom:12}}>
                            <Field label="Berechnen?"><div style={{display:'flex',gap:8,marginTop:2}}><Pill active={!pos.gratis} onClick={function(){updPos(pos.id,'gratis',false);}}>Ja</Pill><Pill active={!!pos.gratis} onClick={function(){updPos(pos.id,'gratis',true);}}>Gratis</Pill></div></Field>
                          </div>
                          <Field label="Beschreibung"><input value={pos.beschreibung||''} onChange={function(e){updPos(pos.id,'beschreibung',e.target.value);}} style={iw} placeholder="z.B. Unterammergau – München"/></Field>
                        </div>
                      )}
                    </div>
                  </div>
                );
              })}
            </div>
          )}

          {step===3&&(function(){
            var kd=kunden.find(function(c){return c.id===a.kundeId;});
            var rabattTyp=a.rabattTyp||'prozent';
            var rabattVal=a.rabatt||0;
            var rabattBetrag=rabattTyp==='betrag'?rabattVal:netto*rabattVal/100;
            var nettoNachRabatt=netto-rabattBetrag;
            var mwst=nettoNachRabatt*0.19;
            return(
              <div>
                <div className="sk-grid-2" style={{marginBottom:16}}>
                  <Card style={{background:'var(--cream)'}}><div style={{fontSize:12,color:'#888',marginBottom:4}}>Kunde</div><div style={{fontWeight:600}}>{kd?kd.name:'–'}</div><div style={{fontSize:13,color:'#666'}}>{kd?[kd.strasse,kd.plzOrt].filter(Boolean).join(', '):'–'}</div></Card>
                  <Card style={{background:'var(--cream)'}}><div style={{fontSize:12,color:'#888',marginBottom:4}}>Projekt</div><div style={{fontWeight:600}}>{a.titel||'–'}</div><div style={{fontSize:13,color:'#666'}}>Kalkulation #{String(a.nr||nextNr).padStart(4,'0')} · {a.datum}</div></Card>
                </div>
                <Card style={{marginBottom:14}}>
                  <div style={{fontWeight:700,marginBottom:12,fontSize:14}}>Positionen</div>
                  <div className="sk-tbl">
                  <table style={{width:'100%',borderCollapse:'collapse',fontSize:14}}>
                    <tbody>
                      {a.positionen.map(function(pos,i){
                        var b=posNetto(pos);var lbl=posLabel(pos);var isAlt=!!pos.isAlternativ;
                        if((pos.typ==='planung'&&!pos.berechnen)||pos.gratis) lbl+=' (gratis)';
                        if(isAlt) lbl+=' – Alternativ';
                        return(
                          <tr key={pos.id} style={{borderBottom:'1px solid var(--border)',opacity:isAlt?0.5:1}}>
                            <td style={{padding:'8px 4px',color:'#aaa',fontSize:12,width:28}}>{'0'+(i+1)}</td>
                            <td style={{padding:'8px 4px',color:isAlt?'#999':'inherit'}}>{lbl}</td>
                            <td style={{padding:'8px 4px',textAlign:'right',fontWeight:500,textDecoration:isAlt?'line-through':'none'}}>{isAlt||pos.gratis||(pos.typ==='planung'&&!pos.berechnen)?'–':fmt(b)}</td>
                          </tr>
                        );
                      })}
                      <tr style={{borderTop:'2px solid #1E1E1E'}}><td colSpan={2} style={{padding:'10px 4px',fontWeight:700}}>Zwischensumme netto</td><td style={{padding:'10px 4px',textAlign:'right',fontWeight:700,fontSize:16}}>{fmt(netto)}</td></tr>
                      <tr>
                        <td colSpan={2} style={{padding:'6px 4px',color:'#C0392B',fontSize:13}}>
                          <div style={{display:'flex',alignItems:'center',gap:6}}>
                            <span>Rabatt</span>
                            <input type="number" value={rabattVal} min="0" step={rabattTyp==='betrag'?10:0.5}
                              onChange={function(e){setA(function(p){return Object.assign({},p,{rabatt:+e.target.value});});}}
                              style={{width:70,padding:'2px 6px',fontSize:13,border:'1px solid #fcc',borderRadius:4,textAlign:'right'}}/>
                            <button onClick={function(){setA(function(p){return Object.assign({},p,{rabattTyp:'prozent',rabatt:0});});}}
                              style={{padding:'2px 8px',fontSize:12,borderRadius:'4px 0 0 4px',border:'1px solid #fcc',background:rabattTyp==='prozent'?'#C0392B':'#fff',color:rabattTyp==='prozent'?'#fff':'#C0392B',cursor:'pointer'}}>%</button>
                            <button onClick={function(){setA(function(p){return Object.assign({},p,{rabattTyp:'betrag',rabatt:0});});}}
                              style={{padding:'2px 8px',fontSize:12,borderRadius:'0 4px 4px 0',border:'1px solid #fcc',borderLeft:'none',background:rabattTyp==='betrag'?'#C0392B':'#fff',color:rabattTyp==='betrag'?'#fff':'#C0392B',cursor:'pointer',marginLeft:-1}}>€</button>
                          </div>
                        </td>
                        <td style={{padding:'6px 4px',textAlign:'right',color:'#C0392B',fontSize:13}}>
                          {rabattBetrag>0?'– '+fmt(rabattBetrag):'–'}
                        </td>
                      </tr>
                      <tr><td colSpan={2} style={{padding:'6px 4px',color:'#888',fontSize:13}}>19 % MwSt. auf {fmt(nettoNachRabatt)}</td><td style={{padding:'6px 4px',textAlign:'right',color:'#888',fontSize:13}}>{fmt(mwst)}</td></tr>
                      <tr style={{background:'#1E1E1E'}}><td colSpan={2} style={{padding:'10px 8px',color:'#fff',fontWeight:700,borderRadius:'6px 0 0 6px'}}>Brutto gesamt</td><td style={{padding:'10px 8px',color:'#fff',textAlign:'right',fontWeight:700,fontSize:16,borderRadius:'0 6px 6px 0'}}>{fmt(nettoNachRabatt+mwst)}</td></tr>
                    </tbody>
                  </table>
                  </div>
                </Card>
                <div style={{display:'flex',gap:8,flexWrap:'wrap'}}>
                  <button className="btn-outline" style={{flex:'1 1 120px',padding:11,fontSize:13}} onClick={doSave}>Nur speichern</button>
                  <button className="btn-dark" style={{flex:'2 1 160px',padding:11,fontSize:13,background:'#3B6FE0'}} onClick={doSaveUndAngebot}>📄 Als Angebot</button>
                  <button className="btn-dark" disabled={rechnungGestellt} style={{flex:'2 1 160px',padding:11,fontSize:13,background:rechnungGestellt?'#888':'#27AE60',transition:'background .3s',cursor:rechnungGestellt?'default':'pointer'}} onClick={doSaveAlsRechnung}>{rechnungGestellt?'✓ Rechnung erstellt …':'🧾 Direkt Rechnung stellen'}</button>
                </div>
              </div>
            );
          })()}
        </div>

        <div className="sk-wizard-footer" style={{padding:'14px 24px',borderTop:'1px solid var(--border)',display:'flex',justifyContent:'space-between',background:'#fafafa',flexShrink:0}}>
          <button className="btn-outline" onClick={function(){if(step>0)setStep(step-1);else onClose();}}>{step===0?'Abbrechen':'← Zurück'}</button>
          {step<3&&<button className="btn-dark" onClick={function(){setStep(step+1);}}>Weiter →</button>}
          {step===3&&<button className="btn-dark" onClick={doSave}>&#10003; Speichern</button>}
        </div>
      </div>
    </div>
  );
}

// ─── PRIVATE AUSGABEN ────────────────────────────────────────────────────────
function PrivatPage({state,dispatch}){
  var ausgaben=state.privatAusgaben||[];
  var [form,setForm]=useState(null);
  var PERSONEN=['Schuster','Krapf'];

  function openNew(){
    setForm({id:uid(),person:'Schuster',datum:today(),beschreibung:'',betrag:0,kategorie:'Material',projekt:'',_ts:Date.now()});
  }
  function save(){
    if(!form.beschreibung||!form.betrag){alert('Bitte Beschreibung und Betrag ausfüllen.');return;}
    dispatch({type:'SAVE_PRIVAT',payload:Object.assign({},form,{betrag:parseFloat(form.betrag)||0,_ts:Date.now()})});
    setForm(null);
  }

  // Summierung pro Person
  var totals={};
  PERSONEN.forEach(function(p){totals[p]=0;});
  ausgaben.forEach(function(a){if(totals[a.person]!==undefined) totals[a.person]+=a.betrag||0;});

  var KATEGORIEN=['Material','Maschine/Gerät','Werkzeug','Bürobedarf','Sonstiges'];

  return(
    <div className="sk-page" style={{flex:1,overflow:'auto'}}>
      <div style={{display:'flex',alignItems:'center',justifyContent:'space-between',marginBottom:24}}>
        <div>
          <h1 style={{fontSize:22,fontWeight:700}}>Private Ausgaben</h1>
          <div style={{fontSize:13,color:'#888',marginTop:4}}>Firmenkäufe mit privatem Eigennutzen – Übersicht &amp; Nachverfolgung</div>
        </div>
        <button className="btn-dark" onClick={openNew} style={{background:'#3B6FE0'}}>+ Ausgabe erfassen</button>
      </div>

      {form&&(
        <Modal title="Private Ausgabe erfassen" onClose={function(){setForm(null);}}>
          <Field label="Person">
            <select value={form.person} onChange={function(e){setForm(Object.assign({},form,{person:e.target.value}));}} style={{width:'100%'}}>
              {PERSONEN.map(function(p){return <option key={p} value={p}>{p}</option>;})}
            </select>
          </Field>
          <Field label="Datum">
            <input type="text" value={form.datum} onChange={function(e){setForm(Object.assign({},form,{datum:e.target.value}));}} style={{width:'100%'}}/>
          </Field>
          <Field label="Kategorie">
            <select value={form.kategorie||'Material'} onChange={function(e){setForm(Object.assign({},form,{kategorie:e.target.value}));}} style={{width:'100%'}}>
              {KATEGORIEN.map(function(k){return <option key={k} value={k}>{k}</option>;})}
            </select>
          </Field>
          <Field label="Beschreibung">
            <input value={form.beschreibung} onChange={function(e){setForm(Object.assign({},form,{beschreibung:e.target.value}));}} style={{width:'100%'}} placeholder="z.B. Oberfräse Festool, Holzplatten, ..."/>
          </Field>
          <Field label="Betrag (brutto €)">
            <input type="number" value={form.betrag} min="0" step="10" onChange={function(e){setForm(Object.assign({},form,{betrag:e.target.value}));}} style={{width:'100%'}}/>
          </Field>
          <Field label="Projektbezug (optional)">
            <input value={form.projekt||''} onChange={function(e){setForm(Object.assign({},form,{projekt:e.target.value}));}} style={{width:'100%'}} placeholder="Welches Projekt wurde als Anlass genutzt?"/>
          </Field>
          <Field label="Notiz">
            <textarea value={form.notiz||''} onChange={function(e){setForm(Object.assign({},form,{notiz:e.target.value}));}} style={{width:'100%',height:60,resize:'vertical'}} placeholder="Warum privat? Aufbewahrungsort des Belegs, ..."/>
          </Field>
          <div style={{display:'flex',gap:10,marginTop:8}}>
            <button className="btn-outline" style={{flex:1}} onClick={function(){setForm(null);}}>Abbrechen</button>
            <button className="btn-dark" style={{flex:2}} onClick={save}>Speichern</button>
          </div>
        </Modal>
      )}

      {/* Übersicht pro Person */}
      <div style={{display:'grid',gridTemplateColumns:'repeat(auto-fit,minmax(200px,1fr))',gap:16,marginBottom:24}}>
        {PERSONEN.map(function(p){
          var total=totals[p]||0;
          var anzahl=ausgaben.filter(function(a){return a.person===p;}).length;
          return(
            <Card key={p}>
              <div style={{fontSize:13,color:'#888',marginBottom:4}}>{p}</div>
              <div style={{fontSize:26,fontWeight:800,color:'#C0392B'}}>{fmt(total)}</div>
              <div style={{fontSize:12,color:'#aaa',marginTop:4}}>{anzahl} Ausgabe{anzahl!==1?'n':''}</div>
            </Card>
          );
        })}
        <Card>
          <div style={{fontSize:13,color:'#888',marginBottom:4}}>Gesamt</div>
          <div style={{fontSize:26,fontWeight:800,color:'#1E1E1E'}}>{fmt(PERSONEN.reduce(function(t,p){return t+(totals[p]||0);},0))}</div>
          <div style={{fontSize:12,color:'#aaa',marginTop:4}}>{ausgaben.length} Ausgaben total</div>
        </Card>
      </div>

      <Card>
        {ausgaben.length===0&&<div style={{color:'#999',padding:'12px 0',fontSize:14}}>Noch keine privaten Ausgaben erfasst.</div>}
        {ausgaben.length>0&&(
          <div className="sk-tbl">
          <table style={{width:'100%',borderCollapse:'collapse',fontSize:14}}>
            <thead><tr style={{borderBottom:'1px solid var(--border)'}}>
              {['Datum','Person','Kategorie','Beschreibung','Projekt','Betrag',''].map(function(h){return <th key={h} style={{textAlign:'left',padding:'6px 8px',fontSize:12,color:'#888',fontWeight:600}}>{h}</th>;})}
            </tr></thead>
            <tbody>
              {[].concat(ausgaben).sort(function(a,b){return b._ts>a._ts?1:-1;}).map(function(a){
                return(
                  <tr key={a.id} style={{borderBottom:'1px solid var(--border)'}}>
                    <td style={{padding:'9px 8px',color:'#888'}}>{a.datum||'–'}</td>
                    <td style={{padding:'9px 8px'}}><span style={{fontWeight:600,padding:'2px 8px',borderRadius:10,background:a.person==='Schuster'?'#E3F0FF':'#E8F5E9',color:a.person==='Schuster'?'#3B6FE0':'#27AE60',fontSize:12}}>{a.person}</span></td>
                    <td style={{padding:'9px 8px',fontSize:12,color:'#666'}}>{a.kategorie||'–'}</td>
                    <td style={{padding:'9px 8px'}}>
                      <div style={{fontWeight:500}}>{a.beschreibung||'–'}</div>
                      {a.notiz&&<div style={{fontSize:11,color:'#aaa',marginTop:2}}>{a.notiz}</div>}
                    </td>
                    <td style={{padding:'9px 8px',fontSize:12,color:'#888'}}>{a.projekt||'–'}</td>
                    <td style={{padding:'9px 8px',textAlign:'right',fontWeight:700,color:'#C0392B'}}>{fmt(a.betrag||0)}</td>
                    <td style={{padding:'9px 8px',textAlign:'right'}}>
                      <button className="btn-outline btn-sm" onClick={function(){setForm(Object.assign({},a));}} style={{marginRight:4}}>✎</button>
                      <button className="btn-danger btn-sm" onClick={function(){if(confirm('Ausgabe löschen?'))dispatch({type:'DEL_PRIVAT',payload:a.id});}}>✕</button>
                    </td>
                  </tr>
                );
              })}
            </tbody>
          </table>
          </div>
        )}
      </Card>
    </div>
  );
}

// ─── EINSTELLUNGEN ───────────────────────────────────────────────────────────
function EinstellungenPage({state,dispatch,syncNow}){
  var [s,setS]=useState(Object.assign({},state.settings));
  var [testResult,setTestResult]=useState(null);
  var [mpreise,setMpreise]=useState(state.materialpreise||[]);
  function upd(k,v){setS(function(p){return Object.assign({},p,{[k]:v});});}
  function doSave(){dispatch({type:'SAVE_SETTINGS',payload:s});dispatch({type:'SAVE_MATERIALPREISE',payload:mpreise});setTimeout(function(){if(syncNow)syncNow();},300);alert('Gespeichert ✓');}
  function addMP(){setMpreise(function(p){return[].concat(p,[{id:uid(),name:'Neues Material',einheit:'m²',preis:0}]);});}
  function updMP(id,field,val){setMpreise(function(p){return p.map(function(m){return m.id===id?Object.assign({},m,Object.fromEntries([[field,val]])):m;});});}
  function delMP(id){setMpreise(function(p){return p.filter(function(m){return m.id!==id;});});}
  function testConnection(){
    setTestResult('testing');
    if(!s.workerUrl||!s.workerSecret){setTestResult('missing');return;}
    fetch(s.workerUrl,{headers:{'X-Secret':s.workerSecret}}).then(function(r){
      if(r.status===401){setTestResult('wrong_pw');return;}
      if(!r.ok){setTestResult('error');return;}
      return r.json().then(function(ex){return fetch(s.workerUrl,{method:'POST',headers:{'X-Secret':s.workerSecret,'Content-Type':'application/json'},body:JSON.stringify(ex||{})}).then(function(r2){setTestResult(r2.ok?'ok':'post_err');});});
    }).catch(function(){setTestResult('network');});
  }
  var iw={width:'100%'};var IW={width:'100%',maxWidth:120};
  function exportData(){var blob=new Blob([JSON.stringify(state,null,2)],{type:'application/json'});var url=URL.createObjectURL(blob);var el=document.createElement('a');el.href=url;el.download='schusterkrapf_daten.json';el.click();URL.revokeObjectURL(url);}
  function importData(e){var f=e.target.files[0];if(!f)return;var r=new FileReader();r.onload=function(ev){try{var d=JSON.parse(ev.target.result);dispatch({type:'IMPORT',payload:d});alert('Importiert ✓');}catch(err){alert('Fehler beim Import');}};r.readAsText(f);}
  return(
    <div className="sk-page" style={{flex:1,overflow:'auto'}}>
      <h1 style={{fontSize:22,fontWeight:700,marginBottom:24}}>Einstellungen</h1>
      <div style={{maxWidth:620}}>
        <Card style={{marginBottom:16}}>
          <div style={{fontWeight:700,marginBottom:16,fontSize:15}}>Stundensätze &amp; Kosten</div>
          <div className="sk-grid-3">
            <Field label="Planung (€/h)"><input type="number" value={s.planung} onChange={function(e){upd('planung',+e.target.value);}} style={IW}/></Field>
            <Field label="Werkstatt (€/h)"><input type="number" value={s.werkstatt} onChange={function(e){upd('werkstatt',+e.target.value);}} style={IW}/></Field>
            <Field label="Montage (€/h)"><input type="number" value={s.montage} onChange={function(e){upd('montage',+e.target.value);}} style={IW}/></Field>
            <Field label="Fahrt (€/km)"><input type="number" value={s.fahrt} onChange={function(e){upd('fahrt',+e.target.value);}} style={IW} step="0.01"/></Field>
            <Field label="Std. Aufschlag (%)"><input type="number" value={s.aufschlagStd} onChange={function(e){upd('aufschlagStd',+e.target.value);}} style={IW}/></Field>
            <Field label="Geräte Aufschlag (%)"><input type="number" value={s.aufschlagGeraet} onChange={function(e){upd('aufschlagGeraet',+e.target.value);}} style={IW}/></Field>
          </div>
        </Card>
        <Card style={{marginBottom:16}}>
          <div style={{fontWeight:700,marginBottom:16,fontSize:15}}>Firmendaten (für PDF)</div>
          <Field label="Firmenname"><input value={s.firma} onChange={function(e){upd('firma',e.target.value);}} style={iw}/></Field>
          <div className="sk-grid-2">
            <Field label="Straße"><input value={s.strasse} onChange={function(e){upd('strasse',e.target.value);}} style={iw}/></Field>
            <Field label="PLZ Ort"><input value={s.plzOrt} onChange={function(e){upd('plzOrt',e.target.value);}} style={iw}/></Field>
            <Field label="Telefon"><input value={s.tel} onChange={function(e){upd('tel',e.target.value);}} style={iw}/></Field>
            <Field label="E-Mail"><input value={s.email} onChange={function(e){upd('email',e.target.value);}} style={iw}/></Field>
            <Field label="Steuer-Nr."><input value={s.steuer} onChange={function(e){upd('steuer',e.target.value);}} style={iw}/></Field>
            <Field label="Ort (Datumzeile)"><input value={s.ort} onChange={function(e){upd('ort',e.target.value);}} style={iw}/></Field>
            <Field label="IBAN"><input value={s.iban} onChange={function(e){upd('iban',e.target.value);}} style={iw}/></Field>
            <Field label="BIC"><input value={s.bic} onChange={function(e){upd('bic',e.target.value);}} style={iw}/></Field>
          </div>
          <Field label="Bank"><input value={s.bank} onChange={function(e){upd('bank',e.target.value);}} style={{width:200}}/></Field>
        </Card>
        <button className="btn-dark" onClick={doSave} style={{width:'100%',padding:12,marginBottom:20,fontSize:15}}>Einstellungen speichern</button>
        <Card style={{marginBottom:16}}>
          <div style={{fontWeight:700,marginBottom:12,fontSize:15}}>☁ Cloud-Sync</div>
          <Field label="Worker URL"><input value={s.workerUrl||''} onChange={function(e){upd('workerUrl',e.target.value);setTestResult(null);}} style={iw} placeholder="https://kalkulation.schusterundkrapf.workers.dev"/></Field>
          <Field label="Passwort (SECRET)"><input type="password" value={s.workerSecret||''} onChange={function(e){upd('workerSecret',e.target.value);setTestResult(null);}} style={iw} placeholder="dein gewähltes Passwort"/></Field>
          <div style={{display:'flex',alignItems:'center',gap:12,marginBottom:8}}>
            <button className="btn-outline btn-sm" onClick={testConnection} disabled={!s.workerUrl||!s.workerSecret||testResult==='testing'}>{testResult==='testing'?'Teste…':'Verbindung testen'}</button>
            {testResult==='ok'&&<span style={{color:'#27AE60',fontSize:13}}>✓ OK</span>}
            {(testResult&&testResult!=='ok'&&testResult!=='testing')&&<span style={{color:'#C0392B',fontSize:13}}>✗ Fehler</span>}
          </div>
        </Card>
        <Card style={{marginBottom:16}}>
          <div style={{fontWeight:700,marginBottom:12,fontSize:15}}>🔨 Materialpreise (Eigenbau-Kalkulation)</div>
          <div style={{fontSize:13,color:'#555',marginBottom:12}}>Preise werden im Eigenbau-Kalkulator verwendet. Alle Preise als Einkaufspreis (EK) – Aufschlag wird beim Kalkulieren eingestellt.</div>
          <table style={{width:'100%',borderCollapse:'collapse',fontSize:13,marginBottom:8}}>
            <thead><tr style={{background:'#f5f3ef'}}>
              <th style={{padding:'5px 6px',textAlign:'left',fontSize:11,color:'#888',fontWeight:600}}>Material</th>
              <th style={{padding:'5px 6px',textAlign:'left',fontSize:11,color:'#888',fontWeight:600}}>Einheit</th>
              <th style={{padding:'5px 6px',textAlign:'right',fontSize:11,color:'#888',fontWeight:600}}>EK-Preis</th>
              <th style={{width:28}}></th>
            </tr></thead>
            <tbody>
              {mpreise.map(function(m){return(
                <tr key={m.id} style={{borderBottom:'1px solid var(--border)'}}>
                  <td style={{padding:'4px 6px'}}><input value={m.name} onChange={function(e){updMP(m.id,'name',e.target.value);}} style={{width:'100%',border:'1px solid var(--border)',borderRadius:4,padding:'4px 6px',fontFamily:'inherit',fontSize:13}}/></td>
                  <td style={{padding:'4px 6px'}}>
                    <select value={m.einheit} onChange={function(e){updMP(m.id,'einheit',e.target.value);}} style={{border:'1px solid var(--border)',borderRadius:4,padding:'4px 6px',fontFamily:'inherit',fontSize:13}}>
                      {['m²','lm','Stk.','kg','l','m³'].map(function(e){return <option key={e} value={e}>{e}</option>;})}
                    </select>
                  </td>
                  <td style={{padding:'4px 6px'}}>
                    <input type="number" value={m.preis} min="0" step="0.5" onChange={function(e){updMP(m.id,'preis',+e.target.value);}} style={{width:80,border:'1px solid var(--border)',borderRadius:4,padding:'4px 6px',fontFamily:'inherit',fontSize:13,textAlign:'right'}}/>
                  </td>
                  <td style={{padding:'4px'}}><button onClick={function(){delMP(m.id);}} style={{background:'none',border:'none',color:'#C0392B',cursor:'pointer',fontSize:14}}>✕</button></td>
                </tr>
              );})}
            </tbody>
          </table>
          <button className="btn-outline btn-sm" onClick={addMP} style={{width:'100%',marginBottom:8}}>+ Material hinzufügen</button>
        </Card>
        <Card style={{marginBottom:16}}>
          <div style={{fontWeight:700,marginBottom:12,fontSize:15}}>⏱ Zeiterfassung (Google Sheet)</div>
          <div style={{fontSize:13,color:'#555',lineHeight:1.7}}>
            Die Zeitdaten werden automatisch über den Cloudflare Worker geladen – kein separates Feld nötig.<br/>
            <strong>Einmalige Einrichtung:</strong> In Cloudflare Workers → kalkulation → Settings → Variables:<br/>
            Variable <code>ZEIT_URL</code> = deine Apps Script Web-App URL (ohne <code>?api=zeiten</code>)
          </div>
        </Card>
        <Card style={{marginBottom:16}}>
          <div style={{fontWeight:700,marginBottom:12,fontSize:15}}>📒 sevDesk</div>
          <Field label="API-Token (direkt)"><input type="password" value={s.sevDeskToken||''} onChange={function(e){upd('sevDeskToken',e.target.value);}} style={{width:'100%'}} placeholder="Dein sevDesk API-Token"/></Field>
          <div style={{fontSize:11,color:'#aaa',marginBottom:12}}>sevDesk → Einstellungen → Benutzer → API-Token · Alternativ: Token leer lassen und <code>SEVDESK_TOKEN</code> als Cloudflare Worker Secret hinterlegen (GoBD-Empfehlung: Token nie im Browser speichern).</div>
          <SevDeskTestButton settings={s}/>
        </Card>
        <Card>
          <div style={{fontWeight:700,marginBottom:10,fontSize:15}}>Export / Import</div>
          <div style={{display:'flex',gap:10}}>
            <button className="btn-dark" onClick={exportData} style={{flex:1,padding:10}}>&#128228; Exportieren</button>
            <label style={{flex:1}}><div className="btn-outline" style={{padding:10,textAlign:'center',cursor:'pointer',borderRadius:6,fontSize:14,border:'1px solid var(--border)'}}>&#128229; Importieren</div><input type="file" accept=".json" onChange={importData} style={{display:'none'}}/></label>
          </div>
        </Card>
      </div>
    </div>
  );
}

// ─── BUDGET & ZEITEN ─────────────────────────────────────────────────────────
// Berechnet geplante Stunden aus Angebots-Positionen
function calcAngebotsStunden(angebot,settings){
  var h={planung:0,werkstatt:0,montage:0,gesamt:0};
  (angebot.positionen||[]).forEach(function(pos){
    if(pos.typ==='planung'){h.planung+=(pos.stunden||0);}
    else if(pos.typ==='moebel'){
      var stdH=pos.stunden!==undefined?pos.stunden:(pos.werkstattStunden||0);
      var lPlan=pos.stunden!==undefined?0:(pos.planungStunden||0);
      if(pos.stundenSatz==='planung') h.planung+=stdH;
      else if(pos.stundenSatz==='montage') h.montage+=stdH;
      else h.werkstatt+=stdH;
      if(lPlan) h.planung+=lPlan;
    } else if(pos.typ==='montage'){
      h.montage+=(pos.stunden||0)*(pos.personen||1);
    }
  });
  h.gesamt=h.planung+h.werkstatt+h.montage;
  return h;
}

function BudgetPage({state,dispatch}){
  var settings=state.settings||{};
  var angebote=(state.angebote||[]).filter(function(a){return !a.storniert;});
  var kunden=state.kunden||[];
  // Zeitdaten leben im App-State → überleben Navigation
  var googleData=state.zeitCache||null;
  var [loading,setLoading]=useState(false);
  var [fehler,setFehler]=useState(null);
  var kalkulationen=state.kalkulationen||[];
  var interneProjekte=state.interneProjekte||[];
  var [internOffen,setInternOffen]=useState(false);
  // linkMap direkt aus State ableiten – kein lokaler useState, damit Cloud-Sync greift
  var linkMap={};
  angebote.forEach(function(a){if(a.googleProjektId) linkMap[a.googleProjektId]={typ:'angebot',id:a.id};});
  kalkulationen.forEach(function(k){if(k.googleProjektId) linkMap[k.googleProjektId]={typ:'kalkulation',id:k.id};});
  var [linkEdit,setLinkEdit]=useState(null);

  function ladeDaten(){
    if(!settings.workerUrl||!settings.workerSecret){setFehler('Bitte erst Worker-URL und Passwort in den Einstellungen (Cloud-Sync) konfigurieren.');return;}
    setLoading(true);setFehler(null);
    fetch(settings.workerUrl+'/zeiten',{headers:{'X-Secret':settings.workerSecret}})
      .then(function(r){return r.json();})
      .then(function(d){
        if(d.error) throw new Error(d.error);
        dispatch({type:'SAVE_ZEITDATEN',payload:d});
        setLoading(false);
      })
      .catch(function(e){setFehler('Fehler: '+e.message);setLoading(false);});
  }

  // Speichert Verknüpfung: typ = 'angebot' | 'kalkulation', itemId = id des Eintrags
  function speichereLink(googleProjektId,typ,itemId){
    // alte Verknüpfung entfernen
    var old=linkMap[googleProjektId];
    if(old){
      if(old.typ==='angebot'){
        var oa=angebote.find(function(a){return a.id===old.id;});
        if(oa) dispatch({type:'SAVE_ANGEBOT',payload:Object.assign({},oa,{googleProjektId:null,_ts:Date.now()})});
      } else {
        var ok=kalkulationen.find(function(k){return k.id===old.id;});
        if(ok) dispatch({type:'SAVE_KALKULATION',payload:Object.assign({},ok,{googleProjektId:null,_ts:Date.now()})});
      }
    }
    // neue Verknüpfung setzen – _ts aktualisieren damit Cloud-Merge korrekt entscheidet
    if(typ&&itemId){
      if(typ==='angebot'){
        var na=angebote.find(function(a){return a.id===itemId;});
        if(na) dispatch({type:'SAVE_ANGEBOT',payload:Object.assign({},na,{googleProjektId:googleProjektId,_ts:Date.now()})});
      } else {
        var nk=kalkulationen.find(function(k){return k.id===itemId;});
        if(nk) dispatch({type:'SAVE_KALKULATION',payload:Object.assign({},nk,{googleProjektId:googleProjektId,_ts:Date.now()})});
      }
    }
    setLinkEdit(null);
  }

  // Verknüpfte IDs sammeln für "ohne Zeiterfassung" Liste
  var verknuepfteAngIds=Object.values(linkMap).filter(function(l){return l.typ==='angebot';}).map(function(l){return l.id;});
  var verknuepfteKalkIds=Object.values(linkMap).filter(function(l){return l.typ==='kalkulation';}).map(function(l){return l.id;});
  var offeneAngebote=angebote.filter(function(a){
    return (a.status==='Versendet'||a.status==='Freigegeben')&&!verknuepfteAngIds.includes(a.id);
  });
  var offeneKalks=kalkulationen.filter(function(k){return !verknuepfteKalkIds.includes(k.id);});

  function fmtH(h){return h.toFixed(1)+' h';}
  function fmtEuro(v){return v.toLocaleString('de-DE',{minimumFractionDigits:0,maximumFractionDigits:0})+' €';}

  // Erkennt Stundensatz-Typ anhand Task-Name (Keyword-Matching)
  function detectRateType(name){
    var n=(name||'').toLowerCase();
    if(/planung|entwurf|konstruktion|konzept|kommunikation|briefing|design/.test(n)) return 'planung';
    if(/werkstatt|produktion|fertigung|herstellung/.test(n)) return 'werkstatt';
    if(/montage|einbau|aufbau|installation/.test(n)) return 'montage';
    return null;
  }

  // Aggregiert Tasks eines Projekts nach Rate-Typ
  function aggregateByRate(tasks,s){
    var acc={planung:{h:0,tasks:[]},werkstatt:{h:0,tasks:[]},montage:{h:0,tasks:[]},sonstige:{h:0,tasks:[]}};
    (tasks||[]).forEach(function(t){
      var typ=detectRateType(t.name);
      var key=typ||'sonstige';
      acc[key].h+=t.gesamtH||0;
      if(t.gesamtH>0) acc[key].tasks.push(t.name);
    });
    return acc;
  }

  // Kein Auto-Fetch – Daten bleiben im State erhalten, nur auf Knopfdruck aktualisieren

  return(
    <div className="sk-page" style={{flex:1,overflow:'auto'}}>
      <div style={{display:'flex',alignItems:'center',justifyContent:'space-between',marginBottom:20,flexWrap:'wrap',gap:10}}>
        <h1 style={{fontSize:22,fontWeight:700,margin:0}}>⏱ Budget & Zeiten</h1>
        <div style={{display:'flex',gap:8,alignItems:'center'}}>
          {googleData&&<span style={{fontSize:11,color:'#999'}}>Stand: {new Date(googleData.ts).toLocaleString('de-DE')}</span>}
          <button className="btn-outline btn-sm" onClick={ladeDaten} disabled={loading}>{loading?'Lädt…':'↻ Aktualisieren'}</button>
        </div>
      </div>

      {(!settings.workerUrl||!settings.workerSecret)&&(
        <Card style={{marginBottom:20,borderLeft:'3px solid #E67E22',background:'#FFFBF5'}}>
          <div style={{fontWeight:600,marginBottom:6}}>Cloud-Sync nicht konfiguriert</div>
          <div style={{fontSize:13,color:'#666',lineHeight:1.6}}>
            Bitte erst <strong>Einstellungen → Cloud-Sync</strong> (Worker-URL und Passwort) einrichten.<br/>
            Außerdem muss <code>ZEIT_URL</code> als Variable im Cloudflare Worker hinterlegt sein.
          </div>
        </Card>
      )}
      {fehler&&<Card style={{marginBottom:16,borderLeft:'3px solid #C0392B',background:'#FEF9F9'}}><div style={{color:'#C0392B',fontSize:13}}>{fehler}</div></Card>}

      {/* Projekte mit Zeitdaten – nur externe (nicht intern markierte) */}
      {googleData&&(googleData.projekte||[]).filter(function(p){return p.id!=='__unbekannt__'&&interneProjekte.indexOf(p.id)<0;}).map(function(gp){
        var link=linkMap[gp.id];
        var ang=link&&link.typ==='angebot'?angebote.find(function(a){return a.id===link.id;}):null;
        var kalk=link&&link.typ==='kalkulation'?kalkulationen.find(function(k){return k.id===link.id;}):null;
        var linked=ang||kalk;
        var kunde=linked?kunden.find(function(k){return k.id===linked.kundeId;}):null;
        var geplant=linked?calcAngebotsStunden(linked,settings):{gesamt:0,planung:0,werkstatt:0,montage:0};
        var istH=gp.gesamtH||0;
        var sollH=geplant.gesamt;
        var pct=sollH>0?Math.min(istH/sollH,1.05):0;
        var barColor=pct<0.75?'#27AE60':(pct<0.95?'#E67E22':'#C0392B');
        var restH=sollH-istH;
        var isEdit=linkEdit===gp.id;
        var linkLabel=ang?(fmtAngebotNr(ang.angebotNr)+' (Angebot)'):kalk?('Kalkulation #'+kalk.nr):null;

        return(
          <Card key={gp.id} style={{marginBottom:14}}>
            <div style={{flex:1,minWidth:0}}>
              {/* Kopfzeile */}
              <div style={{display:'flex',alignItems:'center',gap:8,marginBottom:4,flexWrap:'wrap'}}>
                <div style={{fontWeight:700,fontSize:15}}>{gp.name}</div>
                {ang&&<span style={{fontSize:11,background:'#E8F0FE',color:'#3B6FE0',borderRadius:4,padding:'2px 7px'}}>Angebot</span>}
                {kalk&&<span style={{fontSize:11,background:'#F0F4FF',color:'#555',borderRadius:4,padding:'2px 7px'}}>Kalkulation</span>}
                {!linked&&<span style={{fontSize:11,color:'#aaa',fontStyle:'italic'}}>Nicht verknüpft</span>}
              </div>
              {linked&&<div style={{fontSize:12,color:'#888',marginBottom:10}}>{linkLabel} · {kunde?kunde.name:'–'} · {linked.titel||'–'}</div>}

              {/* Zahlen */}
              <div style={{display:'flex',gap:20,marginBottom:8,flexWrap:'wrap'}}>
                <div>
                  <div style={{fontSize:11,color:'#888',textTransform:'uppercase',letterSpacing:.5}}>Getracked (Ist)</div>
                  <div style={{fontWeight:700,fontSize:20}}>{fmtH(istH)}</div>
                  <div style={{fontSize:11,color:'#999',marginTop:1}}>
                    {Object.keys(gp.benutzer||{}).map(function(u){return u+': '+fmtH(gp.benutzer[u]);}).join(' · ')}
                  </div>
                </div>
                {sollH>0&&(
                  <div>
                    <div style={{fontSize:11,color:'#888',textTransform:'uppercase',letterSpacing:.5}}>Kalk. (Soll)</div>
                    <div style={{fontWeight:700,fontSize:20}}>{fmtH(sollH)}</div>
                    <div style={{fontSize:11,color:'#999',marginTop:1}}>
                      {geplant.planung>0?'Plan: '+fmtH(geplant.planung)+' ':''}
                      {geplant.werkstatt>0?'WS: '+fmtH(geplant.werkstatt)+' ':''}
                      {geplant.montage>0?'Mont: '+fmtH(geplant.montage):''}
                    </div>
                  </div>
                )}
                {sollH>0&&(
                  <div>
                    <div style={{fontSize:11,color:'#888',textTransform:'uppercase',letterSpacing:.5}}>{restH>=0?'Verbleibend':'Überzogen'}</div>
                    <div style={{fontWeight:700,fontSize:20,color:restH>=0?(pct<0.75?'#27AE60':'#E67E22'):'#C0392B'}}>{fmtH(Math.abs(restH))}</div>
                    <div style={{fontSize:11,color:'#999',marginTop:1}}>{Math.round(pct*100)} % verbraucht</div>
                  </div>
                )}
              </div>

              {/* Progress-Bar */}
              {sollH>0&&(
                <div style={{background:'#F0F0F0',borderRadius:4,height:8,overflow:'hidden',marginBottom:10}}>
                  <div style={{width:Math.min(pct*100,100)+'%',height:'100%',background:barColor,borderRadius:4,transition:'width .5s'}}/>
                </div>
              )}

              {/* Task-Aufschlüsselung nach Stundensatz – nur wenn API tasks liefert */}
              {(gp.tasks&&gp.tasks.length>0)&&(function(){
                var byRate=aggregateByRate(gp.tasks,settings);
                var raten=[
                  {key:'planung',label:'Planung',rate:settings.planung||80,color:'#3B6FE0'},
                  {key:'werkstatt',label:'Werkstatt',rate:settings.werkstatt||55,color:'#7B5EA7'},
                  {key:'montage',label:'Montage',rate:settings.montage||55,color:'#27AE60'},
                  {key:'sonstige',label:'Sonstige',rate:null,color:'#aaa'},
                ];
                var gesamtKosten=raten.reduce(function(t,r){return t+(r.rate?byRate[r.key].h*r.rate:0);},0);
                return(
                  <div style={{background:'#FAFAFA',borderRadius:6,padding:'10px 12px',marginBottom:10,fontSize:12}}>
                    <div style={{fontWeight:600,color:'#666',marginBottom:8,fontSize:11,textTransform:'uppercase',letterSpacing:.5}}>Getracked nach Stundensatz</div>
                    {raten.map(function(r){
                      var acc=byRate[r.key];
                      if(acc.h===0) return null;
                      return(
                        <div key={r.key} style={{display:'flex',alignItems:'baseline',gap:8,marginBottom:4,flexWrap:'wrap'}}>
                          <span style={{color:r.color,fontWeight:600,minWidth:68}}>{r.label}</span>
                          <span style={{color:'#555',fontWeight:600}}>{fmtH(acc.h)}</span>
                          {r.rate&&<span style={{color:'#999'}}>@ {r.rate} €/h</span>}
                          {r.rate&&<span style={{color:'#444',fontWeight:600}}>{fmtEuro(acc.h*r.rate)}</span>}
                          {acc.tasks.length>0&&<span style={{color:'#bbb',fontSize:10}}>({acc.tasks.join(', ')})</span>}
                        </div>
                      );
                    })}
                    {gesamtKosten>0&&(
                      <div style={{borderTop:'1px solid #eee',marginTop:6,paddingTop:6,display:'flex',justifyContent:'space-between'}}>
                        <span style={{color:'#555',fontWeight:600}}>Gesamt getracked</span>
                        <span style={{fontWeight:700,color:'#1E1E1E'}}>{fmtH(istH)} · {fmtEuro(gesamtKosten)}</span>
                      </div>
                    )}
                  </div>
                );
              })()}

              {/* Link-Editor */}
              {isEdit?(
                <div style={{display:'flex',gap:8,alignItems:'flex-start',marginTop:6,flexWrap:'wrap'}}>
                  <select style={{flex:1,minWidth:180,padding:'6px 8px',borderRadius:6,border:'1px solid var(--border)',fontSize:13,fontFamily:'inherit'}}
                    defaultValue={link?(link.typ+':'+link.id):''}
                    onChange={function(e){
                      var val=e.target.value;
                      if(!val){speichereLink(gp.id,null,null);}
                      else{var parts=val.split(':');speichereLink(gp.id,parts[0],parts[1]);}
                    }}>
                    <option value=''>– Keine Verknüpfung –</option>
                    <optgroup label='── Kalkulationen ──'>
                      {kalkulationen.map(function(k){
                        var kd=kunden.find(function(x){return x.id===k.kundeId;});
                        return <option key={k.id} value={'kalkulation:'+k.id}>#{k.nr} · {kd?kd.name:'?'} · {k.titel||'–'}</option>;
                      })}
                    </optgroup>
                    <optgroup label='── Angebote ──'>
                      {angebote.map(function(a){
                        var kd=kunden.find(function(x){return x.id===a.kundeId;});
                        return <option key={a.id} value={'angebot:'+a.id}>{fmtAngebotNr(a.angebotNr)} · {kd?kd.name:'?'} · {a.titel||'–'}</option>;
                      })}
                    </optgroup>
                  </select>
                  <button className="btn-outline btn-sm" onClick={function(){setLinkEdit(null);}}>✕</button>
                </div>
              ):(
                <div style={{display:'flex',gap:8,marginTop:6,flexWrap:'wrap'}}>
                  <button className="btn-outline btn-sm" onClick={function(){setLinkEdit(gp.id);}}>
                    {linked?'🔗 Verknüpfung ändern':'🔗 Verknüpfen'}
                  </button>
                  <button className="btn-outline btn-sm" style={{color:'#aaa',fontSize:11}} onClick={function(){dispatch({type:'SET_INTERN_PROJEKT',payload:{id:gp.id,intern:true}});}}>
                    Als intern ausblenden
                  </button>
                </div>
              )}
            </div>
          </Card>
        );
      })}

      {/* Nicht zugeordnete Zeiten */}
      {googleData&&(googleData.projekte||[]).filter(function(p){return p.id==='__unbekannt__';}).map(function(gp){
        return(
          <Card key='__unbekannt__' style={{marginBottom:14,borderLeft:'3px solid #E67E22',opacity:.8}}>
            <div style={{fontWeight:600,fontSize:13,color:'#E67E22',marginBottom:4}}>⚠ Nicht zugeordnete Zeiten</div>
            <div style={{fontSize:13,color:'#666'}}>{fmtH(gp.gesamtH)} – Zeiteinträge ohne Projektzuordnung im Google Sheet</div>
          </Card>
        );
      })}

      {/* Interne Projekte – kollabierbar, klein dargestellt */}
      {googleData&&interneProjekte.length>0&&(function(){
        var internListe=(googleData.projekte||[]).filter(function(p){return interneProjekte.indexOf(p.id)>=0;});
        if(internListe.length===0) return null;
        var gesamtInternH=internListe.reduce(function(t,p){return t+(p.gesamtH||0);},0);
        return(
          <div style={{marginTop:20}}>
            <button onClick={function(){setInternOffen(function(o){return !o;});}} style={{
              display:'flex',alignItems:'center',gap:8,background:'none',border:'none',
              cursor:'pointer',fontFamily:'inherit',padding:'8px 0',width:'100%',textAlign:'left'
            }}>
              <span style={{fontSize:12,fontWeight:600,color:'#aaa',textTransform:'uppercase',letterSpacing:.5}}>Interne Projekte</span>
              <span style={{fontSize:11,color:'#bbb',marginLeft:4}}>{fmtH(gesamtInternH)} gesamt</span>
              <span style={{color:'#bbb',marginLeft:'auto',fontSize:13}}>{internOffen?'▲':'▼'}</span>
            </button>
            {internOffen&&internListe.map(function(gp){
              var hasTasks=gp.tasks&&gp.tasks.length>0;
              return(
                <div key={gp.id} style={{background:'#fafafa',border:'1px solid #eee',borderRadius:8,marginBottom:6,overflow:'hidden'}}>
                  {/* Kopfzeile */}
                  <div style={{display:'flex',alignItems:'center',gap:12,padding:'10px 14px'}}>
                    <div style={{flex:1}}>
                      <span style={{fontSize:13,color:'#666',fontWeight:600}}>{gp.name}</span>
                      <span style={{fontSize:11,color:'#bbb',marginLeft:10}}>
                        {Object.keys(gp.benutzer||{}).map(function(u){return u+': '+fmtH(gp.benutzer[u]);}).join(' · ')}
                      </span>
                    </div>
                    <span style={{fontWeight:700,fontSize:14,color:'#888'}}>{fmtH(gp.gesamtH)}</span>
                    <button style={{background:'none',border:'none',cursor:'pointer',fontSize:11,color:'#bbb',fontFamily:'inherit',padding:'2px 6px',flexShrink:0}}
                      onClick={function(){dispatch({type:'SET_INTERN_PROJEKT',payload:{id:gp.id,intern:false}});}}>
                      ↩
                    </button>
                  </div>
                  {/* Task-Aufschlüsselung */}
                  {hasTasks&&(
                    <div style={{borderTop:'1px solid #eee',padding:'8px 14px 10px'}}>
                      {(gp.tasks||[]).map(function(t){
                        return(
                          <div key={t.id} style={{display:'flex',alignItems:'center',gap:8,padding:'3px 0',borderBottom:'1px solid #f5f5f5'}}>
                            <span style={{flex:1,fontSize:12,color:'#777'}}>{t.name}</span>
                            <span style={{fontSize:11,color:'#aaa'}}>
                              {Object.keys(t.benutzer||{}).map(function(u){return u+': '+fmtH(t.benutzer[u]);}).join(' · ')}
                            </span>
                            <span style={{fontSize:12,fontWeight:600,color:'#999',minWidth:40,textAlign:'right'}}>{fmtH(t.gesamtH)}</span>
                          </div>
                        );
                      })}
                    </div>
                  )}
                </div>
              );
            })}
          </div>
        );
      })()}

      {/* Kalkulationen & Angebote ohne Zeitverknüpfung */}
      {(offeneKalks.length>0||offeneAngebote.length>0)&&(
        <div style={{marginTop:28}}>
          <div style={{fontSize:12,fontWeight:600,color:'#888',textTransform:'uppercase',letterSpacing:.5,marginBottom:10}}>Ohne Zeiterfassung</div>
          {offeneKalks.map(function(k){
            var kd=kunden.find(function(x){return x.id===k.kundeId;});
            var h=calcAngebotsStunden(k,settings);
            return(
              <div key={k.id} style={{display:'flex',alignItems:'center',gap:12,padding:'10px 14px',background:'#fff',border:'1px solid var(--border)',borderRadius:8,marginBottom:6}}>
                <span style={{fontSize:11,background:'#F5F5F5',color:'#888',borderRadius:4,padding:'2px 6px',flexShrink:0}}>Kalk.</span>
                <div style={{flex:1,fontSize:13}}>
                  <span style={{fontWeight:600}}>#{k.nr}</span>
                  <span style={{color:'#ccc',margin:'0 6px'}}>·</span>
                  {kd?kd.name:'–'}
                  <span style={{color:'#ccc',margin:'0 6px'}}>·</span>
                  {k.titel||'–'}
                </div>
                {h.gesamt>0&&<span style={{fontSize:12,color:'#bbb',flexShrink:0}}>{fmtH(h.gesamt)} kalk.</span>}
              </div>
            );
          })}
          {offeneAngebote.map(function(a){
            var kd=kunden.find(function(x){return x.id===a.kundeId;});
            var h=calcAngebotsStunden(a,settings);
            return(
              <div key={a.id} style={{display:'flex',alignItems:'center',gap:12,padding:'10px 14px',background:'#fff',border:'1px solid var(--border)',borderRadius:8,marginBottom:6}}>
                <span style={{fontSize:11,background:'#E8F0FE',color:'#3B6FE0',borderRadius:4,padding:'2px 6px',flexShrink:0}}>Angebot</span>
                <div style={{flex:1,fontSize:13}}>
                  <span style={{fontWeight:600}}>{fmtAngebotNr(a.angebotNr)}</span>
                  <span style={{color:'#ccc',margin:'0 6px'}}>·</span>
                  {kd?kd.name:'–'}
                  <span style={{color:'#ccc',margin:'0 6px'}}>·</span>
                  {a.titel||'–'}
                </div>
                {h.gesamt>0&&<span style={{fontSize:12,color:'#bbb',flexShrink:0}}>{fmtH(h.gesamt)} kalk.</span>}
              </div>
            );
          })}
        </div>
      )}

      {!googleData&&!loading&&(
        <div style={{textAlign:'center',padding:'60px 20px'}}>
          <div style={{fontSize:32,marginBottom:12}}>⏱</div>
          <div style={{color:'#888',fontSize:15,marginBottom:16}}>Noch keine Zeitdaten geladen.</div>
          <button className="btn-dark" onClick={ladeDaten} style={{padding:'10px 24px'}}>Jetzt laden</button>
        </div>
      )}
    </div>
  );
}

// ─── REDUCER ─────────────────────────────────────────────────────────────────
function reducer(state,action){
  switch(action.type){
    case 'SAVE_ZEITDATEN':return Object.assign({},state,{zeitCache:action.payload});
    case 'SET_INTERN_PROJEKT':{
      var list=(state.interneProjekte||[]).slice();
      if(action.payload.intern){if(list.indexOf(action.payload.id)<0) list.push(action.payload.id);}
      else list=list.filter(function(x){return x!==action.payload.id;});
      return Object.assign({},state,{interneProjekte:list});
    }
    case 'SAVE_KUNDE':{
      var ex=state.kunden.find(function(k){return k.id===action.payload.id;});
      return Object.assign({},state,{kunden:ex?state.kunden.map(function(k){return k.id===action.payload.id?action.payload:k;}):[].concat(state.kunden,[action.payload])});
    }
    case 'DEL_KUNDE':return Object.assign({},state,{kunden:state.kunden.filter(function(k){return k.id!==action.payload;}),_deletedKunden:[].concat(state._deletedKunden||[],[action.payload])});
    case 'SAVE_KALKULATION':{
      var kalks=state.kalkulationen||[];
      var ex=kalks.find(function(k){return k.id===action.payload.id;});
      var ns=Object.assign({},state,{kalkulationen:ex?kalks.map(function(k){return k.id===action.payload.id?action.payload:k;}):[].concat(kalks,[action.payload])});
      if(!ex) ns.nextKalkulationNr=(state.nextKalkulationNr||1)+1;
      return ns;
    }
    case 'DEL_KALKULATION':return Object.assign({},state,{kalkulationen:(state.kalkulationen||[]).filter(function(k){return k.id!==action.payload;}),_deletedKalkulationen:[].concat(state._deletedKalkulationen||[],[action.payload])});
    case 'SAVE_ANGEBOT':{
      var angs=state.angebote||[];
      var ex=angs.find(function(a){return a.id===action.payload.id;});
      var ns=Object.assign({},state,{angebote:ex?angs.map(function(a){return a.id===action.payload.id?action.payload:a;}):[].concat(angs,[action.payload])});
      // Nummer wird NICHT beim Erstellen vergeben – erst beim Versenden
      return ns;
    }
    case 'SET_ANGEBOT_STATUS':{
      var angs=state.angebote||[];
      var ns=Object.assign({},state);
      // Selbstheilender Zähler: immer Maximum aus Counter, tatsächlich vergeben Nummern, Mindestwert 1000
      var maxUsedNr=angs.reduce(function(m,a){return Math.max(m,parseInt(a.angebotNr)||0);},0);
      var nextNr=Math.max(state.nextAngebotNr||0,maxUsedNr+1,1000);
      ns.angebote=angs.map(function(a){
        if(a.id!==action.payload.id) return a;
        var updated=Object.assign({},a,{status:action.payload.status,_ts:Date.now()});
        // Nummer erst vergeben wenn Versendet und noch keine Nummer vorhanden
        if(action.payload.status==='Versendet'&&!a.angebotNr){
          updated.angebotNr=nextNr;
          ns.nextAngebotNr=nextNr+1;
        }
        return updated;
      });
      return ns;
    }
    case 'DEL_ANGEBOT':{
      var newAngs=(state.angebote||[]).filter(function(a){return a.id!==action.payload;});
      var maxNr=newAngs.reduce(function(m,a){return Math.max(m,parseInt(a.angebotNr)||0);},0);
      return Object.assign({},state,{angebote:newAngs,nextAngebotNr:Math.max(state.nextAngebotNr||0,maxNr+1,1000),_deletedAngebote:[].concat(state._deletedAngebote||[],[action.payload])});
    }
    case 'UEBERARBEITEN_ANGEBOT':{
      var angs=state.angebote||[];
      var orig=angs.find(function(a){return a.id===action.payload;});
      if(!orig) return state;
      var newId='ang_'+Date.now()+'_'+Math.random().toString(36).slice(2,7);
      var kopie=Object.assign({},orig,{
        id:newId,
        angebotNr:null,
        status:'Entwurf',
        ersetztAngebotId:orig.id,
        _ts:Date.now()
      });
      var updatedAngs=angs.map(function(a){
        if(a.id!==action.payload) return a;
        return Object.assign({},a,{status:'Überarbeitet',ersetztDurch:newId,_ts:Date.now()});
      });
      updatedAngs=[].concat(updatedAngs,[kopie]);
      return Object.assign({},state,{angebote:updatedAngs});
    }
    case 'SAVE_ANZAHLUNG':{
      var list=state.anzahlungen||[];
      var ex=list.find(function(r){return r.id===action.payload.id;});
      var ns=Object.assign({},state,{anzahlungen:ex?list.map(function(r){return r.id===action.payload.id?action.payload:r;}):[].concat(list,[action.payload])});
      if(!ex) ns.nextANr=(state.nextANr||1)+1;
      return ns;
    }
    case 'DEL_ANZAHLUNG':return Object.assign({},state,{anzahlungen:(state.anzahlungen||[]).filter(function(r){return r.id!==action.payload;}),_deletedAnzahlungen:[].concat(state._deletedAnzahlungen||[],[action.payload])});
    case 'SAVE_SCHLUSS':{
      var list=state.schlussrechnungen||[];
      var ex=list.find(function(r){return r.id===action.payload.id;});
      var ns=Object.assign({},state,{schlussrechnungen:ex?list.map(function(r){return r.id===action.payload.id?action.payload:r;}):[].concat(list,[action.payload])});
      // Nummernkreis nur erhöhen wenn keine Entwurf-Speicherung (Nummer wird erst bei Freigabe vergeben)
      if(!ex && action.payload.status!=='Entwurf') ns.nextSNr=(state.nextSNr||1)+1;
      return ns;
    }
    case 'FREIGABE_SCHLUSS':{
      // Entwurf freigeben: Rechnungsnummer vergeben und Status auf 'Freigegeben' setzen
      var list=state.schlussrechnungen||[];
      // Selbstheilender Zähler: Maximum aus gespeichertem Counter UND tatsächlich vergebenen Nummern
      var maxUsedSNr=list.reduce(function(m,r){return Math.max(m,parseInt(r.rechnungNr||'0',10)||0);},0);
      var nextSNrVal=Math.max(state.nextSNr||1002,maxUsedSNr+1);
      var rNr=String(nextSNrVal).padStart(4,'0');
      var updated=list.map(function(r){
        if(r.id!==action.payload) return r;
        return Object.assign({},r,{rechnungNr:rNr,status:'Freigegeben',_ts:Date.now()});
      });
      return Object.assign({},state,{schlussrechnungen:updated,nextSNr:nextSNrVal+1});
    }
    case 'DEL_SCHLUSS':{
      // Entwurf: einfach löschen ohne _deletedSchluss (keine GoBD-Relevanz)
      var delItem=(state.schlussrechnungen||[]).find(function(r){return r.id===action.payload;});
      var isEntwurf=delItem&&delItem.status==='Entwurf';
      var newList=(state.schlussrechnungen||[]).filter(function(r){return r.id!==action.payload;});
      if(isEntwurf) return Object.assign({},state,{schlussrechnungen:newList});
      return Object.assign({},state,{schlussrechnungen:newList,_deletedSchluss:[].concat(state._deletedSchluss||[],[action.payload])});
    }
    case 'SAVE_PRIVAT':{
      var list=state.privatAusgaben||[];
      var ex=list.find(function(r){return r.id===action.payload.id;});
      return Object.assign({},state,{privatAusgaben:ex?list.map(function(r){return r.id===action.payload.id?action.payload:r;}):[].concat(list,[action.payload])});
    }
    case 'DEL_PRIVAT':return Object.assign({},state,{privatAusgaben:(state.privatAusgaben||[]).filter(function(r){return r.id!==action.payload;}),_deletedPrivat:[].concat(state._deletedPrivat||[],[action.payload])});
    case 'SAVE_MATERIALPREISE':return Object.assign({},state,{materialpreise:action.payload});
    case 'SAVE_REISE':{
      var list=state.reisekostenberichte||[];
      var ex=list.find(function(r){return r.id===action.payload.id;});
      var ns=Object.assign({},state,{reisekostenberichte:ex?list.map(function(r){return r.id===action.payload.id?action.payload:r;}):[].concat(list,[action.payload])});
      if(!ex) ns.nextReiseNr=(state.nextReiseNr||1)+1;
      return ns;
    }
    case 'DEL_REISE':return Object.assign({},state,{reisekostenberichte:(state.reisekostenberichte||[]).filter(function(r){return r.id!==action.payload;}),_deletedReise:[].concat(state._deletedReise||[],[action.payload])});
    case 'SAVE_SETTINGS':return Object.assign({},state,{settings:action.payload});
    case 'IMPORT':return Object.assign({},DEFAULT,action.payload);
    // ── GoBD: Lock, Storno, Duplicate ────────────────────────────────────────
    case 'LOCK_RECORD':{
      var col=action.payload.collection;
      var auditAction=action.payload.action||'Exportiert nach sevDesk';
      var upd=(state[col]||[]).map(function(r){
        if(r.id!==action.payload.id) return r;
        var log=[].concat(r.auditLog||[],[{ts:Date.now(),action:auditAction}]);
        return Object.assign({},r,{locked:true,auditLog:log,_ts:Date.now()});
      });
      var patch={};patch[col]=upd;return Object.assign({},state,patch);
    }
    case 'STORNO_RECORD':{
      var col=action.payload.collection;
      var grund=action.payload.grund||'';
      var upd=(state[col]||[]).map(function(r){
        if(r.id!==action.payload.id) return r;
        var logEntry={ts:Date.now(),action:'Storniert'};
        if(grund) logEntry.grund=grund;
        var log=[].concat(r.auditLog||[],[logEntry]);
        return Object.assign({},r,{storniert:true,locked:true,stornoGrund:grund||r.stornoGrund,auditLog:log,_ts:Date.now()});
      });
      var patch={};patch[col]=upd;return Object.assign({},state,patch);
    }
    case 'DUPLICATE_RECORD':{
      var col=action.payload.collection;
      var orig=(state[col]||[]).find(function(r){return r.id===action.payload.id;});
      if(!orig) return state;
      var dup=Object.assign({},orig,{
        id:uid(),_ts:Date.now(),
        locked:undefined,storniert:undefined,
        sevdeskLinked:undefined,sevdeskUrl:undefined,
        auditLog:[{ts:Date.now(),action:'Kopie erstellt von '+action.payload.id}]
      });
      var ns=Object.assign({},state);
      if(col==='angebote'){dup.angebotNr=null;dup.status='Entwurf';}
      else if(col==='anzahlungen'){var na=state.nextANr||1;dup.rechnungNr=String(na).padStart(4,'0');ns.nextANr=na+1;}
      else if(col==='schlussrechnungen'){var ns2=state.nextSNr||1;dup.rechnungNr=String(ns2).padStart(4,'0');ns.nextSNr=ns2+1;}
      else if(col==='reisekostenberichte'){var nr2=state.nextReiseNr||1;dup.nr=nr2;ns.nextReiseNr=nr2+1;}
      var colPatch={};colPatch[col]=[].concat(state[col]||[],[dup]);
      return Object.assign({},ns,colPatch);
    }
    default:return state;
  }
}

// ─── APP ─────────────────────────────────────────────────────────────────────
function App(){
  var saved=loadLS();
  var init=saved?Object.assign({},DEFAULT,saved):DEFAULT;
  // Fehlende neue Felder ergänzen (Migration)
  // Migration: auftraege → kalkulationen
  // Auch wenn kalkulationen:[] bereits gespeichert wurde aber auftraege vorhanden sind
  if(saved && saved.auftraege && saved.auftraege.length>0 && (!saved.kalkulationen||saved.kalkulationen.length===0)){
    init.kalkulationen=saved.auftraege;
  }
  if(!init.angebote) init.angebote=[];
  if(!init.anzahlungen) init.anzahlungen=[];
  if(!init.schlussrechnungen) init.schlussrechnungen=[];
  // Zähler immer aus tatsächlichen Daten neu berechnen (self-healing)
  var maxKalkNr=(init.kalkulationen||[]).reduce(function(m,k){return Math.max(m,k.nr||0);},0);
  init.nextKalkulationNr=maxKalkNr+1;
  var maxAngNr=(init.angebote||[]).reduce(function(m,a){return Math.max(m,a.angebotNr||0);},0);
  init.nextAngebotNr=maxAngNr+1;
  var maxANr=(init.anzahlungen||[]).reduce(function(m,r){return Math.max(m,parseInt(r.rechnungNr||'0',10)||0);},0);
  init.nextANr=maxANr+1;
  var maxSNr=(init.schlussrechnungen||[]).reduce(function(m,r){return Math.max(m,parseInt(r.rechnungNr||'0',10)||0);},0);
  init.nextSNr=maxSNr+1;
  // Nummernkreise sevDesk: Angebote 1000+ (AN-26-XXXX), Schluss 1002+ (RE-26-XXXX), Anzahlung 2001+ (RE-26-XXXX)
  if(init.nextAngebotNr<1000) init.nextAngebotNr=1000;
  if(init.nextSNr<1002) init.nextSNr=1002;
  if(init.nextANr<2001) init.nextANr=2001;

  // Initialize reisekostenberichte if missing
  if(!init.reisekostenberichte) init.reisekostenberichte=[];
  if(!init.nextReiseNr) init.nextReiseNr=1;

  var [state,dispatchBase]=useState(init);
  var [syncStatus,setSyncStatus]=useState('idle');
  var isMounted=useRef(true);
  var stateRef=useRef(state);
  stateRef.current=state;

  useEffect(function(){isMounted.current=true;return function(){isMounted.current=false;};},[]);

  var syncNow=useCallback(function(){
    var cfg=stateRef.current.settings;
    if(!cfg.workerUrl||!cfg.workerSecret) return;
    setSyncStatus('syncing');
    cloudLoad(cfg).then(function(remote){
      if(!isMounted.current) return;
      var local=stateRef.current;
      var merged=remote?mergeStates(local,remote):local;
      dispatchBase(merged);saveLS(merged);
      cloudSave(cfg,merged).then(function(ok){if(isMounted.current) setSyncStatus(ok?'ok':'error');});
    }).catch(function(){if(isMounted.current) setSyncStatus('error');});
  },[]);

  var syncTimerRef=useRef(null);

  // Aktionen die KEINEN Cloud-Sync auslösen (nur lokaler Cache / UI-State)
  var NO_SYNC_ACTIONS=['SAVE_ZEITDATEN'];

  var dispatch=useCallback(function(action){
    dispatchBase(function(prev){
      var next=reducer(prev,action);
      next=Object.assign({},next,{_ts:Date.now()});
      saveLS(next);return next;
    });
    // Kein Auto-Sync für reine Cache-Aktionen
    if(NO_SYNC_ACTIONS.indexOf(action.type)>=0) return;
    // Debounced auto-sync: 30 Sekunden nach letzter echter Änderung
    if(syncTimerRef.current) clearTimeout(syncTimerRef.current);
    syncTimerRef.current=setTimeout(function(){syncNow();},30000);
  },[syncNow]);

  // Startup-Sync: beim ersten Laden einmalig von der Cloud holen (damit Handy Desktop-Daten bekommt)
  var startupSyncDone=useRef(false);
  useEffect(function(){
    if(startupSyncDone.current) return;
    var cfg=stateRef.current.settings;
    if(!cfg.workerUrl||!cfg.workerSecret) return;
    startupSyncDone.current=true;
    syncNow();
  },[syncNow]);

  // Periodischer Auto-Sync alle 5 Minuten (sparsam mit Cloudflare-Tokens)
  useEffect(function(){
    var cfg=stateRef.current.settings;
    if(!cfg.workerUrl||!cfg.workerSecret) return;
    var iv=setInterval(function(){syncNow();},300000);
    return function(){clearInterval(iv);};
  },[syncNow,state.settings.workerUrl,state.settings.workerSecret]);

  var [page,setPage]=useState('dashboard');
  var [wizard,setWizard]=useState(null);

  return(
    <div style={{display:'flex',minHeight:'100vh'}}>
      <Sidebar page={page} setPage={setPage} syncStatus={syncStatus} syncNow={syncNow}/>
      <div style={{flex:1,display:'flex',flexDirection:'column',overflow:'auto'}}>
        {page==='dashboard'&&<Dashboard state={state} setPage={setPage}/>}
        {page==='budget'&&<BudgetPage state={state} dispatch={dispatch}/>}
        {page==='kunden'&&<KundenPage state={state} dispatch={dispatch}/>}
        {page==='kalkulation'&&<KalkulationPage state={state} dispatch={dispatch} startNeu={function(){setWizard({});}} startEdit={function(k){setWizard(k);}}/>}
        {page==='angebote'&&<AngebotePage state={state} dispatch={dispatch} setPage={setPage}/>}
        {page==='anzahlung'&&<AzahlungPage state={state} dispatch={dispatch} setPage={setPage}/>}
        {page==='schluss'&&<SchlussPage state={state} dispatch={dispatch}/>}
        {page==='reisekosten'&&<ReisekostenPage state={state} dispatch={dispatch}/>}
        {page==='privat'&&<PrivatPage state={state} dispatch={dispatch}/>}
        {page==='einstellungen'&&<EinstellungenPage state={state} dispatch={dispatch} syncNow={syncNow}/>}
      </div>
      <BottomNav page={page} setPage={setPage} syncStatus={syncStatus} syncNow={syncNow}/>
      {wizard!==null&&<KalkWizard state={state} dispatch={dispatch} initial={wizard.id?wizard:null} onClose={function(){setWizard(null);}} onAlsAngebot={function(){setPage('angebote');setWizard(null);}}/>}
    </div>
  );
}

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