// BookUno Web — Customer Account, Business Onboarding (desktop), Business Dashboard

/* ─── Customer Account (shell + bookings) ─────────────────── */
function readBookunoLocalBookings() {
  try {
    return JSON.parse(window.localStorage.getItem("bookuno.local_customer_bookings") || "[]");
  } catch {
    return [];
  }
}

function RescheduleModal({ booking, onClose, onSaved }) {
  const svcName = booking.services?.name || booking.service_name || 'Booking';
  const [date, setDate] = React.useState('');
  const [time, setTime] = React.useState('');
  const [saving, setSaving] = React.useState(false);
  const [err, setErr] = React.useState('');

  async function handleSave() {
    if (!date || !time) { setErr('Please pick a date and time.'); return; }
    const startsAt = new Date(date + 'T' + time + ':00').toISOString();
    setSaving(true);
    setErr('');
    try {
      await window.BOOKUNO_API.updateBooking(booking.id, { starts_at: startsAt });
      onSaved(booking.id, startsAt);
      onClose();
    } catch (e) {
      setErr(e?.message || 'Could not reschedule. Please try again.');
      setSaving(false);
    }
  }

  return (
    <div style={{ position: 'fixed', inset: 0, background: 'rgba(10,8,24,0.6)', zIndex: 9999, display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 16 }}
      onClick={function(e) { if (e.target === e.currentTarget) onClose(); }}>
      <div style={{ background: '#fff', borderRadius: 20, padding: '32px 28px', maxWidth: 400, width: '100%', boxShadow: '0 24px 60px rgba(0,0,0,0.2)' }}>
        <h3 style={{ fontSize: 20, fontWeight: 800, marginBottom: 6 }}>Reschedule booking</h3>
        <p style={{ fontSize: 13.5, color: 'var(--muted)', marginBottom: 22 }}>{svcName}</p>
        <label style={{ fontSize: 13, fontWeight: 700, display: 'block', marginBottom: 7 }}>New date</label>
        <div className="input" style={{ marginBottom: 14 }}>
          <input type="date" value={date} onChange={function(e) { setDate(e.target.value); }} style={{ flex: 1, border: 0, outline: 0, background: 'transparent', fontSize: 14.5 }}/>
        </div>
        <label style={{ fontSize: 13, fontWeight: 700, display: 'block', marginBottom: 7 }}>New time</label>
        <div className="input" style={{ marginBottom: 22 }}>
          <input type="time" value={time} onChange={function(e) { setTime(e.target.value); }} style={{ flex: 1, border: 0, outline: 0, background: 'transparent', fontSize: 14.5 }}/>
        </div>
        {err && <div style={{ fontSize: 13, color: '#C53030', fontWeight: 600, marginBottom: 14 }}>{err}</div>}
        <div style={{ display: 'flex', gap: 10 }}>
          <button className="btn outline" style={{ flex: 1 }} onClick={onClose} disabled={saving}>Close</button>
          <button className="btn primary" style={{ flex: 1 }} onClick={handleSave} disabled={saving}>{saving ? 'Saving…' : 'Confirm'}</button>
        </div>
      </div>
    </div>
  );
}

function AccountPlaceholderPanel({ icon, title, body, cta, onCta }) {
  return (
    <div style={{ padding: '64px 32px', textAlign: 'center', borderRadius: 20, background: 'var(--bg-2)', border: '1px dashed var(--line)' }}>
      <div style={{ fontSize: 40, marginBottom: 14 }}>{icon}</div>
      <div style={{ fontWeight: 800, fontSize: 18, marginBottom: 8 }}>{title}</div>
      <p style={{ color: 'var(--muted)', fontSize: 14, maxWidth: 300, margin: '0 auto 20px' }}>{body}</p>
      {cta && <button className="btn primary" onClick={onCta}>{cta}</button>}
    </div>
  );
}

function DeleteAccountSection() {
  const [step, setStep] = React.useState('idle'); // idle | confirm | deleting | done | error
  const [errMsg, setErrMsg] = React.useState('');

  async function handleDelete() {
    setStep('deleting');
    setErrMsg('');
    try {
      await window.BOOKUNO_API.deleteAccount();
      setStep('done');
      // Sign out session and navigate home after short delay
      await window.BOOKUNO_API.signOut().catch(() => {});
      setTimeout(() => { window.location.hash = '#/'; window.location.reload(); }, 1800);
    } catch (e) {
      setErrMsg(e?.message || 'Could not delete account. Please try again or contact support@bookuno.co.uk.');
      setStep('error');
    }
  }

  if (step === 'done') {
    return (
      <div style={{ marginTop: 24, padding: 20, borderRadius: 16, background: '#f0fff4', border: '1.5px solid #9ae6b4', textAlign: 'center' }}>
        <div style={{ fontSize: 26, marginBottom: 8 }}>✅</div>
        <div style={{ fontWeight: 800, fontSize: 14 }}>Account deleted</div>
        <p style={{ fontSize: 13, color: 'var(--muted)', marginTop: 6 }}>Your account and data have been permanently removed. Redirecting…</p>
      </div>
    );
  }

  return (
    <div style={{ marginTop: 24, padding: 20, borderRadius: 16, background: '#fff8f8', border: '1.5px solid #ffd0d0' }}>
      <div style={{ fontWeight: 800, fontSize: 14, color: 'var(--red, #c53030)', marginBottom: 6 }}>Delete account</div>
      <p style={{ fontSize: 13, color: 'var(--muted)', marginBottom: 14 }}>Permanently delete your account and all associated data. This cannot be undone.</p>

      {step === 'error' && (
        <div style={{ fontSize: 13, color: '#c53030', background: '#fff5f5', borderRadius: 10, padding: '10px 14px', marginBottom: 14 }}>{errMsg}</div>
      )}

      {step === 'confirm' ? (
        <div>
          <p style={{ fontSize: 13, fontWeight: 700, color: '#c53030', marginBottom: 14 }}>Are you sure? This will permanently erase your bookings, saved businesses, and account. It cannot be undone.</p>
          <div style={{ display: 'flex', gap: 10 }}>
            <button className="btn outline sm" onClick={() => setStep('idle')} style={{ flex: 1 }}>Cancel</button>
            <button className="btn sm" onClick={handleDelete} disabled={step === 'deleting'}
              style={{ flex: 1, background: '#c53030', color: '#fff', borderColor: '#c53030' }}>
              {step === 'deleting' ? 'Deleting…' : 'Yes, delete my account'}
            </button>
          </div>
        </div>
      ) : (
        <button className="btn outline sm" onClick={() => setStep('confirm')}
          style={{ color: 'var(--red, #c53030)', borderColor: 'var(--red, #c53030)' }}>
          Delete my account
        </button>
      )}
    </div>
  );
}

function PageAccount({ logoVariant = 'default' }) {
  const [panel, setPanel] = React.useState('book');
  const [tab, setTab] = React.useState('upcoming');
  const [bookings, setBookings] = React.useState(null);
  const [localBookings] = React.useState(readBookunoLocalBookings);
  const [rescheduleBooking, setRescheduleBooking] = React.useState(null);
  const mainRef = React.useRef(null);

  function handleNav(id) {
    if (id !== 'profile') {
      setPanel(id);
      // On mobile (<= 900px) scroll the main panel into view after switching
      if (window.innerWidth <= 900) {
        requestAnimationFrame(() => {
          mainRef.current?.scrollIntoView({ behavior: 'smooth', block: 'start' });
        });
      }
    }
  }

  React.useEffect(() => {
    if (window.BOOKUNO_API?.listCustomerBookings) {
      window.BOOKUNO_API.listCustomerBookings().then(data => setBookings(Array.isArray(data) ? data : [])).catch(() => setBookings([]));
    } else if (window.BOOKUNO_API?.listBookings) {
      window.BOOKUNO_API.listBookings().then(data => setBookings(Array.isArray(data) ? data : [])).catch(() => setBookings([]));
    } else {
      setBookings([]);
    }
  }, []);

  const now = new Date();
  const allBookings = bookings || [];

  const upcoming = allBookings.filter(b => b.status !== 'cancelled' && new Date(b.date || b.starts_at) >= now);
  const past = allBookings.filter(b => b.status !== 'cancelled' && new Date(b.date || b.starts_at) < now);
  const cancelled = allBookings.filter(b => b.status === 'cancelled');
  const displayed = tab === 'upcoming' ? upcoming : tab === 'past' ? past : cancelled;

  async function handleCancel(b) {
    const ok = window.confirm('Cancel this booking? This cannot be undone.');
    if (!ok) return;
    try {
      await window.BOOKUNO_API.cancelBooking(b.id, 'Customer cancelled');
      setBookings(prev => (prev || []).map(x => x.id === b.id ? { ...x, status: 'cancelled' } : x));
    } catch (e) {
      alert(e?.message || 'Could not cancel. Please try again.');
    }
  }

  function handleRescheduleSaved(id, startsAt) {
    setBookings(prev => (prev || []).map(x => x.id === id ? { ...x, starts_at: startsAt, date: new Date(startsAt) } : x));
  }

  function fmtDate(iso) {
    const d = new Date(iso);
    return { day: d.toLocaleDateString('en-GB', { weekday: 'short' }).toUpperCase(), date: d.getDate(), month: d.toLocaleDateString('en-GB', { month: 'short' }).toUpperCase(), time: d.toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit' }) };
  }

  function statusTag(s) { return s === 'confirmed' ? 'ok' : s === 'cancelled' ? 'red' : 'amber'; }

  return (
    <div className="web">
      <TopNav active="discover" logoVariant={logoVariant} loggedIn/>
      <div className="shell">
        <CustomerSide active={panel} onNav={handleNav}/>
        <main ref={mainRef} style={{ minWidth: 0 }}>

          {/* ── Overview panel ── */}
          {panel === 'home' && (
            <div>
              <div className="micro" style={{ color: 'var(--pri)', marginBottom: 4 }}>ACCOUNT</div>
              <h1 style={{ fontSize: 28, fontWeight: 800, letterSpacing: -0.025, marginTop: 4, marginBottom: 24 }}>Overview</h1>
              <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(160px,1fr))', gap: 14, marginBottom: 28 }}>
                {[
                  { label: 'Upcoming', value: upcoming.length + localBookings.length, icon: '📅', onClick: () => setPanel('book') },
                  { label: 'Past', value: past.length, icon: '🕓', onClick: () => setPanel('book') },
                  { label: 'Cancelled', value: cancelled.length, icon: '✕', onClick: () => setPanel('book') },
                ].map(s => (
                  <button key={s.label} onClick={s.onClick} style={{ padding: '20px 18px', borderRadius: 18, background: 'var(--bg-2)', border: '1.5px solid var(--line)', textAlign: 'left', cursor: 'pointer' }}>
                    <div style={{ fontSize: 26 }}>{s.icon}</div>
                    <div style={{ fontWeight: 900, fontSize: 28, marginTop: 8 }}>{s.value}</div>
                    <div style={{ fontSize: 12, fontWeight: 700, color: 'var(--muted)', marginTop: 2 }}>{s.label}</div>
                  </button>
                ))}
              </div>
              <div style={{ display: 'flex', gap: 10, flexWrap: 'wrap' }}>
                <button className="btn primary" onClick={() => window.location.hash = '/booking/start'}><Ic.plus style={{ width: 14, height: 14 }}/> Book a service</button>
                <button className="btn outline" onClick={() => setPanel('book')}>View all bookings</button>
              </div>
            </div>
          )}

          {/* ── My bookings panel ── */}
          {panel === 'book' && (<>
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 20 }}>
            <div>
              <div className="micro" style={{ color: 'var(--pri)' }}>YOUR CALENDAR</div>
              <h1 style={{ fontSize: 28, fontWeight: 800, letterSpacing: -0.025, marginTop: 4 }}>My bookings</h1>
            </div>
            <button className="btn primary" onClick={() => window.location.hash = '/booking/start'}><Ic.plus style={{ width: 16, height: 16 }}/> Book a service</button>
          </div>

          <div className="pillbar" style={{ display: 'inline-flex', padding: 5, gap: 4, background: 'var(--chip)', borderRadius: 12, marginBottom: 20 }}>
            {[['upcoming', `Upcoming · ${upcoming.length + localBookings.length}`], ['past', `Past · ${past.length}`], ['cancelled', 'Cancelled']].map(([id, label]) => (
              <button key={id} onClick={() => setTab(id)} style={{ padding: '8px 14px', borderRadius: 8, background: tab === id ? '#fff' : 'transparent', boxShadow: tab === id ? 'var(--sh-1)' : 'none', fontWeight: 700, fontSize: 13, color: tab === id ? 'var(--ink)' : 'var(--muted)', border: 'none', cursor: 'pointer' }}>{label}</button>
            ))}
          </div>

          {/* LocalStorage bookings (from public booking flow) */}
          {tab === 'upcoming' && localBookings.length > 0 && (
            <div style={{ display: 'grid', gap: 14, marginBottom: 18 }}>
              {localBookings.map((b, i) => (
                <Rv key={b.id || i} delay={i*40}>
                  <div className="card" style={{ padding: 18, border: '2px solid var(--pri)', background: 'linear-gradient(135deg, var(--pri-50), #fff)' }}>
                    <div style={{ display: 'flex', justifyContent: 'space-between', gap: 14, alignItems: 'flex-start', flexWrap: 'wrap' }}>
                      <div>
                        <span className="badge amber dot">{b.status || 'Pending confirmation'}</span>
                        <h3 style={{ fontSize: 18, fontWeight: 900, letterSpacing: '-0.02em', marginTop: 10 }}>{b.serviceName || 'Booking request'}</h3>
                        <p style={{ color: 'var(--muted)', marginTop: 4, fontSize: 13 }}>{b.businessName || 'Bookuno business'} · {b.dateLabel || 'Today'} at {b.timeLabel || '14:30'}</p>
                      </div>
                      <div style={{ textAlign: 'right' }}>
                        <div style={{ fontWeight: 900, fontSize: 18 }}>{b.priceLabel || '£0'}</div>
                        <div style={{ color: 'var(--muted)', fontSize: 12, marginTop: 3 }}>{b.durationLabel || ''}</div>
                      </div>
                    </div>
                  </div>
                </Rv>
              ))}
            </div>
          )}

          {/* Real bookings from Supabase */}
          {bookings === null ? (
            <div style={{ padding: 40, textAlign: 'center', color: 'var(--muted)' }}>Loading…</div>
          ) : displayed.length === 0 && !(tab === 'upcoming' && localBookings.length > 0) ? (
            <div style={{ padding: '48px 32px', textAlign: 'center', borderRadius: 20, background: 'var(--bg-2)', border: '1px dashed var(--line)' }}>
              <div style={{ fontSize: 32, marginBottom: 12 }}>📅</div>
              <div style={{ fontWeight: 700, marginBottom: 6 }}>No {tab} bookings</div>
              {tab === 'upcoming' && <button className="btn primary" style={{ marginTop: 12 }} onClick={() => window.location.hash = '/booking/start'}>Book a service</button>}
            </div>
          ) : (
            <div style={{ display: 'grid', gap: 14 }}>
              {displayed.map((b, i) => {
                const dt = fmtDate(b.date || b.starts_at);
                const dur = b.duration_minutes ? `${b.duration_minutes} min` : '';
                const svcName = b.services?.name || b.service_name || 'Booking';
                const bizName = b.businesses?.name || b.business_name || '';
                return (
                  <Rv key={b.id} delay={i*50}>
                    <div className="card" style={{ padding: 0, overflow: 'hidden' }}>
                      <div style={{ padding: 18, display: 'flex', gap: 18, alignItems: 'center' }}>
                        <Cover seed={i} h={88} r={14} style={{ width: 78, padding: 0, flexShrink: 0, alignItems: 'center', justifyContent: 'center', flexDirection: 'column' }}>
                          <div style={{ color: '#fff', fontWeight: 700, fontSize: 11, opacity: 0.85, letterSpacing: 0.05 }}>{dt.day}</div>
                          <div style={{ color: '#fff', fontWeight: 800, fontSize: 28, letterSpacing: -0.02, lineHeight: 1 }}>{dt.date}</div>
                          <div style={{ color: '#fff', fontWeight: 700, fontSize: 9.5, opacity: 0.85, marginTop: 2 }}>{dt.month}</div>
                        </Cover>
                        <div style={{ flex: 1, minWidth: 0 }}>
                          <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 2 }}>
                            <span className={`badge ${statusTag(b.status)} dot`}>{b.status || 'pending'}</span>
                            <span style={{ fontSize: 12, color: 'var(--muted)' }}>{dt.time}{dur ? ` · ${dur}` : ''}</span>
                          </div>
                          <div style={{ fontWeight: 800, fontSize: 17, letterSpacing: -0.015, marginTop: 4 }}>{svcName}</div>
                          <div style={{ fontSize: 13, color: 'var(--muted)', marginTop: 2 }}>{bizName}</div>
                        </div>
                        <div style={{ textAlign: 'right', flexShrink: 0 }}>
                          {b.total_cents > 0 && <div style={{ fontWeight: 800, fontSize: 17 }}>£{(b.total_cents/100).toFixed(0)}</div>}
                          <div style={{ display: 'flex', gap: 8, marginTop: 8 }}>
                            {tab === 'upcoming' && (
                              <>
                                <button className="btn outline sm" style={{ color: 'var(--red, #e53e3e)', borderColor: 'var(--red, #e53e3e)' }} onClick={() => handleCancel(b)}>Cancel</button>
                                <button className="btn outline sm" onClick={() => setRescheduleBooking(b)}>Reschedule</button>
                              </>
                            )}
                          </div>
                        </div>
                      </div>
                    </div>
                  </Rv>
                );
              })}
            </div>
          )}

          <div style={{ marginTop: 40, padding: 32, borderRadius: 20, background: 'linear-gradient(135deg, var(--pri-50), #fff)', border: '1px dashed var(--pri-200)', display: 'flex', alignItems: 'center', gap: 18, flexWrap: 'wrap' }}>
            <div style={{ width: 56, height: 56, borderRadius: 16, background: 'var(--pri)', color: '#fff', display: 'grid', placeItems: 'center' }}>
              <Ic.gift style={{ width: 26, height: 26 }}/>
            </div>
            <div style={{ flex: 1, minWidth: 200 }}>
              <div style={{ fontWeight: 800, fontSize: 16 }}>Rebook in one tap</div>
              <div style={{ fontSize: 13, color: 'var(--muted)', marginTop: 2 }}>Find businesses near you with availability this week.</div>
            </div>
            <button className="btn primary" onClick={() => window.location.hash = '/search'}>See available →</button>
          </div>
          </>)}

          {/* ── Messages panel ── */}
          {panel === 'msg' && (
            <div>
              <div className="micro" style={{ color: 'var(--pri)', marginBottom: 4 }}>INBOX</div>
              <h1 style={{ fontSize: 28, fontWeight: 800, letterSpacing: -0.025, marginTop: 4, marginBottom: 24 }}>Messages</h1>
              <AccountPlaceholderPanel icon="💬" title="No messages yet" body="When you message a business or they confirm your booking, conversations will appear here."/>
            </div>
          )}

          {/* ── Reviews panel ── */}
          {panel === 'rev' && (
            <div>
              <div className="micro" style={{ color: 'var(--pri)', marginBottom: 4 }}>YOUR REVIEWS</div>
              <h1 style={{ fontSize: 28, fontWeight: 800, letterSpacing: -0.025, marginTop: 4, marginBottom: 24 }}>Reviews</h1>
              <AccountPlaceholderPanel icon="⭐" title="No reviews yet" body="After a completed booking you'll be invited to leave a review. It helps other customers find great businesses." cta="Find a business" onCta={() => window.location.hash = '/search'}/>
            </div>
          )}

          {/* ── Payments panel ── */}
          {panel === 'pay' && (
            <div>
              <div className="micro" style={{ color: 'var(--pri)', marginBottom: 4 }}>ACCOUNT</div>
              <h1 style={{ fontSize: 28, fontWeight: 800, letterSpacing: -0.025, marginTop: 4, marginBottom: 24 }}>Payments</h1>
              <div style={{ padding: 24, borderRadius: 18, background: 'var(--bg-2)', border: '1.5px solid var(--line)', marginBottom: 16 }}>
                <div style={{ fontWeight: 800, fontSize: 15, marginBottom: 8 }}>Payment methods</div>
                <p style={{ fontSize: 13, color: 'var(--muted)', marginBottom: 16 }}>No payment methods saved. Most Bookuno businesses accept payment directly — no card details needed to request a booking.</p>
                <div style={{ padding: '12px 16px', borderRadius: 12, background: '#fff', border: '1.5px dashed var(--line)', fontSize: 13, color: 'var(--muted)', textAlign: 'center' }}>Card saving coming soon</div>
              </div>
              <div style={{ padding: 24, borderRadius: 18, background: 'var(--bg-2)', border: '1.5px solid var(--line)' }}>
                <div style={{ fontWeight: 800, fontSize: 15, marginBottom: 8 }}>Billing history</div>
                <p style={{ fontSize: 13, color: 'var(--muted)' }}>No payments recorded yet.</p>
              </div>
            </div>
          )}

          {/* ── Privacy & data panel ── */}
          {panel === 'privacy' && (
            <div>
              <div className="micro" style={{ color: 'var(--pri)', marginBottom: 4 }}>ACCOUNT</div>
              <h1 style={{ fontSize: 28, fontWeight: 800, letterSpacing: -0.025, marginTop: 4, marginBottom: 24 }}>Privacy & data</h1>
              {[
                { title: 'Your data', body: 'Bookuno stores your name, email, and booking history to run your account. We never sell your personal data.' },
                { title: 'Marketing', body: 'You may receive booking reminders and occasional offers from businesses you have booked with. Unsubscribe at any time via email.' },
                { title: 'Cookies', body: 'We use essential cookies to keep you signed in and remember your preferences.', link: { label: 'Cookie policy', href: '/privacy.html#cookies' } },
              ].map(item => (
                <div key={item.title} style={{ padding: '18px 20px', borderRadius: 16, background: 'var(--bg-2)', border: '1.5px solid var(--line)', marginBottom: 12 }}>
                  <div style={{ fontWeight: 800, fontSize: 14, marginBottom: 6 }}>{item.title}</div>
                  <p style={{ fontSize: 13, color: 'var(--muted)', marginBottom: item.link ? 10 : 0 }}>{item.body}</p>
                  {item.link && <a href={item.link.href} style={{ fontSize: 12, fontWeight: 700, color: 'var(--pri)' }}>{item.link.label} →</a>}
                </div>
              ))}
              <div style={{ display: 'flex', gap: 10, flexWrap: 'wrap', marginTop: 20 }}>
                <a href="/privacy.html" className="btn outline sm">Privacy policy</a>
                <a href="/terms.html" className="btn outline sm">Terms of service</a>
              </div>
              <DeleteAccountSection/>
            </div>
          )}

        </main>
      </div>
      <Footer logoVariant={logoVariant}/>
      {rescheduleBooking && (
        <RescheduleModal
          booking={rescheduleBooking}
          onClose={() => setRescheduleBooking(null)}
          onSaved={handleRescheduleSaved}
        />
      )}
    </div>
  );
}

/* ─── Business Onboarding (desktop, split layout) ─────────── */

// Business types are driven by the full service taxonomy (window.SERVICE_GROUPS,
// defined in web/components.jsx) so a provider of any local service can sign up.
// The chosen id is passed through as business_type; lib/bookuno-api
// normalizeBusinessType() maps it to the coarse enum (unknown → general_service),
// and the granular services they pick are saved as their `services` rows.
const BIZ_TYPES = (window.SERVICE_GROUPS || [])
  .map(g => ({ id: g.id, label: g.name, icon: g.icon, services: g.services || [], cat: g.cat || '' }))
  .concat([{ id: 'other', label: 'Something else', icon: '🏢', services: [], cat: '' }]);

// Curated services for common sub-trades that live *inside* a broader group
// (e.g. a barber is under "Hair & Beauty"). When the owner searched one of
// these terms, seed its services instead of the group's generic salon list.
const SUBTRADE_SERVICES = {
  barber: [{ name:'Skin fade', price:24, duration:45 },{ name:'Cut & style', price:22, duration:45 },{ name:'Beard trim', price:12, duration:20 },{ name:'Hot towel shave', price:20, duration:30 },{ name:'Kids cut', price:14, duration:30 }],
};

// Curated starter services (good default price/duration) for the highest-demand
// groups. Any group without a curated entry derives its suggestions from the
// taxonomy's own service list (see the effect that builds `services`).
const SUGGESTED_SERVICES = {
  hair_beauty:      [{ name:'Cut & blow dry',price:45,duration:60},{ name:'Colour',price:85,duration:120},{ name:'Highlights',price:95,duration:150},{ name:'Trim',price:25,duration:30},{ name:'Keratin treatment',price:150,duration:180}],
  nails:            [{ name:'Manicure',price:25,duration:45},{ name:'Pedicure',price:35,duration:60},{ name:'Gel nails',price:40,duration:75},{ name:'Acrylic set',price:55,duration:90},{ name:'Nail art',price:15,duration:30}],
  wellbeing:        [{ name:'Swedish massage',price:60,duration:60},{ name:'Deep tissue',price:70,duration:60},{ name:'Hot stone',price:80,duration:75},{ name:'Facial',price:55,duration:60},{ name:'Couples massage',price:120,duration:60}],
  fitness:          [{ name:'1-to-1 session',price:50,duration:60},{ name:'Assessment',price:30,duration:45},{ name:'Group class',price:15,duration:45},{ name:'Online coaching',price:40,duration:60}],
  cleaning:         [{ name:'Regular clean',price:60,duration:120},{ name:'Deep clean',price:120,duration:240},{ name:'End of tenancy',price:200,duration:300},{ name:'Oven clean',price:50,duration:90}],
  other:            [{ name:'Consultation',price:0,duration:60},{ name:'Standard session',price:50,duration:60},{ name:'Premium session',price:100,duration:90}],
};

const DEFAULT_HOURS = [
  { day:'Monday',    open:true,  opens:'09:00', closes:'17:00' },
  { day:'Tuesday',   open:true,  opens:'09:00', closes:'17:00' },
  { day:'Wednesday', open:true,  opens:'09:00', closes:'17:00' },
  { day:'Thursday',  open:true,  opens:'09:00', closes:'17:00' },
  { day:'Friday',    open:true,  opens:'09:00', closes:'17:00' },
  { day:'Saturday',  open:true,  opens:'09:00', closes:'14:00' },
  { day:'Sunday',    open:false, opens:'09:00', closes:'17:00' },
];

const STEP_LABELS = [
  'What kind of business',
  'The basics',
  'Where you work',
  'Your services',
  'Opening hours',
  'Team & rules',
  'Pick a plan',
  'Create password',
];

function OnboardingSidebar({ step, bizType }) {
  const bizLabel = BIZ_TYPES.find(b => b.id === bizType)?.label || 'business';
  return (
    <aside style={{ background: 'linear-gradient(180deg, #1A0E4A 0%, #2B1373 100%)', color: '#fff', padding: '32px 24px', position: 'relative', overflow: 'hidden', display: 'flex', flexDirection: 'column' }}>
      <div className="blob" style={{ width: 240, height: 240, background: '#FF8FCB', top: 200, right: -80, opacity: 0.3, position: 'absolute', borderRadius: '50%' }}/>
      <div style={{ position: 'relative', flex: 1 }}>
        <div style={{ fontSize: 10.5, fontWeight: 800, letterSpacing: 0.08, opacity: 0.7, textTransform: 'uppercase' }}>BUSINESS SETUP</div>
        <h2 style={{ fontSize: 24, fontWeight: 800, letterSpacing: -0.025, marginTop: 6, lineHeight: 1.15 }}>Let's get your {bizLabel} live.</h2>
        <p style={{ opacity: 0.75, fontSize: 13.5, marginTop: 10 }}>5 minutes. 3 months free, cancel anytime.</p>

        <div role="list" aria-label={`Setup progress — step ${step} of ${STEP_LABELS.length}`} style={{ marginTop: 32, display: 'grid', gap: 2 }}>
          {STEP_LABELS.map((label, i) => {
            const idx = i + 1;
            const done = idx < step;
            const active = idx === step;
            return (
              <div key={i} role="listitem" aria-current={active ? 'step' : undefined} style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '9px 6px', opacity: done ? 0.65 : active ? 1 : 0.45 }}>
                <div style={{ width: 26, height: 26, borderRadius: '50%', background: active ? '#fff' : done ? 'rgba(255,255,255,0.25)' : 'rgba(255,255,255,0.08)', color: active ? '#2B1373' : '#fff', display: 'grid', placeItems: 'center', fontSize: 11.5, fontWeight: 800, flexShrink: 0 }}>
                  {done ? <Ic.check style={{ width: 12, height: 12 }}/> : idx}
                </div>
                <span style={{ fontSize: 13, fontWeight: active ? 700 : 500 }}>{label}</span>
              </div>
            );
          })}
        </div>
      </div>
      <div style={{ position: 'relative', marginTop: 32, padding: 16, borderRadius: 14, background: 'rgba(255,255,255,0.1)', backdropFilter: 'blur(8px)' }}>
        <div style={{ fontSize: 12.5, opacity: 0.85 }}>Need help?</div>
        <div style={{ fontWeight: 800, fontSize: 14, marginTop: 2 }}>help@bookuno.co.uk</div>
      </div>
    </aside>
  );
}

function StepActions({ step, totalSteps, onBack, onContinue, continueLabel, loading, disabled }) {
  return (
    <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginTop: 36 }}>
      {step > 1
        ? <button className="btn outline" onClick={onBack} disabled={loading}><Ic.chevL style={{ width: 16, height: 16 }}/> Back</button>
        : <a href="#/auth" style={{ fontSize: 13.5, color: 'var(--muted)', fontWeight: 600, textDecoration: 'none' }}>Already have an account? <span style={{ color: 'var(--pri)', fontWeight: 700 }}>Sign in</span></a>
      }
      <button className="btn primary lg" onClick={onContinue} disabled={loading || disabled}>
        {loading ? 'Saving…' : (continueLabel || (step < totalSteps ? 'Continue →' : 'Finish'))}
      </button>
    </div>
  );
}

function FieldError({ msg, id }) {
  if (!msg) return null;
  return <div id={id} role="alert" style={{ fontSize: 12.5, color: 'var(--red, #e53e3e)', fontWeight: 600, marginTop: 5 }}>{msg}</div>;
}

// Currency symbol for the owner's own service prices (what THEY charge their
// customers), derived from the country picked in step 3. The platform plan
// itself is billed in GBP by Stripe regardless — that price stays shown in £.
const COUNTRY_CURRENCY = { GB: '£', IE: '€', US: '$', CA: 'C$', AU: 'A$' };
function currencySymbolFor(country) {
  return COUNTRY_CURRENCY[country] || '£';
}

function PageBizOnboarding({ logoVariant = 'default' }) {
  const totalSteps = 8;
  // Monetization flow switch (set in bookuno-config.js):
  //   'after_value' (default) — reach the live storefront/dashboard first, then
  //                             start the trial (capture the card) from a
  //                             contextual go-live prompt.
  //   'at_signup'             — legacy: capture the card via Stripe Checkout at
  //                             the end of onboarding.
  // Lets the card-after-value change be reverted or A/B'd without a code deploy.
  const cardAtSignup = ((window.BOOKUNO_CONFIG && window.BOOKUNO_CONFIG.cardCapture) || 'after_value') === 'at_signup';
  const [step, setStep] = React.useState(1);
  const [errors, setErrors] = React.useState({});
  const [loading, setLoading] = React.useState(false);
  const [session, setSession] = React.useState(null);

  // Step 1
  const [bizType, setBizType] = React.useState('');
  const [bizSearch, setBizSearch] = React.useState('');
  const [subTrade, setSubTrade] = React.useState(''); // e.g. 'barber' within Hair & Beauty
  // Step 2
  const [bizName, setBizName] = React.useState('');
  const [ownerName, setOwnerName] = React.useState('');
  const [email, setEmail] = React.useState('');
  const [phone, setPhone] = React.useState('');
  // Step 3
  const [address, setAddress] = React.useState('');
  const [city, setCity] = React.useState('');
  const [postcode, setPostcode] = React.useState('');
  const [country, setCountry] = React.useState('GB');
  // Step 4 — services list: { name, duration, price, selected }
  const [services, setServices] = React.useState([]);
  // Step 5 — opening hours
  const [hours, setHours] = React.useState(DEFAULT_HOURS.map(h => ({ ...h })));
  // Step 6
  const [teamSize, setTeamSize] = React.useState('1');
  const [advanceNotice, setAdvanceNotice] = React.useState('none');
  const [maxAdvance, setMaxAdvance] = React.useState('1_month');
  // Step 7
  const [plan, setPlan] = React.useState('yearly');
  // Step 8
  const [password, setPassword] = React.useState('');
  const [confirmPassword, setConfirmPassword] = React.useState('');

  // ── Analytics ───────────────────────────────────────────────────────────────
  const track = React.useCallback((event, props) => {
    try { window.BOOKUNO_ANALYTICS && window.BOOKUNO_ANALYTICS.track(event, props); } catch { /* never break the wizard */ }
  }, []);

  // Fire once on entry, and on every step view so per-step drop-off is visible.
  React.useEffect(() => { track('onboarding_started'); }, [track]);
  React.useEffect(() => {
    track('onboarding_step_viewed', { step, step_label: STEP_LABELS[step - 1] || null, biz_type: bizType || null });
  }, [step, track]); // eslint-disable-line

  // ── Draft persistence ──────────────────────────────────────────────────────
  // The whole wizard is React-local, so a refresh or "Save & exit" used to lose
  // everything. Persist all non-secret fields to localStorage and rehydrate on
  // return. Passwords are never written to storage.
  const DRAFT_KEY = 'bookuno.onboarding.draft.v1';
  const hydratedRef = React.useRef(false);
  const skipNextSuggest = React.useRef(false);

  React.useEffect(() => {
    try {
      const raw = localStorage.getItem(DRAFT_KEY);
      const d = raw ? JSON.parse(raw) : null;
      if (d && typeof d === 'object') {
        if (d.step) setStep(d.step);
        if (d.bizType) setBizType(d.bizType);
        if (d.bizName) setBizName(d.bizName);
        if (d.ownerName) setOwnerName(d.ownerName);
        if (d.email) setEmail(d.email);
        if (d.phone) setPhone(d.phone);
        if (d.address) setAddress(d.address);
        if (d.city) setCity(d.city);
        if (d.postcode) setPostcode(d.postcode);
        if (d.country) setCountry(d.country);
        if (Array.isArray(d.services) && d.services.length) { setServices(d.services); skipNextSuggest.current = true; }
        if (Array.isArray(d.hours) && d.hours.length) setHours(d.hours);
        if (d.teamSize) setTeamSize(d.teamSize);
        if (d.advanceNotice) setAdvanceNotice(d.advanceNotice);
        if (d.maxAdvance) setMaxAdvance(d.maxAdvance);
        if (d.plan) setPlan(d.plan);
      }
    } catch { /* corrupt/absent draft — start fresh */ }
    hydratedRef.current = true;
  }, []);

  React.useEffect(() => {
    if (!hydratedRef.current) return;
    try {
      localStorage.setItem(DRAFT_KEY, JSON.stringify({
        step, bizType, bizName, ownerName, email, phone,
        address, city, postcode, country, services, hours,
        teamSize, advanceNotice, maxAdvance, plan,
      }));
    } catch { /* storage unavailable/full — non-fatal, wizard still works in-memory */ }
  }, [step, bizType, bizName, ownerName, email, phone, address, city, postcode, country, services, hours, teamSize, advanceNotice, maxAdvance, plan]);

  // Load suggested services when bizType changes. Prefer a curated list; else
  // derive from the taxonomy group's own services; else a generic fallback.
  React.useEffect(() => {
    if (!bizType) return;
    // A restored draft already carries the user's edited services — don't let
    // the first post-hydration bizType effect overwrite them.
    if (skipNextSuggest.current) { skipNextSuggest.current = false; return; }
    let base = (subTrade && SUBTRADE_SERVICES[subTrade]) || SUGGESTED_SERVICES[bizType];
    if (!base) {
      const group = (window.SERVICE_GROUPS || []).find(g => g.id === bizType);
      if (group) base = group.services.slice(0, 8).map(name => ({ name, duration: 60, price: 0 }));
    }
    if (!base) base = SUGGESTED_SERVICES.other;
    const suggestions = base.map((s, i) => ({
      id: i,
      name: s.name,
      duration: s.duration,
      price: s.price,
      selected: i < 3,
    }));
    setServices(suggestions);
  }, [bizType, subTrade]);

  // Check session on mount
  React.useEffect(() => {
    if (window.BOOKUNO_API && window.BOOKUNO_API.getSession) {
      window.BOOKUNO_API.getSession().then(s => setSession(s)).catch(() => {});
    }
  }, []);

  function validate() {
    const errs = {};
    if (step === 1 && !bizType) errs.bizType = 'Please choose a business type.';
    if (step === 2) {
      if (!bizName.trim()) errs.bizName = 'Business name is required.';
      if (!ownerName.trim()) errs.ownerName = 'Your name is required.';
      if (!email.trim()) errs.email = 'Email is required.';
    }
    if (step === 3) {
      if (!address.trim()) errs.address = 'Address is required.';
      if (!city.trim()) errs.city = 'City is required.';
      if (!postcode.trim()) errs.postcode = 'Postcode is required.';
    }
    if (step === 4 && services.filter(s => s.selected).length === 0) {
      errs.services = 'Select at least one service.';
    }
    if (step === 8 && !session) {
      if (!password) errs.password = 'Password is required.';
      else if (password.length < 8) errs.password = 'Password must be at least 8 characters.';
      if (password !== confirmPassword) errs.confirmPassword = 'Passwords do not match.';
    }
    setErrors(errs);
    const keys = Object.keys(errs);
    if (keys.length) track('onboarding_field_error', { step, fields: keys });
    return keys.length === 0;
  }

  async function handleContinue() {
    if (!validate()) return;

    track('onboarding_step_completed', { step, step_label: STEP_LABELS[step - 1] || null, biz_type: bizType || null });

    // Step 7 → 8: if already signed in, skip password step and submit
    if (step === 7 && session) {
      await submitOnboarding();
      return;
    }

    if (step === totalSteps) {
      await submitOnboarding();
      return;
    }

    setStep(s => s + 1);
  }

  async function submitOnboarding() {
    setLoading(true);
    try {
      // Step 1: create account if not already signed in
      if (!session) {
        const normEmail = email.trim().toLowerCase();
        try {
          const signUpRes = await window.BOOKUNO_API.signUp({
            email: normEmail,
            password,
            name: ownerName.trim() || email.split('@')[0],
            role: 'owner',
          });
          const newUserId = signUpRes?.user?.id || signUpRes?.session?.user?.id || null;
          if (newUserId && window.BOOKUNO_ANALYTICS) window.BOOKUNO_ANALYTICS.identify(newUserId, { role: 'owner' });
          track('account_created', { method: 'password' });
          // Give Supabase a moment to establish the session
          await new Promise(r => setTimeout(r, 800));
        } catch (suErr) {
          // The email already has an account — commonly because the person
          // signed up on the customer side first. Rather than dead-end an
          // 8-step wizard, sign them in with the password they just entered and
          // carry on creating their business. Only fall back to the "sign in to
          // continue" prompt if that password doesn't match.
          const suRaw = suErr?.message || (typeof suErr === 'string' ? suErr : '') || '';
          const already = /already registered|already exists|already have an account|user already|already in use|duplicate/i.test(suRaw);
          if (!already) throw suErr;
          try {
            await window.BOOKUNO_API.signIn(normEmail, password);
            track('onboarding_recovered_existing_account', {});
            await new Promise(r => setTimeout(r, 500));
          } catch (siErr) {
            // Password didn't match the existing account — surface the clear
            // "sign in to continue" recovery (the outer catch keys off this).
            throw new Error('This email is already registered — sign in to continue.');
          }
        }
      }

      const selectedServices = services.filter(s => s.selected);
      await window.BOOKUNO_API.completeOnboarding({
        plan,
        business: {
          name: bizName,
          type: bizType,
          business_type: bizType,
          email: email,
          phone: phone,
          owner_name: ownerName,
          address: address,
          city: city,
          postcode: postcode,
          country: country,
        },
        services: selectedServices.map(s => ({
          name: s.name,
          duration: s.duration,
          price: s.price,
        })),
        hours: hours.map(h => ({
          day: h.day,
          open: h.open,
          opensAt: h.opens,
          closesAt: h.closes,
        })),
        teamSize,
        advanceNotice,
        maxAdvance,
      });

      // Onboarding persisted — clear the saved draft so a later visit starts clean.
      try { localStorage.removeItem(DRAFT_KEY); } catch { /* ignore */ }
      // Remember the plan the owner picked so the dashboard trial-start prompt
      // can pre-select it when they choose to start their trial.
      try { localStorage.setItem('bookuno.preferred_plan', plan); } catch { /* ignore */ }
      track('onboarding_completed', { plan, services_count: selectedServices.length, team_size: teamSize, country, card_capture: cardAtSignup ? 'at_signup' : 'after_value' });

      if (cardAtSignup) {
        // Legacy flow: capture the card via Stripe Checkout now (3-month trial,
        // no charge today). On success Stripe returns to the billing success page.
        try {
          track('checkout_session_requested', { plan, interval: plan });
          const checkout = await window.BOOKUNO_API.createCheckoutSession('pro', plan, undefined);
          if (checkout?.url) {
            track('checkout_redirect', { plan });
            window.location.href = checkout.url;
            return;
          }
          track('checkout_session_failed', { plan, reason: 'no_url' });
        } catch (checkoutErr) {
          track('checkout_session_failed', { plan, reason: (checkoutErr && checkoutErr.message) || 'error' });
          console.error('[onboarding] checkout session failed', checkoutErr);
        }
        // Fall through to the dashboard if checkout couldn't be created.
      }

      // Default (after_value): the storefront is already live
      // (public_booking_enabled is set at onboarding and the public booking flow
      // gates only on that, not on the platform subscription). So land the owner
      // on their dashboard with a live, shareable storefront. The free-trial
      // card is captured later from a contextual "start your free trial" prompt
      // (dashboard banner + billing page), once they've seen the value.
      window.location.href = '/business-app/app.html#today';
    } catch (err) {
      const raw = err?.message || (typeof err === 'string' ? err : null) || '';
      // Turn the late "email already registered" failure into a clear recovery
      // instead of a raw error at the end of an 8-step wizard.
      const alreadyRegistered = /already registered|already exists|already have an account|user already|already in use|duplicate/i.test(raw);
      track('onboarding_submit_failed', { reason: alreadyRegistered ? 'email_taken' : (raw || 'unknown') });
      if (alreadyRegistered) {
        setErrors({
          submit: 'This email already has a Bookuno account. Sign in to continue — everything you entered is saved.',
          emailTaken: true,
        });
      } else {
        setErrors({ submit: raw || 'Something went wrong. Please try again.' });
      }
      setLoading(false);
    }
  }

  function updateService(id, field, value) {
    setServices(prev => prev.map(s => s.id === id ? { ...s, [field]: value } : s));
  }

  function addCustomService() {
    setServices(prev => [...prev, { id: Date.now(), name: '', duration: 60, price: 0, selected: true }]);
  }

  function removeService(id) {
    setServices(prev => prev.filter(s => s.id !== id));
  }

  function updateHour(idx, field, value) {
    setHours(prev => prev.map((h, i) => i === idx ? { ...h, [field]: value } : h));
  }

  const stepLabel = STEP_LABELS[step - 1];

  return (
    <div className="web" style={{ minHeight: '100vh' }}>
      {/* Slim top bar */}
      <header style={{ padding: '16px 28px', display: 'flex', alignItems: 'center', justifyContent: 'space-between', borderBottom: '1px solid var(--line)', background: '#fff', position: 'sticky', top: 0, zIndex: 30 }}>
        <BULogo variant={logoVariant} size={26}/>
        <div style={{ display: 'flex', alignItems: 'center', gap: 14 }}>
          <span style={{ fontSize: 12.5, color: 'var(--muted)', fontWeight: 600 }}>Step {step} of {totalSteps}</span>
          <button className="btn ghost sm" onClick={() => window.location.href = '/'}>Save & exit</button>
        </div>
      </header>

      <div className="onboarding-split" style={{ display: 'grid', gridTemplateColumns: '300px 1fr', minHeight: 'calc(100vh - 65px)' }}>
        <OnboardingSidebar step={step} bizType={bizType}/>

        <main style={{ padding: '44px 56px 80px', background: 'var(--bg)', overflowY: 'auto' }}>
          <div style={{ maxWidth: 680, margin: '0 auto' }}>
            {/* Progress bar */}
            <div style={{ height: 4, background: 'var(--line)', borderRadius: 2, marginBottom: 32, overflow: 'hidden' }}>
              <div style={{ height: '100%', width: `${(step / totalSteps) * 100}%`, background: 'linear-gradient(90deg, var(--pri), #FF6B9C)', borderRadius: 2, transition: 'width 0.3s ease' }}/>
            </div>
            <div style={{ fontSize: 11, fontWeight: 800, letterSpacing: 0.07, color: 'var(--pri)', textTransform: 'uppercase', marginBottom: 6 }}>
              STEP {step} OF {totalSteps} · {stepLabel.toUpperCase()}
            </div>

            {/* Onboarding reassurance banner */}
            <div style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '8px 14px', borderRadius: 10, background: 'var(--pri-50)', border: '1px solid var(--pri-100, var(--pri-50))', marginBottom: 20, fontSize: 12.5, color: 'var(--pri-700)', fontWeight: 600, flexWrap: 'wrap', rowGap: 4 }}>
              <span>🔒</span>
              <span>Your profile won't go live until you complete setup.</span>
              <span style={{ color: 'var(--muted)', margin: '0 4px' }}>·</span>
              <span>3 months free · cancel anytime.</span>
              <span style={{ color: 'var(--muted)', margin: '0 4px' }}>·</span>
              <span>You can change anything later.</span>
            </div>

            {/* ── Step 1: Business type ── */}
            {step === 1 && (
              <>
                <h1 style={{ fontSize: 30, fontWeight: 800, letterSpacing: -0.03 }}>What kind of business are you?</h1>
                <p style={{ fontSize: 15, color: 'var(--muted)', marginTop: 8 }}>We'll customise your setup based on your answer. Whatever you do, there's a category for it.</p>
                <p style={{ fontSize: 12.5, color: 'var(--muted)', marginTop: 6 }}>Not sure? You can change this later.</p>
                <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginTop: 20, background: '#fff', border: '1.5px solid var(--line)', borderRadius: 12, padding: '0 14px', height: 48 }}>
                  <Ic.search style={{ width: 17, height: 17, color: 'var(--muted)' }}/>
                  <input value={bizSearch} onChange={e => setBizSearch(e.target.value)} placeholder="Search categories — e.g. plumber, photographer, tutor"
                    style={{ flex: 1, border: 0, outline: 0, background: 'transparent', fontFamily: 'inherit', fontSize: 14.5, fontWeight: 600, color: 'var(--ink)' }}/>
                </div>
                {(() => {
                  const sq = bizSearch.trim().toLowerCase();
                  const shown = sq ? BIZ_TYPES.filter(b =>
                    b.label.toLowerCase().includes(sq) ||
                    b.cat.toLowerCase().includes(sq) ||
                    b.services.some(s => s.toLowerCase().includes(sq))
                  ) : BIZ_TYPES;
                  return shown.length ? (
                    <div role="radiogroup" aria-label="Business type" style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(148px, 1fr))', gap: 12, marginTop: 18 }}>
                      {shown.map(b => (
                        <button type="button" key={b.id} role="radio" aria-checked={bizType === b.id} aria-label={b.label} onClick={() => { setBizType(b.id); setSubTrade(SUBTRADE_SERVICES[sq] ? sq : ''); setErrors({}); }}
                          style={{ padding: '18px 12px', borderRadius: 16, border: bizType === b.id ? '2.5px solid var(--pri)' : '1.5px solid var(--line)', background: bizType === b.id ? 'var(--pri-50)' : '#fff', cursor: 'pointer', display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 8, transition: 'all 0.12s', boxShadow: bizType === b.id ? 'var(--sh-pri)' : 'var(--sh-1)' }}>
                          <span aria-hidden="true" style={{ width: 52, height: 52, borderRadius: 16, background: bizType === b.id ? '#fff' : 'var(--pri-50)', display: 'grid', placeItems: 'center', fontSize: 24, lineHeight: 1 }}>{b.icon}</span>
                          <span style={{ fontSize: 13.5, fontWeight: 700, textAlign: 'center', color: bizType === b.id ? 'var(--pri-700)' : 'var(--ink)' }}>{b.label}</span>
                        </button>
                      ))}
                    </div>
                  ) : (
                    <p style={{ color: 'var(--muted)', fontSize: 14, padding: '24px 0' }}>No categories match "{bizSearch}". Pick <button type="button" onClick={() => { setBizType('other'); setBizSearch(''); }} style={{ all: 'unset', cursor: 'pointer', color: 'var(--pri)', fontWeight: 700 }}>Something else</button> and add your own.</p>
                  );
                })()}
                <FieldError msg={errors.bizType}/>
                <StepActions step={step} totalSteps={totalSteps} onBack={() => setStep(s => s - 1)} onContinue={handleContinue} disabled={step === 1 && !bizType}/>
              </>
            )}

            {/* ── Step 2: The basics ── */}
            {step === 2 && (
              <>
                <h1 style={{ fontSize: 30, fontWeight: 800, letterSpacing: -0.03 }}>The basics</h1>
                <p style={{ fontSize: 15, color: 'var(--muted)', marginTop: 8 }}>Tell us about your business so customers can find and contact you.</p>
                <p style={{ fontSize: 12.5, color: 'var(--muted)', marginTop: 6 }}>This is shown publicly on your storefront. You can edit it any time.</p>
                <div style={{ display: 'grid', gap: 16, marginTop: 28 }}>
                  {[
                    { label: 'Business name', value: bizName, onChange: e => setBizName(e.target.value), key: 'bizName', placeholder: 'e.g. Knot & Comb' },
                    { label: 'Your name (owner)', value: ownerName, onChange: e => setOwnerName(e.target.value), key: 'ownerName', placeholder: 'e.g. Mark Johnson' },
                    { label: 'Contact email', value: email, onChange: e => setEmail(e.target.value), key: 'email', placeholder: 'hello@yourbusiness.com', type: 'email' },
                    { label: 'Phone number', value: phone, onChange: e => setPhone(e.target.value), key: 'phone', placeholder: '+44 7700 000000', type: 'tel' },
                  ].map(f => (
                    <div key={f.key}>
                      <label htmlFor={`ob-${f.key}`} style={{ fontSize: 13, fontWeight: 700, display: 'block', marginBottom: 7 }}>{f.label}</label>
                      <div className="input lg">
                        <input id={`ob-${f.key}`} value={f.value} onChange={f.onChange} placeholder={f.placeholder} type={f.type || 'text'}
                          aria-invalid={errors[f.key] ? 'true' : undefined} aria-describedby={errors[f.key] ? `err-${f.key}` : undefined}/>
                      </div>
                      <FieldError id={`err-${f.key}`} msg={errors[f.key]}/>
                    </div>
                  ))}
                </div>
                <StepActions step={step} totalSteps={totalSteps} onBack={() => setStep(s => s - 1)} onContinue={handleContinue}/>
              </>
            )}

            {/* ── Step 3: Location ── */}
            {step === 3 && (
              <>
                <h1 style={{ fontSize: 30, fontWeight: 800, letterSpacing: -0.03 }}>Where do you work?</h1>
                <p style={{ fontSize: 15, color: 'var(--muted)', marginTop: 8 }}>Customers will use this to find your location.</p>
                <div style={{ display: 'grid', gap: 16, marginTop: 28 }}>
                  {[
                    { label: 'Address line 1', value: address, onChange: e => setAddress(e.target.value), key: 'address', placeholder: '123 High Street' },
                    { label: 'City', value: city, onChange: e => setCity(e.target.value), key: 'city', placeholder: 'Manchester' },
                    { label: 'Postcode', value: postcode, onChange: e => setPostcode(e.target.value), key: 'postcode', placeholder: 'M1 1AE' },
                  ].map(f => (
                    <div key={f.key}>
                      <label htmlFor={`ob-${f.key}`} style={{ fontSize: 13, fontWeight: 700, display: 'block', marginBottom: 7 }}>{f.label}</label>
                      <div className="input lg"><input id={`ob-${f.key}`} value={f.value} onChange={f.onChange} placeholder={f.placeholder}
                        aria-invalid={errors[f.key] ? 'true' : undefined} aria-describedby={errors[f.key] ? `err-${f.key}` : undefined}/></div>
                      <FieldError id={`err-${f.key}`} msg={errors[f.key]}/>
                    </div>
                  ))}
                  <div>
                    <label htmlFor="ob-country" style={{ fontSize: 13, fontWeight: 700, display: 'block', marginBottom: 7 }}>Country</label>
                    <div className="input lg">
                      <select id="ob-country" value={country} onChange={e => setCountry(e.target.value)} style={{ flex: 1, border: 0, outline: 0, background: 'transparent', fontSize: 14.5 }}>
                        <option value="GB">United Kingdom</option>
                        <option value="IE">Ireland</option>
                        <option value="US">United States</option>
                        <option value="AU">Australia</option>
                        <option value="CA">Canada</option>
                      </select>
                    </div>
                  </div>
                </div>
                <StepActions step={step} totalSteps={totalSteps} onBack={() => setStep(s => s - 1)} onContinue={handleContinue}/>
              </>
            )}

            {/* ── Step 4: Services ── */}
            {step === 4 && (
              <>
                <h1 style={{ fontSize: 30, fontWeight: 800, letterSpacing: -0.03 }}>Pick the services you offer</h1>
                <p style={{ fontSize: 15, color: 'var(--muted)', marginTop: 8, maxWidth: 520 }}>Suggested for a {BIZ_TYPES.find(b => b.id === bizType)?.label || 'business'}. Tweak or add your own — you can change these later.</p>
                <div style={{ display: 'grid', gap: 10, marginTop: 28 }}>
                  {services.map((s, i) => (
                    <Rv key={s.id} delay={i * 30}>
                      <div style={{ padding: 14, display: 'flex', alignItems: 'center', gap: 12, borderRadius: 14, background: s.selected ? 'var(--pri-50)' : '#fff', border: s.selected ? '2px solid var(--pri)' : '1.5px solid var(--line)', cursor: 'pointer', transition: 'all 0.12s' }}
                        onClick={() => updateService(s.id, 'selected', !s.selected)}>
                        <span style={{ width: 22, height: 22, borderRadius: 6, background: s.selected ? 'var(--pri)' : 'transparent', border: s.selected ? 'none' : '1.5px solid var(--line)', display: 'grid', placeItems: 'center', flexShrink: 0 }}>
                          {s.selected && <Ic.check style={{ width: 12, height: 12, color: '#fff' }}/>}
                        </span>
                        <div style={{ flex: 1, minWidth: 0 }} onClick={e => e.stopPropagation()}>
                          <input value={s.name} onChange={e => updateService(s.id, 'name', e.target.value)}
                            style={{ width: '100%', border: 0, outline: 0, background: 'transparent', fontSize: 14.5, fontWeight: 700, padding: 0, color: 'var(--ink)' }}
                            placeholder="Service name"/>
                        </div>
                        <div style={{ display: 'flex', alignItems: 'center', gap: 5, background: '#fff', borderRadius: 10, padding: '6px 10px', border: '1px solid var(--line)', flexShrink: 0 }} onClick={e => e.stopPropagation()}>
                          <Ic.clock style={{ width: 11, height: 11, color: 'var(--muted)' }}/>
                          <input value={s.duration} onChange={e => updateService(s.id, 'duration', e.target.value)}
                            style={{ width: 44, border: 0, outline: 0, fontSize: 13, fontWeight: 700, textAlign: 'right', background: 'transparent' }} type="number"/>
                          <span style={{ fontSize: 12, color: 'var(--muted)' }}>min</span>
                        </div>
                        <div style={{ display: 'flex', alignItems: 'center', gap: 4, background: '#fff', borderRadius: 10, padding: '6px 10px', border: '1px solid var(--line)', flexShrink: 0 }} onClick={e => e.stopPropagation()}>
                          <span style={{ fontSize: 13, color: 'var(--muted)' }}>{currencySymbolFor(country)}</span>
                          <input value={s.price} onChange={e => updateService(s.id, 'price', e.target.value)}
                            style={{ width: 46, border: 0, outline: 0, fontSize: 13, fontWeight: 800, textAlign: 'right', background: 'transparent' }} type="number"/>
                        </div>
                        <button type="button" style={{ width: 30, height: 30, borderRadius: 8, background: 'transparent', border: '1px solid var(--line)', display: 'grid', placeItems: 'center', flexShrink: 0, cursor: 'pointer' }}
                          onClick={e => { e.stopPropagation(); removeService(s.id); }}>
                          <Ic.close style={{ width: 13, height: 13 }}/>
                        </button>
                      </div>
                    </Rv>
                  ))}
                  <button type="button" className="btn outline" style={{ width: 'fit-content', borderStyle: 'dashed', marginTop: 4 }} onClick={addCustomService}>
                    <Ic.plus style={{ width: 15, height: 15 }}/> Add a custom service
                  </button>
                </div>
                {services.filter(s => s.selected).length > 0 && (
                  <div style={{ marginTop: 18, padding: 14, borderRadius: 14, background: '#fff', display: 'flex', alignItems: 'center', gap: 10, border: '1px solid var(--line)' }}>
                    <Ic.spark style={{ width: 17, height: 17, color: 'var(--pri)' }}/>
                    <div style={{ fontSize: 13, color: 'var(--ink-2)' }}>
                      <b>{services.filter(s => s.selected).length} service{services.filter(s => s.selected).length !== 1 ? 's' : ''} selected.</b> You can add more any time from your dashboard.
                    </div>
                  </div>
                )}
                <FieldError msg={errors.services}/>
                <StepActions step={step} totalSteps={totalSteps} onBack={() => setStep(s => s - 1)} onContinue={handleContinue}/>
              </>
            )}

            {/* ── Step 5: Opening hours ── */}
            {step === 5 && (
              <>
                <h1 style={{ fontSize: 30, fontWeight: 800, letterSpacing: -0.03 }}>Opening hours</h1>
                <p style={{ fontSize: 15, color: 'var(--muted)', marginTop: 8 }}>Set your default hours. You can override specific dates later.</p>
                <div style={{ display: 'grid', gap: 10, marginTop: 28 }}>
                  {hours.map((h, i) => (
                    <div key={i} style={{ display: 'flex', alignItems: 'center', gap: 14, padding: '12px 16px', borderRadius: 14, background: '#fff', border: '1.5px solid var(--line)' }}>
                      <button type="button" role="switch" aria-checked={h.open} aria-label={`${h.day} — ${h.open ? 'open' : 'closed'}`} onClick={() => updateHour(i, 'open', !h.open)}
                        style={{ width: 42, height: 24, borderRadius: 999, background: h.open ? 'var(--pri)' : 'var(--chip)', border: 'none', position: 'relative', cursor: 'pointer', flexShrink: 0, transition: 'background 0.15s' }}>
                        <span style={{ position: 'absolute', top: 3, left: h.open ? 20 : 3, width: 18, height: 18, borderRadius: '50%', background: '#fff', transition: 'left 0.15s', boxShadow: '0 1px 3px rgba(0,0,0,0.2)' }}/>
                      </button>
                      <span style={{ width: 90, fontSize: 13.5, fontWeight: 700 }}>{h.day}</span>
                      {h.open ? (
                        <>
                          <input type="time" value={h.opens} onChange={e => updateHour(i, 'opens', e.target.value)} style={{ flex: 1, border: '1.5px solid var(--line)', borderRadius: 10, padding: '0 10px', height: 38, background: '#fff', fontSize: 13.5, fontWeight: 700, outline: 0 }}/>
                          <span style={{ fontSize: 13, color: 'var(--muted)' }}>to</span>
                          <input type="time" value={h.closes} onChange={e => updateHour(i, 'closes', e.target.value)} style={{ flex: 1, border: '1.5px solid var(--line)', borderRadius: 10, padding: '0 10px', height: 38, background: '#fff', fontSize: 13.5, fontWeight: 700, outline: 0 }}/>
                        </>
                      ) : (
                        <span style={{ fontSize: 13, color: 'var(--muted)', fontStyle: 'italic' }}>Closed</span>
                      )}
                    </div>
                  ))}
                </div>
                <StepActions step={step} totalSteps={totalSteps} onBack={() => setStep(s => s - 1)} onContinue={handleContinue}/>
              </>
            )}

            {/* ── Step 6: Team & rules ── */}
            {step === 6 && (
              <>
                <h1 style={{ fontSize: 30, fontWeight: 800, letterSpacing: -0.03 }}>Team & booking rules</h1>
                <p style={{ fontSize: 15, color: 'var(--muted)', marginTop: 8 }}>We'll use this to configure your calendar defaults.</p>
                <div style={{ marginTop: 28 }}>
                  <label id="lbl-staff" style={{ fontSize: 13, fontWeight: 700, display: 'block', marginBottom: 10 }}>How many staff members?</label>
                  <div role="radiogroup" aria-labelledby="lbl-staff" style={{ display: 'flex', gap: 10, flexWrap: 'wrap' }}>
                    {[['1','Just me'],['2-5','2 – 5'],['6-10','6 – 10'],['10+','10+']].map(([v, l]) => (
                      <button type="button" key={v} role="radio" aria-checked={teamSize === v} onClick={() => setTeamSize(v)}
                        style={{ padding: '10px 20px', borderRadius: 12, border: teamSize === v ? '2px solid var(--pri)' : '1.5px solid var(--line)', background: teamSize === v ? 'var(--pri-50)' : '#fff', fontWeight: 700, fontSize: 14, cursor: 'pointer', color: teamSize === v ? 'var(--pri-700)' : 'var(--ink)' }}>
                        {l}
                      </button>
                    ))}
                  </div>

                  <label id="lbl-notice" style={{ fontSize: 13, fontWeight: 700, display: 'block', marginTop: 24, marginBottom: 10 }}>Minimum advance notice</label>
                  <div role="radiogroup" aria-labelledby="lbl-notice" style={{ display: 'flex', gap: 10, flexWrap: 'wrap' }}>
                    {[['none','None'],['1h','1 hour'],['2h','2 hours'],['24h','24 hours']].map(([v, l]) => (
                      <button type="button" key={v} role="radio" aria-checked={advanceNotice === v} onClick={() => setAdvanceNotice(v)}
                        style={{ padding: '10px 20px', borderRadius: 12, border: advanceNotice === v ? '2px solid var(--pri)' : '1.5px solid var(--line)', background: advanceNotice === v ? 'var(--pri-50)' : '#fff', fontWeight: 700, fontSize: 14, cursor: 'pointer', color: advanceNotice === v ? 'var(--pri-700)' : 'var(--ink)' }}>
                        {l}
                      </button>
                    ))}
                  </div>

                  <label id="lbl-maxadv" style={{ fontSize: 13, fontWeight: 700, display: 'block', marginTop: 24, marginBottom: 10 }}>How far in advance can customers book?</label>
                  <div role="radiogroup" aria-labelledby="lbl-maxadv" style={{ display: 'flex', gap: 10, flexWrap: 'wrap' }}>
                    {[['1_week','1 week'],['2_weeks','2 weeks'],['1_month','1 month'],['3_months','3 months']].map(([v, l]) => (
                      <button type="button" key={v} role="radio" aria-checked={maxAdvance === v} onClick={() => setMaxAdvance(v)}
                        style={{ padding: '10px 20px', borderRadius: 12, border: maxAdvance === v ? '2px solid var(--pri)' : '1.5px solid var(--line)', background: maxAdvance === v ? 'var(--pri-50)' : '#fff', fontWeight: 700, fontSize: 14, cursor: 'pointer', color: maxAdvance === v ? 'var(--pri-700)' : 'var(--ink)' }}>
                        {l}
                      </button>
                    ))}
                  </div>
                </div>
                <StepActions step={step} totalSteps={totalSteps} onBack={() => setStep(s => s - 1)} onContinue={handleContinue}/>
              </>
            )}

            {/* ── Step 7: Plan ── */}
            {step === 7 && (
              <>
                <h1 style={{ fontSize: 30, fontWeight: 800, letterSpacing: -0.03 }}>Choose your plan</h1>
                <p style={{ fontSize: 15, color: 'var(--muted)', marginTop: 8 }}>{cardAtSignup
                  ? "3 months free on both plans. We'll save your card securely — no charge for 3 months, cancel any time."
                  : "No card needed to finish setup. You'll start your 3 months free from your dashboard — pick the plan you'd like to start with."}</p>
                <div role="radiogroup" aria-label="Billing plan" style={{ display: 'grid', gap: 14, marginTop: 28 }}>
                  {[
                    { id: 'monthly', label: 'Monthly', price: '£29', period: '/month', sub: 'Cancel any time', badge: null },
                    { id: 'yearly',  label: 'Yearly',  price: '£19', period: '/month', sub: 'Billed £228/year — save £120', badge: 'BEST VALUE' },
                  ].map(p => (
                    <button type="button" key={p.id} role="radio" aria-checked={plan === p.id} aria-label={`${p.label} — ${p.price}${p.period}${p.badge ? ', best value' : ''}`} onClick={() => setPlan(p.id)}
                      style={{ padding: '22px 24px', borderRadius: 18, border: plan === p.id ? '2.5px solid var(--pri)' : '1.5px solid var(--line)', background: plan === p.id ? 'var(--pri-50)' : '#fff', cursor: 'pointer', textAlign: 'left', transition: 'all 0.12s', position: 'relative' }}>
                      {p.badge && <span style={{ position: 'absolute', top: -10, right: 16, fontSize: 10, fontWeight: 800, background: 'var(--pri)', color: '#fff', padding: '3px 10px', borderRadius: 999 }}>{p.badge}</span>}
                      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
                        <div>
                          <div style={{ fontSize: 16, fontWeight: 800, marginBottom: 4 }}>{p.label}</div>
                          <div>
                            <span style={{ fontSize: 32, fontWeight: 900, letterSpacing: -0.03 }}>{p.price}</span>
                            <span style={{ fontSize: 13, color: 'var(--muted)', marginLeft: 3 }}>{p.period}</span>
                          </div>
                          <div style={{ fontSize: 12.5, color: 'var(--muted)', marginTop: 4 }}>{p.sub}</div>
                        </div>
                        <div style={{ width: 24, height: 24, borderRadius: '50%', border: plan === p.id ? 'none' : '2px solid var(--line)', background: plan === p.id ? 'var(--pri)' : 'transparent', display: 'grid', placeItems: 'center', flexShrink: 0 }}>
                          {plan === p.id && <Ic.check style={{ width: 13, height: 13, color: '#fff' }}/>}
                        </div>
                      </div>
                    </button>
                  ))}
                </div>
                <div style={{ marginTop: 16, padding: '14px 18px', borderRadius: 12, background: 'var(--bg-2)', fontSize: 13, color: 'var(--muted)', display: 'flex', alignItems: 'flex-start', gap: 10 }}>
                  <Ic.check style={{ width: 14, height: 14, color: 'var(--green, #38a169)', flexShrink: 0, marginTop: 1 }}/>
                  <span>{cardAtSignup
                    ? "Both plans include unlimited bookings, all features, SMS & email reminders, Stripe payments, and priority support. Your 3 months free starts today — we'll securely save your card and won't charge you until it ends."
                    : "Both plans include unlimited bookings, all features, SMS & email reminders, Stripe payments, and priority support. No card needed to finish setup — you'll start your 3 months free (cancel anytime) from your dashboard once your storefront is live."}</span>
                </div>
                <StepActions step={step} totalSteps={totalSteps} onBack={() => setStep(s => s - 1)}
                  onContinue={handleContinue}
                  continueLabel={cardAtSignup && session ? 'Start free trial →' : 'Continue →'}
                  loading={loading}/>
              </>
            )}

            {/* ── Step 8: Password ── */}
            {step === 8 && (
              <>
                <h1 style={{ fontSize: 30, fontWeight: 800, letterSpacing: -0.03 }}>Create a password</h1>
                <p style={{ fontSize: 15, color: 'var(--muted)', marginTop: 8 }}>You'll use this to sign in to your Bookuno dashboard.</p>
                <div style={{ display: 'grid', gap: 16, marginTop: 28 }}>
                  <div>
                    <label htmlFor="ob-password" style={{ fontSize: 13, fontWeight: 700, display: 'block', marginBottom: 7 }}>Password</label>
                    <div className="input lg">
                      <input id="ob-password" type="password" value={password} onChange={e => setPassword(e.target.value)} placeholder="Min. 8 characters"
                        aria-invalid={errors.password ? 'true' : undefined} aria-describedby={errors.password ? 'err-password' : undefined}/>
                    </div>
                    <FieldError id="err-password" msg={errors.password}/>
                  </div>
                  <div>
                    <label htmlFor="ob-confirm-password" style={{ fontSize: 13, fontWeight: 700, display: 'block', marginBottom: 7 }}>Confirm password</label>
                    <div className="input lg">
                      <input id="ob-confirm-password" type="password" value={confirmPassword} onChange={e => setConfirmPassword(e.target.value)} placeholder="Repeat your password"
                        aria-invalid={errors.confirmPassword ? 'true' : undefined} aria-describedby={errors.confirmPassword ? 'err-confirm-password' : undefined}/>
                    </div>
                    <FieldError id="err-confirm-password" msg={errors.confirmPassword}/>
                  </div>
                </div>
                {errors.submit && (
                  <div style={{ marginTop: 16, padding: 14, borderRadius: 12, background: '#FFF5F5', border: '1.5px solid #FC8181', fontSize: 13.5, color: '#C53030', fontWeight: 600 }}>
                    {errors.submit}
                    {errors.emailTaken && (
                      <> <a href="#/auth?mode=login" style={{ color: 'var(--pri)', fontWeight: 800 }}>Sign in →</a></>
                    )}
                  </div>
                )}
                <StepActions step={step} totalSteps={totalSteps} onBack={() => setStep(s => s - 1)}
                  onContinue={handleContinue} continueLabel={cardAtSignup ? 'Start free trial →' : 'Create my account & go live →'} loading={loading}/>
              </>
            )}

            {errors.submit && step !== 8 && (
              <div style={{ marginTop: 16, padding: 14, borderRadius: 12, background: '#FFF5F5', border: '1.5px solid #FC8181', fontSize: 13.5, color: '#C53030', fontWeight: 600 }}>
                {errors.submit}
                {errors.emailTaken && (
                  <> <a href="#/auth?mode=login" style={{ color: 'var(--pri)', fontWeight: 800 }}>Sign in →</a></>
                )}
              </div>
            )}
          </div>
        </main>
      </div>
    </div>
  );
}

/* ─── Business Dashboard (desktop) ───────────────────────────
   DESIGN-CANVAS MOCK ONLY. This screen renders hardcoded "Mark / Knot & Comb"
   sample data and exists purely for the internal design artboards
   (web/app.jsx, web/overview.jsx). The real owner dashboard is the live app at
   business-app/ (PageToday). Do NOT wire this to a production route — it has no
   real data source and would show fake numbers to a signed-in owner. */
function PageBizDashboard({ logoVariant = 'default' }) {
  return (
    <div className="web">
      <TopNav active="business" logoVariant={logoVariant} loggedIn/>

      <div className="shell" style={{ maxWidth: 1340, padding: '28px 28px 60px' }}>
        <BusinessSide active="today"/>
        <main style={{ minWidth: 0 }}>
          {/* Header */}
          <div style={{ display: 'flex', alignItems: 'flex-end', justifyContent: 'space-between', marginBottom: 22, flexWrap: 'wrap', gap: 14 }}>
            <div>
              <div className="micro" style={{ color: 'var(--pri)' }}>{new Date().toLocaleDateString('en-GB', { weekday: 'short', day: 'numeric', month: 'short' }).toUpperCase()} · TODAY</div>
              <h1 style={{ fontSize: 28, fontWeight: 800, letterSpacing: -0.025, marginTop: 4 }}>Welcome back, Mark.</h1>
              <p style={{ fontSize: 14, color: 'var(--muted)', marginTop: 4 }}>8 bookings on the schedule. 2 walk-in slots still open.</p>
            </div>
            <div style={{ display: 'flex', gap: 8 }}>
              <button className="btn outline"><Ic.share style={{ width: 16, height: 16 }}/> Share storefront</button>
              <button className="btn primary"><Ic.plus style={{ width: 16, height: 16 }}/> New booking</button>
            </div>
          </div>

          {/* Stat grid */}
          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(180px, 1fr))', gap: 14, marginBottom: 22 }}>
            {[
              { l: "Today's bookings", v: '8',   d: '+2 vs avg',     dt: 'up' },
              { l: 'Revenue today',    v: '£186',d: '+£42 vs avg',   dt: 'up' },
              { l: 'Booked',           v: '92%', d: '2 slots open',  dt: 'flat' },
              { l: 'New customers',    v: '3',   d: 'this week',     dt: 'up' },
            ].map((s, i) => (
              <Rv key={i} delay={i*40}>
                <div className="card" style={{ padding: 18 }}>
                  <div style={{ fontSize: 12.5, color: 'var(--muted)', fontWeight: 600 }}>{s.l}</div>
                  <div style={{ fontSize: 28, fontWeight: 800, letterSpacing: -0.025, marginTop: 4 }}>{s.v}</div>
                  <div style={{ fontSize: 11.5, fontWeight: 700, color: s.dt === 'up' ? 'var(--green)' : 'var(--muted)', marginTop: 4 }}>{s.d}</div>
                </div>
              </Rv>
            ))}
          </div>

          {/* Two-col layout */}
          <div className="col-2 wide-left" style={{ gap: 18 }}>
            {/* Today schedule */}
            <Rv>
              <div className="card" style={{ padding: 0 }}>
                <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '18px 18px 14px' }}>
                  <h3 style={{ fontSize: 17, fontWeight: 800, letterSpacing: -0.02 }}>Today's schedule</h3>
                  <div style={{ display: 'flex', gap: 6 }}>
                    <button className="chip on" style={{ height: 30, fontSize: 12 }}>All staff</button>
                    <button className="chip" style={{ height: 30, fontSize: 12 }}>Mark</button>
                    <button className="chip" style={{ height: 30, fontSize: 12 }}>Aisha</button>
                    <button className="chip" style={{ height: 30, fontSize: 12 }}>Dom</button>
                  </div>
                </div>
                <div style={{ borderTop: '1px solid var(--line)' }}>
                  {[
                    { t: '09:00', n: 'Tom Hayes',     s: 'Skin fade · 45 min', m: 'Mark',  c: 'var(--green)', avatar: 'TH' },
                    { t: '10:00', n: 'Hannah Bell',   s: 'Beard trim · 15 min', m: 'Aisha', c: 'var(--pri)',   avatar: 'HB' },
                    { t: '10:30', n: 'Walk-in slot',  s: 'Open · 30 min',       m: '—',     c: 'var(--muted-2)', open: true },
                    { t: '11:30', n: 'Jay Patel',     s: 'Classic cut · 30 min',m: 'Mark',  c: 'var(--green)', avatar: 'JP' },
                    { t: '13:00', n: 'Sara Reza',     s: 'Skin fade · 45 min',  m: 'Mark',  c: 'var(--green)', avatar: 'SR' },
                    { t: '14:30', n: 'Sam Wright',    s: 'Father & son · 60 m', m: 'Dom',   c: 'var(--amber)', avatar: 'SW' },
                    { t: '15:30', n: 'Maria Costa',   s: 'Beard sculpt · 20 m', m: 'Aisha', c: 'var(--pri)',   avatar: 'MC' },
                    { t: '16:30', n: 'Walk-in slot',  s: 'Open · 30 min',       m: '—',     c: 'var(--muted-2)', open: true },
                  ].map((r, i) => (
                    <div key={i} style={{ display: 'flex', alignItems: 'center', gap: 14, padding: '14px 18px', borderTop: i ? '1px solid var(--line-2)' : 0, opacity: r.open ? 0.6 : 1 }}>
                      <div style={{ fontWeight: 800, fontSize: 13, width: 50, color: 'var(--ink)' }}>{r.t}</div>
                      <div style={{ width: 3, alignSelf: 'stretch', borderRadius: 2, background: r.c }}/>
                      {r.avatar ? (
                        <div style={{ width: 36, height: 36, borderRadius: '50%', background: 'linear-gradient(135deg, #A8E0FF, #7C5BFF)', color: '#fff', display: 'grid', placeItems: 'center', fontSize: 12, fontWeight: 800 }}>{r.avatar}</div>
                      ) : (
                        <div style={{ width: 36, height: 36, borderRadius: '50%', background: 'var(--chip)', color: 'var(--muted)', display: 'grid', placeItems: 'center' }}>
                          <Ic.plus style={{ width: 16, height: 16 }}/>
                        </div>
                      )}
                      <div style={{ flex: 1, minWidth: 0 }}>
                        <div style={{ fontWeight: 700, fontSize: 14 }}>{r.n}</div>
                        <div style={{ fontSize: 12, color: 'var(--muted)' }}>{r.s} · {r.m}</div>
                      </div>
                      <button className="btn outline sm">{r.open ? 'Add booking' : 'Open'}</button>
                    </div>
                  ))}
                </div>
              </div>
            </Rv>

            {/* Right column */}
            <div style={{ display: 'grid', gap: 18 }}>
              <Rv delay={60}>
                <div className="card" style={{ padding: 18, background: 'linear-gradient(135deg, var(--pri-50), #fff)', border: '1px solid var(--pri-100)' }}>
                  <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
                    <div style={{ width: 44, height: 44, borderRadius: 13, background: 'var(--pri)', color: '#fff', display: 'grid', placeItems: 'center' }}>
                      <Ic.spark style={{ width: 20, height: 20 }}/>
                    </div>
                    <div>
                      <div style={{ fontWeight: 800, fontSize: 14 }}>Your storefront is live</div>
                      <div style={{ fontSize: 11.5, color: 'var(--muted)' }}>bookuno.com/knot-comb</div>
                    </div>
                  </div>
                  <button className="btn primary sm block" style={{ marginTop: 14 }}><Ic.share style={{ width: 14, height: 14 }}/> Copy & share link</button>
                </div>
              </Rv>

              <Rv delay={120}>
                <div className="card" style={{ padding: 18 }}>
                  <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: 14 }}>
                    <h3 style={{ fontSize: 15, fontWeight: 800 }}>This week</h3>
                    <a href="/business-app/app.html#insights" style={{ fontSize: 12, color: 'var(--pri)', fontWeight: 700 }}>Details →</a>
                  </div>
                  {/* Mini chart */}
                  <svg viewBox="0 0 280 120" style={{ width: '100%', height: 120 }}>
                    <defs>
                      <linearGradient id="grd" x1="0" x2="0" y1="0" y2="1">
                        <stop offset="0%" stopColor="var(--pri)" stopOpacity="0.4"/>
                        <stop offset="100%" stopColor="var(--pri)" stopOpacity="0"/>
                      </linearGradient>
                    </defs>
                    <path d="M0,90 L40,72 L80,80 L120,50 L160,58 L200,30 L240,42 L280,28 L280,120 L0,120 Z" fill="url(#grd)"/>
                    <path d="M0,90 L40,72 L80,80 L120,50 L160,58 L200,30 L240,42 L280,28" fill="none" stroke="var(--pri)" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"/>
                    {[[0,90],[40,72],[80,80],[120,50],[160,58],[200,30],[240,42],[280,28]].map(([x,y],i) => (
                      <circle key={i} cx={x} cy={y} r="3" fill="#fff" stroke="var(--pri)" strokeWidth="2"/>
                    ))}
                  </svg>
                  <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 10.5, color: 'var(--muted-2)', marginTop: 4 }}>
                    {['M','T','W','Th','F','Sa','Su'].map(d => <span key={d}>{d}</span>)}
                  </div>
                  <div style={{ marginTop: 14, padding: 12, borderRadius: 10, background: 'var(--bg)' }}>
                    <div style={{ fontSize: 22, fontWeight: 800, letterSpacing: -0.025 }}>£1,284</div>
                    <div style={{ fontSize: 12, color: 'var(--green)', fontWeight: 700 }}>+18% vs last week</div>
                  </div>
                </div>
              </Rv>

              <Rv delay={180}>
                <div className="card" style={{ padding: 18 }}>
                  <h3 style={{ fontSize: 15, fontWeight: 800, marginBottom: 14 }}>Messages</h3>
                  <div style={{ display: 'grid', gap: 12 }}>
                    {[
                      { n: 'Sara Reza', t: 'Can I push 15:30 back to 16:00?', d: 'now', unread: true },
                      { n: 'Tom Hayes', t: 'Thanks for the cut on Friday!', d: '2h' },
                      { n: 'Jay Patel', t: 'Booking question — kids cut age?', d: 'yesterday' },
                    ].map((m, i) => (
                      <button key={i} style={{ display: 'flex', alignItems: 'flex-start', gap: 10, padding: 8, borderRadius: 10, background: m.unread ? 'var(--pri-50)' : 'transparent', textAlign: 'left' }}>
                        <div style={{ width: 32, height: 32, borderRadius: '50%', background: ['linear-gradient(135deg, #FFB4A2, #FF7B9C)','linear-gradient(135deg, #A8E0FF, #7C5BFF)','linear-gradient(135deg, #C5F0CF, #3DA9FF)'][i], color: '#fff', display: 'grid', placeItems: 'center', fontSize: 11, fontWeight: 800, flexShrink: 0 }}>{m.n[0]}</div>
                        <div style={{ flex: 1, minWidth: 0 }}>
                          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
                            <span style={{ fontWeight: 700, fontSize: 13 }}>{m.n}</span>
                            <span style={{ fontSize: 10.5, color: 'var(--muted-2)' }}>{m.d}</span>
                          </div>
                          <div style={{ fontSize: 12, color: 'var(--muted)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', marginTop: 1 }}>{m.t}</div>
                        </div>
                      </button>
                    ))}
                  </div>
                </div>
              </Rv>
            </div>
          </div>
        </main>
      </div>
    </div>
  );
}

Object.assign(window, { PageAccount, PageBizOnboarding, PageBizDashboard });
