// Coverage Desk — story seeding + distribution.
// Trend (from the Wire or manual) -> AI research brief -> auto-match contributors
// -> send INVITATIONS (claim / feedback / pass, not top-down assignments) -> live board.
// Uses window.claude.complete for the brief when available; scripted fallback otherwise.

const CVG_DESKS  = ['Civic','Business','Politics','Sports','Education','Culture','Health','Housing','Legal'];
const CVG_CITIES = ['Charlotte','Raleigh','Durham','Greensboro','Winston-Salem','Fayetteville','Wilmington','Statewide'];

// ---- contributor pool: creators (new media) + publisher outlets ----
function buildPool(){
  const creators = (window.CREATORS||[]).map(c=>({
    who:c.name, handle:c.handle, kind:c.type, city:c.city, reach:c.reach,
    score:c.score, mission:c.mission, beat:creatorBeat(c), group:'creator', color:'navy',
  }));
  const pubs = (window.PUBLISHERS||[]).map(p=>({
    who:p.name, handle:p.short, kind:'Publisher', city:p.city, reach:'DA '+p.da,
    score:Math.min(96, 55+p.da), mission:88, beat:pubBeat(p), group:'publisher', color:p.color,
  }));
  return [...creators, ...pubs];
}
function creatorBeat(c){
  const n=(c.name+' '+c.handle).toLowerCase();
  if(/money|wealth|biz|business/.test(n)) return 'Business';
  if(/hbcu|sideline|sport|aggie/.test(n)) return 'Sports';
  if(/civic|council|mayor|pastor|rev|daye/.test(n)) return 'Civic';
  if(/health/.test(n)) return 'Health';
  return 'Culture';
}
function pubBeat(p){
  const m=(p.mission||'').toLowerCase();
  if(/invest|account/.test(m)) return 'Politics';
  if(/business|wealth/.test(m)) return 'Business';
  return 'Civic';
}

// ---- match scoring against a package (desk + city) ----
function scoreMatch(cand, desk, city){
  let s = 20;
  const reasons = [];
  if(city!=='Statewide' && cand.city===city){ s+=42; reasons.push(city+' local'); }
  else if(city==='Statewide'){ s+=14; reasons.push('statewide fit'); }
  if(cand.beat===desk){ s+=30; reasons.push(desk+' beat'); }
  s += Math.round((cand.score-60)*0.4);
  s += Math.round((cand.mission-80)*0.3);
  s = Math.max(12, Math.min(98, s));
  if(cand.group==='creator') reasons.push(cand.kind.toLowerCase());
  if(!reasons.length) reasons.push('network reach');
  return { score:s, reason:reasons.slice(0,2).join(' · ') };
}

// ---- per-contributor angle (deterministic, credible) ----
function angleFor(cand, ev, city){
  const short = shortEvent(ev);
  const where = city==='Statewide' ? 'across NC' : `in ${city}`;
  switch(cand.kind){
    case 'Podcast':   return `Explainer episode: what ${short} means ${where}.`;
    case 'YouTube':   return `Short field doc on ${short}.`;
    case 'TikTok':    return `60-sec: ${short}, decoded for Black NC.`;
    case 'IG':        return `Carousel + reel: ${short} in plain terms.`;
    case 'X':         return `Community-reaction thread ${where}.`;
    case 'Substack':  return `Newsletter breakdown of ${short}.`;
    case 'Publisher': return `Reported piece for the network on ${short}.`;
    case 'Civic':     return `On-the-ground voices + local data on ${short}.`;
    default:          return `Original take on ${short}.`;
  }
}
function shortEvent(ev){
  let e = (ev||'').trim().replace(/\.$/,'');
  if(e.length>62) e = e.slice(0,60).replace(/\s+\S*$/,'')+'…';
  return e ? e.charAt(0).toLowerCase()+e.slice(1) : 'this story';
}

// ---- scripted research brief fallback ----
function scriptedBrief(ev, desk, city){
  const where = city==='Statewide'?'across North Carolina':`in ${city}`;
  return {
    angle:`Go past the headline on ${shortEvent(ev)} — who it touches ${where}, who decides, and what happens next.`,
    questions:[
      `What exactly changed, and when does it take effect ${where}?`,
      'Who benefits first, and who is left out of the current version?',
      'What records or figures would confirm the claims being made?',
      'What is the next public decision point to watch?',
    ],
    formats:['Reported explainer','Short-form video','Community reaction','Data breakdown'],
  };
}

function CoverageDesk({setRoute}){
  const [packages, setPackages] = React.useState(()=> (window.COVERAGE_PACKAGES||[]).map(p=>({...p})));
  const [view, setView]   = React.useState('home');     // home | seed
  const [tab, setTab]     = React.useState('board');     // board | activity
  const [openId, setOpenId] = React.useState(packages[0]?.id || null);
  const [llmOn] = React.useState(typeof window!=='undefined' && window.claude && typeof window.claude.complete==='function');

  const stats = React.useMemo(()=>{
    const all = packages.flatMap(p=>p.invites);
    return {
      active: packages.length,
      out: all.length,
      claimed: all.filter(i=>i.status==='claimed').length,
      feedback: all.filter(i=>i.status==='feedback').length,
    };
  },[packages]);

  function updateInvite(pkgId, who, patch){
    setPackages(ps=>ps.map(p=> p.id!==pkgId ? p : {...p, invites:p.invites.map(i=> i.who!==who?i:{...i,...patch})}));
  }
  function addPackage(pkg){
    setPackages(ps=>[pkg,...ps]);
    setOpenId(pkg.id); setView('home');
    // simulate contributors responding to the invitations over time
    const inv = pkg.invites;
    const plan = [
      [1400, 0, {status:'viewed', at:'now'}],
      [2600, 0, {status:'claimed', at:'1m'}],
      [3400, 1, {status:'viewed', at:'2m'}],
      [4200, 2, {status:'claimed', at:'3m'}],
      [5200, 1, {status:'feedback', at:'4m', note: inv[1] ? tweakAngle(inv[1]) : ''}],
    ];
    plan.forEach(([ms, idx, patch])=>{
      if(!inv[idx]) return;
      setTimeout(()=>updateInvite(pkg.id, inv[idx].who, patch), ms);
    });
  }

  return (
    <AdminShell route="coverage" setRoute={setRoute} title="Coverage desk" breadcrumb="CONTENT · STORY SEEDING"
      actions={ view==='seed'
        ? <button className="btn gh" onClick={()=>setView('home')}>← All packages</button>
        : <>
            <Seg value={tab==='board'?'Board':'Writer activity'} onChange={v=>setTab(v==='Board'?'board':'activity')} options={['Board','Writer activity']}/>
            <span className="pill muted" style={{marginRight:2}}>
              <span className="d" style={{background:llmOn?'#1f8a5b':'var(--gold-2)'}}></span>{llmOn?'AI RESEARCH ON':'SCRIPTED MODE'}
            </span>
            <button className="btn g" onClick={()=>setView('seed')}>+ Seed coverage</button>
          </> }>

      {tab==='activity' && view!=='seed'
        ? <WriterActivity/>
        : <>
      <div className="split-4" style={{marginBottom:18}}>
        <Stat n={String(stats.active)} l="Active packages" d="seeded from trends"/>
        <Stat n={String(stats.out)} l="Invitations out" d="claim or pass" />
        <Stat n={String(stats.claimed)} l="Claimed by contributors" d="in production" up={true}/>
        <Stat n={String(stats.feedback)} l="Feedback / counter-pitch" d="needs your reply" up={stats.feedback?false:undefined}/>
      </div>

      {view==='seed'
        ? <SeedFlow llmOn={llmOn} onCancel={()=>setView('home')} onLaunch={addPackage} existingCount={packages.length}/>
        : <PackageList packages={packages} openId={openId} setOpenId={setOpenId} updateInvite={updateInvite} onSeed={()=>setView('seed')}/> }
        </> }
    </AdminShell>
  );
}

/* ============================ WRITER ACTIVITY ============================ */
function WriterActivity(){
  const rows = window.WRITER_ACTIVITY || [];
  const [sort, setSort] = React.useState('Reliability');
  const withRates = rows.map(r=>({
    ...r,
    openRate: Math.round(r.opened/r.invited*100),
    claimRate: Math.round(r.claimed/r.invited*100),
    deliverRate: r.claimed? Math.round(r.delivered/r.claimed*100):0,
    revNum: parseInt((r.revenue||'').replace(/[^0-9]/g,''))||0,
  }));
  const sorted = [...withRates].sort((a,b)=>
    sort==='Reliability'? b.reliability-a.reliability :
    sort==='Claim rate'? b.claimRate-a.claimRate :
    sort==='Revenue'? b.revNum-a.revNum :
    a.respHrs-b.respHrs);

  const totInv = rows.reduce((s,r)=>s+r.invited,0);
  const totClaim = rows.reduce((s,r)=>s+r.claimed,0);
  const totDeliv = rows.reduce((s,r)=>s+r.delivered,0);
  const avgResp = (rows.reduce((s,r)=>s+r.respHrs,0)/(rows.length||1)).toFixed(1);

  return (
    <div>
      <div className="split-4" style={{marginBottom:18}}>
        <Stat n={Math.round(totClaim/totInv*100)+'%'} l="Network claim rate" d={`${totClaim}/${totInv} invitations`} up={true}/>
        <Stat n={avgResp+'h'} l="Avg time to respond" d="across writers"/>
        <Stat n={Math.round(totDeliv/totClaim*100)+'%'} l="Delivery rate" d={`${totDeliv}/${totClaim} claimed`}/>
        <Stat n={String(rows.filter(r=>r.reliability<75).length)} l="Low-reliability writers" d="needs attention" up={false}/>
      </div>

      <Card title="Writer activity & reliability" meta={`${rows.length} contributors`}
        actions={<Seg value={sort} onChange={setSort} options={['Reliability','Claim rate','Response','Revenue']}/>}>
        <table className="tbl" style={{marginLeft:-14,marginRight:-14,width:'calc(100% + 28px)'}}>
          <thead><tr>
            <th>WRITER</th><th>TYPE</th><th className="r">INVITED</th><th className="r">OPEN %</th>
            <th className="r">CLAIM %</th><th className="r">DELIVERED</th><th className="r">AVG RESP</th>
            <th className="r">REVENUE</th><th className="r">RELIABILITY</th>
          </tr></thead>
          <tbody>
            {sorted.map(r=>(
              <tr key={r.name}>
                <td>
                  <div style={{fontSize:12.5,fontWeight:600}}>{r.name}</div>
                  <div className="mono" style={{fontSize:9.5,color:'var(--navy)',letterSpacing:'.04em'}}>{r.handle}</div>
                </td>
                <td><Pill k={r.type==='Podcast'?'info':r.type==='YouTube'?'bad':r.type==='Substack'?'live':r.type==='Publisher'?'g':'warn'}>{r.type.toUpperCase()}</Pill></td>
                <td className="r mono num">{r.invited}</td>
                <td className="r mono num" style={{color:r.openRate>=80?'var(--green)':'var(--ink)'}}>{r.openRate}%</td>
                <td className="r mono num" style={{color:r.claimRate>=60?'var(--green)':r.claimRate>=40?'var(--ink)':'var(--gold-2)'}}>{r.claimRate}%</td>
                <td className="r mono num">{r.delivered}/{r.claimed}</td>
                <td className="r mono num" style={{color:r.respHrs<=4?'var(--green)':r.respHrs<=8?'var(--ink)':'var(--red)'}}>{r.respHrs}h</td>
                <td className="r mono num" style={{fontWeight:600,color:r.revNum?'var(--green)':'var(--mute)'}}>{r.revenue}</td>
                <td className="r"><Score n={r.reliability}/></td>
              </tr>
            ))}
          </tbody>
        </table>
        <div className="mono" style={{fontSize:9.5,color:'var(--mute)',marginTop:12,lineHeight:1.55,letterSpacing:'.02em',paddingTop:4}}>
          Reliability blends open rate, claim rate, on-time delivery, and response speed across every coverage invitation. Writers below 75 surface for a check-in.
        </div>
      </Card>
    </div>
  );
}

/* ============================ HOME · package list ============================ */
function PackageList({packages, openId, setOpenId, updateInvite, onSeed}){
  if(!packages.length){
    return <Card title="No coverage packages yet"><div style={{padding:'8px 0 4px'}}>
      <div style={{fontSize:13,color:'var(--ink-2)',lineHeight:1.5,maxWidth:520}}>Seed a package from a trending event on the Wire — or enter your own — and TUEPAC auto-matches contributors and sends them an invitation to claim an angle.</div>
      <button className="btn g" style={{marginTop:14}} onClick={onSeed}>+ Seed coverage</button>
    </div></Card>;
  }
  return (
    <div style={{display:'flex',flexDirection:'column',gap:14}}>
      {packages.map(p=>(
        <PackageCard key={p.id} pkg={p} open={openId===p.id} onToggle={()=>setOpenId(openId===p.id?null:p.id)} updateInvite={updateInvite}/>
      ))}
    </div>
  );
}

function PackageCard({pkg, open, onToggle, updateInvite}){
  const inv = pkg.invites;
  const counts = {
    claimed: inv.filter(i=>i.status==='claimed').length,
    feedback: inv.filter(i=>i.status==='feedback').length,
    open: inv.filter(i=>i.status==='sent'||i.status==='viewed').length,
    passed: inv.filter(i=>i.status==='passed').length,
  };
  return (
    <div className="card" style={{padding:0}}>
      <div onClick={onToggle} style={{display:'grid',gridTemplateColumns:'1fr auto',gap:14,alignItems:'center',padding:'14px 18px',cursor:'default',borderBottom:open?'1px solid var(--rule-line)':'none'}}>
        <div style={{minWidth:0}}>
          <div style={{display:'flex',alignItems:'center',gap:8,marginBottom:5,flexWrap:'wrap'}}>
            <span className="mono" style={{fontSize:9.5,letterSpacing:'.1em',color:'var(--mute)'}}>{pkg.id}</span>
            <Pill k={pkg.source==='wire'?'info':'muted'}>{pkg.source==='wire'?'★ FROM WIRE':'MANUAL'}</Pill>
            <span className="mono" style={{fontSize:9.5,letterSpacing:'.08em',color:'var(--ink-2)'}}>{pkg.desk.toUpperCase()} · {pkg.city.toUpperCase()} · {pkg.created}</span>
          </div>
          <div className="serif" style={{fontSize:17,fontWeight:700,lineHeight:1.18,letterSpacing:'-.008em',textWrap:'pretty'}}>{pkg.event}</div>
        </div>
        <div style={{display:'flex',alignItems:'center',gap:14}}>
          <div style={{display:'flex',gap:6}}>
            <MiniCount n={counts.claimed} l="claimed" c="var(--green)"/>
            <MiniCount n={counts.feedback} l="feedback" c="var(--gold-2)"/>
            <MiniCount n={counts.open} l="open" c="var(--mute)"/>
          </div>
          <span className="mono" style={{fontSize:14,color:'var(--mute)'}}>{open?'▾':'▸'}</span>
        </div>
      </div>

      {open && (
        <div style={{padding:'16px 18px 18px'}}>
          <div style={{display:'grid',gridTemplateColumns:'1fr 300px',gap:20,alignItems:'flex-start'}}>
            <div>
              <div className="eyebrow" style={{marginBottom:8}}>INVITATIONS · {inv.length}</div>
              <div style={{display:'flex',flexDirection:'column',gap:8}}>
                {inv.map(i=><InviteRow key={i.who} inv={i} onUpdate={patch=>updateInvite(pkg.id,i.who,patch)}/>)}
              </div>
            </div>
            <div style={{display:'flex',flexDirection:'column',gap:12}}>
              <div style={{padding:'12px 14px',background:'var(--paper)',border:'1px solid var(--rule-line)'}}>
                <div className="eyebrow" style={{marginBottom:6}}>PACKAGE ANGLE</div>
                <div className="serif" style={{fontSize:13.5,lineHeight:1.5,color:'var(--ink)',fontStyle:'italic'}}>{pkg.angle}</div>
              </div>
              {pkg.questions?.length>0 && (
                <div style={{padding:'12px 14px',background:'var(--bg)',border:'1px solid var(--rule-line)'}}>
                  <div className="eyebrow" style={{marginBottom:8}}>KEY QUESTIONS</div>
                  <ol style={{margin:0,paddingLeft:16,fontSize:12,color:'var(--ink-2)',lineHeight:1.6}}>
                    {pkg.questions.map((q,i)=><li key={i}>{q}</li>)}
                  </ol>
                </div>
              )}
              <div className="mono" style={{fontSize:10,color:'var(--mute)',lineHeight:1.6,letterSpacing:'.02em'}}>
                Invitations are opt-in. Contributors <b style={{color:'var(--green)'}}>claim</b> an angle, <b style={{color:'var(--gold-2)'}}>counter-pitch</b>, or <b>pass</b> — nothing is assigned top-down.
              </div>
            </div>
          </div>
        </div>
      )}
    </div>
  );
}

function MiniCount({n,l,c}){
  return (
    <span style={{display:'inline-flex',alignItems:'baseline',gap:4}} title={l}>
      <span className="mono num" style={{fontSize:13,fontWeight:700,color:c}}>{n}</span>
      <span className="mono" style={{fontSize:8.5,letterSpacing:'.1em',color:'var(--mute)',textTransform:'uppercase'}}>{l}</span>
    </span>
  );
}

function initialOf(inv){
  const h = inv.handle || '';
  if(h && h[0] !== '@') return h[0].toUpperCase();
  return (inv.who || '?')[0].toUpperCase();
}

const STATUS_META = {
  sent:     ['muted','SENT'],
  viewed:   ['info','VIEWED'],
  claimed:  ['live','CLAIMED'],
  feedback: ['warn','FEEDBACK'],
  passed:   ['bad','PASSED'],
};

function InviteRow({inv, onUpdate}){
  const [k,lbl] = STATUS_META[inv.status] || STATUS_META.sent;
  return (
    <div style={{border:'1px solid var(--rule-line)',background:'var(--bg)',padding:'10px 12px'}}>
      <div style={{display:'grid',gridTemplateColumns:'auto 1fr auto',gap:10,alignItems:'center'}}>
        <span style={{width:26,height:26,flex:'0 0 auto',background:`var(--${inv.color||'navy'})`,color:'var(--ivory)',fontFamily:'var(--serif)',fontWeight:800,fontSize:11,display:'flex',alignItems:'center',justifyContent:'center'}}>{initialOf(inv)}</span>
        <div style={{minWidth:0}}>
          <div style={{display:'flex',alignItems:'center',gap:7,flexWrap:'wrap'}}>
            <span style={{fontSize:12.5,fontWeight:600}}>{inv.who}</span>
            <span className="mono" style={{fontSize:9.5,color:'var(--navy)',letterSpacing:'.04em'}}>{inv.handle}</span>
            <span className="mono" style={{fontSize:9,color:'var(--mute)',letterSpacing:'.06em'}}>· {inv.kind.toUpperCase()} · {inv.city.toUpperCase()}</span>
          </div>
          <div style={{fontSize:11.5,color:'var(--ink-2)',lineHeight:1.4,marginTop:3,textWrap:'pretty'}}>{inv.angle}</div>
        </div>
        <div style={{display:'flex',flexDirection:'column',alignItems:'flex-end',gap:4}}>
          <Pill k={k}>{lbl}</Pill>
          {inv.at && inv.at!=='—' && <span className="mono" style={{fontSize:8.5,color:'var(--mute)',letterSpacing:'.06em'}}>{inv.at}</span>}
        </div>
      </div>

      {inv.status==='feedback' && (
        <div style={{marginTop:9,paddingTop:9,borderTop:'1px solid var(--rule-line)',display:'grid',gridTemplateColumns:'1fr auto',gap:10,alignItems:'center'}}>
          <div style={{fontSize:11.5,fontStyle:'italic',color:'var(--gold-2)',lineHeight:1.4}}>“{inv.note}”</div>
          <div style={{display:'flex',gap:6}}>
            <button className="btn gh s" onClick={()=>onUpdate({status:'claimed',angle:inv.note||inv.angle,note:undefined,at:'now'})}>Accept pitch</button>
            <button className="btn gh s" onClick={()=>onUpdate({status:'viewed',note:undefined,at:'now'})}>Nudge original</button>
          </div>
        </div>
      )}
      {inv.status==='passed' && inv.note && (
        <div style={{marginTop:9,paddingTop:9,borderTop:'1px solid var(--rule-line)',fontSize:11,color:'var(--mute)',fontStyle:'italic'}}>Passed — {inv.note}</div>
      )}
    </div>
  );
}

/* ============================ SEED FLOW ============================ */
function SeedFlow({llmOn, onCancel, onLaunch, existingCount}){
  const [step, setStep]   = React.useState('trend'); // trend | brief | dispatch
  const [srcMode, setSrc] = React.useState('wire');
  const [picked, setPicked] = React.useState(null);   // wire trend obj
  const [mEvent, setMEvent] = React.useState('');
  const [mDesk, setMDesk]   = React.useState('Civic');
  const [mCity, setMCity]   = React.useState('Charlotte');

  const [brief, setBrief]   = React.useState(null);
  const [genErr, setGenErr] = React.useState('');
  const [matches, setMatches] = React.useState([]);   // [{...cand, score, reason, angle, on}]
  const [sendMode, setSendMode] = React.useState('review'); // review | auto (zero-review)

  const ev   = srcMode==='wire' ? (picked?.note||'') : mEvent;
  const desk = srcMode==='wire' ? (picked?.desk||'Civic') : mDesk;
  const city = srcMode==='wire' ? (picked?.city||'Charlotte') : mCity;
  const canBrief = srcMode==='wire' ? !!picked : mEvent.trim().length>6;

  async function generate(){
    setStep('brief'); setGenErr(''); setBrief('loading');
    // matches computed immediately (deterministic)
    const pool = buildPool();
    const scored = pool.map(c=>{ const m=scoreMatch(c,desk,city); return {...c,...m,angle:angleFor(c,ev,city)}; })
      .sort((a,b)=>b.score-a.score);
    const top = scored.slice(0,6).map((c,i)=>({...c, on: c.score>=48 || i<3 }));
    setMatches(top);

    let b = null;
    if(llmOn){
      try{
        const prompt = `You are a senior editor at TUEPAC, a network of Black-owned North Carolina news publishers. A story is trending: "${ev}" (${desk} desk, ${city}). Return ONLY minified JSON: {"angle": one-sentence editorial angle that goes past the headline, "questions": array of 4 short reporting questions, "formats": array of 4 short content-format labels}.`;
        const raw = await window.claude.complete(prompt);
        const clean=String(raw).replace(/```json|```/g,'').trim();
        b = JSON.parse(clean.slice(clean.indexOf('{'), clean.lastIndexOf('}')+1));
        if(!b.angle||!Array.isArray(b.questions)) throw new Error('thin');
      }catch(err){ b=null; setGenErr('Live model unavailable — used a scripted brief.'); }
    }
    if(!b) b = scriptedBrief(ev,desk,city);
    setBrief(b);
    // zero-review mode: fire invitations automatically, no match-review step
    if(sendMode==='auto') setTimeout(()=>launch(top, b), 1300);
  }

  function toggleMatch(who){ setMatches(ms=>ms.map(m=>m.who===who?{...m,on:!m.on}:m)); }

  function launch(chosenList, briefObj){
    const useMatches = Array.isArray(chosenList) ? chosenList : matches;
    const useBrief = briefObj || brief;
    setStep('dispatch');
    const chosen = useMatches.filter(m=>m.on);
    const id = 'CVG-' + String(5+existingCount).padStart(3,'0');
    const pkg = {
      id, event: ev, source: srcMode, trend: srcMode==='wire'?picked.tag:'—',
      desk, city, created:'just now', angle: useBrief.angle, questions: useBrief.questions,
      sendMode,
      invites: chosen.map(m=>({ who:m.who, handle:m.handle, kind:m.kind, city:m.city, color:m.color, angle:m.angle, status:'sent', at:'now' })),
    };
    // simulate live responses coming back
    setTimeout(()=>onLaunch(pkg), 1500);
  }

  return (
    <div>
      <SeedRail step={step}/>

      {step==='trend' && (
        <div style={{display:'grid',gridTemplateColumns:'1fr 300px',gap:18,alignItems:'flex-start'}}>
          <Card title="Pick a trending event" meta="what's moving this week"
            actions={<Seg value={srcMode==='wire'?'From the Wire':'Manual entry'} onChange={v=>setSrc(v==='From the Wire'?'wire':'manual')} options={['From the Wire','Manual entry']}/>}>
            {srcMode==='wire' ? (
              <div style={{display:'flex',flexDirection:'column',gap:8}}>
                {(window.WIRE_TRENDS||[]).map(t=>{
                  const on = picked?.tag===t.tag;
                  return (
                    <div key={t.tag} onClick={()=>setPicked(t)} style={{display:'grid',gridTemplateColumns:'auto 1fr auto',gap:12,alignItems:'center',padding:'11px 13px',border:'1px solid '+(on?'var(--black)':'var(--rule-line)'),background:on?'var(--paper)':'var(--bg)',cursor:'default'}}>
                      <span style={{width:16,height:16,flex:'0 0 auto',border:'1px solid '+(on?'var(--black)':'var(--rule-line)'),background:on?'var(--black)':'transparent',color:'var(--ivory)',fontSize:10,display:'flex',alignItems:'center',justifyContent:'center'}}>{on?'✓':''}</span>
                      <div style={{minWidth:0}}>
                        <div style={{display:'flex',alignItems:'center',gap:8,flexWrap:'wrap'}}>
                          <span style={{fontSize:13,fontWeight:700}}>{t.tag}</span>
                          <span className="mono" style={{fontSize:9,letterSpacing:'.08em',color:'var(--mute)'}}>{t.cat.toUpperCase()} · {t.desk.toUpperCase()} · {t.city.toUpperCase()}</span>
                        </div>
                        <div style={{fontSize:11.5,color:'var(--ink-2)',lineHeight:1.4,marginTop:3,textWrap:'pretty'}}>{t.note}</div>
                      </div>
                      <div style={{textAlign:'right'}}>
                        <div className="mono num" style={{fontSize:12,fontWeight:700,color:'var(--green)'}}>{t.momentum}</div>
                        <div className="mono" style={{fontSize:9,color:'var(--mute)'}}>{t.posts} posts</div>
                      </div>
                    </div>
                  );
                })}
              </div>
            ) : (
              <div>
                <div className="eyebrow" style={{marginBottom:6}}>EVENT / STORY</div>
                <textarea value={mEvent} onChange={e=>setMEvent(e.target.value)} placeholder="e.g. State treasurer announces $40M set-aside for minority-owned banks — details land Thursday"
                  style={{width:'100%',minHeight:64,padding:'8px 10px',border:'1px solid var(--rule-line)',background:'var(--bg)',fontSize:13,fontFamily:'var(--sans)',outline:'none',resize:'vertical'}}/>
                <div style={{display:'grid',gridTemplateColumns:'1fr 1fr',gap:14,marginTop:14}}>
                  <div>
                    <div className="eyebrow" style={{marginBottom:6}}>DESK</div>
                    <select value={mDesk} onChange={e=>setMDesk(e.target.value)} style={selStyle}>{CVG_DESKS.map(d=><option key={d}>{d}</option>)}</select>
                  </div>
                  <div>
                    <div className="eyebrow" style={{marginBottom:6}}>CITY FOCUS</div>
                    <select value={mCity} onChange={e=>setMCity(e.target.value)} style={selStyle}>{CVG_CITIES.map(c=><option key={c}>{c}</option>)}</select>
                  </div>
                </div>
              </div>
            )}
            <div style={{display:'flex',justifyContent:'space-between',alignItems:'center',gap:8,marginTop:16,flexWrap:'wrap'}}>
              <div style={{display:'flex',alignItems:'center',gap:9}}>
                <span className="eyebrow" style={{marginBottom:0}}>SEND MODE</span>
                <Seg value={sendMode==='auto'?'Auto-send':'Review matches'} onChange={v=>setSendMode(v==='Auto-send'?'auto':'review')} options={['Review matches','Auto-send']}/>
              </div>
              <div style={{display:'flex',gap:8}}>
                <button className="btn gh" onClick={onCancel}>Cancel</button>
                <button className="btn k" disabled={!canBrief} style={{opacity:canBrief?1:.4}} onClick={generate}>Research &amp; build brief →</button>
              </div>
            </div>
            <div className="mono" style={{fontSize:9,color:'var(--mute)',marginTop:8,letterSpacing:'.02em',textAlign:'right',lineHeight:1.5}}>
              {sendMode==='auto'
                ? '⚡ Zero-review: invitations fire automatically to every match once the brief is ready.'
                : 'You\u2019ll review and can uncheck matches before invitations send.'}
            </div>
          </Card>

          <div style={{padding:'14px 16px',background:'var(--paper)',border:'1px solid var(--rule-line)'}}>
            <div className="eyebrow" style={{marginBottom:8}}>HOW SEEDING WORKS</div>
            {[['1','Pick a trend from the Wire, or enter your own event.'],
              ['2','TUEPAC researches an angle + key questions.'],
              ['3','Contributors are auto-matched by beat + city.'],
              ['4','Invitations are sent — each is claimed, countered, or passed.']].map(([n,t])=>(
              <div key={n} style={{display:'grid',gridTemplateColumns:'auto 1fr',gap:10,padding:'6px 0',alignItems:'flex-start'}}>
                <span style={{width:18,height:18,flex:'0 0 auto',background:'var(--black)',color:'var(--ivory)',fontFamily:'var(--mono)',fontSize:9.5,display:'flex',alignItems:'center',justifyContent:'center'}}>{n}</span>
                <span style={{fontSize:11.5,color:'var(--ink-2)',lineHeight:1.45}}>{t}</span>
              </div>
            ))}
          </div>
        </div>
      )}

      {step==='brief' && (
        <div style={{display:'grid',gridTemplateColumns:'1fr 340px',gap:18,alignItems:'flex-start'}}>
          <Card title="Research brief" meta={brief==='loading'?(llmOn?'claude · researching':'composing'):'editable'}>
            {brief==='loading' ? (
              <div style={{padding:'26px 0'}}>
                <div className="mono" style={{fontSize:10,letterSpacing:'.16em',color:'var(--mute)',textTransform:'uppercase',textAlign:'center'}}>Researching the angle…</div>
                <div style={{marginTop:16,display:'flex',flexDirection:'column',gap:8,maxWidth:480,marginInline:'auto'}}>
                  {[86,70,92,60].map((w,i)=><div key={i} style={{height:9,width:w+'%',background:'linear-gradient(90deg,var(--rule-line),var(--canvas))',animation:`cvgp 1.1s ${i*.12}s ease-in-out infinite`}}/>) }
                </div>
              </div>
            ) : (
              <div>
                {genErr && <div style={{padding:'7px 10px',marginBottom:12,fontSize:10.5,background:'rgba(214,161,58,.1)',border:'1px solid rgba(185,134,32,.32)',color:'var(--gold-2)'}}><span className="mono" style={{fontSize:9,letterSpacing:'.12em'}}>⚠ FALLBACK</span> · {genErr}</div>}
                <div className="eyebrow" style={{marginBottom:6}}>EVENT</div>
                <div className="serif" style={{fontSize:16,fontWeight:700,lineHeight:1.2,textWrap:'pretty'}}>{ev}</div>
                <div className="mono" style={{fontSize:9.5,color:'var(--mute)',letterSpacing:'.08em',marginTop:6}}>{desk.toUpperCase()} · {city.toUpperCase()} · {srcMode==='wire'?'FROM WIRE':'MANUAL'}</div>

                <div className="eyebrow" style={{margin:'16px 0 6px'}}>ANGLE</div>
                <textarea value={brief.angle} onChange={e=>setBrief({...brief,angle:e.target.value})}
                  style={{width:'100%',minHeight:60,padding:'8px 10px',border:'1px solid var(--rule-line)',background:'var(--bg)',fontFamily:'var(--serif)',fontSize:14,lineHeight:1.5,outline:'none',resize:'vertical'}}/>

                <div className="eyebrow" style={{margin:'16px 0 6px'}}>KEY QUESTIONS</div>
                <ol style={{margin:0,paddingLeft:18,fontSize:12.5,color:'var(--ink-2)',lineHeight:1.7}}>
                  {brief.questions.map((q,i)=><li key={i}>{q}</li>)}
                </ol>

                {brief.formats && <>
                  <div className="eyebrow" style={{margin:'16px 0 6px'}}>SUGGESTED FORMATS</div>
                  <div style={{display:'flex',gap:6,flexWrap:'wrap'}}>{brief.formats.map(f=><Pill key={f} k="muted">{f}</Pill>)}</div>
                </>}
              </div>
            )}
          </Card>

          <div style={{display:'flex',flexDirection:'column',gap:14}}>
            <Card title="Auto-matched contributors" meta={sendMode==='auto'?'auto-send · locked':`${matches.filter(m=>m.on).length} selected`}>
              <div style={{fontSize:11,color:'var(--mute)',marginBottom:10,lineHeight:1.4}}>
                {sendMode==='auto'
                  ? 'Zero-review is on — these matches are being invited automatically. Switch to Review mode next time to curate first.'
                  : 'Ranked by beat + city fit across creators and network publishers. Uncheck anyone you don\u2019t want invited.'}
              </div>
              <div style={{display:'flex',flexDirection:'column'}}>
                {matches.map(m=>(
                  <div key={m.who} onClick={()=>{ if(sendMode!=='auto') toggleMatch(m.who); }} style={{display:'grid',gridTemplateColumns:'auto 1fr auto',gap:9,alignItems:'center',padding:'8px 0',borderBottom:'1px solid var(--rule-line)',cursor:'default',opacity:m.on?1:.4}}>
                    <span style={{width:15,height:15,flex:'0 0 auto',border:'1px solid '+(m.on?'var(--green)':'var(--rule-line)'),background:m.on?'var(--green)':'transparent',color:'#fff',fontSize:9,display:'flex',alignItems:'center',justifyContent:'center'}}>{m.on?'✓':''}</span>
                    <div style={{minWidth:0}}>
                      <div style={{fontSize:12,fontWeight:600,whiteSpace:'nowrap',overflow:'hidden',textOverflow:'ellipsis'}}>{m.who}</div>
                      <div className="mono" style={{fontSize:9,color:'var(--mute)',letterSpacing:'.04em'}}>{m.kind.toUpperCase()} · {m.reason.toUpperCase()}</div>
                    </div>
                    <Score n={m.score}/>
                  </div>
                ))}
              </div>
            </Card>
            {sendMode==='auto'
              ? <div style={{textAlign:'right'}}><span className="mono" style={{fontSize:10,letterSpacing:'.1em',color:'var(--green)',textTransform:'uppercase'}}>⚡ Auto-sending {matches.filter(m=>m.on).length} invitations…</span></div>
              : <div style={{display:'flex',justifyContent:'flex-end',gap:8}}>
                  <button className="btn gh" onClick={()=>setStep('trend')}>← Back</button>
                  <button className="btn g" disabled={brief==='loading'||!matches.some(m=>m.on)} style={{opacity:(brief!=='loading'&&matches.some(m=>m.on))?1:.4}} onClick={()=>launch()}>
                    Send {matches.filter(m=>m.on).length} invitations →
                  </button>
                </div> }
          </div>
        </div>
      )}

      {step==='dispatch' && (
        <Card title="Sending invitations" meta="dispatching">
          <div style={{padding:'26px 0',textAlign:'center'}}>
            <div style={{width:46,height:46,margin:'0 auto',border:'2px solid var(--green)',color:'var(--green)',borderRadius:'50%',display:'flex',alignItems:'center',justifyContent:'center',fontSize:22,animation:'cvgp 1s ease-in-out infinite'}}>↗</div>
            <div className="mono" style={{fontSize:10.5,letterSpacing:'.16em',color:'var(--mute)',textTransform:'uppercase',marginTop:16}}>Notifying {matches.filter(m=>m.on).length} contributors…</div>
            <div style={{fontSize:12,color:'var(--ink-2)',marginTop:8}}>They can claim an angle, counter-pitch, or pass. You'll see responses on the package board.</div>
          </div>
        </Card>
      )}

      <style>{`@keyframes cvgp{0%,100%{opacity:.45}50%{opacity:1}}`}</style>
    </div>
  );
}

const selStyle = {width:'100%',padding:'8px 10px',border:'1px solid var(--rule-line)',background:'var(--bg)',fontSize:13,fontFamily:'var(--sans)',outline:'none'};

// contributor counter-pitch, derived from their invited angle
function tweakAngle(inv){
  switch(inv.kind){
    case 'Podcast':  return 'Want to make it a 2-part series — can commit if the deadline slides a week.';
    case 'Publisher':return 'Prefer a data-led piece over a straight recap; can our team pull the filings?';
    case 'TikTok':   return 'Can do it, but a creator-explainer format lands better than 60 sec.';
    default:         return 'On board — proposing a community-forum recap instead for bigger reach.';
  }
}

function SeedRail({step}){
  const steps=[['trend','Trend'],['brief','Brief & match'],['dispatch','Send']];
  const order=['trend','brief','dispatch'];
  const cur=order.indexOf(step);
  return (
    <div style={{display:'flex',alignItems:'center',marginBottom:16}}>
      {steps.map(([k,lbl],i)=>{
        const active=step===k, done=order.indexOf(k)<cur;
        return (
          <React.Fragment key={k}>
            <div style={{display:'flex',alignItems:'center',gap:7}}>
              <span style={{width:20,height:20,display:'flex',alignItems:'center',justifyContent:'center',fontFamily:'var(--mono)',fontSize:10,fontWeight:600,background:active?'var(--black)':done?'var(--green)':'var(--bg)',color:active||done?'var(--ivory)':'var(--mute)',border:'1px solid '+(active?'var(--black)':done?'var(--green)':'var(--rule-line)')}}>{done?'✓':i+1}</span>
              <span className="mono" style={{fontSize:10,letterSpacing:'.1em',textTransform:'uppercase',color:active?'var(--ink)':'var(--mute)',fontWeight:active?600:400}}>{lbl}</span>
            </div>
            {i<steps.length-1 && <div style={{flex:'0 0 28px',height:1,background:'var(--rule-line)',margin:'0 10px'}}/>}
          </React.Fragment>
        );
      })}
    </div>
  );
}

window.CoverageDesk = CoverageDesk;
