// Reports — CRM-standard reports with marketing focus (sources, geography, conversion)
const REPORT_DEFS = [
  { key: 'marketing', label: 'Marketing', desc: 'Lead sources, channels & geography' },
  { key: 'conversion', label: 'Conversion', desc: 'Funnel & stage progression' },
  { key: 'sales', label: 'Sales performance', desc: 'Designer leaderboard & win rates' },
  { key: 'showrooms', label: 'Showrooms', desc: 'Side-by-side location comparison' },
  { key: 'activity', label: 'Activity', desc: 'Calls, emails, meetings per designer' },
  { key: 'lost', label: 'Lost jobs', desc: 'Loss rate & top reasons for lost jobs' },
];

// Shared scope computation — used by the page AND the export dialog so they stay in sync.
// Returns role-restricted + showroom/designer/date-filtered data, plus the reps/showrooms
// that actually have data in scope (so empty rows are hidden once a filter is applied).
function computeReportScope({ contacts, deals, reps, showrooms, currentUser, showroom, rep, period, customFrom, customTo }) {
  const isSales = currentUser.role === 'sales_designer';
  let aC = contacts, aD = deals;

  // Role access
  if (isSales) {
    const myName = reps.find(r => r.id === currentUser.repId)?.name || currentUser.name;
    aC = aC.filter(c => currentUser.works.includes(c.homeShowroom) && c.owner === myName);
    aD = aD.filter(d => currentUser.works.includes(d.showroom) && d.rep === currentUser.repId);
  }
  // Showroom
  if (showroom && showroom !== 'all') {
    aC = aC.filter(c => c.homeShowroom === showroom);
    aD = aD.filter(d => d.showroom === showroom);
  }
  // Designer options depend on showroom
  const repOptions = (showroom && showroom !== 'all' ? reps.filter(r => (r.works || []).includes(showroom)) : reps)
    .filter(r => !isSales || r.id === currentUser.repId);
  // Designer
  const effRep = isSales ? currentUser.repId : (rep || 'all');
  if (effRep && effRep !== 'all') {
    const rName = reps.find(r => r.id === effRep)?.name;
    aC = aC.filter(c => c.owner === rName);
    aD = aD.filter(d => d.rep === effRep);
  }
  // Date range
  const today = new Date();
  const inPeriod = (dateStr) => {
    if (period === 'custom') {
      if (!dateStr) return false;
      if (customFrom && dateStr < customFrom) return false;
      if (customTo && dateStr > customTo) return false;
      return true;
    }
    if (period === 'all') return true;
    if (!dateStr) return false;
    const d = new Date(dateStr);
    if (isNaN(d)) return false;
    const mb = period === '1mo' ? 1 : period === '3mo' ? 3 : period === '12mo' ? 12 : 60;
    const cutoff = new Date(today); cutoff.setMonth(cutoff.getMonth() - mb);
    return d >= cutoff;
  };
  // Contacts may lack a firstContact date (e.g. imported from Supabase without that field).
  // Fall back to lastContact, then include undated contacts in every period so they're never invisible.
  const periodContacts = aC.filter(c => {
    const d = c.firstContact || c.lastContact;
    if (!d) return true;
    return inPeriod(d);
  });
  const periodDeals = aD.filter(d => inPeriod(d.created));

  // Which reps / showrooms actually belong in scope (so we don't show empty rows once filtered)
  const scopeReps = (effRep && effRep !== 'all')
    ? reps.filter(r => r.id === effRep)
    : (repOptions.length ? repOptions : reps);

  const activeIds = new Set();
  periodContacts.forEach(c => activeIds.add(c.homeShowroom));
  periodDeals.forEach(d => activeIds.add(d.showroom));
  let scopeShowrooms = showrooms;
  if (showroom && showroom !== 'all') scopeShowrooms = showrooms.filter(s => s.id === showroom);
  else if (effRep && effRep !== 'all') scopeShowrooms = showrooms.filter(s => activeIds.has(s.id));

  return { accessContacts: aC, accessDeals: aD, periodContacts, periodDeals, repOptions, scopeReps, scopeShowrooms, effRep, isSales };
}

function buildReportMeta({ reps, showrooms, currentUser, showroom, effRep, period, customFrom, customTo, periodContacts, periodDeals }) {
  const T = window.T || ((k) => k);
  const isSales = currentUser.role === 'sales_designer';
  const PERIOD_LABELS = { '1mo': 'Last 30 days', '3mo': 'Last 3 months', '12mo': 'Last 12 months', '5y': 'Last 5 years', all: 'All time' };
  const showroomLabel = (!showroom || showroom === 'all')
    ? (isSales ? 'My ' + T('locs_lc') : 'All ' + T('locs_lc'))
    : (showrooms.find(s => s.id === showroom)?.name || showroom);
  const repLabel = (effRep && effRep !== 'all')
    ? (reps.find(r => r.id === effRep)?.name || effRep)
    : 'All ' + T('reps_lc');
  const periodLabel = period === 'custom' ? `${customFrom || '…'} → ${customTo || '…'}` : PERIOD_LABELS[period];
  return {
    showroomLabel, repLabel, periodLabel,
    generatedStr: new Date().toLocaleString('en-AU', { day: 'numeric', month: 'short', year: 'numeric', hour: '2-digit', minute: '2-digit' }),
    contacts: periodContacts.length, deals: periodDeals.length,
  };
}

function Reports({ contacts, deals, reps, showrooms, currentUser, showroomScope, openDeal }) {
  const T = window.T || ((k) => k);
  const [tab, setTab] = React.useState('marketing');
  const [period, setPeriod] = React.useState('12mo');
  const [customFrom, setCustomFrom] = React.useState('');
  const [customTo, setCustomTo] = React.useState('');
  const [filterShowroom, setFilterShowroom] = React.useState(showroomScope || 'all');
  const [filterRep, setFilterRep] = React.useState('all');
  const [exportFmt, setExportFmt] = React.useState(null); // null = closed, otherwise initial format

  React.useEffect(() => { setFilterShowroom(showroomScope || 'all'); }, [showroomScope]);

  const isSales = currentUser.role === 'sales_designer';
  const data = { contacts, deals, reps, showrooms };

  const scope = computeReportScope({ ...data, currentUser, showroom: filterShowroom, rep: filterRep, period, customFrom, customTo });
  const meta = buildReportMeta({ ...data, currentUser, showroom: filterShowroom, effRep: scope.effRep, period, customFrom, customTo, periodContacts: scope.periodContacts, periodDeals: scope.periodDeals });

  const showroomOptions = isSales ? showrooms.filter(s => currentUser.works.includes(s.id)) : showrooms;

  return (
    <div>
      {/* Tabs + actions */}
      <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 12 }}>
        <div className="seg seg-reports">
          {REPORT_DEFS.map(d => (
            <button key={d.key} className={tab === d.key ? 'on' : ''} onClick={() => setTab(d.key)}>{d.label}</button>
          ))}
        </div>
        <div style={{ marginLeft: 'auto', display: 'flex', alignItems: 'center', gap: 8 }}>
          <button className="btn sm" onClick={() => setExportFmt('pdf')} title="Set parameters and open a print-ready document">
            <Icon name="book" size={12}/> Print
          </button>
          <button className="btn sm primary" onClick={() => setExportFmt('excel')} title="Choose reports, parameters & format to download">
            <Icon name="download" size={12}/> Export
          </button>
        </div>
      </div>

      {/* Parameters bar */}
      <div style={{ display: 'flex', alignItems: 'center', gap: 10, flexWrap: 'wrap',
        padding: '10px 14px', background: 'var(--surface-2)', border: '1px solid var(--border)',
        borderRadius: 'var(--r-md)', marginBottom: 18 }}>
        <span style={{ display: 'inline-flex', alignItems: 'center', gap: 6, fontSize: 10.5, textTransform: 'uppercase',
          letterSpacing: '0.07em', color: 'var(--ink-3)', fontWeight: 600 }}>
          <Icon name="sliders" size={13}/> Parameters
        </span>

        {/* Showroom */}
        <label style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}>
          <span style={{ fontSize: 11, color: 'var(--ink-3)' }}>{T('location')}</span>
          {isSales && showroomOptions.length <= 1 ? (
            <span className="pill"><span className={`ss-tab-dot ${showroomOptions[0]?.id || ''}`}></span>{showroomOptions[0]?.name || '—'}</span>
          ) : (
            <select className="sel" value={filterShowroom} onChange={(e) => { setFilterShowroom(e.target.value); setFilterRep('all'); }}>
              <option value="all">{isSales ? `My ${T('locs_lc')}` : `All ${T('locs_lc')}`}</option>
              {showroomOptions.map(s => <option key={s.id} value={s.id}>{s.name} · {s.state}</option>)}
            </select>
          )}
        </label>

        {/* Designer */}
        {!isSales && (
          <label style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}>
            <span style={{ fontSize: 11, color: 'var(--ink-3)' }}>{T('rep')}</span>
            <select className="sel" value={filterRep} onChange={(e) => setFilterRep(e.target.value)}>
              <option value="all">All {T('reps_lc')}</option>
              {scope.repOptions.map(r => <option key={r.id} value={r.id}>{r.name}</option>)}
            </select>
          </label>
        )}

        {/* Date range */}
        <label style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}>
          <span style={{ fontSize: 11, color: 'var(--ink-3)' }}>Date range</span>
          <select className="sel" value={period} onChange={(e) => setPeriod(e.target.value)}>
            <option value="1mo">Last 30 days</option>
            <option value="3mo">Last 3 months</option>
            <option value="12mo">Last 12 months</option>
            <option value="5y">Last 5 years</option>
            <option value="all">All time</option>
            <option value="custom">Custom range…</option>
          </select>
        </label>
        {period === 'custom' && (
          <React.Fragment>
            <input type="date" className="sel" value={customFrom} onChange={(e) => setCustomFrom(e.target.value)} style={{ fontSize: 12 }} title="From"/>
            <span style={{ fontSize: 11, color: 'var(--ink-3)' }}>to</span>
            <input type="date" className="sel" value={customTo} onChange={(e) => setCustomTo(e.target.value)} style={{ fontSize: 12 }} title="To"/>
          </React.Fragment>
        )}

        {(filterShowroom !== 'all' || filterRep !== 'all' || period !== '12mo') && !isSales && (
          <button className="btn sm" style={{ padding: '4px 9px' }}
            onClick={() => { setFilterShowroom('all'); setFilterRep('all'); setPeriod('12mo'); }}>
            Reset
          </button>
        )}

        <span style={{ marginLeft: 'auto', fontSize: 11, color: 'var(--ink-3)' }}>
          <span className="num" style={{ color: 'var(--ink-2)', fontWeight: 500 }}>{meta.contacts}</span> contacts ·{' '}
          <span className="num" style={{ color: 'var(--ink-2)', fontWeight: 500 }}>{meta.deals}</span> deals in range
        </span>
      </div>

      {tab === 'marketing' && <MarketingReport contacts={scope.periodContacts} deals={scope.periodDeals} showrooms={showrooms}/>}
      {tab === 'conversion' && <ConversionReport contacts={scope.accessContacts} deals={scope.accessDeals} reps={scope.scopeReps} showrooms={scope.scopeShowrooms} period={period}/>}
      {tab === 'sales' && <SalesReport contacts={scope.periodContacts} deals={scope.periodDeals} reps={scope.scopeReps} showrooms={scope.scopeShowrooms} showroomScope={filterShowroom}/>}
      {tab === 'showrooms' && <ShowroomReport contacts={scope.periodContacts} deals={scope.periodDeals} showrooms={scope.scopeShowrooms}/>}
      {tab === 'activity' && <ActivityReport contacts={scope.periodContacts} deals={scope.periodDeals} reps={scope.scopeReps}/>}
      {tab === 'lost' && <LostReport deals={scope.periodDeals} reps={scope.scopeReps} showrooms={scope.scopeShowrooms} openDeal={openDeal}/>}

      {exportFmt && (
        <ReportExportModal
          data={data}
          currentUser={currentUser}
          defaultTab={tab}
          initFormat={exportFmt}
          init={{ showroom: filterShowroom, rep: filterRep, period, customFrom, customTo }}
          onClose={() => setExportFmt(null)}
        />
      )}
    </div>
  );
}

// === Marketing: where do customers come from, where do they live ===
function MarketingReport({ contacts, deals, showrooms }) {
  // Source breakdown
  const sourceAgg = {};
  for (const c of contacts) {
    const s = c.primarySource || '— Not recorded —';
    if (!sourceAgg[s]) sourceAgg[s] = { total: 0, qualified: 0, sold: 0, value: 0 };
    sourceAgg[s].total++;
    if (['qualified','in_pipeline','customer_active','past_customer','lost_quote'].includes(c.lifecycle)) sourceAgg[s].qualified++;
    if (['past_customer','customer_active','dormant_past_customer'].includes(c.lifecycle)) {
      sourceAgg[s].sold++;
      sourceAgg[s].value += c.ltv || 0;
    }
  }
  const sourceRows = Object.entries(sourceAgg).sort((a, b) => b[1].total - a[1].total);
  const maxSource = sourceRows[0]?.[1].total || 1;

  // Means breakdown
  const meansAgg = {};
  for (const c of contacts) {
    const m = c.primaryMeans || '— Not recorded —';
    if (!meansAgg[m]) meansAgg[m] = { total: 0, sold: 0 };
    meansAgg[m].total++;
    if (['past_customer','customer_active','dormant_past_customer'].includes(c.lifecycle)) meansAgg[m].sold++;
  }
  const meansRows = Object.entries(meansAgg).sort((a, b) => b[1].total - a[1].total);
  const maxMeans = meansRows[0]?.[1].total || 1;

  // Geography — suburb breakdown
  const suburbAgg = {};
  for (const c of contacts) {
    const su = (c.suburb || '— Not recorded —').trim();
    if (!suburbAgg[su]) suburbAgg[su] = { total: 0, sold: 0, value: 0, showrooms: {} };
    suburbAgg[su].total++;
    if (['past_customer','customer_active','dormant_past_customer'].includes(c.lifecycle)) {
      suburbAgg[su].sold++;
      suburbAgg[su].value += c.ltv || 0;
    }
    suburbAgg[su].showrooms[c.homeShowroom] = (suburbAgg[su].showrooms[c.homeShowroom] || 0) + 1;
  }
  const suburbRows = Object.entries(suburbAgg)
    .filter(([s]) => s !== '— Not recorded —')
    .sort((a, b) => b[1].total - a[1].total)
    .slice(0, 20);
  const maxSuburb = suburbRows[0]?.[1].total || 1;

  return (
    <div>
      <div className="grid-kpis">
        <Kpi label="Total contacts" value={fmt.num(contacts.length)} foot={`${contacts.filter(c => ['past_customer','customer_active','dormant_past_customer'].includes(c.lifecycle)).length} became customers`} deltaPct={0.058}/>
        <Kpi label="Top source" value={sourceRows[0]?.[0] || '—'} foot={`${sourceRows[0]?.[1].total || 0} contacts · ${fmt.pct((sourceRows[0]?.[1].sold || 0) / (sourceRows[0]?.[1].total || 1), 0)} convert`} deltaPct={0.04}/>
        <Kpi label="Top channel" value={meansRows[0]?.[0] || '—'} foot={`${meansRows[0]?.[1].total || 0} contacts`} deltaPct={0.02}/>
        <Kpi label="Top suburb" value={suburbRows[0]?.[0] || '—'} foot={`${suburbRows[0]?.[1].total || 0} contacts · ${suburbRows[0]?.[1].sold || 0} sold`} deltaPct={0.018}/>
      </div>

      <div className="grid-2">
        <div className="card">
          <div className="card-hd">
            <div>
              <div className="card-title">Lead source performance</div>
              <div className="card-sub">Where contacts heard about KUB · ranked by volume</div>
            </div>
          </div>
          <div className="tbl-wrap">
            <table className="tbl">
              <thead>
                <tr>
                  <th>Source</th>
                  <th className="num-col">Contacts</th>
                  <th className="num-col">Sold</th>
                  <th className="num-col">Conv. rate</th>
                  <th className="num-col">Revenue</th>
                </tr>
              </thead>
              <tbody>
                {sourceRows.map(([src, v]) => {
                  const conv = v.total ? v.sold / v.total : 0;
                  return (
                    <tr key={src}>
                      <td>
                        <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                          <span className="dot" style={{ width: 8, height: 8, borderRadius: 4, background: sourceColor(src), display: 'inline-block' }}></span>
                          <div style={{ flex: 1, minWidth: 0 }}>
                            <div style={{ fontSize: 12.5, fontWeight: 500 }}>{src}</div>
                            <div style={{ height: 4, background: 'var(--surface-3)', borderRadius: 2, marginTop: 4 }}>
                              <div style={{ width: (v.total / maxSource * 100) + '%', height: '100%', background: sourceColor(src), borderRadius: 2 }}></div>
                            </div>
                          </div>
                        </div>
                      </td>
                      <td className="num-col"><span className="num">{v.total}</span></td>
                      <td className="num-col"><span className="num" style={{ color: 'var(--won)', fontWeight: v.sold > 0 ? 500 : 400 }}>{v.sold}</span></td>
                      <td className="num-col">
                        <span className="num" style={{ color: conv > 0.2 ? 'var(--won)' : conv > 0.1 ? 'var(--ink-2)' : 'var(--ink-3)' }}>
                          {fmt.pct(conv, 0)}
                        </span>
                      </td>
                      <td className="num-col">
                        <span className="num">{v.value > 0 ? fmt.money(v.value, true) : '—'}</span>
                      </td>
                    </tr>
                  );
                })}
              </tbody>
            </table>
          </div>
        </div>

        <div className="card">
          <div className="card-hd">
            <div>
              <div className="card-title">First-contact channel</div>
              <div className="card-sub">How customers reached out</div>
            </div>
          </div>
          <div className="card-pad">
            {meansRows.map(([m, v]) => {
              const conv = v.total ? v.sold / v.total : 0;
              return (
                <div key={m} className="chart-row" style={{ gridTemplateColumns: '120px 1fr 80px' }}>
                  <div className="lbl">{m}</div>
                  <div className="bar-wrap" style={{ height: 22 }}>
                    <div className="bar" style={{ width: (v.total / maxMeans * 100) + '%', background: 'var(--ink-2)' }}>
                      {v.total / maxMeans > 0.2 && <span className="num">{v.total}</span>}
                    </div>
                  </div>
                  <div className="val"><span className="num">{fmt.pct(conv, 0)}</span> conv</div>
                </div>
              );
            })}
          </div>
        </div>
      </div>

      <div className="card">
        <div className="card-hd">
          <div>
            <div className="card-title">Top suburbs by contact volume</div>
            <div className="card-sub">Where your customers live · target marketing efforts here</div>
          </div>
        </div>
        <div className="tbl-wrap" style={{ maxHeight: 480, overflowY: 'auto' }}>
          <table className="tbl">
            <thead>
              <tr>
                <th>#</th>
                <th>Suburb</th>
                <th>Closest showroom</th>
                <th className="num-col">Contacts</th>
                <th className="num-col">Sold</th>
                <th className="num-col">Conv. rate</th>
                <th className="num-col">Total revenue</th>
                <th>Volume</th>
              </tr>
            </thead>
            <tbody>
              {suburbRows.map(([suburb, v], i) => {
                const conv = v.total ? v.sold / v.total : 0;
                const closestId = Object.entries(v.showrooms).sort((a, b) => b[1] - a[1])[0]?.[0];
                const closest = showrooms.find(s => s.id === closestId);
                return (
                  <tr key={suburb}>
                    <td className="mono" style={{ fontSize: 11, color: 'var(--ink-3)' }}>{String(i + 1).padStart(2, '0')}</td>
                    <td style={{ fontWeight: 500 }}>{suburb}</td>
                    <td>
                      {closest && (
                        <span className="pill">
                          <span className={`ss-tab-dot ${closest.id}`}></span>
                          {closest.name}
                        </span>
                      )}
                    </td>
                    <td className="num-col"><span className="num">{v.total}</span></td>
                    <td className="num-col"><span className="num" style={{ color: 'var(--won)', fontWeight: v.sold > 0 ? 500 : 400 }}>{v.sold}</span></td>
                    <td className="num-col"><span className="num">{fmt.pct(conv, 0)}</span></td>
                    <td className="num-col"><span className="num">{v.value > 0 ? fmt.money(v.value, true) : '—'}</span></td>
                    <td style={{ width: 140 }}>
                      <div style={{ height: 6, background: 'var(--surface-3)', borderRadius: 3 }}>
                        <div style={{ width: (v.total / maxSuburb * 100) + '%', height: '100%', background: 'var(--accent)', borderRadius: 3 }}></div>
                      </div>
                    </td>
                  </tr>
                );
              })}
            </tbody>
          </table>
        </div>
        {suburbRows.length === 20 && (
          <div style={{ padding: '8px 14px', fontSize: 11, color: 'var(--ink-3)', textAlign: 'center', borderTop: '1px solid var(--border)' }}>
            Showing top 20 suburbs · use Export for the full list
          </div>
        )}
      </div>
    </div>
  );
}

// === Sales: designer performance, conversion funnel ===
function SalesReport({ contacts, deals, reps, showrooms, showroomScope }) {
  const scopedReps = showroomScope === 'all' ? reps : reps.filter(r => r.works.includes(showroomScope));
  const agg = scopedReps.map(r => {
    const repDeals = deals.filter(d => d.rep === r.id);
    const repContacts = contacts.filter(c => c.owner === r.name);
    const won = repDeals.filter(d => d.stage === 'sold');
    const lost = repDeals.filter(d => d.stage === 'lost');
    const active = repDeals.filter(d => d.stage !== 'sold' && d.stage !== 'lost');
    const wonVal = won.reduce((s, d) => s + d.value, 0);
    const closed = won.length + lost.length;
    return {
      ...r,
      contactCount: repContacts.length,
      wonVal, wonCount: won.length, lostCount: lost.length, activeCount: active.length,
      activeVal: active.reduce((s, d) => s + d.value, 0),
      winRate: closed ? won.length / closed : 0,
      avgDeal: won.length ? wonVal / won.length : 0,
    };
  }).sort((a, b) => b.wonVal - a.wonVal);

  // Funnel
  const totalContacts = contacts.length;
  const qualified = contacts.filter(c => ['qualified','in_pipeline','customer_active','past_customer','lost_quote'].includes(c.lifecycle)).length;
  const quoted = contacts.filter(c => (c.dealCount || 0) > 0 || ['in_pipeline','customer_active','past_customer','dormant_customer_active','dormant_past_customer'].includes(c.lifecycle)).length;
  const sold = contacts.filter(c => ['past_customer','customer_active','dormant_past_customer'].includes(c.lifecycle)).length;

  const funnel = [
    { id: 'contacts', label: 'Total Contacts', count: totalContacts, prevCount: null },
    { id: 'qualified', label: 'Qualified', count: qualified, prevCount: totalContacts },
    { id: 'quoted', label: 'Quoted', count: quoted, prevCount: qualified },
    { id: 'sold', label: 'Sold', count: sold, prevCount: quoted },
  ];

  return (
    <div>
      <div className="grid-kpis">
        <Kpi label="Total contacts" value={fmt.num(totalContacts)} foot="entered the funnel" deltaPct={0.04}/>
        <Kpi label="Qualified" value={fmt.num(qualified)} foot={`${fmt.pct(qualified / Math.max(totalContacts, 1), 0)} of contacts`} deltaPct={0.02}/>
        <Kpi label="Quoted" value={fmt.num(quoted)} foot={`${fmt.pct(quoted / Math.max(qualified, 1), 0)} of qualified`} deltaPct={0.01}/>
        <Kpi label="Sold" value={fmt.num(sold)} foot={`${fmt.pct(sold / Math.max(quoted, 1), 0)} of quoted`} deltaPct={0.03}/>
      </div>

      <div className="grid-2">
        <div className="card">
          <div className="card-hd">
            <div>
              <div className="card-title">Conversion funnel</div>
              <div className="card-sub">From first contact to closed sale</div>
            </div>
          </div>
          <div className="card-pad">
            <div className="funnel">
              {funnel.map((f, i) => {
                const w = (f.count / Math.max(totalContacts, 1)) * 100;
                const conv = i === 0 ? null : f.count / Math.max(f.prevCount, 1);
                return (
                  <div className="funnel-row" key={f.id}>
                    <div className="lbl">{f.label}</div>
                    <div className="funnel-bar-wrap">
                      <div className="funnel-bar" style={{ width: Math.max(w, 22) + '%', background: f.id === 'sold' ? 'var(--won)' : f.id === 'quoted' ? 'var(--accent)' : 'var(--ink-2)' }}>
                        <span className="num">{f.count}</span>
                        {conv !== null && <span style={{ opacity: 0.8, fontSize: 11 }}>{fmt.pct(conv, 0)}</span>}
                      </div>
                    </div>
                    <div className="conv">
                      {i === 0 ? 'Start' : `${fmt.pct(f.count / Math.max(totalContacts, 1), 0)} of all`}
                    </div>
                  </div>
                );
              })}
            </div>
          </div>
        </div>

        <div className="card">
          <div className="card-hd">
            <div>
              <div className="card-title">Designer leaderboard</div>
              <div className="card-sub">Closed-won value, period to date</div>
            </div>
          </div>
          <div className="card-pad">
            {agg.slice(0, 10).map((r, i) => {
              const max = Math.max(...agg.map(x => x.wonVal), 1);
              return (
                <div className="chart-row" key={r.id} style={{ gridTemplateColumns: '32px 110px 1fr 70px' }}>
                  <div className="mono" style={{ color: 'var(--ink-3)' }}>#{i + 1}</div>
                  <div className="lbl">
                    <Avatar name={r.name} sz="sm"/>
                    <span style={{ fontSize: 12.5 }}>{r.name}</span>
                  </div>
                  <div className="bar-wrap" style={{ height: 22 }}>
                    <div className="bar accent" style={{ width: (r.wonVal / max * 100) + '%' }}>
                      {r.wonVal / max > 0.2 && <span>{fmt.money(r.wonVal, true)}</span>}
                    </div>
                  </div>
                  <div className="val"><span className="num">{r.wonCount}</span> wins</div>
                </div>
              );
            })}
          </div>
        </div>
      </div>

      <div className="card">
        <div className="card-hd">
          <div>
            <div className="card-title">Designer performance detail</div>
            <div className="card-sub">Per-rep funnel · selected period</div>
          </div>
        </div>
        <div className="tbl-wrap">
          <table className="tbl">
            <thead>
              <tr>
                <th>Designer</th>
                <th className="num-col">Contacts owned</th>
                <th className="num-col">Active quotes</th>
                <th className="num-col">Active $</th>
                <th className="num-col">Wins</th>
                <th className="num-col">Closed-won $</th>
                <th className="num-col">Lost</th>
                <th className="num-col">Win rate</th>
                <th className="num-col">Avg deal</th>
              </tr>
            </thead>
            <tbody>
              {agg.map(r => (
                <tr key={r.id}>
                  <td>
                    <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                      <Avatar name={r.name} sz="sm"/>
                      <span style={{ fontWeight: 500 }}>{r.name}</span>
                    </div>
                  </td>
                  <td className="num-col"><span className="num">{r.contactCount}</span></td>
                  <td className="num-col"><span className="num">{r.activeCount}</span></td>
                  <td className="num-col"><span className="num">{fmt.money(r.activeVal, true)}</span></td>
                  <td className="num-col"><span className="num" style={{ color: 'var(--won)', fontWeight: 500 }}>{r.wonCount}</span></td>
                  <td className="num-col"><span className="num" style={{ fontWeight: 500 }}>{fmt.money(r.wonVal, true)}</span></td>
                  <td className="num-col"><span className="num" style={{ color: 'var(--lost)' }}>{r.lostCount}</span></td>
                  <td className="num-col"><span className="num">{fmt.pct(r.winRate, 0)}</span></td>
                  <td className="num-col"><span className="num">{r.wonCount > 0 ? fmt.money(r.avgDeal, true) : '—'}</span></td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      </div>
    </div>
  );
}

// === Showrooms ===
function ShowroomReport({ contacts, deals, showrooms }) {
  const agg = showrooms.map(s => {
    const sContacts = contacts.filter(c => c.homeShowroom === s.id);
    const sDeals = deals.filter(d => d.showroom === s.id);
    const sold = sDeals.filter(d => d.stage === 'sold');
    const wonVal = sold.reduce((sm, d) => sm + d.value, 0);
    return {
      ...s,
      contactCount: sContacts.length,
      newLeads: sContacts.filter(c => c.lifecycle === 'new_lead').length,
      qualified: sContacts.filter(c => c.lifecycle === 'qualified').length,
      inPipeline: sContacts.filter(c => c.lifecycle === 'in_pipeline').length,
      customers: sContacts.filter(c => ['past_customer','customer_active','dormant_past_customer'].includes(c.lifecycle)).length,
      dormant: sContacts.filter(c => c.lifecycle.startsWith('dormant_')).length,
      dealCount: sDeals.length,
      wonVal, wonCount: sold.length,
    };
  });
  const maxContacts = Math.max(...agg.map(s => s.contactCount), 1);
  const maxWon = Math.max(...agg.map(s => s.wonVal), 1);

  return (
    <div>
      <div className="grid-2">
        <div className="card">
          <div className="card-hd">
            <div>
              <div className="card-title">Contacts by showroom</div>
              <div className="card-sub">Total leads + active customers</div>
            </div>
          </div>
          <div className="card-pad">
            {agg.map(s => (
              <div className="chart-row" key={s.id} style={{ gridTemplateColumns: '120px 1fr 90px' }}>
                <div className="lbl">
                  <span className={`ss-tab-dot ${s.id}`} style={{ width: 8, height: 8, borderRadius: 4 }}></span>
                  {s.name}
                </div>
                <div className="bar-wrap" style={{ height: 22 }}>
                  <div className="bar" style={{ width: (s.contactCount / maxContacts * 100) + '%', background: `var(--ss-${s.id})` }}>
                    {s.contactCount / maxContacts > 0.2 && <span className="num">{s.contactCount}</span>}
                  </div>
                </div>
                <div className="val"><span className="num">{s.customers}</span> customers</div>
              </div>
            ))}
          </div>
        </div>

        <div className="card">
          <div className="card-hd">
            <div>
              <div className="card-title">Revenue by showroom</div>
              <div className="card-sub">Closed-won value · period</div>
            </div>
          </div>
          <div className="card-pad">
            {agg.map(s => (
              <div className="chart-row" key={s.id} style={{ gridTemplateColumns: '120px 1fr 90px' }}>
                <div className="lbl">
                  <span className={`ss-tab-dot ${s.id}`} style={{ width: 8, height: 8, borderRadius: 4 }}></span>
                  {s.name}
                </div>
                <div className="bar-wrap" style={{ height: 22 }}>
                  <div className="bar accent" style={{ width: (s.wonVal / maxWon * 100) + '%' }}>
                    {s.wonVal / maxWon > 0.2 && <span>{fmt.money(s.wonVal, true)}</span>}
                  </div>
                </div>
                <div className="val"><span className="num">{s.wonCount}</span> wins</div>
              </div>
            ))}
          </div>
        </div>
      </div>

      <div className="card">
        <div className="card-hd">
          <div>
            <div className="card-title">Showroom comparison</div>
            <div className="card-sub">All metrics side-by-side</div>
          </div>
        </div>
        <div className="tbl-wrap">
          <table className="tbl">
            <thead>
              <tr>
                <th>Showroom</th>
                <th className="num-col">Contacts</th>
                <th className="num-col">New leads</th>
                <th className="num-col">Qualified</th>
                <th className="num-col">In pipeline</th>
                <th className="num-col">Customers</th>
                <th className="num-col">Dormant</th>
                <th className="num-col">Deals</th>
                <th className="num-col">Won $</th>
              </tr>
            </thead>
            <tbody>
              {agg.map(s => (
                <tr key={s.id}>
                  <td>
                    <span className="pill">
                      <span className={`ss-tab-dot ${s.id}`}></span>
                      <span style={{ fontWeight: 500 }}>{s.name}</span>
                      <span style={{ fontSize: 10, color: 'var(--ink-3)', marginLeft: 4 }}>{s.state}</span>
                    </span>
                  </td>
                  <td className="num-col"><span className="num">{s.contactCount}</span></td>
                  <td className="num-col"><span className="num">{s.newLeads}</span></td>
                  <td className="num-col"><span className="num">{s.qualified}</span></td>
                  <td className="num-col"><span className="num">{s.inPipeline}</span></td>
                  <td className="num-col"><span className="num" style={{ color: 'var(--won)', fontWeight: 500 }}>{s.customers}</span></td>
                  <td className="num-col"><span className="num" style={{ color: 'var(--lost)' }}>{s.dormant}</span></td>
                  <td className="num-col"><span className="num">{s.dealCount}</span></td>
                  <td className="num-col"><span className="num" style={{ fontWeight: 500 }}>{fmt.money(s.wonVal, true)}</span></td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      </div>
    </div>
  );
}

// === Activity report ===
function ActivityReport({ contacts, deals, reps }) {
  // Count activities by type
  let calls = 0, emails = 0, meetings = 0, notes = 0;
  for (const d of deals) {
    for (const a of (d.activities || [])) {
      if (a.type === 'call') calls++;
      else if (a.type === 'email') emails++;
      else if (a.type === 'meeting') meetings++;
      else notes++;
    }
  }
  const total = calls + emails + meetings + notes;

  // Per rep
  const perRep = reps.map(r => {
    const rDeals = deals.filter(d => d.rep === r.id);
    let c = 0, e = 0, m = 0, n = 0;
    for (const d of rDeals) {
      for (const a of (d.activities || [])) {
        if (a.type === 'call') c++;
        else if (a.type === 'email') e++;
        else if (a.type === 'meeting') m++;
        else n++;
      }
    }
    return { ...r, calls: c, emails: e, meetings: m, notes: n, total: c + e + m + n };
  }).sort((a, b) => b.total - a.total);

  if (total === 0) {
    return (
      <div className="card" style={{ padding: '48px 24px', textAlign: 'center', color: 'var(--ink-3)' }}>
        <div style={{ fontSize: 32, marginBottom: 12 }}>📋</div>
        <div style={{ fontSize: 15, fontWeight: 600, color: 'var(--ink-2)', marginBottom: 6 }}>No activity data recorded yet</div>
        <div style={{ fontSize: 13, maxWidth: 420, margin: '0 auto' }}>
          Activities (calls, emails, meetings, notes) are logged when your team updates deals. Once logged, they'll appear here broken down by designer and type.
        </div>
      </div>
    );
  }

  return (
    <div>
      <div className="grid-kpis">
        <Kpi label="Total activities" value={fmt.num(total)} foot={`across ${deals.length} deals`} deltaPct={0.05}/>
        <Kpi label="Phone calls" value={fmt.num(calls)} foot={fmt.pct(calls / Math.max(total, 1), 0) + ' of total'} deltaPct={0.04}/>
        <Kpi label="Emails" value={fmt.num(emails)} foot={fmt.pct(emails / Math.max(total, 1), 0) + ' of total'} deltaPct={0.03}/>
        <Kpi label="Meetings & visits" value={fmt.num(meetings)} foot={fmt.pct(meetings / Math.max(total, 1), 0) + ' of total'} deltaPct={0.02}/>
      </div>

      <div className="card">
        <div className="card-hd">
          <div>
            <div className="card-title">Activity by designer</div>
            <div className="card-sub">Volume of customer touches per rep</div>
          </div>
        </div>
        <div className="tbl-wrap">
          <table className="tbl">
            <thead>
              <tr>
                <th>Designer</th>
                <th className="num-col">Total</th>
                <th className="num-col">Calls</th>
                <th className="num-col">Emails</th>
                <th className="num-col">Meetings</th>
                <th className="num-col">Notes</th>
                <th>Distribution</th>
              </tr>
            </thead>
            <tbody>
              {perRep.map(r => {
                const max = Math.max(...perRep.map(x => x.total), 1);
                return (
                  <tr key={r.id}>
                    <td>
                      <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                        <Avatar name={r.name} sz="sm"/>
                        <span style={{ fontWeight: 500 }}>{r.name}</span>
                      </div>
                    </td>
                    <td className="num-col"><span className="num" style={{ fontWeight: 500 }}>{r.total}</span></td>
                    <td className="num-col"><span className="num">{r.calls}</span></td>
                    <td className="num-col"><span className="num">{r.emails}</span></td>
                    <td className="num-col"><span className="num">{r.meetings}</span></td>
                    <td className="num-col"><span className="num">{r.notes}</span></td>
                    <td style={{ width: 200 }}>
                      <div style={{ display: 'flex', height: 8, borderRadius: 4, overflow: 'hidden', background: 'var(--surface-3)' }}>
                        <div style={{ width: `${(r.calls / Math.max(r.total, 1)) * 100}%`, background: 'var(--info)' }} title={`Calls: ${r.calls}`}></div>
                        <div style={{ width: `${(r.emails / Math.max(r.total, 1)) * 100}%`, background: 'var(--s-in_progress)' }} title={`Emails: ${r.emails}`}></div>
                        <div style={{ width: `${(r.meetings / Math.max(r.total, 1)) * 100}%`, background: 'var(--accent)' }} title={`Meetings: ${r.meetings}`}></div>
                        <div style={{ width: `${(r.notes / Math.max(r.total, 1)) * 100}%`, background: 'var(--ink-4)' }} title={`Notes: ${r.notes}`}></div>
                      </div>
                    </td>
                  </tr>
                );
              })}
            </tbody>
          </table>
        </div>
        <div style={{ padding: '10px 16px', borderTop: '1px solid var(--border)', display: 'flex', gap: 18, fontSize: 11, color: 'var(--ink-3)' }}>
          <span><span style={{ display: 'inline-block', width: 10, height: 10, background: 'var(--info)', borderRadius: 2, verticalAlign: 'middle', marginRight: 4 }}></span>Calls</span>
          <span><span style={{ display: 'inline-block', width: 10, height: 10, background: 'var(--s-in_progress)', borderRadius: 2, verticalAlign: 'middle', marginRight: 4 }}></span>Emails</span>
          <span><span style={{ display: 'inline-block', width: 10, height: 10, background: 'var(--accent)', borderRadius: 2, verticalAlign: 'middle', marginRight: 4 }}></span>Meetings</span>
          <span><span style={{ display: 'inline-block', width: 10, height: 10, background: 'var(--ink-4)', borderRadius: 2, verticalAlign: 'middle', marginRight: 4 }}></span>Notes</span>
        </div>
      </div>
    </div>
  );
}

function sourceColor(src) {
  const map = {
    'Referral': 'oklch(0.5 0.12 145)',
    'Website': 'oklch(0.5 0.1 240)',
    'Google': 'oklch(0.55 0.13 30)',
    'Facebook': 'oklch(0.45 0.12 260)',
    'Driving by': 'oklch(0.55 0.1 80)',
    'BayIsland': 'oklch(0.55 0.12 200)',
    'Newsletter': 'oklch(0.5 0.05 90)',
    'Other': 'oklch(0.55 0.02 90)',
  };
  return map[src] || 'oklch(0.55 0.05 200)';
}

// ============================================================================
//  Export engine — report data models + CSV / Excel / PDF builders
// ============================================================================

const _CUSTOMER_LC = ['past_customer', 'customer_active', 'dormant_past_customer'];
const _QUALIFIED_LC = ['qualified', 'in_pipeline', 'customer_active', 'past_customer', 'lost_quote'];

function buildReportSections(key, ctx) {
  switch (key) {
    case 'marketing': return _mktSections(ctx);
    case 'conversion': return _convSections(ctx);
    case 'sales': return _salesSections(ctx);
    case 'showrooms': return _showroomSections(ctx);
    case 'activity': return _activitySections(ctx);
    case 'lost': return _lostSections(ctx);
    default: return [];
  }
}

function _mktSections({ contacts, showrooms }) {
  const srcAgg = {};
  contacts.forEach(c => {
    const s = c.primarySource || 'Not recorded';
    if (!srcAgg[s]) srcAgg[s] = { contacts: 0, sold: 0, value: 0 };
    srcAgg[s].contacts++;
    if (_CUSTOMER_LC.includes(c.lifecycle)) { srcAgg[s].sold++; srcAgg[s].value += c.ltv || 0; }
  });
  const sourceRows = Object.entries(srcAgg).sort((a, b) => b[1].contacts - a[1].contacts)
    .map(([source, v]) => ({ source, contacts: v.contacts, sold: v.sold, conv: v.contacts ? v.sold / v.contacts : 0, revenue: v.value }));

  const meansAgg = {};
  contacts.forEach(c => {
    const m = c.primaryMeans || 'Not recorded';
    if (!meansAgg[m]) meansAgg[m] = { contacts: 0, sold: 0 };
    meansAgg[m].contacts++;
    if (_CUSTOMER_LC.includes(c.lifecycle)) meansAgg[m].sold++;
  });
  const meansRows = Object.entries(meansAgg).sort((a, b) => b[1].contacts - a[1].contacts)
    .map(([means, v]) => ({ means, contacts: v.contacts, conv: v.contacts ? v.sold / v.contacts : 0 }));

  const subAgg = {};
  contacts.forEach(c => {
    const su = (c.suburb || 'Not recorded').trim();
    if (!subAgg[su]) subAgg[su] = { contacts: 0, sold: 0, value: 0, sh: {} };
    subAgg[su].contacts++;
    if (_CUSTOMER_LC.includes(c.lifecycle)) { subAgg[su].sold++; subAgg[su].value += c.ltv || 0; }
    subAgg[su].sh[c.homeShowroom] = (subAgg[su].sh[c.homeShowroom] || 0) + 1;
  });
  const suburbRows = Object.entries(subAgg).filter(([s]) => s !== 'Not recorded')
    .sort((a, b) => b[1].contacts - a[1].contacts)
    .map(([suburb, v], i) => {
      const closestId = Object.entries(v.sh).sort((a, b) => b[1] - a[1])[0]?.[0];
      return { rank: i + 1, suburb, showroom: showrooms.find(s => s.id === closestId)?.name || '', contacts: v.contacts, sold: v.sold, conv: v.contacts ? v.sold / v.contacts : 0, revenue: v.value };
    });

  return [
    { title: 'Lead source performance', columns: [
      { key: 'source', label: 'Source', type: 'text' },
      { key: 'contacts', label: 'Contacts', type: 'num' },
      { key: 'sold', label: 'Sold', type: 'num' },
      { key: 'conv', label: 'Conversion rate', type: 'pct' },
      { key: 'revenue', label: 'Revenue (AUD)', type: 'money' },
    ], rows: sourceRows },
    { title: 'First-contact channel', columns: [
      { key: 'means', label: 'Channel', type: 'text' },
      { key: 'contacts', label: 'Contacts', type: 'num' },
      { key: 'conv', label: 'Conversion rate', type: 'pct' },
    ], rows: meansRows },
    { title: 'Suburbs by contact volume', columns: [
      { key: 'rank', label: '#', type: 'num' },
      { key: 'suburb', label: 'Suburb', type: 'text' },
      { key: 'showroom', label: 'Closest showroom', type: 'text' },
      { key: 'contacts', label: 'Contacts', type: 'num' },
      { key: 'sold', label: 'Sold', type: 'num' },
      { key: 'conv', label: 'Conversion rate', type: 'pct' },
      { key: 'revenue', label: 'Revenue (AUD)', type: 'money' },
    ], rows: suburbRows },
  ];
}

function _convSections({ contacts, deals }) {
  const total = contacts.length;
  const qualified = contacts.filter(c => _QUALIFIED_LC.includes(c.lifecycle)).length;
  const quoted = contacts.filter(c => (c.dealCount || 0) > 0 || ['in_pipeline','customer_active','past_customer','dormant_customer_active','dormant_past_customer'].includes(c.lifecycle)).length;
  const sold = contacts.filter(c => _CUSTOMER_LC.includes(c.lifecycle)).length;
  const funnel = [
    { stage: 'Total contacts', count: total, prev: null },
    { stage: 'Qualified', count: qualified, prev: total },
    { stage: 'Quoted', count: quoted, prev: qualified },
    { stage: 'Sold', count: sold, prev: quoted },
  ].map(f => ({ stage: f.stage, count: f.count, ofTotal: total ? f.count / total : 0, stepConv: f.prev == null ? null : (f.prev ? f.count / f.prev : 0) }));

  const stageMap = { possible: 'Possible', in_progress: 'In progress', on_hold: 'On hold', sold: 'Sold', lost: 'Lost' };
  const dist = {};
  deals.forEach(d => { if (!dist[d.stage]) dist[d.stage] = { count: 0, value: 0 }; dist[d.stage].count++; dist[d.stage].value += d.value || 0; });
  const distRows = Object.keys(stageMap).filter(k => dist[k]).map(k => ({ stage: stageMap[k], count: dist[k].count, value: dist[k].value }));

  return [
    { title: 'Conversion funnel', columns: [
      { key: 'stage', label: 'Stage', type: 'text' },
      { key: 'count', label: 'Count', type: 'num' },
      { key: 'ofTotal', label: '% of contacts', type: 'pct' },
      { key: 'stepConv', label: 'Step conversion', type: 'pct' },
    ], rows: funnel },
    { title: 'Deal stage distribution', columns: [
      { key: 'stage', label: 'Stage', type: 'text' },
      { key: 'count', label: 'Deals', type: 'num' },
      { key: 'value', label: 'Value (AUD)', type: 'money' },
    ], rows: distRows },
  ];
}

function _salesSections({ contacts, deals, reps }) {
  const agg = reps.map(r => {
    const rd = deals.filter(d => d.rep === r.id);
    const rc = contacts.filter(c => c.owner === r.name);
    const won = rd.filter(d => d.stage === 'sold');
    const lost = rd.filter(d => d.stage === 'lost');
    const active = rd.filter(d => d.stage !== 'sold' && d.stage !== 'lost');
    const wonVal = won.reduce((s, d) => s + d.value, 0);
    const closed = won.length + lost.length;
    return {
      name: r.name, contacts: rc.length, active: active.length,
      activeVal: active.reduce((s, d) => s + d.value, 0), wins: won.length, wonVal,
      lost: lost.length, winRate: closed ? won.length / closed : 0, avgDeal: won.length ? wonVal / won.length : 0,
    };
  }).sort((a, b) => b.wonVal - a.wonVal);
  return [{ title: 'Designer performance', columns: [
    { key: 'name', label: 'Designer', type: 'text' },
    { key: 'contacts', label: 'Contacts owned', type: 'num' },
    { key: 'active', label: 'Active quotes', type: 'num' },
    { key: 'activeVal', label: 'Active value (AUD)', type: 'money' },
    { key: 'wins', label: 'Wins', type: 'num' },
    { key: 'wonVal', label: 'Closed-won (AUD)', type: 'money' },
    { key: 'lost', label: 'Lost', type: 'num' },
    { key: 'winRate', label: 'Win rate', type: 'pct' },
    { key: 'avgDeal', label: 'Avg deal (AUD)', type: 'money' },
  ], rows: agg }];
}

function _showroomSections({ contacts, deals, showrooms }) {
  const rows = showrooms.map(s => {
    const sc = contacts.filter(c => c.homeShowroom === s.id);
    const sd = deals.filter(d => d.showroom === s.id);
    const won = sd.filter(d => d.stage === 'sold');
    return {
      showroom: s.name, state: s.state, contacts: sc.length,
      newLeads: sc.filter(c => c.lifecycle === 'new_lead').length,
      qualified: sc.filter(c => c.lifecycle === 'qualified').length,
      inPipeline: sc.filter(c => c.lifecycle === 'in_pipeline').length,
      customers: sc.filter(c => _CUSTOMER_LC.includes(c.lifecycle)).length,
      dormant: sc.filter(c => (c.lifecycle || '').startsWith('dormant_')).length,
      deals: sd.length, wonVal: won.reduce((m, d) => m + d.value, 0),
    };
  });
  return [{ title: 'Showroom comparison', columns: [
    { key: 'showroom', label: 'Showroom', type: 'text' },
    { key: 'state', label: 'State', type: 'text' },
    { key: 'contacts', label: 'Contacts', type: 'num' },
    { key: 'newLeads', label: 'New leads', type: 'num' },
    { key: 'qualified', label: 'Qualified', type: 'num' },
    { key: 'inPipeline', label: 'In pipeline', type: 'num' },
    { key: 'customers', label: 'Customers', type: 'num' },
    { key: 'dormant', label: 'Dormant', type: 'num' },
    { key: 'deals', label: 'Deals', type: 'num' },
    { key: 'wonVal', label: 'Won (AUD)', type: 'money' },
  ], rows }];
}

function _activitySections({ deals, reps }) {
  let calls = 0, emails = 0, meetings = 0, notes = 0;
  deals.forEach(d => (d.activities || []).forEach(a => {
    if (a.type === 'call') calls++; else if (a.type === 'email') emails++;
    else if (a.type === 'meeting') meetings++; else notes++;
  }));
  const total = calls + emails + meetings + notes;
  const perRep = reps.map(r => {
    const rd = deals.filter(d => d.rep === r.id);
    let c = 0, e = 0, m = 0, n = 0;
    rd.forEach(d => (d.activities || []).forEach(a => {
      if (a.type === 'call') c++; else if (a.type === 'email') e++;
      else if (a.type === 'meeting') m++; else n++;
    }));
    return { name: r.name, total: c + e + m + n, calls: c, emails: e, meetings: m, notes: n };
  }).sort((a, b) => b.total - a.total);
  const summary = [
    { type: 'Phone calls', count: calls, share: total ? calls / total : 0 },
    { type: 'Emails', count: emails, share: total ? emails / total : 0 },
    { type: 'Meetings & visits', count: meetings, share: total ? meetings / total : 0 },
    { type: 'Notes', count: notes, share: total ? notes / total : 0 },
  ];
  return [
    { title: 'Activity by designer', columns: [
      { key: 'name', label: 'Designer', type: 'text' },
      { key: 'total', label: 'Total', type: 'num' },
      { key: 'calls', label: 'Calls', type: 'num' },
      { key: 'emails', label: 'Emails', type: 'num' },
      { key: 'meetings', label: 'Meetings', type: 'num' },
      { key: 'notes', label: 'Notes', type: 'num' },
    ], rows: perRep },
    { title: 'Activity summary', columns: [
      { key: 'type', label: 'Activity type', type: 'text' },
      { key: 'count', label: 'Count', type: 'num' },
      { key: 'share', label: 'Share of total', type: 'pct' },
    ], rows: summary },
  ];
}

// --- Cell formatting per output target ---
function _cell(val, type, target) {
  if (val == null || val === '') return target === 'pdf' ? '—' : '';
  if (type === 'money') return target === 'pdf' ? fmt.money(val) : (typeof val === 'number' ? Math.round(val) : val);
  if (type === 'pct') return (val * 100).toFixed(0) + '%';
  return val;
}

function _downloadFile(filename, content, mime) {
  const blob = new Blob([content], { type: mime });
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url; a.download = filename;
  document.body.appendChild(a); a.click();
  setTimeout(() => { document.body.removeChild(a); URL.revokeObjectURL(url); }, 100);
}

// --- CSV ---
function _csvEsc(v) {
  if (v == null) return '';
  const s = String(v);
  return /[",\n]/.test(s) ? '"' + s.replace(/"/g, '""') + '"' : s;
}
function reportsToCSV(reports, meta) {
  const lines = [];
  lines.push(_csvEsc('Pearler report export'));
  lines.push(_csvEsc(`Scope: ${meta.showroomLabel}  |  ${meta.repLabel}  |  ${meta.periodLabel}`));
  lines.push(_csvEsc('Generated: ' + meta.generatedStr));
  reports.forEach(rep => rep.sections.forEach(sec => {
    lines.push('');
    lines.push(_csvEsc(rep.label + ' — ' + sec.title));
    lines.push(sec.columns.map(c => _csvEsc(c.label)).join(','));
    sec.rows.forEach(row => lines.push(sec.columns.map(c => _csvEsc(_cell(row[c.key], c.type, 'csv'))).join(',')));
  }));
  return '\uFEFF' + lines.join('\r\n');
}

// --- Excel (.xls — styled HTML workbook Excel opens cleanly) ---
function _xesc(s) { return String(s == null ? '' : s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;'); }
function reportsToExcel(reports, meta) {
  let body = '';
  body += `<h1 style="font:bold 16px Calibri,Arial;color:#6e2a1c;margin:0 0 2px">Pearler report export</h1>`;
  body += `<div style="font:11px Arial;color:#666;margin-bottom:14px">Scope: ${_xesc(meta.showroomLabel)} &nbsp;|&nbsp; ${_xesc(meta.repLabel)} &nbsp;|&nbsp; ${_xesc(meta.periodLabel)}<br/>Generated ${_xesc(meta.generatedStr)}</div>`;
  reports.forEach(rep => rep.sections.forEach(sec => {
    body += `<div style="font:bold 13px Calibri,Arial;color:#6e2a1c;margin:14px 0 4px">${_xesc(rep.label)} — ${_xesc(sec.title)}</div>`;
    body += '<table border="1" cellspacing="0" cellpadding="5" style="border-collapse:collapse;font:11px Arial">';
    body += '<tr>' + sec.columns.map(c => `<td style="background:#efe9e3;font-weight:bold;border:1px solid #c8bfb4;text-align:${c.type === 'text' ? 'left' : 'right'}">${_xesc(c.label)}</td>`).join('') + '</tr>';
    sec.rows.forEach(row => {
      body += '<tr>' + sec.columns.map(c => {
        const v = _cell(row[c.key], c.type, 'excel');
        return `<td style="border:1px solid #d8d2c8;text-align:${c.type === 'text' ? 'left' : 'right'}">${_xesc(v)}</td>`;
      }).join('') + '</tr>';
    });
    body += '</table>';
  }));
  return `<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel"><head><meta charset="utf-8"><!--[if gte mso 9]><xml><x:ExcelWorkbook><x:ExcelWorksheets><x:ExcelWorksheet><x:Name>Pearler reports</x:Name><x:WorksheetOptions><x:DisplayGridlines/></x:WorksheetOptions></x:ExcelWorksheet></x:ExcelWorksheets></x:ExcelWorkbook></xml><![endif]--></head><body>${body}</body></html>`;
}

// --- PDF / Print (clean, print-ready document) ---
function reportsToPrintDoc(reports, meta) {
  let body = '';
  reports.forEach((rep, ri) => {
    body += `<section class="rep" ${ri > 0 ? 'style="page-break-before:always"' : ''}>`;
    body += `<h2 class="rep-title">${_xesc(rep.label)} report</h2>`;
    rep.sections.forEach(sec => {
      body += `<h3 class="sec-title">${_xesc(sec.title)}</h3>`;
      body += '<table><thead><tr>' + sec.columns.map(c => `<th class="${c.type === 'text' ? 'l' : 'r'}">${_xesc(c.label)}</th>`).join('') + '</tr></thead><tbody>';
      if (!sec.rows.length) body += `<tr><td class="empty" colspan="${sec.columns.length}">No data in this range</td></tr>`;
      sec.rows.forEach(row => {
        body += '<tr>' + sec.columns.map(c => `<td class="${c.type === 'text' ? 'l' : 'r'}">${_xesc(_cell(row[c.key], c.type, 'pdf'))}</td>`).join('') + '</tr>';
      });
      body += '</tbody></table>';
    });
    body += '</section>';
  });
  return `<!DOCTYPE html><html><head><meta charset="utf-8"><title>Pearler reports</title><style>
    * { box-sizing: border-box; }
    body { font-family: -apple-system, 'Segoe UI', Helvetica, Arial, sans-serif; color: #2b2723; margin: 0; padding: 34px 40px; font-size: 12px; }
    .doc-head { border-bottom: 2px solid #6e2a1c; padding-bottom: 14px; margin-bottom: 22px; }
    .brand { font-size: 20px; font-weight: 700; letter-spacing: -0.02em; color: #6e2a1c; }
    .brand small { color: #8a857e; font-weight: 500; font-size: 12px; margin-left: 8px; letter-spacing: 0; }
    .scope { margin-top: 8px; font-size: 11.5px; color: #555; }
    .scope b { color: #2b2723; }
    .scope .gen { color: #8a857e; }
    .rep-title { font-size: 15px; color: #6e2a1c; margin: 26px 0 2px; }
    .rep:first-child .rep-title { margin-top: 0; }
    .sec-title { font-size: 12.5px; margin: 18px 0 7px; color: #2b2723; }
    table { width: 100%; border-collapse: collapse; margin-bottom: 6px; }
    th, td { padding: 6px 9px; border-bottom: 1px solid #e6e1d9; }
    th { background: #f4efe9; font-size: 10px; text-transform: uppercase; letter-spacing: 0.05em; color: #6a655e; border-bottom: 1.5px solid #d8d2c8; }
    th.r, td.r { text-align: right; font-variant-numeric: tabular-nums; }
    th.l, td.l { text-align: left; }
    tbody tr:nth-child(even) td { background: #faf8f5; }
    td.empty { text-align: center; color: #a8a29a; font-style: italic; }
    .foot { margin-top: 24px; padding-top: 10px; border-top: 1px solid #e6e1d9; font-size: 10px; color: #a8a29a; }
    @media print { body { padding: 0; } @page { margin: 16mm 14mm; } }
  </style></head><body>
    <div class="doc-head">
      <div class="brand">Pearler <small>Reports</small></div>
      <div class="scope"><b>${_xesc(meta.showroomLabel)}</b> &nbsp;·&nbsp; <b>${_xesc(meta.repLabel)}</b> &nbsp;·&nbsp; <b>${_xesc(meta.periodLabel)}</b> &nbsp;·&nbsp; <span class="gen">Generated ${_xesc(meta.generatedStr)}</span></div>
    </div>
    ${body}
    <div class="foot">Pearler CRM · Confidential · ${_xesc(meta.generatedStr)}</div>
  </body></html>`;
}
function openPrintDoc(html) {
  const w = window.open('', '_blank');
  if (!w) { alert('Please allow pop-ups to open the print / PDF view.'); return; }
  w.document.open(); w.document.write(html); w.document.close(); w.focus();
  const go = () => { try { w.print(); } catch (e) {} };
  if (w.document.readyState === 'complete') setTimeout(go, 350);
  else w.onload = () => setTimeout(go, 350);
}

// ============================================================================
//  Export dialog — set parameters (showroom / designer / date) + pick reports + format
// ============================================================================
function ReportExportModal({ data, currentUser, defaultTab, initFormat, init, onClose }) {
  const T = window.T || ((k) => k);
  const isSales = currentUser.role === 'sales_designer';

  const [showroom, setShowroom] = React.useState(init.showroom || 'all');
  const [rep, setRep] = React.useState(init.rep || 'all');
  const [period, setPeriod] = React.useState(init.period || '12mo');
  const [customFrom, setCustomFrom] = React.useState(init.customFrom || '');
  const [customTo, setCustomTo] = React.useState(init.customTo || '');
  const [keys, setKeys] = React.useState(() => new Set([defaultTab]));
  const [format, setFormat] = React.useState(initFormat || 'excel');

  React.useEffect(() => {
    const onKey = (e) => { if (e.key === 'Escape') onClose(); };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [onClose]);

  // Recompute scope live from the dialog's own parameters
  const scope = computeReportScope({ ...data, currentUser, showroom, rep, period, customFrom, customTo });
  const meta = buildReportMeta({ ...data, currentUser, showroom, effRep: scope.effRep, period, customFrom, customTo, periodContacts: scope.periodContacts, periodDeals: scope.periodDeals });
  const showroomOptions = isSales ? data.showrooms.filter(s => currentUser.works.includes(s.id)) : data.showrooms;

  const toggle = (k) => setKeys(prev => { const n = new Set(prev); n.has(k) ? n.delete(k) : n.add(k); return n; });
  const allOn = REPORT_DEFS.every(d => keys.has(d.key));
  const toggleAll = () => setKeys(allOn ? new Set() : new Set(REPORT_DEFS.map(d => d.key)));

  const formats = [
    { id: 'excel', label: 'Excel', sub: '.xls · formatted tables', icon: 'table' },
    { id: 'csv', label: 'CSV', sub: '.csv · raw data', icon: 'download' },
    { id: 'pdf', label: 'PDF / Print', sub: 'print-ready document', icon: 'book' },
  ];

  const doExport = () => {
    const reportKeys = REPORT_DEFS.filter(d => keys.has(d.key)).map(d => d.key);
    if (!reportKeys.length) return;
    const exportCtx = { contacts: scope.periodContacts, deals: scope.periodDeals, reps: scope.scopeReps, showrooms: scope.scopeShowrooms };
    const reports = reportKeys.map(k => ({ key: k, label: (REPORT_DEFS.find(d => d.key === k) || {}).label || k, sections: buildReportSections(k, exportCtx) }));
    const stamp = new Date().toISOString().slice(0, 10);
    const base = (reportKeys.length === 1 ? reportKeys[0] : 'pearler-reports') + '-' + stamp;
    if (format === 'csv') _downloadFile(base + '.csv', reportsToCSV(reports, meta), 'text/csv;charset=utf-8;');
    else if (format === 'excel') _downloadFile(base + '.xls', reportsToExcel(reports, meta), 'application/vnd.ms-excel');
    else openPrintDoc(reportsToPrintDoc(reports, meta));
    onClose();
  };

  const ctlLabel = { fontSize: 11, color: 'var(--ink-3)', display: 'block', marginBottom: 4 };

  return (
    <React.Fragment>
      <div className="detail-overlay" onClick={onClose}></div>
      <div className="invite-modal" style={{ width: 560 }}>
        <div style={{ padding: '18px 22px', borderBottom: '1px solid var(--border)', display: 'flex', alignItems: 'center', gap: 11 }}>
          <div style={{ width: 32, height: 32, borderRadius: 8, background: 'var(--accent-soft)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
            <Icon name={format === 'pdf' ? 'book' : 'download'} size={16} color="var(--accent-ink)"/>
          </div>
          <div style={{ flex: 1 }}>
            <div style={{ fontSize: 15, fontWeight: 600, letterSpacing: '-0.01em' }}>{format === 'pdf' ? 'Print / export reports' : 'Export reports'}</div>
            <div style={{ fontSize: 11.5, color: 'var(--ink-3)' }}>Set parameters, choose reports and a format</div>
          </div>
          <button onClick={onClose} className="detail-close" aria-label="Close"><Icon name="close" size={16}/></button>
        </div>

        <div style={{ padding: '18px 22px', maxHeight: 'min(72vh, 620px)', overflowY: 'auto' }}>
          {/* Parameters */}
          <div style={{ fontSize: 11, textTransform: 'uppercase', letterSpacing: '0.08em', color: 'var(--ink-3)', fontWeight: 600, marginBottom: 9 }}>Parameters</div>
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10, marginBottom: 8 }}>
            <div>
              <label style={ctlLabel}>{T('location')}</label>
              {isSales && showroomOptions.length <= 1 ? (
                <span className="pill"><span className={`ss-tab-dot ${showroomOptions[0]?.id || ''}`}></span>{showroomOptions[0]?.name || '—'}</span>
              ) : (
                <select className="sel" style={{ width: '100%' }} value={showroom} onChange={(e) => { setShowroom(e.target.value); setRep('all'); }}>
                  <option value="all">{isSales ? `My ${T('locs_lc')}` : `All ${T('locs_lc')}`}</option>
                  {showroomOptions.map(s => <option key={s.id} value={s.id}>{s.name} · {s.state}</option>)}
                </select>
              )}
            </div>
            {!isSales && (
              <div>
                <label style={ctlLabel}>{T('rep')}</label>
                <select className="sel" style={{ width: '100%' }} value={rep} onChange={(e) => setRep(e.target.value)}>
                  <option value="all">All {T('reps_lc')}</option>
                  {scope.repOptions.map(r => <option key={r.id} value={r.id}>{r.name}</option>)}
                </select>
              </div>
            )}
            <div>
              <label style={ctlLabel}>Date range</label>
              <select className="sel" style={{ width: '100%' }} value={period} onChange={(e) => setPeriod(e.target.value)}>
                <option value="1mo">Last 30 days</option>
                <option value="3mo">Last 3 months</option>
                <option value="12mo">Last 12 months</option>
                <option value="5y">Last 5 years</option>
                <option value="all">All time</option>
                <option value="custom">Custom range…</option>
              </select>
            </div>
            {period === 'custom' && (
              <div style={{ display: 'flex', alignItems: 'flex-end', gap: 6 }}>
                <div style={{ flex: 1 }}>
                  <label style={ctlLabel}>From</label>
                  <input type="date" className="sel" style={{ width: '100%' }} value={customFrom} onChange={(e) => setCustomFrom(e.target.value)}/>
                </div>
                <div style={{ flex: 1 }}>
                  <label style={ctlLabel}>To</label>
                  <input type="date" className="sel" style={{ width: '100%' }} value={customTo} onChange={(e) => setCustomTo(e.target.value)}/>
                </div>
              </div>
            )}
          </div>
          <div style={{ fontSize: 11, color: 'var(--ink-3)', marginBottom: 20 }}>
            In scope: <span className="num" style={{ color: 'var(--ink-2)', fontWeight: 500 }}>{meta.contacts}</span> contacts ·{' '}
            <span className="num" style={{ color: 'var(--ink-2)', fontWeight: 500 }}>{meta.deals}</span> deals
          </div>

          {/* Reports */}
          <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', marginBottom: 8 }}>
            <div style={{ fontSize: 11, textTransform: 'uppercase', letterSpacing: '0.08em', color: 'var(--ink-3)', fontWeight: 600 }}>Reports to include</div>
            <button onClick={toggleAll} style={{ fontSize: 11.5, color: 'var(--accent-ink)', fontWeight: 500 }}>{allOn ? 'Clear all' : 'Select all'}</button>
          </div>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 6, marginBottom: 22 }}>
            {REPORT_DEFS.map(d => {
              const on = keys.has(d.key);
              return (
                <button key={d.key} onClick={() => toggle(d.key)}
                  style={{ display: 'flex', alignItems: 'center', gap: 11, padding: '10px 12px', textAlign: 'left',
                    border: '1.5px solid ' + (on ? 'var(--accent)' : 'var(--border)'), borderRadius: 'var(--r-md)',
                    background: on ? 'var(--accent-soft)' : 'var(--surface)', transition: 'border-color 0.12s, background 0.12s' }}>
                  <span className={`checkbox ${on ? 'on' : ''}`}>{on && <Icon name="check" size={11} color="white"/>}</span>
                  <span style={{ flex: 1 }}>
                    <span style={{ fontSize: 13, fontWeight: 500, display: 'block' }}>{d.label}</span>
                    <span style={{ fontSize: 11.5, color: 'var(--ink-3)' }}>{d.desc}</span>
                  </span>
                  {d.key === defaultTab && <span className="pill" style={{ fontSize: 10 }}>Current</span>}
                </button>
              );
            })}
          </div>

          {/* Format */}
          <div style={{ fontSize: 11, textTransform: 'uppercase', letterSpacing: '0.08em', color: 'var(--ink-3)', fontWeight: 600, marginBottom: 8 }}>Format</div>
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 8 }}>
            {formats.map(f => {
              const on = format === f.id;
              return (
                <button key={f.id} onClick={() => setFormat(f.id)} className={`preset-card ${on ? 'on' : ''}`}
                  style={{ display: 'flex', flexDirection: 'column', gap: 5, alignItems: 'flex-start' }}>
                  <Icon name={f.icon} size={17} color={on ? 'var(--accent-ink)' : 'var(--ink-2)'}/>
                  <span style={{ fontSize: 13, fontWeight: 600 }}>{f.label}</span>
                  <span style={{ fontSize: 10.5, color: 'var(--ink-3)' }}>{f.sub}</span>
                </button>
              );
            })}
          </div>
        </div>

        <div style={{ padding: '14px 22px', borderTop: '1px solid var(--border)', display: 'flex', alignItems: 'center', gap: 10 }}>
          <span style={{ fontSize: 11.5, color: 'var(--ink-3)', marginRight: 'auto' }}>
            {keys.size} report{keys.size !== 1 ? 's' : ''} · {format === 'pdf' ? 'PDF' : format.toUpperCase()}
          </span>
          <button className="btn" onClick={onClose}>Cancel</button>
          <button className="btn primary" disabled={keys.size === 0}
            style={keys.size === 0 ? { opacity: 0.5, cursor: 'not-allowed' } : null}
            onClick={doExport}>
            <Icon name={format === 'pdf' ? 'book' : 'download'} size={12}/> {format === 'pdf' ? 'Open print view' : format === 'excel' ? 'Export Excel' : 'Export CSV'}
          </button>
        </div>
      </div>
    </React.Fragment>
  );
}

// === Lost jobs: loss rate + top reasons (respects the report parameters) ===
// Most historical lost deals carry no structured reason, so derive one from notes/activity text.
window.deriveLossReason = function (d) {
  if (d.lossReason) return d.lossReason;
  const txt = ((d.notes || '') + ' ' + (d.activities || []).map(a => a.label || '').join(' ')).toLowerCase();
  if (!txt.trim()) return 'Not recorded';
  const has = (...ws) => ws.some(w => txt.includes(w));
  if (has('cheaper', 'competitor', 'another company', 'local cabinet', 'went with', 'better price', 'quote elsewhere', 'someone else')) return 'Went with a competitor';
  if (has('too expensive', 'expensive', 'price', 'cost too', 'over budget', 'out of budget', 'pricey', 'cheaper option')) return 'Too expensive / price';
  if (has('cancel', 'not proceeding', 'not going ahead', 'no longer', 'pulled out', 'sold the house', 'sold their house', 'not moving forward')) return 'Project cancelled';
  if (has('no response', 'no reply', 'went cold', 'not responding', 'no pick up', 'unable to contact', 'ghosted', 'no answer', 'stopped responding', 'no contact', 'unreachable')) return 'Not responding / went cold';
  if (has('budget', 'funds', 'finance', 'afford', 'loan', 'money')) return 'Budget fell through';
  if (has('changed their mind', 'changed mind', 'decided not', 'changed her mind', 'changed his mind', 'reconsider')) return 'Changed their mind';
  if (has('design', 'not happy', "didn't like", 'did not like', 'layout', 'unhappy')) return 'Not happy with the design';
  if (has('timeframe', 'time frame', 'delay', 'next year', 'on hold', 'waiting', 'timing', 'postpone', 'builder', 'not ready')) return 'Timeframe didn’t suit';
  return 'Other';
};

function _lostReasonRows(deals) {
  const lost = deals.filter(d => d.stage === 'lost');
  const agg = {};
  lost.forEach(d => {
    const r = window.deriveLossReason(d);
    if (!agg[r]) agg[r] = { count: 0, value: 0 };
    agg[r].count++; agg[r].value += d.value || 0;
  });
  const rows = Object.entries(agg)
    .map(([reason, v]) => ({ reason, count: v.count, value: v.value, share: lost.length ? v.count / lost.length : 0, avg: v.count ? v.value / v.count : 0 }))
    .sort((a, b) => b.count - a.count);
  return { lost, rows };
}

function _lostSections({ deals }) {
  const { rows } = _lostReasonRows(deals);
  return [{
    title: 'Reasons for lost jobs', columns: [
      { key: 'reason', label: 'Reason', type: 'text' },
      { key: 'count', label: 'Lost jobs', type: 'num' },
      { key: 'share', label: 'Share of losses', type: 'pct' },
      { key: 'value', label: 'Value lost (AUD)', type: 'money' },
    ], rows,
  }];
}

function LostReport({ deals, reps, showrooms, openDeal }) {
  const [drill, setDrill] = React.useState(null); // {type:'reason'|'rep', value, label}
  const lost = deals.filter(d => d.stage === 'lost');
  const sold = deals.filter(d => d.stage === 'sold');
  const closed = lost.length + sold.length;
  const lostRate = closed ? lost.length / closed : 0;
  const lostValue = lost.reduce((s, d) => s + (d.value || 0), 0);
  const storeName = (id) => (showrooms || []).find(s => s.id === id)?.name || id;

  // Cross-filter: each chart reflects the OTHER active selection so reason & designer move together
  const reasonBase = (drill && drill.type === 'rep') ? lost.filter(d => d.rep === drill.value) : lost;
  const repBase = (drill && drill.type === 'reason') ? lost.filter(d => window.deriveLossReason(d) === drill.value) : lost;
  const reasonRowsOf = (arr) => {
    const agg = {};
    arr.forEach(d => { const r = window.deriveLossReason(d); if (!agg[r]) agg[r] = { count: 0, value: 0 }; agg[r].count++; agg[r].value += d.value || 0; });
    return Object.entries(agg).map(([reason, v]) => ({ reason, count: v.count, value: v.value, share: arr.length ? v.count / arr.length : 0 })).sort((a, b) => b.count - a.count);
  };
  const rows = reasonRowsOf(reasonBase);
  const top5 = rows.slice(0, 5);
  const maxCount = top5[0]?.count || 1;

  // Lost jobs by designer (reflects a selected reason)
  const byRep = (reps || []).map(r => ({ id: r.id, name: r.name, count: repBase.filter(d => d.rep === r.id).length }))
    .filter(r => r.count > 0).sort((a, b) => b.count - a.count).slice(0, 8);
  const maxRep = byRep[0]?.count || 1;

  // Time from lead created → loss (compute from the SAME dates shown in the row)
  const daysToLoss = (d) => {
    if (d.created && d.lastActivity) {
      const diff = Math.round((new Date(d.lastActivity) - new Date(d.created)) / 86400000);
      if (!isNaN(diff)) return Math.max(0, diff);
    }
    if (d.createdDaysAgo != null && d.lastActivityDaysAgo != null) return Math.max(0, d.createdDaysAgo - d.lastActivityDaysAgo);
    return null;
  };
  const lostDate = (d) => d.lastActivity || d.expectedClose || '';
  const fullDate = (s) => { if (!s) return '—'; const dt = new Date(s); return isNaN(dt) ? s : dt.toLocaleDateString('en-AU', { day: 'numeric', month: 'short', year: 'numeric' }); };

  if (lost.length === 0) {
    return <div className="card card-pad"><div className="empty">No lost jobs in this scope. Adjust the parameters above.</div></div>;
  }

  // Jobs filtered by the active drill selection
  let jobs = lost;
  if (drill && drill.type === 'reason') jobs = lost.filter(d => window.deriveLossReason(d) === drill.value);
  else if (drill && drill.type === 'rep') jobs = lost.filter(d => d.rep === drill.value);
  jobs = jobs.slice().sort((a, b) => (daysToLoss(b) ?? 0) - (daysToLoss(a) ?? 0));
  const maxDays = Math.max(...jobs.map(d => daysToLoss(d) || 0), 1);
  const avgDays = jobs.length ? Math.round(jobs.reduce((s, d) => s + (daysToLoss(d) || 0), 0) / jobs.length) : 0;

  const isReasonSel = (r) => drill && drill.type === 'reason' && drill.value === r;
  const pickReason = (r) => setDrill(isReasonSel(r) ? null : { type: 'reason', value: r, label: r });
  const isRepSel = (id) => drill && drill.type === 'rep' && drill.value === id;
  const pickRep = (r) => setDrill(isRepSel(r.id) ? null : { type: 'rep', value: r.id, label: r.name });

  return (
    <div>
      <div className="grid-kpis">
        <Kpi label="Lost jobs" value={fmt.num(lost.length)} foot={`of ${closed} closed deals`} deltaPct={-0.02}/>
        <Kpi label="Loss rate" value={fmt.pct(lostRate, 1)} foot="lost ÷ (sold + lost)" deltaPct={-0.015} alert={lostRate > 0.5}/>
        <Kpi label="Value lost" value={fmt.money(lostValue, true)} foot="quoted value of lost jobs" deltaPct={-0.03}/>
        <Kpi label="Top reason" value={rows[0]?.reason || '—'} foot={`${rows[0]?.count || 0} jobs · ${fmt.pct(rows[0]?.share || 0, 0)} of losses`} deltaPct={0}/>
      </div>

      <div className="card" style={{ marginBottom: 18 }}>
        <div className="card-hd">
          <div>
            <div className="card-title">Top 5 reasons for lost jobs</div>
            <div className="card-sub">Click a reason to see the jobs &amp; how long each took to lose</div>
          </div>
        </div>
        <div className="card-pad" style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
          {top5.map((r, i) => (
            <button key={r.reason} onClick={() => pickReason(r.reason)}
              style={{
                display: 'grid', gridTemplateColumns: '24px 1fr 150px', alignItems: 'center', gap: 12,
                padding: '8px 8px', borderRadius: 8, textAlign: 'left', width: '100%', cursor: 'pointer',
                background: isReasonSel(r.reason) ? 'var(--lost-soft)' : 'transparent',
                border: '1px solid ' + (isReasonSel(r.reason) ? 'color-mix(in oklch, var(--lost) 35%, transparent)' : 'transparent'),
                transition: 'background 0.12s',
              }}
              onMouseEnter={(e) => { if (!isReasonSel(r.reason)) e.currentTarget.style.background = 'var(--surface-2)'; }}
              onMouseLeave={(e) => { if (!isReasonSel(r.reason)) e.currentTarget.style.background = 'transparent'; }}>
              <span style={{ width: 24, height: 24, borderRadius: 6, display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 12, fontWeight: 700, fontVariantNumeric: 'tabular-nums', background: i === 0 ? 'var(--lost)' : 'var(--lost-soft)', color: i === 0 ? 'white' : 'oklch(0.45 0.13 28)' }}>{i + 1}</span>
              <div>
                <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 4 }}>
                  <span style={{ fontSize: 13, fontWeight: 500 }}>{r.reason}</span>
                  <span style={{ fontSize: 12, color: 'var(--ink-3)' }}><span className="num" style={{ color: 'var(--ink-2)', fontWeight: 600 }}>{r.count}</span> · {fmt.pct(r.share, 0)}</span>
                </div>
                <div style={{ height: 10, background: 'var(--surface-3)', borderRadius: 5, overflow: 'hidden' }}>
                  <div style={{ width: (r.count / maxCount * 100) + '%', height: '100%', background: 'var(--lost)', borderRadius: 5, transition: 'width 0.4s ease' }}></div>
                </div>
              </div>
              <div style={{ textAlign: 'right', fontSize: 12, color: 'var(--ink-3)' }}>
                <span className="num" style={{ color: 'var(--ink-2)' }}>{fmt.money(r.value, true)}</span> lost
              </div>
            </button>
          ))}
        </div>
      </div>

      <div className="grid-2">
        <div className="card">
          <div className="card-hd">
            <div>
              <div className="card-title">All loss reasons</div>
              <div className="card-sub">Click a row to drill in · {lost.length} lost jobs</div>
            </div>
          </div>
          <div className="tbl-wrap">
            <table className="tbl">
              <thead>
                <tr>
                  <th>Reason</th>
                  <th className="num-col">Jobs</th>
                  <th className="num-col">% of losses</th>
                  <th className="num-col">Value lost</th>
                </tr>
              </thead>
              <tbody>
                {rows.map(r => (
                  <tr key={r.reason} onClick={() => pickReason(r.reason)} className={isReasonSel(r.reason) ? 'selected' : ''} style={{ cursor: 'pointer' }}>
                    <td>
                      <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                        <span style={{ width: 8, height: 8, borderRadius: 4, background: r.reason === 'Not recorded' ? 'var(--ink-4)' : 'var(--lost)', display: 'inline-block' }}></span>
                        <span style={{ fontWeight: 500 }}>{r.reason}</span>
                      </div>
                    </td>
                    <td className="num-col"><span className="num">{r.count}</span></td>
                    <td className="num-col"><span className="num">{fmt.pct(r.share, 0)}</span></td>
                    <td className="num-col"><span className="num">{r.value > 0 ? fmt.money(r.value, true) : '—'}</span></td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        </div>

        <div className="card">
          <div className="card-hd">
            <div>
              <div className="card-title">Lost jobs by designer</div>
              <div className="card-sub">Click a designer to drill in</div>
            </div>
          </div>
          <div className="card-pad">
            {byRep.length === 0 ? <div className="empty">No data.</div> : byRep.map(r => (
              <button key={r.id} onClick={() => pickRep(r)} className="chart-row" style={{ gridTemplateColumns: '120px 1fr 50px', width: '100%', textAlign: 'left', cursor: 'pointer', background: isRepSel(r.id) ? 'var(--lost-soft)' : 'transparent', border: 'none', borderRadius: 6, padding: '6px 6px' }}>
                <div className="lbl"><Avatar name={r.name} sz="sm"/><span style={{ fontSize: 12 }}>{r.name}</span></div>
                <div className="bar-wrap" style={{ height: 20 }}>
                  <div className="bar" style={{ width: (r.count / maxRep * 100) + '%', background: 'var(--lost)' }}>
                    {r.count / maxRep > 0.25 && <span className="num">{r.count}</span>}
                  </div>
                </div>
                <div className="val"><span className="num">{r.count}</span></div>
              </button>
            ))}
          </div>
        </div>
      </div>

      {/* Drill-down: the actual lost jobs + how long each took to lose */}
      <div className="card" style={{ marginTop: 18 }}>
        <div className="card-hd">
          <div>
            <div className="card-title">
              Lost jobs{drill ? ' · ' + drill.label : ''} <span className="num" style={{ color: 'var(--ink-3)', fontWeight: 400 }}>({jobs.length})</span>
            </div>
            <div className="card-sub">Time from lead created to loss{jobs.length ? ` · avg ${avgDays} days to loss` : ''}</div>
          </div>
          {drill && <button className="btn sm" onClick={() => setDrill(null)}><Icon name="close" size={11}/> Clear selection</button>}
        </div>
        <div className="tbl-wrap" style={{ maxHeight: 460, overflowY: 'auto' }}>
          <table className="tbl">
            <thead>
              <tr>
                <th>Customer</th>
                <th>Designer</th>
                <th>{window.T ? window.T('location') : 'Showroom'}</th>
                <th>Reason</th>
                <th className="num-col">Lead created</th>
                <th className="num-col">Lost on</th>
                <th>Time to loss</th>
                <th className="num-col">Value</th>
              </tr>
            </thead>
            <tbody>
              {jobs.slice(0, 150).map(d => {
                const days = daysToLoss(d);
                const barColor = days == null ? 'var(--ink-4)' : days > 120 ? 'var(--lost)' : days > 45 ? 'var(--warn)' : 'var(--won)';
                return (
                  <tr key={d.id} onClick={() => openDeal && openDeal(d)} style={{ cursor: openDeal ? 'pointer' : 'default' }}>
                    <td>
                      <div className="customer-cell">
                        <Avatar name={d.customer} sz="sm"/>
                        <div className="customer-meta"><div className="customer-name">{d.customer}</div><div className="customer-sub mono">{d.quoteNumber || d.id}</div></div>
                      </div>
                    </td>
                    <td style={{ fontSize: 12, color: 'var(--ink-2)' }}>{d.repName}</td>
                    <td style={{ fontSize: 12, color: 'var(--ink-2)' }}>{storeName(d.showroom)}</td>
                    <td><span className="pill" style={{ background: 'var(--lost-soft)', color: 'oklch(0.42 0.12 28)', fontSize: 10.5 }}>{window.deriveLossReason(d)}</span></td>
                    <td className="num-col" style={{ color: 'var(--ink-3)' }}><span className="num">{fullDate(d.created)}</span></td>
                    <td className="num-col" style={{ color: 'var(--ink-3)' }}><span className="num">{fullDate(lostDate(d))}</span></td>
                    <td style={{ minWidth: 150 }}>
                      <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                        <div style={{ flex: 1, height: 6, background: 'var(--surface-3)', borderRadius: 3, overflow: 'hidden' }}>
                          <div style={{ width: ((days || 0) / maxDays * 100) + '%', height: '100%', background: barColor, borderRadius: 3 }}></div>
                        </div>
                        <span className="num" style={{ fontSize: 11.5, color: 'var(--ink-2)', minWidth: 38, textAlign: 'right' }}>{days != null ? days + 'd' : '—'}</span>
                      </div>
                    </td>
                    <td className="num-col"><span className="num">{d.value > 0 ? fmt.money(d.value, true) : '—'}</span></td>
                  </tr>
                );
              })}
            </tbody>
          </table>
          {jobs.length > 150 && <div style={{ padding: 10, textAlign: 'center', fontSize: 11, color: 'var(--ink-3)' }}>Showing first 150 of {jobs.length} — narrow the parameters or pick a reason.</div>}
        </div>
      </div>
    </div>
  );
}

window.Reports = Reports;