// crm-proposal-catalog.jsx — Catalog-driven Proposal Generator
const { useState, useEffect } = React;

const fmt$p = v => isNaN(v) || v == null ? '$0.00' : '$' + Number(v).toLocaleString('en-US', { minimumFractionDigits:2, maximumFractionDigits:2 });

function propLineExt(line) {
  return (parseFloat(line.quantity) || 0) * (parseFloat(line.unitPrice) || 0);
}

function propCalcTotals(lines) {
  const recurring  = lines.filter(l => l.billingType && l.billingType !== 'One Time' && l.billingType !== 'Other');
  const oneTime    = lines.filter(l => !l.billingType || l.billingType === 'One Time' || l.billingType === 'Other');
  const monthly    = recurring.reduce((s, l) => s + propLineExt(l), 0);
  const oneTimeFees= oneTime.reduce((s, l) => s + propLineExt(l), 0);
  return { monthly, oneTimeFees, deposit: monthly * 2 + oneTimeFees };
}

// Excel-serial + HHMM order number — same formula as order forms
function toPropNumber(createdAt) {
  const d   = createdAt ? new Date(createdAt) : new Date();
  const pad = n => String(n).padStart(2, '0');
  return `${Math.floor(d.getTime() / 86400000 + 25569)}${pad(d.getHours())}${pad(d.getMinutes())}`;
}

// ══════════════════════════════════════════════════════════
// (Legacy ProposalCatalogAdmin removed — hardware/products now
//  live in Settings → Field Options → Hardware / Products.)
// ══════════════════════════════════════════════════════════
function _ProposalCatalogAdmin_RETIRED({ showToast }) {
  return null;
}


// ══════════════════════════════════════════════════════════
// ACCOUNT PROPOSALS TAB
// ══════════════════════════════════════════════════════════
function AccountProposalsTab({ account, user, showToast, onChanged }) {
  return <ProposalsTab target={{ kind:'account', id: account.id, companyName: account.companyName,
    industry: account.industry, employeeCount: account.employeeCount }}
    user={user} showToast={showToast} onChanged={onChanged} />;
}

function LeadProposalsTab({ lead, user, showToast, onChanged }) {
  return <ProposalsTab target={{ kind:'lead', id: lead.id, companyName: lead.companyName,
    industry: lead.industry, employeeCount: lead.employeeCount,
    contactName: lead.contactName, contactTitle: lead.contactTitle,
    contactEmail: lead.contactEmail, contactPhone: lead.contactPhone }}
    user={user} showToast={showToast} onChanged={onChanged} />;
}

function ProposalsTab({ target, user, showToast, onChanged }) {
  const [proposals, setProposals] = useState([]);
  const [loading,   setLoading]   = useState(true);
  const [view,      setView]      = useState(null);   // null | 'new' | { mode:'preview', form }
  const [deleting,  setDeleting]  = useState(null);

  const fetchList = () => target.kind === 'lead'
    ? CRM.ProposalFormsAPI.getByLead(target.id)
    : CRM.ProposalFormsAPI.getByAccount(target.id);

  const load = async () => {
    setLoading(true);
    try { setProposals(await fetchList()); }
    catch (e) { showToast('Failed to load proposals.', 'error'); }
    finally { setLoading(false); }
  };

  useEffect(() => { load(); }, []);

  const openReprint = async p => {
    try {
      const full = await CRM.ProposalFormsAPI.getOne(p.id);
      setView({ mode:'preview', form: full });
    } catch (e) { showToast('Failed to load proposal: ' + e.message, 'error'); }
  };

  // "Edit & Save as New": open the intake prefilled from an existing
  // proposal; saving creates a NEW proposal (the original is untouched).
  const openEditAsNew = async p => {
    try {
      const full = await CRM.ProposalFormsAPI.getOne(p.id);
      setView({ mode:'edit', form: full });
    } catch (e) { showToast('Failed to load proposal: ' + e.message, 'error'); }
  };

  const doDelete = async p => {
    if (!confirm('Delete this proposal? This cannot be undone.')) return;
    setDeleting(p.id);
    try {
      await CRM.ProposalFormsAPI.delete(p.id);
      showToast('Proposal deleted.', 'success');
      await load();
      onChanged?.();
    } catch (e) { showToast('Delete failed: ' + e.message, 'error'); }
    finally { setDeleting(null); }
  };

  if (view === 'new' || view?.mode === 'edit') {
    return <ProposalIntake target={target} user={user}
      initial={view?.mode === 'edit' ? view.form : undefined}
      onSaved={form => { load(); onChanged?.(); setView({ mode:'preview', form }); }}
      onCancel={() => setView(null)} showToast={showToast} />;
  }
  if (view?.mode === 'preview') {
    return <ProposalPreview form={view.form} target={target}
      onBack={() => setView(null)}
      onNewProposal={() => setView('new')}
      showToast={showToast} />;
  }

  const th = { padding:'8px 12px', textAlign:'left', fontSize:11, fontWeight:700, color:'var(--g600)', textTransform:'uppercase', letterSpacing:'0.05em', background:'var(--off)', borderBottom:'1px solid var(--g200)' };
  const td = { padding:'8px 12px', fontSize:13, borderBottom:'1px solid var(--g100)', verticalAlign:'middle' };

  return (
    <div>
      <div style={{ display:'flex', justifyContent:'space-between', alignItems:'center', marginBottom:16 }}>
        <div style={{ fontSize:13, color:'var(--g600)' }}>{proposals.length} proposal{proposals.length !== 1 ? 's' : ''}</div>
        <button className="btn btn-primary btn-sm" onClick={() => setView('new')}>+ New Proposal</button>
      </div>

      {loading ? <div style={{ padding:32, textAlign:'center', color:'var(--g400)' }}>Loading…</div> : (
        proposals.length === 0
          ? <div className="empty"><div className="empty-icon">📄</div><div className="empty-text">No proposals yet. Click "+ New Proposal" to create one.</div></div>
          : <div className="card" style={{ overflow:'hidden' }}>
              <table style={{ width:'100%', borderCollapse:'collapse' }}>
                <thead><tr>
                  <th style={th}>Proposal #</th>
                  <th style={th}>Date</th>
                  <th style={th}>Valid Until</th>
                  <th style={th}>Contact</th>
                  <th style={{ ...th, textAlign:'center' }}>Items</th>
                  <th style={th}>Created By</th>
                  <th style={th}></th>
                </tr></thead>
                <tbody>
                  {proposals.map(p => (
                    <tr key={p.id}>
                      <td style={{ ...td, fontFamily:'monospace', fontWeight:600, color:'var(--navy)' }}>{toPropNumber(p.createdAt)}</td>
                      <td style={td}>{p.proposalDate ? new Date(p.proposalDate + 'T00:00:00').toLocaleDateString() : '—'}</td>
                      <td style={td}>{p.validUntil   ? new Date(p.validUntil   + 'T00:00:00').toLocaleDateString() : '—'}</td>
                      <td style={{ ...td, color:'var(--navy)', fontWeight:500 }}>
                        {p.contactName || '—'}
                        {p.acceptedAt && <span className="badge badge-green" style={{ marginLeft:8 }}>✓ Accepted</span>}
                      </td>
                      <td style={{ ...td, textAlign:'center', color:'var(--g400)' }}>{p.lineCount}</td>
                      <td style={{ ...td, color:'var(--g400)' }}>{p.createdByName || '—'}</td>
                      <td style={{ ...td, textAlign:'right' }}>
                        <div style={{ display:'flex', gap:5, justifyContent:'flex-end' }}>
                          <button className="btn btn-ghost btn-xs" onClick={() => openReprint(p)}>🖨 Print</button>
                          <button className="btn btn-ghost btn-xs" title="Open a copy of this proposal for editing — saves as a new proposal"
                            onClick={() => openEditAsNew(p)}>✏ Edit as New</button>
                          <button className="btn btn-xs" disabled={deleting === p.id}
                            style={{ background:'#FEF2F2', color:'#991B1B', border:'1px solid #FECACA' }}
                            onClick={() => doDelete(p)}>
                            {deleting === p.id ? '…' : '🗑'}
                          </button>
                        </div>
                      </td>
                    </tr>
                  ))}
                </tbody>
              </table>
            </div>
      )}
    </div>
  );
}

// ══════════════════════════════════════════════════════════
// PROPOSAL INTAKE
// Hardware items from the catalog; each line can be purchased
// or rented. Price auto-fills but remains editable.
// ══════════════════════════════════════════════════════════
// `initial` (optional): a saved proposal form — prefills everything for the
// "Edit & Save as New" flow. Saving ALWAYS creates a new proposal; the
// original stays untouched.
function ProposalIntake({ target, user, onSaved, onCancel, showToast, initial }) {
  const today   = new Date().toISOString().slice(0, 10);
  const validDt = new Date(); validDt.setDate(validDt.getDate() + 30);
  const validDefault = validDt.toISOString().slice(0, 10);

  const [header, setHeader] = useState({
    // Dates intentionally reset — a copied proposal is a fresh offer
    proposalDate:  today,
    validUntil:    validDefault,
    repName:       initial?.repName || user.name || '',
    industry:      initial?.industry      ?? (target.industry      || ''),
    employeeCount: initial?.employeeCount ?? (target.employeeCount || ''),
    notes:         initial?.notes || '',
  });

  // Setup & Configuration line items shown on the Implementation
  // & Terms page. Editable per-proposal. The fee is free-form text
  // so users can put "$500", "Included", "Upon Request", etc.
  const DEFAULT_SETUP_ITEMS = [
    { name:'System Setup & Configuration',          description:'Full account setup, system configuration, and initial data load', fee:'$500' },
    { name:'Extended Instructor-Led Online Training', description:'Live onboarding session covering all platform features',         fee:'Included' },
    { name:'On-Site Hardware Installation',          description:'Available upon request — rates vary by location',                 fee:'Upon Request' },
  ];
  const [setupItems, setSetupItems] = useState(
    Array.isArray(initial?.setupItems) && initial.setupItems.length
      ? initial.setupItems.map(it => ({ name: it.name || '', description: it.description || '', fee: it.fee || '' }))
      : DEFAULT_SETUP_ITEMS);
  const updateSetupItem = (idx, k, v) =>
    setSetupItems(arr => arr.map((it, i) => i === idx ? { ...it, [k]: v } : it));
  const addSetupItem    = () => setSetupItems(arr => [...arr, { name:'', description:'', fee:'' }]);
  const removeSetupItem = idx => setSetupItems(arr => arr.filter((_, i) => i !== idx));

  const [contact,    setContact]    = useState({
    name:  initial?.contactName  || '',
    title: initial?.contactTitle || '',
    phone: initial?.contactPhone || '',
    email: initial?.contactEmail || '',
  });
  const [contacts,   setContacts]   = useState([]);
  const [contactSel, setContactSel] = useState('');
  const [catalog,    setCatalog]    = useState([]);
  // Map<catalogItemId, { selected, priceType, quantity }> — checkbox-driven selection
  const [picks,      setPicks]      = useState({});
  const [saving,     setSaving]     = useState(false);

  // Named proposal templates (Settings → Proposal Template)
  const [templates,   setTemplates]   = useState([]);
  const [templateKey, setTemplateKey] = useState(initial?.templateKey || '');

  // Per-proposal subscription tier override (null = use template tiers)
  const [customTiers, setCustomTiers] = useState(
    Array.isArray(initial?.pricingTiers) && initial.pricingTiers.length
      ? initial.pricingTiers.map(t => ({ match: t.match || '', label: t.label || '', model: t.model || '', cost: t.cost || '' }))
      : null);
  const selectedTpl = () =>
    (templates.find(t => t.key === templateKey) || templates[0])?.tpl || mergeTemplate(null);

  // Default a hardware option's pick mode based on which prices it has
  const defaultPriceType = h =>
    h.purchasePrice != null ? 'purchase'
    : h.rentalPrice != null ? 'rental'
    : 'purchase';

  useEffect(() => {
    // Hardware/products now come from Field Options (HW_OPTIONS).
    // Each entry may carry rich proposal fields (badge, prices,
    // specs, etc.); items without those fields are still pickable
    // but won't auto-fill a price.
    const cat = (CRM.HW_OPTIONS || []).map(h => ({
      id:            h.id,
      name:          h.name,
      badge:         h.badge         || null,
      badgeType:     h.badgeType     || 'standard',
      imagePath:     h.imagePath     || null,
      purchasePrice: h.purchasePrice != null ? h.purchasePrice : null,
      rentalPrice:   h.rentalPrice   != null ? h.rentalPrice   : null,
      rentalUnit:    h.rentalUnit    || null,
      specs:         Array.isArray(h.specs) ? h.specs : [],
    }));
    setCatalog(cat);

    // Duplicate flow: rebuild picks (incl. per-proposal overrides) from
    // the source proposal's saved lines.
    if (initial && Array.isArray(initial.lines)) {
      const pre = {};
      let missing = 0;
      initial.lines.forEach(l => {
        const h = cat.find(c => c.id === l.catalogItemId);
        if (!h) { missing++; return; }
        pre[h.id] = {
          selected:      true,
          priceType:     l.billingType && l.billingType !== 'One Time' ? 'rental' : 'purchase',
          quantity:      l.quantity || 1,
          name:          l.name || h.name,
          specsText:     (Array.isArray(l.specs) ? l.specs : h.specs || []).join('\n'),
          purchasePrice: l.purchasePrice != null ? String(l.purchasePrice) : (h.purchasePrice != null ? String(h.purchasePrice) : ''),
          rentalPrice:   l.rentalPrice   != null ? String(l.rentalPrice)   : (h.rentalPrice   != null ? String(h.rentalPrice)   : ''),
        };
      });
      setPicks(pre);
      if (missing > 0) showToast(`${missing} item(s) from the original proposal are no longer in the catalog and were skipped.`, 'default');
    }

    // Named templates for the picker
    CRM.ConfigAPI.getAll().then(cfg => {
      const list = loadProposalTemplates(cfg);
      setTemplates(list);
      setTemplateKey(k => k && list.some(t => t.key === k) ? k : list[0].key);
    }).catch(() => setTemplates([{ key:'default', name:'Standard', tpl: mergeTemplate(null) }]));

    if (target.kind === 'lead') {
      // Leads have a single contact stored on the lead itself.
      setContacts([]);
      if (!initial) setContact({
        name:  target.contactName  || '',
        title: target.contactTitle || '',
        phone: target.contactPhone || '',
        email: target.contactEmail || '',
      });
    } else {
      CRM.ContactsAPI.getByAccount(target.id).then(list => {
        setContacts(list);
        if (initial) return;  // keep the duplicated proposal's contact
        const primary = list.find(c => c.isPrimary) || list.find(c => c.primary);
        if (primary) {
          setContactSel(primary.id);
          setContact({ name: primary.name || '', title: primary.title || '', phone: primary.phone || '', email: primary.email || '' });
        }
      }).catch(() => {});
    }
  }, []);

  const setH = (k, v) => setHeader(h => ({ ...h, [k]: v }));
  const setC = (k, v) => setContact(c => ({ ...c, [k]: v }));

  // Catalog price for a given pick mode
  const catalogPrice = (h, priceType) =>
    priceType === 'rental' ? (h.rentalPrice ?? 0) : (h.purchasePrice ?? 0);

  // Toggle a hardware option's checkbox
  const togglePick = (h) => setPicks(p => {
    const cur = p[h.id];
    if (cur?.selected) {
      // Uncheck → keep state for visual "remember last choice"
      return { ...p, [h.id]: { ...cur, selected: false } };
    }
    const priceType = cur?.priceType || defaultPriceType(h);
    return { ...p, [h.id]: {
      selected: true,
      priceType,
      quantity:  cur?.quantity || 1,
      // Per-proposal overrides, editable like Setup & Config below.
      // Prefilled from the catalog; edits only affect this proposal.
      name:          cur?.name          ?? h.name,
      specsText:     cur?.specsText     ?? (h.specs || []).join('\n'),
      purchasePrice: cur?.purchasePrice ?? (h.purchasePrice != null ? String(h.purchasePrice) : ''),
      rentalPrice:   cur?.rentalPrice   ?? (h.rentalPrice   != null ? String(h.rentalPrice)   : ''),
    } };
  });

  // Update a pick's price-type, quantity, or overrides
  const updatePick = (id, k, v) => setPicks(p => ({
    ...p, [id]: { ...(p[id] || { selected:true, quantity:1 }), [k]: v },
  }));

  const setPriceType = (h, priceType) => updatePick(h.id, 'priceType', priceType);

  // Effective per-proposal price for a mode: override if entered, else catalog
  const pickPrice = (h, p, mode) => {
    const ov = mode === 'rental' ? p.rentalPrice : p.purchasePrice;
    return ov != null && ov !== '' ? (parseFloat(ov) || 0) : catalogPrice(h, mode);
  };

  const sectionHd = label => (
    <div style={{ fontSize:11, fontWeight:700, color:'var(--g600)', textTransform:'uppercase', letterSpacing:'0.06em', marginBottom:8 }}>{label}</div>
  );

  const save = async () => {
    // Build one line per checked hardware option, in catalog order
    const selectedLines = catalog
      .filter(h => picks[h.id]?.selected)
      .map((h, i) => {
        const p = picks[h.id];
        const isRental = p.priceType === 'rental';
        // Per-proposal overrides (name / specs / prices) win over the catalog.
        // Overridden purchase/rental prices are snapshotted so the proposal's
        // "or $X purchase / rental" alternate display uses them too.
        const specs = (p.specsText != null ? p.specsText.split('\n') : (h.specs || []))
          .map(s => s.trim()).filter(Boolean);
        return {
          catalogItemId: h.id,
          name:          (p.name || '').trim() || h.name,
          badge:         h.badge       || null,
          badgeType:     h.badgeType   || null,
          imagePath:     h.imagePath   || null,
          purchasePrice: h.purchasePrice != null ? pickPrice(h, p, 'purchase') : null,
          rentalPrice:   h.rentalPrice   != null ? pickPrice(h, p, 'rental')   : null,
          rentalUnit:    h.rentalUnit  || null,
          specs:         specs.length ? JSON.stringify(specs) : null,
          billingType:   isRental ? 'Monthly' : 'One Time',
          quantity:      parseFloat(p.quantity) || 1,
          unitPrice:     pickPrice(h, p, p.priceType),
          sortOrder:     i,
        };
      });

    if (selectedLines.length === 0)
      return showToast('Select at least one hardware option.', 'error');

    setSaving(true);
    try {
      const payload = {
        proposalDate: header.proposalDate || null,
        validUntil:   header.validUntil   || null,
        contactName:  contact.name  || null,
        contactEmail: contact.email || null,
        contactPhone: contact.phone || null,
        contactTitle: contact.title || null,
        repName:      header.repName || null,
        industry:      header.industry      || null,
        employeeCount: header.employeeCount || null,
        notes:        header.notes  || null,
        templateKey:  templateKey || null,
        pricingTiers: customTiers && customTiers.some(t => (t.label || '').trim())
          ? JSON.stringify(customTiers.filter(t => (t.label || '').trim()))
          : null,
        lines:        selectedLines,
        setupItems:   JSON.stringify(
                        setupItems
                          .map(it => ({
                            name:        (it.name || '').trim(),
                            description: (it.description || '').trim(),
                            fee:         (it.fee || '').trim(),
                          }))
                          .filter(it => it.name)
                      ),
      };
      const saved = target.kind === 'lead'
        ? await CRM.ProposalFormsAPI.saveForLead(target.id, payload)
        : await CRM.ProposalFormsAPI.save(target.id, payload);
      showToast('Proposal saved!', 'success');
      onSaved(saved);
    } catch (e) { showToast('Save failed: ' + e.message, 'error'); }
    finally { setSaving(false); }
  };

  return (
    <div>
      <div style={{ display:'flex', alignItems:'center', gap:12, marginBottom:20 }}>
        <button className="btn btn-ghost btn-sm" onClick={onCancel}>← Back</button>
        <h3 style={{ margin:0, fontSize:15, fontWeight:700, color:'var(--navy)' }}>
          {initial ? 'Edit Proposal (saves as new)' : 'New Proposal'} — {target.companyName}
        </h3>
      </div>

      {/* Header */}
      {sectionHd('Proposal Details')}
      <div className="card card-pad" style={{ marginBottom:16 }}>
        <div className="fg fg-3" style={{ marginBottom:10 }}>
          <div className="field"><label>Proposal Date</label>
            <input type="date" value={header.proposalDate} onChange={e => setH('proposalDate', e.target.value)} /></div>
          <div className="field"><label>Valid Until</label>
            <input type="date" value={header.validUntil} onChange={e => setH('validUntil', e.target.value)} /></div>
          <div className="field"><label>Sales Rep</label>
            <input value={header.repName} onChange={e => setH('repName', e.target.value)} /></div>
        </div>
        <div className="fg fg-2" style={{ marginBottom:10 }}>
          <div className="field">
            <label>Proposal Template <span style={{ fontWeight:400, color:'var(--g400)', fontSize:11 }}>(document wording — manage in Settings → Proposal Template)</span></label>
            <select value={templateKey} onChange={e => setTemplateKey(e.target.value)}>
              {templates.length === 0
                ? <option value="">Standard</option>
                : templates.map(t => <option key={t.key} value={t.key}>{t.name}</option>)}
            </select>
          </div>
        </div>
        <div className="fg fg-2" style={{ marginBottom:10 }}>
          <div className="field"><label>Industry</label>
            <select value={header.industry} onChange={e => setH('industry', e.target.value)}>
              <option value="">— Select —</option>
              {(CRM.INDUSTRIES || []).map(i => <option key={i} value={i}>{i}</option>)}
            </select>
          </div>
          <div className="field"><label>Employee Count</label>
            <select value={header.employeeCount} onChange={e => setH('employeeCount', e.target.value)}>
              <option value="">— Select —</option>
              {(CRM.EMPLOYEE_COUNTS || []).map(c => <option key={c} value={c}>{c}</option>)}
            </select>
          </div>
        </div>
        <div className="field">
          <label>Notes <span style={{ fontWeight:400, color:'var(--g400)', fontSize:11 }}>(optional — appears on proposal)</span></label>
          <textarea value={header.notes} onChange={e => setH('notes', e.target.value)} rows={2}
            style={{ width:'100%', resize:'vertical', padding:8, border:'1px solid var(--g200)', borderRadius:4, fontSize:13, fontFamily:'inherit' }} />
        </div>
      </div>

      {/* Contact */}
      {sectionHd('Contact')}
      <div className="card card-pad" style={{ marginBottom:16 }}>
        {contacts.length > 0 && (
          <div className="field" style={{ marginBottom:10 }}>
            <label>Select Contact</label>
            <select value={contactSel} onChange={e => {
              const id = e.target.value;
              setContactSel(id);
              if (!id) return;
              const c = contacts.find(x => x.id === id);
              if (c) setContact({ name: c.name || '', title: c.title || '', phone: c.phone || '', email: c.email || '' });
            }}>
              <option value="">— Enter manually —</option>
              {contacts.map(c => (
                <option key={c.id} value={c.id}>
                  {c.name}{c.title ? ` — ${c.title}` : ''}{c.primary || c.isPrimary ? ' ★' : ''}
                </option>
              ))}
            </select>
          </div>
        )}
        <div className="fg fg-2" style={{ marginBottom:10 }}>
          <div className="field"><label>Name</label>
            <input value={contact.name} onChange={e => { setContactSel(''); setC('name', e.target.value); }} /></div>
          <div className="field"><label>Title</label>
            <input value={contact.title} onChange={e => { setContactSel(''); setC('title', e.target.value); }} /></div>
        </div>
        <div style={{ display:'flex', gap:8 }}>
          <div className="field" style={{ width:160 }}><label>Phone</label>
            <input value={contact.phone} onChange={e => { setContactSel(''); setC('phone', e.target.value); }} /></div>
          <div className="field" style={{ flex:'1 1 0' }}><label>Email</label>
            <input type="email" value={contact.email} onChange={e => { setContactSel(''); setC('email', e.target.value); }} /></div>
        </div>
      </div>

      {/* Hardware / Product checklist */}
      {sectionHd('Hardware / Product Selection')}
      <div className="card card-pad" style={{ marginBottom:16 }}>
        {catalog.length === 0 ? (
          <div style={{ fontSize:13, color:'var(--g400)', padding:8 }}>
            No hardware options configured. Set them up in <strong>Settings → Field Options → Hardware / Products</strong>.
          </div>
        ) : (
          <div style={{ display:'flex', flexDirection:'column', gap:6 }}>
            {catalog.map(h => {
              const p = picks[h.id] || {};
              const checked = !!p.selected;
              const hasBoth = h.purchasePrice != null && h.rentalPrice != null;
              const onlyR   = h.purchasePrice == null && h.rentalPrice != null;
              const pt      = p.priceType || defaultPriceType(h);
              return (
                <div key={h.id} style={{
                  border:'1px solid ' + (checked ? 'var(--navy)' : 'var(--g200)'),
                  background: checked ? '#F4F8FF' : '#fff', borderRadius:6,
                }}>
                <div style={{ display:'flex', alignItems:'center', gap:12, padding:'10px 12px' }}>
                  <input type="checkbox" checked={checked} onChange={() => togglePick(h)}
                    style={{ width:18, height:18, accentColor:'var(--navy)', cursor:'pointer', flexShrink:0 }} />
                  <div style={{ flex:1, minWidth:0 }}>
                    <div style={{ display:'flex', alignItems:'center', gap:8, flexWrap:'wrap' }}>
                      <span style={{ fontSize:14, fontWeight:700, color:'var(--navy)' }}>{h.name}</span>
                      {h.badge && (
                        <span style={{
                          fontSize:10, fontWeight:700, letterSpacing:'.08em', textTransform:'uppercase',
                          padding:'2px 7px', borderRadius:3,
                          background: h.badgeType === 'free' ? '#1A7A4A' : '#BC141E',
                          color:'#fff',
                        }}>{h.badge}</span>
                      )}
                    </div>
                    <div style={{ fontSize:11, color:'var(--g400)', marginTop:3, fontFamily:'monospace' }}>
                      {h.purchasePrice != null && <>Buy {fmt$p(h.purchasePrice)}</>}
                      {hasBoth && <span style={{ margin:'0 6px', color:'var(--g300)' }}>•</span>}
                      {h.rentalPrice != null && <>Rent {fmt$p(h.rentalPrice)}{h.rentalUnit || '/mo'}</>}
                      {h.purchasePrice == null && h.rentalPrice == null && <span style={{ color:'var(--g300)' }}>No price set</span>}
                    </div>
                  </div>
                  {checked && (
                    <>
                      {hasBoth && (
                        <div style={{ display:'flex', borderRadius:4, overflow:'hidden', border:'1px solid var(--g200)', fontSize:11, flexShrink:0 }}>
                          <button type="button"
                            style={{ padding:'5px 10px', background: pt === 'purchase' ? 'var(--navy)' : '#fff', color: pt === 'purchase' ? '#fff' : 'var(--g600)', border:'none', cursor:'pointer', fontWeight:600 }}
                            onClick={() => setPriceType(h, 'purchase')}>Buy</button>
                          <button type="button"
                            style={{ padding:'5px 10px', background: pt === 'rental' ? 'var(--navy)' : '#fff', color: pt === 'rental' ? '#fff' : 'var(--g600)', border:'none', cursor:'pointer', borderLeft:'1px solid var(--g200)', fontWeight:600 }}
                            onClick={() => setPriceType(h, 'rental')}>Rent</button>
                        </div>
                      )}
                      {!hasBoth && (
                        <span style={{ fontSize:11, color:'var(--g400)', flexShrink:0 }}>
                          {onlyR ? 'Monthly' : 'One Time'}
                        </span>
                      )}
                      <div style={{ display:'flex', alignItems:'center', gap:4, flexShrink:0 }}>
                        <span style={{ fontSize:11, color:'var(--g400)' }}>Qty</span>
                        <input type="number" min="1" step="1"
                          value={p.quantity || 1}
                          onChange={e => updatePick(h.id, 'quantity', e.target.value)}
                          style={{ width:56, fontSize:12, padding:'4px 6px', border:'1.5px solid var(--g200)', borderRadius:4, textAlign:'right' }} />
                      </div>
                    </>
                  )}
                </div>
                {checked && (
                  <div style={{ padding:'0 12px 10px 42px', display:'flex', flexDirection:'column', gap:6 }}>
                    <div style={{ display:'flex', alignItems:'center', gap:8 }}>
                      <span style={{ fontSize:11, color:'var(--g400)', width:90, flexShrink:0 }}>Display name</span>
                      <input value={p.name ?? h.name}
                        onChange={e => updatePick(h.id, 'name', e.target.value)}
                        style={{ flex:1, fontSize:12, padding:'5px 7px', border:'1.5px solid var(--g200)', borderRadius:4 }} />
                    </div>
                    <div style={{ display:'flex', alignItems:'center', gap:8 }}>
                      <span style={{ fontSize:11, color:'var(--g400)', width:90, flexShrink:0 }}>Pricing</span>
                      {h.purchasePrice != null && (
                        <span style={{ display:'inline-flex', alignItems:'center', gap:4 }}>
                          <span style={{ fontSize:11, color:'var(--g400)' }}>Buy $</span>
                          <input type="number" min="0" step="0.01"
                            value={p.purchasePrice ?? ''}
                            onChange={e => updatePick(h.id, 'purchasePrice', e.target.value)}
                            style={{ width:90, fontSize:12, padding:'5px 7px', border:'1.5px solid var(--g200)', borderRadius:4, textAlign:'right' }} />
                        </span>
                      )}
                      {h.rentalPrice != null && (
                        <span style={{ display:'inline-flex', alignItems:'center', gap:4 }}>
                          <span style={{ fontSize:11, color:'var(--g400)' }}>Rent $</span>
                          <input type="number" min="0" step="0.01"
                            value={p.rentalPrice ?? ''}
                            onChange={e => updatePick(h.id, 'rentalPrice', e.target.value)}
                            style={{ width:90, fontSize:12, padding:'5px 7px', border:'1.5px solid var(--g200)', borderRadius:4, textAlign:'right' }} />
                          <span style={{ fontSize:11, color:'var(--g400)' }}>{h.rentalUnit || '/mo'}</span>
                        </span>
                      )}
                    </div>
                    <div style={{ display:'flex', alignItems:'flex-start', gap:8 }}>
                      <span style={{ fontSize:11, color:'var(--g400)', width:90, flexShrink:0, marginTop:5 }}>Description</span>
                      <textarea value={p.specsText ?? (h.specs || []).join('\n')}
                        onChange={e => updatePick(h.id, 'specsText', e.target.value)}
                        rows={Math.max(2, (p.specsText ?? (h.specs || []).join('\n')).split('\n').length)}
                        placeholder="One bullet point per line"
                        style={{ flex:1, fontSize:12, padding:'5px 7px', border:'1.5px solid var(--g200)', borderRadius:4, resize:'vertical', fontFamily:'inherit', lineHeight:1.5 }} />
                    </div>
                    <div style={{ fontSize:10, color:'var(--g400)', paddingLeft:98 }}>
                      Edits apply to this proposal only — the catalog item is unchanged.
                    </div>
                  </div>
                )}
                </div>
              );
            })}
          </div>
        )}
      </div>

      {/* Subscription pricing tiers (per-proposal override) */}
      {sectionHd('Subscription Pricing')}
      <div className="card card-pad" style={{ marginBottom:16 }}>
        <label style={{ display:'flex', alignItems:'center', gap:7, fontSize:13, cursor:'pointer' }}>
          <input type="checkbox" checked={!!customTiers}
            onChange={e => setCustomTiers(e.target.checked
              ? (selectedTpl().pricing.tiers || []).map(t => ({ ...t }))
              : null)} />
          Customize subscription tiers &amp; pricing for this proposal
        </label>
        {!customTiers && (
          <div style={{ fontSize:12, color:'var(--g400)', marginTop:6 }}>
            Using the "{(templates.find(t => t.key === templateKey) || templates[0])?.name || 'Standard'}" template's pricing table.
          </div>
        )}
        {customTiers && (
          <div style={{ marginTop:10 }}>
            <table style={{ width:'100%', borderCollapse:'collapse', fontSize:13 }}>
              <thead>
                <tr style={{ background:'var(--off)' }}>
                  <th style={{ textAlign:'left', padding:'6px 8px', color:'var(--g400)', fontWeight:600, width:110 }}>Highlight Match</th>
                  <th style={{ textAlign:'left', padding:'6px 8px', color:'var(--g400)', fontWeight:600 }}>Employee Count</th>
                  <th style={{ textAlign:'left', padding:'6px 8px', color:'var(--g400)', fontWeight:600 }}>Pricing Model</th>
                  <th style={{ textAlign:'right', padding:'6px 8px', color:'var(--g400)', fontWeight:600, width:130 }}>Monthly Cost</th>
                  <th style={{ width:28 }}></th>
                </tr>
              </thead>
              <tbody>
                {customTiers.map((t, idx) => (
                  <tr key={idx}>
                    {['match','label','model','cost'].map(f => (
                      <td key={f} style={{ padding:'4px 4px', verticalAlign:'top' }}>
                        <input style={{ width:'100%', fontSize:12, padding:'5px 7px', border:'1.5px solid var(--g200)', borderRadius:4, textAlign: f==='cost' ? 'right' : 'left' }}
                          value={t[f] || ''}
                          onChange={e => setCustomTiers(arr => arr.map((r, i) => i === idx ? { ...r, [f]: e.target.value } : r))}
                          placeholder={f==='match' ? 'e.g. 26-50' : ''} />
                      </td>
                    ))}
                    <td style={{ padding:'4px 4px', textAlign:'center', verticalAlign:'top' }}>
                      <button style={{ background:'none', border:'none', cursor:'pointer', color:'var(--g400)', fontSize:16, lineHeight:1, padding:2 }}
                        onClick={() => setCustomTiers(arr => arr.filter((_, i) => i !== idx))} title="Remove">×</button>
                    </td>
                  </tr>
                ))}
              </tbody>
            </table>
            <div style={{ display:'flex', alignItems:'center', gap:10, marginTop:8 }}>
              <button className="btn btn-ghost btn-sm" onClick={() => setCustomTiers(arr => [...arr, { match:'', label:'', model:'', cost:'' }])}>+ Add Tier</button>
              <span style={{ fontSize:11, color:'var(--g400)' }}>"Highlight Match" is compared to the proposal's Employee Count to highlight the matching row.</span>
            </div>
          </div>
        )}
      </div>

      {/* Setup & Configuration fees */}
      {sectionHd('Setup & Configuration')}
      <div className="card card-pad" style={{ marginBottom:16 }}>
        <div style={{ fontSize:12, color:'var(--g400)', marginBottom:10 }}>
          Items appear on the Implementation &amp; Terms page of the proposal. Fee is free-form (e.g. <em>$500</em>, <em>Included</em>, <em>Upon Request</em>).
        </div>
        <table style={{ width:'100%', borderCollapse:'collapse', fontSize:13 }}>
          <thead>
            <tr style={{ background:'var(--off)' }}>
              <th style={{ textAlign:'left', padding:'6px 8px', color:'var(--g400)', fontWeight:600 }}>Service</th>
              <th style={{ textAlign:'left', padding:'6px 8px', color:'var(--g400)', fontWeight:600 }}>Description</th>
              <th style={{ textAlign:'right', padding:'6px 8px', color:'var(--g400)', fontWeight:600, width:130 }}>Fee</th>
              <th style={{ width:28 }}></th>
            </tr>
          </thead>
          <tbody>
            {setupItems.map((it, idx) => (
              <tr key={idx}>
                <td style={{ padding:'4px 4px', verticalAlign:'top' }}>
                  <input style={{ width:'100%', fontSize:12, padding:'5px 7px', border:'1.5px solid var(--g200)', borderRadius:4 }}
                    value={it.name} onChange={e => updateSetupItem(idx, 'name', e.target.value)} placeholder="Service name" />
                </td>
                <td style={{ padding:'4px 4px', verticalAlign:'top' }}>
                  <input style={{ width:'100%', fontSize:12, padding:'5px 7px', border:'1.5px solid var(--g200)', borderRadius:4 }}
                    value={it.description} onChange={e => updateSetupItem(idx, 'description', e.target.value)} placeholder="Description" />
                </td>
                <td style={{ padding:'4px 4px', verticalAlign:'top' }}>
                  <input style={{ width:'100%', fontSize:12, padding:'5px 7px', border:'1.5px solid var(--g200)', borderRadius:4, textAlign:'right' }}
                    value={it.fee} onChange={e => updateSetupItem(idx, 'fee', e.target.value)} placeholder="$0" />
                </td>
                <td style={{ padding:'4px 4px', textAlign:'center', verticalAlign:'top' }}>
                  <button style={{ background:'none', border:'none', cursor:'pointer', color:'var(--g400)', fontSize:16, lineHeight:1, padding:2 }}
                    onClick={() => removeSetupItem(idx)} title="Remove">×</button>
                </td>
              </tr>
            ))}
          </tbody>
        </table>
        <button className="btn btn-ghost btn-sm" style={{ marginTop:10 }} onClick={addSetupItem}>+ Add Item</button>
      </div>

      {/* Actions */}
      <div style={{ display:'flex', gap:10 }}>
        <button className="btn btn-primary" onClick={save} disabled={saving}>{saving ? 'Saving…' : 'Save & Preview'}</button>
        <button className="btn btn-ghost" onClick={onCancel}>Cancel</button>
      </div>
    </div>
  );
}

// ══════════════════════════════════════════════════════════
// PROPOSAL PREVIEW — matches Sales Proposal Template.html
// 7-page multi-page document: Cover · Exec Summary ·
// Capabilities · Pricing · Hardware · Implementation · CTA
// ══════════════════════════════════════════════════════════

const PROP_CSS = `
/* ═══════════ Modern (Clean Lines) proposal theme ═══════════ */
.prop-tpl{font-family:'Inter','Helvetica Neue',Arial,sans-serif;color:#162534;-webkit-print-color-adjust:exact;print-color-adjust:exact;}
.prop-tpl .pt-page{width:816px;min-height:1056px;margin:0 auto 32px;background:#fff;position:relative;overflow:hidden;box-shadow:0 4px 32px rgba(0,0,0,.18);}
/* Cover (white, with thin red top bar + navy bottom bar) */
.prop-tpl .pt-cover{min-height:1056px;display:flex;flex-direction:column;justify-content:space-between;padding:0;background:#fff;}
.prop-tpl .pt-cover::before{content:'';position:absolute;top:0;left:0;right:0;height:6px;background:#BC141E;}
.prop-tpl .pt-cover-top{padding:96px 72px 0;flex:1;display:flex;flex-direction:column;justify-content:center;}
.prop-tpl .pt-cover-logo img{height:80px;display:block;}
.prop-tpl .pt-cover-rule{height:1px;background:#E2E6EC;margin:40px 0 48px;}
.prop-tpl .pt-cover-eyebrow{font-size:12px;font-weight:700;letter-spacing:.18em;text-transform:uppercase;color:#BC141E;margin-bottom:18px;}
.prop-tpl .pt-cover-title{font-size:60px;font-weight:800;line-height:1.05;color:#162534;margin-bottom:24px;letter-spacing:-.01em;}
.prop-tpl .pt-cover-subtitle{font-size:17px;font-weight:400;color:#5A6478;line-height:1.55;max-width:520px;margin-bottom:64px;}
.prop-tpl .pt-cover-client-label{font-size:11px;font-weight:700;letter-spacing:.18em;text-transform:uppercase;color:#9AA3B0;margin-bottom:10px;}
.prop-tpl .pt-cover-client-name{font-size:24px;font-weight:800;color:#162534;}
.prop-tpl .pt-cover-client-sub{font-size:14px;color:#5A6478;margin-top:6px;}
.prop-tpl .pt-cover-client-detail{font-size:12px;color:#9AA3B0;margin-top:2px;}
.prop-tpl .pt-cover-bottom{background:#162534;padding:26px 72px;display:flex;justify-content:space-between;align-items:center;}
.prop-tpl .pt-cover-bottom-left,.prop-tpl .pt-cover-bottom-right{font-size:13px;color:rgba(255,255,255,.7);}
/* Section header (white, with horizontal rule between number & title) */
.prop-tpl .pt-section-header{padding:64px 72px 0;display:flex;align-items:center;gap:24px;}
.prop-tpl .pt-sh-num{font-size:13px;font-weight:700;letter-spacing:.18em;color:#BC141E;text-transform:uppercase;flex-shrink:0;}
.prop-tpl .pt-sh-rule{flex:1;height:1px;background:#E2E6EC;}
.prop-tpl .pt-sh-title{font-size:28px;font-weight:800;color:#162534;flex-shrink:0;letter-spacing:-.01em;}
.prop-tpl .pt-content{padding:36px 72px 80px;}
.prop-tpl .pt-footer{position:absolute;bottom:0;left:0;right:0;padding:18px 72px;border-top:1px solid #E2E6EC;display:flex;justify-content:space-between;align-items:center;font-size:11px;color:#9AA3B0;background:#fff;}
/* Shared */
.prop-tpl .pt-lead{font-size:16px;font-weight:400;line-height:1.75;color:#5A6478;margin-bottom:32px;}
.prop-tpl .pt-lead strong{color:#162534;font-weight:600;}
.prop-tpl .pt-hbox{background:#F7F8FA;border-left:3px solid #BC141E;padding:20px 24px;border-radius:0 4px 4px 0;margin-bottom:32px;}
.prop-tpl .pt-hbox p{font-size:14px;line-height:1.7;color:#5A6478;}
.prop-tpl .pt-hbox strong{color:#162534;}
.prop-tpl .pt-stat-strip{display:flex;gap:0;margin-bottom:32px;}
.prop-tpl .pt-stat{flex:1;padding:20px;text-align:center;background:#162534;border-right:1px solid rgba(255,255,255,.1);}
.prop-tpl .pt-stat:last-child{border-right:none;}
.prop-tpl .pt-stat-num{font-size:28px;font-weight:800;color:#fff;line-height:1;margin-bottom:6px;}
.prop-tpl .pt-stat-num span{font-size:16px;color:#BC141E;}
.prop-tpl .pt-stat-label{font-size:11px;color:rgba(255,255,255,.5);text-transform:uppercase;letter-spacing:.08em;}
.prop-tpl .pt-feat-grid{display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-bottom:0;}
.prop-tpl .pt-feat{background:#F7F8FA;border-radius:4px;padding:20px 22px;}
.prop-tpl .pt-feat-title{font-size:13px;font-weight:700;color:#162534;margin-bottom:6px;display:flex;align-items:center;gap:8px;}
.prop-tpl .pt-feat-title::before{content:'';display:inline-block;width:8px;height:8px;border-radius:50%;background:#BC141E;flex-shrink:0;}
.prop-tpl .pt-feat p{font-size:12px;color:#5A6478;line-height:1.6;}
.prop-tpl .pt-cap-list{list-style:none;display:grid;grid-template-columns:1fr 1fr;gap:10px 24px;}
.prop-tpl .pt-cap-list li{font-size:13px;color:#2E3545;line-height:1.5;padding-left:16px;position:relative;}
.prop-tpl .pt-cap-list li::before{content:'—';position:absolute;left:0;color:#BC141E;font-weight:700;}
.prop-tpl .pt-sub{font-size:15px;font-weight:700;color:#162534;margin-bottom:12px;margin-top:28px;}
.prop-tpl .pt-sub:first-child{margin-top:0;}
.prop-tpl .pt-divider{border:none;border-top:1px solid #E2E6EC;margin:28px 0;}
/* Pricing table */
.prop-tpl .pt-table{width:100%;border-collapse:collapse;margin-bottom:32px;}
.prop-tpl .pt-table thead tr{background:#162534;}
.prop-tpl .pt-table thead th{text-align:left;padding:14px 18px;font-size:11px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;color:#fff;}
.prop-tpl .pt-table tbody tr:nth-child(even){background:#F7F8FA;}
.prop-tpl .pt-table tbody td{padding:14px 18px;font-size:13px;color:#2E3545;border-bottom:1px solid #E2E6EC;}
.prop-tpl .pt-table tbody td:last-child{text-align:right;font-weight:600;color:#162534;}
.prop-tpl .pt-price-note{font-size:11px;color:#9AA3B0;margin-top:8px;}
.prop-tpl .pt-table tbody tr.pt-row-selected td{position:relative;background:#FFF1F2;}
.prop-tpl .pt-table tbody tr.pt-row-selected td:first-child{box-shadow:inset 3px 0 0 #BC141E;}
.prop-tpl .pt-table tbody tr.pt-row-selected td{border-top:2px solid #BC141E;border-bottom:2px solid #BC141E;font-weight:700;color:#162534;}
.prop-tpl .pt-table tbody tr.pt-row-selected td:last-child{box-shadow:inset -3px 0 0 #BC141E;}
/* Hardware — compact 3-column grid (fits 6 cards on a single page) */
.prop-tpl .pt-hw-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:12px;}
.prop-tpl .pt-hw-card{border:1px solid #E2E6EC;border-radius:5px;overflow:hidden;background:#fff;}
.prop-tpl .pt-hw-header{background:#162534;padding:9px 12px;display:flex;justify-content:space-between;align-items:center;gap:6px;}
.prop-tpl .pt-hw-header h4{font-size:12px;font-weight:700;color:#fff;margin:0;line-height:1.25;}
.prop-tpl .pt-hw-badge{font-size:8px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;background:#BC141E;color:#fff;padding:2px 6px;border-radius:2px;white-space:nowrap;flex-shrink:0;}
.prop-tpl .pt-hw-badge.free{background:#1A7A4A;}
.prop-tpl .pt-hw-img{width:100%;height:110px;display:flex;align-items:center;justify-content:center;background:#F0F2F5;padding:8px;}
.prop-tpl .pt-hw-img img{max-height:94px;max-width:100%;object-fit:contain;}
.prop-tpl .pt-hw-body{padding:10px 12px;background:#fff;}
.prop-tpl .pt-hw-body ul{list-style:none;padding:0;margin:0;}
.prop-tpl .pt-hw-body ul li{font-size:10px;color:#5A6478;line-height:1.55;padding-left:10px;position:relative;}
.prop-tpl .pt-hw-body ul li::before{content:'·';position:absolute;left:0;color:#BC141E;font-size:16px;line-height:1;top:1px;}
.prop-tpl .pt-hw-footer{padding:9px 12px;background:#F7F8FA;border-top:1px solid #E2E6EC;display:flex;justify-content:space-between;align-items:flex-end;gap:8px;}
.prop-tpl .pt-hw-price-wrap{display:flex;flex-direction:column;}
.prop-tpl .pt-hw-price{font-size:16px;font-weight:800;color:#162534;line-height:1.1;}
.prop-tpl .pt-hw-rental{font-size:10px;color:#9AA3B0;margin-top:2px;}
.prop-tpl .pt-hw-qty{font-size:9px;color:#9AA3B0;text-transform:uppercase;letter-spacing:.08em;}
/* Setup table */
.prop-tpl .pt-setup-table{width:100%;border-collapse:collapse;margin-bottom:24px;}
.prop-tpl .pt-setup-table thead tr{background:#162534;}
.prop-tpl .pt-setup-table thead th{text-align:left;padding:12px 16px;font-size:11px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;color:#fff;}
.prop-tpl .pt-setup-table tbody tr:nth-child(even){background:#F7F8FA;}
.prop-tpl .pt-setup-table tbody td{padding:12px 16px;font-size:13px;color:#2E3545;border-bottom:1px solid #E2E6EC;}
.prop-tpl .pt-setup-table tbody td:last-child{font-weight:600;color:#162534;text-align:right;}
/* Terms */
.prop-tpl .pt-terms{list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:12px;}
.prop-tpl .pt-terms li{padding:14px 18px 14px 48px;position:relative;background:#F7F8FA;border-radius:4px;font-size:13px;color:#2E3545;line-height:1.6;}
.prop-tpl .pt-terms li::before{content:attr(data-n);position:absolute;left:16px;top:14px;font-size:11px;font-weight:700;color:#BC141E;}
/* Deposit summary */
.prop-tpl .pt-deposit{background:#F7F8FA;border:1px solid #E2E6EC;border-radius:4px;padding:16px 20px;margin-top:16px;max-width:380px;margin-left:auto;}
.prop-tpl .pt-deposit-row{display:flex;justify-content:space-between;font-size:13px;color:#5A6478;padding:3px 0;}
.prop-tpl .pt-deposit-total{display:flex;justify-content:space-between;align-items:baseline;font-size:15px;font-weight:800;color:#162534;border-top:2px solid #162534;margin-top:6px;padding-top:6px;}
/* CTA */
.prop-tpl .pt-cta{min-height:1056px;display:flex;flex-direction:column;justify-content:space-between;background:#fff;}
.prop-tpl .pt-cta-top{flex:1;display:flex;flex-direction:column;justify-content:center;padding:96px 72px 48px;}
.prop-tpl .pt-cta-eyebrow{font-size:12px;font-weight:700;letter-spacing:.18em;text-transform:uppercase;color:#BC141E;margin-bottom:24px;}
.prop-tpl .pt-cta-heading{font-size:48px;font-weight:800;line-height:1.05;color:#162534;margin-bottom:28px;letter-spacing:-.01em;}
.prop-tpl .pt-cta-body{font-size:16px;color:#5A6478;line-height:1.7;max-width:560px;margin-bottom:64px;}
.prop-tpl .pt-cta-contacts{display:grid;grid-template-columns:repeat(4,1fr);gap:32px;margin-bottom:32px;}
.prop-tpl .pt-cta-contact-label{font-size:11px;font-weight:700;letter-spacing:.18em;text-transform:uppercase;color:#9AA3B0;margin-bottom:8px;}
.prop-tpl .pt-cta-contact-value{font-size:16px;font-weight:700;color:#162534;}
.prop-tpl .pt-cta-contact-sub{font-size:13px;color:#5A6478;margin-top:4px;}
.prop-tpl .pt-cta-contact-detail{font-size:12px;color:#9AA3B0;margin-top:2px;}
.prop-tpl .pt-cta-divider{display:flex;height:3px;margin:8px 0 32px;}
.prop-tpl .pt-cta-divider-red{flex:0 0 25%;background:#BC141E;}
.prop-tpl .pt-cta-divider-grey{flex:1;background:#E2E6EC;}
.prop-tpl .pt-cta-steps{display:grid;grid-template-columns:repeat(3,1fr);gap:32px;}
.prop-tpl .pt-cta-step{padding:0;}
.prop-tpl .pt-cta-step-num{font-size:11px;font-weight:700;letter-spacing:.18em;color:#BC141E;text-transform:uppercase;margin-bottom:10px;}
.prop-tpl .pt-cta-step-title{font-size:16px;font-weight:800;color:#162534;margin-bottom:8px;}
.prop-tpl .pt-cta-step-desc{font-size:13px;color:#5A6478;line-height:1.6;}
.prop-tpl .pt-cta-bottom{background:#162534;margin:0 72px 56px;padding:24px 28px;display:flex;justify-content:space-between;align-items:center;border-radius:4px;}
.prop-tpl .pt-cta-bottom-brand{display:flex;align-items:center;gap:18px;}
.prop-tpl .pt-cta-bottom-brand img{height:40px;background:white;padding:6px 10px;border-radius:3px;}
.prop-tpl .pt-cta-tagline{font-size:13px;color:rgba(255,255,255,.7);}
.prop-tpl .pt-cta-validity{font-size:12px;color:rgba(255,255,255,.5);text-align:right;line-height:1.6;}
.prop-tpl .pt-pn{font-size:11px;color:#9AA3B0;}
/* Screenshot showcase */
.prop-tpl .pt-showcase{margin:24px 0 0;border-radius:6px;overflow:hidden;border:1px solid #E2E6EC;box-shadow:0 2px 12px rgba(0,0,0,.08);}
.prop-tpl .pt-showcase-label{background:#162534;padding:8px 16px;font-size:10px;font-weight:600;letter-spacing:.12em;text-transform:uppercase;color:rgba(255,255,255,.6);display:flex;align-items:center;gap:8px;}
.prop-tpl .pt-showcase-label::before{content:'';display:inline-block;width:8px;height:8px;border-radius:50%;background:#BC141E;}
.prop-tpl .pt-showcase img{width:100%;display:block;object-fit:cover;}
.prop-tpl .pt-sc-grid{display:grid;grid-template-columns:1fr 1fr 1fr;gap:12px;margin-top:24px;}
.prop-tpl .pt-sc-cell{border-radius:6px;overflow:hidden;border:1px solid #E2E6EC;}
.prop-tpl .pt-sc-cell-label{background:#162534;padding:6px 12px;font-size:10px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;color:rgba(255,255,255,.6);}
.prop-tpl .pt-sc-cell img{width:100%;display:block;object-fit:cover;height:120px;}
/* Print */
@media print {
  /* Visibility (not display) so the .prop-tpl subtree can print while the
     app shell + toolbars stay hidden — same pattern as the order form. */
  body * { visibility:hidden !important; }
  .prop-tpl, .prop-tpl * { visibility:visible !important; }
  #prop-print-bar-outer {
    position:absolute !important; top:0 !important; left:0 !important;
    right:auto !important; bottom:auto !important; width:100% !important;
    overflow:visible !important; padding:0 !important; background:white !important;
  }
  .prop-tpl { display:block !important; position:static !important; }
  .prop-tpl .pt-page {
    width:8.5in !important; height:11in !important; min-height:unset !important;
    margin:0 !important; box-shadow:none !important; overflow:hidden !important;
    break-after:page; page-break-after:always;
  }
  .prop-tpl .pt-page:last-child { break-after:avoid; page-break-after:avoid; }
  @page { size:8.5in 11in portrait; margin:0; }
  html,body { margin:0 !important; padding:0 !important; background:white !important; }
}
`;

function ProposalPreview({ form, target, onBack, onNewProposal, showToast, publicMode }) {
  const totals   = propCalcTotals(form.lines || []);
  const propNum  = toPropNumber(form.createdAt);
  const logoSrc  = CRM.companyLogo || 'uploads/logo-1776607031602.jpg';
  const client   = target?.companyName || form.companyName || 'Client';

  const fmtLong = d => d
    ? new Date(d + 'T00:00:00').toLocaleDateString('en-US', { year:'numeric', month:'long', day:'numeric' })
    : '';
  const propDateFmt = fmtLong(form.proposalDate);
  const validFmt    = fmtLong(form.validUntil);

  // Admin-editable template text (crm-proposal-template.jsx). Defaults
  // render immediately; the proposal's named template loads async.
  const [tpl, setTpl] = useState(() => mergeTemplate(null));
  useEffect(() => {
    CRM.ConfigAPI.getAll().then(cfg => {
      try { setTpl(resolveProposalTemplate(cfg, form.templateKey)); }
      catch (e) { setTpl(mergeTemplate(null)); }
    }).catch(() => {});
  }, [form.templateKey]);

  // Per-proposal subscription tier override beats the template's tiers
  const pricingTiers = Array.isArray(form.pricingTiers) && form.pricingTiers.length
    ? form.pricingTiers
    : null;

  // Token context for ptRender ({client}, {proposalDate}, …)
  const ctx = {
    client,
    industry:      form.industry,
    employeeCount: form.employeeCount,
    proposalDate:  propDateFmt,
    validUntil:    validFmt,
    repName:       form.repName || form.createdByName || '',
    clauses:       tpl.clauses,   // editable {industryClause}/{employeeClause} wording
  };

  const fmtMoney = v => v != null
    ? '$' + Number(v).toLocaleString('en-US', { minimumFractionDigits:0, maximumFractionDigits:0 })
    : null;

  useEffect(() => {
    const s = document.createElement('style');
    s.id = 'prop-tpl-css';
    s.textContent = PROP_CSS;
    document.head.appendChild(s);
    return () => { document.getElementById('prop-tpl-css')?.remove(); };
  }, []);

  const doPrint = () => { window.print(); };

  // Email the proposal: rasterize the on-screen pages to a PDF and open
  // the shared email modal with it pre-attached.
  const [emailFile, setEmailFile] = useState(null);
  const [pdfBusy,   setPdfBusy]   = useState(false);
  const doEmail = async () => {
    setPdfBusy(true);
    try {
      const safe = String(client).replace(/[^\w-]+/g, '_').slice(0, 40);
      setEmailFile(await captureDocumentPdf('.prop-tpl .pt-page', `Proposal-${propNum}-${safe}.pdf`));
    } catch (e) { showToast('Could not build the PDF: ' + e.message, 'error'); }
    finally { setPdfBusy(false); }
  };

  // Copy the customer-facing accept link (creates the token on first use)
  const doAcceptLink = async () => {
    try {
      const r = await CRM.ProposalFormsAPI.acceptLink(form.id);
      const url = window.location.origin + r.url;
      try { await navigator.clipboard.writeText(url); }
      catch (e) { prompt('Copy the link:', url); }
      showToast('Accept link copied to clipboard!', 'success');
    } catch (e) { showToast('Could not create accept link: ' + e.message, 'error'); }
  };

  const lines = form.lines || [];

  // ── Hardware card for each saved line ───────────────────
  function HwCard({ line }) {
    // Fallback: if the saved line is missing badge/image/specs (e.g.
    // it was created before the hardware option had rich content), pull
    // them from the live Field Options entry by catalogItemId.
    const live = (line.catalogItemId && Array.isArray(CRM.HW_OPTIONS))
      ? CRM.HW_OPTIONS.find(h => h.id === line.catalogItemId)
      : null;
    const badge       = line.badge       || live?.badge       || null;
    const badgeType   = line.badgeType   || live?.badgeType   || 'standard';
    const imagePath   = line.imagePath   || live?.imagePath   || null;
    const rentalUnit  = line.rentalUnit  || live?.rentalUnit  || '/mo';
    const purchase    = line.purchasePrice != null ? line.purchasePrice : (live?.purchasePrice ?? null);
    const rentalP     = line.rentalPrice   != null ? line.rentalPrice   : (live?.rentalPrice   ?? null);
    const specsArr    = Array.isArray(line.specs) && line.specs.length
                          ? line.specs
                          : (Array.isArray(live?.specs) ? live.specs : []);

    const isRental  = line.billingType && line.billingType !== 'One Time';
    const isFree    = badgeType === 'free';
    const qty       = parseFloat(line.quantity) || 1;

    // Primary price display
    let priceDisplay, rentalDisplay;
    if (isFree) {
      // Free / Included items always show an explicit $0 price.
      priceDisplay  = isRental ? `$0${rentalUnit}` : '$0';
      rentalDisplay = null;
    } else if (isRental) {
      priceDisplay  = `${fmtMoney(line.unitPrice)}${rentalUnit}`;
      rentalDisplay = purchase ? `or ${fmtMoney(purchase)} purchase` : null;
    } else {
      const p = line.unitPrice != null && line.unitPrice > 0 ? line.unitPrice : purchase;
      priceDisplay  = p != null && p > 0 ? fmtMoney(p) : 'Included';
      rentalDisplay = rentalP ? `or ${fmtMoney(rentalP)}${rentalUnit} rental` : null;
    }

    // Free items always show a green pill; default the label to
    // "Included" if no badge text was entered on the Field Option.
    const badgeText = badge || (isFree ? 'Included' : null);

    return (
      <div className="pt-hw-card">
        <div className="pt-hw-header">
          <h4>{line.name}</h4>
          {badgeText && <span className={`pt-hw-badge${isFree ? ' free' : ''}`}>{badgeText}</span>}
        </div>
        {imagePath && (
          <div className="pt-hw-img">
            <img src={imagePath} alt={line.name} onError={e => { e.target.style.display='none'; }} />
          </div>
        )}
        {specsArr.length > 0 && (
          <div className="pt-hw-body">
            <ul>{specsArr.map((s, i) => <li key={i} dangerouslySetInnerHTML={{ __html: String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;') }} />)}</ul>
          </div>
        )}
        <div className="pt-hw-footer">
          <div className="pt-hw-price-wrap">
            <div className="pt-hw-price">{priceDisplay}</div>
            {rentalDisplay && <div className="pt-hw-rental">{rentalDisplay}</div>}
          </div>
          {qty > 1 && <div className="pt-hw-qty">Qty: {qty}</div>}
        </div>
      </div>
    );
  }

  const footerLine = `CTR/NY · Attendance on Demand Proposal · ${client}`;

  return (
    <div id="prop-print-bar-outer" style={{ position:'fixed', inset:0, background:'#D8DCE4', zIndex:9000, overflowY:'auto', paddingBottom:80 }}>

      {/* Print bar */}
      <div style={{ position:'fixed', bottom:0, left:0, right:0, background:'#162534', padding:'12px 24px', display:'flex', justifyContent:'space-between', alignItems:'center', zIndex:9001 }}>
        <div style={{ display:'flex', gap:10 }}>
          {!publicMode && <button className="btn" style={{ background:'transparent', color:'#fff', border:'1px solid rgba(255,255,255,0.5)' }} onClick={onBack}>← Back to Proposals</button>}
          {!publicMode && onNewProposal && <button className="btn" style={{ background:'transparent', color:'#fff', border:'1px solid rgba(255,255,255,0.5)' }} onClick={onNewProposal}>+ New Proposal</button>}
        </div>
        <div style={{ display:'flex', gap:10, alignItems:'center' }}>
          {!publicMode && <button className="btn" style={{ background:'transparent', color:'#fff', border:'1px solid rgba(255,255,255,0.5)', fontSize:15, padding:'10px 22px' }}
            onClick={doEmail} disabled={pdfBusy}>{pdfBusy ? 'Preparing PDF…' : '✉ Email'}</button>}
          {!publicMode && (form.acceptedAt
            ? <span style={{ background:'#16A34A', color:'#fff', fontSize:13, fontWeight:700, padding:'10px 18px', borderRadius:20, whiteSpace:'nowrap' }}>✓ Accepted by {form.acceptedByName}</span>
            : <button className="btn" style={{ background:'transparent', color:'#fff', border:'1px solid rgba(255,255,255,0.5)', fontSize:15, padding:'10px 22px' }}
                onClick={doAcceptLink}>🔗 Accept Link</button>)}
          <button className="btn btn-primary" style={{ background:'#E8F2FF', color:'#162534', fontSize:15, padding:'10px 28px' }} onClick={doPrint}>🖨 Print / Save PDF</button>
        </div>
      </div>

      <div className="prop-tpl">

        {/* ═══════════════ PAGE 1 — COVER ═══════════════ */}
        <div className="pt-page">
          {form.acceptedAt && (
            <div style={{ position:'absolute', top:120, right:40, transform:'rotate(8deg)', border:'3px solid #16A34A',
              color:'#16A34A', fontWeight:800, fontSize:22, padding:'8px 18px', borderRadius:6,
              textTransform:'uppercase', letterSpacing:'.08em', background:'rgba(255,255,255,.85)', textAlign:'center', zIndex:5 }}>
              Accepted — {form.acceptedByName}
              <div style={{ fontSize:13, fontWeight:700, letterSpacing:'.05em', marginTop:2 }}>{new Date(form.acceptedAt).toLocaleDateString()}</div>
            </div>
          )}
          <div className="pt-cover">
            <div className="pt-cover-top">
              <div className="pt-cover-logo"><img src={logoSrc} alt="CTR/NY" onError={e => { e.target.style.display='none'; }} /></div>
              <div className="pt-cover-rule"></div>
              <div className="pt-cover-eyebrow">{ptRender(tpl.cover.eyebrow, ctx)}</div>
              <div className="pt-cover-title">{ptRender(tpl.cover.title, ctx)}</div>
              <div className="pt-cover-subtitle">{ptRender(tpl.cover.subtitle, ctx)}</div>
              <div>
                <div className="pt-cover-client-label">Prepared for</div>
                <div className="pt-cover-client-name">{client}</div>
                {form.contactName  && <div className="pt-cover-client-sub">{form.contactName}{form.contactTitle ? `, ${form.contactTitle}` : ''}</div>}
                {form.contactPhone && <div className="pt-cover-client-detail">{form.contactPhone}</div>}
                {form.contactEmail && <div className="pt-cover-client-detail">{form.contactEmail}</div>}
              </div>
            </div>
            <div className="pt-cover-bottom">
              <div className="pt-cover-bottom-left">Prepared by CTR/NY &nbsp;·&nbsp; <span>{propDateFmt || '—'}</span>{form.repName ? ` · ${form.repName}` : ''}</div>
              <div className="pt-cover-bottom-right">Attendance on Demand Platform</div>
            </div>
          </div>
        </div>

        {/* ═══════════════ PAGE 2 — EXECUTIVE SUMMARY ═══════════════ */}
        <div className="pt-page">
          <div className="pt-section-header">
            <span className="pt-sh-num">01</span>
            <div className="pt-sh-rule"></div>
            <span className="pt-sh-title">Executive Summary</span>
          </div>
          <div className="pt-content">
            <p className="pt-lead">{ptRender(tpl.exec.lead, ctx)}</p>
            <div className="pt-hbox">
              <p>{ptRender(tpl.exec.challenge, ctx)}</p>
            </div>
            <div className="pt-stat-strip">
              {tpl.exec.stats.map((s, i) => (
                <div className="pt-stat" key={i}>
                  <div className="pt-stat-num">{ptRender(s.num, ctx)}</div>
                  <div className="pt-stat-label">{ptRender(s.label, ctx)}</div>
                </div>
              ))}
            </div>
            <div className="pt-feat-grid">
              {tpl.exec.features.map((f, i) => (
                <div className="pt-feat" key={i}>
                  <div className="pt-feat-title">{ptRender(f.title, ctx)}</div>
                  <p>{ptRender(f.text, ctx)}</p>
                </div>
              ))}
            </div>
            <div className="pt-showcase">
              <div className="pt-showcase-label">Live Analytics Dashboard — Exception Reporting</div>
              <img src="uploads/docx_images/image7.png" alt="AOD Dashboard" onError={e => { e.target.style.display='none'; }} />
            </div>
          </div>
          <div className="pt-footer"><span>{footerLine}</span><span className="pt-pn">Page 2</span></div>
        </div>

        {/* ═══════════════ PAGE 3 — KEY CAPABILITIES ═══════════════ */}
        <div className="pt-page">
          <div className="pt-section-header">
            <span className="pt-sh-num">02</span>
            <div className="pt-sh-rule"></div>
            <span className="pt-sh-title">Key Capabilities</span>
          </div>
          <div className="pt-content">
            <p className="pt-lead">{ptRender(tpl.capabilities.lead, ctx)}</p>
            {tpl.capabilities.groups.map((g, gi) => (
              <React.Fragment key={gi}>
                {gi > 0 && <hr className="pt-divider" />}
                <h3 className="pt-sub">{ptRender(g.heading, ctx)}</h3>
                <ul className="pt-cap-list">
                  {(g.bullets || []).map((b, bi) => <li key={bi}>{ptRender(b, ctx)}</li>)}
                </ul>
                {gi === 0 && (
                  <div style={{ display:'grid', gridTemplateColumns:'1fr 1fr', gap:12, marginTop:24, marginBottom:4 }}>
                    <div className="pt-sc-cell"><div className="pt-sc-cell-label">PTO Requests</div><img src="uploads/docx_images/image8.png" alt="PTO" style={{ width:'100%', display:'block', objectFit:'cover', height:90 }} onError={e => { e.target.style.display='none'; }} /></div>
                    <div className="pt-sc-cell"><div className="pt-sc-cell-label">PTO Accrual Ledger</div><img src="uploads/docx_images/image10.png" alt="Accruals" style={{ width:'100%', display:'block', objectFit:'cover', height:90 }} onError={e => { e.target.style.display='none'; }} /></div>
                  </div>
                )}
              </React.Fragment>
            ))}
            <div className="pt-sc-grid">
              <div className="pt-sc-cell"><div className="pt-sc-cell-label">Timecard View</div><img src="uploads/docx_images/image6.png" alt="Timecard" onError={e => { e.target.style.display='none'; }} /></div>
              <div className="pt-sc-cell"><div className="pt-sc-cell-label">Scheduling</div><img src="uploads/docx_images/image11.png" alt="Scheduling" onError={e => { e.target.style.display='none'; }} /></div>
              <div className="pt-sc-cell"><div className="pt-sc-cell-label">Geo-Fencing</div><img src="uploads/docx_images/image12.png" alt="Geofencing" onError={e => { e.target.style.display='none'; }} /></div>
            </div>
          </div>
          <div className="pt-footer"><span>{footerLine}</span><span className="pt-pn">Page 3</span></div>
        </div>

        {/* ═══════════════ PAGE 4 — PRICING ═══════════════ */}
        <div className="pt-page">
          <div className="pt-section-header">
            <span className="pt-sh-num">03</span>
            <div className="pt-sh-rule"></div>
            <span className="pt-sh-title">Pricing</span>
          </div>
          <div className="pt-content">
            <p className="pt-lead">{ptRender(tpl.pricing.lead, ctx)}</p>
            <h3 className="pt-sub">Software Subscription — Monthly Fees</h3>
            <table className="pt-table">
              <thead><tr><th>Employee Count</th><th>Pricing Model</th><th style={{ textAlign:'right' }}>Monthly Cost</th></tr></thead>
              <tbody>
                {(() => {
                  // Loose compare: strip whitespace + em/en-dashes → ASCII hyphen
                  const norm = s => String(s || '').replace(/\s+/g,'').replace(/[–—]/g,'-').toLowerCase();
                  const sel  = norm(form.employeeCount);
                  return (pricingTiers || tpl.pricing.tiers).map((t, i) => (
                    <tr key={t.match || i} className={norm(t.match) === sel ? 'pt-row-selected' : ''}>
                      <td>{ptRender(t.label, ctx)}</td><td>{ptRender(t.model, ctx)}</td><td>{ptRender(t.cost, ctx)}</td>
                    </tr>
                  ));
                })()}
              </tbody>
            </table>
            <p className="pt-price-note">{ptRender(tpl.pricing.priceNote, ctx)}</p>
            <hr className="pt-divider" />
            <h3 className="pt-sub">{ptRender(tpl.pricing.includedHeading, ctx)}</h3>
            <table className="pt-table">
              <thead><tr><th>Item</th><th>Description</th><th style={{ textAlign:'right' }}>Price</th></tr></thead>
              <tbody>
                {tpl.pricing.included.map((it, i) => (
                  <tr key={i}><td>{ptRender(it.item, ctx)}</td><td>{ptRender(it.description, ctx)}</td><td>{ptRender(it.price, ctx)}</td></tr>
                ))}
              </tbody>
            </table>
          </div>
          <div className="pt-footer"><span>{footerLine}</span><span className="pt-pn">Page 4</span></div>
        </div>

        {/* ═══════════════ PAGE 5 — HARDWARE OPTIONS ═══════════════ */}
        <div className="pt-page">
          <div className="pt-section-header">
            <span className="pt-sh-num">04</span>
            <div className="pt-sh-rule"></div>
            <span className="pt-sh-title">Hardware Options</span>
          </div>
          <div className="pt-content">
            <p className="pt-lead">{ptRender(tpl.hardware.lead, ctx)}</p>
            {lines.length > 0 ? (
              <div className="pt-hw-grid">
                {lines.map((line, i) => <HwCard key={line.id || i} line={line} />)}
              </div>
            ) : (
              <div style={{ padding:'40px', textAlign:'center', color:'#9AA3B0', fontSize:14 }}>No hardware items selected.</div>
            )}
            {lines.length > 0 && (
              <p className="pt-price-note" style={{ marginTop:16 }}>{ptRender(tpl.hardware.installNote, ctx)}</p>
            )}
          </div>
          <div className="pt-footer"><span>{footerLine}</span><span className="pt-pn">Page 5</span></div>
        </div>

        {/* ═══════════════ PAGE 6 — IMPLEMENTATION & TERMS ═══════════════ */}
        <div className="pt-page">
          <div className="pt-section-header">
            <span className="pt-sh-num">05 – 06</span>
            <div className="pt-sh-rule"></div>
            <span className="pt-sh-title">Implementation &amp; Terms</span>
          </div>
          <div className="pt-content">
            <h3 className="pt-sub">Setup &amp; Configuration</h3>
            <table className="pt-setup-table">
              <thead><tr><th>Service</th><th>Description</th><th style={{ textAlign:'right' }}>Fee</th></tr></thead>
              <tbody>
                {(() => {
                  const fallback = [
                    { name:'System Setup & Configuration',           description:'Full account setup, system configuration, and initial data load', fee:'$500' },
                    { name:'Extended Instructor-Led Online Training', description:'Live onboarding session covering all platform features',         fee:'Included' },
                    { name:'On-Site Hardware Installation',           description:'Available upon request — rates vary by location',                fee:'Upon Request' },
                  ];
                  const items = Array.isArray(form.setupItems) && form.setupItems.length
                    ? form.setupItems
                    : fallback;
                  return items.map((it, i) => (
                    <tr key={i}><td>{it.name}</td><td>{it.description}</td><td>{it.fee}</td></tr>
                  ));
                })()}
              </tbody>
            </table>
            <hr className="pt-divider" />
            <h3 className="pt-sub">Terms &amp; Conditions</h3>
            <ul className="pt-terms">
              {tpl.terms.bullets.map((b, i) => (
                <li key={i} data-n={String(i + 1).padStart(2, '0')}>{ptRender(b, ctx)}</li>
              ))}
            </ul>
            {form.notes && <>
              <hr className="pt-divider" />
              <h3 className="pt-sub">Additional Notes</h3>
              <div style={{ fontSize:13, color:'#2E3545', lineHeight:1.7, whiteSpace:'pre-wrap', background:'#F7F8FA', borderRadius:4, padding:'14px 18px' }}>{form.notes}</div>
            </>}
          </div>
          <div className="pt-footer"><span>{footerLine}</span><span className="pt-pn">Page 6</span></div>
        </div>

        {/* ═══════════════ PAGE 7 — NEXT STEPS / CTA ═══════════════ */}
        <div className="pt-page">
          <div className="pt-cta">
            <div className="pt-cta-top">
              <div className="pt-cta-eyebrow">{ptRender(tpl.cta.eyebrow, ctx)}</div>
              <div className="pt-cta-heading">{ptRender(tpl.cta.heading, ctx)}</div>
              <div className="pt-cta-body">{ptRender(tpl.cta.body, ctx)}</div>
              <div className="pt-cta-contacts">
                <div>
                  <div className="pt-cta-contact-label">Prepared For</div>
                  <div className="pt-cta-contact-value">{client}</div>
                  {form.contactName  && <div className="pt-cta-contact-sub">{form.contactName}{form.contactTitle ? `, ${form.contactTitle}` : ''}</div>}
                  {form.contactEmail && <div className="pt-cta-contact-detail" style={{ wordBreak:'break-word' }}>{form.contactEmail}</div>}
                  {form.contactPhone && <div className="pt-cta-contact-detail">{form.contactPhone}</div>}
                </div>
                <div>
                  <div className="pt-cta-contact-label">Your CTR/NY Rep</div>
                  <div className="pt-cta-contact-value">{form.repName || form.createdByName || '—'}</div>
                </div>
                <div>
                  <div className="pt-cta-contact-label">Contact</div>
                  <div className="pt-cta-contact-value" style={{ fontSize:12, fontWeight:600, wordBreak:'break-word' }}>{form.createdByEmail || '—'}</div>
                  {form.createdByPhone && <div className="pt-cta-contact-detail">{form.createdByPhone}</div>}
                </div>
                <div>
                  <div className="pt-cta-contact-label">Valid Until</div>
                  <div className="pt-cta-contact-value">{validFmt || '—'}</div>
                </div>
              </div>
              <div className="pt-cta-divider">
                <div className="pt-cta-divider-red"></div>
                <div className="pt-cta-divider-grey"></div>
              </div>
              <div className="pt-cta-steps">
                {tpl.cta.steps.map((s, i) => (
                  <div className="pt-cta-step" key={i}>
                    <div className="pt-cta-step-num">Step {String(i + 1).padStart(2, '0')}</div>
                    <div className="pt-cta-step-title">{ptRender(s.title, ctx)}</div>
                    <div className="pt-cta-step-desc">{ptRender(s.desc, ctx)}</div>
                  </div>
                ))}
              </div>
            </div>
            <div className="pt-cta-bottom">
              <div className="pt-cta-bottom-brand">
                <img src={logoSrc} alt="CTR/NY" />
                <div className="pt-cta-tagline">Workforce Management Solutions</div>
              </div>
              <div className="pt-cta-validity">
                {validFmt && <>Proposal valid through {validFmt}<br/></>}
                {propDateFmt && <>Prepared {propDateFmt}</>}
              </div>
            </div>
          </div>
        </div>

      </div>{/* /prop-tpl */}

      {emailFile && (
        <SendEmailModal
          toEmail={form.contactEmail || ''}
          toName={form.contactName || client}
          leadId={form.leadId || null}
          accountId={form.accountId || null}
          initialFiles={[emailFile]}
          initialSubject={`Proposal ${propNum} — ${client}`}
          zIndex={9500}
          onClose={() => setEmailFile(null)}
          showToast={showToast}
        />
      )}
    </div>
  );
}
