const {useState,useEffect,useMemo,useRef} = React;
const P=window.MG_P, W=window.MG_W;

const KAT=[
 ['Gesichtsbehandlungen','beh-gesicht.jpg','Schöne Haut ist kein Zufall – sie ist Pflege, die bleibt.',[
  ['Kosmetische Grundbehandlung','90 Min.',85,'Reinigung, Hautanalyse, Peeling, Enzym-Peeling, Tiefenreinigung, Wirkstoffampulle, Gesichts- & Dekolleté-Massage, Spezialmaske – inklusive Abschlusspflege.'],
  ['Basis Kurzbehandlung','60 Min.',75,'Die kompakte Gesichtsbehandlung: Reinigung, Hautanalyse, Peeling, Enzym-Peeling, Tiefenreinigung, Spezialampulle mit Maske.'],
  ['Comfort Männerbehandlung','90 Min.',85,'Speziell auf Männerhaut abgestimmt: Reinigung, Hautanalyse, Peeling, Enzym-Peeling, Tiefenreinigung, Spezialampulle mit Maske.'],
  ['Akne ade (bis 18 Jahre)','45 Min.',65,'Für junge Haut: Reinigung, Hautanalyse, Enzym-Peeling, Tiefenreinigung, Wirkstoffkonzentrat mit Spezialmaske und Abschlusspflege.'],
  ['Frische Kick Behandlung','50 Min.',65,'Der schnelle Glow: Reinigung, Peeling, Enzym-Peeling und Feuchtigkeitsmaske mit Abschlusspflege.'],
  ['Augenbehandlung','30 Min.',35,'Belebend-kühlende Modellage für die zarte Augenpartie. Strafft, glättet Linien und reduziert Müdigkeitsspuren.'],
  ['Manuelle Lymphdrainage','40 Min.',55,'Gesicht & Dekolleté: aktiviert den Lymphabfluss, entstaut das Gewebe und sorgt für ein frisches Hautbild.']]],
 ['Luxusbehandlungen','studio-5375.jpg','Zwei Masken, zwei Massagen – und 120 Minuten nur für dich.',[
  ['Soin Mille Lumière','120 Min.',155,'Unsere luxuriöseste Anti-Aging-Gesichtsbehandlung mit zwei Massagen und zwei Masken. Regeneriert, strafft und verleiht unvergleichliche Ausstrahlung.'],
  ['Maria Galland Lift Expert','90 Min.',110,'100 % manuelle Gesichtsbehandlung für sichtbare Festigung und Definition der Konturen.'],
  ['Masque Modelant','100 Min.',100,'Klassische Gesichtsbehandlung mit selbst erwärmender Mineralienmaske. Vitalisiert und sorgt für einen natürlich gelifteten Teint.']]],
 ['Mikrodermabrasion','beh-microderm.jpg','Feine Abtragung, große Wirkung: dein Teint wirkt sofort ebenmäßiger.',[
  ['Economy Behandlung','ca. 45 Min.',75,'Reinigung, Enzym-Peeling, Microdermabrasion, Wirkstoffampulle, Abschlusspflege.'],
  ['Business Behandlung','ca. 60 Min.',100,'Wie Economy, zusätzlich mit Tiefenausreinigung und Feuchtigkeitsmaske.'],
  ['Comfort Behandlung','ca. 75 Min.',110,'Das volle Programm inklusive Tiefenausreinigung, Dekolleté- und Spezialmaske.']]],
 ['Microneedling & BB Glow','beh-microneedling.jpg','Kollagen entsteht nicht über Nacht – aber heute fängt es an.',[
  ['Microneedling Gesicht, Hals & Dekolleté','ca. 90 Min.',150,'Feinste Mikroimpulse aktivieren die Zellerneuerung und regen Kollagen & Elastin an.'],
  ['BB Glow Gesicht','ca. 60 Min.',100,'Die sanfte Anti-Aging-Behandlung für strahlende, ebenmäßige Haut.'],
  ['BB Glow Gesicht, Hals & Dekolleté','ca. 75 Min.',130,'BB Glow für das große Areal in einer Sitzung.'],
  ['BB Glow 3er-Paket','3 Termine',280,'Drei Behandlungen im Paket – für ein länger anhaltendes Ergebnis zum Vorteilspreis.']]],
 ['Rücken & Körper','studio-5397.jpg','Der Rücken bekommt selten Aufmerksamkeit – hier schon.',[
  ['Rückenbehandlung','ca. 60 Min.',65,'Gegen akute Hautunreinheiten am Rücken: Reinigung, Peeling, Bedampfung und Tiefenausreinigung.'],
  ['Rückenbehandlung mit Mikrodermabrasion','ca. 75 Min.',85,'Die Rückenbehandlung, ergänzt um eine Mikrodermabrasion für ein feineres Hautbild.']]],
 ['Wimpern & Brauen','beh-wimpern-hero.jpg','Ein wacher Blick sagt mehr als tausend Worte.',[
  ['Wimpernverlängerung Erstbehandlung','ca. 120 Min.',140,'Wimper-für-Wimper-Technik für einen natürlich vollen Augenaufschlag.'],
  ['Refill','ca. 50 Min.',58,'Auffüllen der Wimpernverlängerung – empfohlen alle 2–4 Wochen.'],
  ['Refill lang','ca. 75 Min.',63,'Ausführliches Refill bei längerer Pause seit der letzten Behandlung.'],
  ['Büscheltechnik','ca. 50 Min.',73,'Kleine Wimpernbüschel statt einzelner Wimpern – schneller aufgebaut, sichtbar voller.'],
  ['Wimpernlifting','ca. 60 Min.',78,'Natürlicher Schwung ohne Extensions – inklusive Färben.'],
  ['Wimpern entfernen','ca. 30 Min.',33,'Schonendes, vollständiges Ablösen der Extensions ohne die eigenen Wimpern zu strapazieren.'],
  ['Wimpern färben','15 Min.',23,'Intensiviert helle Wimpern für einen wachen Blick.'],
  ['Augenbrauen färben','15 Min.',14,'Intensiviert helle Brauen für mehr Tiefe und Definition.','ab 14 €'],
  ['Augenbrauen zupfen','15 Min.',14,'Präzise Form, abgestimmt auf deine Gesichtszüge.','ab 14 €']]],
 ['Make-up','hf-makeup.jpg','Das beste Make-up lässt dich aussehen wie dich – nur ausgeschlafener.',[
  ['Kleines Make-up nach der Behandlung','ca. 20 Min.',25,'Der dezente Feinschliff direkt im Anschluss an deine Kosmetikbehandlung – in der Behandlung selbst nicht enthalten.'],
  ['Tages Make-up','30 Min.',47,'Frisches, natürliches Make-up für den Tag – typgerecht abgestimmt.'],
  ['Abend Make-up','45 Min.',67,'Ausdrucksstarkes Make-up für besondere Anlässe.'],
  ['Make-up Beratung','60 Min.',78,'Persönliche Beratung mit Übungsteil zum Selbermachen.']]],
 ['Depilation mit Wachs','hf-waxing-bienenwachs.jpg','Glatte Haut für Wochen – ausschließlich mit Bienenwachs.',[
  ['Oberlippe','ca. 10 Min.',15,'Schnell und präzise – in der Mittagspause erledigt.'],
  ['Kinn','ca. 10 Min.',15,'Gründliche Entfernung feiner wie kräftiger Härchen.'],
  ['Wangen','ca. 10 Min.',16,'Für einen ebenmäßigen, weichen Teint.'],
  ['Paketpreis Gesicht','ca. 20 Min.',35,'Oberlippe, Kinn und Wangen in einem Termin – günstiger als einzeln.'],
  ['Achseln','ca. 15 Min.',30,'Zügig und gründlich, mit beruhigender Pflege im Anschluss.'],
  ['Unterarme','ca. 20 Min.',30,'Gleichmäßig glatte Arme für mehrere Wochen.'],
  ['Unterschenkel','ca. 25 Min.',40,'Vom Knie abwärts – der Klassiker für den Sommer.'],
  ['Beine komplett','ca. 45 Min.',63,'Ober- und Unterschenkel in einem Durchgang.'],
  ['Bikinizone (nur für Frauen)','ca. 20 Min.',35,'Saubere Kante entlang der Bikinilinie.'],
  ['Bikinizone komplett (nur für Frauen)','ca. 30 Min.',55,'Die vollständige Enthaarung der Intimzone.'],
  ['Rücken','ca. 30 Min.',45,'Auch die Stellen, an die du selbst nicht kommst.'],
  ['Brust','ca. 30 Min.',45,'Gründlich und mit beruhigender Nachpflege.'],
  ['Paketpreis Rücken & Brust','ca. 50 Min.',70,'Beide Zonen in einem Termin – günstiger als einzeln.']]],
 ['Hände & Nägel','beh-manikuere.jpg','Gepflegte Hände sind deine schönste Visitenkarte.',[
  ['Maniküre','45 Min.',45,'Formen, Nagelhautpflege und Abschluss mit pflegendem Öl.'],
  ['Maniküre intensiv','60 Min.',51,'Ausführliche Maniküre mit zusätzlicher Pflege für beanspruchte Hände.'],
  ['Wellnessmaniküre mit Parfümbad','60 Min.',55,'Maniküre plus entspannendes Parfümbad und Handmassage.'],
  ['Shellac','45 Min.',55,'Langanhaltender Glanz, der 2–3 Wochen perfekt hält.'],
  ['Naturnagelverstärkung','60 Min.',55,'Stabilisiert weiche und brüchige Nägel – ganz ohne Kunstnagel.'],
  ['Shellac lösen','ca. 30 Min.',30,'Schonendes Ablösen ohne den Naturnagel anzugreifen.'],
  ['Reparatur des Nagels','ca. 15 Min.',9,'Ein eingerissener Nagel wird stabilisiert und wieder in Form gebracht.']]],
 ['Fußpflege','beh-fusspflege.jpg','Verwöhne die Füße, die dich jeden Tag tragen.',[
  ['Pediküre','45 Min.',46,'Fußbad, Hornhautentfernung, Nagel- und Nagelhautpflege.'],
  ['Pediküre mit Lack','60 Min.',62,'Pediküre inklusive Lackierung in deiner Wunschfarbe.'],
  ['French Pediküre mit Lack','60 Min.',62,'Pediküre mit klassischem French-Finish in Lack.'],
  ['French Pediküre mit Gel','60 Min.',62,'French-Finish in Gel – besonders lange haltbar.'],
  ['Gel-/Carbonlack Pediküre','60 Min.',62,'Robuster Gel- oder Carbonlack für dauerhaften Glanz.'],
  ['Wellnesspediküre','60 Min.',57,'Pediküre mit zusätzlicher Maske und entspannender Fußmassage.'],
  ['Fußmassage','ca. 15 Min.',20,'Kurze, wohltuende Massage – einzeln oder im Anschluss.']]]];

/* Add-ons: 4. Feld = Kategorien, in denen das Add-on angeboten wird
   (die Preisliste nennt diese Preise "innerhalb einer Kosmetikbehandlung"). */
const KOSMETIK=['Gesichtsbehandlungen','Luxusbehandlungen','Mikrodermabrasion','Microneedling & BB Glow','Rücken & Körper'];
const ADDONS=[
 ['Mikrodermabrasion als Add-on',40,'Sanfte Abtragung der obersten Hautschicht – direkt in deine Behandlung integriert.',['Gesichtsbehandlungen','Luxusbehandlungen']],
 ['Kleines Make-up danach',25,'Der dezente Feinschliff direkt im Anschluss – du gehst fertig gestylt aus dem Studio.',KOSMETIK],
 ['Wimpern färben',19,'Intensiviert helle Wimpern während deiner Behandlung – ohne Extra-Termin.',KOSMETIK],
 ['Augenbrauen färben',13,'Mehr Tiefe und Definition – im Anschluss an deine Behandlung.',KOSMETIK],
 ['Augenbrauen zupfen',13,'Präzise Form, direkt mitgemacht.',KOSMETIK],
 ['Oberlippe oder Kinn mit Wachs',14,'Kleine Depilation innerhalb der Kosmetikbehandlung – günstiger als einzeln.',KOSMETIK],
 ['Nagelreparatur',8,'Ein eingerissener Nagel wird während der Behandlung mitrepariert.',['Hände & Nägel','Fußpflege']]];
const STAFF=[['Egal','Nächste freie Mitarbeiterin'],['Svetlana','Inhaberin · seit 2004'],['Eugenia','Kosmetikerin · seit 2006']];
const SLOTS=['09:00','10:00','11:00','12:30','14:00','15:00','16:00','17:30'];
const STEPS=['Behandlung','Termin','Deine Daten','Bestätigung'];

/* --- Anzahlung ----------------------------------------------------------
   Bei JEDER Online-Buchung werden ANZ_BETRAG € im Voraus gezahlt (Ansage
   der Inhaberin vom 24.08.2026 — vorher galt eine 100-€-Schwelle).
   Rechtlich ist das eine Vorauszahlung auf den Behandlungspreis, KEINE
   "Reservierungsgebühr" – die Bezeichnung ändert nach § 306a BGB ohnehin
   nichts. Der Einbehalt bei No-Show ist pauschalierter Schadensersatz und
   braucht den Gegenbeweisvorbehalt aus § 309 Nr. 5 BGB. */
const ANZ_BETRAG=25;
const anzPflicht=summe=>summe>0;

/* --- Willkommensgutschein (?code=LAVIE10-XXXX) --------------------------
   Kommt aus der Newsletter-Willkommens-Mail. Die Anzeige hier ist nur
   Vorschau — ob der Code wirklich gilt (eigener Code, unbenutzt, ERSTE
   Buchung), entscheidet der Server bei der Buchung; er rechnet den Preis
   selbst und weist das Ergebnis in der Bestätigungs-Mail aus. */
const GUTSCHEIN_CODE=(()=>{try{const c=new URLSearchParams(location.search).get('code')||'';
  return /^[A-Z0-9-]{6,20}$/i.test(c)?c.toUpperCase():null}catch(e){return null}})();
/* Quelle der Wahrheit ist das Gutscheincode-FELD (Schritt 3) — der URL-Code
   füllt es nur vor. So kann die Kundin den Code auch von Hand eintragen
   oder korrigieren, und Anzeige wie Buchung nutzen denselben Wert. */
const codeNorm=c=>{const x=(c||'').trim().toUpperCase();return /^[A-Z0-9-]{6,20}$/.test(x)?x:null};
const rabattWert=(s,code)=>code?Math.round(s*0.9*100)/100:s;
const eurF=n=>n.toFixed(2).replace('.',',').replace(',00','');

/* Zitate der Kundin fuer die Wartescreens. `a` = Urheberin, wird als
   Eyebrow unter dem Zitat gesetzt. Die Auswahl passiert EINMAL beim Start
   des jeweiligen Screens (State in MgBuchung) – wuerde man im Render
   wuerfeln, wechselte das Zitat bei jedem Re-Render mitten in der
   Einblendung. */
const ZITATE=[
 {t:'Deine Haut – das einzig wahre Accessoire, das du ein Leben lang trägst. Pflege, was dich täglich begleitet.'},
 {t:'Kosmetik ist die Kunst, die Geburtsurkunde zu dementieren.',a:'Olga Tschechowa'}];
const waehle=arr=>arr[Math.floor(Math.random()*arr.length)];

/* --- Einwilligungen -----------------------------------------------------
   Ein Kanal = eine eigene Checkbox. Gebündelte Werbe-Einwilligungen sind
   nicht "spezifisch" im Sinne von Art. 4 Nr. 11 DSGVO. Keine Box ist
   vorausgewählt (EuGH "Planet49"). Der Wortlaut wird mitprotokolliert,
   damit später beweisbar ist, wofür genau zugestimmt wurde (Art. 7 I DSGVO). */
const CONSENT_VERSION='2026-08-16.1';
const KANAELE=[
 ['m_email','E-Mail','Angebote, Pflegetipps und Aktionen – bestätigt per Double-Opt-in.'],
 ['m_sms','SMS','Kurze Nachrichten zu kurzfristig frei gewordenen Terminen und Aktionen.'],
 ['m_wa','WhatsApp','Angebote und Neuigkeiten per WhatsApp – zusätzlich im Chat zu bestätigen.']];
const TXT_WERBUNG=k=>{const f=KANAELE.find(x=>x[0]===k);return f?'Ich willige ein, Werbung von La Vie per '+f[1]+' zu erhalten ('+f[2]+'). Widerruf jederzeit und kostenfrei möglich.':''};
const TXT_GESUND='Ich willige ausdrücklich ein, dass La Vie meine Angaben zu Haut, Allergien und Gesundheit zur Vorbereitung und Durchführung der Behandlung verarbeitet (Art. 9 Abs. 2 lit. a DSGVO).';
/* Die Ausfallpauschale hängt an der Anzahlung: Sie darf den typischen Schaden
   nicht übersteigen (§ 309 Nr. 5 lit. a BGB). Bei einer 30-€-Behandlung wären
   25 € unverhältnismäßig – dort wird deshalb nichts pauschaliert. */
const TXT_STORNO=anz=>anz
 ? 'Ich habe die Buchungs- und Stornobedingungen gelesen und akzeptiere sie, einschließlich der Anzahlung von '+ANZ_BETRAG+' € und der Ausfallregelung bei Absagen weniger als 24 Stunden vor dem Termin.'
 : 'Ich habe die Buchungs- und Stornobedingungen gelesen und akzeptiere sie und sage meinen Termin rechtzeitig ab, falls ich ihn nicht wahrnehmen kann.';
const TXT_DS='Ich habe die Datenschutzerklärung zur Kenntnis genommen.';

const days=()=>{const o=[];const d=new Date();d.setDate(d.getDate()+1);while(o.length<7){if(d.getDay()!==0)o.push(new Date(d));d.setDate(d.getDate()+1)}return o};
const fmt=d=>d.toLocaleDateString('de-DE',{weekday:'long',day:'numeric',month:'long'});
const fmtS=d=>d.toLocaleDateString('de-DE',{weekday:'short',day:'numeric',month:'short'});

function Steps({step}){
  return <div className="mg-steps" style={{display:'flex',borderBottom:'1px solid var(--line)',marginBottom:'clamp(30px,4vw,54px)'}}>
    {STEPS.map((l,i)=><div key={l} className="mg-step" data-l={l} data-cur={i===step?'1':'0'} style={{display:'flex',alignItems:'center',gap:10,padding:'0 clamp(14px,2vw,28px) 16px',borderBottom:'1px solid '+(i===step?'var(--ink)':'transparent'),marginBottom:-1,opacity:i>step?.45:1,transition:'opacity .4s var(--ease),border-color .4s var(--ease)'}}>
      <span style={{fontSize:12,letterSpacing:'.14em',color:i<=step?'var(--ink)':'var(--ink-mute)'}}>{String(i+1).padStart(2,'0')}</span>
      <span className="mg-step-l" style={{fontSize:14.5,color:i<=step?'var(--ink)':'var(--ink-mute)'}}>{l}</span>
      <span className="mg-step-cur" style={{display:'none',fontSize:13.5,marginLeft:8}}>{l}</span>
    </div>)}
  </div>;
}

function Step1({sel,setSel,addons,setAddons,persons,setPersons}){
  const [open,setOpen]=useState(0);
  const [info,setInfo]=useState('');
  return <div>
    <div style={{borderTop:'1px solid var(--line)'}}>
      {KAT.map(([kat,img,quote,items],ki)=><div key={kat} style={{borderBottom:'1px solid var(--line)'}}>
        <button onClick={()=>setOpen(open===ki?-1:ki)} style={{width:'100%',display:'flex',alignItems:'center',justifyContent:'space-between',gap:16,background:'none',border:'none',cursor:'pointer',padding:'22px 2px',fontFamily:'Jost,sans-serif',fontSize:18,fontWeight:300,color:'var(--ink)',textAlign:'left'}}>
          <span style={{display:'flex',alignItems:'center',gap:16}}>
            <img loading="lazy" src={P+img} alt="" style={{width:52,height:52,objectFit:'cover',flexShrink:0}}/>
            <span>{kat}<span style={{display:'block',fontSize:12.5,color:'var(--ink-mute)',marginTop:3}}>{items.length} Behandlungen · ab {Math.min(...items.map(x=>x[2]))} €</span></span>
          </span>
          <span style={{fontSize:20,transform:open===ki?'rotate(45deg)':'none',transition:'transform .4s var(--ease)'}}>+</span>
        </button>
        <div style={{display:'grid',gridTemplateRows:open===ki?'1fr':'0fr',transition:'grid-template-rows .45s var(--ease)'}}><div style={{overflow:'hidden'}}>
          <div style={{paddingBottom:14,opacity:open===ki?1:0,transition:'opacity .4s var(--ease)'}}>
            {items.map(([n,dur,pr,desc,plabel])=>{const on=sel&&sel.name===n;const io=info===n;
            return <div key={n} onClick={()=>{setSel({name:n,dur,preis:pr,kat,img,quote,plabel});setAddons(a=>a.filter(x=>{const ad=ADDONS.find(y=>y[0]===x);return !ad||!ad[3]||ad[3].includes(kat)}))}} style={{cursor:'pointer',padding:'16px 18px',marginBottom:8,background:on?'var(--paper-2)':'transparent',border:'1px solid '+(on?'var(--ink)':'var(--line)'),transition:'border-color .3s var(--ease),background .3s var(--ease)'}}>
              <div style={{display:'flex',alignItems:'center',justifyContent:'space-between',gap:14}}>
                <span style={{display:'flex',alignItems:'center',gap:10,fontSize:15.5}}>
                  <span style={{width:15,height:15,borderRadius:'50%',border:'1px solid '+(on?'var(--ink)':'var(--ink-mute)'),display:'inline-flex',alignItems:'center',justifyContent:'center',flexShrink:0}}>{on&&<span style={{width:7,height:7,borderRadius:'50%',background:'var(--ink)'}}></span>}</span>
                  {n}
                  <button onClick={e=>{e.stopPropagation();setInfo(io?'':n)}} title="Details" style={{width:20,height:20,borderRadius:'50%',border:'1px solid var(--line)',background:io?'var(--ink)':'transparent',color:io?'#fff':'var(--ink-mute)',cursor:'pointer',fontSize:11,lineHeight:1,flexShrink:0,fontFamily:'Jost,sans-serif'}}>i</button>
                </span>
                <span style={{fontSize:14,color:'var(--ink-mute)',whiteSpace:'nowrap'}}>{dur} · <span style={{color:'var(--ink)'}}>{plabel||pr+' €'}</span></span>
              </div>
              <div style={{display:'grid',gridTemplateRows:io?'1fr':'0fr',transition:'grid-template-rows .35s var(--ease)'}}><div style={{overflow:'hidden'}}>
                <p className="mg-p" style={{fontSize:13.5,padding:'10px 0 2px 25px',margin:0,maxWidth:560}}>{desc}</p>
              </div></div>
            </div>;})}
          </div>
        </div></div>
      </div>)}
    </div>
    {sel&&<div style={{marginTop:34}}>
      <div className="mg-eyebrow">Ergänzen</div>
      <div style={{marginTop:14,display:'flex',flexDirection:'column',gap:10}}>
        {ADDONS.filter(([,,,kats])=>!kats||kats.includes(sel.kat)).map(([n,pr,desc])=>{const on=addons.includes(n);
        return <label key={n} style={{display:'flex',gap:12,alignItems:'flex-start',padding:'15px 18px',border:'1px solid '+(on?'var(--ink)':'var(--line)'),cursor:'pointer',transition:'border-color .3s var(--ease)'}}>
          <input type="checkbox" checked={on} onChange={e=>setAddons(e.target.checked?[...addons,n]:addons.filter(a=>a!==n))} style={{accentColor:'var(--ink)',marginTop:3}}/>
          <span style={{flex:1}}><span style={{fontSize:15}}>{n}</span><span className="mg-p" style={{display:'block',fontSize:13,marginTop:3}}>{desc}</span></span>
          <span style={{fontSize:14,whiteSpace:'nowrap'}}>+{pr} €</span>
        </label>;})}
      </div>
      <div className="mg-eyebrow" style={{marginTop:30}}>Für wie viele Personen?</div>
      <div style={{display:'flex',gap:10,marginTop:14,flexWrap:'wrap'}}>
        {[1,2,3].map(p=><button key={p} onClick={()=>setPersons(p)} style={{border:'1px solid '+(persons===p?'var(--ink)':'var(--line)'),background:persons===p?'var(--ink)':'transparent',color:persons===p?'#fff':'var(--ink)',cursor:'pointer',fontFamily:'Jost,sans-serif',fontSize:14,padding:'12px 24px',transition:'all .3s var(--ease)'}}>{p===1?'Nur ich':p+' Personen'}</button>)}
      </div>
      {persons>1&&<p className="mg-p" style={{fontSize:13,marginTop:10}}>Wir reservieren {persons} Plätze parallel – bei Bedarf melden wir uns zur Feinabstimmung.</p>}
    </div>}
  </div>;
}

function Step2({staff,setStaff,tag,setTag,slot,setSlot}){
  const D=useMemo(days,[]);
  return <div>
    <div className="mg-eyebrow">Mitarbeiterin</div>
    <div style={{display:'grid',gridTemplateColumns:'repeat(auto-fit,minmax(180px,1fr))',gap:10,marginTop:14}}>
      {STAFF.map(([s,d])=><button key={s} onClick={()=>setStaff(s)} style={{textAlign:'left',padding:'16px 18px',border:'1px solid '+(staff===s?'var(--ink)':'var(--line)'),background:staff===s?'var(--paper-2)':'transparent',cursor:'pointer',fontFamily:'Jost,sans-serif',transition:'all .3s var(--ease)'}}>
        <span style={{fontSize:15.5,display:'block',color:'var(--ink)'}}>{s}</span>
        <span style={{fontSize:12.5,color:'var(--ink-mute)'}}>{d}</span>
      </button>)}
    </div>
    <div className="mg-eyebrow" style={{marginTop:34}}>Tag <span style={{textTransform:'none',letterSpacing:0,fontSize:12}}>· buchbar bis max. 1 Woche im Voraus</span></div>
    <div style={{display:'grid',gridTemplateColumns:'repeat(auto-fit,minmax(108px,1fr))',gap:10,marginTop:14}}>
      {D.map(d=>{const k=d.toDateString();const on=tag===k;
      return <button key={k} onClick={()=>setTag(k)} style={{padding:'14px 8px',border:'1px solid '+(on?'var(--ink)':'var(--line)'),background:on?'var(--ink)':'transparent',color:on?'#fff':'var(--ink)',cursor:'pointer',fontFamily:'Jost,sans-serif',fontSize:14,transition:'all .3s var(--ease)'}}>{fmtS(d)}</button>;})}
    </div>
    {tag&&<>
      <div className="mg-eyebrow" style={{marginTop:34}}>Uhrzeit</div>
      <div style={{display:'grid',gridTemplateColumns:'repeat(auto-fit,minmax(94px,1fr))',gap:10,marginTop:14}}>
        {SLOTS.map(s=>{const on=slot===s;
        return <button key={s} onClick={()=>setSlot(s)} style={{padding:'14px 8px',border:'1px solid '+(on?'var(--ink)':'var(--line)'),background:on?'var(--ink)':'transparent',color:on?'#fff':'var(--ink)',cursor:'pointer',fontFamily:'Jost,sans-serif',fontSize:14.5,transition:'all .3s var(--ease)'}}>{s}</button>;})}
      </div>
      {/* Erstbesuch = Beratung geschenkt (Ansage der Inhaberin): Kundinnen
          sollen wissen, dass sie beim ersten Mal mehr Zeit einplanen. Die
          zusätzliche halbe Stunde blockt der Server im Kalender automatisch. */}
      <div style={{display:'flex',gap:12,alignItems:'flex-start',background:'var(--paper-2)',border:'1px solid var(--line)',padding:'14px 18px',marginTop:24,maxWidth:560}}>
        <span aria-hidden="true" style={{fontSize:14,lineHeight:1.6}}>✦</span>
        <p className="mg-p" style={{fontSize:13,margin:0}}>Zum ersten Mal bei uns? Dann gehört eine ausführliche persönliche Beratung dazu – mit Hautanalyse und Produktempfehlung, als Geschenk. Plane dafür etwa 30 Minuten zusätzlich ein; wir reservieren die Zeit automatisch mit.</p>
      </div>
    </>}
  </div>;
}

function Step3({daten,setDaten}){
  const [err,setErr]=useState({});
  const set=(k,v)=>setDaten({...daten,[k]:v});
  const notiz=(daten.notiz||'').trim();
  const alleAn=KANAELE.every(([k])=>daten[k]);
  window.__validate3=()=>{
    const e={};
    if(!daten.name)e.name='Bitte gib deinen Namen an.';
    if(!daten.email||!/.+@.+\..+/.test(daten.email))e.email='Bitte gib eine gültige E-Mail-Adresse an.';
    if(!daten.tel)e.tel='Für die Terminbestätigung brauchen wir deine Nummer.';
    if(!daten.ds)e.ds='Bitte bestätige, dass du die Datenschutzerklärung gelesen hast.';
    /* Gesundheitsangaben sind Art.-9-Daten: dafür reicht der Vertrag nicht,
       es braucht eine ausdrückliche Einwilligung. Nur abfragen, wenn die
       Notiz überhaupt ausgefüllt ist – sonst ist es unnötige Reibung. */
    if(notiz&&!daten.gesund)e.gesund='Deine Notiz kann Gesundheitsangaben enthalten – dafür brauchen wir deine ausdrückliche Einwilligung.';
    setErr(e);return Object.keys(e).length===0;
  };
  /* Pflichtfelder werden mit * markiert – das ist die gewohnte Konvention,
     die Legende steht am Ende des Schritts. */
  const F=(k,label,type,pflicht)=><div>
    <label className="mg-eyebrow" style={{fontSize:11,display:'block',marginBottom:7}}>{label}{pflicht&&<span aria-hidden="true"> *</span>}</label>
    <input type={type||'text'} required={!!pflicht} aria-required={pflicht?'true':undefined} value={daten[k]||''} onChange={e=>set(k,e.target.value)} style={{width:'100%',background:'var(--paper-2)',border:'1px solid '+(err[k]?'#a33':'var(--line)'),outline:'none',padding:'14px 18px',fontFamily:'Jost,sans-serif',fontSize:15,color:'var(--ink)'}}/>
    {err[k]&&<div style={{fontSize:12,color:'#a33',marginTop:6}}>{err[k]}</div>}
  </div>;
  const CB=(k,children)=><label style={{display:'flex',gap:11,alignItems:'flex-start',cursor:'pointer',lineHeight:1.6}}>
    <input type="checkbox" checked={!!daten[k]} onChange={e=>set(k,e.target.checked)} style={{accentColor:'var(--ink)',marginTop:3}}/>
    <span style={{fontSize:12.5,color:'var(--ink-soft)'}}>{children}</span>
  </label>;
  return <div style={{display:'flex',flexDirection:'column',gap:18,maxWidth:620}}>
    <div style={{display:'grid',gridTemplateColumns:'repeat(auto-fit,minmax(200px,1fr))',gap:14}}>{F('name','Name',null,true)}{F('tel','Telefon / WhatsApp','tel',true)}</div>
    {F('email','E-Mail','email',true)}
    <div>
      <label className="mg-eyebrow" style={{fontSize:11,display:'block',marginBottom:7}}>Notiz an uns (optional)</label>
      <textarea rows={3} value={daten.notiz||''} onChange={e=>set('notiz',e.target.value)} placeholder="Wünsche, Hautthemen, Allergien – alles, was uns hilft." style={{width:'100%',background:'var(--paper-2)',border:'1px solid '+(err.gesund?'#a33':'var(--line)'),outline:'none',padding:'14px 18px',fontFamily:'Jost,sans-serif',fontSize:15,resize:'vertical',color:'var(--ink)'}}></textarea>
      {notiz&&<div style={{marginTop:12}}>
        {CB('gesund',<><span style={{color:'var(--ink)'}} aria-hidden="true">* </span>{TXT_GESUND}</>)}
        {err.gesund&&<div style={{fontSize:12,color:'#a33',marginTop:6,marginLeft:26}}>{err.gesund}</div>}
      </div>}
    </div>
    {F('code','Gutscheincode (optional)')}

    {/* Terminerinnerung ist Vertragsnebenleistung, keine Werbung – hier wird
        nur der Kanal gewählt, keine Einwilligung eingeholt. */}
    <div style={{borderTop:'1px solid var(--line)',paddingTop:20}}>
      <div className="mg-eyebrow">Terminerinnerung</div>
      <p className="mg-p" style={{fontSize:13,margin:'10px 0 12px'}}>Wir erinnern dich 24 Stunden vor dem Termin. Wähle, wie es dir am liebsten ist.</p>
      <div style={{display:'flex',gap:10,flexWrap:'wrap'}}>
        {[['E-Mail','email'],['SMS','sms'],['WhatsApp','wa']].map(([l,v])=><button key={v} type="button" onClick={()=>set('erinnerung',v)} style={{border:'1px solid '+((daten.erinnerung||'email')===v?'var(--ink)':'var(--line)'),background:(daten.erinnerung||'email')===v?'var(--ink)':'transparent',color:(daten.erinnerung||'email')===v?'#fff':'var(--ink)',cursor:'pointer',fontFamily:'Jost,sans-serif',fontSize:14,padding:'11px 22px',transition:'all .3s var(--ease)'}}>{l}</button>)}
      </div>
    </div>

    {/* Werbe-Einwilligungen: getrennt pro Kanal, keine vorausgewählt,
        und die Buchung funktioniert ohne jede davon (kein Kopplungsverbot-
        Verstoß nach Art. 7 Abs. 4 DSGVO). */}
    <div style={{border:'1px solid var(--line)',padding:'22px 24px'}}>
      <div style={{display:'flex',justifyContent:'space-between',alignItems:'baseline',gap:14,flexWrap:'wrap'}}>
        <div className="mg-eyebrow">Bleib in Kontakt</div>
        <button type="button" onClick={()=>{const n={...daten};KANAELE.forEach(([k])=>n[k]=!alleAn);setDaten(n)}} style={{border:'none',background:'none',cursor:'pointer',fontFamily:'Jost,sans-serif',fontSize:12.5,color:'var(--ink)',textDecoration:'underline',textUnderlineOffset:3,padding:0}}>{alleAn?'Alle abwählen':'Alle aktivieren'}</button>
      </div>
      <p className="mg-p" style={{fontSize:13.5,margin:'12px 0 16px'}}>Als Dankeschön bekommst du <span style={{color:'var(--ink)'}}>10 % auf deine übernächste Behandlung</span> und einen Gutschein zum Geburtstag. Wir melden uns höchstens zweimal im Monat.</p>
      <div style={{display:'flex',flexDirection:'column',gap:11,borderTop:'1px solid var(--line)',paddingTop:16}}>
        {KANAELE.map(([k,label,desc])=><label key={k} style={{display:'flex',gap:11,alignItems:'flex-start',cursor:'pointer',lineHeight:1.6}}>
          <input type="checkbox" checked={!!daten[k]} onChange={e=>set(k,e.target.checked)} style={{accentColor:'var(--ink)',marginTop:3}}/>
          <span style={{fontSize:12.5,color:'var(--ink-soft)'}}><span style={{color:'var(--ink)'}}>{label}</span> – {desc}</span>
        </label>)}
      </div>
      <p className="mg-p" style={{fontSize:11.5,marginTop:14,lineHeight:1.7}}>Freiwillig: Dein Termin kommt auch ohne zustande. Du kannst jede Einwilligung jederzeit und kostenfrei widerrufen – per Abmeldelink, mit „STOPP" als Antwort oder formlos an <a href="mailto:info@lavie-wirdschoener.de" style={{color:'var(--ink-soft)'}}>info@lavie-wirdschoener.de</a>.</p>
    </div>

    <div style={{borderTop:'1px solid var(--line)',paddingTop:20,display:'flex',flexDirection:'column',gap:12}}>
      {CB('ds',<><span style={{color:'var(--ink)'}} aria-hidden="true">* </span>{TXT_DS} Meine Daten werden zur Durchführung des Termins verarbeitet (Art. 6 Abs. 1 lit. b DSGVO) – Details in der <a href="datenschutz.html" style={{color:'var(--ink)'}}>Datenschutzerklärung</a>.</>)}
      {err.ds&&<div style={{fontSize:12,color:'#a33',marginLeft:26}}>{err.ds}</div>}
      {/* Pflichthinweis aus § 7 Abs. 3 Nr. 3 UWG – muss schon bei der Erhebung
          der Adresse erfolgen, nicht erst in der ersten Werbemail. */}
      <p className="mg-p" style={{fontSize:11.5,lineHeight:1.7,marginTop:2}}>Nach deinem Besuch schicken wir dir gelegentlich Hinweise zu vergleichbaren Behandlungen an deine E-Mail-Adresse. Du kannst dem jederzeit widersprechen, ohne dass dafür andere als die Übermittlungskosten nach Basistarifen entstehen – in jeder Nachricht per Link oder formlos an uns.</p>
      <p className="mg-p" style={{fontSize:11.5,marginTop:14,paddingTop:12,borderTop:'1px solid var(--line)'}}>* Pflichtfeld</p>
    </div>
  </div>;
}

/* Die Klauseln direkt im Funnel lesbar zu machen ist nicht nur bequemer –
   es ist die Einbeziehung nach § 305 Abs. 2 BGB: zumutbare Kenntnisnahme
   VOR Vertragsschluss. Ein Link, der aus dem Funnel herausfuehrt, erfuellt
   das schlechter (und kostet auf dem Handy den Buchungsstand).
   Auszug – die vollstaendigen Fassungen liegen auf agb.html / widerruf.html. */
const RECHTSTEXT={
 storno:{t:'Buchungs- und Stornobedingungen',datei:'agb.html',abs:[
  ['Anzahlung',['Bei jeder Online-Buchung erheben wir eine Anzahlung von 25 €. Wir reservieren dafür die vollständige Behandlungszeit exklusiv für dich.','Die Anzahlung wird vollständig auf den Preis der Behandlung angerechnet. Der Restbetrag ist nach der Behandlung im Studio zu zahlen.','Die Zahlung läuft über unseren Zahlungsdienstleister Whop. Kartendaten gibst du ausschließlich dort ein; wir erhalten und speichern sie nicht.']],
  ['Absage und Umbuchung',['Du kannst deinen Termin bis 24 Stunden vor Behandlungsbeginn kostenfrei absagen oder verschieben. Eine geleistete Anzahlung erstatten wir in diesem Fall vollständig.','Für die Absage genügt eine Nachricht per E-Mail, Telefon oder WhatsApp. Maßgeblich ist der Zeitpunkt des Zugangs bei uns.']],
  ['Ausfallregelung',['Sagst du einen anzahlungspflichtigen Termin später als 24 Stunden vorher ab oder nimmst ihn ohne Absage nicht wahr, behalten wir die Anzahlung von 25 € als pauschalierten Schadensersatz für die reservierte und nicht anderweitig vergebene Behandlungszeit ein.','Dir bleibt ausdrücklich der Nachweis vorbehalten, dass uns kein Schaden oder ein wesentlich geringerer Schaden entstanden ist. In diesem Fall erstatten wir die Anzahlung ganz oder anteilig.','Können wir den frei gewordenen Termin kurzfristig anderweitig vergeben, entsteht uns kein Schaden. Wir erstatten die Anzahlung dann vollständig.','Bei Terminen ohne Anzahlung – etwa telefonisch oder im Studio vereinbart – erheben wir keine Ausfallpauschale.','Bei Verhinderung aus wichtigem Grund, etwa Krankheit, finden wir eine einvernehmliche Lösung. Sprich uns einfach an.']],
  ['Verspätung',['Erscheinst du später als vereinbart, verkürzt sich die Behandlungszeit entsprechend, damit nachfolgende Termine eingehalten werden können. Der vereinbarte Preis bleibt unverändert.']],
  ['Angaben zu deiner Gesundheit',['Bitte teile uns vor der Behandlung alles mit, was für die Durchführung wichtig ist – insbesondere Allergien, Unverträglichkeiten, Hauterkrankungen, Medikamente oder eine Schwangerschaft.']]]},
 widerruf:{t:'Widerrufsbelehrung',datei:'widerruf.html',abs:[
  ['Widerrufsrecht',['Du hast das Recht, binnen vierzehn Tagen ohne Angabe von Gründen diesen Vertrag zu widerrufen. Die Frist beträgt vierzehn Tage ab dem Tag des Vertragsabschlusses.','Für den Widerruf genügt eine eindeutige Erklärung – per Brief, Telefon oder E-Mail an Kosmetikstudio La Vie, Burgstraße 7, 27793 Wildeshausen, info@lavie-wirdschoener.de.']],
  ['Folgen des Widerrufs',['Wir zahlen dir alle erhaltenen Zahlungen einschließlich einer geleisteten Anzahlung unverzüglich und spätestens binnen vierzehn Tagen ab Eingang deines Widerrufs zurück – über dasselbe Zahlungsmittel und ohne Entgelte.']],
  ['Behandlung vor Ablauf der Frist',['Liegt dein Termin innerhalb der nächsten vierzehn Tage, benötigen wir deine ausdrückliche Zustimmung zum vorzeitigen Beginn. Nimmst du die Behandlung in Anspruch und widerrufst anschließend, ist ein angemessener Betrag für die bereits erbrachte Leistung zu zahlen.']]]}};

function RechtModal({thema,onClose}){
  window.useScrollLock(true);
  useEffect(()=>{
    const f=e=>{if(e.key==='Escape')onClose()};
    window.addEventListener('keydown',f);
    return()=>window.removeEventListener('keydown',f);
  },[onClose]);
  const d=RECHTSTEXT[thema];
  /* Portal an document.body ist hier Pflicht, nicht Geschmack: Der Schritt-
     Container laeuft mit `animation:mg-rise` auf transform und wird dadurch
     zum Containing Block fuer position:fixed. Innerhalb davon klebte das
     Overlay am Container statt am Bildschirm – der Schliessen-Knopf landete
     ausserhalb des sichtbaren Bereichs. */
  return ReactDOM.createPortal(
    <div onClick={onClose} style={{position:'fixed',inset:0,zIndex:130,background:'rgba(20,18,17,.5)',display:'flex',alignItems:'center',justifyContent:'center',padding:'clamp(10px,4vw,32px)',animation:'mg-fade .35s var(--ease) both'}}>
    {/* Der Rahmen ist hoehenbegrenzt, gescrollt wird NUR die Textspalte –
        sonst haengt auf kleinen Displays der Schliessen-Knopf ausserhalb. */}
    <div onClick={e=>e.stopPropagation()} role="dialog" aria-modal="true" aria-label={d.t} style={{background:'#fff',border:'1px solid var(--line)',width:'100%',maxWidth:640,maxHeight:'min(86vh,860px)',display:'flex',flexDirection:'column'}}>
      <div style={{display:'flex',justifyContent:'space-between',alignItems:'flex-start',gap:16,padding:'20px clamp(18px,3vw,30px) 15px',borderBottom:'1px solid var(--line)',flexShrink:0}}>
        <div><div className="mg-eyebrow">Rechtliches</div><h3 className="mg-h3" style={{fontSize:19,margin:'8px 0 0'}}>{d.t}</h3></div>
        <button onClick={onClose} aria-label="Schließen" style={{border:'none',background:'none',cursor:'pointer',fontSize:24,lineHeight:1,color:'var(--ink-mute)',padding:0,marginTop:-2,fontFamily:'Jost,sans-serif'}}>×</button>
      </div>
      <div style={{overflowY:'auto',WebkitOverflowScrolling:'touch',overscrollBehavior:'contain',padding:'clamp(16px,2.6vw,24px) clamp(18px,3vw,30px) 22px'}}>
        {d.abs.map(([h,ps],i)=><div key={h} style={{marginTop:i?24:0}}>
          <h4 style={{fontFamily:'Jost,sans-serif',fontSize:15,fontWeight:400,margin:0,color:'var(--ink)'}}>{h}</h4>
          {ps.map((p,j)=><p key={j} className="mg-p" style={{fontSize:13.5,marginTop:8,lineHeight:1.75}}>{p}</p>)}
        </div>)}
      </div>
      <div style={{borderTop:'1px solid var(--line)',padding:'13px clamp(18px,3vw,30px)',display:'flex',justifyContent:'space-between',alignItems:'center',gap:12,flexWrap:'wrap',flexShrink:0}}>
        <a href={d.datei} target="_blank" rel="noopener" className="mg-link" style={{fontSize:12}}>Vollständige Fassung</a>
        <button className="mg-btn" onClick={onClose} style={{height:40}}>Verstanden</button>
      </div>
    </div>
  </div>, document.body);
}

function Step4({sel,addons,staff,tag,slot,daten,setDaten,persons,total,onDone}){
  const [modal,setModal]=useState('');
  const [err,setErr]=useState('');
  const anz=anzPflicht(total*persons);
  const code=codeNorm(daten.code);
  const pay=()=>{
    if(!daten.storno){setErr('Bitte bestätige die Buchungs- und Stornobedingungen.');return}
    setErr('');onDone();
  };
  window.__pay=pay;
  const row=(l,r,mute)=><div style={{display:'flex',justifyContent:'space-between',gap:16,fontSize:14.5,color:mute?'var(--ink-mute)':'var(--ink)',padding:'9px 0'}}><span>{l}</span><span>{r}</span></div>;
  return <div style={{maxWidth:620}}>
    <div style={{border:'1px solid var(--line)',padding:'26px 28px'}}>
      <div className="mg-eyebrow">Zusammenfassung</div>
      <div style={{marginTop:14,borderTop:'1px solid var(--line)'}}>
        {row(sel.name+' ('+sel.dur+')',sel.plabel||sel.preis+' €')}
        {ADDONS.filter(([n])=>addons.includes(n)).map(([n,p])=><div key={n}>{row('+ '+n,p+' €',true)}</div>)}
        {persons>1&&row('Personen',persons,true)}
        {row(fmt(new Date(tag))+' · '+slot+' Uhr',staff==='Egal'?'nächste freie Mitarbeiterin':staff,true)}
        {code&&row('Gutschein '+code+' · −10 %','−'+eurF(total*persons-rabattWert(total*persons,code))+' €',true)}
        <div style={{borderTop:'1px solid var(--line)',marginTop:8,paddingTop:12,display:'flex',justifyContent:'space-between',fontSize:17}}><span>Gesamt</span><span>{code?eurF(rabattWert(total*persons,code)):total*persons} €</span></div>
      </div>
    </div>
    {anz&&<div style={{border:'1px solid var(--line)',padding:'26px 28px',marginTop:16}}>
      <div className="mg-eyebrow">Anzahlung {ANZ_BETRAG} €</div>
      {code&&<div style={{display:'flex',justifyContent:'space-between',gap:12,alignItems:'baseline',background:'var(--paper-2)',border:'1px solid var(--line)',padding:'12px 16px',margin:'10px 0 4px',flexWrap:'wrap'}}>
        <span className="mg-eyebrow" style={{fontSize:11}}>Gutschein {code}</span>
        <span style={{fontSize:13.5}}>−10 % für deine erste Behandlung: <span style={{textDecoration:'line-through',color:'var(--ink-mute)'}}>{eurF(total*persons)} €</span> → <strong>{eurF(rabattWert(total*persons,code))} €</strong></span>
      </div>}
      <p className="mg-p" style={{fontSize:13.5,margin:'10px 0 16px'}}>Mit der Anzahlung reservieren wir die volle Behandlungszeit exklusiv für dich. Die {ANZ_BETRAG} € werden vollständig auf den Behandlungspreis angerechnet – offen bleiben {eurF(rabattWert(total*persons,code)-ANZ_BETRAG)} € vor Ort.</p>
      {/* Bewusst kein Kartenfeld auf dieser Seite: Kartendaten werden erst im
          gehosteten Whop-Checkout eingegeben und berühren La Vie nie. */}
      <div style={{display:'flex',gap:12,alignItems:'flex-start',background:'var(--paper-2)',padding:'16px 18px'}}>
        <span aria-hidden="true" style={{fontSize:14,lineHeight:1.5}}>→</span>
        <p className="mg-p" style={{fontSize:13,margin:0}}>Im nächsten Schritt öffnet sich die gesicherte Zahlungsseite unseres Zahlungsdienstleisters <span style={{color:'var(--ink)'}}>Whop</span>. Deine Kartendaten werden dort eingegeben – wir sehen und speichern sie nicht.</p>
      </div>
    </div>}

    {/* Einbeziehung nach § 305 Abs. 2 BGB: die Regel steht im Klartext hier,
        nicht nur als Link. Der Gegenbeweisvorbehalt (§ 309 Nr. 5 lit. b BGB)
        und die Erstattung bei Neuvergabe sind Wirksamkeitsvoraussetzung. */}
    <div style={{border:'1px solid var(--line)',padding:'26px 28px',marginTop:16}}>
      <div className="mg-eyebrow">Wenn etwas dazwischenkommt</div>
      <div style={{marginTop:14,borderTop:'1px solid var(--line)',paddingTop:14,display:'flex',flexDirection:'column',gap:10}}>
        {(anz?[['Bis 24 Stunden vorher','Absage kostenfrei, Anzahlung wird vollständig erstattet'],
               ['Später als 24 Stunden oder ohne Absage',ANZ_BETRAG+' € werden als pauschalierter Schadensersatz einbehalten'],
               ['Termin wird neu vergeben','Du bekommst die Anzahlung vollständig zurück']]
              :[['Bis 24 Stunden vorher','Absage kostenfrei – kurze Nachricht genügt'],
               ['Später als 24 Stunden oder ohne Absage','Keine Gebühr – sag uns bitte trotzdem kurz Bescheid']]
        ).map(([l,r])=><div key={l} style={{display:'flex',justifyContent:'space-between',gap:16,fontSize:13.5,lineHeight:1.6}}><span style={{color:'var(--ink-mute)',flex:'1 1 46%'}}>{l}</span><span style={{flex:'1 1 46%',textAlign:'right'}}>{r}</span></div>)}
      </div>
      <p className="mg-p" style={{fontSize:12,marginTop:14,lineHeight:1.7}}>{anz?'Dir bleibt der Nachweis vorbehalten, dass uns kein oder ein wesentlich geringerer Schaden entstanden ist. ':'Die reservierte Zeit gehört dir – eine rechtzeitige Absage hilft uns, sie jemand anderem anzubieten. '}Ausführlich in den <button type="button" onClick={()=>setModal('storno')} style={{all:'unset',cursor:'pointer',color:'var(--ink)',textDecoration:'underline',textUnderlineOffset:3}}>Buchungs- und Stornobedingungen</button>.</p>
      <label style={{display:'flex',gap:11,alignItems:'flex-start',cursor:'pointer',lineHeight:1.6,marginTop:16,borderTop:'1px solid var(--line)',paddingTop:16}}>
        <input type="checkbox" checked={!!daten.storno} onChange={e=>setDaten({...daten,storno:e.target.checked})} style={{accentColor:'var(--ink)',marginTop:3}}/>
        <span style={{fontSize:12.5,color:'var(--ink-soft)'}}><span style={{color:'var(--ink)'}} aria-hidden="true">* </span>{TXT_STORNO(anz)}</span>
      </label>
      {err&&<div style={{fontSize:12,color:'#a33',marginTop:10,marginLeft:26}}>{err}</div>}
      <button type="button" onClick={()=>setModal('storno')} className="mg-btn mg-btn--ghost" style={{marginTop:16,height:42,fontSize:13}}>Bedingungen lesen</button>
    </div>

    <p className="mg-p" style={{fontSize:11.5,marginTop:16,lineHeight:1.7}}>Bei einer Online-Buchung steht dir ein 14-tägiges Widerrufsrecht zu; eine geleistete Anzahlung erstatten wir dann vollständig. Einzelheiten in der <button type="button" onClick={()=>setModal('widerruf')} style={{all:'unset',cursor:'pointer',color:'var(--ink)',textDecoration:'underline',textUnderlineOffset:3}}>Widerrufsbelehrung</button>.</p>
    <p className="mg-p" style={{fontSize:11.5,marginTop:10}}>* Pflichtfeld</p>
    {modal&&<RechtModal thema={modal} onClose={()=>setModal('')}/>}
  </div>;
}

function Aside({sel,addons,persons,total,staff,tag,slot,step,next,can,label,code}){
  if(!sel)return <div style={{border:'1px solid var(--line)',padding:'30px 28px'}}>
    <div className="mg-eyebrow">Deine Auswahl</div>
    <p className="mg-p" style={{fontSize:14,marginTop:14}}>Wähle links eine Behandlung – wir zeigen dir hier laufend deine Zusammenfassung.</p>
  </div>;
  return <div style={{border:'1px solid var(--line)'}}>
    <img loading="lazy" src={P+sel.img} alt="" style={{width:'100%',height:170,objectFit:'cover'}}/>
    <div style={{padding:'24px 26px'}}>
      <div className="mg-eyebrow">Deine Auswahl</div>
      <div style={{fontSize:18,marginTop:12,lineHeight:1.3}}>{sel.name}</div>
      <div className="mg-p" style={{fontSize:13.5,marginTop:5}}>{sel.kat} · {sel.dur}</div>
      <div style={{borderTop:'1px solid var(--line)',marginTop:18,paddingTop:14,display:'flex',flexDirection:'column',gap:8,fontSize:14}}>
        <div style={{display:'flex',justifyContent:'space-between'}}><span style={{color:'var(--ink-mute)'}}>Behandlung</span><span>{sel.plabel||sel.preis+' €'}</span></div>
        {ADDONS.filter(([n])=>addons.includes(n)).map(([n,p])=><div key={n} style={{display:'flex',justifyContent:'space-between'}}><span style={{color:'var(--ink-mute)'}}>{n}</span><span>+{p} €</span></div>)}
        {persons>1&&<div style={{display:'flex',justifyContent:'space-between'}}><span style={{color:'var(--ink-mute)'}}>Personen</span><span>× {persons}</span></div>}
        {staff&&<div style={{display:'flex',justifyContent:'space-between'}}><span style={{color:'var(--ink-mute)'}}>Bei</span><span>{staff}</span></div>}
        {tag&&slot&&<div style={{display:'flex',justifyContent:'space-between'}}><span style={{color:'var(--ink-mute)'}}>Termin</span><span>{fmtS(new Date(tag))}, {slot}</span></div>}
        {code&&<div style={{display:'flex',justifyContent:'space-between'}}><span style={{color:'var(--ink-mute)'}}>Gutschein {code}</span><span>−{eurF(total*persons-rabattWert(total*persons,code))} €</span></div>}
      </div>
      <div style={{borderTop:'1px solid var(--line)',marginTop:14,paddingTop:14,display:'flex',justifyContent:'space-between',fontSize:18}}><span>Gesamt</span><span>{code?<span><span style={{textDecoration:'line-through',color:'var(--ink-mute)',fontSize:14,marginRight:8}}>{total*persons} €</span>{eurF(rabattWert(total*persons,code))} €</span>:total*persons+' €'}</span></div>
      {code&&<p className="mg-p" style={{fontSize:12,marginTop:8}}>10-%-Willkommensgutschein angewendet – gilt für deine erste Behandlung, wird bei der Buchung geprüft.</p>}
      {anzPflicht(total*persons)&&<p className="mg-p" style={{fontSize:12,marginTop:10}}>Davon {ANZ_BETRAG} € Anzahlung bei der Buchung – wird angerechnet.</p>}
      <button className="mg-btn" onClick={next} style={{width:'100%',marginTop:18,opacity:can?1:.4,pointerEvents:can?'auto':'none'}}>{label}</button>
      {step<3&&<p className="mg-p" style={{fontSize:11.5,marginTop:10,textAlign:'center'}}>Noch unverbindlich – du bestätigst erst am Ende.</p>}
    </div>
  </div>;
}

/* Gemeinsame Huelle beider Wartescreens: links das Motiv, rechts der Text.
   Die Seite dahinter wird festgehalten, damit man nicht ins Leere scrollt. */
function LoaderShell({img,zoom,children}){
  window.useScrollLock(true);
  return <div style={{position:'fixed',inset:0,zIndex:120,background:'#fff',display:'flex',animation:'mg-fade .5s var(--ease) both'}}>
    <div className="mg-hide-m" style={{width:'46%',position:'relative',overflow:'hidden'}}>
      <img src={img} alt="" style={{width:'100%',height:'100%',objectFit:'cover',transform:zoom?'scale(1.06)':'scale(1)',transition:'transform 1.6s var(--ease)'}}/>
    </div>
    <div style={{flex:1,display:'flex',flexDirection:'column',justifyContent:'center',padding:'clamp(36px,6vw,90px)'}}>
      <img className="mg-load-img" src={img} alt="" style={{display:'none',width:'100%',maxWidth:340,height:170,objectFit:'cover',marginBottom:28,transform:zoom?'scale(1.03)':'scale(1)',transition:'transform 1.6s var(--ease)'}}/>
      {children}
    </div>
  </div>;
}

const Punkte=()=><div style={{display:'flex',gap:5}}>{[0,1,2].map(n=><span key={n} style={{width:6,height:6,borderRadius:'50%',background:'var(--ink)',animation:'mg-dot 1.3s '+(n*.18)+'s infinite'}}></span>)}</div>;

const Zitat=({z,delay})=>!z?null:<div style={{animation:'mg-rise .9s '+(delay||0)+'s var(--ease) both'}}>
  <blockquote className="mg-h2" style={{fontSize:'clamp(21px,2.4vw,32px)',fontWeight:200,lineHeight:1.4,margin:'26px 0 0',maxWidth:520}}>„{z.t}"</blockquote>
  {z.a&&<div className="mg-eyebrow" style={{marginTop:16}}>{z.a}</div>}
</div>;

function Loader({phase,sel,tag,slot,zitat}){
  const g=phase==='grats';
  return <LoaderShell img={P+sel.img} zoom={g}>
    {!g?<div key="c">
      <Punkte/>
      <Zitat z={zitat||{t:sel.quote}} delay={.15}/>
      <p className="mg-p" style={{marginTop:30,maxWidth:460,animation:'mg-rise .9s .9s var(--ease) both'}}>Einen Moment bitte – wir prüfen, ob <span style={{color:'var(--ink)'}}>{sel.name}</span> am {fmt(new Date(tag))} um {slot} Uhr noch frei ist.</p>
      <p className="mg-p" style={{fontSize:13,marginTop:12,animation:'mg-rise .9s 1.7s var(--ease) both'}}>Unsere Termine sind sehr gefragt – danke für deine Geduld.</p>
    </div>:<div key="g">
      <div className="mg-eyebrow" style={{animation:'mg-rise .8s var(--ease) both'}}>Verfügbar</div>
      <h2 className="mg-h2" style={{margin:'18px 0 0',animation:'mg-rise .8s .25s var(--ease) both'}}>Dein Wunschtermin<br/>ist frei.</h2>
      <p className="mg-p" style={{marginTop:20,fontSize:16,animation:'mg-rise .8s .6s var(--ease) both'}}>{fmt(new Date(tag))}, {slot} Uhr — wir halten ihn kurz für dich reserviert.</p>
      <div style={{width:0,height:1,background:'var(--ink)',marginTop:34,animation:'mg-line 1.2s .8s var(--ease) forwards'}}></div>
    </div>}
  </LoaderShell>;
}

/* Wartescreen nach dem Absenden der Buchung. Zwei Zustaende: erst das
   Eintragen, dann eine kurze Bestaetigung, bevor die Bestaetigungsseite
   uebernimmt. Bewusst ruhig gehalten – kein Konfetti, kein Haken-Icon. */
function BuchungsLoader({phase,sel,tag,slot,anz,name,zitat}){
  const g=phase==='gebucht';
  return <LoaderShell img={P+sel.img} zoom={g}>
    {!g?<div key="s">
      <Punkte/>
      <Zitat z={zitat} delay={.15}/>
      <p className="mg-p" style={{marginTop:30,maxWidth:460,animation:'mg-rise .9s .9s var(--ease) both'}}>Einen Moment – wir tragen deinen Termin ein{anz?' und bestätigen deine Anzahlung':''}.</p>
      <p className="mg-p" style={{fontSize:13,marginTop:12,animation:'mg-rise .9s 1.7s var(--ease) both'}}>Bitte schließe dieses Fenster nicht.</p>
    </div>:<div key="f">
      <div className="mg-eyebrow" style={{animation:'mg-rise .8s var(--ease) both'}}>Gebucht</div>
      <h2 className="mg-h2" style={{margin:'18px 0 0',animation:'mg-rise .8s .25s var(--ease) both'}}>Dein Termin steht{name?', '+name:''}.</h2>
      <p className="mg-p" style={{marginTop:20,fontSize:16,animation:'mg-rise .8s .6s var(--ease) both'}}>{fmt(new Date(tag))}, {slot} Uhr — wir freuen uns auf dich.</p>
      <div style={{width:0,height:1,background:'var(--ink)',marginTop:34,animation:'mg-line 1.2s .8s var(--ease) forwards'}}></div>
    </div>}
  </LoaderShell>;
}

function Done({sel,tag,slot,staff,daten,total,persons}){
  const ics='data:text/calendar;charset=utf-8,'+encodeURIComponent('BEGIN:VCALENDAR\nVERSION:2.0\nBEGIN:VEVENT\nSUMMARY:'+sel.name+' – La Vie Wildeshausen\nLOCATION:Burgstraße 7, 27793 Wildeshausen\nDESCRIPTION:Termin um '+slot+' Uhr\nEND:VEVENT\nEND:VCALENDAR');
  return <section className="mg-sec">
    <div className="mg-narrow" style={{maxWidth:900}}>
      <div className="mg-split" style={{border:'1px solid var(--line)'}}>
        <img loading="lazy" src={P+sel.img} alt="" style={{width:'100%',height:'100%',minHeight:320,objectFit:'cover'}}/>
        <div style={{padding:'clamp(36px,4vw,58px)'}}>
          <div className="mg-eyebrow">Bestätigt</div>
          <h1 className="mg-h2" style={{margin:'16px 0 0',fontSize:'clamp(26px,3vw,38px)'}}>Wir freuen uns auf dich{daten.name?', '+daten.name.split(' ')[0]:''}.</h1>
          <div style={{marginTop:26,borderTop:'1px solid var(--line)'}}>
            {[['Behandlung',sel.name],['Termin',fmt(new Date(tag))+', '+slot+' Uhr'],['Bei',staff==='Egal'?'nächste freie Mitarbeiterin':staff],[persons>1?'Personen':'Gesamt',persons>1?persons:total+' €']].map(([l,v])=><div key={l} style={{display:'flex',justifyContent:'space-between',gap:16,padding:'12px 0',borderBottom:'1px solid var(--line)',fontSize:14.5}}><span style={{color:'var(--ink-mute)'}}>{l}</span><span style={{textAlign:'right'}}>{v}</span></div>)}
          </div>
          <p className="mg-p" style={{fontSize:13.5,marginTop:18}}>Du erhältst gleich eine Bestätigung per E-Mail. Wir erinnern dich 24 Stunden vorher{daten.erinnerung&&daten.erinnerung!=='email'?' per '+(daten.erinnerung==='sms'?'SMS':'WhatsApp'):''}.</p>
          {(daten.m_email||daten.m_wa)&&<p className="mg-p" style={{fontSize:12.5,marginTop:10}}>Für {daten.m_email&&daten.m_wa?'Newsletter und WhatsApp':daten.m_email?'den Newsletter':'WhatsApp'} liegt noch eine Bestätigung von dir an – wir schicken dir gleich den Link. Erst danach hörst du von uns.</p>}
          <div style={{display:'flex',gap:12,marginTop:26,flexWrap:'wrap'}}>
            <a href={ics} className="mg-btn">Zum Kalender</a>
            <a href="index.html" className="mg-btn mg-btn--ghost">Zur Startseite</a>
          </div>
        </div>
      </div>
    </div>
  </section>;
}

/* Einwilligungsnachweis: WER hat WANN WOFÜR in WELCHER Textfassung zugestimmt.
   Ohne diesen Datensatz ist jede Einwilligung im Streitfall wertlos – die
   Beweislast liegt beim Studio (Art. 7 Abs. 1 DSGVO). Die IP-Adresse gehört
   dazu, lässt sich aber nur serverseitig sauber erfassen: Der Endpunkt muss
   sie beim Empfang selbst an den Datensatz hängen. */
function consentRecord(daten,ctx){
  const ts=new Date().toISOString();
  const e=(zweck,erteilt,text)=>({zweck,erteilt:!!erteilt,text,ts});
  return {
    version:CONSENT_VERSION, ts, quelle:'buchungsfunnel', url:location.href,
    person:{name:daten.name||'',email:daten.email||'',tel:daten.tel||''},
    eintraege:[
      e('datenschutz_kenntnisnahme',daten.ds,TXT_DS),
      e('stornobedingungen',daten.storno,TXT_STORNO(!!ctx.anzahlung)),
      e('gesundheitsdaten',daten.gesund,TXT_GESUND),
      ...KANAELE.map(([k])=>e('werbung_'+k.slice(2),daten[k],TXT_WERBUNG(k))),
      e('erinnerungskanal',true,'Terminerinnerung 24 h vorher per '+(daten.erinnerung||'email'))
    ],
    /* Vor der Bestätigung darf über diese Kanäle nichts Werbliches raus. */
    bestaetigung:{email:daten.m_email?'ausstehend':'entfällt',whatsapp:daten.m_wa?'ausstehend':'entfällt'},
    buchung:ctx
  };
}
function consentSenden(rec){
  const key='lavie-consent-queue';
  const q=JSON.parse(localStorage.getItem(key)||'[]');
  q.push(rec);localStorage.setItem(key,JSON.stringify(q));
  const api=window.LAVIE_API;
  if(!api){console.info('[La Vie] Kein Backend gesetzt – Einwilligung wartet lokal in der Warteschlange.');return Promise.resolve(null)}
  /* Gibt die Server-Antwort zurück ({ok,kundin,termin,kid}) – die Termin-UUID
     braucht der Anzahlungs-Schritt (Whop). Fehler ⇒ null, der
     Datensatz bleibt in der Warteschlange. */
  return fetch(api+'/consent',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(rec)})
    .then(r=>{if(!r.ok)throw new Error('HTTP '+r.status);return r.json()})
    .then(ant=>{
      const rest=JSON.parse(localStorage.getItem(key)||'[]').filter(x=>x.ts!==rec.ts);
      localStorage.setItem(key,JSON.stringify(rest));
      return ant;})
    .catch(err=>{console.warn('[La Vie] Einwilligung nicht übertragen, bleibt in der Warteschlange:',err.message);return null});
}

/* Anzahlung über Whop – bewusst "optional redirect": Erst wenn der Server
   die Buchung angenommen hat UND /api/anzahlung eine Checkout-URL liefert
   (d.h. der Whop-Zugang ist hinterlegt), wird umgeleitet. In jedem anderen
   Fall (503 = noch nicht konfiguriert, Netzfehler) läuft die Buchung wie
   bisher ohne Zahlung weiter – der Termin ist dann trotzdem angefragt. */
function anzahlungStarten(consentAntwort,ctx){
  const api=window.LAVIE_API;
  if(!api)return;
  consentAntwort.then(ant=>{
    if(!ant||!ant.termin)return null;
    return fetch(api+'/anzahlung',{method:'POST',headers:{'Content-Type':'application/json'},
      body:JSON.stringify({termin:ant.termin,email:ctx.email||'',behandlung:ctx.behandlung,datum:ctx.datum,uhrzeit:ctx.uhrzeit})})
      .then(r=>r.ok?r.json():null).catch(()=>null);
  }).then(a=>{if(a&&a.url)location.href=a.url}).catch(()=>{});
}

/* Rückkehr vom Whop-Checkout (?anzahlung=erfolgt|abgebrochen): Der Funnel-
   Zustand ist nach dem Redirect weg — der Termin ist aber längst angefragt
   (er entsteht VOR der Zahlung). Also keine leere Buchungsmaske zeigen,
   sondern eine ehrliche Bestätigung. */
function AnzahlungRueckkehr({status}){
  const ok=status==='erfolgt';
  return <section className="mg-sec">
    <div className="mg-wrap" style={{maxWidth:640,padding:'clamp(60px,10vw,120px) 0',textAlign:'left'}}>
      <div className="mg-eyebrow">{ok?'Anzahlung erhalten':'Zahlung abgebrochen'}</div>
      <h1 className="mg-h1" style={{margin:'18px 0 0',fontSize:'clamp(32px,4vw,54px)'}}>
        {ok?'Danke – dein Termin ist eingetragen.':'Deine Terminanfrage ist trotzdem angekommen.'}</h1>
      <p className="mg-p" style={{marginTop:20,maxWidth:520}}>
        {ok
          ?'Wir haben deine Anzahlung über 25 € erhalten – sie wird vollständig auf den Behandlungspreis angerechnet. Du bekommst in Kürze eine Bestätigung per E-Mail.'
          :'Die Zahlung wurde nicht abgeschlossen. Deine Terminanfrage liegt uns aber vor – wir melden uns bei dir wegen der Anzahlung, oder du rufst uns kurz an: 04431 7209569.'}</p>
      <div style={{marginTop:30}}>
        <a className="mg-btn" href="/">Zur Startseite</a>
      </div>
    </div>
  </section>;
}

function MgBuchung(){
  const anzStatus=new URLSearchParams(location.search).get('anzahlung');
  const [step,setStep]=useState(0);
  const [sel,setSel]=useState(null);
  const [addons,setAddons]=useState([]);
  const [persons,setPersons]=useState(1);
  const [staff,setStaff]=useState('');
  const [tag,setTag]=useState('');
  const [slot,setSlot]=useState('');
  const [daten,setDaten]=useState(GUTSCHEIN_CODE?{code:GUTSCHEIN_CODE}:{});
  const [phase,setPhase]=useState('');
  const [buchPhase,setBuchPhase]=useState('');
  const [zitat,setZitat]=useState(null);
  const [fin,setFin]=useState(false);
  const addSum=ADDONS.filter(([n])=>addons.includes(n)).reduce((s,[,p])=>s+p,0);
  const total=(sel?sel.preis:0)+addSum;
  const can=step===0?!!sel:step===1?!!(staff&&tag&&slot):true;
  /* Nach jedem Schrittwechsel an den Anfang.
     Wichtig: Das darf NICHT synchron im Klick-Handler passieren. Der
     Ladebildschirm sperrt das Scrollen und stellt beim Entsperren die alte
     Position wieder her (useScrollLock in mg-ui.jsx) – ein sofortiges
     scrollTo wuerde davon ueberschrieben, und man landete wieder unten.
     Als Effekt laeuft es garantiert nach dem Aufraeumen der Sperre. */
  useEffect(()=>{
    const id=requestAnimationFrame(()=>window.scrollTo({top:0,behavior:'smooth'}));
    return()=>cancelAnimationFrame(id);
  },[step,fin]);
  const next=()=>{
    if(step===0){setStep(1);return}
    if(step===1){
      /* Beim Pruefen darf auch die behandlungsbezogene Zeile drankommen. */
      setZitat(waehle([{t:sel.quote},...ZITATE]));
      setPhase('check');setTimeout(()=>setPhase('grats'),5000);setTimeout(()=>{setPhase('');setStep(2)},8000);return}
    if(step===2){if(window.__validate3&&window.__validate3())setStep(3);return}
    if(step===3){window.__pay&&window.__pay()}
  };
  if(anzStatus)return <div><window.MgNav/><AnzahlungRueckkehr status={anzStatus}/><window.MgFooter/></div>;
  if(fin)return <div><window.MgNav/><Done sel={sel} tag={tag} slot={slot} staff={staff} daten={daten} total={total*persons} persons={persons}/><window.MgFooter/></div>;
  return <div>
    <window.MgNav/>
    <section style={{borderBottom:'1px solid var(--line)',background:'var(--paper-2)'}}>
      <div className="mg-wrap" style={{padding:'clamp(40px,5vw,70px) 0'}}>
        <div className="mg-eyebrow">Online-Buchung</div>
        <h1 className="mg-h1" style={{margin:'16px 0 0',fontSize:'clamp(32px,4vw,54px)'}}>Dein Termin bei La Vie</h1>
        <p className="mg-p" style={{marginTop:16,maxWidth:520}}>In vier Schritten gebucht – rund um die Uhr, mit sofortiger Bestätigung.</p>
      </div>
    </section>
    <section className="mg-sec" style={{paddingTop:'clamp(30px,3.4vw,48px)'}}>
      <div className="mg-wrap">
        <Steps step={step}/>
        <div style={{display:'grid',gridTemplateColumns:'minmax(0,1fr) 340px',gap:'clamp(28px,4vw,64px)',alignItems:'start'}} className="mg-book">
          <div key={step} style={{animation:'mg-rise .6s var(--ease) both'}}>
            {step===0&&<Step1 sel={sel} setSel={setSel} addons={addons} setAddons={setAddons} persons={persons} setPersons={setPersons}/>}
            {step===1&&<Step2 staff={staff} setStaff={setStaff} tag={tag} setTag={setTag} slot={slot} setSlot={setSlot}/>}
            {step===2&&<Step3 daten={daten} setDaten={setDaten}/>}
            {step===3&&<Step4 sel={sel} addons={addons} staff={staff} tag={tag} slot={slot} daten={daten} setDaten={setDaten} persons={persons} total={total} onDone={()=>{
              const antwort=consentSenden(consentRecord(daten,{behandlung:sel.name,datum:tag,uhrzeit:slot,mitarbeiterin:staff,personen:persons,
                summe:rabattWert(total*persons,codeNorm(daten.code)),summe_vor_rabatt:total*persons,gutschein:codeNorm(daten.code)||undefined,
                /* Behandlungsdauer für die Kalender-Blockung; bei Erstbesuch
                   legt der Server automatisch 30 Beratungs-Minuten drauf. */
                minuten:(()=>{const m=(String(sel.dur||'').match(/\d+/g)||[]).map(Number);return m.length?Math.max(...m):60})(),
                anzahlung:anzPflicht(total*persons)?ANZ_BETRAG:0}));
              if(anzPflicht(total*persons))anzahlungStarten(antwort,{email:daten.email,behandlung:sel.name,datum:tag,uhrzeit:slot});
              /* Erst der Wartescreen, dann die Bestaetigungsseite.
                 Hier bewusst nur die Zitate der Kundin, keine Kategoriezeile. */
              setZitat(waehle(ZITATE));
              setBuchPhase('senden');
              setTimeout(()=>setBuchPhase('gebucht'),2300);
              setTimeout(()=>{setBuchPhase('');setFin(true)},4100);
            }}/>}
            <div style={{display:'flex',justifyContent:'space-between',gap:14,marginTop:36,borderTop:'1px solid var(--line)',paddingTop:24}}>
              {step>0?<button className="mg-btn mg-btn--ghost" onClick={()=>setStep(step-1)}>Zurück</button>:<span></span>}
              <button className="mg-btn" onClick={next} style={{opacity:can?1:.4,pointerEvents:can?'auto':'none'}}>{step===0?'Weiter zum Termin':step===1?'Verfügbarkeit prüfen':step===2?'Weiter zur Bestätigung':(anzPflicht(total*persons)?ANZ_BETRAG+' € anzahlen & buchen':'Verbindlich buchen')}</button>
            </div>
          </div>
          <aside className="mg-hide-m" style={{position:'sticky',top:104}}>
            <Aside sel={sel} addons={addons} persons={persons} total={total} staff={staff} tag={tag} slot={slot} step={step} next={next} can={can} code={codeNorm(daten.code)} label={step===0?'Weiter zum Termin':step===1?'Verfügbarkeit prüfen':step===2?'Weiter zur Bestätigung':(anzPflicht(total*persons)?ANZ_BETRAG+' € anzahlen & buchen':'Verbindlich buchen')}/>
            <div style={{marginTop:16,border:'1px solid var(--line)',padding:'20px 22px'}}>
              <div className="mg-eyebrow">Lieber persönlich?</div>
              <p className="mg-p" style={{fontSize:13.5,margin:'10px 0 14px'}}>Ruf uns an – wir finden gemeinsam den passenden Termin.</p>
              <a href="tel:+4944317209569" className="mg-link" style={{fontSize:12}}>04431 720 95 69</a>
            </div>
          </aside>
        </div>
      </div>
    </section>
    {sel&&step<2&&<div className="mg-sticky">
      <div style={{display:'flex',alignItems:'center',gap:14,background:'#fff',borderTop:'1px solid var(--line)',padding:'12px clamp(16px,4vw,32px)'}}>
        <img loading="lazy" src={P+sel.img} alt="" style={{width:44,height:44,objectFit:'cover',flexShrink:0}}/>
        <div style={{flex:1,minWidth:0}}>
          <div style={{fontSize:14.5,whiteSpace:'nowrap',overflow:'hidden',textOverflow:'ellipsis'}}>{sel.name}</div>
          <div style={{fontSize:12.5,color:'var(--ink-mute)'}}>{sel.dur} · {total*persons} €</div>
        </div>
        <button className="mg-btn" style={{height:42}} onClick={next} disabled={!can}>{step===0?'Weiter':'Prüfen'}</button>
      </div>
    </div>}
    {phase&&<Loader phase={phase} sel={sel} tag={tag} slot={slot} zitat={zitat}/>}
    {buchPhase&&<BuchungsLoader phase={buchPhase} sel={sel} tag={tag} slot={slot} anz={anzPflicht(total*persons)} name={(daten.name||'').split(' ')[0]} zitat={zitat}/>}
    <window.MgFooter/>
    <window.MgCookie/>
  </div>;
}
window.MgBuchung=MgBuchung;
