/* ============================================================
   MO RAN SPA — Customer Portal app (B2C, mobile-first)
   Requires portal-store.js (window.PortalStore) + portal.css
   ============================================================ */
const { useState, useEffect, useRef, useCallback } = React;
const PS = window.PortalStore;

/* ---- icons ---- */
const IP = {
  home:"M3 11l9-8 9 8M5 10v10a1 1 0 001 1h4v-6h4v6h4a1 1 0 001-1V10",
  cal:"M4 5h16v16H4zM4 9h16M8 3v4M16 3v4M9 13h2v2H9z",
  gift:"M20 12v8a1 1 0 01-1 1H5a1 1 0 01-1-1v-8M2 7h20v5H2zM12 7v14M12 7S10 3 7.5 4 9 7 12 7zM12 7s2-4 4.5-3S15 7 12 7z",
  user:"M12 12a4 4 0 100-8 4 4 0 000 8zM4 21c0-4 3.6-7 8-7s8 3 8 7",
  check:"M20 6L9 17l-5-5", chev:"M9 18l6-6-6-6", back:"M15 18l-6-6 6-6",
  clock:"M12 7v5l3 2M12 22a10 10 0 100-20 10 10 0 000 20z", star:"M12 3l2.9 6 6.1.5-4.6 4 1.4 6-5.8-3.5L6.2 19.5l1.4-6L3 9.5 9.1 9z",
  spark:"M12 3v4M12 17v4M3 12h4M17 12h4M6 6l2.5 2.5M18 18l-2.5-2.5M18 6l-2.5 2.5M6 18l2.5-2.5",
  share:"M4 12v8a1 1 0 001 1h14a1 1 0 001-1v-8M16 6l-4-4-4 4M12 2v14",
  copy:"M9 9h10v10H9zM5 15V5h10", phone:"M5 3h4l2 5-3 2a14 14 0 006 6l2-3 5 2v4a2 2 0 01-2 2A17 17 0 013 5a2 2 0 012-2",
  logout:"M9 21H5a2 2 0 01-2-2V5a2 2 0 012-2h4M16 17l5-5-5-5M21 12H9", edit:"M12 20h9M16.5 3.5a2.1 2.1 0 013 3L7 19l-4 1 1-4z",
  leaf:"M11 21c-4 0-8-3-8-9 7 0 11 2 11 9M21 3c0 9-4 13-10 13",
  ticket:"M3 8a2 2 0 012-2h14a2 2 0 012 2 2 2 0 000 4 2 2 0 00-2 2v0a2 2 0 01-2 2H5a2 2 0 01-2-2 2 2 0 000-4 2 2 0 002-2z"
};
function Ic({n, ...r}){ return <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.9" strokeLinecap="round" strokeLinejoin="round" {...r}><path d={IP[n]}/></svg>; }

const fmt = (n)=>PS.meta.formatVnd(n);
const dows=['Sun','Mon','Tue','Wed','Thu','Fri','Sat'], mons=['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
function parseD(s){ const [y,m,d]=s.split('-').map(Number); return new Date(y,m-1,d); }
function dLabel(s){ const d=parseD(s); return { dow:dows[d.getDay()], num:d.getDate(), mon:mons[d.getMonth()] }; }

let toastFn=()=>{};
function PToast(){ const [t,setT]=useState(null); useEffect(()=>{ toastFn=(m,k)=>{ setT({m,k}); setTimeout(()=>setT(null),2400); }; },[]); return t?<div className={"ptoast "+(t.k||"")}>{t.m}</div>:null; }
const toast=(m,k)=>toastFn(m,k);

/* ============================================================ LOGIN (OTP) */
function Login({ theme, onDone }){
  const [step,setStep]=useState('phone');
  const [phone,setPhone]=useState('');
  const [code,setCode]=useState(['','','','']);
  const [busy,setBusy]=useState(false);
  const [demo,setDemo]=useState('');
  const refs=[useRef(),useRef(),useRef(),useRef()];

  const sendCode=async()=>{
    if(phone.replace(/\D/g,'').length<8){ toast('Enter a valid phone number','err'); return; }
    setBusy(true); const r=await PS.requestOtp(phone); setBusy(false);
    setDemo(r.demoCode); setStep('code'); setTimeout(()=>refs[0].current&&refs[0].current.focus(),100);
    toast('Code sent via '+(r.channel==='zalo'?'Zalo':'SMS'),'ok');
  };
  const setDigit=(i,v)=>{ if(!/^\d?$/.test(v))return; const c=[...code]; c[i]=v; setCode(c); if(v&&i<3)refs[i+1].current.focus(); if(i===3&&v)setTimeout(()=>verify(c.join('')),120); };
  const verify=async(full)=>{
    const val=full||code.join(''); if(val.length<4)return;
    setBusy(true); const r=await PS.verifyOtp(phone,val); setBusy(false);
    if(r.ok){ onDone(); } else { toast(r.error||'Invalid code','err'); setCode(['','','','']); refs[0].current&&refs[0].current.focus(); }
  };

  return (<div className="login">
    <div className="login-hero">
      <img src={theme.logo} alt=""/>
      <h1>{theme.name}</h1>
      <p>{step==='phone'?'Sign in to book & track your rewards':'Enter the 4-digit code we sent you'}</p>
    </div>
    <div className="login-body">
      {step==='phone' && (<>
        <div><label className="label">Phone number</label>
          <input className="inp" inputMode="tel" placeholder="e.g. 090 123 4567" value={phone} onChange={e=>setPhone(e.target.value)} onKeyDown={e=>e.key==='Enter'&&sendCode()}/></div>
        <button className="btn btn-pri btn-block" onClick={sendCode} disabled={busy}>{busy?<span className="spin"/>:<><Ic n="phone"/>Send code via Zalo / SMS</>}</button>
        <p className="small muted center">No password needed. We'll text you a one-time code.</p>
      </>)}
      {step==='code' && (<>
        <div className="otp-boxes">{code.map((c,i)=>(<input key={i} ref={refs[i]} inputMode="numeric" maxLength={1} value={c} onChange={e=>setDigit(i,e.target.value)} onKeyDown={e=>{if(e.key==='Backspace'&&!c&&i>0)refs[i-1].current.focus();}}/>))}</div>
        {demo && <div className="demo-hint">Demo mode — your code is <b>{demo}</b></div>}
        <button className="btn btn-pri btn-block" onClick={()=>verify()} disabled={busy}>{busy?<span className="spin"/>:'Verify & continue'}</button>
        <button className="btn btn-ghost btn-block btn-sm" onClick={()=>{setStep('phone');setCode(['','','','']);}}>← Change number</button>
      </>)}
    </div>
    <div className="powered">Powered by <b>Admin_Spa</b></div>
  </div>);
}

/* ============================================================ HOME */
function Home({ me, appts, go }){
  const next=PS.meta.nextTier(me.points), curTier=PS.meta.tierFor(me.points);
  const prog=next? Math.min(100, Math.round(((me.points-curTier.min)/(next.min-curTier.min))*100)) : 100;
  return (<div className="scr">
    <div className="brandbar">
      <div className="row">
        <img className="brandlogo" src={window.__theme.logo} alt=""/>
        <div className="grow"><h1>{window.__theme.name}</h1><div className="sub">{window.__theme.subtitle}</div></div>
      </div>
      <div className="greet"><div className="sub">Welcome back</div><div className="hi">{me.name||'Guest'} 🌿</div></div>
    </div>
    <div className="pad stack" style={{marginTop:-14}}>
      <div className="loyalty">
        <span className="tier"><Ic n="leaf" style={{width:13,height:13}}/>{me.tier} member</span>
        <div className="pts">{me.points} <span style={{fontSize:'1rem',opacity:.8}}>pts</span></div>
        {next? <div className="small" style={{opacity:.9}}>{next.min-me.points} pts to <b>{next.name}</b></div> : <div className="small" style={{opacity:.9}}>Top tier reached ✦</div>}
        <div className="pbar"><i style={{width:prog+'%'}}/></div>
        {me.pkg && <div className="pkg"><div><div className="tiny" style={{opacity:.8,textTransform:'uppercase',letterSpacing:'.1em'}}>Your package</div>{me.pkg.name}</div><div style={{textAlign:'right'}}><b>{me.pkg.left}</b><div className="tiny" style={{opacity:.8}}>sessions left</div></div></div>}
      </div>

      <button className="btn btn-pri btn-block cta-pulse" onClick={()=>go('book')}><Ic n="cal"/>Book an appointment</button>

      <div>
        <div className="row between" style={{marginBottom:8}}><div className="sec-t" style={{margin:0}}>Upcoming</div>{appts.upcoming.length>0&&<button className="small" style={{color:'var(--accent)',fontWeight:700}} onClick={()=>go('book')}>+ New</button>}</div>
        {appts.upcoming.length===0 ? (
          <div className="card card-p center" style={{padding:'26px 18px'}}><div className="muted small">No upcoming visits yet.</div><button className="btn btn-ghost btn-sm" style={{marginTop:12}} onClick={()=>go('book')}>Book your first →</button></div>
        ) : appts.upcoming.map(a=>(<ApptRow key={a.id} a={a}/>))}
      </div>

      <div className="offer" onClick={()=>go('rewards')}>
        <div className="ic"><Ic n="spark"/></div>
        <div className="grow"><b style={{fontSize:'.95rem'}}>You have {me.points} points</b><div className="small muted">Redeem a free Foot + Jade Ritual at 600 pts</div></div>
        <Ic n="chev" style={{color:'var(--mute)'}}/>
      </div>
    </div>
  </div>);
}
function ApptRow({ a, onCancel }){
  const L=dLabel(a.date); const st={confirmed:['ok','Confirmed'],completed:['done','Completed'],cancelled:['cancel','Cancelled']}[a.status]||['ok',a.status];
  return (<div className="card" style={{marginBottom:10}}><div className="appt">
    <div className="when"><div className="d">{L.num}</div><div className="m">{L.mon}</div></div>
    <div className="info grow"><b>{a.svcName}</b><div className="meta">{L.dow} · {a.time} · {a.staffName}</div>
      <div className="row" style={{gap:6,marginTop:6}}><span className={"badge "+st[0]}>{st[1]}</span>{a.usedPackage&&<span className="badge pkg">Package</span>}</div></div>
    {onCancel && a.status==='confirmed' && <button className="btn btn-ghost btn-sm" onClick={()=>onCancel(a)}>Cancel</button>}
  </div></div>);
}

/* ============================================================ BOOKING (3 steps) */
function Booking({ me, onBooked, presetSvc }){
  const [step,setStep]=useState(1);
  const [services,setServices]=useState(null);
  const [therapists,setTherapists]=useState(null);
  const [svc,setSvc]=useState(null);
  const [staff,setStaff]=useState(null); // null = any
  const [date,setDate]=useState(PS.meta.daysFromNow(0));
  const [slots,setSlots]=useState(null);
  const [time,setTime]=useState(null);
  const [usePkg,setUsePkg]=useState(false);
  const [busy,setBusy]=useState(false);
  const [done,setDone]=useState(null);

  useEffect(()=>{ PS.services().then(s=>{ setServices(s); if(presetSvc){ const m=s.find(x=>x.id===presetSvc); if(m){ setSvc(m); setStep(2); } } }); },[]);
  useEffect(()=>{ if(svc){ setTherapists(null); PS.therapists(svc.id).then(setTherapists); setUsePkg(!!(svc.pkgEligible&&me.pkg&&me.pkg.left>0)); } },[svc]);
  useEffect(()=>{ if(step===3&&svc){ setSlots(null); setTime(null); PS.slots(date,svc.id,staff?staff.id:null).then(setSlots); } },[step,date,staff]);

  const days=Array.from({length:10},(_,i)=>PS.meta.daysFromNow(i));

  const confirm=async()=>{
    setBusy(true);
    const r=await PS.book({ svcId:svc.id, staffId:staff?staff.id:null, staffName:staff?staff.name:'Any therapist', price:svc.price, date, time, usePackage:usePkg, refCode:window.__ref });
    setBusy(false);
    if(r.ok){ setDone({ svcName:svc.name, staffName:staff?staff.name:'Any therapist', date, time, usedPackage:r.usedPackage, price:svc.price }); }
    else if(r.error==='slot_taken'){ toast('That slot was just taken','err'); setSlots(s=>(s||[]).filter(x=>x!==time)); setTime(null); }
    else toast('Could not book — try again','err');
  };

  if(done){ const L=dLabel(done.date); return (<div className="scr"><div className="success">
    <div className="checkmark"><Ic n="check"/></div>
    <div className="sec-t" style={{fontSize:'1.7rem'}}>You're booked!</div>
    <p className="muted" style={{margin:'0 0 6px'}}>A confirmation has been sent to your Zalo.</p>
    <div className="card card-p" style={{width:'100%',textAlign:'left',marginTop:8}}>
      <div className="summary" style={{background:'transparent',border:'none',padding:0}}>
        <div className="li"><span className="k">Service</span><span className="v">{done.svcName}</span></div>
        <div className="li"><span className="k">When</span><span className="v">{L.dow} {L.num} {L.mon} · {done.time}</span></div>
        <div className="li"><span className="k">Therapist</span><span className="v">{done.staffName}</span></div>
        <div className="li"><span className="k">{done.usedPackage?'Payment':'Price'}</span><span className="v">{done.usedPackage?'Package session':fmt(done.price)}</span></div>
      </div>
    </div>
    <button className="btn btn-pri btn-block" style={{marginTop:18}} onClick={()=>onBooked()}>Done</button>
  </div></div>); }

  return (<div className="scr">
    <div className="pad" style={{paddingBottom:8}}>
      <div className="row between" style={{marginBottom:14}}>
        {step>1? <button className="btn btn-ghost btn-sm" style={{minHeight:38,padding:'0 10px'}} onClick={()=>setStep(step-1)}><Ic n="back"/></button> : <div style={{width:38}}/>}
        <div className="sec-t" style={{margin:0,whiteSpace:'nowrap'}}>{['Choose a service','Choose a therapist','Pick a time'][step-1]}</div>
        <div style={{width:38}}/>
      </div>
      <div className="steps">{[1,2,3].map(s=>(<span key={s} className={"st"+(s<=step?' on':'')}/>))}</div>
    </div>

    <div className="pad stack" style={{paddingTop:6}}>
      {step===1 && (services? services.map(s=>(
        <button key={s.id} className={"pick"+(svc&&svc.id===s.id?' sel':'')} onClick={()=>{setSvc(s);setStaff(null);setStep(2);}}>
          <div className="tx"><b>{s.name}</b><div className="meta">{s.dur} min · {s.group}</div></div>
          {s.pkgEligible&&me.pkg&&me.pkg.left>0 ? <span className="pkgtag">Package OK</span> : null}
          <div className="price">{fmt(s.price)}</div>
        </button>
      )) : <SkelList/>)}

      {step===2 && (<>
        <button className={"pick"+(staff===null?' sel':'')} onClick={()=>{setStaff(null);setStep(3);}}>
          <div className="ava">★</div><div className="tx"><b>Any therapist</b><div className="meta">Earliest availability · recommended</div></div><Ic n="chev" style={{color:'var(--mute)'}}/>
        </button>
        {therapists? therapists.map(t=>(
          <button key={t.id} className={"pick"+(staff&&staff.id===t.id?' sel':'')} onClick={()=>{setStaff(t);setStep(3);}}>
            <div className="ava">{t.name[0]}</div><div className="tx"><b>{t.name}</b><div className="meta"><Ic n="star" style={{width:12,height:12,display:'inline',verticalAlign:'-1px',color:'var(--accent)'}}/> {t.rating.toFixed(1)} rating</div></div><Ic n="chev" style={{color:'var(--mute)'}}/>
          </button>
        )) : <SkelList/>}
      </>)}

      {step===3 && (<>
        <div><label className="label">Date</label>
          <div className="days">{days.map(d=>{ const L=dLabel(d); return (<button key={d} className={"day"+(date===d?' sel':'')} onClick={()=>setDate(d)}><div className="dow">{L.dow}</div><div className="num">{L.num}</div></button>); })}</div></div>
        <div><label className="label">Available times {staff?('· '+staff.name):'· any therapist'}</label>
          {slots===null ? <div className="slots">{Array.from({length:8}).map((_,i)=>(<div key={i} className="skel" style={{height:42}}/>))}</div>
            : slots.length===0 ? <div className="empty" style={{padding:'24px'}}><div className="muted small">No open times on this day.{staff&&' Try “Any therapist” or another date.'}</div></div>
            : <div className="slots">{slots.map(t=>(<button key={t} className={"slot"+(time===t?' sel':'')} onClick={()=>setTime(t)}>{t}</button>))}</div>}
        </div>
        {svc&&svc.pkgEligible&&me.pkg&&me.pkg.left>0 && (
          <div className="card card-p row between"><div><b className="small">Use a package session</b><div className="tiny muted">{me.pkg.left} left · no charge today</div></div>
            <button className="toggle" data-on={usePkg?'1':'0'} onClick={()=>setUsePkg(v=>!v)}/></div>
        )}
        {time && (<div className="summary">
          <div className="li"><span className="k">Service</span><span className="v">{svc.name}</span></div>
          <div className="li"><span className="k">Therapist</span><span className="v">{staff?staff.name:'Any therapist'}</span></div>
          <div className="li"><span className="k">When</span><span className="v">{dLabel(date).dow} {dLabel(date).num} {dLabel(date).mon} · {time}</span></div>
          <div className="li"><span className="k">{usePkg?'Payment':'Price'}</span><span className="v">{usePkg?'Package session':fmt(svc.price)}</span></div>
        </div>)}
      </>)}
    </div>

    {step===3 && (<div className="actionbar"><button className="btn btn-acc btn-block" disabled={!time||busy} onClick={confirm}>{busy?<span className="spin"/>:<>Confirm booking{time?' · '+time:''}</>}</button></div>)}
  </div>);
}
function SkelList(){ return <>{Array.from({length:5}).map((_,i)=>(<div key={i} className="skel" style={{height:64}}/>))}</>; }

/* ============================================================ REWARDS */
function Rewards({ me, setMe }){
  const [rewards,setRewards]=useState(null);
  const [ref,setRef]=useState(null);
  const [busy,setBusy]=useState(null);
  useEffect(()=>{ PS.rewards().then(setRewards); PS.referral().then(setRef); },[]);
  const curTier=PS.meta.tierFor(me.points), next=PS.meta.nextTier(me.points);
  const prog=next? Math.min(100,Math.round(((me.points-curTier.min)/(next.min-curTier.min))*100)):100;

  const redeem=async(r)=>{
    if(me.points<r.points){ toast(`Need ${r.points-me.points} more pts`,'err'); return; }
    setBusy(r.id); const res=await PS.redeem(r.id); setBusy(null);
    if(res.ok){ setMe(m=>({...m,points:res.points})); setRewards(rs=>rs.map(x=>x.id===r.id?{...x,redeemed:true}:x)); toast('Redeemed! Voucher '+res.voucher,'ok'); }
    else toast('Could not redeem','err');
  };
  const shareRef=()=>{ const url=location.origin+location.pathname+'?ref='+ref.code; if(navigator.share){ navigator.share({title:'Mo Ran Spa',text:'Book with my code for a treat 🌿',url}); } else { navigator.clipboard&&navigator.clipboard.writeText(url); toast('Referral link copied','ok'); } };

  return (<div className="scr">
    <div className="brandbar"><div className="eyebrow" style={{color:'var(--accent-soft,#C58B58)'}}>Rewards</div><div className="greet" style={{marginTop:6}}><div className="hi" style={{fontSize:'1.5rem'}}>Points & perks</div></div></div>
    <div className="pad stack" style={{marginTop:-14}}>
      <div className="loyalty">
        <span className="tier"><Ic n="leaf" style={{width:13,height:13}}/>{me.tier}</span>
        <div className="pts">{me.points}<span style={{fontSize:'1rem',opacity:.8}}> pts</span></div>
        {next? <div className="small" style={{opacity:.9}}>{next.min-me.points} pts to {next.name}</div>:<div className="small" style={{opacity:.9}}>Top tier ✦</div>}
        <div className="pbar"><i style={{width:prog+'%'}}/></div>
      </div>

      {me.pkg && <div className="card card-p row between"><div className="row" style={{gap:10}}><div className="ava" style={{background:'#EAF0FB',color:'#3A6FA0'}}><Ic n="ticket"/></div><div><b className="small">{me.pkg.name}</b><div className="tiny muted">Prepaid sessions</div></div></div><div style={{textAlign:'right'}}><b style={{fontFamily:'var(--serif)',fontSize:'1.6rem',color:'var(--brand)'}}>{me.pkg.left}</b><div className="tiny muted">left</div></div></div>}

      <div className="sec-t" style={{marginBottom:0}}>Redeem points</div>
      {rewards? rewards.map(r=>(<div key={r.id} className="card"><div className="reward">
        <div className="rp">{r.points}<span className="u">points</span></div>
        <div className="grow"><b className="small">{r.title}</b>{me.points>=r.points? <div className="tiny" style={{color:'var(--ok)'}}>You can redeem this</div>:<div className="tiny muted">{r.points-me.points} pts to go</div>}</div>
        <button className="btn btn-sm" style={{background:r.redeemed?'var(--line)':me.points>=r.points?'var(--accent)':'var(--line)',color:r.redeemed||me.points<r.points?'var(--mute)':'#fff'}} disabled={r.redeemed||me.points<r.points||busy===r.id} onClick={()=>redeem(r)}>{busy===r.id?'…':r.redeemed?'Redeemed':'Redeem'}</button>
      </div></div>)) : <SkelList/>}

      <div className="sec-t" style={{marginBottom:0,marginTop:6}}>Invite friends</div>
      {ref && <div className="card card-p stack">
        <div className="small muted">Share your code — you both earn <b style={{color:'var(--accent)'}}>60 pts</b> on their first visit.</div>
        <div className="refcode"><b>{ref.code}</b><button className="btn btn-sm" style={{background:'rgba(255,255,255,.18)',color:'#fff'}} onClick={()=>{navigator.clipboard&&navigator.clipboard.writeText(ref.code);toast('Code copied','ok');}}><Ic n="copy"/>Copy</button></div>
        <button className="btn btn-pri btn-block" onClick={shareRef}><Ic n="share"/>Share via Zalo</button>
        <div className="tiny muted center">{ref.invited} friends joined · {ref.earnedPoints} pts earned</div>
      </div>}
    </div>
  </div>);
}

/* ============================================================ PROFILE */
function Profile({ me, setMe, appts, onCancel, onLogout }){
  const [editing,setEditing]=useState(false);
  const [name,setName]=useState(me.name);
  const save=()=>{ const m={...me,name}; setMe(m); PS.saveMe(m); setEditing(false); toast('Profile saved','ok'); };
  const setConsent=(k)=>{ const m={...me,consent:{...me.consent,[k]:!me.consent[k]}}; setMe(m); PS.saveMe(m); };
  return (<div className="scr">
    <div className="brandbar"><div className="eyebrow" style={{color:'#C58B58'}}>My account</div>
      <div className="row" style={{marginTop:12}}>
        <div className="ava" style={{width:52,height:52,fontSize:'1.3rem'}}>{(me.name||'G')[0].toUpperCase()}</div>
        <div className="grow">{editing? <input className="inp" style={{padding:'8px 12px'}} value={name} onChange={e=>setName(e.target.value)} placeholder="Your name"/> : <div className="hi" style={{fontSize:'1.4rem'}}>{me.name||'Add your name'}</div>}<div className="sub">{me.phone}</div></div>
        {editing? <button className="btn btn-sm" style={{background:'#fff',color:'var(--brand)'}} onClick={save}>Save</button> : <button className="btn btn-sm" style={{background:'rgba(255,255,255,.18)',color:'#fff'}} onClick={()=>setEditing(true)}><Ic n="edit"/></button>}
      </div>
    </div>
    <div className="pad stack">
      <div className="row" style={{gap:10}}>
        <div className="card card-p grow center"><div style={{fontFamily:'var(--serif)',fontSize:'1.8rem',color:'var(--brand)',fontWeight:600}}>{me.points}</div><div className="tiny muted">Points</div></div>
        <div className="card card-p grow center"><div style={{fontFamily:'var(--serif)',fontSize:'1.8rem',color:'var(--brand)',fontWeight:600}}>{me.tier}</div><div className="tiny muted">Tier</div></div>
        <div className="card card-p grow center"><div style={{fontFamily:'var(--serif)',fontSize:'1.8rem',color:'var(--brand)',fontWeight:600}}>{appts.history.filter(a=>a.status==='completed').length}</div><div className="tiny muted">Visits</div></div>
      </div>

      <div className="sec-t" style={{marginBottom:0}}>Visit history</div>
      {appts.upcoming.map(a=>(<ApptRow key={a.id} a={a} onCancel={onCancel}/>))}
      {appts.history.length? appts.history.map(a=>(<ApptRow key={a.id} a={a}/>)) : appts.upcoming.length?null:<div className="empty"><div className="ic"><Ic n="cal"/></div><div className="small">No visits yet</div></div>}

      <div className="sec-t" style={{marginBottom:0,marginTop:6}}>Preferences</div>
      <div className="card">
        <div className="row between card-p" style={{borderBottom:'1px solid var(--line-2)'}}><div><b className="small">Marketing messages</b><div className="tiny muted">Offers & seasonal promos via Zalo</div></div><button className="toggle" data-on={me.consent.marketing?'1':'0'} onClick={()=>setConsent('marketing')}/></div>
        <div className="row between card-p"><div><b className="small">Photo consent</b><div className="tiny muted">Allow before/after photos</div></div><button className="toggle" data-on={me.consent.photo?'1':'0'} onClick={()=>setConsent('photo')}/></div>
      </div>

      <button className="btn btn-ghost btn-block" onClick={onLogout} style={{marginTop:6,color:'var(--danger)'}}><Ic n="logout"/>Sign out</button>
      <div className="powered">Powered by <b>Admin_Spa</b> · Member since {me.joined}</div>
    </div>
  </div>);
}

/* ============================================================ ROOT */
function Portal(){
  const theme=PS.theme();
  useEffect(()=>{ const r=document.documentElement.style;
    r.setProperty('--brand',theme.brand); r.setProperty('--accent',theme.accent); r.setProperty('--brand-ink',theme.brandInk);
    window.__theme=theme;
    const q=new URLSearchParams(location.search); window.__ref=q.get('ref')||null; window.__presetSvc=q.get('svc')||null;
  },[]);
  const [authed,setAuthed]=useState(PS.isAuthed());
  const [tab,setTab]=useState('home');
  const [me,setMe]=useState(null);
  const [appts,setAppts]=useState({upcoming:[],history:[]});
  const [loading,setLoading]=useState(true);

  const refresh=useCallback(async()=>{ const [m,a]=await Promise.all([PS.me(),PS.appointments()]); setMe(m); setAppts(a); setLoading(false); },[]);
  useEffect(()=>{ if(authed) refresh(); },[authed,refresh]);

  const cancel=async(a)=>{ if(!confirm('Cancel this appointment?'))return; const r=await PS.cancel(a.id); if(r.ok){ toast('Appointment cancelled','ok'); refresh(); } };
  const logout=()=>{ PS.logout(); setAuthed(false); setTab('home'); };

  if(!authed) return (<div className="phone"><Login theme={theme} onDone={()=>setAuthed(true)}/><PToast/></div>);
  if(loading||!me) return (<div className="phone"><div className="scr pad stack"><div className="skel" style={{height:120,marginTop:10}}/><div className="skel" style={{height:150}}/><div className="skel" style={{height:60}}/></div></div>);

  return (<div className="phone">
    {tab==='home' && <Home me={me} appts={appts} go={setTab}/>}
    {tab==='book' && <Booking me={me} presetSvc={window.__presetSvc} onBooked={()=>{ window.__presetSvc=null; refresh(); setTab('home'); }}/>}
    {tab==='rewards' && <Rewards me={me} setMe={setMe}/>}
    {tab==='me' && <Profile me={me} setMe={setMe} appts={appts} onCancel={cancel} onLogout={logout}/>}
    <nav className="tabbar">
      {[['home','Home','home'],['book','Book','cal'],['rewards','Rewards','gift'],['me','Me','user']].map(([k,l,ic])=>(
        <button key={k} className={"tab"+(tab===k?' active':'')} onClick={()=>{ if(k==='book')window.__presetSvc=null; setTab(k); }}><Ic n={ic}/>{l}</button>))}
    </nav>
    <PToast/>
  </div>);
}

ReactDOM.createRoot(document.getElementById('portal-root')).render(<Portal/>);
