// modules.jsx — all 11 module pages for Pinnacle Command Centre
// Dependencies: window.Card, window.PageHead, window.KPI, window.StageBadge,
//               window.InvoiceBadge, window.Icon, window.NameAvatar (components.jsx)
//               window.CLIENTS, window.LEADS, window.INVOICES, window.DIARY_EVENTS,
//               window.ONBOARDING_STEPS, window.DOCS (data.jsx)

const STAGES = ['Lead','Prospect','Demo','Proposal','Onboarding','Active','Churned'];
const PLAN_PRICES = { 'Basic': 550, 'All-Inclusive': 1500, 'Custom': 0 };

/* ════════════════════════════════════════════════════════════════════
   SUPABASE HELPERS — used by ClientsPage, LeadsPage, OnboardingPage
   window.pccDb is initialised in supabase-rest.jsx
════════════════════════════════════════════════════════════════════ */
function slugify(s) {
  return (s || '').toLowerCase().trim()
    .replace(/[^a-z0-9]+/g, '-')
    .replace(/^-+|-+$/g, '')
    .slice(0, 32);
}
function prefixOf(s) {
  return (s || '').toUpperCase().replace(/[^A-Z]/g, '').slice(0, 2) || 'PA';
}
function fmtR(n) {
  return 'R ' + Number(n || 0).toLocaleString('en-ZA');
}

/* Build a mailto: link that opens Tiaan's own mail client pre-filled with the
   onboarding-documents request. The owner reviews and sends manually — nothing
   is auto-sent, no credentials touch the browser. */
function buildOnboardingMailto(client) {
  const to      = (client.contact_email || '').trim();
  const name    = (client.contact_name || client.business_name || 'there').trim();
  const subject = `Pinnacle PA — Onboarding documents for ${client.business_name || 'your business'}`;
  const lines = [
    `Hi ${name},`,
    '',
    `Welcome to Pinnacle PA. To complete the setup of your ${client.plan ? client.plan + ' ' : ''}package, please complete and return the documents below:`,
    '',
    '  1. Signed Service Agreement',
    '  2. Debit Order Mandate',
    '  3. FICA documents — ID / passport, proof of address (not older than 3 months), and CIPC business registration',
    '  4. Bank confirmation letter',
    client.vat_registered ? '  5. VAT registration certificate' : null,
    '',
    'Simply reply to this email with the completed documents attached. Let me know if anything is unclear.',
    '',
    'Kind regards,',
    'Tiaan',
    'Pinnacle PA',
    'info@pinnaclepa.biz · 076 919 0182',
  ].filter(l => l !== null);
  const body = lines.join('\r\n');
  return `mailto:${to}?subject=${encodeURIComponent(subject)}&body=${encodeURIComponent(body)}`;
}

/* Open the pre-filled onboarding email. Guards against a missing recipient so
   the owner is told to add a contact email rather than getting a blank compose. */
function emailOnboardingDocs(client) {
  if (!client.contact_email || !client.contact_email.trim()) {
    alert(`No contact email on file for ${client.business_name}. Add one via Edit first.`);
    return;
  }
  window.open(buildOnboardingMailto(client), '_blank');
  try {
    window.pccDb.logActivity('client.email_docs', client.business_name,
      `Onboarding documents email opened → ${client.contact_email}`);
  } catch (_) {}
}

/* ════════════════════════════════════════════════════════════════════
   CLIENTS PAGE — fully Supabase-backed (master_clients table)
   Field mapping: business_name, contact_name, contact_email,
                  contact_phone, pipeline_stage, monthly_value, plan,
                  subdomain_slug, stock_prefix, address, city, province
════════════════════════════════════════════════════════════════════ */
const EMPTY_CLIENT_FORM = {
  business_name: '', contact_name: '', contact_email: '', contact_phone: '',
  pipeline_stage: 'Lead', plan: 'Basic', monthly_value: '550',
  subdomain_slug: '', stock_prefix: '',
  address: '', city: '', province: 'Gauteng',
  vat_registered: false, vat_number: '',
  mandate_signed: false, terms_accepted: false,
  notes: '', setup_fee_paid: false,
};

function ClientsPage() {
  const [clients, setClients] = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const [error, setError]     = React.useState(null);
  const [showAdd, setShowAdd] = React.useState(false);
  const [form, setForm]       = React.useState(EMPTY_CLIENT_FORM);
  const [editId, setEditId]   = React.useState(null);
  const [saving, setSaving]   = React.useState(false);

  // ── Fetch from Supabase on mount ────────────────────────────────────
  const fetchClients = React.useCallback(async () => {
    try {
      setError(null);
      const rows = await window.pccDb.clients.list();
      setClients(rows || []);
      window.CLIENTS = rows || [];
    } catch (e) {
      setError(e.message);
    } finally {
      setLoading(false);
    }
  }, []);

  React.useEffect(() => { fetchClients(); }, [fetchClients]);

  // ── Form handlers ───────────────────────────────────────────────────
  function openAdd() {
    setForm(EMPTY_CLIENT_FORM);
    setEditId(null);
    setShowAdd(true);
  }
  function openEdit(c) {
    setForm({
      business_name:  c.business_name  || '',
      contact_name:   c.contact_name   || '',
      contact_email:  c.contact_email  || '',
      contact_phone:  c.contact_phone  || '',
      pipeline_stage: c.pipeline_stage || 'Lead',
      plan:           c.plan           || 'Basic',
      monthly_value:  String(c.monthly_value || 550),
      subdomain_slug: c.subdomain_slug || '',
      stock_prefix:   c.stock_prefix   || '',
      address:        c.address        || '',
      city:           c.city           || '',
      province:       c.province       || 'Gauteng',
      vat_registered: !!c.vat_registered,
      vat_number:     c.vat_number     || '',
      mandate_signed: !!c.mandate_signed_at,
      terms_accepted: !!c.terms_accepted_at,
      notes:          c.notes          || '',
      setup_fee_paid: !!c.setup_fee_paid,
    });
    setEditId(c.id);
    setShowAdd(true);
  }

  // Auto-derive slug + prefix from business name as user types
  function setName(name) {
    setForm(f => ({
      ...f,
      business_name: name,
      subdomain_slug: f.subdomain_slug || slugify(name),
      stock_prefix:   f.stock_prefix   || prefixOf(name),
    }));
  }
  function setPlan(plan) {
    setForm(f => ({
      ...f,
      plan,
      monthly_value: plan === 'Custom' ? f.monthly_value : String(PLAN_PRICES[plan]),
    }));
  }

  async function handleSave() {
    if (!form.business_name.trim()) return;
    setSaving(true);
    const now = new Date().toISOString();
    const payload = {
      business_name:  form.business_name.trim(),
      contact_name:   form.contact_name.trim() || null,
      contact_email:  form.contact_email.trim() || null,
      contact_phone:  form.contact_phone.trim() || null,
      pipeline_stage: form.pipeline_stage,
      plan:           form.plan,
      monthly_value:  Number(form.monthly_value) || 0,
      subdomain_slug: form.subdomain_slug.trim() || null,
      stock_prefix:   form.stock_prefix.trim().toUpperCase() || null,
      address:        form.address.trim() || null,
      city:           form.city.trim() || null,
      province:       form.province || null,
      vat_registered: !!form.vat_registered,
      vat_number:     form.vat_registered ? (form.vat_number.trim() || null) : null,
      mandate_signed_at: form.mandate_signed ? now : null,
      terms_accepted_at: form.terms_accepted ? now : null,
      setup_fee_paid: !!form.setup_fee_paid,
      notes:          form.notes.trim() || null,
      signup_source:  'manual',
    };
    try {
      if (editId) {
        const updated = await window.pccDb.clients.update(editId, payload);
        setClients(prev => prev.map(c => c.id === editId ? updated : c));
        window.pccDb.logActivity('client.updated', updated.business_name, `Stage: ${updated.pipeline_stage}`);
      } else {
        const created = await window.pccDb.clients.create(payload);
        setClients(prev => [created, ...prev]);
        window.pccDb.logActivity('client.created', created.business_name, `Plan: ${created.plan} · ${fmtR(created.monthly_value)}/mo`);
      }
      setShowAdd(false);
    } catch (e) {
      alert('Save failed: ' + e.message);
    } finally {
      setSaving(false);
    }
  }

  async function handleDelete(c) {
    if (!window.confirm(`Delete ${c.business_name}? This cannot be undone.`)) return;
    try {
      await window.pccDb.clients.remove(c.id);
      setClients(prev => prev.filter(x => x.id !== c.id));
      window.pccDb.logActivity('client.deleted', c.business_name);
    } catch (e) {
      alert('Delete failed: ' + e.message);
    }
  }

  // ── Calculations ────────────────────────────────────────────────────
  const calc = window.pccDb.calcPipeline(clients);

  // ── Render ──────────────────────────────────────────────────────────
  return (
    <div className="page-content">
      <PageHead eyebrow="RELATIONSHIPS" title="Clients"
        lede="Live pipeline backed by Supabase. New clients default to Basic — override per-client."
        action={
          <button className="btn-primary" onClick={openAdd}>
            <Icon name="plus" size={15} color="var(--gold-700)" /> New Client
          </button>
        } />

      {loading && (
        <div style={{ padding:'40px 0', textAlign:'center', color:'var(--muted)', fontSize:13 }}>
          Loading from Supabase…
        </div>
      )}
      {error && (
        <div style={{ padding:12, background:'rgba(255,107,107,0.1)', border:'1px solid #FF6B6B', borderRadius:8, color:'#FF6B6B', fontSize:12, marginBottom:12 }}>
          Supabase error: {error}
        </div>
      )}

      {!loading && (
        <>
          <div className="kpi-grid">
            <KPI label="Active MRR"        value={Number(calc.mrrActive).toLocaleString('en-ZA')} currency="R" trend={`${calc.active} client${calc.active!==1?'s':''}`} trendDir="up" />
            <KPI label="Weighted Pipeline" value={Number(calc.weightedMrr).toLocaleString('en-ZA')} currency="R" trend="monthly · risk-adjusted" trendDir="flat" />
            <KPI label="ARR (Active)"      value={Number(calc.arrActive).toLocaleString('en-ZA')} currency="R" trend="12 × MRR" trendDir="up" />
            <KPI label="Conversion"        value={`${calc.conversionPct}%`} trend={`${calc.leads} in pipeline`} trendDir="flat" />
          </div>

          <Card sub="PIPELINE" title="Deal Flow">
            <div className="pipeline">
              {STAGES.map(stage => {
                const items = clients.filter(c => (c.pipeline_stage||'Lead') === stage);
                return (
                  <div key={stage} className="kanban-col">
                    <div className="kanban-col-head">
                      <span>{stage}</span>
                      <span className="kanban-count">{items.length}</span>
                    </div>
                    {items.map(c => (
                      <div key={c.id} className="kanban-card" style={{ cursor:'pointer' }} onClick={() => openEdit(c)}>
                        <div className="kanban-name">{c.business_name}</div>
                        <div className="kanban-meta">
                          {c.plan && <span>{c.plan}</span>}
                          {Number(c.monthly_value) > 0 && <span className="kanban-val"> · {fmtR(c.monthly_value)}</span>}
                        </div>
                      </div>
                    ))}
                  </div>
                );
              })}
            </div>
          </Card>

          <Card sub="ALL CLIENTS" title="Client list" style={{ marginTop:16 }}>
            {clients.length === 0 ? (
              <div style={{ padding:'24px 0', textAlign:'center', color:'var(--muted)', fontSize:13, fontStyle:'italic' }}>
                No clients yet — click New Client to add one.
              </div>
            ) : (
              <table className="tbl">
                <thead>
                  <tr><th>Code</th><th>Business</th><th>Stage</th><th>Plan</th><th>Monthly</th><th>Provisioned</th><th>Actions</th></tr>
                </thead>
                <tbody>
                  {clients.map(c => (
                    <tr key={c.id}>
                      <td style={{ fontFamily:"'JetBrains Mono',monospace", fontSize:11 }}>{c.client_code || '—'}</td>
                      <td style={{ fontFamily:"'Cormorant Garamond',serif", fontStyle:'italic', fontSize:15 }}>{c.business_name}</td>
                      <td><StageBadge stage={c.pipeline_stage || 'Lead'} /></td>
                      <td>{c.plan || '—'}</td>
                      <td style={{ fontWeight:600 }}>{Number(c.monthly_value) > 0 ? fmtR(c.monthly_value) : '—'}</td>
                      <td style={{ fontSize:11 }}>
                        {c.provisioned
                          ? <span style={{ color:'#00E5A0' }}>✓ Live</span>
                          : <span style={{ color:'var(--muted)' }}>{c.provisioning_status || 'pending'}</span>}
                      </td>
                      <td style={{ display:'flex', gap:6 }}>
                        <button className="btn-ghost" style={{ fontSize:11, padding:'3px 8px' }} onClick={() => openEdit(c)}>Edit</button>
                        <button className="btn-ghost" style={{ fontSize:11, padding:'3px 8px' }}
                          title={c.contact_email ? `Email onboarding documents to ${c.contact_email}` : 'No contact email on file'}
                          onClick={() => emailOnboardingDocs(c)}>
                          <Icon name="mail" size={12} color="var(--gold-400)" />
                        </button>
                        <button className="btn-ghost" style={{ fontSize:11, padding:'3px 8px', color:'var(--ruby-400)', borderColor:'var(--ruby-400)' }}
                          onClick={() => handleDelete(c)}>✕</button>
                      </td>
                    </tr>
                  ))}
                </tbody>
              </table>
            )}
          </Card>
        </>
      )}

      {/* Add / Edit modal — comprehensive dealer signup form */}
      {showAdd && (
        <div className="modal-overlay" onClick={e => e.target===e.currentTarget && setShowAdd(false)}>
          <div className="modal" style={{ maxWidth:720 }}>
            <div className="modal-title">{editId ? 'Edit Client' : 'New Client'} — All dealer signup data</div>
            <div className="modal-grid">
              {/* ── Business identity ──────────────────────────── */}
              <div className="field full">
                <label>Business Name *</label>
                <input className="input" value={form.business_name}
                  onChange={e => setName(e.target.value)}
                  placeholder="e.g. Apex Motors" autoFocus />
              </div>
              <div className="field">
                <label>Contact Person</label>
                <input className="input" value={form.contact_name}
                  onChange={e => setForm(f=>({...f,contact_name:e.target.value}))}
                  placeholder="Owner / decision maker" />
              </div>
              <div className="field">
                <label>Phone</label>
                <input className="input" type="tel" value={form.contact_phone}
                  onChange={e => setForm(f=>({...f,contact_phone:e.target.value}))}
                  placeholder="082 123 4567" />
              </div>
              <div className="field full">
                <label>Email</label>
                <input className="input" type="email" value={form.contact_email}
                  onChange={e => setForm(f=>({...f,contact_email:e.target.value}))}
                  placeholder="owner@business.co.za" />
              </div>

              {/* ── Address ───────────────────────────────────── */}
              <div className="field full">
                <label>Address</label>
                <input className="input" value={form.address}
                  onChange={e => setForm(f=>({...f,address:e.target.value}))}
                  placeholder="123 Main Road" />
              </div>
              <div className="field">
                <label>City</label>
                <input className="input" value={form.city}
                  onChange={e => setForm(f=>({...f,city:e.target.value}))}
                  placeholder="Pretoria" />
              </div>
              <div className="field">
                <label>Province</label>
                <select className="input" value={form.province}
                  onChange={e => setForm(f=>({...f,province:e.target.value}))}>
                  {['Gauteng','Western Cape','KwaZulu-Natal','Eastern Cape','Free State','Limpopo','Mpumalanga','North West','Northern Cape'].map(p => <option key={p}>{p}</option>)}
                </select>
              </div>

              {/* ── Pipeline + Plan ───────────────────────────── */}
              <div className="field">
                <label>Pipeline Stage</label>
                <select className="input" value={form.pipeline_stage}
                  onChange={e => setForm(f=>({...f,pipeline_stage:e.target.value}))}>
                  {STAGES.map(s => <option key={s}>{s}</option>)}
                </select>
              </div>
              <div className="field">
                <label>Plan</label>
                <select className="input" value={form.plan}
                  onChange={e => setPlan(e.target.value)}>
                  <option value="Basic">Basic — R 550/mo</option>
                  <option value="All-Inclusive">All-Inclusive — R 1,500/mo</option>
                  <option value="Custom">Custom (override)</option>
                </select>
              </div>
              <div className="field full">
                <label>Monthly Value (R) — {form.plan === 'Custom' ? 'enter custom amount' : 'override only if needed'}</label>
                <input className="input" type="number" min="0" value={form.monthly_value}
                  onChange={e => setForm(f=>({...f,monthly_value:e.target.value}))} />
              </div>

              {/* ── Dealer dashboard provisioning ─────────────── */}
              <div className="field">
                <label>Subdomain Slug</label>
                <input className="input" value={form.subdomain_slug}
                  onChange={e => setForm(f=>({...f,subdomain_slug:slugify(e.target.value)}))}
                  placeholder="apex-motors" />
                <div style={{ fontSize:10, color:'var(--muted)', marginTop:4 }}>
                  → {form.subdomain_slug || 'slug'}.dealer-dash.pinnaclepa.biz
                </div>
              </div>
              <div className="field">
                <label>Stock Prefix (2 letters)</label>
                <input className="input" value={form.stock_prefix} maxLength="2"
                  onChange={e => setForm(f=>({...f,stock_prefix:e.target.value.toUpperCase().replace(/[^A-Z]/g,'')}))}
                  placeholder="AM" />
                <div style={{ fontSize:10, color:'var(--muted)', marginTop:4 }}>
                  → {form.stock_prefix || 'AM'}-2605-0001
                </div>
              </div>

              {/* ── Compliance & VAT ──────────────────────────── */}
              <div className="field">
                <label>
                  <input type="checkbox" checked={form.vat_registered}
                    onChange={e => setForm(f=>({...f,vat_registered:e.target.checked}))} />
                  &nbsp; VAT Registered
                </label>
              </div>
              {form.vat_registered && (
                <div className="field">
                  <label>VAT Number</label>
                  <input className="input" value={form.vat_number}
                    onChange={e => setForm(f=>({...f,vat_number:e.target.value}))}
                    placeholder="4xxxxxxxxx" />
                </div>
              )}

              {/* ── Setup tracking ────────────────────────────── */}
              <div className="field">
                <label>
                  <input type="checkbox" checked={form.setup_fee_paid}
                    onChange={e => setForm(f=>({...f,setup_fee_paid:e.target.checked}))} />
                  &nbsp; Setup Fee Paid (EFT received)
                </label>
              </div>
              <div className="field">
                <label>
                  <input type="checkbox" checked={form.mandate_signed}
                    onChange={e => setForm(f=>({...f,mandate_signed:e.target.checked}))} />
                  &nbsp; Debit Order Mandate Signed
                </label>
              </div>
              <div className="field full">
                <label>
                  <input type="checkbox" checked={form.terms_accepted}
                    onChange={e => setForm(f=>({...f,terms_accepted:e.target.checked}))} />
                  &nbsp; Terms &amp; Conditions Accepted
                </label>
              </div>

              {/* ── Notes ─────────────────────────────────────── */}
              <div className="field full">
                <label>Notes</label>
                <textarea className="input" value={form.notes}
                  onChange={e => setForm(f=>({...f,notes:e.target.value}))}
                  placeholder="Anything Claude Code needs to know during provisioning…"
                  style={{ minHeight:60, resize:'vertical' }} />
              </div>
            </div>
            <div className="modal-foot">
              <button className="btn-ghost" onClick={() => setShowAdd(false)} disabled={saving}>Cancel</button>
              <button className="btn-primary" onClick={handleSave} disabled={saving}>
                {saving ? 'Saving…' : (editId ? 'Save Changes' : 'Add Client')}
              </button>
            </div>
          </div>
        </div>
      )}
    </div>
  );
}

/* ════════════════════════════════════════════════════════════════════
   ONBOARDING PAGE — live view of clients in Onboarding stage
   Steps are auto-created by trg_default_onboarding when stage flips
════════════════════════════════════════════════════════════════════ */
function OnboardingPage() {
  const [clients, setClients] = React.useState([]);
  const [stepsMap, setStepsMap] = React.useState({});  // {client_id: [step,…]}
  const [loading, setLoading] = React.useState(true);
  const [error, setError]     = React.useState(null);

  const refresh = React.useCallback(async () => {
    try {
      setError(null);
      const all = await window.pccDb.clients.list();
      const onboarding = (all || []).filter(c => c.pipeline_stage === 'Onboarding');
      setClients(onboarding);
      // Fetch steps for each onboarding client in parallel
      const stepsArr = await Promise.all(
        onboarding.map(c => window.pccDb.onboarding.list(`client_id=eq.${c.id}&order=step_order.asc`).catch(() => []))
      );
      const map = {};
      onboarding.forEach((c, i) => { map[c.id] = stepsArr[i] || []; });
      setStepsMap(map);
    } catch (e) {
      setError(e.message);
    } finally {
      setLoading(false);
    }
  }, []);

  React.useEffect(() => { refresh(); }, [refresh]);

  async function toggleStep(step) {
    try {
      const updated = await window.pccDb.onboarding.update(step.id, {
        completed:    !step.completed,
        completed_at: !step.completed ? new Date().toISOString() : null,
      });
      setStepsMap(prev => {
        const next = { ...prev };
        next[step.client_id] = (next[step.client_id] || []).map(s => s.id === step.id ? updated : s);
        return next;
      });
    } catch (e) {
      alert('Could not update step: ' + e.message);
    }
  }

  async function promoteToActive(client) {
    if (!window.confirm(`Move ${client.business_name} to Active? This marks onboarding complete.`)) return;
    try {
      await window.pccDb.clients.update(client.id, {
        pipeline_stage: 'Active',
        subscription_status: 'active',
      });
      window.pccDb.logActivity('client.promoted', client.business_name, 'Onboarding → Active');
      refresh();
    } catch (e) {
      alert('Promotion failed: ' + e.message);
    }
  }

  return (
    <div className="page-content">
      <PageHead eyebrow="ONBOARDING" title="Client Setup"
        lede="Live 6-step checklist auto-created when a client enters Onboarding stage."
        action={
          <button className="btn-primary" onClick={() => window.__nav && window.__nav('clients')}>
            <Icon name="plus" size={15} color="var(--gold-700)" /> New Client
          </button>
        } />

      {loading && <div style={{ padding:'40px 0', textAlign:'center', color:'var(--muted)', fontSize:13 }}>Loading…</div>}
      {error && <div style={{ padding:12, background:'rgba(255,107,107,0.1)', border:'1px solid #FF6B6B', borderRadius:8, color:'#FF6B6B', fontSize:12, marginBottom:12 }}>Error: {error}</div>}

      {!loading && clients.length === 0 && (
        <Card>
          <div style={{ padding:'24px 0', textAlign:'center', color:'var(--muted)', fontSize:13, fontStyle:'italic' }}>
            No clients in Onboarding stage yet.<br />
            Click <strong>New Client</strong> above (or set an existing client's stage to <strong>Onboarding</strong> on the Clients page) — the 6-step checklist auto-generates here, and you can then email them their documents.
          </div>
        </Card>
      )}

      {!loading && clients.length > 0 && (
        <div style={{ display:'grid', gridTemplateColumns:'repeat(auto-fill,minmax(380px,1fr))', gap:16 }}>
          {clients.map(client => {
            const steps  = stepsMap[client.id] || [];
            const done   = steps.filter(s => s.completed).length;
            const total  = steps.length;
            const allDone = total > 0 && done === total;
            return (
              <Card key={client.id} sub={client.client_code || 'NEW'} title={client.business_name}>
                <div style={{ fontSize:11, color:'var(--muted)', marginBottom:8 }}>
                  {client.subdomain_slug
                    ? <>→ <span style={{ color:'var(--gold-400)' }}>{client.subdomain_slug}.dealer-dash.pinnaclepa.biz</span></>
                    : <span style={{ color:'#FF6B6B' }}>⚠ no subdomain set</span>}
                </div>
                <div style={{ fontSize:11, color:'var(--muted)', marginBottom:12 }}>
                  Provisioning: {client.provisioned
                    ? <span style={{ color:'#00E5A0' }}>✓ Live ({client.dashboard_url || '—'})</span>
                    : <span style={{ color:'#FFB347' }}>● {client.provisioning_status || 'pending'} (Claude Code will pick up)</span>}
                </div>
                <div className="prov-steps">
                  {total === 0 ? (
                    <div style={{ fontSize:11, color:'var(--muted)', fontStyle:'italic', padding:'8px 0' }}>
                      No steps yet — trigger should fire on next stage update.
                    </div>
                  ) : steps.map(step => (
                    <div key={step.id} className="prov-step" style={{ cursor:'pointer' }} onClick={() => toggleStep(step)}>
                      <div className={`prov-num${step.completed ? ' done' : ''}`}>
                        {step.completed ? '✓' : step.step_order}
                      </div>
                      <div className={`prov-label${step.completed ? ' done' : ''}`}>{step.step_label}</div>
                    </div>
                  ))}
                </div>
                <div style={{ display:'flex', justifyContent:'space-between', alignItems:'center', marginTop:12, fontSize:11, color:'var(--muted)' }}>
                  <span>{done}/{total} complete</span>
                  <div style={{ display:'flex', gap:6 }}>
                    <button className="btn-ghost" style={{ fontSize:11, padding:'4px 10px' }}
                      title={client.contact_email ? `Email onboarding documents to ${client.contact_email}` : 'No contact email on file'}
                      onClick={() => emailOnboardingDocs(client)}>
                      <Icon name="mail" size={13} color="var(--gold-400)" /> Email documents
                    </button>
                    {allDone && (
                      <button className="btn-primary" style={{ fontSize:11, padding:'4px 10px' }} onClick={() => promoteToActive(client)}>
                        Move to Active →
                      </button>
                    )}
                  </div>
                </div>
              </Card>
            );
          })}
        </div>
      )}
    </div>
  );
}

/* ════════════════════════════════════════════════════════════════════
   LEADS PAGE — fully Supabase-backed (leads table)
   Field mapping: business_name, contact_name, contact_email,
                  contact_phone, source, message, status
════════════════════════════════════════════════════════════════════ */
const EMPTY_LEAD_FORM = {
  business_name:'', contact_name:'', contact_email:'', contact_phone:'',
  source:'referral', message:'', status:'new',
};

function LeadsPage() {
  const [leads, setLeads]     = React.useState([]);
  const [clientCount, setClientCount] = React.useState(0);
  const [loading, setLoading] = React.useState(true);
  const [error, setError]     = React.useState(null);
  const [showAdd, setShowAdd] = React.useState(false);
  const [form, setForm]       = React.useState(EMPTY_LEAD_FORM);
  const [editId, setEditId]   = React.useState(null);
  const [saving, setSaving]   = React.useState(false);

  // Values MUST match the DB CHECK constraints (leads_source_check / leads_status_check).
  const SOURCES = [
    { v:'referral', l:'Referral' }, { v:'website', l:'Website' },
    { v:'outreach', l:'Outreach (cold call / LinkedIn)' }, { v:'demo_request', l:'Demo request' },
    { v:'manual', l:'Manual / other' },
  ];
  const STATUSES = ['new','contacted','quoted','converted','lost'];

  const refresh = React.useCallback(async () => {
    try {
      setError(null);
      const [l, c] = await Promise.all([
        window.pccDb.leads.list(),
        window.pccDb.clients.list(),
      ]);
      setLeads(l || []);
      setClientCount((c || []).length);
      window.LEADS = l || [];
    } catch (e) {
      setError(e.message);
    } finally {
      setLoading(false);
    }
  }, []);

  React.useEffect(() => { refresh(); }, [refresh]);

  function openAdd() { setForm(EMPTY_LEAD_FORM); setEditId(null); setShowAdd(true); }
  function openEdit(l) {
    setForm({
      business_name: l.business_name || '',
      contact_name:  l.contact_name  || '',
      contact_email: l.contact_email || '',
      contact_phone: l.contact_phone || '',
      source:        l.source        || 'Referral',
      message:       l.message       || '',
      status:        l.status        || 'new',
    });
    setEditId(l.id);
    setShowAdd(true);
  }

  async function handleSave() {
    if (!form.business_name.trim()) return;
    setSaving(true);
    const payload = {
      business_name: form.business_name.trim(),
      contact_name:  form.contact_name.trim() || null,
      contact_email: form.contact_email.trim() || null,
      contact_phone: form.contact_phone.trim() || null,
      source:        form.source,
      message:       form.message.trim() || null,
      status:        form.status,
    };
    try {
      if (editId) {
        const updated = await window.pccDb.leads.update(editId, payload);
        setLeads(prev => prev.map(l => l.id === editId ? updated : l));
      } else {
        const created = await window.pccDb.leads.create(payload);
        setLeads(prev => [created, ...prev]);
        window.pccDb.logActivity('lead.created', created.business_name, `Source: ${created.source}`);
      }
      setShowAdd(false);
    } catch (e) {
      alert('Save failed: ' + e.message);
    } finally {
      setSaving(false);
    }
  }

  async function handleDelete(l) {
    if (!window.confirm(`Delete lead ${l.business_name}?`)) return;
    try {
      await window.pccDb.leads.remove(l.id);
      setLeads(prev => prev.filter(x => x.id !== l.id));
    } catch (e) {
      alert('Delete failed: ' + e.message);
    }
  }

  // Convert lead → client (writes to master_clients, marks lead as converted)
  async function convertToClient(l) {
    if (!window.confirm(`Convert ${l.business_name} to a Prospect-stage client?`)) return;
    try {
      const created = await window.pccDb.clients.create({
        business_name:  l.business_name,
        contact_name:   l.contact_name,
        contact_email:  l.contact_email,
        contact_phone:  l.contact_phone,
        pipeline_stage: 'Prospect',
        plan:           'Basic',
        monthly_value:  550,
        subdomain_slug: slugify(l.business_name),
        stock_prefix:   prefixOf(l.business_name),
        signup_source:  l.source === 'website' ? 'website' : 'manual',
        notes:          l.message,
      });
      await window.pccDb.leads.update(l.id, {
        status: 'converted',
        converted_to_client_id: created.id,
        closed_at: new Date().toISOString(),
      });
      window.pccDb.logActivity('lead.converted', l.business_name, `→ Client ${created.client_code || created.id}`);
      refresh();
    } catch (e) {
      alert('Conversion failed: ' + e.message);
    }
  }

  // ── Calculations ────────────────────────────────────────────────────
  const totalLeads   = leads.length;
  const newCount     = leads.filter(l => l.status === 'new').length;
  const qualified    = leads.filter(l => l.status === 'qualified').length;
  const converted    = leads.filter(l => l.status === 'converted').length;
  const pct = n => totalLeads > 0 ? Math.round(n/totalLeads*100) : 0;
  const funnel = [
    { label:'Total Leads',  pct: totalLeads>0 ? 100 : 0 },
    { label:'Qualified',    pct: pct(qualified)         },
    { label:'Converted',    pct: pct(converted)         },
  ];

  return (
    <div className="page-content">
      <PageHead eyebrow="PIPELINE" title="Leads"
        lede="Inbound prospects from website + manual entries. Convert to client to start the pipeline."
        action={
          <button className="btn-primary" onClick={openAdd}>
            <Icon name="plus" size={15} color="var(--gold-700)" /> New Lead
          </button>
        } />

      {loading && <div style={{ padding:'40px 0', textAlign:'center', color:'var(--muted)', fontSize:13 }}>Loading…</div>}
      {error && <div style={{ padding:12, background:'rgba(255,107,107,0.1)', border:'1px solid #FF6B6B', borderRadius:8, color:'#FF6B6B', fontSize:12, marginBottom:12 }}>Error: {error}</div>}

      {!loading && (
        <div style={{ display:'grid', gridTemplateColumns:'300px 1fr', gap:16 }}>
          <Card sub="CONVERSION FUNNEL" title="Flow">
            <div className="funnel" style={{ marginTop:12 }}>
              {funnel.map((f,i) => (
                <div key={i} className="funnel-row">
                  <div className="funnel-label">{f.label}</div>
                  <div className="funnel-bar-wrap"><div className="funnel-bar" style={{ width:`${f.pct}%` }} /></div>
                  <div className="funnel-pct">{f.pct}%</div>
                </div>
              ))}
            </div>
            <div style={{ marginTop:16, padding:12, background:'var(--glass-2)', borderRadius:8, fontSize:11, color:'var(--muted)' }}>
              <div><strong style={{ color:'var(--ink)' }}>{newCount}</strong> awaiting first contact</div>
              <div><strong style={{ color:'var(--ink)' }}>{clientCount}</strong> live clients in pipeline</div>
            </div>
          </Card>
          <Card sub="ALL LEADS" title="Prospects">
            {leads.length === 0 ? (
              <div style={{ padding:'24px 0', textAlign:'center', color:'var(--muted)', fontSize:13, fontStyle:'italic' }}>
                No leads yet — click New Lead to add one.
              </div>
            ) : (
              <table className="tbl">
                <thead>
                  <tr><th>Business</th><th>Source</th><th>Status</th><th>Date</th><th>Actions</th></tr>
                </thead>
                <tbody>
                  {leads.map(l => (
                    <tr key={l.id}>
                      <td style={{ fontFamily:"'Cormorant Garamond',serif", fontStyle:'italic', fontSize:15 }}>
                        {l.business_name}
                        {l.contact_name && <div style={{ fontFamily:'Manrope', fontStyle:'normal', fontSize:11, color:'var(--muted)' }}>{l.contact_name}</div>}
                      </td>
                      <td><span className="badge badge-lead">{l.source}</span></td>
                      <td style={{ fontSize:11, color: l.status==='converted' ? '#00E5A0' : l.status==='lost' ? '#FF6B6B' : 'var(--muted)' }}>{l.status}</td>
                      <td style={{ color:'var(--muted)', fontSize:12 }}>
                        {l.created_at && new Date(l.created_at).toLocaleDateString('en-ZA',{day:'numeric',month:'short'})}
                      </td>
                      <td style={{ display:'flex', gap:6 }}>
                        {l.status !== 'converted' && (
                          <button className="btn-primary" style={{ fontSize:10, padding:'3px 8px' }} onClick={() => convertToClient(l)}>→ Client</button>
                        )}
                        <button className="btn-ghost" style={{ fontSize:11, padding:'3px 8px' }} onClick={() => openEdit(l)}>Edit</button>
                        <button className="btn-ghost" style={{ fontSize:11, padding:'3px 8px', color:'var(--ruby-400)', borderColor:'var(--ruby-400)' }}
                          onClick={() => handleDelete(l)}>✕</button>
                      </td>
                    </tr>
                  ))}
                </tbody>
              </table>
            )}
          </Card>
        </div>
      )}

      {/* Add / Edit modal */}
      {showAdd && (
        <div className="modal-overlay" onClick={e => e.target===e.currentTarget && setShowAdd(false)}>
          <div className="modal">
            <div className="modal-title">{editId ? 'Edit Lead' : 'New Lead'}</div>
            <div className="modal-grid">
              <div className="field full">
                <label>Business Name *</label>
                <input className="input" value={form.business_name}
                  onChange={e => setForm(f=>({...f,business_name:e.target.value}))}
                  placeholder="e.g. Blue Sky Transport" autoFocus />
              </div>
              <div className="field">
                <label>Contact Person</label>
                <input className="input" value={form.contact_name}
                  onChange={e => setForm(f=>({...f,contact_name:e.target.value}))} />
              </div>
              <div className="field">
                <label>Phone</label>
                <input className="input" type="tel" value={form.contact_phone}
                  onChange={e => setForm(f=>({...f,contact_phone:e.target.value}))} />
              </div>
              <div className="field full">
                <label>Email</label>
                <input className="input" type="email" value={form.contact_email}
                  onChange={e => setForm(f=>({...f,contact_email:e.target.value}))} />
              </div>
              <div className="field">
                <label>Source</label>
                <select className="input" value={form.source}
                  onChange={e => setForm(f=>({...f,source:e.target.value}))}>
                  {SOURCES.map(s => <option key={s.v} value={s.v}>{s.l}</option>)}
                </select>
              </div>
              <div className="field">
                <label>Status</label>
                <select className="input" value={form.status}
                  onChange={e => setForm(f=>({...f,status:e.target.value}))}>
                  {STATUSES.map(s => <option key={s} value={s}>{s.charAt(0).toUpperCase()+s.slice(1)}</option>)}
                </select>
              </div>
              <div className="field full">
                <label>Message / Notes</label>
                <textarea className="input" value={form.message}
                  onChange={e => setForm(f=>({...f,message:e.target.value}))}
                  placeholder="What did they ask for? Referral name? Next step?"
                  style={{ minHeight:60, resize:'vertical' }} />
              </div>
            </div>
            <div className="modal-foot">
              <button className="btn-ghost" onClick={() => setShowAdd(false)} disabled={saving}>Cancel</button>
              <button className="btn-primary" onClick={handleSave} disabled={saving}>
                {saving ? 'Saving…' : (editId ? 'Save Changes' : 'Add Lead')}
              </button>
            </div>
          </div>
        </div>
      )}
    </div>
  );
}

/* ════════════════════════════════════════════════════════════════════
   PROVISIONING PAGE — Claude Code handoff queue dashboard
   Shows what's pending pickup, in progress, done, and failed
════════════════════════════════════════════════════════════════════ */
function ProvisioningPage() {
  const [clients, setClients] = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const [error, setError]     = React.useState(null);

  const refresh = React.useCallback(async () => {
    try {
      setError(null);
      const all = await window.pccDb.clients.list();
      // Only show clients that have entered the provisioning lifecycle
      // (Onboarding stage onwards, OR already provisioned)
      const queue = (all || []).filter(c =>
        ['Onboarding','Active'].includes(c.pipeline_stage) || c.provisioned
      );
      setClients(queue);
    } catch (e) {
      setError(e.message);
    } finally {
      setLoading(false);
    }
  }, []);

  React.useEffect(() => { refresh(); }, [refresh]);

  const pending     = clients.filter(c => !c.provisioned && (c.provisioning_status||'pending') === 'pending');
  const inProgress  = clients.filter(c => !c.provisioned && c.provisioning_status === 'in_progress');
  const done        = clients.filter(c => c.provisioned);
  const failed      = clients.filter(c => c.provisioning_status === 'failed');

  return (
    <div className="page-content">
      <PageHead eyebrow="HANDOFF" title="Provisioning Queue"
        lede="Live status of dealer-dashboard provisioning. Claude Code polls master_clients WHERE pipeline_stage='Onboarding' AND provisioned=false." />

      {loading && <div style={{ padding:'40px 0', textAlign:'center', color:'var(--muted)', fontSize:13 }}>Loading…</div>}
      {error && <div style={{ padding:12, background:'rgba(255,107,107,0.1)', border:'1px solid #FF6B6B', borderRadius:8, color:'#FF6B6B', fontSize:12, marginBottom:12 }}>Error: {error}</div>}

      {!loading && (
        <>
          <div className="kpi-grid">
            <KPI label="Pending"     value={pending.length}    trend="awaiting Claude Code"    trendDir="flat" />
            <KPI label="In Progress" value={inProgress.length} trend="being provisioned"        trendDir="flat" />
            <KPI label="Live"        value={done.length}       trend="dealer dashboards online" trendDir="up" />
            <KPI label="Failed"      value={failed.length}     trend="needs review"             trendDir="down" flag={failed.length > 0 ? 'ruby' : undefined} />
          </div>

          {clients.length === 0 && (
            <Card style={{ marginTop:16 }}>
              <div style={{ padding:'24px 0', textAlign:'center', color:'var(--muted)', fontSize:13, fontStyle:'italic' }}>
                Queue is empty.<br />
                Move a client to "Onboarding" in the Clients page to trigger provisioning.
              </div>
            </Card>
          )}

          {clients.length > 0 && (
            <Card sub="QUEUE" title="All provisioning records" style={{ marginTop:16 }}>
              <table className="tbl">
                <thead>
                  <tr><th>Code</th><th>Dealer</th><th>Modules</th><th>Dashboard</th><th>Stage</th></tr>
                </thead>
                <tbody>
                  {clients.map(c => {
                    const pill = (label, on) => (
                      <span style={{ display:'inline-block', fontSize:10, fontWeight:700, padding:'2px 8px', borderRadius:999, marginRight:5,
                        border:'1px solid ' + (on ? 'var(--positive)' : 'var(--line-strong)'),
                        color: on ? 'var(--positive)' : 'var(--muted)', background: on ? 'rgba(102,187,106,0.12)' : 'transparent' }}>
                        {on ? '● ' : '○ '}{label}
                      </span>
                    );
                    return (
                      <tr key={c.id}>
                        <td style={{ fontFamily:"'JetBrains Mono',monospace", fontSize:11 }}>{c.client_code || '—'}</td>
                        <td>
                          <div style={{ fontFamily:"'Cormorant Garamond',serif", fontStyle:'italic', fontSize:14 }}>{c.business_name}</div>
                          {(c.contact_name || c.contact_phone) && (
                            <div style={{ fontSize:10, color:'var(--muted)' }}>{[c.contact_name, c.contact_phone].filter(Boolean).join(' · ')}</div>
                          )}
                        </td>
                        <td style={{ whiteSpace:'nowrap' }}>
                          {pill('Live', !!c.provisioned)}
                          {pill('Active', c.pipeline_stage === 'Active')}
                          {pill('Paid', !!c.setup_fee_paid)}
                        </td>
                        <td style={{ fontSize:11 }}>
                          {c.dashboard_url
                            ? <a href={c.dashboard_url} target="_blank" rel="noopener" style={{ color:'var(--gold-400)' }}>open ↗</a>
                            : c.subdomain_slug
                              ? <span style={{ fontFamily:"'JetBrains Mono',monospace", fontSize:10, color:'var(--muted)' }}>{c.subdomain_slug}.dealer-dash.pinnaclepa.biz</span>
                              : <span style={{ color:'var(--muted)' }}>—</span>}
                        </td>
                        <td><StageBadge stage={c.pipeline_stage} /></td>
                      </tr>
                    );
                  })}
                </tbody>
              </table>
            </Card>
          )}

          <Card sub="CLAUDE CODE" title="How the handoff works" style={{ marginTop:16 }}>
            <div style={{ fontSize:12, color:'var(--ink-2)', lineHeight:1.6 }}>
              When a client's <code>pipeline_stage</code> flips to <strong>Onboarding</strong>, two things happen automatically:
              <ol style={{ marginTop:8, paddingLeft:20 }}>
                <li>Trigger <code>trg_default_onboarding</code> creates 6 onboarding steps for the client</li>
                <li>Their row becomes pickup-eligible for Claude Code (<code>provisioned=false</code>, <code>provisioning_status='pending'</code>)</li>
              </ol>
              Claude Code polls Supabase, provisions the dealer dashboard subdomain, then writes back <code>provisioned=true</code> + <code>dashboard_url</code>. See <code>CLAUDE-CODE-HANDOFF-PROVISIONING.md</code> for the full spec.
            </div>
          </Card>
        </>
      )}
    </div>
  );
}

/* ════════════════════════════════════════════════════════════════════
   WEBSITE PAGE
════════════════════════════════════════════════════════════════════ */
function WebsitePage() {
  const DEFAULT_SITE = 'https://pinnaclepa.biz';
  const [url, setUrl]     = React.useState(() => localStorage.getItem('pcc-site-url') || DEFAULT_SITE);
  const [draft, setDraft] = React.useState(url);
  const [saved, setSaved] = React.useState(false);
  function saveUrl() {
    const u = draft.trim().replace(/\/$/, '');
    setUrl(u); localStorage.setItem('pcc-site-url', u); setSaved(true);
  }
  const base = url.replace(/\/$/, '');
  const pages = [['Home',''],['Services','/services'],['Pricing','/pricing'],['About','/about'],['Contact','/contact']];
  const linkStyle = { padding:'9px 0', borderBottom:'1px solid var(--line-cool)', fontSize:13, color:'var(--ink-2)', textDecoration:'none', display:'flex', alignItems:'center', gap:8 };

  return (
    <div className="page-content">
      <PageHead eyebrow="WEBSITE" title="Public Site"
        lede="Quick access to the live Pinnacle PA website (hosted on Netlify)."
        action={<a className="btn-primary" href={base} target="_blank" rel="noopener" style={{ textDecoration:'none' }}>
          <Icon name="globe" size={15} color="var(--gold-700)" /> Open live site ↗</a>} />
      <div style={{ display:'grid', gridTemplateColumns:'260px 1fr', gap:16 }}>
        <Card sub="PAGES" title="Quick links">
          {pages.map(([label, path]) => (
            <a key={label} href={base + path} target="_blank" rel="noopener" style={linkStyle}>
              <Icon name="chev" size={14} color="var(--muted)" />{label} <span style={{ color:'var(--muted)', fontSize:11 }}>↗</span>
            </a>
          ))}
        </Card>
        <Card sub="SITE ADDRESS" title="Live URL">
          <div style={{ fontSize:12, color:'var(--muted)', marginBottom:14, lineHeight:1.6 }}>
            The public marketing site is hosted on Netlify. Source lives at the project-root <code>index.html</code> — edit there and redeploy to publish.
          </div>
          <div className="field">
            <label>Public URL</label>
            <div style={{ display:'flex', gap:8 }}>
              <input className="input" value={draft} onChange={e => { setDraft(e.target.value); setSaved(false); }} style={{ flex:1 }} />
              <button className="btn-ghost" onClick={saveUrl}>Save</button>
            </div>
            {saved && <div style={{ fontSize:11, color:'var(--positive)', marginTop:6 }}>✓ Saved</div>}
          </div>
          <a href={base} target="_blank" rel="noopener" style={{ color:'var(--gold-400)', fontSize:13, fontWeight:600, textDecoration:'none' }}>{base} ↗</a>
        </Card>
      </div>
    </div>
  );
}

/* ════════════════════════════════════════════════════════════════════
   FINANCE PAGE
════════════════════════════════════════════════════════════════════ */
const INVOICE_SERVICE_CODES = ['DDS','WBS','QIN','PAY','OCR','BUS','CON','MNT'];
const INVOICE_STATUSES = ['draft','sent','paid','overdue','cancelled'];
function emptyInvoiceForm() {
  const today = toYMD(new Date());
  const due = new Date(); due.setDate(due.getDate() + 7);
  return {
    client_id: '', service_code: 'DDS', issue_date: today, due_date: toYMD(due),
    amount_excl_vat: '', status: 'sent', notes: '',
  };
}

/* ── Finance hub helpers (expenses / income / subscriptions) ──────────── */
const EXPENSE_CATEGORIES = ['subscription','tools','contractor','marketing','fuel','travel','office','equipment','professional_fees','bank_fees','general'];
const INCOME_CATEGORIES  = ['other','interest','refund','grant','reseller','consulting','rebate'];
const SUB_CYCLES     = ['weekly','monthly','quarterly','annual'];
const SUB_CURRENCIES = ['USD','ZAR','EUR','GBP'];
function emptyExpenseForm() { return { expense_date: toYMD(new Date()), category:'general', vendor:'', description:'', amount:'', payment_method:'', reference:'', notes:'' }; }
function emptyIncomeForm()  { return { income_date: toYMD(new Date()), source:'', category:'other', amount:'', description:'', notes:'' }; }
function emptySubForm()     { return { name:'', vendor:'', amount:'', currency:'USD', billing_cycle:'monthly', next_due_date: toYMD(new Date()), account:'pinnacle', active:true, notes:'' }; }
function curSym(c) { return c==='ZAR' ? 'R ' : c==='USD' ? '$' : c==='EUR' ? '€' : c==='GBP' ? '£' : (c+' '); }
function fmtMoney(amount, currency) { return curSym(currency) + Number(amount||0).toLocaleString('en-ZA', { maximumFractionDigits:2 }); }
function monthlyEquiv(sub) {
  const a = Number(sub.amount||0);
  if (sub.billing_cycle === 'weekly')    return a * 4.345;
  if (sub.billing_cycle === 'quarterly') return a / 3;
  if (sub.billing_cycle === 'annual')    return a / 12;
  return a;
}
function cycleAdvance(dateStr, cycle) {
  const d = new Date((dateStr || toYMD(new Date())) + 'T00:00:00');
  if (cycle === 'weekly')    d.setDate(d.getDate() + 7);
  else if (cycle === 'quarterly') d.setMonth(d.getMonth() + 3);
  else if (cycle === 'annual')    d.setFullYear(d.getFullYear() + 1);
  else d.setMonth(d.getMonth() + 1);
  return toYMD(d);
}
function daysUntil(dateStr) {
  if (!dateStr) return null;
  const d = new Date(dateStr + 'T00:00:00');
  const t = new Date(); t.setHours(0,0,0,0);
  return Math.round((d - t) / 86400000);
}
function isThisMonth(dateStr) {
  if (!dateStr) return false;
  const now = new Date();
  return dateStr.slice(0,7) === (now.getFullYear() + '-' + String(now.getMonth()+1).padStart(2,'0'));
}
const ERRBOX = { padding:12, background:'rgba(161,30,42,0.10)', border:'1.5px solid var(--ruby-400)', borderRadius:8, color:'var(--ruby-400)', fontSize:12, marginBottom:12 };

/* ── Finance hub: tabbed Revenue / Expenses / Income / Subscriptions ── */
function FinancePage() {
  const [tab, setTab] = React.useState('revenue');
  const TABS = [['revenue','Revenue'],['payments','Payments'],['expenses','Expenses'],['income','Income'],['subscriptions','Subscriptions']];
  return (
    <div className="page-content">
      <PageHead eyebrow="FINANCE" title="Financial"
        lede="Revenue, expenses, income and subscription reminders — all backed by Supabase." />
      <div className="tabs" style={{ marginBottom:18 }}>
        {TABS.map(([id,label]) => (
          <div key={id} className={`tab${tab===id ? ' active' : ''}`} onClick={() => setTab(id)}>{label}</div>
        ))}
      </div>
      {tab === 'revenue'       && <RevenueTab />}
      {tab === 'payments'      && <PaymentsTab />}
      {tab === 'expenses'      && <ExpensesTab />}
      {tab === 'income'        && <IncomeTab />}
      {tab === 'subscriptions' && <SubscriptionsTab />}
    </div>
  );
}

/* ── Revenue tab — invoices ─────────────────────────────────────────── */
function RevenueTab() {
  const [invoices, setInvoices] = React.useState([]);
  const [clients, setClients]   = React.useState([]);
  const [loading, setLoading]   = React.useState(true);
  const [error, setError]       = React.useState(null);
  const [showAdd, setShowAdd]   = React.useState(false);
  const [form, setForm]         = React.useState(emptyInvoiceForm());
  const [saving, setSaving]     = React.useState(false);

  const fetchAll = React.useCallback(async () => {
    try {
      setError(null);
      const [inv, cl] = await Promise.all([window.pccDb.invoices.list(), window.pccDb.clients.list()]);
      setInvoices(inv || []); setClients(cl || []);
    } catch (e) { setError(e.message); } finally { setLoading(false); }
  }, []);
  React.useEffect(() => { if (window.pccDb) fetchAll(); }, [fetchAll]);

  const clientName = (id) => { const c = clients.find(x => x.id === id); return c ? c.business_name : '—'; };
  const amt = (inv) => Number(inv.amount_incl_vat != null ? inv.amount_incl_vat : (Number(inv.amount_excl_vat||0) + Number(inv.vat_amount||0)));
  const sumBy = (st) => invoices.filter(i => i.status === st).reduce((s,i) => s + amt(i), 0);
  const paid = sumBy('paid'), outstanding = sumBy('sent'), overdue = sumBy('overdue');
  const billed = paid + outstanding + overdue;
  const collectedPct = billed > 0 ? Math.round(paid / billed * 100) : 0;
  const overdueCount = invoices.filter(i => i.status === 'overdue').length;
  const outstandingCount = invoices.filter(i => i.status === 'sent').length;

  function openAdd() { setForm(emptyInvoiceForm()); setShowAdd(true); }
  function nextInvoiceNumber(clientId, serviceCode) {
    const c = clients.find(x => x.id === clientId);
    const code = (c && c.client_code) ? c.client_code : '000';
    const seq = invoices.filter(i => i.client_id === clientId).length + 1;
    return `PPA-${serviceCode}-${code}-${String(seq).padStart(4,'0')}`;
  }
  async function handleSave() {
    if (!form.client_id) { alert('Pick a client first.'); return; }
    if (!form.amount_excl_vat || Number(form.amount_excl_vat) <= 0) { alert('Enter an amount.'); return; }
    setSaving(true);
    try {
      const payload = {
        invoice_number: nextInvoiceNumber(form.client_id, form.service_code),
        client_id: form.client_id, service_code: form.service_code,
        issue_date: form.issue_date, due_date: form.due_date || null,
        amount_excl_vat: Number(form.amount_excl_vat), vat_amount: 0, status: form.status,
        paid_at: form.status === 'paid' ? new Date().toISOString() : null,
        notes: form.notes.trim() || null,
      };
      await window.pccDb.invoices.create(payload);
      window.pccDb.logActivity('invoice.created', payload.invoice_number, `${clientName(form.client_id)} · ${fmtR(payload.amount_excl_vat)}`);
      setShowAdd(false); await fetchAll();
    } catch (e) { alert('Save failed: ' + e.message); } finally { setSaving(false); }
  }
  async function markPaid(inv) {
    try {
      await window.pccDb.invoices.update(inv.id, { status:'paid', paid_at: new Date().toISOString() });
      window.pccDb.logActivity('invoice.paid', inv.invoice_number, fmtR(amt(inv)));
      setInvoices(prev => prev.map(x => x.id === inv.id ? { ...x, status:'paid' } : x));
    } catch (e) { alert('Update failed: ' + e.message); }
  }
  async function deleteInvoice(inv) {
    if (!window.confirm(`Delete ${inv.invoice_number}? This cannot be undone.`)) return;
    try { await window.pccDb.invoices.remove(inv.id); setInvoices(prev => prev.filter(x => x.id !== inv.id)); }
    catch (e) { alert('Delete failed: ' + e.message); }
  }

  return (
    <div>
      {error && <div style={ERRBOX}>Supabase error: {error}</div>}
      <div style={{ display:'flex', justifyContent:'flex-end', marginBottom:12 }}>
        <button className="btn-primary" onClick={openAdd}><Icon name="plus" size={15} color="var(--gold-700)" /> New Invoice</button>
      </div>
      <div className="kpi-grid">
        <KPI label="Revenue (paid)" value={paid.toLocaleString('en-ZA')}        currency="R" trend="collected"                                                trendDir="up" />
        <KPI label="Outstanding"    value={outstanding.toLocaleString('en-ZA')} currency="R" trend={`${outstandingCount} sent`}                              trendDir="flat" />
        <KPI label="Overdue"        value={overdue.toLocaleString('en-ZA')}     currency="R" trend={`${overdueCount} invoice${overdueCount !== 1 ? 's' : ''}`} trendDir="down" flag={overdueCount > 0 ? 'ruby' : undefined} />
        <KPI label="Collected"      value={`${collectedPct}%`}                               trend="of billed"                                                trendDir="up" />
      </div>
      <Card sub="INVOICES" title="Billing">
        {loading ? (
          <div style={{ padding:'40px 0', textAlign:'center', color:'var(--muted)', fontSize:13 }}>Loading invoices…</div>
        ) : invoices.length === 0 ? (
          <div style={{ padding:'24px 0', textAlign:'center', color:'var(--muted)', fontSize:13, fontStyle:'italic' }}>No invoices yet — click New Invoice to create one.</div>
        ) : (
          <table className="tbl">
            <thead><tr><th>Invoice #</th><th>Client</th><th>Amount</th><th>Due</th><th>Status</th><th>Actions</th></tr></thead>
            <tbody>
              {invoices.map(inv => (
                <tr key={inv.id}>
                  <td style={{ fontFamily:"'JetBrains Mono',monospace", fontSize:11 }}>{inv.invoice_number || '—'}</td>
                  <td>{clientName(inv.client_id)}</td>
                  <td style={{ fontFamily:"'Cormorant Garamond',serif", fontStyle:'italic' }}>{fmtR(amt(inv))}</td>
                  <td style={{ color:'var(--muted)', fontSize:12 }}>{inv.due_date ? new Date(inv.due_date).toLocaleDateString('en-ZA',{day:'numeric',month:'short',year:'numeric'}) : '—'}</td>
                  <td><InvoiceBadge status={inv.status} /></td>
                  <td style={{ display:'flex', gap:6 }}>
                    {inv.status !== 'paid' && <button className="btn-ghost" style={{ fontSize:11, padding:'3px 8px' }} onClick={() => markPaid(inv)}>Mark paid</button>}
                    <button className="btn-ghost" style={{ fontSize:11, padding:'3px 8px', color:'var(--ruby-400)', borderColor:'var(--ruby-400)' }} onClick={() => deleteInvoice(inv)}>✕</button>
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
        )}
      </Card>
      {showAdd && (
        <div className="modal-overlay" onClick={e => e.target===e.currentTarget && setShowAdd(false)}>
          <div className="modal" style={{ maxWidth:560 }}>
            <div className="modal-title">New invoice</div>
            <div className="modal-grid">
              <div className="field full"><label>Client *</label>
                <select className="input" value={form.client_id} onChange={e => setForm(f => ({ ...f, client_id: e.target.value }))}>
                  <option value="">— select client —</option>
                  {clients.map(c => <option key={c.id} value={c.id}>{c.business_name}{c.client_code ? ` (${c.client_code})` : ''}</option>)}
                </select>
              </div>
              <div className="field"><label>Service</label>
                <select className="input" value={form.service_code} onChange={e => setForm(f => ({ ...f, service_code: e.target.value }))}>
                  {INVOICE_SERVICE_CODES.map(s => <option key={s} value={s}>{s}</option>)}
                </select>
              </div>
              <div className="field"><label>Status</label>
                <select className="input" value={form.status} onChange={e => setForm(f => ({ ...f, status: e.target.value }))}>
                  {INVOICE_STATUSES.map(s => <option key={s} value={s}>{s.charAt(0).toUpperCase()+s.slice(1)}</option>)}
                </select>
              </div>
              <div className="field"><label>Issue date</label><input className="input" type="date" value={form.issue_date} onChange={e => setForm(f => ({ ...f, issue_date: e.target.value }))} /></div>
              <div className="field"><label>Due date</label><input className="input" type="date" value={form.due_date} onChange={e => setForm(f => ({ ...f, due_date: e.target.value }))} /></div>
              <div className="field full"><label>Amount (R, excl. VAT)</label>
                <input className="input" type="number" min="0" step="0.01" value={form.amount_excl_vat} onChange={e => setForm(f => ({ ...f, amount_excl_vat: e.target.value }))} placeholder="e.g. 1500" />
                <div style={{ fontSize:11, color:'var(--muted)', marginTop:4 }}>VAT R0.00 (not registered) · Invoice # auto-generated on save</div>
              </div>
              <div className="field full"><label>Notes</label><textarea className="input" rows={2} value={form.notes} onChange={e => setForm(f => ({ ...f, notes: e.target.value }))} placeholder="Optional…" /></div>
            </div>
            <div style={{ display:'flex', justifyContent:'flex-end', gap:8, marginTop:18 }}>
              <button className="btn-ghost" disabled={saving} onClick={() => setShowAdd(false)}>Cancel</button>
              <button className="btn-primary" disabled={saving} onClick={handleSave}>{saving ? 'Saving…' : 'Create invoice'}</button>
            </div>
          </div>
        </div>
      )}
    </div>
  );
}

/* ── Payments tab — verified Paystack website payments (read-only) ──── */
function PaymentsTab() {
  const [rows, setRows]       = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const [error, setError]     = React.useState(null);

  const fetchRows = React.useCallback(async () => {
    try { setError(null); setRows((await window.pccDb.payments.list()) || []); }
    catch (e) { setError(e.message); } finally { setLoading(false); }
  }, []);
  React.useEffect(() => { if (window.pccDb) fetchRows(); }, [fetchRows]);

  const success = rows.filter(r => r.status === 'success');
  const total = success.reduce((s,r) => s + Number(r.amount||0), 0);
  const mtd   = success.filter(r => isThisMonth(String(r.paid_at || r.created_at || '').slice(0,10))).reduce((s,r) => s + Number(r.amount||0), 0);

  return (
    <div>
      {error && <div style={ERRBOX}>Supabase error: {error}</div>}
      <div className="kpi-grid">
        <KPI label="Received (paid)" value={total.toLocaleString('en-ZA')} currency="R" trend={`${success.length} payment${success.length!==1?'s':''}`} trendDir="up" />
        <KPI label="This month"      value={mtd.toLocaleString('en-ZA')}   currency="R" trend="via Paystack" trendDir="up" />
        <KPI label="Transactions"    value={rows.length} trend="all statuses" trendDir="flat" />
      </div>
      <Card sub="PAYSTACK" title="Website payments">
        {loading ? (
          <div style={{ padding:'40px 0', textAlign:'center', color:'var(--muted)', fontSize:13 }}>Loading payments…</div>
        ) : rows.length === 0 ? (
          <div style={{ padding:'24px 0', textAlign:'center', color:'var(--muted)', fontSize:13, fontStyle:'italic' }}>
            No payments yet. Once Paystack is connected, every verified website payment lands here automatically.
          </div>
        ) : (
          <table className="tbl">
            <thead><tr><th>Date</th><th>Reference</th><th>Customer</th><th>Channel</th><th>Amount</th><th>Status</th></tr></thead>
            <tbody>
              {rows.map(p => (
                <tr key={p.id}>
                  <td style={{ color:'var(--muted)', fontSize:12 }}>{p.paid_at ? new Date(p.paid_at).toLocaleDateString('en-ZA',{day:'numeric',month:'short',year:'numeric'}) : '—'}</td>
                  <td style={{ fontFamily:"'JetBrains Mono',monospace", fontSize:11 }}>{p.reference}</td>
                  <td>{p.customer_name || p.email || '—'}</td>
                  <td style={{ fontSize:12, color:'var(--ink-3)' }}>{p.channel || '—'}</td>
                  <td style={{ fontFamily:"'Cormorant Garamond',serif", fontStyle:'italic', color:'var(--positive)' }}>{fmtR(p.amount)}</td>
                  <td><InvoiceBadge status={p.status === 'success' ? 'paid' : p.status} /></td>
                </tr>
              ))}
            </tbody>
          </table>
        )}
      </Card>
    </div>
  );
}

/* ── Expenses tab ───────────────────────────────────────────────────── */
function ExpensesTab() {
  const [rows, setRows]       = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const [error, setError]     = React.useState(null);
  const [showAdd, setShowAdd] = React.useState(false);
  const [form, setForm]       = React.useState(emptyExpenseForm());
  const [editId, setEditId]   = React.useState(null);
  const [saving, setSaving]   = React.useState(false);

  const fetchRows = React.useCallback(async () => {
    try { setError(null); setRows((await window.pccDb.expenses.list()) || []); }
    catch (e) { setError(e.message); } finally { setLoading(false); }
  }, []);
  React.useEffect(() => { if (window.pccDb) fetchRows(); }, [fetchRows]);

  const amt = (r) => Number(r.amount_total != null ? r.amount_total : (Number(r.amount||0) + Number(r.vat_amount||0)));
  const totalAll = rows.reduce((s,r) => s + amt(r), 0);
  const totalMTD = rows.filter(r => isThisMonth(r.expense_date)).reduce((s,r) => s + amt(r), 0);
  const byCat = {}; rows.forEach(r => { byCat[r.category] = (byCat[r.category]||0) + amt(r); });
  const topCat = Object.keys(byCat).sort((a,b) => byCat[b]-byCat[a])[0];

  function openAdd() { setEditId(null); setForm(emptyExpenseForm()); setShowAdd(true); }
  function openEdit(r) {
    setEditId(r.id);
    setForm({ expense_date:r.expense_date, category:r.category||'general', vendor:r.vendor||'', description:r.description||'', amount:String(r.amount||''), payment_method:r.payment_method||'', reference:r.reference||'', notes:r.notes||'' });
    setShowAdd(true);
  }
  async function handleSave() {
    if (!form.amount || Number(form.amount) <= 0) { alert('Enter an amount.'); return; }
    setSaving(true);
    try {
      // amount_total is GENERATED — never write it. VAT off → vat_amount 0.
      const payload = {
        expense_date: form.expense_date, category: form.category,
        vendor: form.vendor.trim() || null, description: form.description.trim() || null,
        amount: Number(form.amount), vat_amount: 0,
        payment_method: form.payment_method.trim() || null, reference: form.reference.trim() || null,
        notes: form.notes.trim() || null,
      };
      if (editId) { await window.pccDb.expenses.update(editId, payload); }
      else { await window.pccDb.expenses.create(payload); window.pccDb.logActivity('system.expense_added', form.vendor.trim() || form.category, fmtR(payload.amount)); }
      setShowAdd(false); await fetchRows();
    } catch (e) { alert('Save failed: ' + e.message); } finally { setSaving(false); }
  }
  async function del(r) {
    if (!window.confirm(`Delete this expense (${fmtR(amt(r))})?`)) return;
    try { await window.pccDb.expenses.remove(r.id); setRows(prev => prev.filter(x => x.id !== r.id)); }
    catch (e) { alert('Delete failed: ' + e.message); }
  }

  return (
    <div>
      {error && <div style={ERRBOX}>Supabase error: {error}</div>}
      <div style={{ display:'flex', justifyContent:'flex-end', marginBottom:12 }}>
        <button className="btn-primary" onClick={openAdd}><Icon name="plus" size={15} color="var(--gold-700)" /> New Expense</button>
      </div>
      <div className="kpi-grid">
        <KPI label="Spent (this month)" value={totalMTD.toLocaleString('en-ZA')} currency="R" trend="current month" trendDir="down" />
        <KPI label="Spent (total)"      value={totalAll.toLocaleString('en-ZA')} currency="R" trend={`${rows.length} entr${rows.length!==1?'ies':'y'}`} trendDir="flat" />
        <KPI label="Top category"       value={topCat ? topCat.replace('_',' ') : '—'} trend={topCat ? fmtR(byCat[topCat]) : 'none yet'} trendDir="flat" />
        <KPI label="Categories"         value={Object.keys(byCat).length} trend="in use" trendDir="flat" />
      </div>
      <Card sub="EXPENSES" title="Spending">
        {loading ? <div style={{ padding:'40px 0', textAlign:'center', color:'var(--muted)', fontSize:13 }}>Loading expenses…</div>
        : rows.length === 0 ? <div style={{ padding:'24px 0', textAlign:'center', color:'var(--muted)', fontSize:13, fontStyle:'italic' }}>No expenses yet — click New Expense to add one.</div>
        : (
          <table className="tbl">
            <thead><tr><th>Date</th><th>Category</th><th>Vendor</th><th>Description</th><th>Amount</th><th>Actions</th></tr></thead>
            <tbody>
              {rows.map(r => (
                <tr key={r.id}>
                  <td style={{ color:'var(--muted)', fontSize:12 }}>{new Date(r.expense_date).toLocaleDateString('en-ZA',{day:'numeric',month:'short',year:'numeric'})}</td>
                  <td><span className="badge badge-lead">{(r.category||'general').replace('_',' ')}</span></td>
                  <td>{r.vendor || '—'}</td>
                  <td style={{ fontSize:12, color:'var(--ink-3)' }}>{r.description || '—'}</td>
                  <td style={{ fontFamily:"'Cormorant Garamond',serif", fontStyle:'italic' }}>{fmtR(amt(r))}</td>
                  <td style={{ display:'flex', gap:6 }}>
                    <button className="btn-ghost" style={{ fontSize:11, padding:'3px 8px' }} onClick={() => openEdit(r)}>Edit</button>
                    <button className="btn-ghost" style={{ fontSize:11, padding:'3px 8px', color:'var(--ruby-400)', borderColor:'var(--ruby-400)' }} onClick={() => del(r)}>✕</button>
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
        )}
      </Card>
      {showAdd && (
        <div className="modal-overlay" onClick={e => e.target===e.currentTarget && setShowAdd(false)}>
          <div className="modal" style={{ maxWidth:560 }}>
            <div className="modal-title">{editId ? 'Edit expense' : 'New expense'}</div>
            <div className="modal-grid">
              <div className="field"><label>Date</label><input className="input" type="date" value={form.expense_date} onChange={e => setForm(f => ({ ...f, expense_date:e.target.value }))} /></div>
              <div className="field"><label>Category</label>
                <select className="input" value={form.category} onChange={e => setForm(f => ({ ...f, category:e.target.value }))}>
                  {EXPENSE_CATEGORIES.map(c => <option key={c} value={c}>{c.replace('_',' ').replace(/\b\w/g,m=>m.toUpperCase())}</option>)}
                </select>
              </div>
              <div className="field"><label>Vendor</label><input className="input" value={form.vendor} onChange={e => setForm(f => ({ ...f, vendor:e.target.value }))} placeholder="e.g. Telkom" /></div>
              <div className="field"><label>Amount (R)</label><input className="input" type="number" min="0" step="0.01" value={form.amount} onChange={e => setForm(f => ({ ...f, amount:e.target.value }))} placeholder="e.g. 499" /></div>
              <div className="field"><label>Payment method</label><input className="input" value={form.payment_method} onChange={e => setForm(f => ({ ...f, payment_method:e.target.value }))} placeholder="EFT / Card / Cash" /></div>
              <div className="field"><label>Reference</label><input className="input" value={form.reference} onChange={e => setForm(f => ({ ...f, reference:e.target.value }))} placeholder="Optional" /></div>
              <div className="field full"><label>Description</label><input className="input" value={form.description} onChange={e => setForm(f => ({ ...f, description:e.target.value }))} placeholder="What was it for?" /></div>
              <div className="field full"><label>Notes</label><textarea className="input" rows={2} value={form.notes} onChange={e => setForm(f => ({ ...f, notes:e.target.value }))} placeholder="Optional…" /></div>
            </div>
            <div style={{ display:'flex', justifyContent:'flex-end', gap:8, marginTop:18 }}>
              <button className="btn-ghost" disabled={saving} onClick={() => setShowAdd(false)}>Cancel</button>
              <button className="btn-primary" disabled={saving} onClick={handleSave}>{saving ? 'Saving…' : (editId ? 'Save changes' : 'Add expense')}</button>
            </div>
          </div>
        </div>
      )}
    </div>
  );
}

/* ── Income tab — additional / other income ─────────────────────────── */
function IncomeTab() {
  const [rows, setRows]       = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const [error, setError]     = React.useState(null);
  const [showAdd, setShowAdd] = React.useState(false);
  const [form, setForm]       = React.useState(emptyIncomeForm());
  const [editId, setEditId]   = React.useState(null);
  const [saving, setSaving]   = React.useState(false);

  const fetchRows = React.useCallback(async () => {
    try { setError(null); setRows((await window.pccDb.income.list()) || []); }
    catch (e) { setError(e.message); } finally { setLoading(false); }
  }, []);
  React.useEffect(() => { if (window.pccDb) fetchRows(); }, [fetchRows]);

  const totalAll = rows.reduce((s,r) => s + Number(r.amount||0), 0);
  const totalMTD = rows.filter(r => isThisMonth(r.income_date)).reduce((s,r) => s + Number(r.amount||0), 0);

  function openAdd() { setEditId(null); setForm(emptyIncomeForm()); setShowAdd(true); }
  function openEdit(r) {
    setEditId(r.id);
    setForm({ income_date:r.income_date, source:r.source||'', category:r.category||'other', amount:String(r.amount||''), description:r.description||'', notes:r.notes||'' });
    setShowAdd(true);
  }
  async function handleSave() {
    if (!form.source.trim()) { alert('Enter a source.'); return; }
    if (!form.amount || Number(form.amount) <= 0) { alert('Enter an amount.'); return; }
    setSaving(true);
    try {
      const payload = { income_date: form.income_date, source: form.source.trim(), category: form.category, amount: Number(form.amount), description: form.description.trim() || null, notes: form.notes.trim() || null };
      if (editId) { await window.pccDb.income.update(editId, payload); }
      else { await window.pccDb.income.create(payload); window.pccDb.logActivity('system.income_added', form.source.trim(), fmtR(payload.amount)); }
      setShowAdd(false); await fetchRows();
    } catch (e) { alert('Save failed: ' + e.message); } finally { setSaving(false); }
  }
  async function del(r) {
    if (!window.confirm(`Delete this income entry (${fmtR(r.amount)})?`)) return;
    try { await window.pccDb.income.remove(r.id); setRows(prev => prev.filter(x => x.id !== r.id)); }
    catch (e) { alert('Delete failed: ' + e.message); }
  }

  return (
    <div>
      {error && <div style={ERRBOX}>Supabase error: {error}</div>}
      <div style={{ display:'flex', justifyContent:'flex-end', marginBottom:12 }}>
        <button className="btn-primary" onClick={openAdd}><Icon name="plus" size={15} color="var(--gold-700)" /> New Income</button>
      </div>
      <div className="kpi-grid">
        <KPI label="Income (this month)" value={totalMTD.toLocaleString('en-ZA')} currency="R" trend="current month" trendDir="up" />
        <KPI label="Income (total)"      value={totalAll.toLocaleString('en-ZA')} currency="R" trend={`${rows.length} entr${rows.length!==1?'ies':'y'}`} trendDir="flat" />
        <KPI label="Sources"             value={new Set(rows.map(r=>r.source)).size} trend="distinct" trendDir="flat" />
      </div>
      <Card sub="OTHER INCOME" title="Additional income">
        {loading ? <div style={{ padding:'40px 0', textAlign:'center', color:'var(--muted)', fontSize:13 }}>Loading income…</div>
        : rows.length === 0 ? <div style={{ padding:'24px 0', textAlign:'center', color:'var(--muted)', fontSize:13, fontStyle:'italic' }}>No income entries yet — click New Income to add one. (Client invoices live under Revenue.)</div>
        : (
          <table className="tbl">
            <thead><tr><th>Date</th><th>Source</th><th>Category</th><th>Description</th><th>Amount</th><th>Actions</th></tr></thead>
            <tbody>
              {rows.map(r => (
                <tr key={r.id}>
                  <td style={{ color:'var(--muted)', fontSize:12 }}>{new Date(r.income_date).toLocaleDateString('en-ZA',{day:'numeric',month:'short',year:'numeric'})}</td>
                  <td style={{ fontWeight:600 }}>{r.source}</td>
                  <td><span className="badge badge-lead">{r.category}</span></td>
                  <td style={{ fontSize:12, color:'var(--ink-3)' }}>{r.description || '—'}</td>
                  <td style={{ fontFamily:"'Cormorant Garamond',serif", fontStyle:'italic', color:'var(--positive)' }}>{fmtR(r.amount)}</td>
                  <td style={{ display:'flex', gap:6 }}>
                    <button className="btn-ghost" style={{ fontSize:11, padding:'3px 8px' }} onClick={() => openEdit(r)}>Edit</button>
                    <button className="btn-ghost" style={{ fontSize:11, padding:'3px 8px', color:'var(--ruby-400)', borderColor:'var(--ruby-400)' }} onClick={() => del(r)}>✕</button>
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
        )}
      </Card>
      {showAdd && (
        <div className="modal-overlay" onClick={e => e.target===e.currentTarget && setShowAdd(false)}>
          <div className="modal" style={{ maxWidth:520 }}>
            <div className="modal-title">{editId ? 'Edit income' : 'New income'}</div>
            <div className="modal-grid">
              <div className="field"><label>Date</label><input className="input" type="date" value={form.income_date} onChange={e => setForm(f => ({ ...f, income_date:e.target.value }))} /></div>
              <div className="field"><label>Category</label>
                <select className="input" value={form.category} onChange={e => setForm(f => ({ ...f, category:e.target.value }))}>
                  {INCOME_CATEGORIES.map(c => <option key={c} value={c}>{c.charAt(0).toUpperCase()+c.slice(1)}</option>)}
                </select>
              </div>
              <div className="field full"><label>Source *</label><input className="input" value={form.source} onChange={e => setForm(f => ({ ...f, source:e.target.value }))} placeholder="e.g. Bank interest, Reseller commission" /></div>
              <div className="field full"><label>Amount (R)</label><input className="input" type="number" min="0" step="0.01" value={form.amount} onChange={e => setForm(f => ({ ...f, amount:e.target.value }))} placeholder="e.g. 1200" /></div>
              <div className="field full"><label>Description</label><input className="input" value={form.description} onChange={e => setForm(f => ({ ...f, description:e.target.value }))} placeholder="Optional" /></div>
              <div className="field full"><label>Notes</label><textarea className="input" rows={2} value={form.notes} onChange={e => setForm(f => ({ ...f, notes:e.target.value }))} placeholder="Optional…" /></div>
            </div>
            <div style={{ display:'flex', justifyContent:'flex-end', gap:8, marginTop:18 }}>
              <button className="btn-ghost" disabled={saving} onClick={() => setShowAdd(false)}>Cancel</button>
              <button className="btn-primary" disabled={saving} onClick={handleSave}>{saving ? 'Saving…' : (editId ? 'Save changes' : 'Add income')}</button>
            </div>
          </div>
        </div>
      )}
    </div>
  );
}

/* ── Subscriptions tab — recurring bills + due reminders ────────────── */
function SubscriptionsTab() {
  const [rows, setRows]       = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const [error, setError]     = React.useState(null);
  const [showAdd, setShowAdd] = React.useState(false);
  const [form, setForm]       = React.useState(emptySubForm());
  const [editId, setEditId]   = React.useState(null);
  const [saving, setSaving]   = React.useState(false);

  const fetchRows = React.useCallback(async () => {
    try {
      setError(null);
      const r = (await window.pccDb.subscriptions.list()) || [];
      r.sort((a,b) => (a.next_due_date||'9999') < (b.next_due_date||'9999') ? -1 : 1);
      setRows(r);
    } catch (e) { setError(e.message); } finally { setLoading(false); }
  }, []);
  React.useEffect(() => { if (window.pccDb) fetchRows(); }, [fetchRows]);

  const active = rows.filter(r => r.active !== false);
  // Monthly-equivalent totals grouped by currency (can't add USD + ZAR).
  const monthlyByCur = {};
  active.forEach(r => { monthlyByCur[r.currency] = (monthlyByCur[r.currency]||0) + monthlyEquiv(r); });
  const monthlyStr = Object.keys(monthlyByCur).length ? Object.keys(monthlyByCur).sort().map(c => fmtMoney(monthlyByCur[c], c)).join('  ·  ') : '—';
  const dueSoon = active.filter(r => { const d = daysUntil(r.next_due_date); return d != null && d >= 0 && d <= 7; }).length;
  const overdue = active.filter(r => { const d = daysUntil(r.next_due_date); return d != null && d < 0; }).length;

  function openAdd() { setEditId(null); setForm(emptySubForm()); setShowAdd(true); }
  function openEdit(r) {
    setEditId(r.id);
    setForm({ name:r.name||'', vendor:r.vendor||'', amount:String(r.amount||''), currency:r.currency||'USD', billing_cycle:r.billing_cycle||'monthly', next_due_date:r.next_due_date||toYMD(new Date()), account:r.account||'pinnacle', active:r.active!==false, notes:r.notes||'' });
    setShowAdd(true);
  }
  async function handleSave() {
    if (!form.name.trim()) { alert('Give the subscription a name.'); return; }
    setSaving(true);
    try {
      const payload = { name:form.name.trim(), vendor:form.vendor.trim()||null, amount:Number(form.amount)||0, currency:form.currency, billing_cycle:form.billing_cycle, next_due_date:form.next_due_date||null, account:form.account, active:!!form.active, notes:form.notes.trim()||null, category:'subscription' };
      if (editId) { await window.pccDb.subscriptions.update(editId, payload); }
      else { await window.pccDb.subscriptions.create(payload); window.pccDb.logActivity('system.subscription_added', form.name.trim(), fmtMoney(payload.amount, payload.currency)); }
      setShowAdd(false); await fetchRows();
    } catch (e) { alert('Save failed: ' + e.message); } finally { setSaving(false); }
  }
  async function markPaid(r) {
    const nd = cycleAdvance(r.next_due_date, r.billing_cycle);
    try {
      await window.pccDb.subscriptions.update(r.id, { next_due_date: nd });
      window.pccDb.logActivity('system.subscription_paid', r.name, `Next due ${nd}`);
      fetchRows();
    } catch (e) { alert('Update failed: ' + e.message); }
  }
  async function del(r) {
    if (!window.confirm(`Delete subscription "${r.name}"?`)) return;
    try { await window.pccDb.subscriptions.remove(r.id); setRows(prev => prev.filter(x => x.id !== r.id)); }
    catch (e) { alert('Delete failed: ' + e.message); }
  }

  const dueChip = (r) => {
    const d = daysUntil(r.next_due_date);
    if (d == null) return <span style={{ color:'var(--muted)', fontSize:11 }}>—</span>;
    if (d < 0)  return <span style={{ color:'var(--ruby-400)', fontWeight:700, fontSize:11 }}>{Math.abs(d)}d overdue</span>;
    if (d === 0) return <span style={{ color:'var(--ruby-400)', fontWeight:700, fontSize:11 }}>due today</span>;
    if (d <= 7) return <span style={{ color:'#C9852B', fontWeight:700, fontSize:11 }}>in {d}d</span>;
    return <span style={{ color:'var(--muted)', fontSize:11 }}>in {d}d</span>;
  };

  return (
    <div>
      {error && <div style={ERRBOX}>Supabase error: {error}</div>}
      <div style={{ display:'flex', justifyContent:'flex-end', marginBottom:12 }}>
        <button className="btn-primary" onClick={openAdd}><Icon name="plus" size={15} color="var(--gold-700)" /> New Subscription</button>
      </div>
      <div className="kpi-grid">
        <KPI label="Monthly cost" value={monthlyStr} trend="active subs" trendDir="flat" />
        <KPI label="Due this week" value={dueSoon} trend="next 7 days" trendDir="flat" flag={dueSoon > 0 ? 'ruby' : undefined} />
        <KPI label="Overdue"       value={overdue} trend="needs paying" trendDir="down" flag={overdue > 0 ? 'ruby' : undefined} />
        <KPI label="Active"        value={active.length} trend={`${rows.length} total`} trendDir="flat" />
      </div>
      <Card sub="REMINDERS" title="Recurring bills">
        {loading ? <div style={{ padding:'40px 0', textAlign:'center', color:'var(--muted)', fontSize:13 }}>Loading subscriptions…</div>
        : rows.length === 0 ? <div style={{ padding:'24px 0', textAlign:'center', color:'var(--muted)', fontSize:13, fontStyle:'italic' }}>No subscriptions yet — click New Subscription to add one.</div>
        : (
          <table className="tbl">
            <thead><tr><th>Service</th><th>Amount</th><th>Cycle</th><th>Next due</th><th>When</th><th>Account</th><th>Actions</th></tr></thead>
            <tbody>
              {rows.map(r => (
                <tr key={r.id} style={{ opacity: r.active === false ? 0.5 : 1 }}>
                  <td><div style={{ fontWeight:600 }}>{r.name}</div>{r.vendor && <div style={{ fontSize:10, color:'var(--muted)' }}>{r.vendor}</div>}</td>
                  <td style={{ fontFamily:"'Cormorant Garamond',serif", fontStyle:'italic' }}>{Number(r.amount) > 0 ? fmtMoney(r.amount, r.currency) : <span style={{ fontSize:11, fontStyle:'normal', color:'var(--muted)' }}>set amount</span>}</td>
                  <td style={{ fontSize:12, color:'var(--ink-3)' }}>{r.billing_cycle}</td>
                  <td style={{ fontSize:12, color:'var(--muted)' }}>{r.next_due_date ? new Date(r.next_due_date).toLocaleDateString('en-ZA',{day:'numeric',month:'short'}) : '—'}</td>
                  <td>{dueChip(r)}</td>
                  <td style={{ fontSize:11, color:'var(--muted)' }}>{r.account}</td>
                  <td style={{ display:'flex', gap:6 }}>
                    <button className="btn-ghost" style={{ fontSize:11, padding:'3px 8px' }} onClick={() => markPaid(r)} title="Mark paid — rolls due date forward">Paid</button>
                    <button className="btn-ghost" style={{ fontSize:11, padding:'3px 8px' }} onClick={() => openEdit(r)}>Edit</button>
                    <button className="btn-ghost" style={{ fontSize:11, padding:'3px 8px', color:'var(--ruby-400)', borderColor:'var(--ruby-400)' }} onClick={() => del(r)}>✕</button>
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
        )}
      </Card>
      {showAdd && (
        <div className="modal-overlay" onClick={e => e.target===e.currentTarget && setShowAdd(false)}>
          <div className="modal" style={{ maxWidth:560 }}>
            <div className="modal-title">{editId ? 'Edit subscription' : 'New subscription'}</div>
            <div className="modal-grid">
              <div className="field"><label>Name *</label><input className="input" value={form.name} onChange={e => setForm(f => ({ ...f, name:e.target.value }))} placeholder="e.g. Anthropic" /></div>
              <div className="field"><label>Vendor</label><input className="input" value={form.vendor} onChange={e => setForm(f => ({ ...f, vendor:e.target.value }))} placeholder="Optional" /></div>
              <div className="field"><label>Amount</label><input className="input" type="number" min="0" step="0.01" value={form.amount} onChange={e => setForm(f => ({ ...f, amount:e.target.value }))} placeholder="e.g. 20" /></div>
              <div className="field"><label>Currency</label>
                <select className="input" value={form.currency} onChange={e => setForm(f => ({ ...f, currency:e.target.value }))}>
                  {SUB_CURRENCIES.map(c => <option key={c} value={c}>{c}</option>)}
                </select>
              </div>
              <div className="field"><label>Billing cycle</label>
                <select className="input" value={form.billing_cycle} onChange={e => setForm(f => ({ ...f, billing_cycle:e.target.value }))}>
                  {SUB_CYCLES.map(c => <option key={c} value={c}>{c.charAt(0).toUpperCase()+c.slice(1)}</option>)}
                </select>
              </div>
              <div className="field"><label>Next due date</label><input className="input" type="date" value={form.next_due_date} onChange={e => setForm(f => ({ ...f, next_due_date:e.target.value }))} /></div>
              <div className="field"><label>Account</label>
                <select className="input" value={form.account} onChange={e => setForm(f => ({ ...f, account:e.target.value }))}>
                  <option value="pinnacle">Pinnacle</option>
                  <option value="private">Private</option>
                </select>
              </div>
              <div className="field"><label style={{ display:'flex', alignItems:'center', gap:8, cursor:'pointer', marginTop:24 }}>
                <input type="checkbox" checked={form.active} onChange={e => setForm(f => ({ ...f, active:e.target.checked }))} /> Active
              </label></div>
              <div className="field full"><label>Notes</label><textarea className="input" rows={2} value={form.notes} onChange={e => setForm(f => ({ ...f, notes:e.target.value }))} placeholder="Optional…" /></div>
            </div>
            <div style={{ display:'flex', justifyContent:'space-between', alignItems:'center', marginTop:18 }}>
              <div>{editId && <button className="btn-ghost" style={{ color:'var(--ruby-400)', borderColor:'var(--ruby-400)' }} disabled={saving} onClick={() => { setShowAdd(false); del({ id:editId, name:form.name }); }}>Delete</button>}</div>
              <div style={{ display:'flex', gap:8 }}>
                <button className="btn-ghost" disabled={saving} onClick={() => setShowAdd(false)}>Cancel</button>
                <button className="btn-primary" disabled={saving} onClick={handleSave}>{saving ? 'Saving…' : (editId ? 'Save changes' : 'Add subscription')}</button>
              </div>
            </div>
          </div>
        </div>
      )}
    </div>
  );
}

/* ════════════════════════════════════════════════════════════════════
   DIARY PAGE
════════════════════════════════════════════════════════════════════ */
/* ── Diary helpers ──────────────────────────────────────────────────── */
const DIARY_HOURS = ['08:00','09:00','10:00','11:00','12:00','13:00','14:00','15:00','16:00','17:00'];
const DIARY_CATEGORIES = ['task','meeting','call','deadline','admin','personal'];
const DIARY_CAT_COLOR = {
  task:'var(--gold-400)', meeting:'var(--sapphire)', call:'var(--gold-500)',
  deadline:'var(--ruby-400)', admin:'var(--ink-3)', personal:'var(--muted)',
};
// Local YYYY-MM-DD (NOT toISOString — that's UTC and can shift the day in SAST).
function toYMD(d) {
  return d.getFullYear() + '-' + String(d.getMonth()+1).padStart(2,'0') + '-' + String(d.getDate()).padStart(2,'0');
}
function hhmm(t) { return t ? String(t).slice(0,5) : ''; }
function diaryHourOf(t) { return t ? parseInt(String(t).slice(0,2),10) : null; }
function emptyDiaryForm(date, startHour) {
  const sh = startHour != null ? String(startHour).padStart(2,'0') : '09';
  const eh = startHour != null ? String(startHour+1).padStart(2,'0') : '10';
  return {
    entry_date: date || toYMD(new Date()),
    start_time: sh + ':00', end_time: eh + ':00',
    title: '', description: '', category: 'task', completed: false,
  };
}

function DiaryPage() {
  const [entries, setEntries] = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const [error, setError]     = React.useState(null);
  const [showForm, setShowForm] = React.useState(false);
  const [form, setForm]       = React.useState(emptyDiaryForm());
  const [editId, setEditId]   = React.useState(null);
  const [saving, setSaving]   = React.useState(false);
  const [dayView, setDayView] = React.useState(null); // Date object or null

  const today = new Date();

  const fetchEntries = React.useCallback(async () => {
    try {
      setError(null);
      const rows = await window.pccDb.diary.list();
      // sort by date then start_time, nulls last
      (rows || []).sort((a,b) => {
        if (a.entry_date !== b.entry_date) return a.entry_date < b.entry_date ? -1 : 1;
        return (a.start_time||'99') < (b.start_time||'99') ? -1 : 1;
      });
      setEntries(rows || []);
    } catch (e) {
      setError(e.message);
    } finally {
      setLoading(false);
    }
  }, []);

  React.useEffect(() => { if (window.pccDb) fetchEntries(); }, [fetchEntries]);

  function openAdd(date, startHour) {
    setEditId(null);
    setForm(emptyDiaryForm(date, startHour));
    setShowForm(true);
  }
  function openEdit(ev) {
    setEditId(ev.id);
    setForm({
      entry_date: ev.entry_date,
      start_time: hhmm(ev.start_time) || '',
      end_time:   hhmm(ev.end_time) || '',
      title: ev.title || '', description: ev.description || '',
      category: ev.category || 'task', completed: !!ev.completed,
    });
    setShowForm(true);
  }

  async function handleSave() {
    if (!form.title.trim()) { alert('Give the entry a title first.'); return; }
    setSaving(true);
    try {
      const payload = {
        entry_date: form.entry_date,
        start_time: form.start_time || null,
        end_time:   form.end_time || null,
        title:      form.title.trim(),
        description: form.description.trim() || null,
        category:   form.category || 'task',
        completed:  !!form.completed,
      };
      if (editId) {
        await window.pccDb.diary.update(editId, payload);
        window.pccDb.logActivity('system.diary_updated', payload.title, payload.entry_date);
      } else {
        await window.pccDb.diary.create(payload);
        window.pccDb.logActivity('system.diary_created', payload.title, payload.entry_date);
      }
      setShowForm(false);
      await fetchEntries();
    } catch (e) {
      alert('Save failed: ' + e.message);
    } finally {
      setSaving(false);
    }
  }

  async function handleDelete() {
    if (!editId) return;
    if (!window.confirm(`Delete "${form.title}"? This cannot be undone.`)) return;
    setSaving(true);
    try {
      await window.pccDb.diary.remove(editId);
      window.pccDb.logActivity('system.diary_deleted', form.title);
      setShowForm(false);
      await fetchEntries();
    } catch (e) {
      alert('Delete failed: ' + e.message);
    } finally {
      setSaving(false);
    }
  }

  async function toggleComplete(ev, e) {
    e.stopPropagation();
    try {
      await window.pccDb.diary.update(ev.id, { completed: !ev.completed });
      setEntries(prev => prev.map(x => x.id === ev.id ? { ...x, completed: !ev.completed } : x));
    } catch (err) { alert('Update failed: ' + err.message); }
  }

  const days = [-1, 0, 1].map(offset => {
    const d = new Date(today); d.setDate(today.getDate() + offset);
    return { date: d, offset, ymd: toYMD(d) };
  });
  const entriesFor = (ymd) => entries.filter(e => e.entry_date === ymd);

  // Small reusable chip for one entry inside the strip / day view.
  const EntryChip = ({ ev, showTime = true }) => (
    <div onClick={() => openEdit(ev)}
      style={{
        borderRadius:6, padding:'6px 10px', marginBottom:4, cursor:'pointer',
        background:'var(--glass-2)', borderLeft:'3px solid ' + (DIARY_CAT_COLOR[ev.category] || 'var(--gold-400)'),
        opacity: ev.completed ? 0.55 : 1,
      }}>
      <div style={{ display:'flex', justifyContent:'space-between', alignItems:'center', gap:6 }}>
        <div style={{ minWidth:0 }}>
          {showTime && (ev.start_time || ev.end_time) && (
            <div style={{ fontSize:10, fontWeight:700, color:'var(--muted)' }}>
              {hhmm(ev.start_time)}{ev.end_time ? '–' + hhmm(ev.end_time) : ''}
            </div>
          )}
          <div style={{ fontSize:12, fontWeight:600, color:'var(--ink)',
            textDecoration: ev.completed ? 'line-through' : 'none',
            whiteSpace:'nowrap', overflow:'hidden', textOverflow:'ellipsis' }}>{ev.title}</div>
        </div>
        <button title={ev.completed ? 'Mark not done' : 'Mark done'}
          onClick={(e) => toggleComplete(ev, e)}
          style={{ flexShrink:0, width:18, height:18, borderRadius:4, cursor:'pointer',
            border:'1.5px solid var(--line-strong)',
            background: ev.completed ? 'var(--gold-400)' : 'transparent',
            color: ev.completed ? 'var(--gold-700)' : 'transparent', fontSize:11, lineHeight:'14px' }}>✓</button>
      </div>
    </div>
  );

  return (
    <div className="page-content">
      <PageHead eyebrow="DIARY" title="Schedule"
        lede="Live schedule backed by Supabase. Click a day to open its full hourly view."
        action={
          <button className="btn-primary" onClick={() => openAdd()}>
            <Icon name="plus" size={15} color="var(--gold-700)" /> New Entry
          </button>
        } />

      {error && (
        <div style={{ padding:12, background:'rgba(161,30,42,0.10)', border:'1.5px solid var(--ruby-400)', borderRadius:8, color:'var(--ruby-400)', fontSize:12, marginBottom:12 }}>
          Supabase error: {error}
        </div>
      )}

      <Card sub="THIS WEEK" title="3-Day View">
        {loading ? (
          <div style={{ padding:'40px 0', textAlign:'center', color:'var(--muted)', fontSize:13 }}>Loading schedule…</div>
        ) : (
          <div className="diary-strip" style={{ marginTop:8 }}>
            <div className="diary-time-col">
              {DIARY_HOURS.map(h => <div key={h} className="diary-time-slot">{h}</div>)}
            </div>
            {days.map(({ date, offset, ymd }) => {
              const dayEntries = entriesFor(ymd);
              return (
                <div key={offset}>
                  <div onClick={() => setDayView(date)}
                    title="Open full hourly view"
                    style={{ paddingBottom:8, borderBottom:'1px solid var(--line)', cursor:'pointer' }}>
                    <div className="diary-day-head" style={offset === 0 ? {
                      background:'var(--gold-gradient-text)',
                      WebkitBackgroundClip:'text', backgroundClip:'text', color:'transparent'
                    } : {}}>
                      {date.getDate()}
                    </div>
                    <div className="diary-day-label">
                      {date.toLocaleDateString('en-ZA',{weekday:'short'})}
                    </div>
                  </div>
                  <div style={{ position:'relative', paddingTop:4, minHeight:40 }}>
                    {offset === 0 && (
                      <div className="diary-now-line"><div className="diary-now-dot" /></div>
                    )}
                    {dayEntries.length === 0 && (
                      <div onClick={() => openAdd(ymd)}
                        style={{ fontSize:11, color:'var(--muted)', fontStyle:'italic', cursor:'pointer', padding:'6px 2px' }}>
                        + add
                      </div>
                    )}
                    {dayEntries.map(ev => <EntryChip key={ev.id} ev={ev} />)}
                  </div>
                </div>
              );
            })}
          </div>
        )}
      </Card>

      {/* ── Full-day hourly view modal ─────────────────────────────────── */}
      {dayView && (
        <div className="modal-overlay" onClick={e => e.target===e.currentTarget && setDayView(null)}>
          <div className="modal" style={{ maxWidth:560, maxHeight:'82vh', overflowY:'auto' }}>
            <div style={{ display:'flex', justifyContent:'space-between', alignItems:'baseline', marginBottom:10 }}>
              <div className="modal-title" style={{ marginBottom:0 }}>
                {dayView.toLocaleDateString('en-ZA',{weekday:'long', day:'numeric', month:'long'})}
              </div>
              <button className="btn-ghost" style={{ fontSize:11, padding:'4px 10px' }}
                onClick={() => openAdd(toYMD(dayView))}>+ Add entry</button>
            </div>
            {(() => {
              const dv = entriesFor(toYMD(dayView));
              const unscheduled = dv.filter(e => !e.start_time);
              return (
                <div>
                  {DIARY_HOURS.map(h => {
                    const hr = parseInt(h.slice(0,2),10);
                    const slot = dv.filter(e => diaryHourOf(e.start_time) === hr);
                    return (
                      <div key={h} style={{ display:'flex', gap:10, borderTop:'1px solid var(--line-cool)', minHeight:44, padding:'6px 0' }}>
                        <div style={{ width:46, flexShrink:0, fontSize:11, fontWeight:700, color:'var(--muted)', paddingTop:4 }}>{h}</div>
                        <div style={{ flex:1, cursor: slot.length ? 'default' : 'pointer' }}
                          onClick={() => { if (!slot.length) openAdd(toYMD(dayView), hr); }}>
                          {slot.length === 0
                            ? <div style={{ fontSize:11, color:'var(--muted)', fontStyle:'italic', paddingTop:4 }}>+</div>
                            : slot.map(ev => <EntryChip key={ev.id} ev={ev} showTime={false} />)}
                        </div>
                      </div>
                    );
                  })}
                  {unscheduled.length > 0 && (
                    <div style={{ marginTop:14 }}>
                      <div style={{ fontSize:10, fontWeight:700, letterSpacing:'0.14em', textTransform:'uppercase', color:'var(--muted)', marginBottom:6 }}>No time set</div>
                      {unscheduled.map(ev => <EntryChip key={ev.id} ev={ev} showTime={false} />)}
                    </div>
                  )}
                </div>
              );
            })()}
            <div style={{ marginTop:16, textAlign:'right' }}>
              <button className="btn-ghost" onClick={() => setDayView(null)}>Close</button>
            </div>
          </div>
        </div>
      )}

      {/* ── Add / Edit entry modal ─────────────────────────────────────── */}
      {showForm && (
        <div className="modal-overlay" onClick={e => e.target===e.currentTarget && setShowForm(false)}>
          <div className="modal" style={{ maxWidth:520 }}>
            <div className="modal-title">{editId ? 'Edit entry' : 'New entry'}</div>
            <div className="modal-grid">
              <div className="field full">
                <label>Title *</label>
                <input className="input" autoFocus value={form.title}
                  onChange={e => setForm(f => ({ ...f, title: e.target.value }))}
                  placeholder="e.g. Call Grand Prix Motors" />
              </div>
              <div className="field">
                <label>Date *</label>
                <input className="input" type="date" value={form.entry_date}
                  onChange={e => setForm(f => ({ ...f, entry_date: e.target.value }))} />
              </div>
              <div className="field">
                <label>Category</label>
                <select className="input" value={form.category}
                  onChange={e => setForm(f => ({ ...f, category: e.target.value }))}>
                  {DIARY_CATEGORIES.map(c => <option key={c} value={c}>{c.charAt(0).toUpperCase()+c.slice(1)}</option>)}
                </select>
              </div>
              <div className="field">
                <label>Start time</label>
                <input className="input" type="time" value={form.start_time}
                  onChange={e => setForm(f => ({ ...f, start_time: e.target.value }))} />
              </div>
              <div className="field">
                <label>End time</label>
                <input className="input" type="time" value={form.end_time}
                  onChange={e => setForm(f => ({ ...f, end_time: e.target.value }))} />
              </div>
              <div className="field full">
                <label>Notes</label>
                <textarea className="input" rows={3} value={form.description}
                  onChange={e => setForm(f => ({ ...f, description: e.target.value }))}
                  placeholder="Optional details…" />
              </div>
              <div className="field full">
                <label style={{ display:'flex', alignItems:'center', gap:8, cursor:'pointer' }}>
                  <input type="checkbox" checked={form.completed}
                    onChange={e => setForm(f => ({ ...f, completed: e.target.checked }))} />
                  Mark as completed
                </label>
              </div>
            </div>
            <div style={{ display:'flex', justifyContent:'space-between', alignItems:'center', marginTop:18 }}>
              <div>
                {editId && (
                  <button className="btn-ghost" style={{ color:'var(--ruby-400)', borderColor:'var(--ruby-400)' }}
                    disabled={saving} onClick={handleDelete}>Delete</button>
                )}
              </div>
              <div style={{ display:'flex', gap:8 }}>
                <button className="btn-ghost" disabled={saving} onClick={() => setShowForm(false)}>Cancel</button>
                <button className="btn-primary" disabled={saving} onClick={handleSave}>
                  {saving ? 'Saving…' : (editId ? 'Save changes' : 'Add entry')}
                </button>
              </div>
            </div>
          </div>
        </div>
      )}
    </div>
  );
}

/* ════════════════════════════════════════════════════════════════════
   DOCUMENTS PAGE
════════════════════════════════════════════════════════════════════ */
const DOC_TYPES = ['letterhead','terms_conditions','privacy_policy','policy','contract','service_agreement','service_brief','welcome_pack','welcome_letter','invoice','quote','debit_order','other'];
const DOC_TYPE_ICON = { invoice:'🧾', quote:'📝', debit_order:'🏦', welcome_pack:'📦', welcome_letter:'✉️', service_agreement:'📜', service_brief:'📋', letterhead:'📄', terms_conditions:'📑', privacy_policy:'🔒', policy:'📋', contract:'✍️', other:'📄' };
const DOC_TYPE_LABEL = { invoice:'Invoice', quote:'Quote', debit_order:'Debit Order', welcome_pack:'Welcome Pack', welcome_letter:'Welcome Letter', service_agreement:'Service Agreement', service_brief:'Service Brief', letterhead:'Letterhead', terms_conditions:'Terms & Conditions', privacy_policy:'Privacy Policy', policy:'Policy', contract:'Contract', other:'Other' };
const DOC_FOLDERS = ['Pinnacle PA','Dealer Dashboard','Business Centre','LifeCapsule','Celestial','General'];
function emptyDocForm() { return { template_name:'', template_type:'letterhead', folder:'Pinnacle PA', content:'', file_url:'', active:true }; }

function DocumentsPage() {
  const [docs, setDocs]       = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const [error, setError]     = React.useState(null);
  const [showForm, setShowForm] = React.useState(false);
  const [form, setForm]       = React.useState(emptyDocForm());
  const [editId, setEditId]   = React.useState(null);
  const [saving, setSaving]   = React.useState(false);
  const [collapsed, setCollapsed] = React.useState({});

  const fetchDocs = React.useCallback(async () => {
    try {
      setError(null);
      const rows = await window.pccDb.documents.list();
      setDocs(rows || []);
    } catch (e) { setError(e.message); }
    finally { setLoading(false); }
  }, []);
  React.useEffect(() => { if (window.pccDb) fetchDocs(); }, [fetchDocs]);

  function openAdd() { setEditId(null); setForm(emptyDocForm()); setShowForm(true); }
  function openEdit(d) {
    setEditId(d.id);
    setForm({ template_name:d.template_name||'', template_type:d.template_type||'letterhead', folder:d.folder||'General', content:d.content||'', file_url:d.file_url||'', active:d.active!==false });
    setShowForm(true);
  }
  function openAddIn(folder) { setEditId(null); setForm({ ...emptyDocForm(), folder }); setShowForm(true); }

  async function handleSave() {
    if (!form.template_name.trim()) { alert('Give the template a name.'); return; }
    setSaving(true);
    try {
      const payload = {
        template_name: form.template_name.trim(),
        template_type: form.template_type,
        folder: (form.folder || 'General').trim() || 'General',
        content: form.content || null,
        file_url: (form.file_url || '').trim() || null,
        active: !!form.active,
      };
      if (editId) {
        await window.pccDb.documents.update(editId, payload);
      } else {
        await window.pccDb.documents.create(payload);
        window.pccDb.logActivity('system.document_created', payload.template_name, DOC_TYPE_LABEL[payload.template_type]);
      }
      setShowForm(false);
      await fetchDocs();
    } catch (e) { alert('Save failed: ' + e.message); }
    finally { setSaving(false); }
  }
  async function handleDelete() {
    if (!editId) return;
    if (!window.confirm(`Delete "${form.template_name}"? This cannot be undone.`)) return;
    setSaving(true);
    try {
      await window.pccDb.documents.remove(editId);
      setShowForm(false);
      await fetchDocs();
    } catch (e) { alert('Delete failed: ' + e.message); }
    finally { setSaving(false); }
  }

  return (
    <div className="page-content">
      <PageHead eyebrow="DOCUMENTS" title="Templates"
        lede="Service agreements, welcome packs, and billing documents — stored in Supabase."
        action={<button className="btn-primary" onClick={openAdd}><Icon name="plus" size={15} color="var(--gold-700)" /> New Template</button>} />

      {error && (
        <div style={{ padding:12, background:'rgba(161,30,42,0.10)', border:'1.5px solid var(--ruby-400)', borderRadius:8, color:'var(--ruby-400)', fontSize:12, marginBottom:12 }}>
          Supabase error: {error}
        </div>
      )}

      {loading ? (
        <div style={{ padding:'40px 0', textAlign:'center', color:'var(--muted)', fontSize:13 }}>Loading templates…</div>
      ) : docs.length === 0 ? (
        <div style={{ padding:'40px 0', textAlign:'center', color:'var(--muted)', fontSize:13, fontStyle:'italic' }}>
          No templates yet — click New Template to add one.
        </div>
      ) : (() => {
        // Group templates into folders (apps). Known folders first, then any extras.
        const byFolder = {};
        docs.forEach(d => { const f = d.folder || 'General'; (byFolder[f] = byFolder[f] || []).push(d); });
        const order = [...DOC_FOLDERS.filter(f => byFolder[f]), ...Object.keys(byFolder).filter(f => !DOC_FOLDERS.includes(f))];
        return order.map(folder => {
          const items = byFolder[folder];
          const isCollapsed = !!collapsed[folder];
          return (
            <Card key={folder} sub="FOLDER" title={`${folder} · ${items.length}`} style={{ marginTop:14 }}
              action={
                <div style={{ display:'flex', gap:6 }}>
                  <button className="btn-ghost" style={{ fontSize:11, padding:'4px 10px' }} onClick={() => openAddIn(folder)}>+ Add</button>
                  <button className="btn-ghost" style={{ fontSize:13, padding:'4px 12px', lineHeight:1 }}
                    onClick={() => setCollapsed(c => ({ ...c, [folder]: !c[folder] }))}>{isCollapsed ? '▸' : '▾'}</button>
                </div>
              }>
              {!isCollapsed && (
                <div className="doc-grid" style={{ marginTop:4 }}>
                  {items.map(doc => (
                    <div key={doc.id} className="doc-card" style={{ cursor:'pointer' }} onClick={() => openEdit(doc)}>
                      <div style={{ position:'relative', zIndex:3 }}>
                        <div className="doc-icon">{doc.file_url ? '📎' : (DOC_TYPE_ICON[doc.template_type] || '📄')}</div>
                        <div className="doc-name">{doc.template_name}</div>
                        <div style={{ fontSize:11, color:'var(--muted)', marginTop:4 }}>{DOC_TYPE_LABEL[doc.template_type] || doc.template_type}</div>
                        {doc.file_url ? (
                          <a href={doc.file_url} target="_blank" rel="noopener" onClick={e => e.stopPropagation()}
                            style={{ display:'inline-block', marginTop:7, fontSize:11, fontWeight:700, color:'var(--gold-400)', textDecoration:'none' }}>
                            View / Download ↗
                          </a>
                        ) : (
                          <div style={{ fontSize:10, marginTop:6, color: doc.active === false ? 'var(--muted)' : (doc.content ? 'var(--positive)' : '#C9852B') }}>
                            {doc.active === false ? '○ Inactive' : (doc.content ? '● Ready' : '○ Empty — add content')}
                          </div>
                        )}
                      </div>
                    </div>
                  ))}
                </div>
              )}
            </Card>
          );
        });
      })()}

      {showForm && (
        <div className="modal-overlay" onClick={e => e.target===e.currentTarget && setShowForm(false)}>
          <div className="modal" style={{ maxWidth:640 }}>
            <div className="modal-title">{editId ? 'Edit template' : 'New template'}</div>
            <div className="modal-grid">
              <div className="field">
                <label>Template name *</label>
                <input className="input" autoFocus value={form.template_name}
                  onChange={e => setForm(f => ({ ...f, template_name: e.target.value }))}
                  placeholder="e.g. Standard Service Agreement" />
              </div>
              <div className="field">
                <label>Type</label>
                <select className="input" value={form.template_type}
                  onChange={e => setForm(f => ({ ...f, template_type: e.target.value }))}>
                  {DOC_TYPES.map(t => <option key={t} value={t}>{DOC_TYPE_LABEL[t]}</option>)}
                </select>
              </div>
              <div className="field full">
                <label>Folder / App</label>
                <input className="input" list="doc-folders" value={form.folder}
                  onChange={e => setForm(f => ({ ...f, folder: e.target.value }))} placeholder="e.g. Pinnacle PA" />
                <datalist id="doc-folders">{DOC_FOLDERS.map(f => <option key={f} value={f} />)}</datalist>
              </div>
              <div className="field full">
                <label>File link (PDF / URL) — optional</label>
                <input className="input" value={form.file_url}
                  onChange={e => setForm(f => ({ ...f, file_url: e.target.value }))}
                  placeholder="documents/MyForm.pdf  or  https://…" />
              </div>
              <div className="field full">
                <label>Content</label>
                <textarea className="input" rows={8} value={form.content}
                  onChange={e => setForm(f => ({ ...f, content: e.target.value }))}
                  placeholder="Template body. Use {{client_name}}, {{amount}} etc. as placeholders." />
              </div>
              <div className="field full">
                <label style={{ display:'flex', alignItems:'center', gap:8, cursor:'pointer' }}>
                  <input type="checkbox" checked={form.active}
                    onChange={e => setForm(f => ({ ...f, active: e.target.checked }))} />
                  Active
                </label>
              </div>
            </div>
            <div style={{ display:'flex', justifyContent:'space-between', alignItems:'center', marginTop:18 }}>
              <div>
                {editId && (
                  <button className="btn-ghost" style={{ color:'var(--ruby-400)', borderColor:'var(--ruby-400)' }}
                    disabled={saving} onClick={handleDelete}>Delete</button>
                )}
              </div>
              <div style={{ display:'flex', gap:8 }}>
                <button className="btn-ghost" disabled={saving} onClick={() => setShowForm(false)}>Cancel</button>
                <button className="btn-primary" disabled={saving} onClick={handleSave}>{saving ? 'Saving…' : (editId ? 'Save changes' : 'Add template')}</button>
              </div>
            </div>
          </div>
        </div>
      )}
    </div>
  );
}

/* ════════════════════════════════════════════════════════════════════
   REPORTS PAGE
════════════════════════════════════════════════════════════════════ */
function ReportsPage() {
  const [clients, setClients] = React.useState([]);
  const [leads, setLeads]     = React.useState([]);
  React.useEffect(() => {
    if (!window.pccDb) return;
    Promise.all([window.pccDb.clients.list(), window.pccDb.leads.list()])
      .then(([c, l]) => { setClients(c||[]); setLeads(l||[]); })
      .catch(() => {});
  }, []);
  const totalLeads   = leads.length;
  const totalClients = clients.length;
  const pct = (n) => totalLeads > 0 ? Math.round(n / totalLeads * 100) : 0;
  const funnel = [
    { label:'Leads',   pct: totalLeads > 0 ? 100 : 0 },
    { label:'Clients', pct: pct(totalClients)         },
  ];
  const clientRevenue = clients.filter(c => Number(c.monthly_value) > 0).sort((a,b) => Number(b.monthly_value) - Number(a.monthly_value));
  const maxMrr = clientRevenue[0] ? Number(clientRevenue[0].monthly_value) : 1;

  return (
    <div className="page-content">
      <PageHead eyebrow="ANALYTICS" title="Reports"
        lede="Pipeline performance, revenue by client, and P&L overview." />
      <div style={{ display:'grid', gridTemplateColumns:'1fr 1fr', gap:16 }}>
        <Card sub="CONVERSION FUNNEL" title="Pipeline Flow">
          <div className="funnel" style={{ marginTop:12 }}>
            {funnel.map((f,i) => (
              <div key={i} className="funnel-row">
                <div className="funnel-label">{f.label}</div>
                <div className="funnel-bar-wrap"><div className="funnel-bar" style={{ width:`${f.pct}%` }} /></div>
                <div className="funnel-pct">{f.pct}%</div>
              </div>
            ))}
          </div>
        </Card>
        <Card sub="REVENUE BY CLIENT" title="MRR Breakdown">
          <div style={{ display:'flex', flexDirection:'column', gap:10, marginTop:12 }}>
            {clientRevenue.map((c,i) => (
              <div key={i}>
                <div style={{ display:'flex', justifyContent:'space-between', fontSize:12, marginBottom:3 }}>
                  <span style={{ fontFamily:"'Cormorant Garamond',serif", fontStyle:'italic' }}>{c.business_name}</span>
                  <span style={{ fontWeight:700, background:'var(--gold-gradient-text)', WebkitBackgroundClip:'text', backgroundClip:'text', color:'transparent' }}>
                    R {Number(c.monthly_value).toLocaleString('en-ZA')}
                  </span>
                </div>
                <div style={{ height:8, background:'var(--glass-2)', borderRadius:4, overflow:'hidden' }}>
                  <div style={{ width:`${Math.round(Number(c.monthly_value)/maxMrr*100)}%`, height:'100%', background:'var(--gold-gradient)', borderRadius:4 }} />
                </div>
              </div>
            ))}
          </div>
        </Card>
      </div>
    </div>
  );
}

/* ════════════════════════════════════════════════════════════════════
   SETTINGS PAGE
════════════════════════════════════════════════════════════════════ */
const EMPTY_SETTINGS = {
  business_name:'', contact_email:'', contact_phone:'',
  vat_registered:false, vat_number:'',
  working_hours_start:'08:00', working_hours_end:'17:00',
  bank_name:'', account_number:'', branch_code:'',
};
function SettingsPage() {
  const [tab, setTab] = React.useState('business');
  const [s, setS] = React.useState(EMPTY_SETTINGS);
  const [loading, setLoading] = React.useState(true);
  const [saving, setSaving]   = React.useState(false);
  const [saved, setSaved]     = React.useState(false);
  const [error, setError]     = React.useState(null);
  const [activeTheme, setActiveTheme] = React.useState(() => localStorage.getItem('pcc-theme') || 'default');

  const selectTheme = React.useCallback((themeId) => {
    window.applyTheme(themeId);
    setActiveTheme(themeId);
  }, []);

  React.useEffect(() => {
    if (!window.pccDb) { setLoading(false); return; }
    window.pccDb.settings.get(1).then(row => {
      if (row) {
        const b = row.banking || {};
        setS({
          business_name: row.business_name || '',
          contact_email: row.contact_email || '',
          contact_phone: row.contact_phone || '',
          vat_registered: !!row.vat_registered,
          vat_number: row.vat_number || '',
          working_hours_start: (row.working_hours_start || '08:00:00').slice(0,5),
          working_hours_end: (row.working_hours_end || '17:00:00').slice(0,5),
          bank_name: b.bank_name || '', account_number: b.account_number || '', branch_code: b.branch_code || '',
        });
      }
    }).catch(e => setError(e.message)).finally(() => setLoading(false));
  }, []);

  const upd = (k, v) => { setS(prev => ({ ...prev, [k]: v })); setSaved(false); };

  async function saveSettings() {
    setSaving(true); setError(null);
    try {
      const payload = {
        business_name: s.business_name, contact_email: s.contact_email, contact_phone: s.contact_phone,
        vat_registered: !!s.vat_registered, vat_number: s.vat_number.trim() || null,
        working_hours_start: s.working_hours_start, working_hours_end: s.working_hours_end,
        banking: { bank_name: s.bank_name, account_number: s.account_number, branch_code: s.branch_code },
        updated_at: new Date().toISOString(),
      };
      await window.pccDb.settings.update(1, payload);
      window.pccDb.logActivity('system.settings_updated', 'Business settings');
      setSaved(true);
    } catch (e) { setError(e.message); }
    finally { setSaving(false); }
  }

  return (
    <div className="page-content">
      <PageHead eyebrow="CONFIGURATION" title="Settings"
        lede="Business profile, appearance, and preferences — saved to Supabase." />
      <Card>
        <div className="tabs">
          {[['business','Business'],['appearance','Appearance'],['billing','Billing'],['notifications','Notifications']].map(([id,label]) => (
            <div key={id} className={`tab${tab === id ? ' active' : ''}`} onClick={() => setTab(id)}>{label}</div>
          ))}
        </div>
        {error && (
          <div style={{ padding:10, background:'rgba(161,30,42,0.10)', border:'1.5px solid var(--ruby-400)', borderRadius:8, color:'var(--ruby-400)', fontSize:12, margin:'12px 0' }}>
            {error}
          </div>
        )}
        {loading && (tab === 'business' || tab === 'billing') && (
          <div style={{ padding:'24px 0', color:'var(--muted)', fontSize:13 }}>Loading settings…</div>
        )}
        {!loading && tab === 'business' && (
          <div>
            <div style={{ display:'grid', gridTemplateColumns:'1fr 1fr', gap:16 }}>
              <div className="field"><label>Business Name</label><input className="input" value={s.business_name} onChange={e => upd('business_name', e.target.value)} /></div>
              <div className="field"><label>Email</label><input className="input" value={s.contact_email} onChange={e => upd('contact_email', e.target.value)} /></div>
              <div className="field"><label>Phone</label><input className="input" value={s.contact_phone} onChange={e => upd('contact_phone', e.target.value)} /></div>
              <div className="field"><label>VAT Number</label><input className="input" value={s.vat_number} onChange={e => upd('vat_number', e.target.value)} placeholder="—" disabled={!s.vat_registered} /></div>
              <div className="field"><label>Working hours — start</label><input className="input" type="time" value={s.working_hours_start} onChange={e => upd('working_hours_start', e.target.value)} /></div>
              <div className="field"><label>Working hours — end</label><input className="input" type="time" value={s.working_hours_end} onChange={e => upd('working_hours_end', e.target.value)} /></div>
              <div className="field" style={{ gridColumn:'1/-1' }}>
                <label>VAT Registration</label>
                <div style={{ display:'flex', alignItems:'center', gap:12, marginTop:4 }}>
                  <div className={`switch${s.vat_registered ? ' on' : ''}`} onClick={() => upd('vat_registered', !s.vat_registered)}>
                    <div className="switch-knob" />
                  </div>
                  <span style={{ fontSize:13, color: s.vat_registered ? 'var(--gold-500)' : 'var(--muted)' }}>
                    {s.vat_registered ? 'VAT registered — 15% applied to invoices' : 'Not VAT registered (toggle off)'}
                  </span>
                </div>
              </div>
            </div>
            <div style={{ display:'flex', alignItems:'center', gap:12, marginTop:18 }}>
              <button className="btn-primary" disabled={saving} onClick={saveSettings}>{saving ? 'Saving…' : 'Save settings'}</button>
              {saved && <span style={{ fontSize:12, color:'var(--positive)' }}>✓ Saved</span>}
            </div>
          </div>
        )}
        {!loading && tab === 'billing' && (
          <div>
            <div style={{ fontSize:12, color:'var(--muted)', marginBottom:14 }}>
              Banking details below appear on invoices and debit-order documents.
            </div>
            <div style={{ display:'grid', gridTemplateColumns:'1fr 1fr', gap:16 }}>
              <div className="field"><label>Bank Name</label><input className="input" value={s.bank_name} onChange={e => upd('bank_name', e.target.value)} placeholder="e.g. FNB" /></div>
              <div className="field"><label>Account Number</label><input className="input" value={s.account_number} onChange={e => upd('account_number', e.target.value)} /></div>
              <div className="field"><label>Branch Code</label><input className="input" value={s.branch_code} onChange={e => upd('branch_code', e.target.value)} /></div>
            </div>
            <div style={{ display:'flex', alignItems:'center', gap:12, marginTop:18 }}>
              <button className="btn-primary" disabled={saving} onClick={saveSettings}>{saving ? 'Saving…' : 'Save settings'}</button>
              {saved && <span style={{ fontSize:12, color:'var(--positive)' }}>✓ Saved</span>}
            </div>
          </div>
        )}
        {tab === 'appearance' && (
          <div>
            <div style={{ marginBottom:20 }}>
              <div style={{ fontFamily:"'Cormorant Garamond',serif", fontStyle:'italic', fontSize:22, marginBottom:4 }}>
                Theme
              </div>
              <div style={{ fontSize:12, color:'var(--muted)' }}>
                Choose how Pinnacle Command Centre looks and feels.
              </div>
            </div>
            <div className="theme-grid">
              {window.THEMES.map(theme => {
                const isActive = activeTheme === theme.id;
                return (
                  <div key={theme.id}
                    className={`theme-card${isActive ? ' active' : ''}`}
                    onClick={() => selectTheme(theme.id)}>
                    <div className="theme-preview" style={{ background:theme.preview.bg }}>
                      <div className="theme-preview-accent" style={{ background:theme.preview.accent }} />
                      <div className="theme-preview-topbar" style={{ background:theme.preview.card }}>
                        <div className="theme-preview-logo" style={{ background:theme.preview.accent }} />
                        <div className="theme-preview-nav">
                          <span style={{ background:theme.preview.ink, opacity:0.25 }} />
                          <span style={{ background:theme.preview.accent, opacity:0.6 }} />
                          <span style={{ background:theme.preview.ink, opacity:0.25 }} />
                        </div>
                      </div>
                      <div className="theme-preview-body">
                        <div className="theme-mini-card" style={{ background:theme.preview.card }}>
                          <div style={{ width:'60%', height:3, borderRadius:2, background:theme.preview.ink, opacity:0.15 }} />
                          <div style={{ width:'35%', height:3, borderRadius:2, background:theme.preview.accent, opacity:0.4, marginTop:4 }} />
                        </div>
                        <div className="theme-mini-card" style={{ background:theme.preview.card }}>
                          <div style={{ width:'45%', height:3, borderRadius:2, background:theme.preview.ink, opacity:0.15 }} />
                          <div style={{ width:'55%', height:3, borderRadius:2, background:theme.preview.accent, opacity:0.4, marginTop:4 }} />
                        </div>
                        <div className="theme-mini-card" style={{ background:theme.preview.card }}>
                          <div style={{ width:'50%', height:3, borderRadius:2, background:theme.preview.ink, opacity:0.15 }} />
                          <div style={{ width:'40%', height:3, borderRadius:2, background:theme.preview.accent, opacity:0.4, marginTop:4 }} />
                        </div>
                      </div>
                    </div>
                    <div className="theme-card-info">
                      <div className="theme-card-name">{theme.name}</div>
                      <div className="theme-card-desc">{theme.desc}</div>
                    </div>
                    {isActive && <div className="theme-card-check">✓</div>}
                  </div>
                );
              })}
            </div>
          </div>
        )}
        {tab === 'notifications' && (
          <p style={{ color:'var(--muted)', fontStyle:'italic', padding:'20px 0' }}>
            Notification preferences coming soon.
          </p>
        )}
      </Card>
    </div>
  );
}

/* ═══════ OPERATIONS PAGE (live data — replaces the old fake "Agents" sim) ═══════ */

/* Live operations board — each monitor reports REAL counts from Supabase and
   links to where you act on them. No simulated "agents", no fake progress. */
/* Per-agent "at work" visual — a premium 3D render of a gold-chrome robot agent
   working at a glowing holographic workstation (SAMS style), brought to life with
   a slow Ken-Burns float, an agent-coloured sheen sweep, and a live pulse. Each
   card is desynced via --agd; the ambient `active` pulse brightens it. */
function AgentBot({ active, idx, color }) {
  const delay = ((idx || 0) * 0.5).toFixed(2) + 's';
  return (
    <div className={'agentbot agentbot-photo' + (active ? ' active' : '')}
         style={{ '--agc': color || 'var(--gold-400)', '--agd': delay }}>
      <img className="ab-photo" src="agent-iso-sample.jpg?v=51" alt=""
           onError={(e) => { e.target.style.display = 'none'; }} />
      <span className="ab-scan" />
      <span className="ab-live2" />
    </div>
  );
}

const AGENT_KEYS = ['leads','invoices','diary','clients','docs','mrr'];
function AgentsPage() {
  const [data, setData]           = React.useState(null);
  const [loading, setLoading]     = React.useState(true);
  const [error, setError]         = React.useState(null);
  const [refreshedAt, setRefresh] = React.useState(null);
  const [working, setWorking]     = React.useState({});  // per-agent ambient "working" state
  const iframeRef    = React.useRef(null);   // 3D scene iframe — fed real data via postMessage
  const monitorsRef  = React.useRef([]);
  const metaRef      = React.useRef({ success:100, done:0, loops:0 });
  const loopCountRef = React.useRef(0);

  const load = React.useCallback(async () => {
    setLoading(true); setError(null);
    try {
      const [clients, leads, invoices, diary, docs] = await Promise.all([
        window.pccDb.clients.list(),
        window.pccDb.leads.list().catch(() => []),
        window.pccDb.invoices.list(),
        window.pccDb.diary.list(`entry_date=eq.${toYMD(new Date())}`),
        window.pccDb.documents.list(),
      ]);
      setData({ clients:clients||[], leads:leads||[], invoices:invoices||[], diary:diary||[], docs:docs||[] });
      loopCountRef.current += 1;
      setRefresh(new Date());
    } catch (e) { setError(e.message); }
    finally { setLoading(false); }
  }, []);
  React.useEffect(() => { if (window.pccDb) load(); }, [load]);

  // Each agent "works" at its own random interval (desynchronised, like staff in
  // separate cubicles). A working tick also triggers a THROTTLED real refresh, so
  // the activity reflects genuine monitoring rather than fake animation.
  React.useEffect(() => {
    let alive = true;
    const timers = {};
    let lastLoad = Date.now();
    // Each agent runs a continuous, staggered monitoring cycle — frequent + overlapping so the
    // floor is always busy (all agents working), and every cycle reflects real Supabase data
    // (throttled live refresh). Not fake animation: each tick re-derives the agent's metric.
    const scheduleAgent = (key, first) => {
      const delay = first ? (300 + Math.random() * 2000) : (2500 + Math.random() * 5000); // busy 2.5–7.5s
      timers[key] = setTimeout(() => {
        if (!alive) return;
        setWorking(w => ({ ...w, [key]: true }));
        if (window.pccDb && Date.now() - lastLoad > 12000) { lastLoad = Date.now(); load(); }
        setTimeout(() => { if (alive) setWorking(w => ({ ...w, [key]: false })); }, 2500 + Math.random() * 2500);
        scheduleAgent(key);
      }, delay);
    };
    AGENT_KEYS.forEach(k => scheduleAgent(k, true));
    return () => { alive = false; Object.values(timers).forEach(clearTimeout); };
  }, [load]);

  // Feed the 3D scene the SAME real monitors + working state the cards use, so the
  // bots reflect genuine Supabase data (not just animation).
  const postToScene = React.useCallback(() => {
    const win = iframeRef.current && iframeRef.current.contentWindow;
    if (!win) return;
    win.postMessage({
      source: 'pcc',
      working,
      monitors: monitorsRef.current.map(m => ({ key:m.key, name:m.name, role:m.role, metric:m.metric, sub:m.sub, detail:m.detail })),
      metrics: metaRef.current,
    }, '*');
  }, [working]);
  React.useEffect(() => { postToScene(); }, [data, working, postToScene]);
  React.useEffect(() => {
    const onMsg = (e) => { if (e.data && e.data.source === 'pcc-scene' && e.data.ready) postToScene(); };
    window.addEventListener('message', onMsg);
    return () => window.removeEventListener('message', onMsg);
  }, [postToScene]);

  const invAmt = (i) => Number(i.amount_incl_vat != null ? i.amount_incl_vat : (Number(i.amount_excl_vat||0) + Number(i.vat_amount||0)));

  let monitors = [];
  if (data) {
    const activeClients = data.clients.filter(c => c.pipeline_stage === 'Active');
    const onboarding    = data.clients.filter(c => c.pipeline_stage === 'Onboarding');
    const churned       = data.clients.filter(c => c.pipeline_stage === 'Churned');
    const mrr           = activeClients.reduce((s,c) => s + Number(c.monthly_value||0), 0);
    const unpaid        = data.invoices.filter(i => i.status === 'sent' || i.status === 'overdue');
    const overdue       = data.invoices.filter(i => i.status === 'overdue');
    const unpaidSum     = unpaid.reduce((s,i) => s + invAmt(i), 0);
    const paidSum       = data.invoices.filter(i => i.status === 'paid').reduce((s,i) => s + invAmt(i), 0);
    const nextEntry     = data.diary.filter(e => e.start_time).sort((a,b) => (a.start_time < b.start_time ? -1 : 1))[0];
    const activeDocs    = data.docs.filter(d => d.active !== false);

    monitors = [
      { key:'leads',   name:'Lead Scout',      role:'PROSPECTING',   icon:'🎯', metric:data.leads.length, sub:'leads',     detail:`${data.leads.length} in the funnel.`,                                  color:'var(--sapphire)', nav:'leads' },
      { key:'invoices',name:'Invoice Bot',     role:'BILLING',       icon:'🧾', metric:unpaid.length,      sub:'unpaid',    detail:`${fmtR(unpaidSum)} outstanding · ${overdue.length} overdue.`,          color:'var(--gold-400)', nav:'finance' },
      { key:'diary',   name:'Diary Assistant', role:'SCHEDULE',      icon:'📅', metric:data.diary.length,  sub:'today',     detail: nextEntry ? `Next: ${nextEntry.title} @ ${hhmm(nextEntry.start_time)}` : 'Nothing scheduled today.', color:'var(--gold-500)', nav:'diary' },
      { key:'clients', name:'Client Monitor',  role:'RELATIONSHIPS', icon:'👥', metric:activeClients.length, sub:'active',  detail:`${onboarding.length} onboarding · ${churned.length} churned.`,         color:'var(--sapphire)', nav:'clients' },
      { key:'docs',    name:'Doc Handler',     role:'DOCUMENTS',     icon:'📄', metric:activeDocs.length,  sub:'templates', detail:`${data.docs.length} total templates on file.`,                         color:'var(--gold-400)', nav:'documents' },
      { key:'mrr',     name:'Revenue Analyst', role:'FINANCE',       icon:'📈', metric:fmtR(mrr),          sub:'MRR',       detail:`${fmtR(paidSum)} collected · ${fmtR(mrr*12)} ARR.`,                     color:'var(--gold-400)', nav:'reports' },
    ];
    const overdueN = data.invoices.filter(i => i.status === 'overdue').length;
    const churnedN = data.clients.filter(c => c.pipeline_stage === 'Churned').length;
    const totalItems = data.leads.length + data.invoices.length + data.clients.length + data.docs.length + data.diary.length;
    metaRef.current = { success: (overdueN === 0 && churnedN === 0) ? 100 : Math.max(80, 100 - overdueN*4 - churnedN*3), done: totalItems, loops: loopCountRef.current };
  }
  monitorsRef.current = monitors;

  return (
    <div className="page-content">
      <PageHead eyebrow="LIVE OPS" title="Operations"
        lede="Real-time operational monitors — live counts straight from your data."
        action={<button className="btn-ghost" disabled={loading} onClick={load}>{loading ? 'Refreshing…' : '↻ Refresh'}</button>} />

      <div style={{ position:'relative', width:'100%', height:640, borderRadius:14, overflow:'hidden', border:'1px solid var(--line)', marginBottom:18, background:'var(--cream)' }}>
        <iframe ref={iframeRef} src="office-live.html?v=1" onLoad={postToScene} title="Agent Workspace — live office" style={{ width:'100%', height:'100%', border:0, display:'block' }} loading="lazy"></iframe>
      </div>

      {error && (
        <div style={{ padding:12, background:'rgba(161,30,42,0.10)', border:'1.5px solid var(--ruby-400)', borderRadius:8, color:'var(--ruby-400)', fontSize:12, marginBottom:12 }}>
          Supabase error: {error}
        </div>
      )}

      {/* Per-agent desk cards removed — the live 3D workspace above is the single view. */}

      {refreshedAt && (
        <div style={{ fontSize:11, color:'var(--muted)', marginTop:16 }}>
          Live · last updated {refreshedAt.toLocaleTimeString('en-ZA',{hour:'2-digit',minute:'2-digit',second:'2-digit'})}
        </div>
      )}
    </div>
  );
}

/* ════════════════════════════════════════════════════════════════════
   INFRASTRUCTURE PAGE — live stack map (animated app→db→host→storage)
════════════════════════════════════════════════════════════════════ */
const INFRA_KINDS    = ['app','supabase','cloudflare','netlify','storage','domain'];
const INFRA_STATUSES = ['active','paused','free','planned','deleted'];
const INFRA_KIND_COLOR = { app:'var(--infra-app)', supabase:'var(--supabase)', cloudflare:'var(--cloudflare)', netlify:'var(--netlify)', storage:'var(--storage)', domain:'var(--gold-500)' };
const INFRA_KIND_LABEL = { app:'App', supabase:'Supabase', cloudflare:'Cloudflare', netlify:'Netlify', storage:'Storage', domain:'Domain' };
const INFRA_STATUS_COLOR = { active:'var(--positive)', paused:'#C9852B', free:'var(--muted)', planned:'var(--sapphire)', deleted:'var(--ruby-400)' };
function shortName(n) { return String(n||'').replace(/^(Supabase: |CF Pages: |CF Worker: |R2: |Netlify: )/, ''); }
function emptyInfraForm() { return { name:'', kind:'app', status:'active', account:'', url:'', ref:'', connects_to:'', notes:'' }; }

function InfrastructurePage() {
  const [rows, setRows]       = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const [error, setError]     = React.useState(null);
  const [showAdd, setShowAdd] = React.useState(false);
  const [form, setForm]       = React.useState(emptyInfraForm());
  const [editId, setEditId]   = React.useState(null);
  const [saving, setSaving]   = React.useState(false);

  const fetchRows = React.useCallback(async () => {
    try {
      setError(null);
      const r = (await window.pccDb.infra.list()) || [];
      r.sort((a,b) => (a.sort_order||0) - (b.sort_order||0));
      setRows(r);
    } catch (e) { setError(e.message); } finally { setLoading(false); }
  }, []);
  React.useEffect(() => { if (window.pccDb) fetchRows(); }, [fetchRows]);

  const byName = {}; rows.forEach(r => { byName[r.name] = r; });
  const apps = rows.filter(r => r.kind === 'app' && r.status !== 'deleted');
  const kindCount = (k) => rows.filter(r => r.kind === k && r.status !== 'deleted').length;

  function openAdd() { setEditId(null); setForm(emptyInfraForm()); setShowAdd(true); }
  function openEdit(r) {
    setEditId(r.id);
    setForm({ name:r.name||'', kind:r.kind||'app', status:r.status||'active', account:r.account||'', url:r.url||'', ref:r.ref||'', connects_to:(r.connects_to||[]).join('\n'), notes:r.notes||'' });
    setShowAdd(true);
  }
  async function handleSave() {
    if (!form.name.trim()) { alert('Give the project a name.'); return; }
    setSaving(true);
    try {
      const payload = {
        name: form.name.trim(), kind: form.kind, status: form.status,
        account: form.account.trim() || null, url: form.url.trim() || null, ref: form.ref.trim() || null,
        connects_to: form.connects_to.split('\n').map(s => s.trim()).filter(Boolean),
        notes: form.notes.trim() || null,
      };
      if (editId) { await window.pccDb.infra.update(editId, payload); }
      else { await window.pccDb.infra.create(payload); window.pccDb.logActivity('system.infra_added', form.name.trim(), INFRA_KIND_LABEL[form.kind]); }
      setShowAdd(false); await fetchRows();
    } catch (e) { alert('Save failed: ' + e.message); } finally { setSaving(false); }
  }
  async function del(r) {
    if (!window.confirm(`Remove "${r.name}" from the map?`)) return;
    try { await window.pccDb.infra.remove(r.id); setRows(prev => prev.filter(x => x.id !== r.id)); }
    catch (e) { alert('Delete failed: ' + e.message); }
  }

  const FlowNode = ({ node }) => (
    <div className={'infra-node infra-node-' + node.kind} style={{ cursor:'pointer' }} onClick={() => openEdit(node)} title={node.account || ''}>
      <div className="infra-node-name">{shortName(node.name)}</div>
      <div className="infra-node-meta">{INFRA_KIND_LABEL[node.kind]}{node.ref ? ' · ' + node.ref.slice(0,14) : (node.url ? ' · ' + node.url : '')}</div>
    </div>
  );

  return (
    <div className="page-content">
      <PageHead eyebrow="STACK" title="Infrastructure"
        lede="Your live projects and how they connect — apps flow to their database, host and storage."
        action={<button className="btn-primary" onClick={openAdd}><Icon name="plus" size={15} color="var(--gold-700)" /> Add Project</button>} />

      {error && <div style={ERRBOX}>Supabase error: {error}</div>}

      <div className="kpi-grid">
        <KPI label="Active projects" value={rows.filter(r => r.status !== 'deleted').length} trend="tracked" trendDir="flat" />
        <KPI label="Apps"            value={kindCount('app')} trend="products" trendDir="flat" />
        <KPI label="Supabase DBs"    value={kindCount('supabase')} trend="databases" trendDir="flat" />
        <KPI label="Cloudflare"      value={kindCount('cloudflare')} trend="pages + workers" trendDir="flat" />
      </div>

      {/* Legend */}
      <div style={{ display:'flex', flexWrap:'wrap', gap:10, margin:'4px 0 14px' }}>
        {INFRA_KINDS.map(k => (
          <div key={k} className="infra-pill"><span className="infra-dot" style={{ background:INFRA_KIND_COLOR[k] }} /> {INFRA_KIND_LABEL[k]}</div>
        ))}
      </div>

      <Card sub="ARCHITECTURE" title="How it connects">
        {loading ? <div style={{ padding:'40px 0', textAlign:'center', color:'var(--muted)', fontSize:13 }}>Loading map…</div>
        : apps.length === 0 ? <div style={{ padding:'24px 0', textAlign:'center', color:'var(--muted)', fontSize:13, fontStyle:'italic' }}>No apps yet — add a project of type “App” and list what it connects to.</div>
        : (
          <div style={{ marginTop:6 }}>
            {apps.map(app => (
              <div className="infra-flow" key={app.id}>
                <FlowNode node={app} />
                {(app.connects_to || []).map((nm, i) => {
                  const t = byName[nm];
                  return (
                    <React.Fragment key={i}>
                      <div className="infra-arrow" />
                      {t ? <FlowNode node={t} /> : (
                        <div className="infra-node" style={{ borderLeftColor:'var(--muted)' }}>
                          <div className="infra-node-name">{shortName(nm)}</div>
                          <div className="infra-node-meta">not in inventory</div>
                        </div>
                      )}
                    </React.Fragment>
                  );
                })}
              </div>
            ))}
          </div>
        )}
      </Card>

      {/* Inventory grouped by platform */}
      {['supabase','cloudflare','storage','netlify','domain'].map(kind => {
        const items = rows.filter(r => r.kind === kind);
        if (!items.length) return null;
        return (
          <Card key={kind} sub={INFRA_KIND_LABEL[kind].toUpperCase()} title={INFRA_KIND_LABEL[kind] + ' projects'} style={{ marginTop:16 }}>
            <div className="infra-col-grid">
              {items.map(r => (
                <div key={r.id} className={'infra-node infra-node-' + r.kind} style={{ maxWidth:'none', cursor:'pointer' }} onClick={() => openEdit(r)}>
                  <div style={{ display:'flex', justifyContent:'space-between', alignItems:'flex-start', gap:6 }}>
                    <div className="infra-node-name">{shortName(r.name)}</div>
                    <span className="infra-dot" style={{ background:INFRA_STATUS_COLOR[r.status], marginTop:4 }} title={r.status} />
                  </div>
                  {r.account && <div className="infra-node-meta">{r.account}</div>}
                  {r.ref && <div className="infra-node-meta" style={{ fontFamily:"'JetBrains Mono',monospace" }}>{r.ref}</div>}
                  {r.notes && <div className="infra-node-meta" style={{ fontStyle:'italic' }}>{r.notes}</div>}
                </div>
              ))}
            </div>
          </Card>
        );
      })}

      {showAdd && (
        <div className="modal-overlay" onClick={e => e.target===e.currentTarget && setShowAdd(false)}>
          <div className="modal" style={{ maxWidth:560 }}>
            <div className="modal-title">{editId ? 'Edit project' : 'Add project'}</div>
            <div className="modal-grid">
              <div className="field full"><label>Name *</label><input className="input" autoFocus value={form.name} onChange={e => setForm(f => ({ ...f, name:e.target.value }))} placeholder="e.g. CF Pages: my-app" /></div>
              <div className="field"><label>Type</label>
                <select className="input" value={form.kind} onChange={e => setForm(f => ({ ...f, kind:e.target.value }))}>
                  {INFRA_KINDS.map(k => <option key={k} value={k}>{INFRA_KIND_LABEL[k]}</option>)}
                </select>
              </div>
              <div className="field"><label>Status</label>
                <select className="input" value={form.status} onChange={e => setForm(f => ({ ...f, status:e.target.value }))}>
                  {INFRA_STATUSES.map(s => <option key={s} value={s}>{s.charAt(0).toUpperCase()+s.slice(1)}</option>)}
                </select>
              </div>
              <div className="field"><label>Account / login</label><input className="input" value={form.account} onChange={e => setForm(f => ({ ...f, account:e.target.value }))} placeholder="e.g. info@pinnaclepa.biz" /></div>
              <div className="field"><label>URL</label><input className="input" value={form.url} onChange={e => setForm(f => ({ ...f, url:e.target.value }))} placeholder="optional" /></div>
              <div className="field full"><label>Ref / ID</label><input className="input" value={form.ref} onChange={e => setForm(f => ({ ...f, ref:e.target.value }))} placeholder="Supabase ref / worker id…" /></div>
              <div className="field full"><label>Connects to (one per line — match other project names exactly)</label>
                <textarea className="input" rows={3} value={form.connects_to} onChange={e => setForm(f => ({ ...f, connects_to:e.target.value }))} placeholder={'Supabase: dealers-dashboards\nCF Pages: pinnacle-command-centre'} />
              </div>
              <div className="field full"><label>Notes</label><input className="input" value={form.notes} onChange={e => setForm(f => ({ ...f, notes:e.target.value }))} placeholder="optional" /></div>
            </div>
            <div style={{ display:'flex', justifyContent:'space-between', alignItems:'center', marginTop:18 }}>
              <div>{editId && <button className="btn-ghost" style={{ color:'var(--ruby-400)', borderColor:'var(--ruby-400)' }} disabled={saving} onClick={() => { const id=editId, nm=form.name; setShowAdd(false); del({ id, name:nm }); }}>Remove</button>}</div>
              <div style={{ display:'flex', gap:8 }}>
                <button className="btn-ghost" disabled={saving} onClick={() => setShowAdd(false)}>Cancel</button>
                <button className="btn-primary" disabled={saving} onClick={handleSave}>{saving ? 'Saving…' : (editId ? 'Save changes' : 'Add project')}</button>
              </div>
            </div>
          </div>
        </div>
      )}
    </div>
  );
}

/* ════════════════════════════════════════════════════════════════════
   EXPORTS
════════════════════════════════════════════════════════════════════ */
window.ClientsPage      = ClientsPage;
window.OnboardingPage   = OnboardingPage;
window.LeadsPage        = LeadsPage;
window.ProvisioningPage = ProvisioningPage;
window.WebsitePage      = WebsitePage;
window.FinancePage      = FinancePage;
window.DiaryPage        = DiaryPage;
window.DocumentsPage    = DocumentsPage;
window.ReportsPage      = ReportsPage;
window.SettingsPage     = SettingsPage;
window.AgentsPage       = AgentsPage;
window.InfrastructurePage = InfrastructurePage;
