// MaxAMove CRM — Leads Pipeline (Kanban) + Jobs Board

// Build the customer-portal URL for a given estimate UUID. Used by both the
// Leads drawer (per estimate card) and the Jobs board (per job row).
function _portalUrlForEstimate(estimateId) {
  if (!estimateId) return null;
  const origin = (typeof window !== 'undefined' && window.location && window.location.origin) || '';
  return `${origin}/portal.html?est=${estimateId}`;
}
function _openPortal(estimateId) {
  const url = _portalUrlForEstimate(estimateId);
  if (!url) { alert('No estimate linked yet — create or open one first.'); return; }
  window.open(url, '_blank', 'noopener');
}
async function _copyPortalLink(estimateId) {
  const url = _portalUrlForEstimate(estimateId);
  if (!url) { alert('No estimate linked yet — create or open one first.'); return false; }
  try { await navigator.clipboard.writeText(url); return true; }
  catch { window.prompt('Copy this portal link:', url); return false; }
}

// ─── LEADS PIPELINE ───
function LeadsPipeline({ leads, onLeadsChange, onNavigate }) {
  const [selected, setSelected] = React.useState(null);
  const [dragging, setDragging] = React.useState(null);
  const [dragOver, setDragOver] = React.useState(null);
  const [showAdd, setShowAdd] = React.useState(false);
  const [saving, setSaving] = React.useState(false);
  const [error, setError] = React.useState('');
  const [newLead, setNewLead] = React.useState({ name: '', phone: '', email: '', source: 'Google', costOfLead: '', stage: 'new', moveDate: '', fromCity: '', toCity: '', size: '2br', value: '', notes: '' });

  // Lead sources are user-editable in Settings. Pull the live list each time
  // the page mounts so newly-added sources show up without a hard refresh.
  const [sourceOptions, setSourceOptions] = React.useState(window.DEFAULT_LEAD_SOURCES || ['Google', 'Thumbtack', 'Referral', 'Yelp', 'Facebook', 'Nextdoor', 'Website', 'Other']);
  React.useEffect(() => {
    if (typeof window.loadSettings !== 'function') return;
    let cancelled = false;
    window.loadSettings()
      .then(s => { if (!cancelled && Array.isArray(s.leadSources) && s.leadSources.length) setSourceOptions(s.leadSources); })
      .catch(() => {});
    return () => { cancelled = true; };
  }, []);

  // Stage moves: optimistic UI + Supabase write
  const moveLeadToStage = async (leadId, stage) => {
    try {
      await window.moveLeadStage(leadId, stage);
      if (onLeadsChange) onLeadsChange();
    } catch (e) {
      setError('Failed to move lead: ' + e.message);
      if (onLeadsChange) onLeadsChange();   // rollback to server state
    }
  };

  // Confirm a Tentative lead → Booked. Looks up the linked estimate (if any),
  // upgrades its pricing.reservationType to 'guaranteed', creates a Job from it,
  // and moves the lead to 'booked'. Used when the customer comes back with the
  // missing details and Gavin wants to lock the booking in himself.
  const confirmTentativeBooking = async (lead) => {
    if (!confirm(`Confirm ${lead.name}'s booking?\n\nThis will:\n• Move the lead to Booked\n• Upgrade the estimate to a guaranteed reservation\n• Auto-create a Job from the estimate`)) return;
    try {
      // 1. Find the linked estimate (most recent for this lead)
      const { data: est } = await window.sb
        .from('estimates')
        .select('id, customer_id, pricing, move_data')
        .eq('lead_id', lead.id)
        .order('created_at', { ascending: false })
        .limit(1)
        .maybeSingle();
      // 2. Update estimate's pricing.reservationType → guaranteed (if estimate exists)
      if (est) {
        const mergedPricing = { ...(est.pricing || {}), reservationType: 'guaranteed' };
        await window.sb.from('estimates').update({ pricing: mergedPricing }).eq('id', est.id);
        // 3. Auto-create job from the estimate (skip if one already exists)
        if (typeof window.createJob === 'function' && est.customer_id) {
          const { data: existingJob } = await window.sb
            .from('jobs').select('id').eq('estimate_id', est.id).maybeSingle();
          if (!existingJob) {
            try {
              let liveTotal = 0;
              try { liveTotal = window.calcTotals ? window.calcTotals(est.pricing || {}, est.move_data || {}).total : 0; } catch {}
              await window.createJob({
                customerId: est.customer_id,
                estimateUuid: est.id,
                scheduledDate: est.move_data?.moveDate || new Date().toISOString().slice(0,10),
                fromAddress: est.move_data?.fromAddress,
                toAddress: est.move_data?.toAddress,
                size: est.move_data?.moveSize,
                // Carry assigned crew from the estimate so it shows up in
                // each crew member's Crew HQ schedule. Crew lives in
                // pricing.crewIds (the wizard's crew picker writes there).
                crewIds: Array.isArray(est.pricing?.crewIds) ? est.pricing.crewIds : [],
                status: 'pending',
                value: liveTotal || (est.pricing && est.pricing.total) || lead.value || 0,
              });
            } catch (jobErr) {
              console.warn('[confirmTentative] createJob failed:', jobErr);
            }
          }
        }
      }
      // 4. Move lead to Booked
      await window.moveLeadStage(lead.id, 'booked');
      setSelected(null);
      if (onLeadsChange) onLeadsChange();
    } catch (e) {
      setError('Failed to confirm booking: ' + e.message);
    }
  };

  const addLead = async () => {
    if (!newLead.name) return;
    setSaving(true); setError('');
    try {
      await window.createLead({
        name: newLead.name,
        phone: newLead.phone,
        email: newLead.email,
        source: newLead.source,
        costOfLead: newLead.costOfLead === '' ? null : parseFloat(newLead.costOfLead),
        stage: newLead.stage,
        moveDate: newLead.moveDate,
        fromCity: newLead.fromCity,
        toCity: newLead.toCity,
        size: newLead.size,
        value: parseFloat(newLead.value) || 0,
        notes: newLead.notes,
      });
      setShowAdd(false);
      setNewLead({ name: '', phone: '', email: '', source: 'Google', costOfLead: '', stage: 'new', moveDate: '', fromCity: '', toCity: '', size: '2br', value: '', notes: '' });
      if (onLeadsChange) onLeadsChange();
    } catch (e) {
      setError('Failed to add lead: ' + e.message);
    } finally {
      setSaving(false);
    }
  };

  const deleteLead = async (id) => {
    if (!confirm('Delete this lead permanently?')) return;
    try {
      await window.deleteLead(id);
      setSelected(null);
      if (onLeadsChange) onLeadsChange();
    } catch (e) {
      setError('Failed to delete: ' + e.message);
    }
  };

  // Per-estimate "copied" flag for brief inline feedback on the Copy Link button.
  const [copiedFor, setCopiedFor] = React.useState(null);
  const copyPortalLink = async (estimateId) => {
    const ok = await _copyPortalLink(estimateId);
    if (ok) {
      setCopiedFor(estimateId);
      setTimeout(() => setCopiedFor(null), 1800);
    }
  };
  const openPortal = _openPortal;

  // Quick inline editor for the Cost of Lead — used in the detail drawer so
  // Gavin can fill in acquisition cost on leads that came in via quote.html
  // (where the customer obviously can't enter it).
  const editCostOfLead = async (lead) => {
    const cur = lead.costOfLead != null ? String(lead.costOfLead) : '';
    const raw = prompt(`Cost of lead for ${lead.name}\n\nWhat did you pay to acquire this lead? (Thumbtack credit, ad spend, etc.)\n\nLeave blank to clear.`, cur);
    if (raw === null) return;
    const trimmed = raw.trim();
    const value = trimmed === '' ? null : Number(trimmed);
    if (trimmed !== '' && (!isFinite(value) || value < 0)) {
      alert('Enter a non-negative number, or leave blank.');
      return;
    }
    try {
      await window.updateLead(lead.id, { costOfLead: value });
      if (onLeadsChange) onLeadsChange();
    } catch (e) {
      setError('Failed to save cost of lead: ' + e.message);
    }
  };

  const selectedLead = leads.find(l => l.id === selected);

  // Linked-estimates panel: when a lead is selected, fetch every estimate that's linked
  // to that lead via lead_id. Display summary cards in the drawer so the owner can see
  // what's been quoted without leaving the leads view OR accidentally creating a duplicate.
  const [leadEstimates, setLeadEstimates] = React.useState([]);
  const [leadEstimatesLoading, setLeadEstimatesLoading] = React.useState(false);
  // Map estimate_id → job_id, so we can show "✓ Job created" instead of a second
  // "Create Job" button on estimates that already have a materialized job.
  const [jobByEstimate, setJobByEstimate] = React.useState({});
  React.useEffect(() => {
    if (!selectedLead) { setLeadEstimates([]); setJobByEstimate({}); return; }
    setLeadEstimatesLoading(true);
    window.sb.from('estimates')
      .select('id, number, status, move_data, inventory, pricing, customer_id, sent_at, signed_at, created_at')
      .eq('lead_id', selectedLead.id)
      .order('created_at', { ascending: false })
      .then(async ({ data }) => {
        const estimates = data || [];
        setLeadEstimates(estimates);
        const ids = estimates.map(e => e.id);
        if (ids.length > 0) {
          const { data: jobs } = await window.sb.from('jobs').select('id, estimate_id').in('estimate_id', ids);
          const map = {};
          (jobs || []).forEach(j => { if (j.estimate_id) map[j.estimate_id] = j.id; });
          setJobByEstimate(map);
        } else {
          setJobByEstimate({});
        }
        setLeadEstimatesLoading(false);
      });
  }, [selectedLead && selectedLead.id]);

  // Create a Job from a SPECIFIC signed estimate. Used by the per-estimate
  // "Create Job" button in the linked-estimates panel — fires after a customer
  // signs from the portal, regardless of what stage the lead is in.
  // Idempotent: refuses to create a second job if one already points to this estimate.
  const createJobFromEstimate = async (est) => {
    if (!est || !est.id) return;
    if (jobByEstimate[est.id]) {
      alert('A job has already been created from this estimate.');
      return;
    }
    if (typeof window.createJob !== 'function') {
      setError('Job creation is not available — try refreshing the page.');
      return;
    }
    if (!confirm(`Create a Job from estimate #${est.number}?\n\nThe lead will move to Booked and the new Job will appear in the Jobs Board.`)) return;
    try {
      const mergedPricing = { ...(est.pricing || {}), reservationType: 'guaranteed' };
      await window.sb.from('estimates').update({ pricing: mergedPricing }).eq('id', est.id);
      const m = est.move_data || {};
      let liveTotal = 0;
      try { liveTotal = window.calcTotals ? window.calcTotals(est.pricing || {}, m).total : 0; } catch {}
      const newJob = await window.createJob({
        customerId: est.customer_id || selectedLead.customerId,
        estimateUuid: est.id,
        scheduledDate: m.moveDate || new Date().toISOString().slice(0, 10),
        fromAddress: m.fromAddress,
        toAddress: m.toAddress,
        size: m.moveSize,
        // Carry assigned crew from the estimate so it shows up in each
        // crew member's Crew HQ schedule. Crew lives in pricing.crewIds
        // (the wizard's crew picker writes there).
        crewIds: Array.isArray(est.pricing?.crewIds) ? est.pricing.crewIds : [],
        status: 'pending',
        value: liveTotal || (est.pricing && est.pricing.total) || selectedLead.value || 0,
      });
      setJobByEstimate(prev => ({ ...prev, [est.id]: newJob && newJob.id }));
      if (selectedLead.stage !== 'booked' && selectedLead.stage !== 'won') {
        try { await window.moveLeadStage(selectedLead.id, 'booked'); } catch {}
      }
      setSelected(null);
      if (onLeadsChange) onLeadsChange();
    } catch (e) {
      setError('Failed to create job: ' + (e && e.message));
    }
  };

  // Open an existing estimate in the wizard (stashes UUID; appv2 picks it up on mount)
  const openExistingEstimate = (estId) => {
    try { localStorage.setItem('mam_estimate_to_load', estId); } catch {}
    if (onNavigate) onNavigate('estimates');
    setSelected(null);
  };

  // Create a NEW estimate pre-filled from this lead (existing flow)
  const createNewEstimateFromLead = () => {
    if (!onNavigate || !selectedLead) return;
    try {
      localStorage.setItem('mam_lead_for_estimate', JSON.stringify({
        leadId: selectedLead.id,
        firstName: (selectedLead.name || '').split(/\s+/)[0] || '',
        lastName: (selectedLead.name || '').split(/\s+/).slice(1).join(' ') || '',
        email: selectedLead.email,
        phone: selectedLead.phone,
        moveDate: selectedLead.moveDate,
        fromAddress: selectedLead.fromCity,
        toAddress: selectedLead.toCity,
        size: selectedLead.size,
        source: selectedLead.source,
        notes: selectedLead.notes,
      }));
    } catch (e) {}
    onNavigate('estimates');
    setSelected(null);
  };

  return (
    <div style={{ height: '100%', display: 'flex', flexDirection: 'column' }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 20, flexShrink: 0 }}>
        <div>
          <h1 style={{ fontSize: 22, fontWeight: 900, fontFamily: 'Poppins', marginBottom: 2 }}>Leads Pipeline</h1>
          <p style={{ color: 'var(--muted)', fontSize: 14 }}>{leads.length} total leads · drag cards between stages</p>
        </div>
        <button onClick={() => setShowAdd(true)} style={{ padding: '9px 18px', borderRadius: 10, background: 'linear-gradient(135deg,#14C6BF,#3DCC7E)', color: '#fff', border: 'none', cursor: 'pointer', fontWeight: 700, fontFamily: 'inherit', fontSize: 13 }}>+ New Lead</button>
      </div>

      {error && (
        <div style={{ background: '#fef2f2', border: '1px solid #fecaca', color: '#b91c1c', padding: '10px 14px', borderRadius: 9, fontSize: 13, marginBottom: 14, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
          <span>{error}</span>
          <button onClick={() => setError('')} style={{ background: 'transparent', border: 'none', color: '#b91c1c', cursor: 'pointer', fontWeight: 700 }}>Dismiss</button>
        </div>
      )}

      {/* Kanban Board */}
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(5, 1fr)', gap: 14, flex: 1, minHeight: 0 }}>
        {PIPELINE_STAGES.map(stage => {
          const stageLeads = leads.filter(l => l.stage === stage.id);
          const stageValue = stageLeads.reduce((s, l) => s + (l.value || 0), 0);
          return (
            <div key={stage.id}
              onDragOver={e => { e.preventDefault(); setDragOver(stage.id); }}
              onDragLeave={() => setDragOver(null)}
              onDrop={e => { e.preventDefault(); if (dragging) moveLeadToStage(dragging, stage.id); setDragging(null); setDragOver(null); }}
              style={{ background: dragOver === stage.id ? stage.bg : 'var(--bg)', borderRadius: 14, border: `2px ${dragOver === stage.id ? 'solid' : 'dashed'} ${dragOver === stage.id ? stage.color : 'var(--border)'}`, padding: 12, display: 'flex', flexDirection: 'column', transition: 'all 0.15s', overflow: 'hidden' }}>
              <div style={{ marginBottom: 12, flexShrink: 0 }}>
                <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
                  <span style={{ fontWeight: 800, fontSize: 13, color: stage.color, fontFamily: 'Poppins' }}>{stage.label}</span>
                  <span style={{ background: stage.bg, color: stage.color, borderRadius: 20, padding: '2px 8px', fontSize: 11, fontWeight: 700, border: `1px solid ${stage.color}30` }}>{stageLeads.length}</span>
                </div>
                <div style={{ fontSize: 11, color: 'var(--muted)', marginTop: 2 }}>${stageValue.toLocaleString()} pipeline value</div>
              </div>
              <div style={{ flex: 1, overflowY: 'auto', display: 'flex', flexDirection: 'column', gap: 8 }}>
                {stageLeads.map(lead => {
                  const days = daysUntil(lead.moveDate);
                  const urgent = days !== null && days <= 7 && days >= 0;
                  return (
                    <div key={lead.id}
                      draggable
                      onDragStart={() => setDragging(lead.id)}
                      onDragEnd={() => setDragging(null)}
                      onClick={() => setSelected(lead.id)}
                      style={{ background: 'var(--card)', borderRadius: 10, padding: '12px 12px', border: `1.5px solid ${urgent ? '#fbbf24' : 'var(--border)'}`, cursor: 'grab', boxShadow: dragging === lead.id ? '0 8px 24px rgba(0,0,0,0.12)' : '0 1px 4px rgba(0,0,0,0.04)', opacity: dragging === lead.id ? 0.5 : 1, transition: 'box-shadow 0.15s' }}>
                      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: 6 }}>
                        <div style={{ fontWeight: 700, fontSize: 13, lineHeight: 1.2 }}>{lead.name}</div>
                        {urgent && <span style={{ fontSize: 9, background: '#fef3c7', color: '#b45309', padding: '2px 6px', borderRadius: 4, fontWeight: 700, whiteSpace: 'nowrap' }}>URGENT</span>}
                      </div>
                      <div style={{ fontSize: 11, color: 'var(--muted)', marginBottom: 4 }}>{lead.fromCity} → {lead.toCity}</div>
                      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
                        <span style={{ fontSize: 11, background: 'var(--accent-light)', color: 'var(--accent-dark)', padding: '2px 7px', borderRadius: 5, fontWeight: 600 }}>{lead.size}</span>
                        <span style={{ fontSize: 12, fontWeight: 800, color: 'var(--accent)' }}>${(lead.value || 0).toLocaleString()}</span>
                      </div>
                      {lead.moveDate && (
                        <div style={{ fontSize: 10, color: urgent ? '#b45309' : 'var(--muted)', marginTop: 5, fontWeight: urgent ? 700 : 400, display: 'flex', alignItems: 'center', gap: 4 }}>
                          <Icon name="calendar" size={11}/> {fmtShortDate(lead.moveDate)}{days !== null ? ` (${days < 0 ? Math.abs(days) + 'd ago' : days === 0 ? 'today' : 'in ' + days + 'd'})` : ''}
                        </div>
                      )}
                      <div style={{ fontSize: 10, color: 'var(--muted)', marginTop: 3 }}>via {lead.source}</div>
                    </div>
                  );
                })}
                {stageLeads.length === 0 && (
                  <div style={{ textAlign: 'center', padding: '20px 0', color: 'var(--muted)', fontSize: 12 }}>Drop leads here</div>
                )}
              </div>
            </div>
          );
        })}
      </div>

      {/* Lead Detail Drawer */}
      {selectedLead && (
        <div style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.4)', zIndex: 100, display: 'flex', justifyContent: 'flex-end' }} onClick={() => setSelected(null)}>
          <div style={{ width: 420, background: 'var(--card)', height: '100%', overflowY: 'auto', padding: 28, boxShadow: '-8px 0 40px rgba(0,0,0,0.15)' }} onClick={e => e.stopPropagation()}>
            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 20 }}>
              <h2 style={{ fontSize: 18, fontWeight: 900, fontFamily: 'Poppins' }}>Lead Details</h2>
              <button onClick={() => setSelected(null)} style={{ background: 'none', border: 'none', fontSize: 20, cursor: 'pointer', color: 'var(--muted)' }}>×</button>
            </div>
            <div style={{ display: 'flex', gap: 12, alignItems: 'center', marginBottom: 20, padding: 16, background: 'var(--bg)', borderRadius: 12 }}>
              <div style={{ width: 48, height: 48, borderRadius: '50%', background: 'var(--accent)', display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#fff', fontWeight: 900, fontSize: 16 }}>{selectedLead.name.split(' ').map(n => n[0]).join('')}</div>
              <div>
                <div style={{ fontWeight: 800, fontSize: 16 }}>{selectedLead.name}</div>
                <div style={{ fontSize: 13, color: 'var(--muted)' }}>{selectedLead.phone} · {selectedLead.email}</div>
              </div>
            </div>
            {[
              ['Move Date', fmtDate(selectedLead.moveDate)],
              ['From', selectedLead.fromCity],
              ['To', selectedLead.toCity],
              ['Move Size', selectedLead.size],
              ['Est. Value', '$' + (selectedLead.value || 0).toLocaleString()],
              ['Lead Source', selectedLead.source],
            ].map(([k, v]) => (
              <div key={k} style={{ display: 'flex', justifyContent: 'space-between', padding: '9px 0', borderBottom: '1px solid var(--border)', fontSize: 13 }}>
                <span style={{ color: 'var(--muted)', fontWeight: 600 }}>{k}</span>
                <span style={{ fontWeight: 700 }}>{v || '—'}</span>
              </div>
            ))}
            {/* Cost of Lead — editable. Sits right under Lead Source so Gavin can
                track acquisition cost (Thumbtack credit, ad spend, etc.) and update
                it later for leads that came in via the website. */}
            <div onClick={() => editCostOfLead(selectedLead)}
              title="Click to edit"
              style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '9px 0', borderBottom: '1px solid var(--border)', fontSize: 13, cursor: 'pointer' }}>
              <span style={{ color: 'var(--muted)', fontWeight: 600 }}>Cost of Lead</span>
              <span style={{ fontWeight: 700, color: selectedLead.costOfLead != null ? 'var(--text)' : 'var(--accent)', display: 'inline-flex', alignItems: 'center', gap: 6 }}>
                {selectedLead.costOfLead != null ? '$' + Number(selectedLead.costOfLead).toFixed(2) : '+ Add'}
                <Icon name="pencil" size={12}/>
              </span>
            </div>
            <div style={{ display: 'flex', justifyContent: 'space-between', padding: '9px 0', borderBottom: '1px solid var(--border)', fontSize: 13 }}>
              <span style={{ color: 'var(--muted)', fontWeight: 600 }}>Created</span>
              <span style={{ fontWeight: 700 }}>{fmtDate(selectedLead.created) || '—'}</span>
            </div>
            {selectedLead.notes && (
              <div style={{ marginTop: 16, padding: 12, background: '#fffbeb', borderRadius: 10, fontSize: 13, color: '#78350f' }}>
                <div style={{ fontWeight: 700, marginBottom: 4 }}>Notes</div>
                {selectedLead.notes}
              </div>
            )}
            {selectedLead.stage === 'tentative' && (
              <div style={{ marginTop: 18, padding: 14, background: '#faf5ff', border: '1.5px solid #a855f7', borderRadius: 12 }}>
                <div style={{ fontWeight: 800, fontSize: 14, color: '#7e22ce', marginBottom: 6, display: 'flex', alignItems: 'center', gap: 6 }}><Icon name="clock" size={15}/> Tentative Hold</div>
                <div style={{ fontSize: 12, color: 'var(--text)', marginBottom: 12, lineHeight: 1.5 }}>
                  Customer is holding this slot while finalizing details. When they come back with the missing info, click below to lock it in — we'll auto-create the job and update the estimate.
                </div>
                <button onClick={() => confirmTentativeBooking(selectedLead)}
                  style={{ width: '100%', padding: '12px', borderRadius: 10, border: 'none', background: 'linear-gradient(135deg, #14C6BF, #3DCC7E)', color: '#fff', fontWeight: 800, fontSize: 14, fontFamily: 'inherit', cursor: 'pointer' }}>
                  Confirm Booking & Create Job
                </button>
              </div>
            )}

            {/* Linked Estimates panel — shows everything quoted to this lead so the owner
                doesn't have to leave the drawer (and doesn't accidentally create a dupe). */}
            <div style={{ marginTop: 20 }}>
              <div style={{ fontSize: 11, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.07em', color: 'var(--muted)', marginBottom: 8 }}>
                Estimates ({leadEstimates.length})
              </div>
              {leadEstimatesLoading && (
                <div style={{ padding: 12, color: 'var(--muted)', fontSize: 13, textAlign: 'center' }}>Loading…</div>
              )}
              {!leadEstimatesLoading && leadEstimates.length === 0 && (
                <div style={{ padding: 14, background: 'var(--bg)', borderRadius: 10, fontSize: 12, color: 'var(--muted)', textAlign: 'center' }}>
                  No estimates yet for this lead.
                </div>
              )}
              {leadEstimates.map(est => {
                const p = est.pricing || {};
                const m = est.move_data || {};
                const inv = Array.isArray(est.inventory) ? est.inventory : [];
                const totalLbs = inv.reduce((sum, it) => {
                  const w = typeof window.getItemWeight === 'function' ? window.getItemWeight(it) : (Number(it.weight) || 0);
                  return sum + (Number(it.qty) || 1) * (Number(w) || 0);
                }, 0);
                let total = 0;
                let nteTotal = null;
                let nteHours = null;
                try {
                  const t = window.calcTotals ? window.calcTotals(p, m) : { total: 0 };
                  total = t.total || 0;
                  if (t.nteEnabled && t.nteTotal != null && Number(t.nteTotal) > 0) {
                    nteTotal = Number(t.nteTotal);
                    nteHours = t.nteHours;
                  }
                } catch {}
                const isTentative = p.reservationType === 'tentative';
                const statusBadge = (() => {
                  const s = est.status;
                  if (s === 'signed') return { label: isTentative ? 'Tentative' : 'Signed', bg: isTentative ? '#faf5ff' : '#f0fdf4', fg: isTentative ? '#7e22ce' : '#16a34a' };
                  if (s === 'sent')   return { label: 'Sent',     bg: '#eff6ff', fg: '#2563eb' };
                  if (s === 'declined') return { label: 'Declined', bg: '#fef2f2', fg: '#b91c1c' };
                  if (s === 'expired')  return { label: 'Expired',  bg: '#f1f5f9', fg: '#475569' };
                  return { label: 'Draft', bg: '#f8fafc', fg: '#475569' };
                })();
                const surcharges = Array.isArray(p.surcharges) ? p.surcharges : [];
                const tripFee = p.tripFeeWaived ? 0 : (Number(p.tripFee) || 0);
                return (
                  <div key={est.id} style={{ padding: 14, background: 'var(--bg)', borderRadius: 12, border: '1.5px solid var(--border)', marginBottom: 10 }}>
                    <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: 8 }}>
                      <div>
                        <div style={{ fontWeight: 800, fontSize: 14 }}>#{est.number}</div>
                        <div style={{ fontSize: 11, color: 'var(--muted)', marginTop: 2 }}>
                          Created {fmtDate(est.created_at?.slice(0,10))}
                          {est.sent_at && <> · Sent {fmtDate(est.sent_at.slice(0,10))}</>}
                          {est.signed_at && <> · Signed {fmtDate(est.signed_at.slice(0,10))}</>}
                        </div>
                      </div>
                      <div style={{ padding: '3px 9px', borderRadius: 12, background: statusBadge.bg, color: statusBadge.fg, fontSize: 10, fontWeight: 800, textTransform: 'uppercase', letterSpacing: '.05em', whiteSpace: 'nowrap' }}>
                        {statusBadge.label}
                      </div>
                    </div>
                    <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '6px 10px', fontSize: 12, marginBottom: 8 }}>
                      <div>
                        <span style={{ color: 'var(--muted)' }}>Total</span>
                        <div style={{ fontWeight: 800, fontSize: 16, color: 'var(--accent-dark)' }}>${total.toFixed(2)}</div>
                        {nteTotal != null && (
                          <div style={{ fontSize: 11, color: 'var(--accent-dark)', fontWeight: 700, marginTop: 2, display: 'inline-flex', alignItems: 'center', gap: 4 }}>
                            <Icon name="lock" size={10}/> NTE {nteHours ? `${nteHours} hrs` : ''} ${nteTotal.toFixed(2)}
                          </div>
                        )}
                      </div>
                      <div><span style={{ color: 'var(--muted)' }}>Move date</span><div style={{ fontWeight: 700 }}>{fmtDate(m.moveDate) || '—'}</div></div>
                      <div><span style={{ color: 'var(--muted)' }}>Crew × hrs</span><div style={{ fontWeight: 700 }}>{p.movers || '—'} × {p.hours || '—'} hrs</div></div>
                      <div><span style={{ color: 'var(--muted)' }}>Inventory</span><div style={{ fontWeight: 700 }}>{inv.length} items {totalLbs > 0 && <>· {totalLbs.toLocaleString()} lbs</>}</div></div>
                      <div><span style={{ color: 'var(--muted)' }}>Service</span><div style={{ fontWeight: 700 }}>{p.serviceType === 'labor_only' ? 'Labor Only' : 'Full Service'}</div></div>
                      <div><span style={{ color: 'var(--muted)' }}>From → To</span><div style={{ fontWeight: 700, fontSize: 11 }}>{m.fromAddress || '—'} → {m.toAddress || '—'}</div></div>
                    </div>
                    {(tripFee > 0 || surcharges.length > 0) && (
                      <div style={{ fontSize: 11, color: 'var(--muted)', marginBottom: 8 }}>
                        Add-ons: {[
                          tripFee > 0 ? `Trip Fee $${tripFee}` : null,
                          ...surcharges.map(s => `${s.name} $${s.price}`),
                        ].filter(Boolean).join(' · ')}
                      </div>
                    )}
                    {m.specialInstructions && (
                      <div style={{ fontSize: 11, color: 'var(--text)', padding: 8, background: '#fffbeb', borderRadius: 8, marginBottom: 8 }}>
                        <b>Notes:</b> {m.specialInstructions}
                      </div>
                    )}
                    {est.status === 'signed' && (
                      jobByEstimate[est.id] ? (
                        <div style={{ width: '100%', padding: '9px', borderRadius: 8, background: '#f0fdf4', color: '#16a34a', fontWeight: 800, fontSize: 12, textAlign: 'center', marginBottom: 8, border: '1.5px solid #bbf7d0', display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 5 }}>
                          <Icon name="check" size={13}/> Job created
                        </div>
                      ) : (
                        <button onClick={() => createJobFromEstimate(est)}
                          style={{ width: '100%', padding: '11px', borderRadius: 9, border: 'none', background: 'linear-gradient(135deg, #14C6BF, #3DCC7E)', color: '#fff', fontWeight: 800, fontSize: 13, fontFamily: 'inherit', cursor: 'pointer', marginBottom: 8 }}>
                          Create Job from this Estimate →
                        </button>
                      )
                    )}
                    <button onClick={() => openExistingEstimate(est.id)}
                      style={{ width: '100%', padding: '9px', borderRadius: 8, border: '1.5px solid var(--accent)', background: '#fff', color: 'var(--accent-dark)', fontWeight: 700, fontFamily: 'inherit', fontSize: 12, cursor: 'pointer', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: 6, marginBottom: 6 }}>
                      <Icon name="clipboard" size={13}/> Open / Edit Estimate →
                    </button>
                    {/* Customer Portal — view it like the customer sees it, or copy the link to send. */}
                    <div style={{ display: 'flex', gap: 6 }}>
                      <button onClick={() => openPortal(est.id)}
                        title="Open this estimate's customer portal in a new tab"
                        style={{ flex: 1, padding: '9px', borderRadius: 8, border: '1.5px solid var(--border)', background: 'var(--bg)', color: 'var(--text)', fontWeight: 700, fontFamily: 'inherit', fontSize: 12, cursor: 'pointer', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: 6 }}>
                        <Icon name="eye" size={13}/> View Portal
                      </button>
                      <button onClick={() => copyPortalLink(est.id)}
                        title="Copy the portal link to your clipboard so you can text/email it"
                        style={{ flex: 1, padding: '9px', borderRadius: 8, border: '1.5px solid var(--border)', background: copiedFor === est.id ? '#ecfdf5' : 'var(--bg)', color: copiedFor === est.id ? '#16a34a' : 'var(--text)', fontWeight: 700, fontFamily: 'inherit', fontSize: 12, cursor: 'pointer', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: 6 }}>
                        <Icon name={copiedFor === est.id ? 'check' : 'send'} size={13}/> {copiedFor === est.id ? 'Copied!' : 'Copy Link'}
                      </button>
                    </div>
                  </div>
                );
              })}
              {/* Smart action button: only show "+ New Estimate" when there are NO existing
                  estimates, OR as a small secondary link when estimates already exist. */}
              {leadEstimates.length === 0 ? (
                <button onClick={createNewEstimateFromLead}
                  style={{ width: '100%', marginTop: 4, padding: '11px', borderRadius: 9, background: 'linear-gradient(135deg,#14C6BF,#3DCC7E)', color: '#fff', fontWeight: 700, fontFamily: 'inherit', fontSize: 13, cursor: 'pointer', textAlign: 'center', border: 'none', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: 6 }}>
                  <Icon name="clipboard" size={14}/> Create Estimate →
                </button>
              ) : (
                <button onClick={createNewEstimateFromLead}
                  style={{ width: '100%', marginTop: 4, padding: '8px', borderRadius: 8, background: 'transparent', color: 'var(--muted)', fontWeight: 600, fontFamily: 'inherit', fontSize: 11, cursor: 'pointer', textAlign: 'center', border: '1px dashed var(--border)' }}>
                  + Create another estimate
                </button>
              )}
            </div>

            <div style={{ marginTop: 20, display: 'flex', flexDirection: 'column', gap: 8 }}>
              <div style={{ fontSize: 11, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.07em', color: 'var(--muted)', marginBottom: 4 }}>Move to Stage</div>
              {PIPELINE_STAGES.map(s => (
                <button key={s.id} onClick={() => { moveLeadToStage(selectedLead.id, s.id); setSelected(null); }}
                  style={{ padding: '9px', borderRadius: 9, border: `1.5px solid ${s.color}40`, background: selectedLead.stage === s.id ? s.bg : 'transparent', color: s.color, fontWeight: 700, fontFamily: 'inherit', fontSize: 13, cursor: 'pointer', textAlign: 'left', display: 'flex', alignItems: 'center', gap: 6 }}>
                  {selectedLead.stage === s.id && <Icon name="check" size={13}/>}<span>{s.label}</span>
                </button>
              ))}
              <button onClick={() => deleteLead(selectedLead.id)}
                style={{ marginTop: 8, padding: '9px', borderRadius: 9, background: '#fef2f2', color: '#b91c1c', fontWeight: 700, fontFamily: 'inherit', fontSize: 12, cursor: 'pointer', textAlign: 'center', border: '1.5px solid #fecaca' }}>
                Delete lead
              </button>
            </div>
          </div>
        </div>
      )}

      {/* Add Lead Modal */}
      {showAdd && (
        <div style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.5)', zIndex: 100, display: 'flex', alignItems: 'center', justifyContent: 'center' }} onClick={() => setShowAdd(false)}>
          <div style={{ background: 'var(--card)', borderRadius: 16, padding: 28, width: 480, maxHeight: '85vh', overflowY: 'auto' }} onClick={e => e.stopPropagation()}>
            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 20 }}>
              <h2 style={{ fontSize: 18, fontWeight: 900, fontFamily: 'Poppins' }}>Add New Lead</h2>
              <button onClick={() => setShowAdd(false)} style={{ background: 'none', border: 'none', fontSize: 20, cursor: 'pointer' }}>×</button>
            </div>
            {[
              { label: 'Full Name *', key: 'name', type: 'text', placeholder: 'Jane Smith' },
              { label: 'Phone', key: 'phone', type: 'tel', placeholder: '(555) 000-0000' },
              { label: 'Email', key: 'email', type: 'email', placeholder: 'jane@email.com' },
              { label: 'From City', key: 'fromCity', type: 'text', placeholder: 'Austin, TX' },
              { label: 'To City', key: 'toCity', type: 'text', placeholder: 'Dallas, TX' },
              { label: 'Move Date', key: 'moveDate', type: 'date', placeholder: '' },
              { label: 'Est. Value ($)', key: 'value', type: 'number', placeholder: '1200' },
            ].map(f => (
              <div key={f.key} style={{ marginBottom: 12 }}>
                <label style={{ display: 'block', fontSize: 11, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.07em', color: 'var(--muted)', marginBottom: 5 }}>{f.label}</label>
                <input type={f.type} value={newLead[f.key]} onChange={e => setNewLead(p => ({ ...p, [f.key]: e.target.value }))} placeholder={f.placeholder}
                  style={{ width: '100%', padding: '9px 12px', borderRadius: 9, border: '1.5px solid var(--border)', background: 'var(--bg)', color: 'var(--text)', fontSize: 14, fontFamily: 'inherit', outline: 'none' }} />
              </div>
            ))}
            <div style={{ marginBottom: 12 }}>
              <label style={{ display: 'block', fontSize: 11, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.07em', color: 'var(--muted)', marginBottom: 5 }}>Source</label>
              <select value={newLead.source} onChange={e => setNewLead(p => ({ ...p, source: e.target.value }))}
                style={{ width: '100%', padding: '9px 12px', borderRadius: 9, border: '1.5px solid var(--border)', background: 'var(--bg)', color: 'var(--text)', fontSize: 14, fontFamily: 'inherit', outline: 'none' }}>
                {sourceOptions.map(s => <option key={s}>{s}</option>)}
              </select>
            </div>
            <div style={{ marginBottom: 14 }}>
              <label style={{ display: 'block', fontSize: 11, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.07em', color: 'var(--muted)', marginBottom: 5 }}>Cost of Lead ($)</label>
              <input type="number" step="0.01" min="0" value={newLead.costOfLead} onChange={e => setNewLead(p => ({ ...p, costOfLead: e.target.value }))} placeholder="e.g. 22.50"
                style={{ width: '100%', padding: '9px 12px', borderRadius: 9, border: '1.5px solid var(--border)', background: 'var(--bg)', color: 'var(--text)', fontSize: 14, fontFamily: 'inherit', outline: 'none' }} />
              <div style={{ fontSize: 11, color: 'var(--muted)', marginTop: 4 }}>What you paid to acquire this lead (Thumbtack credit, ad cost, etc.)</div>
            </div>
            <div style={{ marginBottom: 16 }}>
              <label style={{ display: 'block', fontSize: 11, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.07em', color: 'var(--muted)', marginBottom: 5 }}>Notes</label>
              <textarea value={newLead.notes} onChange={e => setNewLead(p => ({ ...p, notes: e.target.value }))} rows={2}
                style={{ width: '100%', padding: '9px 12px', borderRadius: 9, border: '1.5px solid var(--border)', background: 'var(--bg)', color: 'var(--text)', fontSize: 14, fontFamily: 'inherit', outline: 'none', resize: 'vertical' }} />
            </div>
            <div style={{ display: 'flex', gap: 10 }}>
              <button onClick={() => setShowAdd(false)} disabled={saving} style={{ flex: 1, padding: '10px', borderRadius: 9, border: '1.5px solid var(--border)', background: 'transparent', cursor: saving ? 'wait' : 'pointer', fontFamily: 'inherit', fontWeight: 600, color: 'var(--text)' }}>Cancel</button>
              <button onClick={addLead} disabled={saving} style={{ flex: 2, padding: '10px', borderRadius: 9, background: 'linear-gradient(135deg,#14C6BF,#3DCC7E)', border: 'none', color: '#fff', cursor: saving ? 'wait' : 'pointer', fontFamily: 'inherit', fontWeight: 800, fontSize: 14, opacity: saving ? 0.7 : 1 }}>{saving ? 'Saving…' : 'Add Lead'}</button>
            </div>
          </div>
        </div>
      )}
    </div>
  );
}

// ─── JOBS BOARD ───
function JobsBoard({ jobs, onJobsChange, onNavigate }) {
  const [view, setView] = React.useState('list');
  const [filter, setFilter] = React.useState('all');
  const [photoJob, setPhotoJob] = React.useState(null);
  const [copiedFor, setCopiedFor] = React.useState(null);

  const filtered = jobs.filter(j => filter === 'all' ? true : j.status === filter);
  const statusColors = { confirmed: { bg: '#f0fdf4', color: '#16a34a', label: 'Confirmed' }, pending: { bg: '#eff6ff', color: '#2563eb', label: 'Pending' }, completed: { bg: '#f3f4f6', color: '#6b7280', label: 'Completed' }, cancelled: { bg: '#fef2f2', color: '#ef4444', label: 'Cancelled' } };

  const markComplete = async (id) => {
    try {
      await window.updateJob(id, { status: 'completed' });
      if (onJobsChange) onJobsChange();
    } catch (e) {
      alert('Failed to mark complete: ' + e.message);
    }
  };

  const copyPortalForJob = async (job) => {
    const ok = await _copyPortalLink(job.estimateUuid);
    if (ok) {
      setCopiedFor(job.id);
      setTimeout(() => setCopiedFor(null), 1800);
    }
  };

  return (
    <div>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 20 }}>
        <div>
          <h1 style={{ fontSize: 22, fontWeight: 900, fontFamily: 'Poppins', marginBottom: 2 }}>Jobs Board</h1>
          <p style={{ color: 'var(--muted)', fontSize: 14 }}>{jobs.filter(j => j.status !== 'completed').length} active · {jobs.filter(j => j.status === 'completed').length} completed</p>
        </div>
        <div style={{ display: 'flex', gap: 8 }}>
          <div style={{ display: 'flex', background: 'var(--bg)', borderRadius: 10, border: '1px solid var(--border)', overflow: 'hidden' }}>
            {['list', 'calendar'].map(v => (
              <button key={v} onClick={() => setView(v)} style={{ padding: '8px 14px', border: 'none', background: view === v ? 'var(--accent)' : 'transparent', color: view === v ? '#fff' : 'var(--muted)', cursor: 'pointer', fontFamily: 'inherit', fontWeight: 600, fontSize: 12, display: 'inline-flex', alignItems: 'center', gap: 6 }}>
                <Icon name={v === 'list' ? 'menu' : 'calendar'} size={13}/> {v === 'list' ? 'List' : 'Calendar'}
              </button>
            ))}
          </div>
          <button onClick={() => onNavigate && onNavigate('estimates')} style={{ padding: '9px 16px', borderRadius: 10, background: 'linear-gradient(135deg,#14C6BF,#3DCC7E)', color: '#fff', border: 'none', cursor: 'pointer', fontWeight: 700, fontSize: 13, fontFamily: 'inherit' }}>+ New Estimate</button>
        </div>
      </div>

      {/* Filter tabs */}
      <div style={{ display: 'flex', gap: 6, marginBottom: 16 }}>
        {[['all', 'All Jobs'], ['confirmed', 'Confirmed'], ['pending', 'Pending'], ['completed', 'Completed']].map(([val, lbl]) => (
          <button key={val} onClick={() => setFilter(val)}
            style={{ padding: '6px 14px', borderRadius: 20, border: '1.5px solid ' + (filter === val ? 'var(--accent)' : 'var(--border)'), background: filter === val ? 'var(--accent-light)' : 'transparent', color: filter === val ? 'var(--accent-dark)' : 'var(--muted)', fontFamily: 'inherit', fontWeight: 600, fontSize: 12, cursor: 'pointer' }}>
            {lbl}
          </button>
        ))}
      </div>

      {view === 'list' ? (
        <div style={{ background: 'var(--card)', borderRadius: 14, border: '1px solid var(--border)', overflow: 'hidden' }}>
          <table style={{ width: '100%', borderCollapse: 'collapse', fontSize: 13 }}>
            <thead>
              <tr style={{ background: 'var(--bg)' }}>
                {['Job #', 'Customer', 'Date & Time', 'Route', 'Crew', 'Truck', 'Value', 'Status', ''].map(h => (
                  <th key={h} style={{ padding: '11px 14px', textAlign: 'left', fontSize: 11, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.07em', color: 'var(--muted)', whiteSpace: 'nowrap' }}>{h}</th>
                ))}
              </tr>
            </thead>
            <tbody>
              {filtered.map((job, i) => {
                const s = statusColors[job.status] || statusColors.pending;
                const days = daysUntil(job.date);
                return (
                  <tr key={job.id} style={{ borderTop: '1px solid var(--border)', background: i % 2 === 0 ? 'transparent' : 'var(--bg)' }}>
                    <td style={{ padding: '12px 14px', fontWeight: 700, color: 'var(--accent)', whiteSpace: 'nowrap' }}>{job.number || `#${String(job.id).slice(0, 6)}`}</td>
                    <td style={{ padding: '12px 14px' }}>
                      <div style={{ fontWeight: 700 }}>{job.customer}</div>
                      <div style={{ fontSize: 11, color: 'var(--muted)' }}>{job.phone}</div>
                    </td>
                    <td style={{ padding: '12px 14px', whiteSpace: 'nowrap' }}>
                      <div style={{ fontWeight: 600 }}>{fmtShortDate(job.date)}</div>
                      <div style={{ fontSize: 11, color: days !== null && days <= 2 && job.status !== 'completed' ? '#ef4444' : 'var(--muted)' }}>
                        {job.time}{days !== null && job.status !== 'completed' ? ` · ${days === 0 ? 'Today' : days === 1 ? 'Tomorrow' : days < 0 ? Math.abs(days) + 'd ago' : 'in ' + days + 'd'}` : ''}
                      </div>
                    </td>
                    <td style={{ padding: '12px 14px' }}>
                      <div style={{ fontSize: 12 }}>{job.from}</div>
                      <div style={{ fontSize: 12, color: 'var(--muted)' }}>→ {job.to}</div>
                    </td>
                    <td style={{ padding: '12px 14px' }}>
                      {job.crew.map(c => (
                        <span key={c} style={{ display: 'inline-block', background: 'var(--accent-light)', color: 'var(--accent-dark)', borderRadius: 4, padding: '2px 6px', fontSize: 10, fontWeight: 600, marginRight: 3, marginBottom: 2 }}>{c}</span>
                      ))}
                    </td>
                    <td style={{ padding: '12px 14px', fontSize: 12, color: 'var(--muted)' }}>{job.truck}</td>
                    <td style={{ padding: '12px 14px', fontWeight: 800, color: 'var(--accent)' }}>${job.value.toLocaleString()}</td>
                    <td style={{ padding: '12px 14px' }}>
                      <span style={{ background: s.bg, color: s.color, padding: '4px 10px', borderRadius: 20, fontSize: 11, fontWeight: 700 }}>{s.label}</span>
                    </td>
                    <td style={{ padding: '12px 14px' }}>
                      <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
                        {job.status !== 'completed' && onNavigate && (
                          <button onClick={(e) => { e.stopPropagation(); localStorage.setItem('crm_onjob_selected', job.id); onNavigate('onjob'); }} style={{ padding: '5px 10px', borderRadius: 7, border: 'none', background: 'linear-gradient(135deg,#14C6BF,#3DCC7E)', color: '#fff', cursor: 'pointer', fontSize: 11, fontWeight: 800, fontFamily: 'inherit', whiteSpace: 'nowrap', display: 'inline-flex', alignItems: 'center', gap: 4 }}><Icon name="mobile" size={12}/> On-Job</button>
                        )}
                        {/* Customer portal: open the customer's view, or copy the link to send. */}
                        <button onClick={(e) => { e.stopPropagation(); _openPortal(job.estimateUuid); }}
                          disabled={!job.estimateUuid}
                          title={job.estimateUuid ? 'Open the customer portal in a new tab' : 'No estimate linked to this job yet'}
                          style={{ padding: '5px 10px', borderRadius: 7, border: '1.5px solid var(--border)', background: 'transparent', color: job.estimateUuid ? 'var(--accent-dark)' : 'var(--muted)', cursor: job.estimateUuid ? 'pointer' : 'not-allowed', fontSize: 11, fontWeight: 700, fontFamily: 'inherit', whiteSpace: 'nowrap', display: 'inline-flex', alignItems: 'center', gap: 4, opacity: job.estimateUuid ? 1 : 0.5 }}>
                          <Icon name="eye" size={12}/> Portal
                        </button>
                        <button onClick={(e) => { e.stopPropagation(); copyPortalForJob(job); }}
                          disabled={!job.estimateUuid}
                          title={job.estimateUuid ? 'Copy portal link to clipboard' : 'No estimate linked to this job yet'}
                          style={{ padding: '5px 10px', borderRadius: 7, border: '1.5px solid var(--border)', background: copiedFor === job.id ? '#ecfdf5' : 'transparent', color: copiedFor === job.id ? '#16a34a' : (job.estimateUuid ? 'var(--accent-dark)' : 'var(--muted)'), cursor: job.estimateUuid ? 'pointer' : 'not-allowed', fontSize: 11, fontWeight: 700, fontFamily: 'inherit', whiteSpace: 'nowrap', display: 'inline-flex', alignItems: 'center', gap: 4, opacity: job.estimateUuid ? 1 : 0.5 }}>
                          <Icon name={copiedFor === job.id ? 'check' : 'send'} size={12}/> {copiedFor === job.id ? 'Copied' : 'Copy Link'}
                        </button>
                        <button onClick={(e) => { e.stopPropagation(); setPhotoJob(job); }} style={{ padding: '5px 10px', borderRadius: 7, border: '1.5px solid var(--border)', background: 'transparent', color: 'var(--muted)', cursor: 'pointer', fontSize: 11, fontWeight: 700, fontFamily: 'inherit', whiteSpace: 'nowrap', display: 'inline-flex', alignItems: 'center', gap: 4 }}><Icon name="eye" size={12}/> Photos</button>
                        {job.status !== 'completed' && (
                          <button onClick={() => markComplete(job.id)} style={{ padding: '5px 10px', borderRadius: 7, border: 'none', background: '#f0fdf4', color: '#16a34a', cursor: 'pointer', fontSize: 11, fontWeight: 700, fontFamily: 'inherit', whiteSpace: 'nowrap', display: 'inline-flex', alignItems: 'center', gap: 4 }}><Icon name="check" size={12}/> Done</button>
                        )}
                      </div>
                    </td>
                  </tr>
                );
              })}
            </tbody>
          </table>
          {filtered.length === 0 && <div style={{ textAlign: 'center', padding: '40px', color: 'var(--muted)' }}>No jobs found</div>}
        </div>
      ) : (
        <CalendarView jobs={jobs} />
      )}

      {/* Photo drawer */}
      {photoJob && (
        <div style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,.4)', zIndex: 100, display: 'flex', justifyContent: 'flex-end' }} onClick={() => setPhotoJob(null)}>
          <div style={{ width: 560, maxWidth: '100%', background: 'var(--card)', height: '100%', overflowY: 'auto', padding: 24, boxShadow: '-8px 0 40px rgba(0,0,0,.15)' }} onClick={e => e.stopPropagation()}>
            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: 16 }}>
              <div>
                <div style={{ fontSize: 11, color: 'var(--muted)', fontWeight: 700, textTransform: 'uppercase', letterSpacing: '.08em' }}>Job {photoJob.number || `#${String(photoJob.id).slice(0, 6)}`}</div>
                <h2 style={{ fontSize: 19, fontWeight: 900, fontFamily: 'Poppins' }}>{photoJob.customer} — Photos</h2>
                <div style={{ fontSize: 12, color: 'var(--muted)', marginTop: 2 }}>{photoJob.from} → {photoJob.to} · {fmtShortDate(photoJob.date)}</div>
              </div>
              <button onClick={() => setPhotoJob(null)} style={{ background: 'none', border: 'none', fontSize: 22, cursor: 'pointer', color: 'var(--muted)' }}>×</button>
            </div>
            {typeof JobPhotoGrid !== 'undefined' && <JobPhotoGrid jobId={photoJob.id} label="Photos & Documentation" />}
          </div>
        </div>
      )}
    </div>
  );
}

function CalendarView({ jobs }) {
  const now = new Date();
  const [month, setMonth] = React.useState(now.getMonth());
  const [year, setYear] = React.useState(now.getFullYear());
  const firstDay = new Date(year, month, 1).getDay();
  const daysInMonth = new Date(year, month + 1, 0).getDate();
  const monthNames = ['January','February','March','April','May','June','July','August','September','October','November','December'];
  const jobsByDate = {};
  jobs.forEach(j => { if (!jobsByDate[j.date]) jobsByDate[j.date] = []; jobsByDate[j.date].push(j); });

  const cells = [];
  for (let i = 0; i < firstDay; i++) cells.push(null);
  for (let d = 1; d <= daysInMonth; d++) cells.push(d);

  const todayStr = now.toISOString().split('T')[0];

  return (
    <div style={{ background: 'var(--card)', borderRadius: 14, border: '1px solid var(--border)', padding: 20 }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16 }}>
        <button onClick={() => { if (month === 0) { setMonth(11); setYear(y => y - 1); } else setMonth(m => m - 1); }} style={{ padding: '6px 12px', borderRadius: 8, border: '1px solid var(--border)', background: 'transparent', cursor: 'pointer', fontFamily: 'inherit', fontSize: 14 }}>←</button>
        <div style={{ fontWeight: 800, fontSize: 16, fontFamily: 'Poppins' }}>{monthNames[month]} {year}</div>
        <button onClick={() => { if (month === 11) { setMonth(0); setYear(y => y + 1); } else setMonth(m => m + 1); }} style={{ padding: '6px 12px', borderRadius: 8, border: '1px solid var(--border)', background: 'transparent', cursor: 'pointer', fontFamily: 'inherit', fontSize: 14 }}>→</button>
      </div>
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(7, 1fr)', gap: 4 }}>
        {['Sun','Mon','Tue','Wed','Thu','Fri','Sat'].map(d => (
          <div key={d} style={{ textAlign: 'center', fontSize: 11, fontWeight: 700, color: 'var(--muted)', padding: '4px 0', textTransform: 'uppercase', letterSpacing: '0.06em' }}>{d}</div>
        ))}
        {cells.map((day, i) => {
          if (!day) return <div key={'e' + i} />;
          const dateStr = `${year}-${String(month + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
          const dayJobs = jobsByDate[dateStr] || [];
          const isToday = dateStr === todayStr;
          return (
            <div key={day} style={{ minHeight: 72, padding: 6, borderRadius: 8, background: isToday ? 'var(--accent-light)' : 'var(--bg)', border: `1.5px solid ${isToday ? 'var(--accent)' : 'var(--border)'}` }}>
              <div style={{ fontSize: 12, fontWeight: isToday ? 900 : 500, color: isToday ? 'var(--accent)' : 'var(--text)', marginBottom: 3 }}>{day}</div>
              {dayJobs.map(j => (
                <div key={j.id} style={{ fontSize: 9, background: j.status === 'completed' ? '#e5e7eb' : 'var(--accent)', color: j.status === 'completed' ? '#6b7280' : '#fff', borderRadius: 3, padding: '2px 4px', marginBottom: 2, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', fontWeight: 600 }}>
                  {j.time} {j.customer.split(' ')[0]}
                </div>
              ))}
            </div>
          );
        })}
      </div>
    </div>
  );
}

Object.assign(window, { LeadsPipeline, JobsBoard });
