/* QuoteFlow Prototype · extra admin screens, all reading window.QFStore /
   window.QF_CATALOG. Rendered by AdminApp's screen map. */

/* -------- Proposals -------- */
function AdminProposals({ store, onOpen }) {
  const { Card, StatusBadge, Avatar, Badge } = window.QuoteFlowDesignSystem_41788d;
  const rows = store.getProposals();
  const m = store.getMetrics();
  const Stat = ({ label, value }) => (
    <Card padding={18} style={{ flex: 1 }}>
      <div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.05em', textTransform: 'uppercase', color: 'var(--text-faint)' }}>{label}</div>
      <div style={{ fontFamily: 'var(--font-display)', fontWeight: 700, fontSize: 26, color: 'var(--text-strong)', marginTop: 6 }}>{value}</div>
    </Card>
  );
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 22 }}>
      <div><h1 style={{ fontSize: 30 }}>Proposals</h1><p style={{ marginTop: 6, fontSize: 15, color: 'var(--text-muted)' }}>Every proposal you've sent, with live acceptance &amp; deposit status from the customer portal.</p></div>
      <div style={{ display: 'flex', gap: 16 }}>
        <Stat label="Proposals sent" value={m.proposalsSent} />
        <Stat label="Accepted" value={m.won} />
        <Stat label="Deposits collected" value={gbp(m.deposits)} />
        <Stat label="Won value" value={gbp(m.wonValue)} />
      </div>
      <Card padding={0}>
        {rows.length === 0 ? (
          <div style={{ padding: 40, textAlign: 'center', color: 'var(--text-faint)', fontSize: 14 }}>No proposals yet — open a lead in the pipeline and hit “Send proposal”.</div>
        ) : (
          <table style={{ width: '100%', borderCollapse: 'collapse', fontFamily: 'var(--font-sans)' }}>
            <thead><tr style={{ textAlign: 'left' }}>{['Customer', 'System', 'Total', 'Deposit', 'Status', 'Sent'].map(h => <th key={h} style={{ padding: '11px 24px', fontSize: 11, fontWeight: 700, letterSpacing: '0.04em', textTransform: 'uppercase', color: 'var(--text-faint)', borderBottom: '1px solid var(--border-default)' }}>{h}</th>)}</tr></thead>
            <tbody>
              {rows.map((l, i) => (
                <tr key={l.id} onClick={() => onOpen(l.id)} style={{ borderBottom: i < rows.length - 1 ? '1px solid var(--slate-100)' : 'none', cursor: 'pointer' }}>
                  <td style={{ padding: '13px 24px' }}><div style={{ display: 'flex', alignItems: 'center', gap: 10 }}><Avatar name={l.name} size={32} /><span style={{ fontSize: 14, fontWeight: 600, color: 'var(--text-strong)' }}>{l.name}</span></div></td>
                  <td style={{ padding: '13px 24px', fontSize: 14, color: 'var(--text-body)' }}>{l.system}</td>
                  <td style={{ padding: '13px 24px', fontSize: 14, fontWeight: 600, color: 'var(--text-strong)' }}>{gbp(l.proposal.total)}</td>
                  <td style={{ padding: '13px 24px', fontSize: 13.5, color: 'var(--text-muted)' }}>{gbp(l.proposal.deposit)}</td>
                  <td style={{ padding: '13px 24px' }}>
                    {l.proposal.payment
                      ? <Badge tone="success">Deposit paid</Badge>
                      : l.proposal.status === 'accepted' ? <Badge tone="primary">Accepted</Badge> : <Badge tone="info">Awaiting customer</Badge>}
                  </td>
                  <td style={{ padding: '13px 24px', fontSize: 13, color: 'var(--text-muted)' }}>{store.fmtDate(l.proposal.sentAt)}</td>
                </tr>
              ))}
            </tbody>
          </table>
        )}
      </Card>
    </div>
  );
}
window.AdminProposals = AdminProposals;

/* -------- After Care (won customers become installed base) -------- */
function AdminAfterCare({ store, onOpen }) {
  const { Card, MetricCard, StatusBadge, Avatar, Badge, Button } = window.QuoteFlowDesignSystem_41788d;
  const won = store.getWon();
  const m = store.getMetrics();
  const plans = store.SERVICE_PLANS;
  const planTone = (id) => id === 'Premium care' ? 'primary' : id === 'Standard care' ? 'info' : 'neutral';
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 22 }}>
      <div><h1 style={{ fontSize: 30 }}>After care</h1><p style={{ marginTop: 6, fontSize: 15, color: 'var(--text-muted)' }}>Customers who've accepted become your installed base — manage service plans, warranties &amp; renewals.</p></div>
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(200px, 1fr))', gap: 16 }}>
        <MetricCard label="Installed customers" value={won.length} icon={<Icon name="heart-handshake" size={20} />} />
        <MetricCard dark label="Recurring revenue / yr" value={gbp(m.recurring)} />
        <MetricCard label="Renewals due (30 days)" value={Math.min(2, won.length)} icon={<Icon name="bell-ring" size={20} />} />
        <MetricCard label="Avg. response time" value="3.2h" icon={<Icon name="timer" size={20} />} />
      </div>
      <Card padding={0}>
        {won.length === 0 ? (
          <div style={{ padding: 40, textAlign: 'center', color: 'var(--text-faint)', fontSize: 14 }}>No installed customers yet — once a proposal is accepted in the portal, the customer appears here.</div>
        ) : (
          <table style={{ width: '100%', borderCollapse: 'collapse', fontFamily: 'var(--font-sans)' }}>
            <thead><tr style={{ textAlign: 'left' }}>{['Customer', 'System', 'Service plan', 'Annual', 'Status'].map(h => <th key={h} style={{ padding: '11px 24px', fontSize: 11, fontWeight: 700, letterSpacing: '0.04em', textTransform: 'uppercase', color: 'var(--text-faint)', borderBottom: '1px solid var(--border-default)' }}>{h}</th>)}</tr></thead>
            <tbody>
              {won.map((l, i) => (
                <tr key={l.id} style={{ borderBottom: i < won.length - 1 ? '1px solid var(--slate-100)' : 'none' }}>
                  <td style={{ padding: '13px 24px', cursor: 'pointer' }} onClick={() => onOpen(l.id)}><div style={{ display: 'flex', alignItems: 'center', gap: 11 }}><Avatar name={l.name} size={34} /><div style={{ lineHeight: 1.3 }}><div style={{ fontSize: 14, fontWeight: 600, color: 'var(--text-strong)' }}>{l.name}</div><div style={{ fontSize: 12, color: 'var(--text-faint)' }}>{l.loc}</div></div></div></td>
                  <td style={{ padding: '13px 24px', fontSize: 14, color: 'var(--text-body)' }}><span style={{ display: 'flex', alignItems: 'center', gap: 9 }}><CatTile cat={l.cat} size={28} />{l.system}</span></td>
                  <td style={{ padding: '13px 24px' }}>
                    <select value={l.servicePlan || 'No plan'} onChange={e => store.setServicePlan(l.id, e.target.value)}
                      style={{ appearance: 'none', WebkitAppearance: 'none', padding: '6px 30px 6px 12px', fontFamily: 'var(--font-sans)', fontSize: 13, fontWeight: 600, color: 'var(--text-strong)', background: 'var(--surface-card) url("data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 width=%2712%27 height=%2712%27 viewBox=%270 0 24 24%27 fill=%27none%27 stroke=%27%236F6989%27 stroke-width=%273%27 stroke-linecap=%27round%27 stroke-linejoin=%27round%27%3E%3Cpolyline points=%276 9 12 15 18 9%27/%3E%3C/svg%3E") no-repeat right 10px center', border: '1px solid var(--border-default)', borderRadius: 'var(--radius-full)', cursor: 'pointer', outline: 'none' }}>
                      {plans.map(p => <option key={p.id} value={p.id}>{p.id}</option>)}
                    </select>
                  </td>
                  <td style={{ padding: '13px 24px', fontSize: 13.5, fontWeight: 600, color: store.servicePlanAnnual(l.servicePlan) ? 'var(--text-strong)' : 'var(--text-faint)' }}>{store.servicePlanAnnual(l.servicePlan) ? gbp(store.servicePlanAnnual(l.servicePlan)) + '/yr' : '—'}</td>
                  <td style={{ padding: '13px 24px', cursor: 'pointer' }} onClick={() => onOpen(l.id)}><StatusBadge status="Aftercare" /></td>
                </tr>
              ))}
            </tbody>
          </table>
        )}
      </Card>
    </div>
  );
}
window.AdminAfterCare = AdminAfterCare;

/* -------- Quote Tools (from the shared catalogue packages) -------- */
function AdminQuoteTools({ store, onPreview }) {
  const { Card, Badge, Button, Input, Switch, Select } = window.QuoteFlowDesignSystem_41788d;
  const C = window.QF_CATALOG;
  const [modal, setModal] = React.useState(null); // {mode:'configure'|'create', tool}
  const tools = [
    { name: 'Solar & Battery Quote Tool', cat: 'solar', status: 'Active', desc: 'Design a solar & battery system and get an instant estimate.' },
    { name: 'Battery Storage Quote Tool', cat: 'battery', status: 'Active', desc: 'Add storage to an existing system or load-shift off-peak.' },
    { name: 'EV Charger Quote Tool', cat: 'ev', status: 'Active', desc: 'Choose a smart charger for home or workplace.' },
    { name: 'Heat Pump Quote Tool', cat: 'heat', status: 'Draft', desc: 'Estimate the cost of switching to an air-source heat pump.' },
  ];
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 22 }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', gap: 16, flexWrap: 'wrap' }}>
        <div><h1 style={{ fontSize: 30 }}>Quote tools</h1><p style={{ marginTop: 6, fontSize: 15, color: 'var(--text-muted)' }}>Branded quote journeys you publish to your website. Pricing comes from your shared catalogue.</p></div>
        <Button variant="primary" iconLeft={<Icon name="plus" size={16} />} onClick={() => setModal({ mode: 'create' })}>Create quote tool</Button>
      </div>
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(280px, 1fr))', gap: 18 }}>
        {tools.map((t, i) => (
          <Card key={i} dashed={t.status === 'Draft'} hover={t.status === 'Active'} padding={22} style={{ display: 'flex', flexDirection: 'column', gap: 12, opacity: t.status === 'Draft' ? 0.85 : 1 }}>
            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start' }}>
              <CatTile cat={t.cat} />
              <StatusBadgeSafe status={t.status} />
            </div>
            <h3 style={{ fontFamily: 'var(--font-display)', fontSize: 17, fontWeight: 700, color: 'var(--text-strong)' }}>{t.name}</h3>
            <p style={{ fontSize: 13.5, lineHeight: 1.5, color: 'var(--text-muted)', flex: 1 }}>{t.desc}</p>
            <div style={{ display: 'flex', gap: 8 }}>
              <Button variant="secondary" size="sm" style={{ flex: 1 }} onClick={() => setModal({ mode: 'configure', tool: t })}>Configure</Button>
              <Button variant="ghost" size="sm" style={{ flex: 1 }} iconLeft={<Icon name="external-link" size={14} />} onClick={() => onPreview && onPreview()}>Preview</Button>
            </div>
          </Card>
        ))}
      </div>
      <div>
        <h2 style={{ fontSize: 20, marginBottom: 12 }}>Published packages</h2>
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(240px, 1fr))', gap: 14 }}>
          {C.packages.map((p, i) => (
            <Card key={i} padding={18} style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
                <span style={{ fontFamily: 'var(--font-display)', fontWeight: 700, fontSize: 14.5, color: 'var(--text-strong)' }}>{p.name}</span>
                <Badge tone={p.tier === 'Most popular' ? 'primary' : 'neutral'}>{p.tier}</Badge>
              </div>
              <div style={{ fontSize: 12.5, color: 'var(--text-muted)' }}>{C.kWp(p)} kWp · {C.margin(p)}% margin</div>
              <div style={{ fontFamily: 'var(--font-display)', fontWeight: 700, fontSize: 18, color: 'var(--text-strong)' }}>{gbp(C.price(p))}</div>
            </Card>
          ))}
        </div>
      </div>

      {modal && <QuoteToolModal modal={modal} onClose={() => setModal(null)} onPreview={onPreview} />}
    </div>
  );
}
window.AdminQuoteTools = AdminQuoteTools;

/* Configure / Create modal for quote tools (dummy but interactive). */
function QuoteToolModal({ modal, onClose, onPreview }) {
  const { Card, Button, Input, Switch, Select, Badge } = window.QuoteFlowDesignSystem_41788d;
  const isCreate = modal.mode === 'create';
  const t = modal.tool;
  const [name, setName] = React.useState(isCreate ? '' : t.name);
  const [cat, setCat] = React.useState(isCreate ? 'Solar & battery' : 'Solar & battery');
  const [portal, setPortal] = React.useState(true);
  const [deposits, setDeposits] = React.useState(true);
  const [saved, setSaved] = React.useState(false);
  const save = () => { setSaved(true); setTimeout(onClose, 850); };
  return (
    <div onClick={onClose} style={{ position: 'fixed', inset: 0, background: 'rgba(22,14,42,0.45)', zIndex: 70, display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 20 }}>
      <div onClick={e => e.stopPropagation()} style={{ width: 480, maxWidth: '100%', maxHeight: '90vh', overflowY: 'auto', background: 'var(--surface-card)', borderRadius: 'var(--radius-lg)', boxShadow: 'var(--shadow-xl)' }}>
        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '18px 22px', borderBottom: '1px solid var(--border-default)' }}>
          <h2 style={{ fontFamily: 'var(--font-display)', fontSize: 18, fontWeight: 700 }}>{isCreate ? 'Create quote tool' : 'Configure · ' + t.name}</h2>
          <button onClick={onClose} style={{ border: 'none', background: 'none', cursor: 'pointer', color: 'var(--text-muted)', display: 'flex' }}><Icon name="x" size={20} /></button>
        </div>
        {saved ? (
          <div style={{ padding: 40, display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 12, textAlign: 'center' }}>
            <span style={{ width: 56, height: 56, borderRadius: '50%', background: 'var(--green-50)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}><Icon name="check" size={28} color="var(--accent-deep)" stroke={2.4} /></span>
            <div style={{ fontSize: 16, fontWeight: 700, color: 'var(--text-strong)' }}>{isCreate ? 'Quote tool created' : 'Changes saved'}</div>
          </div>
        ) : (
          <React.Fragment>
            <div style={{ padding: 22, display: 'flex', flexDirection: 'column', gap: 16 }}>
              <Input label="Tool name" value={name} onChange={e => setName(e.target.value)} placeholder="e.g. Solar & Battery Quote Tool" />
              <Select label="Technology" value={cat} onChange={e => setCat(e.target.value)} options={['Solar & battery', 'Battery storage', 'EV charger', 'Heat pump', 'Air conditioning']} />
              <div style={{ background: 'var(--surface-sunken)', borderRadius: 'var(--radius-md)', padding: 14, display: 'flex', flexDirection: 'column', gap: 12 }}>
                <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}><span style={{ fontSize: 13.5, color: 'var(--text-body)' }}>Show customer portal after quote</span><Switch checked={portal} onChange={setPortal} /></div>
                <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}><span style={{ fontSize: 13.5, color: 'var(--text-body)' }}>Collect a deposit on acceptance</span><Switch checked={deposits} onChange={setDeposits} /></div>
              </div>
              <div style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 12.5, color: 'var(--text-muted)' }}>
                <Icon name="info" size={15} color="var(--text-faint)" /> Hardware &amp; pricing are pulled from your shared catalogue.
              </div>
            </div>
            <div style={{ display: 'flex', gap: 10, padding: '16px 22px', borderTop: '1px solid var(--border-default)' }}>
              {!isCreate && <Button variant="secondary" iconLeft={<Icon name="external-link" size={15} />} onClick={() => { onClose(); onPreview && onPreview(); }}>Preview</Button>}
              <span style={{ flex: 1 }} />
              <Button variant="ghost" onClick={onClose}>Cancel</Button>
              <Button variant="primary" onClick={save}>{isCreate ? 'Create tool' : 'Save changes'}</Button>
            </div>
          </React.Fragment>
        )}
      </div>
    </div>
  );
}
window.QuoteToolModal = QuoteToolModal;

/* StatusBadge wrapper that tolerates "Draft"/"Active" custom labels */
function StatusBadgeSafe({ status }) {
  const { StatusBadge, Badge } = window.QuoteFlowDesignSystem_41788d;
  if (status === 'Active') return <Badge tone="success">Active</Badge>;
  if (status === 'Draft') return <Badge tone="neutral">Draft</Badge>;
  return <StatusBadge status={status} />;
}

/* -------- Settings (white-label + behaviour toggles that gate the flow) -------- */
function AdminSettings({ store }) {
  const { Card, Switch, Button, Badge, Input, Select } = window.QuoteFlowDesignSystem_41788d;
  const s = store.getSettings();
  const Field = ({ label, hint, children }) => (
    <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: 24, padding: '16px 0', borderBottom: '1px solid var(--slate-100)' }}>
      <div style={{ maxWidth: 360 }}><div style={{ fontSize: 14.5, fontWeight: 600, color: 'var(--text-strong)' }}>{label}</div>{hint && <div style={{ fontSize: 13, color: 'var(--text-muted)', marginTop: 3, lineHeight: 1.45 }}>{hint}</div>}</div>
      {children}
    </div>
  );
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 20, maxWidth: 760 }}>
      <div><h1 style={{ fontSize: 30 }}>Settings</h1><p style={{ marginTop: 6, fontSize: 15, color: 'var(--text-muted)' }}>White-label your tools and control how the customer journey behaves. These toggles actually change the portal flow in this prototype.</p></div>
      <Card padding={0}>
        <div style={{ padding: '18px 24px', borderBottom: '1px solid var(--border-default)', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
          <h2 style={{ fontSize: 18, fontFamily: 'var(--font-display)', fontWeight: 700 }}>Branding</h2><Badge tone="primary">White-label</Badge>
        </div>
        <div style={{ padding: '4px 24px 10px' }}>
          <Field label="Brand logo" hint="Shown in the customer header & portal.">
            <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}><span style={{ width: 44, height: 44, borderRadius: 'var(--radius-md)', background: 'var(--green-700)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}><Icon name="leaf" size={22} color="#fff" /></span><Button variant="secondary" size="sm" iconLeft={<Icon name="upload" size={15} />}>Replace</Button></div>
          </Field>
          <Field label="Custom domain" hint="Host your quote tool & portal on your own subdomain.">
            <div style={{ width: 260 }}><Input defaultValue="quote.greenline.energy" /></div>
          </Field>
        </div>
      </Card>
      <Card padding={0}>
        <div style={{ padding: '18px 24px', borderBottom: '1px solid var(--border-default)' }}><h2 style={{ fontSize: 18, fontFamily: 'var(--font-display)', fontWeight: 700 }}>Customer journey</h2></div>
        <div style={{ padding: '4px 24px 10px' }}>
          <Field label="Customer portal" hint="Let customers track their quote, accept proposals & upload documents."><Switch checked={s.customerPortal} onChange={v => store.setSetting('customerPortal', v)} /></Field>
          <Field label="E-signature" hint="Capture a signature when a proposal is accepted."><Switch checked={s.eSignature} onChange={v => store.setSetting('eSignature', v)} /></Field>
          <Field label="Take deposits" hint="When on, the portal asks the customer to pay a 15% deposit after accepting. Turn off to accept without payment."><Switch checked={s.takeDeposits} onChange={v => store.setSetting('takeDeposits', v)} /></Field>
        </div>
        <div style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '14px 24px', background: 'var(--surface-sunken)', borderTop: '1px solid var(--border-default)' }}>
          <Icon name="info" size={16} color="var(--text-muted)" />
          <span style={{ fontSize: 13, color: 'var(--text-muted)' }}>“Take deposits” is wired live — toggle it, then accept a proposal in the customer portal to see the difference.</span>
        </div>
      </Card>
    </div>
  );
}
window.AdminSettings = AdminSettings;

/* -------- Profile (editable user details, address, subscription) -------- */
function AdminProfile({ store, onBack }) {
  const { Card, Input, Select, Button, Badge, Avatar } = window.QuoteFlowDesignSystem_41788d;
  const p = store.getProfile();
  const [form, setForm] = React.useState(p);
  const [saved, setSaved] = React.useState(false);
  const set = (k) => (e) => { setForm(Object.assign({}, form, { [k]: e.target.value })); setSaved(false); };
  const dirty = JSON.stringify(form) !== JSON.stringify(p);
  const save = () => { store.setProfile(form); setSaved(true); };

  const plans = [
    { id: 'Launch', price: '£375/mo', desc: 'Smaller installers streamlining their business. Gold support.' },
    { id: 'Scale', price: '£690/mo', desc: 'Growing teams with multiple quote tools & journeys. Priority support.' },
    { id: 'Enterprise', price: 'Custom', desc: 'Multi-branch operations, SSO, custom integrations & SLAs.' },
  ];

  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 22, maxWidth: 860 }}>
      <button onClick={onBack} style={{ display: 'inline-flex', alignItems: 'center', gap: 6, border: 'none', background: 'none', cursor: 'pointer', fontFamily: 'var(--font-sans)', fontSize: 13.5, color: 'var(--text-muted)', alignSelf: 'flex-start' }}>
        <Icon name="arrow-left" size={15} /> Back to dashboard
      </button>

      <div style={{ display: 'flex', alignItems: 'center', gap: 16 }}>
        <Avatar name={form.name} size={60} />
        <div style={{ flex: 1 }}>
          <h1 style={{ fontSize: 28 }}>{form.name}</h1>
          <p style={{ fontSize: 14.5, color: 'var(--text-muted)', marginTop: 2 }}>{form.role} · {form.company}</p>
        </div>
        <Badge tone="primary">{form.plan} plan</Badge>
      </div>

      {/* Personal details */}
      <Card padding={0}>
        <div style={{ padding: '18px 24px', borderBottom: '1px solid var(--border-default)' }}><h2 style={{ fontSize: 18, fontFamily: 'var(--font-display)', fontWeight: 700 }}>Personal details</h2></div>
        <div style={{ padding: 24, display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16 }}>
          <Input label="Full name" value={form.name} onChange={set('name')} />
          <Input label="Role" value={form.role} onChange={set('role')} />
          <Input label="Email" type="email" value={form.email} onChange={set('email')} />
          <Input label="Phone" value={form.phone} onChange={set('phone')} />
        </div>
      </Card>

      {/* Business address */}
      <Card padding={0}>
        <div style={{ padding: '18px 24px', borderBottom: '1px solid var(--border-default)' }}><h2 style={{ fontSize: 18, fontFamily: 'var(--font-display)', fontWeight: 700 }}>Business address</h2></div>
        <div style={{ padding: 24, display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16 }}>
          <div style={{ gridColumn: '1 / -1' }}><Input label="Company" value={form.company} onChange={set('company')} /></div>
          <div style={{ gridColumn: '1 / -1' }}><Input label="Address line 1" value={form.address1} onChange={set('address1')} /></div>
          <div style={{ gridColumn: '1 / -1' }}><Input label="Address line 2" value={form.address2} onChange={set('address2')} /></div>
          <Input label="Town / city" value={form.city} onChange={set('city')} />
          <Input label="Postcode" value={form.postcode} onChange={set('postcode')} />
          <Input label="VAT number" value={form.vat} onChange={set('vat')} />
        </div>
      </Card>

      {/* Subscription plan */}
      <Card padding={0}>
        <div style={{ padding: '18px 24px', borderBottom: '1px solid var(--border-default)', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
          <h2 style={{ fontSize: 18, fontFamily: 'var(--font-display)', fontWeight: 700 }}>Subscription plan</h2>
          <span style={{ fontSize: 13, color: 'var(--text-muted)' }}>Renews {form.renews} · {form.billing}</span>
        </div>
        <div style={{ padding: 24, display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 14 }}>
          {plans.map(pl => {
            const on = form.plan === pl.id;
            return (
              <button key={pl.id} onClick={() => { setForm(Object.assign({}, form, { plan: pl.id })); setSaved(false); }}
                style={{ textAlign: 'left', cursor: 'pointer', background: on ? 'var(--primary-soft)' : 'var(--surface-card)', border: on ? '2px solid var(--primary)' : '1px solid var(--border-default)', borderRadius: 'var(--radius-md)', padding: 16, display: 'flex', flexDirection: 'column', gap: 6 }}>
                <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
                  <span style={{ fontFamily: 'var(--font-display)', fontWeight: 700, fontSize: 16, color: 'var(--text-strong)' }}>{pl.id}</span>
                  {on && <Icon name="check-circle" size={18} color="var(--primary)" />}
                </div>
                <div style={{ fontFamily: 'var(--font-display)', fontWeight: 700, fontSize: 18, color: on ? 'var(--primary)' : 'var(--text-strong)' }}>{pl.price}</div>
                <div style={{ fontSize: 12.5, color: 'var(--text-muted)', lineHeight: 1.45 }}>{pl.desc}</div>
              </button>
            );
          })}
        </div>
        <div style={{ padding: '0 24px 20px' }}>
          <Button variant="secondary" size="sm" iconLeft={<Icon name="credit-card" size={15} />}>Manage billing &amp; invoices</Button>
        </div>
      </Card>

      {/* sticky-ish save bar */}
      <div style={{ display: 'flex', alignItems: 'center', gap: 12, position: 'sticky', bottom: 0, background: 'var(--surface-card)', border: '1px solid var(--border-default)', borderRadius: 'var(--radius-md)', padding: '14px 18px', boxShadow: 'var(--shadow-md)' }}>
        {saved
          ? <span style={{ display: 'flex', alignItems: 'center', gap: 7, fontSize: 13.5, fontWeight: 600, color: 'var(--accent-deep)' }}><Icon name="check-circle" size={16} color="var(--accent-deep)" /> Profile saved</span>
          : <span style={{ fontSize: 13.5, color: 'var(--text-muted)' }}>{dirty ? 'You have unsaved changes' : 'Your profile is up to date'}</span>}
        <span style={{ flex: 1 }} />
        <Button variant="ghost" onClick={() => { setForm(p); setSaved(false); }} disabled={!dirty}>Discard</Button>
        <Button variant="primary" onClick={save} disabled={!dirty} iconLeft={<Icon name="save" size={15} />}>Save changes</Button>
      </div>
    </div>
  );
}
window.AdminProfile = AdminProfile;

/* -------- ProjectBlock: survey + install milestones (admin lead drawer) -------- */
function ProjectBlock({ store, lead }) {
  const { Button, Input } = window.QuoteFlowDesignSystem_41788d;
  const proj = lead.project || {};
  const won = !!(lead.proposal && lead.proposal.status === 'accepted');
  const [surveyDate, setSurveyDate] = React.useState(proj.surveyDate || store.plusDaysISO(5));
  const [surveyNote, setSurveyNote] = React.useState(proj.surveyNote || '');
  const [installDate, setInstallDate] = React.useState(proj.installDate || store.plusDaysISO(21));

  if (!won) {
    return (
      <div style={{ border: '1px dashed var(--border-strong)', borderRadius: 'var(--radius-md)', padding: 14, display: 'flex', alignItems: 'center', gap: 10 }}>
        <Icon name="calendar-clock" size={18} color="var(--text-faint)" />
        <span style={{ fontSize: 13, color: 'var(--text-muted)' }}>Survey &amp; install scheduling unlocks once the proposal is accepted.</span>
      </div>
    );
  }

  const Step = ({ icon, title, done, doneText, children }) => (
    <div style={{ display: 'flex', gap: 11 }}>
      <span style={{ width: 26, height: 26, flex: 'none', borderRadius: '50%', display: 'flex', alignItems: 'center', justifyContent: 'center', marginTop: 2,
        background: done ? 'var(--accent-deep)' : 'var(--surface-sunken)', border: done ? 'none' : '1.5px solid var(--border-strong)' }}>
        <Icon name={done ? 'check' : icon} size={14} color={done ? '#fff' : 'var(--text-muted)'} stroke={done ? 3 : 2} />
      </span>
      <div style={{ flex: 1 }}>
        <div style={{ fontSize: 13.5, fontWeight: 600, color: 'var(--text-strong)' }}>{title}</div>
        {done ? <div style={{ fontSize: 12.5, color: 'var(--accent-deep)', marginTop: 2, fontWeight: 600 }}>{doneText}</div> : <div style={{ marginTop: 8 }}>{children}</div>}
      </div>
    </div>
  );

  return (
    <div style={{ border: '1px solid var(--border-default)', borderRadius: 'var(--radius-md)', overflow: 'hidden' }}>
      <div style={{ padding: '12px 14px', background: 'var(--surface-sunken)', fontSize: 11, fontWeight: 700, letterSpacing: '0.04em', textTransform: 'uppercase', color: 'var(--text-muted)' }}>Project schedule</div>
      <div style={{ padding: 16, display: 'flex', flexDirection: 'column', gap: 18 }}>
        {/* Survey */}
        <Step icon="ruler" title="Technical survey" done={!!proj.surveyDate} doneText={proj.surveyDate ? 'Booked for ' + store.prettyISO(proj.surveyDate) : ''}>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
            <input type="date" value={surveyDate} onChange={e => setSurveyDate(e.target.value)} style={dateStyle} />
            <Input placeholder="Access notes (optional)" value={surveyNote} onChange={e => setSurveyNote(e.target.value)} size="sm" />
            <Button variant="primary" size="sm" iconLeft={<Icon name="calendar-check" size={15} />} onClick={() => store.bookSurvey(lead.id, surveyDate, surveyNote)}>Book survey</Button>
          </div>
        </Step>
        {/* Install — only once survey booked */}
        {proj.surveyDate && (
          <Step icon="wrench" title="Installation" done={!!proj.installDate} doneText={proj.installDate ? (proj.completedAt ? 'Completed' : 'Scheduled for ' + store.prettyISO(proj.installDate)) : ''}>
            <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
              <input type="date" value={installDate} onChange={e => setInstallDate(e.target.value)} style={dateStyle} />
              <Button variant="primary" size="sm" iconLeft={<Icon name="calendar-check" size={15} />} onClick={() => store.scheduleInstall(lead.id, installDate)}>Schedule install</Button>
            </div>
          </Step>
        )}
        {/* Reschedule / complete actions when install is booked */}
        {proj.installDate && !proj.completedAt && (
          <Button variant="primary" size="sm" fullWidth iconLeft={<Icon name="party-popper" size={15} />} onClick={() => store.completeInstall(lead.id)}>Mark install complete</Button>
        )}
        {proj.completedAt && (
          <div style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 13, fontWeight: 600, color: 'var(--accent-deep)', background: 'var(--green-50)', borderRadius: 'var(--radius-md)', padding: '10px 12px' }}>
            <Icon name="badge-check" size={16} color="var(--accent-deep)" /> Installed on {store.prettyISO((new Date(proj.completedAt)).toISOString().slice(0, 10))} — now in after care.
          </div>
        )}
      </div>
    </div>
  );
}
var dateStyle = { width: '100%', boxSizing: 'border-box', height: 40, padding: '0 14px', fontFamily: 'var(--font-sans)', fontSize: 14, color: 'var(--text-strong)', background: 'var(--surface-card)', border: '1px solid var(--border-default)', borderRadius: 'var(--radius-full)', outline: 'none' };
window.ProjectBlock = ProjectBlock;

/* -------- MessagesBlock: email/SMS notification log + manual send (drawer) -------- */
function MessagesBlock({ store, lead }) {
  const { Button } = window.QuoteFlowDesignSystem_41788d;
  const [compose, setCompose] = React.useState(false);
  const [channel, setChannel] = React.useState('email');
  const [subject, setSubject] = React.useState('');
  const msgs = (lead.messages || []).slice().reverse();
  const send = () => {
    if (!subject.trim()) return;
    store.sendMessage(lead.id, channel, subject.trim(), '');
    setSubject(''); setCompose(false);
  };
  return (
    <div style={{ border: '1px solid var(--border-default)', borderRadius: 'var(--radius-md)', overflow: 'hidden' }}>
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '12px 14px', background: 'var(--surface-sunken)' }}>
        <span style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.04em', textTransform: 'uppercase', color: 'var(--text-muted)' }}>Messages sent · {msgs.length}</span>
        <button onClick={() => setCompose(c => !c)} style={{ display: 'inline-flex', alignItems: 'center', gap: 5, border: 'none', background: 'none', cursor: 'pointer', fontFamily: 'var(--font-sans)', fontSize: 12.5, fontWeight: 600, color: 'var(--primary)' }}>
          <Icon name={compose ? 'x' : 'plus'} size={14} color="var(--primary)" /> {compose ? 'Cancel' : 'Send'}
        </button>
      </div>

      {compose && (
        <div style={{ padding: 14, display: 'flex', flexDirection: 'column', gap: 10, borderBottom: '1px solid var(--border-default)' }}>
          <div style={{ display: 'inline-flex', gap: 4, padding: 3, background: 'var(--surface-sunken)', borderRadius: 'var(--radius-full)', alignSelf: 'flex-start' }}>
            {['email', 'sms'].map(ch => (
              <button key={ch} onClick={() => setChannel(ch)} style={{ height: 28, padding: '0 13px', border: 'none', cursor: 'pointer', borderRadius: 'var(--radius-full)', fontFamily: 'var(--font-sans)', fontWeight: 600, fontSize: 12, textTransform: 'capitalize', background: channel === ch ? 'var(--ink-900)' : 'transparent', color: channel === ch ? '#fff' : 'var(--text-muted)' }}>{ch === 'sms' ? 'SMS' : 'Email'}</button>
            ))}
          </div>
          <input value={subject} onChange={e => setSubject(e.target.value)} placeholder={channel === 'sms' ? 'Message to ' + (lead.phone || 'customer') : 'Subject / message to ' + (lead.email || 'customer')}
            style={{ width: '100%', boxSizing: 'border-box', height: 38, padding: '0 14px', fontFamily: 'var(--font-sans)', fontSize: 13.5, color: 'var(--text-strong)', background: 'var(--surface-card)', border: '1px solid var(--border-default)', borderRadius: 'var(--radius-full)', outline: 'none' }} />
          <Button variant="primary" size="sm" iconLeft={<Icon name="send" size={14} />} onClick={send}>Send {channel === 'sms' ? 'SMS' : 'email'}</Button>
          <p style={{ fontSize: 11, color: 'var(--text-faint)', margin: 0 }}>Demo stub — no real message is sent; it's logged below.</p>
        </div>
      )}

      <div style={{ display: 'flex', flexDirection: 'column' }}>
        {msgs.length === 0 && <div style={{ padding: 16, fontSize: 13, color: 'var(--text-faint)', textAlign: 'center' }}>No messages yet.</div>}
        {msgs.map((m, i) => (
          <div key={i} style={{ display: 'flex', gap: 11, padding: '12px 14px', borderBottom: i < msgs.length - 1 ? '1px solid var(--slate-100)' : 'none' }}>
            <span style={{ width: 30, height: 30, flex: 'none', borderRadius: '50%', display: 'flex', alignItems: 'center', justifyContent: 'center', background: m.channel === 'sms' ? '#E0E7FF' : 'var(--green-50)' }}>
              <Icon name={m.channel === 'sms' ? 'message-square' : 'mail'} size={15} color={m.channel === 'sms' ? '#4338CA' : 'var(--accent-deep)'} />
            </span>
            <div style={{ flex: 1, minWidth: 0 }}>
              <div style={{ display: 'flex', justifyContent: 'space-between', gap: 8 }}>
                <span style={{ fontSize: 13, fontWeight: 600, color: 'var(--text-strong)', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{m.subject}</span>
                <span style={{ fontSize: 11, color: 'var(--text-faint)', flex: 'none' }}>{store.fmtDate(m.ts)}</span>
              </div>
              <div style={{ fontSize: 12, color: 'var(--text-muted)', marginTop: 1 }}>{m.channel === 'sms' ? 'SMS' : 'Email'} · {m.to}</div>
              {m.body && <div style={{ fontSize: 12.5, color: 'var(--text-body)', marginTop: 4, lineHeight: 1.4 }}>{m.body}</div>}
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}
window.MessagesBlock = MessagesBlock;

/* -------- ActivityFeed: roll-up of recent activity + messages (dashboard) -------- */
function ActivityFeed({ store, onOpen }) {
  const { Card, Avatar } = window.QuoteFlowDesignSystem_41788d;
  const feed = store.getFeed(14);
  const meta = {
    sms: { icon: 'message-square', bg: '#E0E7FF', fg: '#4338CA' },
    email: { icon: 'mail', bg: 'var(--green-50)', fg: 'var(--accent-deep)' },
    activity: { icon: 'activity', bg: 'var(--surface-sunken)', fg: 'var(--text-muted)' },
  };
  const ago = (ts) => {
    const s = Math.floor((Date.now() - ts) / 1000);
    if (s < 60) return 'just now';
    const m = Math.floor(s / 60); if (m < 60) return m + 'm ago';
    const h = Math.floor(m / 60); if (h < 24) return h + 'h ago';
    const d = Math.floor(h / 24); if (d < 7) return d + 'd ago';
    return store.fmtDate(ts);
  };
  return (
    <Card padding={0} style={{ display: 'flex', flexDirection: 'column', maxHeight: 560 }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '18px 20px', borderBottom: '1px solid var(--border-default)' }}>
        <h2 style={{ fontSize: 18, fontFamily: 'var(--font-display)', fontWeight: 700 }}>Activity</h2>
        <span style={{ fontSize: 11, fontWeight: 700, color: 'var(--text-faint)', background: 'var(--surface-sunken)', borderRadius: 'var(--radius-full)', padding: '2px 9px' }}>Live</span>
      </div>
      <div style={{ overflowY: 'auto', padding: '6px 0' }}>
        {feed.length === 0 && <div style={{ padding: 24, textAlign: 'center', fontSize: 13, color: 'var(--text-faint)' }}>No activity yet.</div>}
        {feed.map((f, i) => {
          const mt = meta[f.kind] || meta.activity;
          return (
            <button key={i} onClick={() => onOpen && onOpen(f.leadId)}
              style={{ display: 'flex', gap: 11, alignItems: 'flex-start', width: '100%', textAlign: 'left', border: 'none', background: 'transparent', cursor: 'pointer', padding: '10px 20px' }}>
              <span style={{ width: 30, height: 30, flex: 'none', borderRadius: '50%', display: 'flex', alignItems: 'center', justifyContent: 'center', background: mt.bg }}>
                <Icon name={mt.icon} size={15} color={mt.fg} />
              </span>
              <div style={{ flex: 1, minWidth: 0 }}>
                <div style={{ fontSize: 13, color: 'var(--text-body)', lineHeight: 1.4 }}><b style={{ color: 'var(--text-strong)', fontWeight: 600 }}>{f.name}</b> · {f.label}</div>
                <div style={{ fontSize: 11.5, color: 'var(--text-faint)', marginTop: 1 }}>{ago(f.ts)}</div>
              </div>
            </button>
          );
        })}
      </div>
    </Card>
  );
}
window.ActivityFeed = ActivityFeed;

/* -------- LeadsChart: SVG line chart of new leads over time -------- */
function LeadsChart({ store, days }) {
  const { Card } = window.QuoteFlowDesignSystem_41788d;
  const data = store.leadsOverTime(days || 30);
  const [hover, setHover] = React.useState(null);   // index of hovered day
  const W = 560, H = 150, padL = 8, padR = 8, padT = 12, padB = 22;
  const max = Math.max(1, ...data.map(d => d.count));
  const n = data.length;
  const x = (i) => padL + (n <= 1 ? 0 : (i / (n - 1)) * (W - padL - padR));
  const y = (v) => padT + (1 - v / max) * (H - padT - padB);
  const pts = data.map((d, i) => [x(i), y(d.count)]);
  const line = pts.map((p, i) => (i ? 'L' : 'M') + p[0].toFixed(1) + ' ' + p[1].toFixed(1)).join(' ');
  const area = line + ' L ' + x(n - 1).toFixed(1) + ' ' + (H - padB) + ' L ' + x(0).toFixed(1) + ' ' + (H - padB) + ' Z';
  const total = data.reduce((s, d) => s + d.count, 0);
  const ticks = data.filter((d, i) => i % Math.ceil(n / 6) === 0 || i === n - 1);
  const hd = hover != null ? data[hover] : null;
  const bandW = (W - padL - padR) / Math.max(1, n);
  return (
    <Card padding={0}>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '18px 24px 8px' }}>
        <div>
          <h2 style={{ fontSize: 20 }}>Leads over time</h2>
          <p style={{ fontSize: 13, color: 'var(--text-muted)', marginTop: 2 }}>{total} new {total === 1 ? 'lead' : 'leads'} in the last {days || 30} days</p>
        </div>
        <span style={{ display: 'inline-flex', alignItems: 'center', gap: 6, fontSize: 12.5, color: 'var(--text-muted)' }}><span style={{ width: 10, height: 10, borderRadius: '50%', background: 'var(--primary)' }} /> New leads / day</span>
      </div>
      <div style={{ padding: '0 16px 14px', position: 'relative' }}>
        <svg viewBox={'0 0 ' + W + ' ' + H} style={{ width: '100%', height: 'auto', display: 'block' }} preserveAspectRatio="none">
          <defs>
            <linearGradient id="qfChartFill" x1="0" y1="0" x2="0" y2="1">
              <stop offset="0" stopColor="var(--primary)" stopOpacity="0.18" />
              <stop offset="1" stopColor="var(--primary)" stopOpacity="0" />
            </linearGradient>
          </defs>
          {[0, 0.5, 1].map((g, i) => (
            <line key={i} x1={padL} x2={W - padR} y1={padT + g * (H - padT - padB)} y2={padT + g * (H - padT - padB)} stroke="var(--slate-150)" strokeWidth="1" />
          ))}
          <path d={area} fill="url(#qfChartFill)" />
          <path d={line} fill="none" stroke="var(--primary)" strokeWidth="2.5" strokeLinejoin="round" strokeLinecap="round" />
          {/* hovered guideline */}
          {hd && <line x1={x(hover)} x2={x(hover)} y1={padT} y2={H - padB} stroke="var(--primary)" strokeWidth="1" strokeDasharray="3 3" opacity="0.5" />}
          {pts.map((p, i) => (data[i].count > 0 || i === hover) ? <circle key={i} cx={p[0]} cy={p[1]} r={i === hover ? 5 : 3} fill="var(--primary)" stroke={i === hover ? '#fff' : 'none'} strokeWidth={i === hover ? 2 : 0} /> : null)}
          {ticks.map((d, i) => {
            const idx = data.indexOf(d);
            return <text key={i} x={x(idx)} y={H - 6} fontSize="9" fontFamily="var(--font-mono)" fill="var(--text-faint)" textAnchor="middle">{d.label}</text>;
          })}
          {/* invisible hover hit-areas, one per day */}
          {data.map((d, i) => (
            <rect key={i} x={x(i) - bandW / 2} y={0} width={bandW} height={H - padB} fill="transparent"
              onMouseEnter={() => setHover(i)} onMouseLeave={() => setHover(h => h === i ? null : h)} style={{ cursor: 'pointer' }} />
          ))}
        </svg>
        {/* tooltip */}
        {hd && (
          <div style={{ position: 'absolute', top: 2, left: 'calc(' + (x(hover) / W * 100) + '% )', transform: 'translateX(-50%)', pointerEvents: 'none',
            background: 'var(--indigo-900)', color: 'var(--cream)', borderRadius: 'var(--radius-md)', padding: '7px 11px', boxShadow: 'var(--shadow-md)', whiteSpace: 'nowrap' }}>
            <div style={{ fontSize: 11, color: 'var(--text-on-dark-muted)' }}>{hd.label}</div>
            <div style={{ fontSize: 14, fontWeight: 700, fontFamily: 'var(--font-display)' }}>{hd.count} {hd.count === 1 ? 'lead' : 'leads'}</div>
          </div>
        )}
      </div>
    </Card>
  );
}
window.LeadsChart = LeadsChart;

/* -------- ToolStats: per-quote-tool conversion -------- */
function ToolStats({ store }) {
  const { Card } = window.QuoteFlowDesignSystem_41788d;
  const rows = store.toolStats();
  const maxLeads = Math.max(1, ...rows.map(r => r.leads));
  return (
    <Card padding={0}>
      <div style={{ padding: '18px 22px 6px' }}><h2 style={{ fontSize: 18, fontFamily: 'var(--font-display)', fontWeight: 700 }}>By quote tool</h2></div>
      <div style={{ padding: '6px 22px 16px', display: 'flex', flexDirection: 'column', gap: 14 }}>
        {rows.map((t, i) => (
          <div key={i} style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 9 }}>
              <CatTile cat={t.cat} size={26} />
              <span style={{ flex: 1, fontSize: 13.5, fontWeight: 600, color: 'var(--text-strong)', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{t.system}</span>
              <span style={{ fontSize: 12.5, color: 'var(--text-muted)' }}>{t.leads} {t.leads === 1 ? 'lead' : 'leads'}</span>
              <span style={{ fontFamily: 'var(--font-display)', fontWeight: 700, fontSize: 13.5, color: t.conversion >= 50 ? 'var(--accent-deep)' : 'var(--text-strong)', width: 40, textAlign: 'right' }}>{t.conversion}%</span>
            </div>
            <div style={{ height: 6, borderRadius: 'var(--radius-full)', background: 'var(--surface-sunken)', overflow: 'hidden' }}>
              <div style={{ width: (t.leads / maxLeads * 100) + '%', height: '100%', borderRadius: 'var(--radius-full)', background: 'var(--accent-gradient)' }} />
            </div>
          </div>
        ))}
        {rows.length === 0 && <div style={{ padding: 16, textAlign: 'center', fontSize: 13, color: 'var(--text-faint)' }}>No leads yet.</div>}
      </div>
    </Card>
  );
}
window.ToolStats = ToolStats;

/* -------- LeadEditBlock: inline edit of a lead's contact fields -------- */
function LeadEditBlock({ store, lead, onDone }) {
  const { Input, Button } = window.QuoteFlowDesignSystem_41788d;
  const [f, setF] = React.useState({ name: lead.name, email: lead.email || '', phone: lead.phone || '', postcode: lead.postcode || '', loc: lead.loc || '' });
  const set = (k) => (e) => setF(Object.assign({}, f, { [k]: e.target.value }));
  const save = () => { store.updateLead(lead.id, f); onDone && onDone(); };
  return (
    <div style={{ background: 'var(--surface-sunken)', borderRadius: 'var(--radius-md)', padding: 16, display: 'flex', flexDirection: 'column', gap: 12 }}>
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
        <div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.04em', textTransform: 'uppercase', color: 'var(--text-faint)' }}>Edit contact</div>
        <button onClick={onDone} aria-label="Close editing" style={{ display: 'inline-flex', border: 'none', background: 'none', cursor: 'pointer', color: 'var(--text-muted)', padding: 0 }}><Icon name="x" size={18} /></button>
      </div>
      <Input label="Full name" value={f.name} onChange={set('name')} />
      <div style={{ display: 'flex', gap: 10 }}>
        <Input label="Email" type="email" value={f.email} onChange={set('email')} style={{ flex: 1 }} />
        <Input label="Phone" value={f.phone} onChange={set('phone')} style={{ flex: 1 }} />
      </div>
      <div style={{ display: 'flex', gap: 10 }}>
        <Input label="Area" value={f.loc} onChange={set('loc')} style={{ flex: 1 }} />
        <Input label="Postcode" value={f.postcode} onChange={set('postcode')} style={{ flex: 1 }} />
      </div>
      <div style={{ display: 'flex', gap: 10, justifyContent: 'flex-end' }}>
        <Button variant="ghost" size="sm" onClick={onDone}>Cancel</Button>
        <Button variant="primary" size="sm" iconLeft={<Icon name="check" size={15} />} onClick={save}>Save changes</Button>
      </div>
    </div>
  );
}
window.LeadEditBlock = LeadEditBlock;

/* -------- PipelineBoard: Trello-style kanban with drag-and-drop --------
   Module-scope (stable identity) so store re-renders & its own drag state
   never remount it mid-drag — the bug when it was inline in AdminApp. */
var QF_STAGE_ACCENT = {
  'New lead': '#5B6BE8', 'Contacted': '#0EA5E9', 'Quote completed': '#8B5CF6',
  'Proposal sent': '#F59E0B', 'Accepted': '#12B981', 'Survey booked': '#0E9488', 'Install booked': '#6366F1', 'Completed': '#12B981', 'Lost': '#94A3B8',
};
function PipelineBoard({ store, onOpen }) {
  const { Card, Avatar } = window.QuoteFlowDesignSystem_41788d;
  const [dragId, setDragId] = React.useState(null);
  const [overStage, setOverStage] = React.useState(null);
  const cols = store.byStage();
  const stages = store.STAGES.filter(s => s !== 'Lost');
  const dragLead = dragId ? store.getLead(dragId) : null;

  const drop = (stage, e) => {
    var id = dragId;
    if (!id && e && e.dataTransfer) { try { id = e.dataTransfer.getData('text/plain'); } catch (err) {} }
    if (id) store.setStage(id, stage);
    setDragId(null); setOverStage(null);
  };

  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 18, height: '100%' }}>
      <div style={{ flex: 'none' }}>
        <h1 style={{ fontSize: 30 }}>Sales pipeline</h1>
        <p style={{ marginTop: 6, fontSize: 15, color: 'var(--text-muted)' }}>Drag a card between columns to move a lead through your stages — or click it to open the full record. Changes save instantly.</p>
      </div>
      <div style={{ display: 'flex', gap: 14, overflowX: 'auto', paddingBottom: 10, alignItems: 'stretch', height: 'calc(100vh - 232px)' }}>
        {stages.map(stage => {
          const items = cols[stage];
          const total = items.reduce((s, l) => s + (l.value || 0), 0);
          const accent = QF_STAGE_ACCENT[stage] || 'var(--slate-400)';
          const isOver = overStage === stage && dragLead && dragLead.stage !== stage;
          return (
            <div key={stage}
              onDragOver={e => { e.preventDefault(); if (overStage !== stage) setOverStage(stage); }}
              onDragLeave={e => { if (overStage === stage && !e.currentTarget.contains(e.relatedTarget)) setOverStage(null); }}
              onDrop={e => { e.preventDefault(); drop(stage, e); }}
              style={{ width: 280, flex: 'none', display: 'flex', flexDirection: 'column', borderRadius: 'var(--radius-lg)', maxHeight: '100%',
                background: isOver ? 'var(--primary-soft)' : 'var(--surface-sunken)',
                outline: isOver ? '2px dashed var(--primary)' : '2px solid transparent', outlineOffset: -2,
                transition: 'background var(--dur-fast), outline-color var(--dur-fast)' }}>
              <div style={{ display: 'flex', alignItems: 'center', gap: 9, padding: '13px 14px 10px', flex: 'none' }}>
                <span style={{ width: 9, height: 9, borderRadius: '50%', background: accent, flex: 'none' }} />
                <span style={{ fontFamily: 'var(--font-sans)', fontWeight: 700, fontSize: 13.5, color: 'var(--text-strong)' }}>{stage}</span>
                <span style={{ fontSize: 12, fontWeight: 700, color: 'var(--text-faint)', background: 'var(--surface-card)', borderRadius: 'var(--radius-full)', padding: '1px 8px' }}>{items.length}</span>
                <span style={{ marginLeft: 'auto', fontSize: 11.5, fontWeight: 600, color: 'var(--text-muted)' }}>{total ? gbp(total) : ''}</span>
              </div>
              <div style={{ display: 'flex', flexDirection: 'column', gap: 9, padding: '0 10px 6px', overflowY: 'auto', flex: 1 }}>
                {items.map(l => (
                  <div key={l.id} draggable
                    onDragStart={e => { setDragId(l.id); e.dataTransfer.effectAllowed = 'move'; try { e.dataTransfer.setData('text/plain', l.id); } catch (err) {} }}
                    onDragEnd={() => { setDragId(null); setOverStage(null); }}
                    onClick={() => onOpen(l.id)}
                    style={{ background: 'var(--surface-card)', borderRadius: 'var(--radius-md)', border: '1px solid var(--border-default)', borderTop: '3px solid ' + accent, boxShadow: 'var(--shadow-xs)', padding: 12, cursor: 'grab', display: 'flex', flexDirection: 'column', gap: 9, opacity: dragId === l.id ? 0.4 : 1 }}>
                    <div style={{ display: 'flex', alignItems: 'center', gap: 9 }}>
                      <CatTile cat={l.cat} size={30} />
                      <div style={{ lineHeight: 1.25, overflow: 'hidden', flex: 1 }}>
                        <div style={{ fontSize: 13.5, fontWeight: 600, color: 'var(--text-strong)', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{l.name}</div>
                        <div style={{ fontSize: 12, color: 'var(--text-muted)', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{l.system}</div>
                      </div>
                      <Icon name="grip-vertical" size={15} color="var(--slate-300)" />
                    </div>
                    <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
                      <span style={{ fontFamily: 'var(--font-display)', fontWeight: 700, fontSize: 15, color: 'var(--text-strong)' }}>{gbp(l.value)}</span>
                      <span style={{ display: 'flex', alignItems: 'center', gap: 3, fontSize: 11, color: 'var(--text-faint)' }}><Icon name="map-pin" size={11} color="var(--text-faint)" />{l.loc}</span>
                    </div>
                    <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', borderTop: '1px solid var(--slate-100)', paddingTop: 8 }}>
                      <span style={{ display: 'flex', alignItems: 'center', gap: 6, fontSize: 11.5, color: 'var(--text-muted)' }}>
                        <Avatar name={l.owner === 'Unassigned' ? '? ?' : l.owner} size={20} />
                        {l.owner === 'Unassigned' ? 'Unassigned' : l.owner.split(' ')[0]}
                      </span>
                      {l.proposal && <Icon name={l.proposal.payment ? 'badge-check' : 'file-text'} size={14} color={l.proposal.payment ? 'var(--accent-deep)' : 'var(--text-faint)'} />}
                    </div>
                  </div>
                ))}
                {items.length === 0 && <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', minHeight: 64, fontSize: 12, color: isOver ? 'var(--primary)' : 'var(--text-faint)', border: '1.5px dashed ' + (isOver ? 'var(--primary)' : 'var(--border-strong)'), borderRadius: 'var(--radius-md)' }}>{isOver ? 'Release to move here' : 'Drop leads here'}</div>}
              </div>
            </div>
          );
        })}
      </div>
    </div>
  );
}
window.PipelineBoard = PipelineBoard;
