// ====================================================================
// LEADS PAGE, full pipeline view, promoted from a Results sub-tab
// ====================================================================
const { useState: useStateL, useMemo: useMemoL } = React;

// ---------------------------------------------------------------
// Data, richer than the old 6-row sample
// ---------------------------------------------------------------
const LEADS_DATA = [
  { id:'L-2419', date:'Apr 14', time:'2:41 PM', who:'Maya K.',     intent:'Deep clean · 4-bed home',          ch:'LSA',    src:'Google Local Services',          type:'Call', dur:'14:21', status:'Booked',   val:680, score:94, owner:'Sarah K.', responded:'2m',  attribution:['Maps pack','LSA call'],          note:"Maya runs operations for Indeed. Asked about a recurring clean for their 40-person office floor. I matched her to your commercial tier." },
  { id:'L-2418', date:'Apr 14', time:'1:58 PM', who:'Derek T.',    intent:'Recurring service · bi-weekly',      ch:'PPC',    src:'Search · austin-house-cleaning',   type:'Form', dur:'–',     status:'Pending',  val:420, score:81, owner:'You',      responded:'–',   attribution:['Search ad','Landing page','Form'], note:"Derek filled the quote form Tuesday at 1:58 PM. Hasn't been replied to in 6h, peer median response is 18min. Likely a bi-weekly recurring plan, your AOV here is $480." },
  { id:'L-2417', date:'Apr 14', time:'12:14 PM',who:'Jen M.',      intent:'Move-out clean · 2-bed apt',    ch:'LSEO',   src:'Organic / Maps profile',         type:'Call', dur:'8:04',  status:'Booked',   val:540, score:88, owner:'Sarah K.', responded:'1m',  attribution:['Local 3-pack','GBP CTA'],         note:"Jen searched 'move out cleaning austin'. Maps profile served. She booked a Saturday slot." },
  { id:'L-2416', date:'Apr 13', time:'6:22 PM', who:'Ryan S.',     intent:'Recurring clean · 3-bed home',        ch:'Social', src:'Meta · carousel-spring',         type:'Chat', dur:'–',     status:'New',      val:280, score:72, owner:'Unassigned',responded:'–',   attribution:['Meta carousel','IG profile','DM'], note:"Cold chat from IG. Asked about monthly cleaning pricing. Reply window is closing, Meta DMs convert 3× better when answered <15min." },
  { id:'L-2415', date:'Apr 13', time:'5:41 PM', who:'Alicia B.',   intent:'Standard clean · 2-bed condo',      ch:'PPC',    src:'Search · standard-clean',           type:'Form', dur:'–',     status:'Booked',   val:320, score:79, owner:'Sarah K.', responded:'4m',  attribution:['Search ad','Standard-clean LP'],     note:"Repeat customer (3 prior cleans). Standard-clean LP form. Auto-confirmed and added to Sunday's route." },
  { id:'L-2414', date:'Apr 13', time:'4:08 PM', who:'Anonymous',   intent:'(Hung up)',                  ch:'LSA',    src:'Google Local Services',          type:'Call', dur:'0:14',  status:'No-show',  val:0,   score:18, owner:'–',        responded:'–',   attribution:['LSA listing','Click-to-call'],    note:"14-second hang-up. LSA dispute eligible, I've drafted the credit request, ready for your one-click submit." },
  { id:'L-2413', date:'Apr 13', time:'2:30 PM', who:'Tom W.',      intent:'Office contract · 12k sqft',          ch:'LSA',    src:'Google Local Services',          type:'Call', dur:'21:48', status:'Won',      val:1820,score:97, owner:'Sarah K.', responded:'1m',  attribution:['LSA call','Quote sent','Booked'], note:"BIG one. 12k-sqft office contract starting May 9. Sarah sent quote, customer paid 50% deposit. Net revenue $1,820." },
  { id:'L-2412', date:'Apr 12', time:'11:50 AM',who:'Priya N.',    intent:'Supply order · pickup',        ch:'PPC',    src:'PMax · evergreen',               type:'Form', dur:'–',     status:'Lost',     val:0,   score:64, owner:'You',      responded:'52m', attribution:['PMax video','PDP','Form'],        note:"Form filled, no answer for 52min. Customer went to a competitor. Add lead-routing automation to cut response to <10min." },
  { id:'L-2411', date:'Apr 12', time:'10:14 AM',who:'Marcus J.',   intent:'Deep clean · 3-bed home',        ch:'LSEO',   src:'Organic / blog',                 type:'Form', dur:'–',     status:'Booked',   val:240, score:75, owner:'You',      responded:'9m',  attribution:['Blog post','Inline CTA'],         note:"Found you via your 'best eco cleaners austin' blog post. Engaged for 7 minutes before clicking CTA." },
  { id:'L-2410', date:'Apr 12', time:'8:42 AM', who:'Gretchen P.', intent:'Office clean · weekly',          ch:'LSA',    src:'Google Local Services',          type:'Call', dur:'11:32', status:'Booked',   val:520, score:90, owner:'Sarah K.', responded:'1m',  attribution:['LSA call'],                       note:"Weekly office clean. Booked on first call." },
  { id:'L-2409', date:'Apr 11', time:'7:08 PM', who:'Kevin R.',    intent:'Recurring home clean · quote',          ch:'Social', src:'Meta · audience-lookalike',      type:'Click',dur:'–',     status:'Lost',     val:0,   score:45, owner:'–',        responded:'–',   attribution:['Meta image','Profile'],            note:"Clicked once, didn't engage. Low intent, Meta lookalike audience could use refresh." },
  { id:'L-2408', date:'Apr 11', time:'3:12 PM', who:'Sara L.',     intent:'Office clean · 1x trial',      ch:'PPC',    src:'Search · office-cleaning-near-me',      type:'Call', dur:'9:14',  status:'Won',      val:380, score:84, owner:'Sarah K.', responded:'1m',  attribution:['Search ad','Click-to-call'],      note:"Booked $380 trial clean for tomorrow." },
];

const STATUS_BADGE = {
  'New':     'badge-info',
  'Pending': 'badge-warn',
  'Booked':  'badge-ok',
  'Won':     'badge-ok',
  'Lost':    'badge-bad',
  'No-show': 'badge-neutral',
};
const CHANNEL_COLOR = {
  PPC:    'var(--ch-ppc)',
  LSA:    'var(--ch-lsa)',
  LSEO:   'var(--ch-seo)',
  Social: 'var(--ch-social)',
};

// ---------------------------------------------------------------
// LEADS PAGE, persona router. HQ gets a portfolio-level operational
// health view; franchisees get the original lead-pipeline view.
// ---------------------------------------------------------------
function LeadsPage({ data, persona }) {
  if (persona === 'hq') return <LeadsPageHq data={data}/>;
  return <LeadsPageFranchisee data={data}/>;
}
window.LeadsPage = LeadsPage;

// ---------------------------------------------------------------
// LEADS PAGE, FRANCHISEE (original)
// ---------------------------------------------------------------
function LeadsPageFranchisee({ data }) {
  const [range,    setRange]    = useStateL({ preset: 'last30', start: new Date(2026, 2, 29), end: new Date(2026, 3, 27), compare: false });
  const [chan,     setChan]     = useStateL('all');
  const [stat,     setStat]     = useStateL('all');
  const [q,        setQ]        = useStateL('');
  const [sort,     setSort]     = useStateL('date');
  const [detail,   setDetail]   = useStateL(null);
  const [selected, setSelected] = useStateL(new Set());

  // Filtering + sorting
  const filtered = useMemoL(() => {
    let out = LEADS_DATA;
    if (chan !== 'all') out = out.filter(r => r.ch === chan);
    if (stat !== 'all') out = out.filter(r => r.status === stat);
    if (q.trim()) {
      const s = q.toLowerCase();
      out = out.filter(r => (r.who + r.intent + r.id + r.src).toLowerCase().includes(s));
    }
    if (sort === 'value') out = [...out].sort((a, b) => b.val - a.val);
    else if (sort === 'score') out = [...out].sort((a, b) => b.score - a.score);
    return out;
  }, [chan, stat, q, sort]);

  // Stats, over the *unfiltered* dataset, gives stable KPI ribbon
  const total       = LEADS_DATA.length;
  // Headline count comes from the same source Home uses, so the two pages agree.
  // LEADS_DATA.length stays the source of truth for table-level counters and
  // sample-based funnel + channel-mix math.
  const totalCaptured = data.kpis.leads.val;
  const booked      = LEADS_DATA.filter(r => r.status === 'Booked' || r.status === 'Won').length;
  const lost        = LEADS_DATA.filter(r => r.status === 'Lost').length;
  const noshow      = LEADS_DATA.filter(r => r.status === 'No-show').length;
  const newPending  = LEADS_DATA.filter(r => r.status === 'New' || r.status === 'Pending').length;
  const wonRev      = LEADS_DATA.filter(r => r.status === 'Won' || r.status === 'Booked').reduce((s, r) => s + r.val, 0);
  const bookedRate  = Math.round(booked / total * 100);
  const avgScore    = Math.round(LEADS_DATA.reduce((s, r) => s + r.score, 0) / total);

  const channelMix = (data.variant === 'bare' ? ['PPC'] : ['LSA','PPC','LSEO','Social']).map(c => ({
    ch: c,
    cnt: data.variant === 'bare' ? LEADS_DATA.length : LEADS_DATA.filter(r => r.ch === c).length,
    rev: data.variant === 'bare'
      ? LEADS_DATA.filter(r => r.status === 'Won' || r.status === 'Booked').reduce((s, r) => s + r.val, 0)
      : LEADS_DATA.filter(r => r.ch === c && (r.status === 'Won' || r.status === 'Booked')).reduce((s, r) => s + r.val, 0),
    color: CHANNEL_COLOR[c],
  }));

  // Funnel buckets
  const funnel = [
    { l: 'Captured',  v: total,                     fg: 'var(--ink-3)' },
    { l: 'Contacted', v: total - newPending,        fg: 'var(--sky)' },
    { l: 'Qualified', v: booked + lost,             fg: 'var(--marigold)' },
    { l: 'Booked',    v: booked,                    fg: 'var(--sage)' },
    { l: 'Won',       v: LEADS_DATA.filter(r => r.status === 'Won').length, fg: 'var(--terra)' },
  ];
  const funnelMax = funnel[0].v;

  const toggleSel = id => {
    setSelected(s => {
      const next = new Set(s);
      if (next.has(id)) next.delete(id); else next.add(id);
      return next;
    });
  };
  const allSelected = filtered.length > 0 && filtered.every(r => selected.has(r.id));
  const toggleAll = () => {
    if (allSelected) setSelected(new Set());
    else setSelected(new Set(filtered.map(r => r.id)));
  };

  return (
    <div className="page active">
      <div className="page-header">
        <div>
          <div className="page-title">Leads.</div>
          <div className="page-sub">{totalCaptured} captured · <strong style={{ color: 'var(--sage)' }}>{booked} booked</strong> · ${wonRev.toLocaleString()} attributed revenue. Median response time <strong style={{ color: 'var(--ink-2)' }}>4 min</strong>, peer median 18 min.</div>
        </div>
        <div className="page-actions">
          <DateRangePicker value={range} onChange={v => { setRange(v); T('Range updated', `Showing ${v.preset && v.preset !== 'custom' ? (window.DRP_PRESET_LABEL?.(v.preset) || 'selected range') : `${(v.start.getMonth()+1)}/${v.start.getDate()} – ${(v.end.getMonth()+1)}/${v.end.getDate()}`}`, 'info'); }} align="right"/>
          <button className="btn btn-ghost btn-sm" onClick={() => T('Export queued', 'CSV with all leads + Lenny notes, emailing you in ~30s.', 'ok')}>Export CSV</button>
          <button className="btn btn-primary btn-sm" onClick={() => T('Routing rule saved', "Lenny will auto-assign new leads, calls to Sarah, forms to you, social DMs to whoever's on shift.", 'ok')}>Lead Routing</button>
        </div>
      </div>

      {/* KPI strip */}
      <div className="grid grid-4" style={{ marginBottom: 16 }}>
        <KpiCard label="Total leads" value={totalCaptured} delta={18} trend={[8,12,10,14,11,16,18,15,19,17,22,20]} color="var(--terra)" dotColor="var(--terra)"/>
        <KpiCard label="Booked rate" value={`${bookedRate}%`} delta={6} trend={[42,44,46,48,50,52,55,58,60,62,64,66]} color="var(--sage)" dotColor="var(--sage)"/>
        <KpiCard label="Won revenue" value={`$${(wonRev/1000).toFixed(1)}K`} delta={22} trend={[2.1,2.4,2.8,3.2,3.6,3.9,4.1,4.4,4.7,5.0,5.3,5.6]} color="var(--marigold)" dotColor="var(--marigold)"/>
        <KpiCard label="Lead quality (Lenny)" value={avgScore} delta={4} trend={[68,70,71,72,73,74,75,76,76,77,78,78]} color="var(--violet)" dotColor="var(--violet)"/>
      </div>

      <SourceNote>Source: Telmetrics or other + FreshNest CRM</SourceNote>

      {/* Lenny opportunity strip */}
      <div className="lenny-card" style={{ marginBottom: 20, padding: '14px 18px', display: 'flex', alignItems: 'center', gap: 14 }}>
        <div style={{ width: 38, height: 38, background: 'var(--paper)', border: '2px solid var(--cream-3)', borderRadius: '50%', overflow: 'hidden', flexShrink: 0, position: 'relative', zIndex: 1 }}>
          <LennyLlama size={38} mood="alert" bg="circle"/>
        </div>
        <div style={{ position: 'relative', zIndex: 1, flex: 1, fontSize: 13.5 }}>
          <strong style={{ color: 'var(--ink)' }}>2 leads need you right now.</strong>
          <span style={{ color: 'var(--ink-3)' }}> Derek T. (form, 6h) and Ryan S. (Meta DM), both unanswered. I drafted replies for both. Approving them now would protect ~$700 in pipeline.</span>
        </div>
        <button className="btn btn-terra btn-sm" style={{ position: 'relative', zIndex: 1 }} onClick={() => T('Replies sent', 'Both drafts approved and delivered, Derek by SMS, Ryan via IG DM.', 'ok')}>Approve both →</button>
      </div>

      {/* Channel mix + funnel */}
      <div className="grid grid-2" style={{ marginBottom: 20 }}>
        {/* Channel mix */}
        <div className="card card-pad">
          <div className="card-header" style={{ marginBottom: 14 }}>
            <div>
              <div className="card-title">Where leads came from</div>
              <div className="card-sub">{total} this period · won revenue by channel</div>
            </div>
          </div>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
            {channelMix.map(c => {
              const pct = (c.cnt / total) * 100;
              return (
                <div key={c.ch}>
                  <div style={{ display: 'flex', alignItems: 'baseline', marginBottom: 6 }}>
                    <span style={{ width: 8, height: 8, borderRadius: '50%', background: c.color, marginRight: 10 }}/>
                    <span style={{ fontSize: 13, fontWeight: 600, color: 'var(--ink)' }}>{c.ch}</span>
                    <span style={{ flex: 1, fontSize: 11.5, color: 'var(--ink-4)', marginLeft: 10 }}>${c.rev.toLocaleString()} attributed</span>
                    <span style={{ fontFamily: 'var(--ff-display)', fontSize: 19, fontWeight: 500, color: 'var(--ink)', fontVariantNumeric: 'tabular-nums' }}>{c.cnt}</span>
                    <span style={{ fontSize: 11, color: 'var(--ink-4)', marginLeft: 4 }}>leads</span>
                  </div>
                  <div style={{ height: 6, background: 'var(--cream-2)', borderRadius: 3, overflow: 'hidden' }}>
                    <div style={{ width: pct + '%', height: '100%', background: c.color, borderRadius: 3 }}/>
                  </div>
                </div>
              );
            })}
          </div>
        </div>

        {/* Funnel */}
        <div className="card card-pad">
          <div className="card-header" style={{ marginBottom: 14 }}>
            <div>
              <div className="card-title">Pipeline funnel</div>
              <div className="card-sub">Captured → Won · {Math.round(LEADS_DATA.filter(r => r.status === 'Won').length / total * 100)}% close rate</div>
            </div>
            <span className="badge badge-ok">↓ 23% drop-off</span>
          </div>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
            {funnel.map((f, i) => {
              const pct = (f.v / funnelMax) * 100;
              const dropPct = i > 0 ? Math.round((1 - f.v / funnel[i-1].v) * 100) : 0;
              return (
                <div key={f.l}>
                  <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
                    <div style={{ width: 80, fontSize: 11.5, color: 'var(--ink-3)', fontWeight: 600 }}>{f.l}</div>
                    <div style={{ flex: 1, position: 'relative', height: 22, background: 'var(--cream-2)', borderRadius: 4, overflow: 'hidden' }}>
                      <div style={{ position: 'absolute', inset: 0, width: pct + '%', background: f.fg, opacity: 0.18 }}/>
                      <div style={{ position: 'absolute', inset: 0, width: pct + '%', borderRight: `2px solid ${f.fg}` }}/>
                      <div style={{ position: 'absolute', inset: 0, paddingLeft: 10, display: 'flex', alignItems: 'center', fontSize: 11.5, fontWeight: 700, color: 'var(--ink)' }}>{f.v}</div>
                    </div>
                    {i > 0 && <div style={{ width: 50, fontSize: 10.5, color: dropPct > 30 ? 'var(--terra)' : 'var(--ink-4)', fontFamily: 'var(--ff-mono)', textAlign: 'right' }}>−{dropPct}%</div>}
                  </div>
                </div>
              );
            })}
          </div>
          <div style={{ marginTop: 14, paddingTop: 12, borderTop: '1px dashed var(--cream-3)', fontSize: 11.5, color: 'var(--ink-3)', display: 'flex', alignItems: 'center', gap: 8 }}>
            <span style={{ width: 6, height: 6, borderRadius: '50%', background: 'var(--terra)' }}/>
            Biggest leak: <strong style={{ color: 'var(--ink-2)' }}>Captured → Contacted</strong>. Lead routing fixes this, Lenny modeled +9 booked/mo.
          </div>
        </div>
      </div>

      <SourceNote>Source: Google Ads + FreshNest CRM</SourceNote>

      {/* Filter ribbon */}
      <div style={{ display: 'flex', gap: 8, alignItems: 'center', marginBottom: 12, flexWrap: 'wrap' }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 6, padding: '6px 10px', background: 'var(--paper)', border: '1px solid var(--line)', borderRadius: 8, flex: '0 1 280px' }}>
          <span style={{ color: 'var(--ink-4)' }}>{Icon.search}</span>
          <input value={q} onChange={e => setQ(e.target.value)} placeholder="Search leads, intent, ID…"
                 style={{ border: 0, outline: 'none', flex: 1, fontSize: 12.5, background: 'transparent', color: 'var(--ink)' }}/>
          {q && <button className="btn btn-quiet btn-xs" onClick={() => setQ('')} style={{ padding: '2px 6px' }}>×</button>}
        </div>

        <FilterChip label="Channel" value={chan} onPick={setChan} options={[
          { v: 'all', l: 'All channels' }, { v: 'LSA', l: 'LSA' }, { v: 'PPC', l: 'Paid Search' }, { v: 'LSEO', l: 'Local SEO' }, { v: 'Social', l: 'Social' },
        ]}/>
        <FilterChip label="Status" value={stat} onPick={setStat} options={[
          { v: 'all', l: 'All statuses' }, { v: 'New', l: 'New' }, { v: 'Pending', l: 'Pending' }, { v: 'Booked', l: 'Booked' }, { v: 'Won', l: 'Won' }, { v: 'Lost', l: 'Lost' }, { v: 'No-show', l: 'No-show' },
        ]}/>
        <FilterChip label="Sort" value={sort} onPick={setSort} options={[
          { v: 'date', l: 'Newest' }, { v: 'value', l: 'Highest value' }, { v: 'score', l: 'Highest quality' },
        ]}/>

        <span style={{ flex: 1 }}/>
        <span style={{ fontSize: 11.5, color: 'var(--ink-4)', fontFamily: 'var(--ff-mono)' }}>{filtered.length} of {total}</span>
      </div>

      {/* Bulk actions bar, only when selections exist */}
      {selected.size > 0 && (
        <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 10, padding: '10px 14px', background: 'var(--ink)', color: 'var(--cream)', borderRadius: 8, fontSize: 12.5 }}>
          <strong>{selected.size} selected</strong>
          <span style={{ color: 'rgba(250,246,239,.6)' }}>·</span>
          <button className="btn btn-xs" style={{ background: 'transparent', color: 'var(--cream)', border: '1px solid rgba(250,246,239,.25)' }} onClick={() => { T('Bulk update', `Marked ${selected.size} leads as Booked.`, 'ok'); setSelected(new Set()); }}>Mark booked</button>
          <button className="btn btn-xs" style={{ background: 'transparent', color: 'var(--cream)', border: '1px solid rgba(250,246,239,.25)' }} onClick={() => { T('Assigned', `Assigned ${selected.size} leads to Sarah K.`, 'info'); setSelected(new Set()); }}>Assign owner</button>
          <button className="btn btn-xs" style={{ background: 'transparent', color: 'var(--cream)', border: '1px solid rgba(250,246,239,.25)' }} onClick={() => { T('Reviews requested', `${selected.size} review-request emails queued (sends after booking confirmation).`, 'ok'); setSelected(new Set()); }}>Request reviews</button>
          <button className="btn btn-xs" style={{ background: 'transparent', color: 'var(--cream)', border: '1px solid rgba(250,246,239,.25)' }} onClick={() => T('Export started', 'CSV with selected leads + Lenny notes.', 'ok')}>Export selected</button>
          <span style={{ flex: 1 }}/>
          <button className="btn btn-xs" style={{ background: 'transparent', color: 'var(--cream)', border: '1px solid rgba(250,246,239,.25)' }} onClick={() => setSelected(new Set())}>Clear</button>
        </div>
      )}

      {/* The big table */}
      <div className="section-label">Recent leads <span className="count">click any row for full detail · {filtered.length} of {total} shown · sample of {totalCaptured} MTD</span></div>
      <div className="card" style={{ overflow: 'hidden' }}>
        <table className="tbl">
          <thead>
            <tr>
              <th style={{ width: 32, paddingLeft: 20 }}>
                <input type="checkbox" checked={allSelected} onChange={toggleAll} style={{ cursor: 'pointer' }}/>
              </th>
              <th style={{ width: 110 }}>When</th>
              <th>Lead</th>
              <th>Intent</th>
              <th style={{ width: 80 }}>Channel</th>
              <th style={{ width: 70 }}>Type</th>
              <th style={{ width: 90 }}>Status</th>
              <th style={{ width: 80 }}>Quality</th>
              <th style={{ textAlign: 'right', width: 90 }}>Value</th>
              <th style={{ width: 70 }}>Owner</th>
              <th style={{ width: 80 }}/>
            </tr>
          </thead>
          <tbody>
            {filtered.map(r => (
              <tr key={r.id} onClick={() => setDetail(r)} style={{ cursor: 'pointer' }}>
                <td style={{ paddingLeft: 20 }} onClick={e => e.stopPropagation()}>
                  <input type="checkbox" checked={selected.has(r.id)} onChange={() => toggleSel(r.id)} style={{ cursor: 'pointer' }}/>
                </td>
                <td>
                  <div className="mono" style={{ fontSize: 11.5, color: 'var(--ink-2)' }}>{r.date}</div>
                  <div style={{ fontSize: 10.5, color: 'var(--ink-4)' }}>{r.time}</div>
                </td>
                <td>
                  <div style={{ fontWeight: 600, color: 'var(--ink)' }}>{r.who}</div>
                  <div style={{ fontSize: 10.5, color: 'var(--ink-4)', fontFamily: 'var(--ff-mono)' }}>{r.id}</div>
                </td>
                <td className="muted" style={{ fontSize: 12 }}>{r.intent}</td>
                <td>
                  <span style={{ display: 'inline-flex', alignItems: 'center', gap: 6, fontSize: 11, fontWeight: 600, padding: '2px 8px', borderRadius: 999, background: 'var(--cream-2)', color: 'var(--ink-2)' }}>
                    <span style={{ width: 6, height: 6, borderRadius: '50%', background: CHANNEL_COLOR[r.ch] }}/>
                    {r.ch}
                  </span>
                </td>
                <td style={{ fontSize: 11.5, color: 'var(--ink-3)' }}>
                  {r.type}{r.dur !== '–' && <div style={{ fontSize: 10, color: 'var(--ink-4)', fontFamily: 'var(--ff-mono)' }}>{r.dur}</div>}
                </td>
                <td><span className={`badge ${STATUS_BADGE[r.status]}`}>{r.status}</span></td>
                <td><QualityPip score={r.score}/></td>
                <td style={{ textAlign: 'right' }} className="strong">{r.val > 0 ? `$${r.val}` : <span style={{ color: 'var(--ink-4)' }}>–</span>}</td>
                <td style={{ fontSize: 11.5, color: 'var(--ink-3)' }}>{r.owner}</td>
                <td onClick={e => e.stopPropagation()}>
                  <button className="btn btn-quiet btn-xs" onClick={() => setDetail(r)}>Open →</button>
                </td>
              </tr>
            ))}
            {filtered.length === 0 && (
              <tr><td colSpan="11" style={{ textAlign: 'center', padding: 40, color: 'var(--ink-4)' }}>No leads match those filters.</td></tr>
            )}
          </tbody>
        </table>
      </div>

      <LeadDetailDrawer lead={detail} onClose={() => setDetail(null)}/>
    </div>
  );
}
// ---------------------------------------------------------------
// FilterChip, compact dropdown used across the filter ribbon
// ---------------------------------------------------------------
function FilterChip({ label, value, onPick, options }) {
  const [open, setOpen] = useStateL(false);
  const cur = options.find(o => o.v === value);
  return (
    <div style={{ position: 'relative' }}>
      <button onClick={() => setOpen(o => !o)}
              style={{ display: 'inline-flex', alignItems: 'center', gap: 6, padding: '7px 12px',
                       background: value !== 'all' && value !== 'date' ? 'var(--ink)' : 'var(--paper)',
                       color: value !== 'all' && value !== 'date' ? 'var(--cream)' : 'var(--ink-2)',
                       border: '1px solid var(--line)', borderRadius: 8, fontSize: 12, fontWeight: 600, cursor: 'pointer' }}>
        <span style={{ color: value !== 'all' && value !== 'date' ? 'rgba(250,246,239,.6)' : 'var(--ink-4)', fontWeight: 500 }}>{label}:</span>
        <span>{cur?.l}</span>
        <span style={{ fontSize: 9, opacity: .6 }}>▾</span>
      </button>
      {open && (
        <>
          <div onClick={() => setOpen(false)} style={{ position: 'fixed', inset: 0, zIndex: 90 }}/>
          <div style={{ position: 'absolute', top: 'calc(100% + 4px)', left: 0, zIndex: 91, background: 'var(--paper)', border: '1px solid var(--line)', borderRadius: 8, boxShadow: '0 8px 24px rgba(45,32,18,.12)', minWidth: 160, padding: 4 }}>
            {options.map(o => (
              <button key={o.v} onClick={() => { onPick(o.v); setOpen(false); }}
                      style={{ display: 'block', width: '100%', textAlign: 'left', padding: '8px 12px', borderRadius: 6, fontSize: 12.5, color: o.v === value ? 'var(--ink)' : 'var(--ink-2)', background: o.v === value ? 'var(--cream-2)' : 'transparent', fontWeight: o.v === value ? 600 : 500, cursor: 'pointer' }}>
                {o.l}
              </button>
            ))}
          </div>
        </>
      )}
    </div>
  );
}

// ---------------------------------------------------------------
// QualityPip, inline mini-bar showing Lenny's lead-quality score
// ---------------------------------------------------------------
function QualityPip({ score }) {
  const tone = score >= 80 ? 'var(--sage)' : score >= 60 ? 'var(--marigold)' : 'var(--terra)';
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
      <div style={{ width: 36, height: 5, background: 'var(--cream-2)', borderRadius: 3, overflow: 'hidden' }}>
        <div style={{ width: score + '%', height: '100%', background: tone, borderRadius: 3 }}/>
      </div>
      <span style={{ fontSize: 11, fontWeight: 700, color: tone, fontFamily: 'var(--ff-mono)' }}>{score}</span>
    </div>
  );
}

// ---------------------------------------------------------------
// LeadDetailDrawer, slides in from right with full context + actions
// ---------------------------------------------------------------
function LeadDetailDrawer({ lead, onClose }) {
  if (!lead) return null;
  const dispute = lead.ch === 'LSA' && (lead.status === 'No-show' || lead.dur === '0:14');

  return (
    <window.ModalPortal><>
      <div onClick={onClose} style={{ position: 'fixed', inset: 0, background: 'rgba(45,32,18,.32)', zIndex: 200, animation: 'fadeIn .15s ease' }}/>
      <div style={{ position: 'fixed', top: 0, right: 0, bottom: 0, width: 540, background: 'var(--paper)', borderLeft: '1px solid var(--line)', boxShadow: '-12px 0 32px rgba(45,32,18,.12)', zIndex: 201, display: 'flex', flexDirection: 'column', animation: 'slideInRight .2s ease' }}>
        {/* Header */}
        <div style={{ padding: '20px 24px 16px', borderBottom: '1px solid var(--line)' }}>
          <div style={{ display: 'flex', alignItems: 'flex-start', gap: 12 }}>
            <div style={{ width: 44, height: 44, borderRadius: '50%', background: CHANNEL_COLOR[lead.ch], opacity: 0.15, color: CHANNEL_COLOR[lead.ch], display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0, fontSize: 16, fontWeight: 700, fontFamily: 'var(--ff-display)' }}>
              {lead.who.split(' ').map(n => n[0]).join('').slice(0, 2)}
            </div>
            <div style={{ flex: 1 }}>
              <div style={{ display: 'flex', alignItems: 'baseline', gap: 8, marginBottom: 2 }}>
                <div style={{ fontFamily: 'var(--ff-display)', fontSize: 20, fontWeight: 600, color: 'var(--ink)', letterSpacing: '-0.015em' }}>{lead.who}</div>
                <div style={{ fontSize: 11, color: 'var(--ink-4)', fontFamily: 'var(--ff-mono)' }}>{lead.id}</div>
              </div>
              <div style={{ fontSize: 13, color: 'var(--ink-3)' }}>{lead.intent}</div>
              <div style={{ display: 'flex', gap: 6, marginTop: 8, flexWrap: 'wrap' }}>
                <span className={`badge ${STATUS_BADGE[lead.status]}`}>{lead.status}</span>
                <span style={{ fontSize: 11, fontWeight: 600, padding: '2px 8px', borderRadius: 999, background: 'var(--cream-2)', color: 'var(--ink-2)', display: 'inline-flex', alignItems: 'center', gap: 6 }}>
                  <span style={{ width: 6, height: 6, borderRadius: '50%', background: CHANNEL_COLOR[lead.ch] }}/>
                  {lead.ch} · {lead.src}
                </span>
                <span style={{ fontSize: 11, fontWeight: 600, padding: '2px 8px', borderRadius: 999, background: 'var(--violet-soft, rgba(77,105,137,.12))', color: 'var(--violet)' }}>Quality {lead.score}</span>
              </div>
            </div>
            <button onClick={onClose} style={{ background: 'transparent', border: 'none', fontSize: 22, color: 'var(--ink-3)', cursor: 'pointer', lineHeight: 1, padding: 0 }}>×</button>
          </div>
        </div>

        {/* Scrollable body */}
        <div style={{ flex: 1, overflowY: 'auto', padding: '20px 24px' }}>
          {/* Quick stats grid */}
          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 10, marginBottom: 20 }}>
            {[
              ['Captured', `${lead.date} · ${lead.time}`],
              ['Contact', lead.type + (lead.dur !== '–' ? ` · ${lead.dur}` : '')],
              ['Response', lead.responded === '–' ? 'Pending' : lead.responded],
              ['Est. value', lead.val > 0 ? `$${lead.val}` : '–'],
            ].map(([l, v]) => (
              <div key={l} style={{ padding: '10px 12px', background: 'var(--cream)', borderRadius: 8, border: '1px solid var(--cream-3)' }}>
                <div style={{ fontSize: 9.5, color: 'var(--ink-4)', textTransform: 'uppercase', letterSpacing: '0.06em', fontWeight: 700, marginBottom: 4 }}>{l}</div>
                <div style={{ fontSize: 12.5, fontWeight: 600, color: 'var(--ink)' }}>{v}</div>
              </div>
            ))}
          </div>

          {/* Lenny's note */}
          <div className="lenny-card" style={{ padding: '14px 16px', marginBottom: 20 }}>
            <div style={{ display: 'flex', gap: 10, alignItems: 'flex-start', position: 'relative', zIndex: 1 }}>
              <div style={{ width: 30, height: 30, background: 'var(--paper)', border: '2px solid var(--cream-3)', borderRadius: '50%', overflow: 'hidden', flexShrink: 0 }}>
                <LennyLlama size={30} mood="thinking" bg="circle"/>
              </div>
              <div style={{ flex: 1, fontSize: 12.5, color: 'var(--ink-2)', lineHeight: 1.55 }}>
                <div style={{ fontSize: 10, fontWeight: 700, color: 'var(--violet)', textTransform: 'uppercase', letterSpacing: '0.06em', marginBottom: 4 }}>Lenny's note</div>
                {lead.note}
              </div>
            </div>
          </div>

          {/* Attribution path */}
          <div style={{ marginBottom: 20 }}>
            <div style={{ fontSize: 10.5, color: 'var(--ink-4)', textTransform: 'uppercase', letterSpacing: '0.07em', fontWeight: 700, marginBottom: 8 }}>Attribution path</div>
            <div style={{ display: 'flex', alignItems: 'center', gap: 6, flexWrap: 'wrap' }}>
              {lead.attribution.map((a, i) => (
                <React.Fragment key={i}>
                  <span style={{ padding: '5px 10px', background: 'var(--cream-2)', border: '1px solid var(--cream-3)', borderRadius: 6, fontSize: 11.5, color: 'var(--ink-2)', fontWeight: 500 }}>{a}</span>
                  {i < lead.attribution.length - 1 && <span style={{ color: 'var(--ink-4)' }}>→</span>}
                </React.Fragment>
              ))}
            </div>
          </div>

          {/* Timeline */}
          <div style={{ marginBottom: 20 }}>
            <div style={{ fontSize: 10.5, color: 'var(--ink-4)', textTransform: 'uppercase', letterSpacing: '0.07em', fontWeight: 700, marginBottom: 12 }}>Timeline</div>
            <div style={{ position: 'relative', paddingLeft: 18 }}>
              <div style={{ position: 'absolute', left: 6, top: 6, bottom: 6, width: 1, background: 'var(--cream-3)' }}/>
              {[
                { dot: 'var(--terra)', t: lead.time, l: 'Lead captured', s: `${lead.ch} · ${lead.src}` },
                { dot: 'var(--sky)',   t: '+ 2 min', l: 'Lenny scored as ' + (lead.score >= 80 ? 'High intent' : lead.score >= 60 ? 'Medium intent' : 'Low intent'), s: `Match score ${lead.score}/100` },
                { dot: 'var(--sage)',  t: lead.responded === '–' ? '–' : '+ ' + lead.responded, l: lead.responded === '–' ? 'Awaiting response' : `Responded by ${lead.owner}`, s: lead.responded === '–' ? 'No outreach yet, peer median 18min' : 'Within SLA' },
                ...(lead.status === 'Booked' || lead.status === 'Won' ? [{ dot: 'var(--ok)', t: 'Today', l: lead.status === 'Won' ? `Booked + revenue logged` : 'Booked', s: `$${lead.val} attributed` }] : []),
                ...(lead.status === 'Lost' ? [{ dot: 'var(--terra)', t: 'Today', l: 'Marked Lost', s: 'No follow-up scheduled' }] : []),
                ...(lead.status === 'No-show' ? [{ dot: 'var(--terra)', t: 'Today', l: 'No-show / hang-up', s: 'LSA dispute eligible' }] : []),
              ].map((e, i) => (
                <div key={i} style={{ position: 'relative', paddingBottom: 14, paddingLeft: 14 }}>
                  <div style={{ position: 'absolute', left: -1, top: 4, width: 13, height: 13, borderRadius: '50%', border: '2px solid var(--paper)', background: e.dot }}/>
                  <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', gap: 8 }}>
                    <div style={{ fontSize: 12.5, fontWeight: 600, color: 'var(--ink)' }}>{e.l}</div>
                    <div style={{ fontSize: 10.5, color: 'var(--ink-4)', fontFamily: 'var(--ff-mono)' }}>{e.t}</div>
                  </div>
                  <div style={{ fontSize: 11.5, color: 'var(--ink-3)', marginTop: 2 }}>{e.s}</div>
                </div>
              ))}
            </div>
          </div>

          {/* Peer benchmark */}
          <div style={{ padding: '12px 14px', background: 'var(--cream)', border: '1px solid var(--cream-3)', borderRadius: 8, fontSize: 12, color: 'var(--ink-3)', lineHeight: 1.55, marginBottom: 8 }}>
            <strong style={{ color: 'var(--ink-2)' }}>Peer benchmark:</strong> {lead.intent.split('·')[0].trim()} leads from <strong style={{ color: 'var(--ink-2)' }}>{lead.ch}</strong> book at <strong style={{ color: 'var(--sage)' }}>{lead.score >= 80 ? '78%' : lead.score >= 60 ? '54%' : '22%'}</strong> across FreshNest locations · avg ticket <strong style={{ color: 'var(--ink-2)' }}>${Math.round(lead.val * 1.15) || 380}</strong>.
          </div>
        </div>

        {/* Sticky action footer */}
        <div style={{ padding: '14px 24px', borderTop: '1px solid var(--line)', background: 'var(--cream)' }}>
          {dispute && (
            <div style={{ marginBottom: 10, padding: '10px 12px', background: 'var(--marigold-soft)', border: '1px solid var(--marigold)', borderRadius: 8, fontSize: 12, color: '#725900', display: 'flex', alignItems: 'center', gap: 10 }}>
              <span>⚠️ <strong>LSA dispute eligible</strong>, call ≤30s + no booking. Lenny drafted the credit request.</span>
              <span style={{ flex: 1 }}/>
              <button className="btn btn-marigold btn-xs" onClick={() => T('Dispute submitted', 'Credit request sent to Google LSA, typically resolved in 2–4 days.', 'ok')}>Submit dispute</button>
            </div>
          )}
          <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
            {lead.status === 'New' || lead.status === 'Pending' ? (
              <>
                <button className="btn btn-primary btn-sm" onClick={() => { T('Reply sent', `Lenny's draft delivered to ${lead.who}.`, 'ok'); onClose(); }}>Send Lenny's reply</button>
                <button className="btn btn-ghost btn-sm" onClick={() => T('Booked', 'Marked as booked + revenue logged.', 'ok')}>Mark booked</button>
                <button className="btn btn-ghost btn-sm" onClick={() => T('Snoozed', `${lead.who} resurfaces tomorrow 9 AM.`, 'info')}>Snooze 24h</button>
              </>
            ) : lead.status === 'Booked' || lead.status === 'Won' ? (
              <>
                <button className="btn btn-primary btn-sm" onClick={() => { T('Review request sent', `Auto-email queued for after the booking date.`, 'ok'); }}>Request a review</button>
                <button className="btn btn-ghost btn-sm" onClick={() => T('Revenue updated', 'Logged actual ticket, attribution refreshed.', 'ok')}>Log actual revenue</button>
              </>
            ) : (
              <>
                <button className="btn btn-primary btn-sm" onClick={() => { T('Reopened', 'Status set to Pending, assigned to you.', 'info'); onClose(); }}>Reopen lead</button>
              </>
            )}
            <span style={{ flex: 1 }}/>
            <button className="btn btn-quiet btn-sm" onClick={() => T('Reassigned', `${lead.who} reassigned to Sarah K.`, 'info')}>Reassign</button>
            <button className="btn btn-quiet btn-sm" onClick={() => { window.dispatchEvent(new CustomEvent('open-lenny')); onClose(); }}>Ask Lenny →</button>
          </div>
        </div>
      </div>
    </></window.ModalPortal>
  );
}

// ====================================================================
// LEADS PAGE, HQ / CORPORATE
// Portfolio-level operational view: how locations are handling inbound.
// ====================================================================

// Stable hash → 0..1 so per-location modeled fields are deterministic
// across renders (mirrors the pattern in results-hq.jsx).
const hashFloatLeads = (s) => {
  let h = 2166136261;
  for (let i = 0; i < s.length; i++) { h ^= s.charCodeAt(i); h = Math.imul(h, 16777619); }
  return ((h >>> 0) % 10000) / 10000;
};

// Derive per-location lead-ops metrics from hqRollup. Score-driven: well-run
// locations answer faster, miss fewer calls, and book more. Bottom-tier
// locations leak hard. Adds variance via hashFloatLeads so it doesn't feel
// strictly monotonic.
function deriveLeadOps(rollup) {
  return rollup.map(r => {
    const v = hashFloatLeads(r.unit + 'leadops');
    // Response time: high score → fast (1–6 min). Low score → slow (35–95 min).
    const baseResp = r.score >= 85 ? 2 + v * 4
                   : r.score >= 75 ? 6 + v * 12
                   : r.score >= 65 ? 18 + v * 28
                                   : 35 + v * 60;
    const responseMin = Math.round(baseResp);
    // Phone answer rate inversely tracks response time. 92→63%.
    const phoneAnswerRate = Math.max(58, Math.min(96, Math.round(95 - baseResp * 0.55 + (v - 0.5) * 6)));
    // Calls received roughly tracks lead volume.
    const callsReceived = Math.round(r.leads * (0.55 + v * 0.25));
    const callsAnswered = Math.round(callsReceived * phoneAnswerRate / 100);
    const callsMissed   = callsReceived - callsAnswered;
    // Modeled missed-call revenue: 14% of missed calls would have booked at $340 AOV.
    const missedRev = Math.round(callsMissed * 0.14 * 340);
    // Unanswered count = forms + DMs older than expected.
    const unanswered = responseMin > 30
      ? Math.max(2, Math.round(r.leads * (0.04 + v * 0.06)))
      : responseMin > 12
        ? Math.round(r.leads * (0.015 + v * 0.025))
        : Math.round(r.leads * 0.005);
    const oldestHrs = unanswered > 0
      ? Math.round(8 + v * 36 + (responseMin > 30 ? 12 : 0))
      : 0;
    // Booked rate: high score → 62–74%, low score → 28–42%.
    const bookedRate = Math.max(22, Math.min(78,
      Math.round(34 + (r.score - 60) * 0.85 + (v - 0.5) * 8)
    ));
    // Owner: every location has someone, pull from a small pool keyed by
    // city so the same name always maps to the same place.
    const ownerPool = ['Sarah K.', 'Marcus D.', 'Priya S.', 'Tom R.', 'Jen M.', 'Diego L.', 'Alicia B.', 'Kevin O.', 'Maya P.', 'Ryan T.'];
    const owner = ownerPool[Math.floor(hashFloatLeads(r.unit + 'owner') * ownerPool.length)];
    return {
      ...r,
      responseMin,
      phoneAnswerRate,
      callsReceived,
      callsAnswered,
      callsMissed,
      missedRev,
      unanswered,
      oldestHrs,
      bookedRate,
      owner,
    };
  });
}

// --------------------------------------------------------------------
// HQ LEADS PAGE
// --------------------------------------------------------------------
function LeadsPageHq({ data }) {
  const [range, setRange] = useStateL({ preset: 'last30', start: new Date(2026, 2, 29), end: new Date(2026, 3, 27), compare: false });
  const ops = useMemoL(() => deriveLeadOps(data.hqRollup || []), [data.hqRollup]);
  const totals = data.hqLeadOps?.totals || {};
  const std    = data.hqLeadOps?.standard || {};
  const n      = ops.length;

  // Sort buckets
  const notResponding = useMemoL(() =>
    [...ops].filter(o => o.responseMin > 30 || o.unanswered >= 4 || o.oldestHrs >= 24)
            .sort((a, b) => b.responseMin - a.responseMin)
            .slice(0, 6)
  , [ops]);

  const phoneAbandon = useMemoL(() =>
    [...ops].sort((a, b) => a.phoneAnswerRate - b.phoneAnswerRate).slice(0, 6)
  , [ops]);

  const topBookers    = useMemoL(() => [...ops].sort((a, b) => b.bookedRate - a.bookedRate).slice(0, 5), [ops]);
  const bottomBookers = useMemoL(() => [...ops].sort((a, b) => a.bookedRate - b.bookedRate).slice(0, 5), [ops]);

  const meetingStandard = useMemoL(() => {
    const meet = ops.filter(o => o.responseMin <= (std.thresholdMin || 4)).length;
    return { meet, pct: Math.round(meet / Math.max(1, n) * 100) };
  }, [ops, std.thresholdMin, n]);

  return (
    <div className="page active">
      <div className="page-header">
        <div>
          <div className="page-title">Lead Operations.</div>
          <div className="page-sub">
            How your locations are handling inbound. <strong style={{ color: 'var(--ink-2)' }}>{n} locations</strong> · <strong style={{ color: 'var(--ink-2)' }}>{(totals.monthlyLeads || 0).toLocaleString()}</strong> leads this month.
          </div>
        </div>
        <div className="page-actions">
          <DateRangePicker value={range} onChange={v => { setRange(v); T('Range updated', `Showing ${v.preset && v.preset !== 'custom' ? (window.DRP_PRESET_LABEL?.(v.preset) || 'selected range') : `${(v.start.getMonth()+1)}/${v.start.getDate()} – ${(v.end.getMonth()+1)}/${v.end.getDate()}`}`, 'info'); }} align="right"/>
          <button className="btn btn-ghost btn-sm" onClick={() => T('Export queued', 'Lead-ops report, emailing CSV in ~30s.', 'ok')}>Export CSV</button>
          <button className="btn btn-primary btn-sm" onClick={() => T('Playbook pushed', `Lead-handling playbook deployed to ${notResponding.length + phoneAbandon.length} flagged locations.`, 'ok')}>Push playbook</button>
        </div>
      </div>

      {/* Portfolio KPI strip */}
      <div className="grid grid-4" style={{ marginBottom: 16 }}>
        <KpiCard label="Total leads" value={(totals.monthlyLeads || 0).toLocaleString()} delta={totals.leadsDelta} trend={[2380,2410,2455,2492,2510,2548,2596,2624,2658,2702,2740,2784]} color="var(--terra)" dotColor="var(--terra)"/>
        <KpiCard label="Brand booked rate" value={`${totals.brandBookedRate}%`} delta={totals.bookedDelta} trend={[52,53,54,54,55,55,56,56,57,57,58,58]} color="var(--sage)" dotColor="var(--sage)"/>
        <KpiCard label="Brand response time" value={`${totals.brandResponseMin}m`} delta={totals.responseDelta} invert trend={[14,13,13,12,12,11,11,10,10,10,9,9]} color="var(--marigold)" dotColor="var(--marigold)"/>
        <KpiCard label="Phone answer rate" value={`${totals.brandPhoneAnswerRate}%`} delta={totals.phoneDelta} trend={[74,75,76,76,77,78,78,79,80,80,81,81]} color="var(--violet)" dotColor="var(--violet)"/>
      </div>

      <SourceNote>Source: Telmetrics or other + FreshNest CRM · booked rate calculated by LOCALACT</SourceNote>

      {/* Locations not responding + Phone abandon */}
      <div className="grid grid-2" style={{ marginBottom: 20 }}>
        <NotRespondingCard rows={notResponding}/>
        <PhoneAbandonCard rows={phoneAbandon}/>
      </div>

      <SourceNote>Source: Telmetrics or other</SourceNote>

      {/* Booking-rate leaderboard */}
      <div className="grid grid-2" style={{ marginBottom: 20 }}>
        <BookingLeaderCard
          title="Top bookers"
          sub="Top 5 locations by lead-to-booked conversion"
          tone="sage"
          rows={topBookers}
        />
        <BookingLeaderCard
          title="Locations needing coaching"
          sub="Bottom 5 by lead-to-booked conversion"
          tone="terra"
          rows={bottomBookers}
        />
      </div>

      <SourceNote>Source: FreshNest CRM · booked rate calculated by LOCALACT</SourceNote>

      {/* Footer rollup strip */}
      <div style={{ padding: '14px 18px', background: 'var(--cream)', border: '1px solid var(--cream-3)', borderRadius: 10, fontSize: 12.5, color: 'var(--ink-3)', display: 'flex', alignItems: 'center', gap: 14, flexWrap: 'wrap' }}>
        <span><strong style={{ color: 'var(--ink-2)' }}>{meetingStandard.pct}%</strong> of locations meet the {std.label}</span>
        <span style={{ color: 'var(--cream-3)' }}>·</span>
        <span><strong style={{ color: 'var(--terra)' }}>{std.flaggedThisWeek}</strong> locations flagged this week</span>
        <span style={{ color: 'var(--cream-3)' }}>·</span>
        <span>last advisor outreach: <strong style={{ color: 'var(--ink-2)' }}>{std.lastOutreach}</strong></span>
        <span style={{ flex: 1 }}/>
        <button className="btn btn-quiet btn-sm" onClick={() => T('Outreach scheduled', 'Brand advisor will reach out to all flagged locations this week.', 'info')}>Schedule outreach</button>
      </div>
    </div>
  );
}

// --------------------------------------------------------------------
// Locations Not Responding card
// --------------------------------------------------------------------
function NotRespondingCard({ rows }) {
  return (
    <div className="card card-pad">
      <div className="card-header" style={{ marginBottom: 12 }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
          <span style={{ width: 22, height: 22, borderRadius: '50%', background: 'var(--terra-soft)', color: 'var(--terra)', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 12, fontWeight: 700 }}>!</span>
          <div>
            <div className="card-title">Locations not responding</div>
            <div className="card-sub">Median response &gt; 30 min OR unanswered leads &gt; 24 h old</div>
          </div>
        </div>
        <span className="badge badge-bad">{rows.length} flagged</span>
      </div>
      <div style={{ display: 'grid', gridTemplateColumns: '1.6fr 0.8fr 0.9fr 0.9fr 0.7fr', gap: 8, padding: '6px 10px 8px', borderBottom: '1px solid var(--cream-3)', fontSize: 10, color: 'var(--ink-4)', textTransform: 'uppercase', letterSpacing: '0.06em', fontWeight: 600 }}>
        <span>Location</span>
        <span style={{ textAlign: 'right' }}>Unans.</span>
        <span style={{ textAlign: 'right' }}>Oldest</span>
        <span>Owner</span>
        <span/>
      </div>
      {rows.map((r, i) => (
        <div key={r.unit} style={{ display: 'grid', gridTemplateColumns: '1.6fr 0.8fr 0.9fr 0.9fr 0.7fr', gap: 8, padding: '10px', borderBottom: i === rows.length - 1 ? 'none' : '1px solid var(--cream-2)', alignItems: 'center', fontSize: 12.5 }}>
          <div>
            <div style={{ color: 'var(--ink)', fontWeight: 600 }}>{r.unit}</div>
            <div style={{ fontSize: 11, color: 'var(--ink-4)', marginTop: 1 }}>{r.city} · response {r.responseMin}m</div>
          </div>
          <div style={{ textAlign: 'right', fontFamily: 'var(--ff-mono)', color: r.unanswered >= 5 ? 'var(--terra)' : 'var(--ink-2)', fontWeight: 600 }}>{r.unanswered}</div>
          <div style={{ textAlign: 'right', fontFamily: 'var(--ff-mono)', color: r.oldestHrs >= 24 ? 'var(--terra)' : 'var(--ink-2)' }}>{r.oldestHrs}h</div>
          <div style={{ fontSize: 12, color: 'var(--ink-3)' }}>{r.owner}</div>
          <div style={{ textAlign: 'right' }}>
            <button className="btn btn-quiet btn-xs"
                    onClick={() => T(`${r.unit}: owner nudged`, `${r.owner} got an SMS + email, ${r.unanswered} leads waiting, oldest ${r.oldestHrs}h.`, 'ok')}>
              Nudge owner →
            </button>
          </div>
        </div>
      ))}
    </div>
  );
}

// --------------------------------------------------------------------
// Phone Abandon card
// --------------------------------------------------------------------
function PhoneAbandonCard({ rows }) {
  const totalMissedRev = rows.reduce((s, r) => s + r.missedRev, 0);
  return (
    <div className="card card-pad" style={{ position: 'relative' }}>
      <div className="card-header" style={{ marginBottom: 12 }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
          <span style={{ width: 22, height: 22, borderRadius: '50%', background: 'var(--marigold-soft)', color: 'var(--marigold)', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 12, fontWeight: 700 }}>☎</span>
          <div>
            <div className="card-title">Phone abandon</div>
            <div className="card-sub">Lowest phone-answer rates · modeled missed revenue</div>
          </div>
        </div>
        <span style={{ fontSize: 11, fontFamily: 'var(--ff-mono)', color: 'var(--terra)', fontWeight: 700 }}>−${totalMissedRev.toLocaleString()}/mo</span>
      </div>
      <div style={{ display: 'grid', gridTemplateColumns: '1.5fr 0.6fr 0.6fr 0.7fr 0.9fr 0.8fr', gap: 8, padding: '6px 10px 8px', borderBottom: '1px solid var(--cream-3)', fontSize: 10, color: 'var(--ink-4)', textTransform: 'uppercase', letterSpacing: '0.06em', fontWeight: 600 }}>
        <span>Location</span>
        <span style={{ textAlign: 'right' }}>Recv</span>
        <span style={{ textAlign: 'right' }}>Ans</span>
        <span style={{ textAlign: 'right' }}>Rate</span>
        <span style={{ textAlign: 'right' }}>Missed $</span>
        <span/>
      </div>
      {rows.map((r, i) => (
        <div key={r.unit} style={{ display: 'grid', gridTemplateColumns: '1.5fr 0.6fr 0.6fr 0.7fr 0.9fr 0.8fr', gap: 8, padding: '10px', borderBottom: i === rows.length - 1 ? 'none' : '1px solid var(--cream-2)', alignItems: 'center', fontSize: 12.5 }}>
          <div>
            <div style={{ color: 'var(--ink)', fontWeight: 600 }}>{r.unit}</div>
            <div style={{ fontSize: 11, color: 'var(--ink-4)', marginTop: 1 }}>{r.city}</div>
          </div>
          <div style={{ textAlign: 'right', fontFamily: 'var(--ff-mono)', color: 'var(--ink-2)' }}>{r.callsReceived}</div>
          <div style={{ textAlign: 'right', fontFamily: 'var(--ff-mono)', color: 'var(--ink-2)' }}>{r.callsAnswered}</div>
          <div style={{ textAlign: 'right', fontFamily: 'var(--ff-mono)', fontWeight: 600,
                        color: r.phoneAnswerRate >= 80 ? 'var(--sage)' : r.phoneAnswerRate >= 70 ? 'var(--marigold)' : 'var(--terra)' }}>
            {r.phoneAnswerRate}%
          </div>
          <div style={{ textAlign: 'right', fontFamily: 'var(--ff-mono)', color: 'var(--terra)', fontWeight: 600 }}>−${r.missedRev.toLocaleString()}</div>
          <div style={{ textAlign: 'right' }}>
            <button className="btn btn-quiet btn-xs"
                    onClick={() => T(`${r.unit}: playbook sent`, `Phone-handling playbook + scripts emailed to ${r.owner}. Modeled recovery: $${r.missedRev.toLocaleString()}/mo.`, 'ok')}>
              Send playbook →
            </button>
          </div>
        </div>
      ))}
    </div>
  );
}

// --------------------------------------------------------------------
// Booking Leaderboard card (Top / Bottom)
// --------------------------------------------------------------------
function BookingLeaderCard({ title, sub, tone, rows }) {
  const dotBg = tone === 'sage' ? 'var(--sage-soft)' : 'var(--terra-soft)';
  const dotFg = tone === 'sage' ? 'var(--sage)'      : 'var(--terra)';
  const arrow = tone === 'sage' ? '↑' : '↓';
  return (
    <div className="card card-pad">
      <div className="card-header" style={{ marginBottom: 12 }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
          <span style={{ width: 22, height: 22, borderRadius: '50%', background: dotBg, color: dotFg, display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 12, fontWeight: 700 }}>{arrow}</span>
          <div>
            <div className="card-title">{title}</div>
            <div className="card-sub">{sub}</div>
          </div>
        </div>
      </div>
      <div style={{ display: 'grid', gridTemplateColumns: '1.6fr 0.7fr 0.7fr 0.7fr', gap: 8, padding: '6px 10px 8px', borderBottom: '1px solid var(--cream-3)', fontSize: 10, color: 'var(--ink-4)', textTransform: 'uppercase', letterSpacing: '0.06em', fontWeight: 600 }}>
        <span>Location</span>
        <span style={{ textAlign: 'right' }}>Leads</span>
        <span style={{ textAlign: 'right' }}>Booked</span>
        <span style={{ textAlign: 'right' }}>Rate</span>
      </div>
      {rows.map((r, i) => (
        <div key={r.unit} style={{ display: 'grid', gridTemplateColumns: '1.6fr 0.7fr 0.7fr 0.7fr', gap: 8, padding: '10px', borderBottom: i === rows.length - 1 ? 'none' : '1px solid var(--cream-2)', alignItems: 'center', fontSize: 12.5, cursor: 'pointer' }}
             onMouseEnter={e => e.currentTarget.style.background = 'var(--cream)'}
             onMouseLeave={e => e.currentTarget.style.background = 'transparent'}
             onClick={() => T(r.unit, `${r.leads} leads · ${Math.round(r.leads * r.bookedRate / 100)} booked · ${r.bookedRate}% rate`, 'info')}>
          <div>
            <div style={{ color: 'var(--ink)', fontWeight: 600 }}>{r.unit}</div>
            <div style={{ fontSize: 11, color: 'var(--ink-4)', marginTop: 1 }}>{r.city}</div>
          </div>
          <div style={{ textAlign: 'right', fontFamily: 'var(--ff-mono)', color: 'var(--ink-2)' }}>{r.leads}</div>
          <div style={{ textAlign: 'right', fontFamily: 'var(--ff-mono)', color: 'var(--ink-2)' }}>{Math.round(r.leads * r.bookedRate / 100)}</div>
          <div style={{ textAlign: 'right', fontFamily: 'var(--ff-mono)', fontWeight: 600, color: dotFg }}>{r.bookedRate}%</div>
        </div>
      ))}
    </div>
  );
}

// Animations
if (typeof document !== 'undefined' && !document.getElementById('leads-anim')) {
  const s = document.createElement('style');
  s.id = 'leads-anim';
  s.textContent = `
    @keyframes slideInRight { from { transform: translateX(40px); opacity: 0; } to { transform: translateX(0); opacity: 1; } }
    @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
  `;
  document.head.appendChild(s);
}
