// screens2.jsx — Claims, CWR/DDEX, Analytics, Public, Search palette
const { useState: useS2, useEffect: useE2, useRef: useR2, useMemo: useM2 } = React;

// ───────────────────────────────────────────────────────────── CLAIMS
// Enrich each base CLAIM with deterministic synthetic detail (parties, evidence,
// timeline, deadline). Keeps the workspace data-rich without bloating ui.jsx.
function _claimDetail(c) {
  // Deterministic hash from id → consistent fake-data per claim
  let h = 0; for (let i = 0; i < c.id.length; i++) h = (h * 31 + c.id.charCodeAt(i)) >>> 0;
  const r = (n) => (h = (h * 1103515245 + 12345) >>> 0, h % n);
  const today = new Date('2026-04-30');
  const deadlineDays = c.severity === 'high' ? -2 + r(5) : c.severity === 'mid' ? 4 + r(7) : 14 + r(10);
  const deadline = new Date(today); deadline.setDate(deadline.getDate() + deadlineDays);
  const opened = new Date(today); opened.setDate(opened.getDate() - c.age);
  const claimantPct = c.pct;
  const counterPct = 100 - c.pct;
  // Disputed band — overlap between the two claims, what's actually in conflict
  const disputed = Math.min(claimantPct, counterPct, c.severity === 'high' ? 40 : 20);
  return {
    deadline,
    deadlineDays,
    opened,
    disputed,
    claimantPct,
    counterPct,
    estimatedAtRisk: Math.round((disputed / 100) * (12000 + r(60000))),
    claimantSociety: ['ASCAP','BMI','PRS','GEMA','SACEM','SOCAN'][r(6)],
    partySociety:    ['ASCAP','BMI','PRS','GEMA','SACEM','SOCAN'][r(6)],
    claimantEvidence: [
      { name:'split_sheet_signed_2018-09-12.pdf', size:`${280 + r(180)} KB`, kind:'split-sheet' },
      { name:'admin_agreement_v3.pdf',            size:`${410 + r(250)} KB`, kind:'agreement'   },
      { name:'session_log_studios_a.txt',         size:`${12 + r(48)} KB`,   kind:'session'     },
    ],
    counterEvidence: [
      { name:'work_for_hire_addendum.pdf',        size:`${190 + r(220)} KB`, kind:'agreement'   },
      { name:'pre_existing_split_2017.pdf',       size:`${340 + r(140)} KB`, kind:'split-sheet' },
    ],
    timeline: [
      { d: c.age,    who: c.method==='cwr'?'system':c.claimant.toLowerCase().replace(/[^a-z]/g,''), what:`Claim opened via ${c.method.toUpperCase()}`, kind:'open' },
      { d: c.age-1,  who:'system',                       what:`Notified counter-party · ${c.party}`, kind:'notify' },
      ...(c.age >= 2 ? [{ d: c.age-2, who: c.party.toLowerCase().replace(/[^a-z]/g,''), what:'Counter-claim filed with split-sheet evidence', kind:'counter' }] : []),
      ...(c.age >= 4 ? [{ d: c.age-4, who:'k.davis',     what:'Requested additional documentation', kind:'note' }] : []),
      ...(c.age >= 6 ? [{ d: c.age-6, who: c.claimant.toLowerCase().replace(/[^a-z]/g,''), what:'Uploaded admin agreement v3', kind:'evidence' }] : []),
      ...(c.status==='resolved' ? [{ d:0, who:'a.cohen', what:'Resolved · accepted claimant terms', kind:'resolve' }] : []),
    ],
    notes: c.severity==='high'
      ? `Counter-party asserts pre-existing 60/40 agreement dated 2018-09-12. Claimant has admin agreement v3 effective 2021-04-01 stating 100% admin in territory. Conflict turns on whether the v3 supersedes the 2018 work-for-hire addendum.`
      : c.severity==='mid'
        ? `Both parties agree on splits in principle; dispute is over CWR participation method (claimant filed via society, counter wants direct admin).`
        : `Procedural — pending counter-party acknowledgement of registered terms.`,
    pendingActions: c.severity==='high' ? 3 : c.severity==='mid' ? 1 : 0,
  };
}
function _evidenceIcon(kind) {
  return { 'split-sheet':'§', 'agreement':'¶', 'session':'•', 'cwr':'⌘' }[kind] || '·';
}
function ScreenClaims({ go }) {
  // Unified view filter — driven by the stat tiles.
  // 'open'      → all open
  // 'hot'       → open + severity=high
  // 'warm'      → open + severity=mid
  // 'resolved'  → resolved (last 7d)
  const [filter, setFilter] = useS2('open');
  const [selected, setSelected] = useS2(null);
  // Per-claim mutations applied on top of the static CLAIMS array.
  // Shape: { [id]: { status, resolution, addedTimeline:[{...}], addedNotes:[...] } }
  const [overlays, setOverlays] = useS2({});
  const [, setTick] = useS2(0); // force re-render after toast queue
  // Apply overlay to a claim
  const view = (c) => {
    const ov = overlays[c.id];
    return ov ? { ...c, ...ov.patch } : c;
  };
  const applied = CLAIMS.map(view);
  const filtered = applied.filter(c => {
    if (filter === 'hot')      return c.status === 'open' && c.severity === 'high';
    if (filter === 'warm')     return c.status === 'open' && c.severity === 'mid';
    if (filter === 'resolved') return c.status === 'resolved';
    if (filter === 'all')      return true;
    return c.status === 'open'; // 'open' default
  });
  const selectedLive = selected ? applied.find(x => x.id === selected.id) : null;
  const detail = selectedLive ? _claimDetail(selectedLive) : null;
  const ovExtras = selected ? overlays[selected.id] : null;

  const mutate = (id, patch, timelineEvent, noteText) => {
    setOverlays(prev => {
      const cur = prev[id] || { patch:{}, addedTimeline:[], addedNotes:[] };
      return {
        ...prev,
        [id]: {
          patch:        { ...cur.patch, ...patch },
          addedTimeline: timelineEvent ? [...cur.addedTimeline, timelineEvent] : cur.addedTimeline,
          addedNotes:    noteText      ? [...cur.addedNotes, noteText]         : cur.addedNotes,
        }
      };
    });
  };

  const act = (kind) => {
    if (!selectedLive) return;
    const id = selectedLive.id;
    const party = selectedLive.party;
    const me = 'a.cohen';
    const now = { d:0, who:me, what:'', kind:'note' };
    if (kind === 'add-evidence') {
      mutate(id, {}, { ...now, what:'Evidence uploaded · supporting documentation', kind:'evidence' });
      window.toast && window.toast(`Evidence added to ${id}`, 'ok');
    } else if (kind === 'add-note') {
      mutate(id, {}, { ...now, what:'Internal note added', kind:'note' }, 'Internal review note added by ' + me + ' on ' + new Date().toLocaleDateString());
      window.toast && window.toast(`Note saved to ${id}`, 'ok');
    } else if (kind === 'request-docs') {
      mutate(id, {}, { ...now, what:`Document request sent to ${party}`, kind:'notify' });
      window.toast && window.toast(`Document request sent to ${party}`, 'ok');
    } else if (kind === 'accept-claimant') {
      mutate(id, { status:'resolved', resolution:'claimant' },
        { ...now, what:`Resolved · accepted claimant terms (${detail.claimantPct}%)`, kind:'resolve' });
      window.toast && window.toast(`${id} resolved · claimant at ${detail.claimantPct}%`, 'ok');
      setSelected(null);
    } else if (kind === 'accept-counter') {
      mutate(id, { status:'resolved', resolution:'counter' },
        { ...now, what:`Resolved · accepted counter-claim (${100-detail.claimantPct}%)`, kind:'resolve' });
      window.toast && window.toast(`${id} resolved · counter at ${100-detail.claimantPct}%`, 'ok');
      setSelected(null);
    } else if (kind === 'counter-propose') {
      mutate(id, { status:'review' },
        { ...now, what:`Counter-proposal drafted · awaiting ${party}`, kind:'note' });
      window.toast && window.toast(`Counter-proposal drafted for ${party}`, 'soft');
    } else if (kind === 'split-disputed') {
      mutate(id, { status:'resolved', resolution:'split' },
        { ...now, what:`Resolved · disputed ${detail.disputed}% split 50/50, held in escrow`, kind:'resolve' });
      window.toast && window.toast(`${id} · ${detail.disputed}% split, held in escrow`, 'ok');
      setSelected(null);
    } else if (kind === 'escalate') {
      mutate(id, { status:'review' },
        { ...now, what:'Escalated to legal · K. Davis assigned', kind:'note' });
      window.toast && window.toast(`${id} escalated to legal`, 'soft');
    } else if (kind === 'reject') {
      mutate(id, { status:'resolved', resolution:'rejected' },
        { ...now, what:`Rejected · ${party} notified`, kind:'resolve' });
      window.toast && window.toast(`${id} rejected · ${party} notified`, 'soft');
      setSelected(null);
    }
  };

  React.useEffect(() => {
    if (!selected) return;
    const onKey = (e) => { if (e.key === 'Escape') setSelected(null); };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [selected]);
  return (
    <div>
      <PageHeader
        eyebrow={`RIGHTS · CLAIMS QUEUE · ${applied.filter(c=>c.status==='open').length} open · ${applied.filter(c=>c.status==='open' && c.severity==='high').length} conflicts hot`}
        title="Claims & conflicts"
        highlight="conflicts"
        actions={
          <>
            <Btn variant="secondary" icon={<Ic.Branch/>} size="sm" onClick={() => go && go('cwr')}>CWR Inbox</Btn>
            <Btn variant="primary" icon={<Ic.Plus/>} size="sm" onClick={() => window.toast && window.toast('New claim flow opens — paste an ISWC or recording ID to start', 'soft')}>New claim</Btn>
          </>
        }
      />

      {/* Severity bar — clickable tiles drive the queue filter */}
      <div style={{display:'grid',gridTemplateColumns:'repeat(4,1fr)',borderTop:'1px solid var(--rule)',borderBottom:'1px solid var(--rule)',marginBottom:18}}>
        {(() => {
          const baseOpen      = window.CATALOG_STATS?.claims?.open || applied.filter(c=>c.status==='open').length;
          const openCount     = applied.filter(c=>c.status==='open').length;
          const hotCount      = applied.filter(c=>c.status==='open' && c.severity==='high').length;
          const warmCount     = applied.filter(c=>c.status==='open' && c.severity==='mid').length;
          const resolvedCount = applied.filter(c=>c.status==='resolved').length;
          const resolvedDelta = Object.values(overlays).filter(o=>o.patch && o.patch.status==='resolved').length;
          const hotAtRisk = applied.filter(c=>c.status==='open' && c.severity==='high')
            .reduce((sum,c)=>{ const d = _claimDetail(c); return sum + d.estimatedAtRisk; }, 0);
          const fmt$ = (n) => n >= 1000 ? `$${Math.round(n/1000)}K` : `$${n}`;
          return [
            {k:'open',     l:'TOTAL OPEN',     v: openCount,     sub: openCount < baseOpen ? `−${baseOpen-openCount} resolved` : '+3 today',  tone:''},
            {k:'hot',      l:'HOT · >5d',      v: hotCount,      sub: `${fmt$(hotAtRisk)} at risk`,                                            tone:'danger'},
            {k:'warm',     l:'WARM',           v: warmCount,     sub:'review needed',                                                          tone:'accent'},
            {k:'resolved', l:'RESOLVED · 7d',  v: resolvedCount, sub: resolvedDelta ? `+${resolvedDelta} just now` : 'avg 2.4d',               tone:'ok'},
          ];
        })().map((s,i,arr)=>{
          const active = filter === s.k;
          return (
            <button key={s.k} onClick={()=>setFilter(s.k)}
              style={{padding:'18px 16px',borderRight:i<arr.length-1?'1px solid var(--rule)':'none',borderTop:'none',borderBottom:'none',borderLeft:'none',
                background: active ? 'var(--ink)' : (s.tone==='accent'?'var(--accent)':'transparent'),
                color: active ? 'var(--bg)' : (s.tone==='accent'?'var(--accent-ink)':'var(--ink)'),
                textAlign:'left',cursor:'pointer',font:'inherit',position:'relative',transition:'background .12s ease'}}
              onMouseEnter={e=>{ if (!active) e.currentTarget.style.background = s.tone==='accent' ? 'var(--accent)' : 'var(--bg-2)'; }}
              onMouseLeave={e=>{ if (!active) e.currentTarget.style.background = s.tone==='accent' ? 'var(--accent)' : 'transparent'; }}>
              <div className="ff-mono upper" style={{fontSize:10,fontWeight:600,letterSpacing:'.08em',marginBottom:8,
                color: active ? 'var(--bg)' : (s.tone==='danger'?'var(--danger)':s.tone==='ok'?'var(--ok)':'inherit')}}>{s.l}</div>
              <div className="ff-display num" style={{fontSize:48,fontWeight:600,letterSpacing:'-0.04em',lineHeight:1}}>{s.v}</div>
              <div className="ff-mono" style={{fontSize:11,color: active ? 'var(--bg)' : (s.tone==='accent'?'var(--accent-ink)':'var(--ink-3)'),marginTop:6,opacity: active ? .75 : 1}}>{s.sub}</div>
              {active && <span aria-hidden style={{position:'absolute',bottom:0,left:0,right:0,height:3,background:'var(--accent,#d97757)'}}/>}
            </button>
          );
        })}
      </div>

      {/* Secondary filter row — show-all escape hatch + result count */}
      <div style={{display:'flex',alignItems:'center',gap:12,marginBottom:14}}>
        <span className="ff-mono upper" style={{fontSize:10,letterSpacing:'.1em',color:'var(--ink-3)'}}>
          {filtered.length} {filter === 'all' ? 'claims total' : filter === 'resolved' ? 'resolved' : filter === 'hot' ? 'hot' : filter === 'warm' ? 'warm' : 'open'}
        </span>
        <span style={{flex:1}}/>
        {filter !== 'all' && (
          <button onClick={()=>setFilter('all')} className="ff-mono upper"
            style={{padding:'4px 10px',fontSize:10,letterSpacing:'.08em',background:'transparent',color:'var(--ink-3)',border:'1px solid var(--rule)',cursor:'pointer'}}>
            Show all ({applied.length})
          </button>
        )}
      </div>

      <div>
        {/* Queue — full width regardless of selection */}
        <div>
          <div style={{borderTop:'1px solid var(--rule)'}}>
            <div className="ff-mono upper" style={{display:'grid',gridTemplateColumns:'24px 1fr 90px 90px 60px 70px 30px',gap:14,
              padding:'10px 14px',fontSize:10,color:'var(--ink-3)',background:'var(--bg-2)',borderBottom:'1px solid var(--rule)'}}>
              <span/><span>WORK / PARTIES</span><span>CLAIM ID</span><span>METHOD</span><span style={{textAlign:'right'}}>%</span><span style={{textAlign:'right'}}>AGE</span><span/>
            </div>
            {filtered.map((c,i)=>(
              <div key={c.id} onClick={()=>setSelected(c)}
                style={{display:'grid',gridTemplateColumns:'24px 1fr 90px 90px 60px 70px 30px',gap:14,
                  padding:'14px',borderBottom:'1px solid var(--rule-soft)',alignItems:'center',cursor:'pointer',
                  background:selected?.id===c.id?'var(--bg-2)':'transparent',
                  borderLeft:c.severity==='high'?'3px solid var(--danger)':c.severity==='mid'?'3px solid var(--accent)':'3px solid transparent'}}
                onMouseEnter={e=>{if(selected?.id!==c.id)e.currentTarget.style.background='var(--bg-2)'}}
                onMouseLeave={e=>{if(selected?.id!==c.id)e.currentTarget.style.background='transparent'}}>
                <Pill tone={c.severity==='high'?'danger':c.severity==='mid'?'accent':'soft'}>{c.severity[0].toUpperCase()}</Pill>
                <div style={{minWidth:0}}>
                  <div style={{fontSize:14,fontWeight:600,letterSpacing:'-0.01em',marginBottom:2}}>{c.work}</div>
                  <div className="ff-mono" style={{fontSize:10,color:'var(--ink-3)'}}>
                    {c.iswc} · <b style={{color:'var(--ink)'}}>{c.claimant}</b> <span style={{color:'var(--ink-4)'}}>vs</span> <b style={{color:'var(--ink)'}}>{c.party}</b>
                  </div>
                </div>
                <span className="ff-mono" style={{fontSize:10,color:'var(--ink-3)'}}>{c.id.replace('C-2026-','')}</span>
                <span className="ff-mono upper" style={{fontSize:10}}>{c.method}</span>
                <span className="ff-mono num" style={{fontSize:13,textAlign:'right'}}>{c.pct}%</span>
                <span className="ff-mono num" style={{fontSize:11,color:'var(--ink-3)',textAlign:'right'}}>{c.age}d</span>
                <Ic.Right width={14} height={14} style={{color:'var(--ink-4)'}}/>
              </div>
            ))}
          </div>
        </div>

        {/* Detail panel — resolution workspace as right-edge drawer */}
        {selected && detail && (<>
          {/* Backdrop */}
          <div onClick={()=>setSelected(null)}
            style={{position:'fixed',inset:0,background:'rgba(0,0,0,0.18)',zIndex:50}}/>
          {/* Drawer */}
          <div style={{position:'fixed',top:0,right:0,bottom:0,width:'min(520px, 92vw)',
            background:'var(--bg)',borderLeft:'1px solid var(--rule)',padding:'24px 28px',
            overflow:'auto',zIndex:51,boxShadow:'-12px 0 32px rgba(0,0,0,0.08)'}}>
            <div style={{display:'flex',justifyContent:'space-between',alignItems:'flex-start',marginBottom:14}}>
              <div className="ff-mono upper" style={{fontSize:10,color:'var(--ink-3)'}}>CLAIM · {selected.id}</div>
              <button onClick={()=>setSelected(null)}><Ic.X width={16} height={16}/></button>
            </div>
            <div className="heading-swap ff-display" style={{fontSize:32,fontWeight:600,letterSpacing:'-0.03em',lineHeight:1,marginBottom:6}}>{selected.work}</div>
            <div className="ff-mono" style={{fontSize:11,color:'var(--ink-3)',marginBottom:14}}>{window.iswcDisplay ? window.iswcDisplay(selected.iswc) : selected.iswc}</div>

            {/* Deadline strip */}
            <div style={{
              display:'grid',gridTemplateColumns:'1fr 1fr',gap:0,
              border:'1px solid var(--rule)',marginBottom:18,
              background: detail.deadlineDays < 0 ? 'rgba(196,68,68,.06)' : detail.deadlineDays < 3 ? 'rgba(217,119,87,.06)' : 'transparent'}}>
              <div style={{padding:'12px 14px',borderRight:'1px solid var(--rule)'}}>
                <div className="ff-mono upper" style={{fontSize:9,letterSpacing:'.1em',color:'var(--ink-3)',marginBottom:4}}>RESPONSE DUE</div>
                <div className="ff-display num" style={{fontSize:22,fontWeight:600,letterSpacing:'-0.02em',lineHeight:1,
                  color: detail.deadlineDays < 0 ? 'var(--danger,#c44)' : detail.deadlineDays < 3 ? 'var(--accent,#d97757)' : 'var(--ink)'}}>
                  {detail.deadlineDays < 0 ? `${Math.abs(detail.deadlineDays)}d overdue` : detail.deadlineDays === 0 ? 'today' : `${detail.deadlineDays}d`}
                </div>
                <div className="ff-mono" style={{fontSize:10,color:'var(--ink-3)',marginTop:4}}>{detail.deadline.toISOString().slice(0,10)}</div>
              </div>
              <div style={{padding:'12px 14px'}}>
                <div className="ff-mono upper" style={{fontSize:9,letterSpacing:'.1em',color:'var(--ink-3)',marginBottom:4}}>$ AT RISK</div>
                <div className="ff-display num" style={{fontSize:22,fontWeight:600,letterSpacing:'-0.02em',lineHeight:1}}>
                  ${detail.estimatedAtRisk.toLocaleString()}
                </div>
                <div className="ff-mono" style={{fontSize:10,color:'var(--ink-3)',marginTop:4}}>{detail.disputed}% disputed</div>
              </div>
            </div>

            {/* Parties — stacked vertically so each gets full rail width */}
            <Section num="A">Parties</Section>
            <div style={{border:'1px solid var(--rule)',marginBottom:14}}>
              {/* Claimant */}
              <div style={{padding:'14px 16px',borderBottom:'1px solid var(--rule)'}}>
                <div style={{display:'flex',justifyContent:'space-between',alignItems:'baseline',marginBottom:8}}>
                  <div>
                    <div className="ff-mono upper" style={{fontSize:9,letterSpacing:'.1em',color:'var(--ink-3)',marginBottom:4}}>CLAIMANT</div>
                    <div style={{fontWeight:600,fontSize:15,letterSpacing:'-0.01em'}}>{selected.claimant}</div>
                    <div className="ff-mono" style={{fontSize:10,color:'var(--ink-3)',marginTop:2}}>publisher · {detail.claimantSociety}</div>
                  </div>
                  <div style={{textAlign:'right'}}>
                    <span className="ff-display num" style={{fontSize:36,fontWeight:600,letterSpacing:'-0.03em',lineHeight:1}}>{detail.claimantPct}</span>
                    <span className="ff-mono" style={{fontSize:11,color:'var(--ink-3)',marginLeft:3}}>%</span>
                  </div>
                </div>
                <div style={{height:3,background:'var(--bg-2)',marginBottom:12,position:'relative'}}>
                  <div style={{position:'absolute',inset:0,width:`${detail.claimantPct}%`,background:'var(--ink)'}}/>
                </div>
                <div className="ff-mono upper" style={{fontSize:9,letterSpacing:'.1em',color:'var(--ink-3)',marginBottom:6}}>EVIDENCE · {detail.claimantEvidence.length}</div>
                {detail.claimantEvidence.map((e,i)=>(
                  <div key={i} className="ff-mono" style={{fontSize:11,padding:'4px 0',display:'flex',gap:8,color:'var(--ink-2)'}}>
                    <span style={{color:'var(--ink-4)',width:10,flexShrink:0}}>{_evidenceIcon(e.kind)}</span>
                    <span style={{flex:1,whiteSpace:'nowrap',overflow:'hidden',textOverflow:'ellipsis'}}>{e.name}</span>
                    <span style={{color:'var(--ink-4)',flexShrink:0}}>{e.size}</span>
                  </div>
                ))}
              </div>
              {/* Counter-party */}
              <div style={{padding:'14px 16px',background:'rgba(0,0,0,.015)'}}>
                <div style={{display:'flex',justifyContent:'space-between',alignItems:'baseline',marginBottom:8}}>
                  <div>
                    <div className="ff-mono upper" style={{fontSize:9,letterSpacing:'.1em',color:'var(--ink-3)',marginBottom:4}}>COUNTER-PARTY</div>
                    <div style={{fontWeight:600,fontSize:15,letterSpacing:'-0.01em'}}>{selected.party}</div>
                    <div className="ff-mono" style={{fontSize:10,color:'var(--ink-3)',marginTop:2}}>publisher · {detail.partySociety}</div>
                  </div>
                  <div style={{textAlign:'right'}}>
                    <span className="ff-display num" style={{fontSize:36,fontWeight:600,letterSpacing:'-0.03em',lineHeight:1}}>{detail.counterPct}</span>
                    <span className="ff-mono" style={{fontSize:11,color:'var(--ink-3)',marginLeft:3}}>%</span>
                  </div>
                </div>
                <div style={{height:3,background:'var(--bg-2)',marginBottom:12,position:'relative'}}>
                  <div style={{position:'absolute',inset:0,width:`${detail.counterPct}%`,background:'var(--accent,#d97757)'}}/>
                </div>
                <div className="ff-mono upper" style={{fontSize:9,letterSpacing:'.1em',color:'var(--ink-3)',marginBottom:6}}>EVIDENCE · {detail.counterEvidence.length}</div>
                {detail.counterEvidence.map((e,i)=>(
                  <div key={i} className="ff-mono" style={{fontSize:11,padding:'4px 0',display:'flex',gap:8,color:'var(--ink-2)'}}>
                    <span style={{color:'var(--ink-4)',width:10,flexShrink:0}}>{_evidenceIcon(e.kind)}</span>
                    <span style={{flex:1,whiteSpace:'nowrap',overflow:'hidden',textOverflow:'ellipsis'}}>{e.name}</span>
                    <span style={{color:'var(--ink-4)',flexShrink:0}}>{e.size}</span>
                  </div>
                ))}
              </div>
            </div>

            {/* Disputed band visualization */}
            <div style={{marginBottom:18,padding:'10px 12px',background:'var(--bg-2)',border:'1px solid var(--rule-soft)'}}>
              <div className="ff-mono upper" style={{fontSize:9,letterSpacing:'.1em',color:'var(--ink-3)',marginBottom:8}}>OVERLAP · IN CONFLICT</div>
              <div style={{position:'relative',height:14,background:'var(--bg)',border:'1px solid var(--rule)'}}>
                <div style={{position:'absolute',top:0,bottom:0,left:0,width:`${detail.claimantPct}%`,background:'var(--ink)',opacity:.55}}/>
                <div style={{position:'absolute',top:0,bottom:0,right:0,width:`${detail.counterPct}%`,background:'var(--accent,#d97757)',opacity:.55}}/>
                <div style={{position:'absolute',top:0,bottom:0,left:`${100-detail.counterPct}%`,width:`${detail.disputed}%`,background:'repeating-linear-gradient(45deg,rgba(196,68,68,.6),rgba(196,68,68,.6) 4px,transparent 4px,transparent 8px)',border:'1px solid var(--danger,#c44)'}}/>
              </div>
              <div className="ff-mono" style={{fontSize:10,color:'var(--ink-3)',marginTop:8,display:'flex',justifyContent:'space-between'}}>
                <span>{detail.claimantPct}% claimed</span>
                <span style={{color:'var(--danger,#c44)',fontWeight:600}}>{detail.disputed}% disputed</span>
                <span>{detail.counterPct}% counter</span>
              </div>
            </div>

            {/* Activity timeline */}
            <Section num="B">Timeline</Section>
            <div style={{position:'relative',marginBottom:18,paddingLeft:16}}>
              <div style={{position:'absolute',left:4,top:6,bottom:6,width:1,background:'var(--rule)'}}/>
              {[...(ovExtras?.addedTimeline||[]), ...detail.timeline].map((e,i)=>(
                <div key={i} style={{position:'relative',paddingBottom:12}}>
                  <span style={{position:'absolute',left:-16,top:4,width:9,height:9,
                    background: e.kind==='resolve' ? 'var(--ok,#3a6a3a)' : e.kind==='counter' ? 'var(--danger,#c44)' : e.kind==='evidence' ? 'var(--accent,#d97757)' : 'var(--ink)',
                    border:'2px solid var(--bg)',
                    borderRadius:'50%'}}/>
                  <div style={{display:'flex',justifyContent:'space-between',gap:10,alignItems:'baseline'}}>
                    <span style={{fontSize:12,color:'var(--ink-2)',lineHeight:1.4,minWidth:0,flex:1}}>{e.what}</span>
                    <span className="ff-mono" style={{fontSize:10,color:'var(--ink-4)',whiteSpace:'nowrap',flexShrink:0}}>{e.d===0?'today':`${e.d}d ago`}</span>
                  </div>
                  <span className="ff-mono" style={{fontSize:9,color:'var(--ink-3)',letterSpacing:'.04em'}}>{e.who}</span>
                </div>
              ))}
            </div>

            {/* Case notes */}
            <Section num="C">Case notes</Section>
            <div style={{padding:'10px 12px',background:'var(--bg-2)',border:'1px solid var(--rule-soft)',marginBottom:14,fontSize:12,lineHeight:1.6,color:'var(--ink-2)'}}>
              {detail.notes}
              {(ovExtras?.addedNotes||[]).map((n,i)=>(
                <div key={i} style={{marginTop:8,paddingTop:8,borderTop:'1px dashed var(--rule)',color:'var(--ink-3)'}}>{n}</div>
              ))}
            </div>
            <div style={{display:'flex',gap:8,marginBottom:18,flexWrap:'wrap'}}>
              <Btn variant="ghost" size="sm" icon={<Ic.Plus/>} onClick={()=>act('add-evidence')}>Add evidence</Btn>
              <Btn variant="ghost" size="sm" onClick={()=>act('add-note')}>Add note</Btn>
              <Btn variant="ghost" size="sm" onClick={()=>act('request-docs')}>Request docs</Btn>
            </div>

            {/* Resolution actions */}
            <div style={{borderTop:'1px solid var(--rule)',paddingTop:14}}>
              <div className="ff-mono upper" style={{fontSize:9,letterSpacing:'.1em',color:'var(--ink-3)',marginBottom:10}}>
                {selectedLive.status==='resolved' ? `RESOLVED · ${ovExtras?.patch?.resolution?.toUpperCase()||'—'}` : 'RESOLVE'}
              </div>
              {selectedLive.status==='resolved' ? (
                <div style={{padding:'12px 14px',background:'var(--bg-2)',border:'1px solid var(--rule)',fontSize:12,color:'var(--ink-2)'}}>
                  This claim is resolved. Disputed amount has been allocated and counter-party notified.
                </div>
              ) : (<>
              {/* Tier 1 — Agree. The 80% path. */}
              <div className="ff-mono upper" style={{fontSize:9,letterSpacing:'.1em',color:'var(--ink-4)',marginBottom:6}}>AGREE</div>
              <div style={{display:'grid',gridTemplateColumns:'1fr 1fr',gap:6,marginBottom:14}}>
                <Btn variant="primary" size="sm" icon={<Ic.Check/>} onClick={()=>act('accept-claimant')}>Accept claimant · {detail.claimantPct}%</Btn>
                <Btn variant="secondary" size="sm" onClick={()=>act('accept-counter')}>Accept counter · {100-detail.claimantPct}%</Btn>
              </div>

              {/* Tier 2 — Negotiate. The exploratory paths. */}
              <div className="ff-mono upper" style={{fontSize:9,letterSpacing:'.1em',color:'var(--ink-4)',marginBottom:6}}>NEGOTIATE</div>
              <div style={{display:'grid',gridTemplateColumns:'1fr 1fr',gap:6,marginBottom:14}}>
                <Btn variant="secondary" size="sm" onClick={()=>act('counter-propose')}>Counter-propose</Btn>
                <Btn variant="secondary" size="sm" onClick={()=>act('split-disputed')}>Split {detail.disputed}% disputed</Btn>
              </div>

              {/* Tier 3 — Escape hatch. Visually demoted to plain text links. */}
              <div style={{display:'flex',gap:18,paddingTop:10,borderTop:'1px dashed var(--rule)'}}>
                <button onClick={()=>act('escalate')} className="ff-mono upper"
                  style={{fontSize:10,letterSpacing:'.08em',color:'var(--ink-3)',background:'none',border:0,cursor:'pointer',padding:0}}>
                  Escalate to legal →
                </button>
                <span style={{flex:1}}/>
                <button onClick={()=>act('reject')} className="ff-mono upper"
                  style={{fontSize:10,letterSpacing:'.08em',color:'var(--danger,#c44)',background:'none',border:0,cursor:'pointer',padding:0}}>
                  Reject all
                </button>
              </div>
              </>)}
            </div>
          </div>
        </>)}
      </div>
    </div>
  );
}

// ───────────────────────────────────────────────────────────── CWR / DDEX
function ScreenCwr({ go }) {
  const [tab, setTab] = useS2('cwr');
  return (
    <div>
      <PageHeader
        eyebrow="DELIVERIES · OUTBOUND TRAFFIC · 24H"
        title="Transmissions"
        highlight="Transmissions"
        actions={
          <div style={{display:'flex',gap:8,alignItems:'center'}}>
            <span className="ff-mono upper" style={{fontSize:10,color:'var(--ink-3)'}}>NEXT BATCH IN</span>
            <span className="ff-display num" style={{fontSize:24,fontWeight:600,letterSpacing:'-0.02em'}}>02:14:33</span>
            <Btn variant="accent" icon={<Ic.Send/>} onClick={() => window.toast && window.toast('Manual batch generation queued', 'ok')}>Generate now</Btn>
          </div>
        }
      />

      {/* Tab strip */}
      <div style={{display:'flex',borderBottom:'1px solid var(--rule)',marginBottom:24}}>
        {[
          {k:'cwr',l:'CWR · Societies',n:6},
          {k:'ddex',l:'DDEX · DSPs',n:14},
          {k:'royalty',l:'Royalty · Inbound',n:12},
          {k:'attempts',l:'Attempts log',n:248},
        ].map(t=>(
          <button key={t.k} onClick={()=>setTab(t.k)} className="ff-mono upper"
            style={{padding:'14px 18px',fontSize:11,fontWeight:600,letterSpacing:'.08em',
              borderBottom: tab===t.k?'3px solid var(--ink)':'3px solid transparent',
              marginBottom:-1,color:tab===t.k?'var(--ink)':'var(--ink-3)',display:'flex',gap:8,alignItems:'baseline'}}>
            {t.l} <span className="num" style={{fontSize:10,opacity:.6,fontWeight:400}}>{t.n}</span>
          </button>
        ))}
      </div>

      {/* Pipeline visualization — newspaper-style trains */}
      <Section num="01">In flight · today</Section>
      <div style={{display:'grid',gridTemplateColumns:'repeat(3,1fr)',gap:0,borderTop:'1px solid var(--rule)',marginBottom:32}}>
        {TXN.map((t,i,arr)=>(
          <div key={t.id} style={{padding:'18px',borderRight:(i+1)%3===0?'none':'1px solid var(--rule)',borderBottom:i<arr.length-3?'1px solid var(--rule)':'none'}}>
            <div style={{display:'flex',justifyContent:'space-between',alignItems:'flex-start',marginBottom:14}}>
              <div>
                <div className="ff-mono num" style={{fontSize:10,color:'var(--ink-4)'}}>SEQ {String(i+1).padStart(3,'0')}</div>
                <div className="heading-swap ff-display" style={{fontSize:28,fontWeight:600,letterSpacing:'-0.03em',marginTop:4}}>{t.recv}</div>
              </div>
              <Pill tone={t.status==='acknowledged'?'ok':t.status==='rejected'?'danger':t.status==='submitted'?'info':'soft'} dot>
                {t.status}
              </Pill>
            </div>
            <div style={{padding:'12px 0',borderTop:'1px solid var(--rule-soft)',borderBottom:'1px solid var(--rule-soft)',display:'flex',justifyContent:'space-between',alignItems:'center'}}>
              <span className="ff-mono num" style={{fontSize:24,fontWeight:600}}>{t.count}</span>
              <span className="ff-mono upper" style={{fontSize:10,color:'var(--ink-3)'}}>WORKS</span>
            </div>
            <div style={{padding:'10px 0 0',display:'flex',justifyContent:'space-between'}}>
              <div>
                <div className="ff-mono upper" style={{fontSize:9,color:'var(--ink-4)'}}>SENT</div>
                <div className="ff-mono num" style={{fontSize:12}}>{t.sent}</div>
              </div>
              <div style={{textAlign:'right'}}>
                <div className="ff-mono upper" style={{fontSize:9,color:'var(--ink-4)'}}>ACK</div>
                <div className="ff-mono num" style={{fontSize:12}}>{t.ack}</div>
              </div>
            </div>
            <div style={{marginTop:12,fontSize:11,color:'var(--ink-3)'}} className="ff-mono">{t.file}</div>
          </div>
        ))}
      </div>

      {/* Society health table */}
      <Section num="02">Society health · 90-day</Section>
      <div style={{borderTop:'1px solid var(--rule)'}}>
        {SOCIETIES.map(s=>(
          <div key={s.acronym} style={{display:'grid',gridTemplateColumns:'80px 1fr 1fr 90px 80px',gap:18,padding:'16px 0',borderBottom:'1px solid var(--rule-soft)',alignItems:'center'}}>
            <span className="ff-display" style={{fontSize:20,fontWeight:600,letterSpacing:'-0.02em'}}>{s.acronym}</span>
            <span className="ff-mono" style={{fontSize:12,color:'var(--ink-3)'}}>{s.country} · {s.territory}</span>
            <span className="ff-mono num" style={{fontSize:11,color:'var(--ink-3)'}}>
              <AsciiBar value={s.ackRate} max={100} width={32}/>
            </span>
            <span className="ff-mono num" style={{fontSize:14,textAlign:'right'}}>{s.ackRate}%</span>
            <span className="ff-mono" style={{fontSize:11,color:'var(--ink-3)',textAlign:'right'}}>{s.lastSent}</span>
          </div>
        ))}
      </div>
    </div>
  );
}

// ───────────────────────────────────────────────────────────── ANALYTICS
function ScreenAnalytics({ go }) {
  return (
    <div>
      <div style={{marginBottom:24}}>
        <div className="ff-mono upper" style={{fontSize:10,color:'var(--ink-3)',marginBottom:6}}>EARNINGS · Q2 2026 · CONSOLIDATED</div>
        <h1 className="heading-swap ff-display" style={{fontSize:'clamp(64px,10vw,160px)',fontWeight:700,letterSpacing:'-0.05em',lineHeight:.85,margin:0}}>
          $248,302
          <span style={{fontSize:'0.32em',color:'var(--ok)',marginLeft:14,letterSpacing:0,verticalAlign:'middle'}}>↑ 8.4%</span>
        </h1>
        <div className="ff-mono" style={{fontSize:13,color:'var(--ink-3)',marginTop:14}}>
          GROSS · MAY 1 − JUL 31 · BEFORE PUBLISHING SHARE & SOCIETY ADMIN ·{' '}
          <a style={{borderBottom:'1px solid var(--ink)',cursor:'pointer'}}>view breakdown →</a>
        </div>
      </div>

      {/* Big chart strip */}
      <div style={{borderTop:'1px solid var(--rule)',borderBottom:'1px solid var(--rule)',padding:'24px 0',marginBottom:32}}>
        <div style={{display:'flex',gap:20,marginBottom:16}}>
          {['1M','3M','6M','12M','YTD','ALL'].map((t,i)=>(
            <button key={t} className="ff-mono upper" style={{fontSize:11,fontWeight:500,letterSpacing:'.08em',
              padding:'4px 10px',background:i===2?'var(--ink)':'transparent',color:i===2?'var(--bg)':'var(--ink-2)',border:'1px solid var(--rule)'}}>{t}</button>
          ))}
        </div>
        <BigChart/>
      </div>

      {/* Source breakdown */}
      <div style={{display:'grid',gridTemplateColumns:'1.4fr 1fr',gap:32}}>
        <div>
          <Section num="01">By source</Section>
          {[
            {l:'Spotify',  v:84221, p:34, c:'#1ed760'},
            {l:'Apple Music',v:52890, p:21, c:'#fa243c'},
            {l:'YouTube',  v:38110, p:15, c:'#ff0000'},
            {l:'Amazon',   v:24411, p:10, c:'#00a8e1'},
            {l:'TikTok',   v:21002, p:8,  c:'#000'},
            {l:'SoundExchange',v:14890,p:6,c:'#1f4ed8'},
            {l:'Other DSPs',v:12778,p:6,c:'#888'},
          ].map(s=>(
            <div key={s.l} style={{display:'grid',gridTemplateColumns:'140px 1fr 100px 50px',gap:14,padding:'14px 0',borderBottom:'1px solid var(--rule-soft)',alignItems:'center'}}>
              <span style={{fontSize:14,fontWeight:600,display:'flex',alignItems:'center',gap:10}}>
                <span style={{width:10,height:10,background:s.c,display:'inline-block'}}/> {s.l}
              </span>
              <div style={{height:6,background:'var(--bg-2)',position:'relative'}}>
                <div style={{position:'absolute',inset:0,width:`${s.p*2.4}%`,background:s.c}}/>
              </div>
              <span className="ff-mono num" style={{fontSize:13,textAlign:'right'}}>${s.v.toLocaleString()}</span>
              <span className="ff-mono num" style={{fontSize:11,color:'var(--ink-3)',textAlign:'right'}}>{s.p}%</span>
            </div>
          ))}
        </div>

        <div>
          <Section num="02">Top earners · Q2</Section>
          {WORKS.slice(0,8).map((w,i)=>(
            <div key={w.id} style={{display:'grid',gridTemplateColumns:'24px 1fr 90px',gap:10,padding:'12px 0',borderBottom:'1px solid var(--rule-soft)',alignItems:'center',cursor:'pointer'}}
              onClick={()=>go('work',w)}>
              <span className="ff-display num" style={{fontSize:18,color:'var(--ink-4)',fontWeight:600}}>{String(i+1).padStart(2,'0')}</span>
              <div>
                <div style={{fontSize:13,fontWeight:600,letterSpacing:'-0.01em'}}>{w.title}</div>
                <div className="ff-mono" style={{fontSize:9,color:'var(--ink-3)'}}>{w.writers[0]}</div>
              </div>
              <span className="ff-mono num" style={{fontSize:13,textAlign:'right'}}>${(w.plays/1e5).toFixed(0).slice(0,5)}</span>
            </div>
          ))}

          <Section num="03">Territories</Section>
          <div style={{display:'grid',gridTemplateColumns:'1fr 1fr',gap:0,borderLeft:'1px solid var(--rule-soft)'}}>
            {[
              {c:'US',v:'$108K',p:44},{c:'GB',v:'$32K',p:13},{c:'DE',v:'$24K',p:10},{c:'FR',v:'$18K',p:7},
              {c:'JP',v:'$14K',p:6},{c:'CA',v:'$12K',p:5},{c:'BR',v:'$8K',p:3},{c:'OTHER',v:'$32K',p:12}
            ].map(t=>(
              <div key={t.c} style={{padding:'12px',borderRight:'1px solid var(--rule-soft)',borderBottom:'1px solid var(--rule-soft)'}}>
                <div className="ff-mono upper" style={{fontSize:10,color:'var(--ink-3)'}}>{t.c}</div>
                <div className="ff-display num" style={{fontSize:20,fontWeight:600,marginTop:2}}>{t.v}</div>
                <div className="ff-mono num" style={{fontSize:9,color:'var(--ink-4)',marginTop:2}}>{t.p}%</div>
              </div>
            ))}
          </div>
        </div>
      </div>

      <PlatformPopularitySection go={go}/>
    </div>
  );
}

// ── Platform popularity (Spotify / Tidal / Qobuz / Apple) ──────────────
function PlatformPopularitySection({ go }) {
  const releases = (typeof RELEASES !== 'undefined') ? RELEASES : [];
  const details  = window.RELEASE_PLATFORM_DETAILS || {};
  const trend    = window.RELEASE_POPULARITY_TREND || {};

  // Build a sorted-by-composite-popularity list with platform breakdown
  const rows = releases
    .map(r => ({ r, d: details[r.id], pop: window.releasePopularityScore?.(r.id) }))
    .filter(x => x.d && x.pop != null)
    .sort((a,b) => b.pop - a.pop);

  // Catalog averages per DSP
  const avg = (k) => {
    const vals = rows.map(x => x.d[k]).filter(v => typeof v === 'number');
    if (!vals.length) return null;
    return Math.round(vals.reduce((a,b)=>a+b,0) / vals.length);
  };
  const platformAvgs = [
    {l:'SPOTIFY',     c:'#1ed760', v:avg('spotifyPopularity')},
    {l:'TIDAL',       c:'#000000', v:avg('tidalPopularity')},
    {l:'QOBUZ',       c:'#0070d8', v:avg('qobuzPopularity')},
    {l:'APPLE MUSIC', c:'#fa233b', v:avg('appleMusicScore')},
  ].filter(x => x.v != null);

  // Selected release for the trend chart
  const [selectedId, setSelectedId] = React.useState(rows[0]?.r.id);
  const selected = rows.find(x => x.r.id === selectedId) || rows[0];
  const series = selected ? trend[selected.r.id] : null;

  if (rows.length === 0) return null;

  return (
    <div style={{marginTop:48,paddingTop:32,borderTop:'1px solid var(--rule)'}}>
      <Section num="04">Platform popularity</Section>

      {/* Catalog DSP averages */}
      <div style={{display:'grid',gridTemplateColumns:`repeat(${platformAvgs.length},1fr)`,gap:0,
        border:'1px solid var(--rule)',marginBottom:32}}>
        {platformAvgs.map((p,i)=>(
          <div key={p.l} style={{padding:'18px 22px',borderRight:i<platformAvgs.length-1?'1px solid var(--rule)':'none'}}>
            <div style={{display:'flex',alignItems:'center',gap:8,marginBottom:10}}>
              <span style={{width:10,height:10,background:p.c,display:'inline-block'}}/>
              <span className="ff-mono upper" style={{fontSize:10,fontWeight:600,letterSpacing:'.1em'}}>{p.l}</span>
            </div>
            <div className="ff-display num" style={{fontSize:48,fontWeight:600,letterSpacing:'-0.04em',lineHeight:1}}>
              {p.v}<span style={{fontSize:18,color:'var(--ink-3)',fontWeight:400}}>/100</span>
            </div>
            <div style={{height:4,background:'var(--bg-2)',marginTop:10,position:'relative',overflow:'hidden'}}>
              <div style={{height:'100%',width:`${p.v}%`,background:p.c}}/>
            </div>
            <div className="ff-mono upper" style={{fontSize:9,letterSpacing:'.1em',color:'var(--ink-3)',marginTop:10}}>
              CATALOG AVG · {rows.length} RELEASES
            </div>
          </div>
        ))}
      </div>

      <div style={{display:'grid',gridTemplateColumns:'1.2fr 1fr',gap:32}}>
        {/* Per-release heatmap (sorted by composite score) */}
        <div>
          <div className="ff-mono upper" style={{fontSize:10,letterSpacing:'.1em',color:'var(--ink-3)',marginBottom:14}}>
            BY RELEASE · SORTED BY COMPOSITE
          </div>
          <div style={{border:'1px solid var(--rule)'}}>
            <div style={{display:'grid',gridTemplateColumns:'1fr 56px 56px 56px 56px 64px',gap:0,
              padding:'9px 14px',background:'var(--bg-2)',borderBottom:'1px solid var(--rule)'}}>
              <span className="ff-mono upper" style={{fontSize:9,color:'var(--ink-3)',letterSpacing:'.08em'}}>RELEASE</span>
              <span className="ff-mono upper" style={{fontSize:9,color:'var(--ink-3)',letterSpacing:'.08em',textAlign:'center'}}>SP</span>
              <span className="ff-mono upper" style={{fontSize:9,color:'var(--ink-3)',letterSpacing:'.08em',textAlign:'center'}}>TI</span>
              <span className="ff-mono upper" style={{fontSize:9,color:'var(--ink-3)',letterSpacing:'.08em',textAlign:'center'}}>QO</span>
              <span className="ff-mono upper" style={{fontSize:9,color:'var(--ink-3)',letterSpacing:'.08em',textAlign:'center'}}>AM</span>
              <span className="ff-mono upper" style={{fontSize:9,color:'var(--ink-3)',letterSpacing:'.08em',textAlign:'right'}}>POP</span>
            </div>
            {rows.map(({r,d,pop}) => {
              const cell = (v, color) => {
                if (typeof v !== 'number') {
                  return <span className="ff-mono num" style={{fontSize:11,color:'var(--ink-4)',textAlign:'center'}}>—</span>;
                }
                const intensity = v / 100;
                return (
                  <div style={{position:'relative',textAlign:'center',padding:'4px 0'}}>
                    <div style={{position:'absolute',inset:'2px 4px',background:color,opacity:0.08 + intensity*0.45}}/>
                    <span className="ff-mono num" style={{fontSize:11,position:'relative',color:'var(--ink)',fontWeight:600}}>{v}</span>
                  </div>
                );
              };
              const isSelected = selected && selected.r.id === r.id;
              return (
                <div key={r.id}
                  onClick={()=>setSelectedId(r.id)}
                  style={{display:'grid',gridTemplateColumns:'1fr 56px 56px 56px 56px 64px',gap:0,
                    padding:'9px 14px',borderBottom:'1px solid var(--rule-soft)',alignItems:'center',cursor:'pointer',
                    background: isSelected ? 'var(--bg-2)' : 'transparent'}}>
                  <div style={{minWidth:0,display:'flex',alignItems:'center',gap:10}}>
                    <span style={{width:18,height:18,background:r.art,flexShrink:0}}/>
                    <div style={{minWidth:0}}>
                      <div style={{fontSize:12,fontWeight:600,whiteSpace:'nowrap',overflow:'hidden',textOverflow:'ellipsis'}}>{r.title}</div>
                      <div className="ff-mono" style={{fontSize:9,color:'var(--ink-3)',whiteSpace:'nowrap',overflow:'hidden',textOverflow:'ellipsis'}}>{r.artist}</div>
                    </div>
                  </div>
                  {cell(d.spotifyPopularity, '#1ed760')}
                  {cell(d.tidalPopularity,   '#000000')}
                  {cell(d.qobuzPopularity,   '#0070d8')}
                  {cell(d.appleMusicScore,   '#fa233b')}
                  <span className="ff-mono num" style={{fontSize:13,fontWeight:600,textAlign:'right'}}>{pop}</span>
                </div>
              );
            })}
          </div>
          <div className="ff-mono" style={{fontSize:10,color:'var(--ink-3)',marginTop:8}}>
            Click a row to load its 90-day trend →
          </div>
        </div>

        {/* 90-day trend chart for selected release */}
        <div>
          <div className="ff-mono upper" style={{fontSize:10,letterSpacing:'.1em',color:'var(--ink-3)',marginBottom:14}}>
            90-DAY POPULARITY TREND
          </div>
          {selected && series ? (
            <div style={{border:'1px solid var(--rule)',padding:'16px 18px'}}>
              <div style={{display:'flex',alignItems:'center',gap:12,marginBottom:14}}>
                <span style={{width:24,height:24,background:selected.r.art,flexShrink:0}}/>
                <div style={{minWidth:0,flex:1}}>
                  <div style={{fontSize:14,fontWeight:600}}>{selected.r.title}</div>
                  <div className="ff-mono" style={{fontSize:10,color:'var(--ink-3)'}}>{selected.r.artist} · {selected.r.date}</div>
                </div>
                <button onClick={()=>window.dispatchEvent(new CustomEvent('astro-open-release',{detail:{id:selected.r.id}}))}
                  className="ff-mono upper"
                  style={{fontSize:9,letterSpacing:'.08em',padding:'5px 10px',background:'transparent',
                    color:'var(--ink-2)',border:'1px solid var(--rule)',cursor:'pointer'}}>
                  OPEN →
                </button>
              </div>
              <PopularityTrendChart series={series}/>
              <div style={{display:'flex',gap:14,marginTop:12,flexWrap:'wrap',fontSize:10}} className="ff-mono">
                <span style={{display:'flex',gap:6,alignItems:'center'}}><span style={{width:10,height:2,background:'#1ed760'}}/> Spotify {selected.d.spotifyPopularity}</span>
                <span style={{display:'flex',gap:6,alignItems:'center'}}><span style={{width:10,height:2,background:'#000000'}}/> Tidal {selected.d.tidalPopularity}</span>
                <span style={{display:'flex',gap:6,alignItems:'center'}}><span style={{width:10,height:2,background:'#0070d8'}}/> Qobuz {selected.d.qobuzPopularity}</span>
                <span style={{display:'flex',gap:6,alignItems:'center'}}><span style={{width:10,height:2,background:'#fa233b'}}/> Apple {selected.d.appleMusicScore}</span>
              </div>
            </div>
          ) : (
            <div style={{padding:'40px 20px',border:'1px dashed var(--rule)',textAlign:'center',color:'var(--ink-3)',fontSize:12}}>
              Select a release to view its 90-day trend.
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

function PopularityTrendChart({ series }) {
  const W = 460, H = 160;
  const lines = [
    { k:'spotify', c:'#1ed760' },
    { k:'tidal',   c:'#000000' },
    { k:'qobuz',   c:'#0070d8' },
    { k:'apple',   c:'#fa233b' },
  ];
  const path = (arr) => {
    const n = arr.length;
    return arr.map((v,i) => {
      const x = (i/(n-1)) * (W-30) + 25;
      const y = H - 16 - (v/100) * (H-32);
      return `${i===0?'M':'L'}${x.toFixed(1)},${y.toFixed(1)}`;
    }).join(' ');
  };
  return (
    <svg width={W} height={H} style={{display:'block',width:'100%',height:'auto'}} viewBox={`0 0 ${W} ${H}`}>
      {/* gridlines */}
      {[0,25,50,75,100].map(p=>{
        const y = H - 16 - (p/100) * (H-32);
        return (
          <g key={p}>
            <line x1={25} x2={W-5} y1={y} y2={y} stroke="var(--rule-soft)" strokeWidth=".5"/>
            <text x={20} y={y+3} textAnchor="end" fontSize={8} fill="var(--ink-4)" fontFamily="IBM Plex Mono">{p}</text>
          </g>
        );
      })}
      {lines.map(L => series[L.k] && (
        <path key={L.k} d={path(series[L.k])} fill="none" stroke={L.c} strokeWidth="1.5"/>
      ))}
    </svg>
  );
}

function BigChart() {
  // Stacked bars over 26 weeks
  const W = 1100, H = 240, weeks = 26;
  const bw = W/weeks;
  const data = useM2(()=>{
    let s = 99;
    const r = ()=>{ s = (s*9301+49297)%233280; return s/233280; };
    return Array.from({length:weeks},()=>({
      sp: 0.4+r()*0.6,
      ap: 0.2+r()*0.4,
      yt: 0.15+r()*0.3,
      ot: 0.1+r()*0.2,
    }));
  },[]);
  return (
    <div style={{position:'relative'}}>
      <svg width={W} height={H} style={{display:'block',width:'100%',height:'auto'}} viewBox={`0 0 ${W} ${H}`}>
        {/* gridlines */}
        {[0,.25,.5,.75,1].map(p=>(
          <g key={p}>
            <line x1={0} x2={W} y1={H-p*H*.9} y2={H-p*H*.9} stroke="var(--rule-soft)" strokeWidth=".5"/>
            <text x={W-2} y={H-p*H*.9-3} textAnchor="end" fontSize={9} fill="var(--ink-4)" fontFamily="IBM Plex Mono">${Math.round(p*120)}K</text>
          </g>
        ))}
        {data.map((d,i)=>{
          const total = d.sp+d.ap+d.yt+d.ot;
          const max = 4;
          const colSum = (d.sp+d.ap+d.yt+d.ot);
          const top = H - (colSum/max)*H*.9;
          let y = H;
          const seg = (val, color) => {
            const h = (val/max)*H*.9;
            y -= h;
            return <rect key={color} x={i*bw + bw*0.15} y={y} width={bw*0.7} height={h} fill={color}/>;
          };
          return (
            <g key={i}>
              {seg(d.ot, 'var(--ink-4)')}
              {seg(d.yt, '#ff0000')}
              {seg(d.ap, '#fa243c')}
              {seg(d.sp, '#1ed760')}
            </g>
          );
        })}
      </svg>
      <div style={{display:'flex',gap:18,marginTop:14,fontSize:11}} className="ff-mono">
        <span style={{display:'flex',gap:6,alignItems:'center'}}><span style={{width:10,height:10,background:'#1ed760'}}/> Spotify</span>
        <span style={{display:'flex',gap:6,alignItems:'center'}}><span style={{width:10,height:10,background:'#fa243c'}}/> Apple Music</span>
        <span style={{display:'flex',gap:6,alignItems:'center'}}><span style={{width:10,height:10,background:'#ff0000'}}/> YouTube</span>
        <span style={{display:'flex',gap:6,alignItems:'center'}}><span style={{width:10,height:10,background:'var(--ink-4)'}}/> Other DSPs</span>
      </div>
    </div>
  );
}

// Normalize aliases from any legacy shape into [{value, type}].
// Accepts: string ("Solange, Saint Heron"), string[] (['Solange']), or modern [{value,type}].
function normalizeAliases(raw) {
  if (!raw) return [];
  if (Array.isArray(raw)) {
    return raw.map(x => {
      if (typeof x === 'string') return { value: x.trim(), type: 'artist' };
      if (x && typeof x === 'object' && x.value) return { value: String(x.value).trim(), type: x.type || 'artist' };
      return null;
    }).filter(x => x && x.value);
  }
  if (typeof raw === 'string') {
    return raw.split(/[,·]/).map(s => s.trim()).filter(Boolean).map(v => ({ value: v, type: 'artist' }));
  }
  return [];
}

// Alias types — kept as a hardcoded list because the prototype uses short codes
// (legal, artist, nickname, …) while ref.alias_types uses underscored codes
// (legal_name, stage_name, …). Reconciling would require a data migration on
// every stored alias. Deferred — schema unification ticket.
const ALIAS_TYPES = [
  { v:'legal',          l:'Legal name',     hint:'Legal/birth name as it appears on contracts.' },
  { v:'artist',         l:'Artist name',    hint:'Stage or performing name.' },
  { v:'nickname',       l:'Nickname',       hint:'Informal name, fan-known shorthand.' },
  { v:'previous',       l:'Previous name',  hint:'Former legal or artist name no longer in use.' },
  { v:'misspelling',    l:'Misspelling',    hint:'Common incorrect spelling — captured for matching.' },
  { v:'search-variant', l:'Search variant', hint:'Romanization, transliteration, or punctuation variant.' },
];
const ALIAS_TYPE_TONE = { legal:'ok', artist:'accent', nickname:'neutral', previous:'neutral', misspelling:'danger', 'search-variant':'neutral' };
const aliasTypeLabel = (t) => (ALIAS_TYPES.find(x=>x.v===t) || ALIAS_TYPES[1]).l;

// AliasesEditor — list editor for typed aliases. In read mode renders chip rows
// grouped/labelled by type. In edit mode renders one row per alias with a
// type select + value input + remove + an add-row control at the bottom.
function AliasesEditor({ value, onChange, editing }) {
  const list = normalizeAliases(value);
  if (!editing) {
    if (list.length === 0) return <span style={{color:'var(--ink-3)'}}>—</span>;
    return (
      <div style={{display:'flex',flexDirection:'column',gap:6}}>
        {list.map((a,i)=>(
          <div key={i} style={{display:'flex',alignItems:'baseline',gap:10}}>
            <span className="ff-mono upper" style={{fontSize:8,letterSpacing:'.1em',color:'var(--ink-3)',minWidth:90}}>{aliasTypeLabel(a.type)}</span>
            <span style={{fontSize:13,color:'var(--ink)',fontWeight: a.type==='misspelling' ? 400 : 500, fontStyle: a.type==='misspelling' ? 'italic' : 'normal', textDecoration: a.type==='previous' ? 'line-through' : 'none', textDecorationColor:'var(--ink-3)'}}>
              {a.value}
            </span>
          </div>
        ))}
      </div>
    );
  }
  const update = (next) => onChange(next);
  return (
    <div style={{display:'flex',flexDirection:'column',gap:6}}>
      {list.map((a,i)=>(
        <div key={i} style={{display:'grid',gridTemplateColumns:'130px 1fr 26px',gap:6,alignItems:'center'}}>
          <select value={a.type} onChange={e=>{ const next=[...list]; next[i]={...a,type:e.target.value}; update(next); }}
            className="ff-mono" style={{height:26,fontSize:11,padding:'0 4px',background:'var(--bg)',border:'1px solid var(--rule)',color:'var(--ink)'}}>
            {ALIAS_TYPES.map(t => <option key={t.v} value={t.v}>{t.l}</option>)}
          </select>
          <input value={a.value} onChange={e=>{ const next=[...list]; next[i]={...a,value:e.target.value}; update(next); }}
            placeholder="alias value" style={{height:26,fontSize:12,padding:'0 6px',background:'var(--bg)',border:'1px solid var(--rule)',color:'var(--ink)',boxSizing:'border-box'}}/>
          <button onClick={()=>{ const next=list.filter((_,j)=>j!==i); update(next); }} aria-label="Remove alias"
            style={{height:26,width:26,border:'1px solid var(--rule)',background:'var(--bg)',cursor:'pointer'}}>×</button>
        </div>
      ))}
      <button onClick={()=>update([...list, {value:'', type:'artist'}])}
        className="ff-mono upper" style={{alignSelf:'flex-start',fontSize:10,letterSpacing:'.08em',color:'var(--ink-2)',background:'transparent',border:'1px dashed var(--rule)',padding:'4px 10px',cursor:'pointer'}}>
        + ADD ALIAS
      </button>
    </div>
  );
}

// SocietyPickerInline — typeahead against the global SOCIETIES directory.
// Stores the acronym in `org` and surfaces the matched society's `kind`
// so the parent row can derive KIND automatically. Free text is allowed
// (so a non-CISAC body can still be entered manually).
function SocietyPickerInline({ value, onPick, onChange, kinds }) {
  const [open, setOpen] = React.useState(false);
  const [hover, setHover] = React.useState(0);
  const ref = React.useRef(null);
  const ql = (value||'').trim().toLowerCase();
  const all = (typeof SOCIETIES !== 'undefined' ? SOCIETIES : []);
  const matches = React.useMemo(() => {
    let pool = kinds && kinds.length ? all.filter(s => kinds.includes(s.kind)) : all;
    if (!ql) return pool.slice(0, 10);
    return pool.filter(s => {
      const blob = `${s.acronym} ${s.name} ${s.territory||''} ${s.country||''}`.toLowerCase();
      return blob.includes(ql);
    }).slice(0, 10);
  }, [ql, all, kinds]);
  React.useEffect(() => {
    if (!open) return;
    const onDoc = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener('mousedown', onDoc);
    return () => document.removeEventListener('mousedown', onDoc);
  }, [open]);
  const linked = value && all.find(s => s.acronym.toLowerCase() === value.trim().toLowerCase());
  return (
    <div ref={ref} style={{position:'relative'}}>
      <input
        value={value || ''}
        onChange={e=>{ onChange(e.target.value); setOpen(true); setHover(0); }}
        onFocus={()=>setOpen(true)}
        onKeyDown={e=>{
          if (!open) return;
          if (e.key === 'ArrowDown') { e.preventDefault(); setHover(h => Math.min(h+1, matches.length-1)); }
          else if (e.key === 'ArrowUp') { e.preventDefault(); setHover(h => Math.max(h-1, 0)); }
          else if (e.key === 'Enter' && matches[hover]) { e.preventDefault(); onPick(matches[hover]); setOpen(false); }
          else if (e.key === 'Escape') setOpen(false);
        }}
        placeholder="Search…"
        className="ff-mono upper"
        style={{height:26,width:'100%',fontSize:11,letterSpacing:'.04em',padding:'0 6px',background:'var(--bg)',border:'1px solid var(--rule)',color:'var(--ink)',boxSizing:'border-box'}}
      />
      {open && (
        <div style={{position:'absolute',top:'calc(100% + 2px)',left:0,minWidth:'100%',width:'max-content',maxWidth:360,zIndex:50,background:'var(--bg)',border:'1px solid var(--rule)',boxShadow:'0 8px 24px rgba(0,0,0,.08)',maxHeight:260,overflowY:'auto'}}>
          {matches.length === 0 ? (
            <div className="ff-mono" style={{padding:'10px 12px',fontSize:11,color:'var(--ink-3)'}}>No societies match — saved as plain text.</div>
          ) : matches.map((s,i)=>(
            <div key={s.acronym} onMouseDown={e=>{ e.preventDefault(); onPick(s); setOpen(false); }}
              onMouseEnter={()=>setHover(i)}
              style={{display:'grid',gridTemplateColumns:'auto 1fr auto',gap:10,alignItems:'center',padding:'7px 12px',cursor:'pointer',background: hover===i ? 'var(--bg-2)' : 'transparent',borderBottom: i<matches.length-1 ? '1px solid var(--rule-soft)' : '0'}}>
              <span className="ff-mono upper" style={{fontSize:10,fontWeight:600,letterSpacing:'.06em',minWidth:54}}>{s.acronym}</span>
              <span style={{fontSize:11,color:'var(--ink-2)',whiteSpace:'nowrap',overflow:'hidden',textOverflow:'ellipsis'}}>{s.name}</span>
              <span className="ff-mono upper" style={{fontSize:9,letterSpacing:'.1em',color:'var(--ink-3)'}}>{s.kind} · {s.country}</span>
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

// ───────────────────────────────────────────────────────────── PUBLIC PAGE
// MemberNameSearch — autocomplete against the ARTISTS directory.
// Stores both `name` (free text) and `aid` (linked profile id) so a member
// can either link to a real profile or remain a string-only credit.
function MemberNameSearch({ value, aid, currentId, onPick, onChange }) {
  const [open, setOpen] = React.useState(false);
  const [hover, setHover] = React.useState(0);
  const ref = React.useRef(null);
  const ql = (value||'').trim().toLowerCase();
  const all = (typeof ARTISTS !== 'undefined' ? ARTISTS : []);
  // Match Person profiles only (filtering out the group itself if currentId given).
  const matches = React.useMemo(() => {
    const pool = all.filter(a => (a.type||'Person') === 'Person' && a.id !== currentId);
    if (!ql) return pool.slice(0, 8);
    return pool.filter(a => {
      const blob = `${a.name} ${a.realName||''} ${a.ipi||''}`.toLowerCase();
      return blob.includes(ql);
    }).slice(0, 8);
  }, [ql, all, currentId]);
  React.useEffect(() => {
    if (!open) return;
    const onDoc = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener('mousedown', onDoc);
    return () => document.removeEventListener('mousedown', onDoc);
  }, [open]);
  const linked = aid && all.find(a => a.id === aid);
  return (
    <div ref={ref} style={{position:'relative'}}>
      <input
        value={value || ''}
        onChange={e=>{ onChange(e.target.value); setOpen(true); setHover(0); if (aid) onPick(null, e.target.value); }}
        onFocus={()=>setOpen(true)}
        onKeyDown={e=>{
          if (!open) return;
          if (e.key === 'ArrowDown') { e.preventDefault(); setHover(h => Math.min(h+1, matches.length-1)); }
          else if (e.key === 'ArrowUp') { e.preventDefault(); setHover(h => Math.max(h-1, 0)); }
          else if (e.key === 'Enter' && matches[hover]) { e.preventDefault(); onPick(matches[hover].id, matches[hover].name); setOpen(false); }
          else if (e.key === 'Escape') setOpen(false);
        }}
        placeholder="Search profiles…"
        style={{height:26,width:'100%',fontSize:12,padding:'0 6px',background:'var(--bg)',border:'1px solid var(--rule)',color:'var(--ink)',boxSizing:'border-box'}}
      />
      {/* Linked indicator */}
      {linked && (
        <div className="ff-mono upper" style={{position:'absolute',right:6,top:'50%',transform:'translateY(-50%)',fontSize:8,letterSpacing:'.1em',color:'var(--accent)',pointerEvents:'none',background:'var(--bg)',padding:'0 4px'}}>● LINKED</div>
      )}
      {open && (
        <div style={{position:'absolute',top:'calc(100% + 2px)',left:0,right:0,zIndex:50,background:'var(--bg)',border:'1px solid var(--rule)',boxShadow:'0 8px 24px rgba(0,0,0,.08)',maxHeight:240,overflowY:'auto'}}>
          {matches.length === 0 ? (
            <div className="ff-mono" style={{padding:'10px 12px',fontSize:11,color:'var(--ink-3)'}}>No profiles match — saved as plain text.</div>
          ) : matches.map((a,i)=>(
            <div key={a.id} onMouseDown={e=>{ e.preventDefault(); onPick(a.id, a.name); setOpen(false); }}
              onMouseEnter={()=>setHover(i)}
              style={{display:'grid',gridTemplateColumns:'1fr auto',gap:8,alignItems:'center',padding:'7px 12px',cursor:'pointer',background: hover===i ? 'var(--bg-2)' : 'transparent',borderBottom: i<matches.length-1 ? '1px solid var(--rule-soft)' : '0'}}>
              <div>
                <div style={{fontSize:12,fontWeight:500}}>{a.name}</div>
                {a.realName && a.realName !== a.name && (
                  <div className="ff-mono" style={{fontSize:9,color:'var(--ink-3)',marginTop:1}}>{a.realName}</div>
                )}
              </div>
              <div className="ff-mono upper" style={{fontSize:9,letterSpacing:'.1em',color:'var(--ink-3)'}}>{a.pro} · {a.country}</div>
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

// ─── Profile section helpers: DSP / metadata / social cells ─────────────
// Compact, monospaced cells used by ScreenPublic sections 09–11. Each cell
// shows a small color-coded "chip" with the platform name, the linked id /
// handle (or "—" when missing), an optional metric stack, and an "open ↗"
// link when a url is supplied. In edit mode the id becomes an input.

function fmtCount(n) {
  if (n == null || n === '') return '—';
  if (typeof n !== 'number') return String(n);
  if (n >= 1e9) return (n/1e9).toFixed(1).replace(/\.0$/,'')+'B';
  if (n >= 1e6) return (n/1e6).toFixed(1).replace(/\.0$/,'')+'M';
  if (n >= 1e3) return (n/1e3).toFixed(1).replace(/\.0$/,'')+'K';
  return String(n);
}

function DspChip({ name, color }) {
  return (
    <div style={{display:'inline-flex',alignItems:'center',gap:8,marginBottom:8}}>
      <span style={{width:10,height:10,background:color||'var(--ink)',display:'inline-block',borderRadius:2,flexShrink:0}}/>
      <span className="ff-mono upper" style={{fontSize:10,fontWeight:600,letterSpacing:'.08em'}}>{name}</span>
    </div>
  );
}

function DspRow({ name, color, id, url, metrics=[], extras=[], editing, onIdChange }) {
  return (
    <div style={{padding:'18px',borderTop:'1px solid var(--rule)',display:'grid',gridTemplateColumns:'minmax(220px,1.1fr) 2fr',gap:24,alignItems:'start'}}>
      <div>
        <DspChip name={name} color={color}/>
        {editing ? (
          <input value={id||''} onChange={e=>onIdChange&&onIdChange(e.target.value)} placeholder="id"
            className="ff-mono" style={{width:'100%',height:30,padding:'0 8px',fontSize:12,background:'var(--bg)',border:'1px solid var(--rule)',color:'var(--ink)',boxSizing:'border-box'}}/>
        ) : (
          <div className="ff-mono" style={{fontSize:12,wordBreak:'break-all',color:id?'var(--ink)':'var(--ink-4)'}}>{id||'—'}</div>
        )}
        {url && !editing && <a href={url} target="_blank" rel="noopener noreferrer" className="ff-mono upper" style={{fontSize:10,letterSpacing:'.08em',color:'var(--ink-3)',marginTop:6,display:'inline-block'}}>OPEN ↗</a>}
      </div>
      <div style={{display:'grid',gridTemplateColumns:`repeat(${Math.max(metrics.length+extras.length,1)},1fr)`,gap:18}}>
        {metrics.map((m,i)=>(
          <div key={'m'+i}>
            <div className="ff-mono upper" style={{fontSize:9,letterSpacing:'.1em',color:'var(--ink-3)',marginBottom:4}}>{m.l}</div>
            <div className="ff-display num" style={{fontSize:24,fontWeight:600,letterSpacing:'-0.03em',lineHeight:1}}>{fmtCount(m.v)}{m.suffix||''}</div>
          </div>
        ))}
        {extras.map((m,i)=>(
          <div key={'x'+i}>
            <div className="ff-mono upper" style={{fontSize:9,letterSpacing:'.1em',color:'var(--ink-3)',marginBottom:4}}>{m.l}</div>
            <div className="ff-mono" style={{fontSize:12,color:'var(--ink-2)'}}>{m.v}</div>
          </div>
        ))}
      </div>
    </div>
  );
}

function DspCell({ name, color, id, url, editing, onIdChange, metrics=[], borderRight, idKey, idAsUrl }) {
  return (
    <div style={{padding:'14px 18px',borderRight:borderRight?'1px solid var(--rule)':'none'}}>
      <DspChip name={name} color={color}/>
      {editing ? (
        <input value={id||''} onChange={e=>onIdChange&&onIdChange(e.target.value)} placeholder={idAsUrl?'url':'id'}
          className="ff-mono" style={{width:'100%',height:28,padding:'0 8px',fontSize:11,background:'var(--bg)',border:'1px solid var(--rule)',color:'var(--ink)',boxSizing:'border-box'}}/>
      ) : (
        <div className="ff-mono" style={{fontSize:11,wordBreak:'break-all',color:id?'var(--ink-2)':'var(--ink-4)'}}>{id||'—'}</div>
      )}
      {metrics.length>0 && !editing && (
        <div style={{display:'flex',gap:14,marginTop:8,flexWrap:'wrap'}}>
          {metrics.map((m,i)=>(
            <div key={i}>
              <div className="ff-mono upper" style={{fontSize:8,letterSpacing:'.1em',color:'var(--ink-3)'}}>{m.l}</div>
              <div className="ff-mono num" style={{fontSize:13,fontWeight:600,marginTop:2}}>{fmtCount(m.v)}</div>
            </div>
          ))}
        </div>
      )}
      {url && !editing && <a href={url} target="_blank" rel="noopener noreferrer" className="ff-mono upper" style={{fontSize:9,letterSpacing:'.08em',color:'var(--ink-3)',marginTop:6,display:'inline-block'}}>OPEN ↗</a>}
    </div>
  );
}

function MetaCell({ name, id, url, synced, extra, editing, onIdChange, borderRight, idKey, idAsUrl }) {
  return (
    <div style={{padding:'14px 18px',borderRight:borderRight?'1px solid var(--rule)':'none'}}>
      <div className="ff-mono upper" style={{fontSize:9,letterSpacing:'.1em',color:'var(--ink-3)',marginBottom:6}}>{name}</div>
      {editing ? (
        <input value={id||''} onChange={e=>onIdChange&&onIdChange(e.target.value)} placeholder={idAsUrl?'url':'id'}
          className="ff-mono" style={{width:'100%',height:28,padding:'0 8px',fontSize:11,background:'var(--bg)',border:'1px solid var(--rule)',color:'var(--ink)',boxSizing:'border-box'}}/>
      ) : (
        <div className="ff-mono" style={{fontSize:12,wordBreak:'break-all',color:id?'var(--ink)':'var(--ink-4)'}}>{id||'—'}</div>
      )}
      {extra && !editing && <div className="ff-mono" style={{fontSize:10,color:'var(--ink-3)',marginTop:4}}>{extra}</div>}
      <div style={{display:'flex',gap:10,marginTop:6,alignItems:'center'}}>
        {url && !editing && <a href={url} target="_blank" rel="noopener noreferrer" className="ff-mono upper" style={{fontSize:9,letterSpacing:'.08em',color:'var(--ink-3)'}}>OPEN ↗</a>}
        {synced && <span className="ff-mono" style={{fontSize:9,color:'var(--ink-4)'}}>synced {formatDate ? formatDate(synced) : synced}</span>}
      </div>
    </div>
  );
}

function SocialCell({ name, handle, followers, verified, url, editing, onChange, borderRight, idAsUrl }) {
  return (
    <div style={{padding:'14px 18px',borderRight:borderRight?'1px solid var(--rule)':'none'}}>
      <div style={{display:'flex',justifyContent:'space-between',alignItems:'baseline',marginBottom:6}}>
        <span className="ff-mono upper" style={{fontSize:10,fontWeight:600,letterSpacing:'.08em'}}>{name}</span>
        {verified && <Pill tone="ok">✓</Pill>}
      </div>
      {editing ? (
        <input value={handle||''} onChange={e=>onChange&&onChange(e.target.value)} placeholder={idAsUrl?'url':'@handle'}
          className="ff-mono" style={{width:'100%',height:28,padding:'0 8px',fontSize:11,background:'var(--bg)',border:'1px solid var(--rule)',color:'var(--ink)',boxSizing:'border-box'}}/>
      ) : (
        <div className="ff-mono" style={{fontSize:12,wordBreak:'break-all',color:handle?'var(--ink)':'var(--ink-4)'}}>{handle?(idAsUrl?handle:'@'+handle):'—'}</div>
      )}
      {followers != null && !editing && (
        <div className="ff-mono num" style={{fontSize:13,fontWeight:600,marginTop:6}}>{fmtCount(followers)} <span className="ff-mono" style={{fontSize:9,color:'var(--ink-3)',fontWeight:400}}>FOLLOWERS</span></div>
      )}
      {url && !editing && <a href={url} target="_blank" rel="noopener noreferrer" className="ff-mono upper" style={{fontSize:9,letterSpacing:'.08em',color:'var(--ink-3)',marginTop:4,display:'inline-block'}}>OPEN ↗</a>}
    </div>
  );
}

function ScreenPublic({ go, profile }) {
  const baseP = profile || ARTISTS[5]; // KAYTRANADA default
  // Pull expanded data from ARTIST_DETAILS and merge over the base record.
  // Edit-mode draft is held locally; Save merges back into both maps.
  const detail = (typeof getArtistDetail === 'function' ? getArtistDetail(baseP.id) : (window.ARTIST_DETAILS||{})[baseP.id]) || {};
  const [editing, setEditing] = useS2(false);
  const [draft, setDraft] = useS2({ ...baseP, ...detail });
  useE2(() => { setDraft({ ...baseP, ...detail }); setEditing(false); }, [baseP.id]);

  const p = editing ? draft : { ...baseP, ...detail };
  const set = (k, v) => setDraft(d => ({ ...d, [k]: v }));
  const save = () => {
    // Split keys back into the legacy ARTISTS row vs ARTIST_DETAILS.
    const legacyKeys = ['name','ipi','pro','type','country','roster','claims','share','color','legal','realName'];
    const idx = (window.ARTISTS||[]).findIndex(a => a.id === baseP.id);
    if (idx >= 0) {
      const legacy = {};
      legacyKeys.forEach(k => { if (k in draft) legacy[k] = draft[k]; });
      window.ARTISTS[idx] = { ...window.ARTISTS[idx], ...legacy };
    }
    const det = { ...draft };
    legacyKeys.forEach(k => delete det[k]); delete det.id;
    window.ARTIST_DETAILS = window.ARTIST_DETAILS || {};
    window.ARTIST_DETAILS[baseP.id] = { ...(window.ARTIST_DETAILS[baseP.id] || {}), ...det };
    setEditing(false);
    if (typeof window.dispatchEvent === 'function') {
      window.dispatchEvent(new CustomEvent('astro-toast',{detail:{msg:`Profile saved · ${draft.name}`, tone:'ok'}}));
    }
  };
  const cancel = () => { setDraft({ ...baseP, ...detail }); setEditing(false); };

  // Field renderer — switches between display value and an inline input in Edit mode.
  // Compact, monospaced label-over-value pattern matching the rest of the app.
  const F = ({ label, k, value, mono=true, multiline=false, kind='text' }) => {
    const v = value !== undefined ? value : (p[k] ?? '');
    // Auto-detect date fields by key name OR explicit kind='date'
    const isDate = kind === 'date' || /^(dob|formed|since|joined|left|created|createdAt|updatedAt|lastVerified|lastVerifiedAt|expires|expiry|taxFormExpiry|effectiveDate|registrationDate|firstPubDate|firstReleaseDate|recordingDate|copyrightDate|stdIdDate|startDate|endDate|publisherStart|publisherEnd)$/i.test(k||'');
    // Auto-detect contact-info fields by key name OR explicit kind
    const inferred = kind === 'text'
      ? (k === 'email' ? 'email'
        : k === 'phone' ? 'phone'
        : k === 'website' ? 'website'
        : (k === 'country' || k === 'nationality' || k === 'treatyCountry') ? 'country'
        : 'text')
      : kind;
    const display = isDate ? (formatDate(v) || '—') : (v || '—');
    const ctrlStyle = {width:'100%',height:30,padding:'0 8px',fontSize:12,background:'var(--bg)',border:'1px solid var(--rule)',color:'var(--ink)',boxSizing:'border-box'};
    return (
      <div>
        <div className="ff-mono upper" style={{fontSize:9,letterSpacing:'.1em',color:'var(--ink-3)',marginBottom:4}}>{label}</div>
        {editing ? (
          multiline ? (
            <textarea value={p[k]||''} onChange={e=>set(k,e.target.value)} className={mono?'ff-mono':''}
              style={{width:'100%',minHeight:64,padding:'6px 8px',fontSize:12,background:'var(--bg)',border:'1px solid var(--rule)',color:'var(--ink)',resize:'vertical',boxSizing:'border-box',fontFamily:'inherit'}}/>
          ) : isDate ? (
            <DateField value={p[k]||''} editing={true} onChange={v=>set(k,v)} style={{width:'100%'}}/>
          ) : inferred === 'email' ? (
            <window.EmailField value={p[k]||''} onChange={v=>set(k,v)} style={ctrlStyle} className={mono?'ff-mono':''}/>
          ) : inferred === 'phone' ? (
            <window.PhoneField value={p[k]||''} onChange={v=>set(k,v)} style={ctrlStyle} defaultCountry={p.country || 'US'}/>
          ) : inferred === 'website' ? (
            <window.WebsiteField value={p[k]||''} onChange={v=>set(k,v)} style={ctrlStyle} className={mono?'ff-mono':''}/>
          ) : inferred === 'country' ? (
            <window.CountrySelect value={p[k]||''} onChange={v=>set(k,v)}
              style={{...ctrlStyle, padding:'0 24px 0 8px', appearance:'none', WebkitAppearance:'none'}}
              className={mono?'ff-mono':''}/>
          ) : (
            <input type={kind} value={p[k]||''} onChange={e=>set(k,e.target.value)} className={mono?'ff-mono':''}
              style={ctrlStyle}/>
          )
        ) : (
          <div className={mono?'ff-mono':''} style={{fontSize:13,color:'var(--ink)',wordBreak:'break-word'}}>{display}</div>
        )}
      </div>
    );
  };

  return (
    <div>
      <div style={{display:'flex',justifyContent:'space-between',alignItems:'center',marginBottom:18,gap:12}}>
        <button onClick={()=>go('catalog')} className="ff-mono upper" style={{fontSize:10,color:'var(--ink-3)',display:'inline-flex',alignItems:'center',gap:6}}>
          <Ic.Left width={12} height={12}/> Public catalog
        </button>
        <div style={{display:'flex',gap:8,alignItems:'center'}}>
          {editing ? (
            <>
              <button onClick={cancel} className="ff-mono upper" style={{padding:'6px 12px',fontSize:10,letterSpacing:'.08em',background:'transparent',color:'var(--ink)',border:'1px solid var(--rule)',cursor:'pointer'}}>DISCARD</button>
              <button onClick={save} className="ff-mono upper" style={{padding:'6px 12px',fontSize:10,letterSpacing:'.08em',background:'var(--ink)',color:'var(--bg)',border:0,cursor:'pointer',fontWeight:600}}>SAVE PROFILE</button>
            </>
          ) : (
            <>
              <Pill tone={p.legal?'accent':'warn'} dot>{p.legal?'VERIFIED ✓ ASTRO':'UNVERIFIED'}</Pill>
              <button onClick={()=>setEditing(true)} className="ff-mono upper" style={{padding:'6px 12px',fontSize:10,letterSpacing:'.08em',background:'transparent',color:'var(--ink)',border:'1px solid var(--rule)',cursor:'pointer'}}>EDIT PROFILE</button>
            </>
          )}
        </div>
      </div>

      {/* Hero — album-cover/poster styling */}
      <div style={{display:'grid',gridTemplateColumns:'1fr 1fr',gap:0,borderTop:'2px solid var(--rule)',borderBottom:'1px solid var(--rule)'}}>
        <div style={{aspectRatio:'1',background:p.color,position:'relative',overflow:'hidden',borderRight:'1px solid var(--rule)'}}>
          <div style={{position:'absolute',inset:0,display:'flex',alignItems:'center',justifyContent:'center'}}>
            <span className="ff-display" style={{fontSize:240,fontWeight:800,color:'rgba(0,0,0,.18)',letterSpacing:'-0.08em'}}>
              {p.name.split(' ').map(n=>n[0]).slice(0,2).join('')}
            </span>
          </div>
          <div style={{position:'absolute',top:18,left:20,right:20,display:'flex',justifyContent:'space-between',color:'rgba(0,0,0,.7)'}}>
            <span className="ff-mono upper" style={{fontSize:11,fontWeight:600}}>ASTRO PROFILE · {p.id.toUpperCase()}</span>
            <span className="ff-mono num" style={{fontSize:11,fontWeight:600}}>IPI {(window.ipiDisplayCompact ? window.ipiDisplayCompact(p.ipi) : p.ipi)}</span>
          </div>
          <div style={{position:'absolute',bottom:20,left:20,right:20,color:'rgba(0,0,0,.7)'}}>
            <Waveform w={460} h={40} bars={70} seed={p.id.charCodeAt(2)*11} color="rgba(0,0,0,.5)"/>
          </div>
        </div>
        <div style={{padding:'40px',display:'flex',flexDirection:'column',justifyContent:'space-between'}}>
          <div>
            <div className="ff-mono upper" style={{fontSize:11,color:'var(--ink-3)',marginBottom:14}}>{p.type} · {p.country} · Affiliated <SocietyLink code={p.pro} style={{fontSize:11,color:'var(--ink-2)'}}/></div>
            <h1 className="heading-swap ff-display" style={{fontSize:'clamp(48px,7vw,108px)',fontWeight:700,letterSpacing:'-0.05em',lineHeight:.86,margin:0}}>
              {p.name}
            </h1>
            <p style={{fontSize:15,color:'var(--ink-2)',marginTop:18,maxWidth:480,lineHeight:1.5}}>
              {p.roster} works on the public registry. Active across {Math.min(8, p.roster)} territories.
              Last update <b>4d ago</b>. Verified by ASTRO based on PRO records and IPI Common Information System.
            </p>
          </div>
          <div style={{display:'flex',gap:8,flexWrap:'wrap',marginTop:24}}>
            <Btn variant="primary" icon={<Ic.Star/>} onClick={() => window.toast && window.toast('Profile claim flow — verify identity to take ownership of this public page', 'soft')}>Claim profile</Btn>
            <Btn variant="secondary" icon={<Ic.Spotify/>} onClick={() => window.toast && window.toast('Open in Spotify (links out)', 'soft')}>Spotify</Btn>
            <Btn variant="secondary" icon={<Ic.Apple/>} onClick={() => window.toast && window.toast('Open in Apple Music (links out)', 'soft')}>Apple</Btn>
            <Btn variant="secondary" icon={<Ic.Youtube/>} onClick={() => window.toast && window.toast('Open in YouTube (links out)', 'soft')}>YouTube</Btn>
          </div>
        </div>
      </div>

      {/* Stats strip */}
      <div style={{display:'grid',gridTemplateColumns:'repeat(5,1fr)',borderBottom:'1px solid var(--rule)'}}>
        {[
          {l:'WORKS',v:p.roster,sub:'as writer'},
          {l:'RECORDINGS',v:Math.round(p.roster*1.8),sub:'as performer'},
          {l:'RELEASES',v:Math.round(p.roster*0.4),sub:'in catalog'},
          {l:'CLAIMS',v:p.claims,sub:p.claims?'open':'no conflicts'},
          {l:'OWNERSHIP',v:p.share+'%',sub:'avg control'},
        ].map((s,i,arr)=>(
          <div key={i} style={{padding:'18px',borderRight:i<arr.length-1?'1px solid var(--rule)':'none'}}>
            <div className="ff-mono upper" style={{fontSize:10,fontWeight:600,letterSpacing:'.08em',marginBottom:8,color:'var(--ink-3)'}}>{s.l}</div>
            <div className="ff-display num" style={{fontSize:32,fontWeight:600,letterSpacing:'-0.04em',lineHeight:1}}>{s.v}</div>
            <div className="ff-mono" style={{fontSize:10,color:'var(--ink-3)',marginTop:6}}>{s.sub}</div>
          </div>
        ))}
      </div>

      {/* Works list */}
      <div style={{marginTop:32}}>
        <Section num="01" action={<a className="ff-mono upper" style={{fontSize:10,cursor:'pointer'}}>All →</a>}>Works · {p.roster}</Section>
        <div style={{borderTop:'1px solid var(--rule)'}}>
          {WORKS.slice(0,6).map((w,i)=>(
            <div key={w.id} onClick={()=>go('work',w)} style={{display:'grid',gridTemplateColumns:'30px 1fr 200px 100px 80px',gap:14,padding:'14px 0',borderBottom:'1px solid var(--rule-soft)',alignItems:'center',cursor:'pointer'}}>
              <span className="ff-display num" style={{fontSize:14,color:'var(--ink-4)'}}>{String(i+1).padStart(2,'0')}</span>
              <div>
                <div style={{fontSize:15,fontWeight:600,letterSpacing:'-0.01em'}}>{w.title}</div>
                <div className="ff-mono" style={{fontSize:10,color:'var(--ink-3)',marginTop:2}}>{window.iswcDisplay ? window.iswcDisplay(w.iswc) : w.iswc}</div>
              </div>
              <Spark data={sparkPlays.slice(i*2,i*2+18)} w={180} h={26} stroke="var(--ink-2)"/>
              <span className="ff-mono num" style={{fontSize:13,textAlign:'right'}}>{(w.plays/1e6).toFixed(1)}M</span>
              <Pill tone={w.status==='registered'?'ok':w.status==='conflict'?'danger':'accent'}>{w.status}</Pill>
            </div>
          ))}
        </div>
      </div>

      {/* 02 · IDENTITY & LEGAL ─────────────────────────────────────────── */}
      <div style={{marginTop:32}}>
        <Section num="02">Identity & legal</Section>
        <div style={{display:'grid',gridTemplateColumns:'repeat(4,1fr)',gap:18,padding:'18px 0',borderTop:'1px solid var(--rule)',borderBottom:'1px solid var(--rule)'}}>
          {/* LEGAL NAME — three parts in edit mode, joined display when read-only.
              Stored as separate firstName/middleName/lastName plus a denormalized
              realName for back-compat with code that reads p.realName. */}
          {editing && p.type !== 'Group' ? (<>
            <div>
              <div className="ff-mono upper" style={{fontSize:9,letterSpacing:'.1em',color:'var(--ink-3)',marginBottom:4}}>FIRST NAME</div>
              <input value={p.firstName||''} onChange={e=>{ const v=e.target.value; set('firstName',v); set('realName',[v,p.middleName,p.lastName].filter(Boolean).join(' ').trim()); }}
                style={{width:'100%',height:30,padding:'0 8px',fontSize:12,background:'var(--bg)',border:'1px solid var(--rule)',color:'var(--ink)',boxSizing:'border-box'}}/>
            </div>
            <div>
              <div className="ff-mono upper" style={{fontSize:9,letterSpacing:'.1em',color:'var(--ink-3)',marginBottom:4}}>MIDDLE NAME(S)</div>
              <input value={p.middleName||''} onChange={e=>{ const v=e.target.value; set('middleName',v); set('realName',[p.firstName,v,p.lastName].filter(Boolean).join(' ').trim()); }}
                style={{width:'100%',height:30,padding:'0 8px',fontSize:12,background:'var(--bg)',border:'1px solid var(--rule)',color:'var(--ink)',boxSizing:'border-box'}}/>
            </div>
            <div>
              <div className="ff-mono upper" style={{fontSize:9,letterSpacing:'.1em',color:'var(--ink-3)',marginBottom:4}}>LAST NAME</div>
              <input value={p.lastName||''} onChange={e=>{ const v=e.target.value; set('lastName',v); set('realName',[p.firstName,p.middleName,v].filter(Boolean).join(' ').trim()); }}
                style={{width:'100%',height:30,padding:'0 8px',fontSize:12,background:'var(--bg)',border:'1px solid var(--rule)',color:'var(--ink)',boxSizing:'border-box'}}/>
            </div>
          </>) : (
            <F label="LEGAL NAME" k="realName" value={p.realName || [p.firstName,p.middleName,p.lastName].filter(Boolean).join(' ') || undefined} mono={false}/>
          )}
          <F label={p.type==='Group'?'FORMED':'DATE OF BIRTH'} k={p.type==='Group'?'formed':'dob'}/>
          <F label="NATIONALITY"  k="nationality"/>
          <F label="COUNTRY OF RESIDENCE" k="country"/>
          <F label="ENTITY TYPE"  k="type"/>
          <F label="VERIFIED"     value={p.legal?'Yes · ASTRO':'Pending'}/>
        </div>
        {/* Aliases — full-width typed list */}
        <div style={{padding:'14px 0',borderBottom:'1px solid var(--rule)'}}>
          <div className="ff-mono upper" style={{fontSize:9,letterSpacing:'.12em',color:'var(--ink-3)',marginBottom:8,display:'flex',alignItems:'baseline',gap:10}}>
            <span>ALIASES</span>
            <span style={{fontSize:9,color:'var(--ink-4)',letterSpacing:'.06em',textTransform:'none'}}>· legal · artist · nickname · previous · misspelling · search variant</span>
          </div>
          <AliasesEditor value={p.aliases} editing={editing} onChange={v=>set('aliases', v)}/>
        </div>
      </div>

      {/* 03 · IDENTIFIERS ───────────────────────────────────────────── */}
      {/* Maps to astro.profile_ipis (multi), astro.profile_isnis (multi),
          astro.profile_identifiers (multi). The "primary" IPI/ISNI lives on
          profiles.ipi / profiles.isni today; additional IPIs/ISNIs live on
          their join tables and are surfaced as the rows below the primary.

          Common case for additional IPIs: a writer affiliated with both ASCAP
          (US) and PRS (UK) typically has a separate IPI per society — the
          society stamps its own member# into the name-number namespace. Same
          story for ISNIs — performer-rights ISNI ≠ writer/publisher ISNI. */}
      <div style={{marginTop:32}}>
        <Section num="03">Identifiers</Section>
        <div style={{display:'grid',gridTemplateColumns:'repeat(4,1fr)',gap:18,padding:'18px 0',borderTop:'1px solid var(--rule)',borderBottom:'1px solid var(--rule)'}}>
          <F label="IPI NAME #"  k="ipi"/>
          <F label="IPI BASE #"  k="ipiBase"/>
          <F label="ISNI"        k="isni"/>
          <F label="DPID"        k="dpid"/>
          <F label="MUSICBRAINZ" k="musicBrainzId"/>
          <F label="WIKIDATA"    k="wikidataId"/>
          <F label="SPOTIFY URI" k="spotifyUri"/>
          <F label="APPLE ID"    k="appleId"/>
        </div>

        {/* Additional IPIs · profile_ipis ─────────────────────────────────
            One IPI row per PRO affiliation. is_primary marks the master IPI;
            others are typically issued by joining societies. */}
        <div style={{marginTop:24}}>
          <div style={{display:'flex',alignItems:'center',justifyContent:'space-between',padding:'8px 0',borderTop:'1px solid var(--rule-soft)'}}>
            <div className="ff-mono upper" style={{fontSize:9,letterSpacing:'.12em',color:'var(--ink-3)'}}>
              Additional IPIs · {(p.additionalIpis||[]).length}
            </div>
            {editing && (
              <button onClick={()=>set('additionalIpis',[...(p.additionalIpis||[]),{ipi:'',pro:'',memberNumber:''}])}
                className="ff-mono upper" style={{fontSize:10,letterSpacing:'.08em',color:'var(--ink)',background:'transparent',border:'1px solid var(--rule)',padding:'4px 10px',cursor:'pointer'}}>+ ADD IPI</button>
            )}
          </div>
          {((p.additionalIpis||[]).length > 0 || editing) && (
            <div className="ff-mono upper" style={{display:'grid',gridTemplateColumns:'180px 110px 1fr 30px',gap:14,padding:'8px 0',fontSize:9,letterSpacing:'.1em',color:'var(--ink-3)',borderBottom:'1px solid var(--rule-soft)'}}>
              <span>IPI NAME #</span><span>PRO</span><span>MEMBER #</span><span/>
            </div>
          )}
          {(p.additionalIpis||[]).map((row,i)=>(
            <div key={i} className="ff-mono" style={{display:'grid',gridTemplateColumns:'180px 110px 1fr 30px',gap:14,padding:'10px 0',fontSize:12,borderBottom:'1px solid var(--rule-soft)',alignItems:'center'}}>
              {editing ? (<>
                <input value={row.ipi}
                  onChange={e=>{ const arr=[...p.additionalIpis]; arr[i]={...row,ipi:e.target.value.replace(/[^\d]/g,'').slice(0,11)}; set('additionalIpis',arr); }}
                  placeholder="11-digit IPI"
                  className="ff-mono num" style={{height:26,fontSize:11,padding:'0 6px',background:'var(--bg)',border:'1px solid var(--rule)',color:'var(--ink)'}}/>
                <input value={row.pro}
                  onChange={e=>{ const arr=[...p.additionalIpis]; arr[i]={...row,pro:e.target.value.toUpperCase()}; set('additionalIpis',arr); }}
                  placeholder="ASCAP"
                  list="ref-society-acronyms"
                  className="ff-mono upper" style={{height:26,fontSize:11,padding:'0 6px',background:'var(--bg)',border:'1px solid var(--rule)',color:'var(--ink)',letterSpacing:'.04em'}}/>
                <input value={row.memberNumber||''}
                  onChange={e=>{ const arr=[...p.additionalIpis]; arr[i]={...row,memberNumber:e.target.value}; set('additionalIpis',arr); }}
                  placeholder="society member #"
                  className="ff-mono" style={{height:26,fontSize:11,padding:'0 6px',background:'var(--bg)',border:'1px solid var(--rule)',color:'var(--ink)'}}/>
                <button onClick={()=>set('additionalIpis', p.additionalIpis.filter((_,j)=>j!==i))}
                  aria-label="Remove" style={{height:26,width:26,border:'1px solid var(--rule)',background:'var(--bg)',cursor:'pointer'}}>×</button>
              </>) : (<>
                <span className="num">{(typeof ipiDisplay==='function'?ipiDisplay(row.ipi):row.ipi) || '—'}</span>
                <span style={{fontWeight:600}}>{row.pro || '—'}</span>
                <span style={{color:'var(--ink-2)'}}>{row.memberNumber || '—'}</span>
                <span/>
              </>)}
            </div>
          ))}
        </div>

        {/* Additional ISNIs · profile_isnis ───────────────────────────────
            ISNI distinguishes a person's roles. A writer/publisher ISNI is
            registered at a different namespace than a performer/recording-
            artist ISNI; both can be on file. */}
        <div style={{marginTop:18}}>
          <div style={{display:'flex',alignItems:'center',justifyContent:'space-between',padding:'8px 0',borderTop:'1px solid var(--rule-soft)'}}>
            <div className="ff-mono upper" style={{fontSize:9,letterSpacing:'.12em',color:'var(--ink-3)'}}>
              Additional ISNIs · {(p.additionalIsnis||[]).length}
            </div>
            {editing && (
              <button onClick={()=>set('additionalIsnis',[...(p.additionalIsnis||[]),{isni:'',role:''}])}
                className="ff-mono upper" style={{fontSize:10,letterSpacing:'.08em',color:'var(--ink)',background:'transparent',border:'1px solid var(--rule)',padding:'4px 10px',cursor:'pointer'}}>+ ADD ISNI</button>
            )}
          </div>
          {((p.additionalIsnis||[]).length > 0 || editing) && (
            <div className="ff-mono upper" style={{display:'grid',gridTemplateColumns:'220px 1fr 30px',gap:14,padding:'8px 0',fontSize:9,letterSpacing:'.1em',color:'var(--ink-3)',borderBottom:'1px solid var(--rule-soft)'}}>
              <span>ISNI</span><span>ROLE / NOTES</span><span/>
            </div>
          )}
          {(p.additionalIsnis||[]).map((row,i)=>(
            <div key={i} className="ff-mono" style={{display:'grid',gridTemplateColumns:'220px 1fr 30px',gap:14,padding:'10px 0',fontSize:12,borderBottom:'1px solid var(--rule-soft)',alignItems:'center'}}>
              {editing ? (<>
                <input value={row.isni}
                  onChange={e=>{ const arr=[...p.additionalIsnis]; arr[i]={...row,isni:typeof formatISNI==='function'?formatISNI(e.target.value):e.target.value}; set('additionalIsnis',arr); }}
                  placeholder="0000 0000 0000 0000"
                  className="ff-mono num" style={{height:26,fontSize:11,padding:'0 6px',background:'var(--bg)',border:'1px solid var(--rule)',color:'var(--ink)'}}/>
                <input value={row.role||''}
                  onChange={e=>{ const arr=[...p.additionalIsnis]; arr[i]={...row,role:e.target.value}; set('additionalIsnis',arr); }}
                  placeholder="e.g. performer · writer · producer"
                  className="ff-mono" style={{height:26,fontSize:11,padding:'0 6px',background:'var(--bg)',border:'1px solid var(--rule)',color:'var(--ink)'}}/>
                <button onClick={()=>set('additionalIsnis', p.additionalIsnis.filter((_,j)=>j!==i))}
                  aria-label="Remove" style={{height:26,width:26,border:'1px solid var(--rule)',background:'var(--bg)',cursor:'pointer'}}>×</button>
              </>) : (<>
                <span className="num">{(typeof isniDisplay==='function'?isniDisplay(row.isni):row.isni) || '—'}</span>
                <span style={{color:'var(--ink-2)'}}>{row.role || '—'}</span>
                <span/>
              </>)}
            </div>
          ))}
        </div>

        {/* Datalist of society acronyms — populated from REF.societies if
            available, else from the legacy SOCIETIES array. Powers the PRO
            input on Additional IPIs. */}
        <datalist id="ref-society-acronyms">
          {(window.REF && window.REF.ready && Array.isArray(window.REF.societies)
            ? window.REF.societies.filter(s => /PRO|MIX|HUB|CMO/i.test(s.cmo_type||''))
            : (typeof SOCIETIES!=='undefined' ? SOCIETIES : [])
          ).slice(0, 800).map((s,i) => (
            <option key={i} value={s.acronym}>{s.full_name || s.name || s.acronym}</option>
          ))}
        </datalist>
      </div>

      {/* 04 · RIGHTS AFFILIATIONS (multi-society) ─────────────────────── */}
      <div style={{marginTop:32}}>
        <Section num="04" action={editing && (
          <button onClick={()=>set('affiliations',[...(p.affiliations||[]),{kind:'PRO',org:'',memberNumber:'',since:'',territory:''}])}
            className="ff-mono upper" style={{fontSize:10,letterSpacing:'.08em',color:'var(--ink)',background:'transparent',border:'1px solid var(--rule)',padding:'4px 10px',cursor:'pointer'}}>+ ADD MEMBERSHIP</button>
        )}>Rights affiliations · {(p.affiliations||[]).length}</Section>
        <div style={{borderTop:'1px solid var(--rule)'}}>
          <div className="ff-mono upper" style={{display:'grid',gridTemplateColumns:'70px 160px 1fr 180px 90px 30px',gap:14,padding:'8px 0',fontSize:9,letterSpacing:'.1em',color:'var(--ink-3)',borderBottom:'1px solid var(--rule)'}}>
            <span>KIND</span><span>ORG</span><span>MEMBER #</span><span>TERRITORY</span><span>SINCE</span><span/>
          </div>
          {(p.affiliations||[]).map((a,i)=>(
            <div key={i} className="ff-mono" style={{display:'grid',gridTemplateColumns:'70px 160px 1fr 180px 90px 30px',gap:14,padding:'10px 0',fontSize:12,borderBottom:'1px solid var(--rule-soft)',alignItems:'center'}}>
              {editing ? (<>
                <span className="ff-mono upper" style={{fontSize:11,fontWeight:600,letterSpacing:'.04em',color:'var(--ink-2)',padding:'0 4px'}}>{a.kind || '—'}</span>
                <SocietyPickerInline
                  value={a.org}
                  onChange={v=>{ const arr=[...p.affiliations]; arr[i]={...a,org:v}; set('affiliations',arr); }}
                  onPick={s=>{
                    const arr=[...p.affiliations];
                    arr[i]={...a, org: s.acronym, kind: s.kind, territory: a.territory || s.territory || ''};
                    set('affiliations',arr);
                  }}
                />
                <input value={a.memberNumber} onChange={e=>{ const arr=[...p.affiliations]; arr[i]={...a,memberNumber:e.target.value}; set('affiliations',arr); }} className="ff-mono" style={{height:26,fontSize:11,padding:'0 6px',background:'var(--bg)',border:'1px solid var(--rule)',color:'var(--ink)'}}/>
                <input value={a.territory} onChange={e=>{ const arr=[...p.affiliations]; arr[i]={...a,territory:e.target.value}; set('affiliations',arr); }} className="ff-mono" style={{height:26,fontSize:11,padding:'0 6px',background:'var(--bg)',border:'1px solid var(--rule)',color:'var(--ink)'}}/>
                <input value={a.since} onChange={e=>{ const arr=[...p.affiliations]; arr[i]={...a,since:e.target.value}; set('affiliations',arr); }} placeholder="YYYY-MM-DD or YYYY" className="ff-mono num" style={{height:26,fontSize:11,padding:'0 6px',background:'var(--bg)',border:'1px solid var(--rule)',color:'var(--ink)',boxSizing:'border-box',fontFamily:'inherit'}}/>
                <button onClick={()=>{ const arr=p.affiliations.filter((_,j)=>j!==i); set('affiliations',arr); }} aria-label="Remove" style={{height:26,width:26,border:'1px solid var(--rule)',background:'var(--bg)',cursor:'pointer'}}>×</button>
              </>) : (<>
                <span style={{fontWeight:600}}>{a.kind}</span>
                <SocietyLink code={a.org}/>
                <span>{a.memberNumber || '—'}</span>
                <span style={{color:'var(--ink-2)'}}>{a.territory || '—'}</span>
                <span className="num">{formatDate(a.since) || '—'}</span>
                <span/>
              </>)}
            </div>
          ))}
          {(p.affiliations||[]).length === 0 && <div className="ff-mono" style={{padding:'14px 0',fontSize:11,color:'var(--ink-3)'}}>No rights affiliations on file.</div>}
        </div>
      </div>

      {/* 05 · GROUP MEMBERS (Group only) ──────────────────────────────── */}
      {p.type === 'Group' && (
        <div style={{marginTop:32}}>
          <Section num="05" action={editing && (
            <button onClick={()=>set('members',[...(p.members||[]),{aid:null,name:'',role:'',joined:''}])}
              className="ff-mono upper" style={{fontSize:10,letterSpacing:'.08em',color:'var(--ink)',background:'transparent',border:'1px solid var(--rule)',padding:'4px 10px',cursor:'pointer'}}>+ ADD MEMBER</button>
          )}>Members · {(p.members||[]).length}</Section>
          <div style={{borderTop:'1px solid var(--rule)'}}>
            <div className="ff-mono upper" style={{display:'grid',gridTemplateColumns:'1fr 220px 110px 110px 30px',gap:14,padding:'8px 0',fontSize:9,letterSpacing:'.1em',color:'var(--ink-3)',borderBottom:'1px solid var(--rule)'}}>
              <span>NAME</span><span>ROLE</span><span>JOINED</span><span>LEFT</span><span/>
            </div>
            {(p.members||[]).map((m,i)=>(
              <div key={i} className="ff-mono" style={{display:'grid',gridTemplateColumns:'1fr 220px 110px 110px 30px',gap:14,padding:'10px 0',fontSize:12,borderBottom:'1px solid var(--rule-soft)',alignItems:'center'}}>
                {editing ? (<>
                  <MemberNameSearch
                    value={m.name}
                    aid={m.aid}
                    currentId={baseP.id}
                    onChange={v=>{ const arr=[...p.members]; arr[i]={...m,name:v}; set('members',arr); }}
                    onPick={(aid, name)=>{ const arr=[...p.members]; arr[i]={...m, aid, name: name!==undefined?name:m.name}; set('members',arr); }}
                  />
                  <input value={m.role} onChange={e=>{ const arr=[...p.members]; arr[i]={...m,role:e.target.value}; set('members',arr); }} className="ff-mono" style={{height:26,fontSize:11,padding:'0 6px',background:'var(--bg)',border:'1px solid var(--rule)',color:'var(--ink)'}}/>
                  <input value={m.joined} onChange={e=>{ const arr=[...p.members]; arr[i]={...m,joined:e.target.value}; set('members',arr); }} type="date" className="ff-mono num" style={{height:26,fontSize:11,padding:'0 4px',background:'var(--bg)',border:'1px solid var(--rule)',color:'var(--ink)',boxSizing:'border-box',fontFamily:'inherit'}}/>,fontSize:11,padding:'0 6px',background:'var(--bg)',border:'1px solid var(--rule)',color:'var(--ink)'}}/>
                  <input value={m.left||''} onChange={e=>{ const arr=[...p.members]; arr[i]={...m,left:e.target.value||undefined}; set('members',arr); }} type="date" className="ff-mono num" style={{height:26,fontSize:11,padding:'0 4px',background:'var(--bg)',border:'1px solid var(--rule)',color:'var(--ink)',boxSizing:'border-box',fontFamily:'inherit'}}/>,fontSize:11,padding:'0 6px',background:'var(--bg)',border:'1px solid var(--rule)',color:'var(--ink)'}}/>
                  <button onClick={()=>{ const arr=p.members.filter((_,j)=>j!==i); set('members',arr); }} aria-label="Remove" style={{height:26,width:26,border:'1px solid var(--rule)',background:'var(--bg)',cursor:'pointer'}}>×</button>
                </>) : (<>
                  {(() => {
                    const linked = m.aid && (typeof ARTISTS!=='undefined') && ARTISTS.find(a=>a.id===m.aid);
                    return linked ? (
                      <button onClick={(e)=>{ e.stopPropagation(); go('public', linked); }}
                        style={{textAlign:'left',background:'transparent',border:0,padding:0,cursor:'pointer',color:'var(--ink)',fontWeight:600,textDecoration:'underline',textDecorationColor:'var(--rule)',textUnderlineOffset:3}}>
                        {m.name}
                      </button>
                    ) : (
                      <span style={{fontWeight:600}}>{m.name}</span>
                    );
                  })()}
                  <span style={{color:'var(--ink-2)'}}>{m.role || '—'}</span>
                  <span className="num">{formatDate(m.joined) || '—'}</span>
                  <span className="num" style={{color: m.left?'var(--danger)':'var(--ink-3)'}}>{m.left ? formatDate(m.left) : 'present'}</span>
                  <span/>
                </>)}
              </div>
            ))}
            {(p.members||[]).length === 0 && <div className="ff-mono" style={{padding:'14px 0',fontSize:11,color:'var(--ink-3)'}}>No members on file. Use Edit to add.</div>}
          </div>
          <div style={{marginTop:14,display:'grid',gridTemplateColumns:'repeat(2,1fr)',gap:18}}>
            <F label="SPLITS CONVENTION" k="splits"/>
            <F label="DISSOLVED"         k="dissolved"/>
          </div>
        </div>
      )}

      {/* 06 · PUBLISHER RELATIONSHIP ──────────────────────────────────── */}
      <div style={{marginTop:32}}>
        <Section num={p.type==='Group'?'06':'05'}>Publisher relationship</Section>
        <div style={{display:'grid',gridTemplateColumns:'repeat(3,1fr)',gap:18,padding:'18px 0',borderTop:'1px solid var(--rule)',borderBottom:'1px solid var(--rule)'}}>
          <F label="PUBLISHER"     k="publisher" mono={false}/>
          <F label="DEAL TYPE"     k="publisherType"/>
          <F label="START"         k="publisherStart"/>
          <F label="TERRITORY"     k="publisherTerritory"/>
          <F label="SCOPE / SHARE" k="publisherScope"/>
          <F label="END"           k="publisherEnd"/>
        </div>
      </div>

      {/* 07 · REPRESENTATION ──────────────────────────────────────────── */}
      <div style={{marginTop:32}}>
        <Section num={p.type==='Group'?'07':'06'}>Representation</Section>
        <div style={{display:'grid',gridTemplateColumns:'repeat(3,1fr)',gap:18,padding:'18px 0',borderTop:'1px solid var(--rule)',borderBottom:'1px solid var(--rule)'}}>
          <F label="MANAGER"     k="manager" mono={false}/>
          <F label="MGMT CO"     k="managerCo" mono={false}/>
          <F label="EMAIL"       k="email"/>
          <F label="AGENT"       k="agent" mono={false}/>
          <F label="AGENCY"      k="agency" mono={false}/>
          <F label="PHONE"       k="phone"/>
          <F label="LAWYER"      k="lawyer" mono={false}/>
          <F label="LAW FIRM"    k="lawFirm" mono={false}/>
          <F label="ADDRESS"     k="address" mono={false}/>
        </div>
      </div>

      {/* 08 · TAX & PAYEE ─────────────────────────────────────────────── */}
      <div style={{marginTop:32}}>
        <Section num={p.type==='Group'?'08':'07'}>Tax & payee</Section>
        <div style={{display:'grid',gridTemplateColumns:'repeat(3,1fr)',gap:18,padding:'18px 0',borderTop:'1px solid var(--rule)',borderBottom:'1px solid var(--rule)'}}>
          <F label="PAYEE ENTITY"      k="payee" mono={false}/>
          <F label="TAX ID"            k="taxId"/>
          <F label="TAX FORM"          k="taxForm"/>
          <F label="FORM EXPIRES"      k="taxFormExpiry"/>
          <F label="WITHHOLDING"       k="withholding"/>
          <F label="TREATY COUNTRY"    k="treatyCountry"/>
          <F label="PAYMENT METHOD"    k="paymentMethod"/>
          <F label="NEIGHBORING RIGHTS PERFORMER" value={p.nrPerformer?'Yes':'No'}/>
          <F label="NR BODY"           k="neighbouringRightsBody"/>
        </div>
      </div>

      {/* 09 · DSP / STREAMING PRESENCE ─────────────────────────────────── */}
      <div style={{marginTop:32}}>
        <Section num={p.type==='Group'?'09':'08'} action={
          <span className="ff-mono upper" style={{fontSize:9,color:'var(--ink-3)',letterSpacing:'.08em'}}>
            {(()=>{
              const dsps = ['spotifyId','appleId','deezerId','tidalId','amazonId','youtubeChannelId','soundcloudId','bandcampUrl','audiomackId','qobuzId','beatportId'];
              const linked = dsps.filter(k=>p[k]).length;
              return `${linked}/${dsps.length} LINKED`;
            })()}
          </span>
        }>DSP & streaming</Section>

        {/* Spotify — flagship row, full-width with metrics */}
        <DspRow
          name="Spotify" color="#1ed760" id={p.spotifyId} url={p.spotifyId?`https://open.spotify.com/artist/${p.spotifyId}`:null}
          editing={editing} onIdChange={v=>set('spotifyId',v)} idKey="spotifyId"
          metrics={[
            {l:'MONTHLY LISTENERS', v:p.spotifyMonthlyListeners},
            {l:'FOLLOWERS', v:p.spotifyFollowers},
            {l:'POPULARITY', v:p.spotifyPopularity, suffix:'/100'},
          ]}
          extras={[
            p.spotifyForArtists && {l:'S4A', v:'✓ Access'},
            p.spotifyGenres && p.spotifyGenres.length && {l:'GENRES', v:p.spotifyGenres.join(' · ')},
          ].filter(Boolean)}
        />

        {/* Apple / Amazon / YouTube row */}
        <div style={{display:'grid',gridTemplateColumns:'1fr 1fr 1fr',borderBottom:'1px solid var(--rule)'}}>
          <DspCell name="Apple Music" color="#fa233b" id={p.appleId} url={p.appleMusicUrl}
            editing={editing} onIdChange={v=>set('appleId',v)}
            metrics={[
              p.appleMusicForArtists && {l:'AM4A', v:'✓'},
              p.appleShazamCount && {l:'SHAZAMS', v:p.appleShazamCount},
            ].filter(Boolean)} borderRight/>
          <DspCell name="Amazon Music" color="#ff9900" id={p.amazonId} url={p.amazonMusicUrl}
            editing={editing} onIdChange={v=>set('amazonId',v)}
            metrics={[ p.amazonForArtists && {l:'AM4A', v:'✓ Access'} ].filter(Boolean)} borderRight/>
          <DspCell name="YouTube" color="#ff0000" id={p.youtubeChannelId} url={p.youtubeChannelId?`https://youtube.com/channel/${p.youtubeChannelId}`:null}
            editing={editing} onIdChange={v=>set('youtubeChannelId',v)}
            metrics={[
              {l:'SUBSCRIBERS', v:p.youtubeSubscribers},
              {l:'TOTAL VIEWS', v:p.youtubeViews},
            ]}/>
        </div>

        {/* Deezer / Tidal / SoundCloud row */}
        <div style={{display:'grid',gridTemplateColumns:'1fr 1fr 1fr',borderBottom:'1px solid var(--rule)'}}>
          <DspCell name="Deezer" color="#a238ff" id={p.deezerId} url={p.deezerUrl||(p.deezerId?`https://deezer.com/artist/${p.deezerId}`:null)}
            editing={editing} onIdChange={v=>set('deezerId',v)}
            metrics={[ p.deezerFans && {l:'FANS', v:p.deezerFans} ].filter(Boolean)} borderRight/>
          <DspCell name="Tidal" color="#000000" id={p.tidalId} url={p.tidalId?`https://tidal.com/artist/${p.tidalId}`:null}
            editing={editing} onIdChange={v=>set('tidalId',v)}
            metrics={[ p.tidalRoles && p.tidalRoles.length && {l:'ROLES', v:p.tidalRoles.join(' · ')} ].filter(Boolean)} borderRight/>
          <DspCell name="SoundCloud" color="#ff5500" id={p.soundcloudId} url={p.soundcloudUrl||(p.soundcloudId?`https://soundcloud.com/${p.soundcloudId}`:null)}
            editing={editing} onIdChange={v=>set('soundcloudId',v)}
            metrics={[ p.soundcloudFollowers && {l:'FOLLOWERS', v:p.soundcloudFollowers} ].filter(Boolean)}/>
        </div>

        {/* Bandcamp / Audiomack / Qobuz / Beatport — compact 4-up */}
        <div style={{display:'grid',gridTemplateColumns:'repeat(4,1fr)',borderBottom:'1px solid var(--rule)'}}>
          <DspCell name="Bandcamp" color="#1da0c3" id={p.bandcampUrl?'linked':null} url={p.bandcampUrl}
            editing={editing} onIdChange={v=>set('bandcampUrl',v)} idKey="bandcampUrl" idAsUrl borderRight/>
          <DspCell name="Audiomack" color="#ffa600" id={p.audiomackId} url={p.audiomackId?`https://audiomack.com/${p.audiomackId}`:null}
            editing={editing} onIdChange={v=>set('audiomackId',v)}
            metrics={[ p.audiomackFollowers && {l:'FOLLOWERS', v:p.audiomackFollowers} ].filter(Boolean)} borderRight/>
          <DspCell name="Qobuz" color="#0070d8" id={p.qobuzId} url={p.qobuzId?`https://qobuz.com/artist/${p.qobuzId}`:null}
            editing={editing} onIdChange={v=>set('qobuzId',v)} borderRight/>
          <DspCell name="Beatport" color="#a4ff37" id={p.beatportId} url={p.beatportId?`https://beatport.com/artist/${p.beatportId}`:null}
            editing={editing} onIdChange={v=>set('beatportId',v)}/>
        </div>

        {/* Songwriter pages (Spotify + Apple) — distinct context */}
        {(p.spotifyForSongwriters || p.spotifyWrittenByPlaylist) && (
          <div style={{display:'grid',gridTemplateColumns:'1fr 1fr',gap:0,borderBottom:'1px solid var(--rule)'}}>
            <div style={{padding:'14px 18px',borderRight:'1px solid var(--rule)'}}>
              <div className="ff-mono upper" style={{fontSize:9,letterSpacing:'.1em',color:'var(--ink-3)',marginBottom:6}}>SPOTIFY SONGWRITER PAGE</div>
              <div className="ff-mono" style={{fontSize:12,wordBreak:'break-all'}}>{p.spotifyForSongwriters || '—'}</div>
              {p.spotifySongwriterPageActive && <Pill tone="ok" style={{marginTop:6}}>ACTIVE</Pill>}
            </div>
            <div style={{padding:'14px 18px'}}>
              <div className="ff-mono upper" style={{fontSize:9,letterSpacing:'.1em',color:'var(--ink-3)',marginBottom:6}}>SPOTIFY "WRITTEN BY" PLAYLIST</div>
              <div className="ff-mono" style={{fontSize:12,wordBreak:'break-all'}}>{p.spotifyWrittenByPlaylist || '—'}</div>
            </div>
          </div>
        )}
      </div>

      {/* 10 · METADATA PARTNERS (MusicBrainz / Discogs / Genius / etc.) ── */}
      <div style={{marginTop:32}}>
        <Section num={p.type==='Group'?'10':'09'}>Metadata partners</Section>
        <div style={{display:'grid',gridTemplateColumns:'repeat(3,1fr)',borderTop:'1px solid var(--rule)',borderBottom:'1px solid var(--rule)'}}>
          <MetaCell name="MusicBrainz" id={p.musicBrainzId} synced={p.musicBrainzSyncedAt}
            url={p.musicBrainzId?`https://musicbrainz.org/artist/${p.musicBrainzId}`:null}
            editing={editing} onIdChange={v=>set('musicBrainzId',v)} borderRight/>
          <MetaCell name="Discogs" id={p.discogsId} synced={p.discogsSyncedAt}
            url={p.discogsId?`https://discogs.com/artist/${p.discogsId}`:null}
            editing={editing} onIdChange={v=>set('discogsId',v)}
            extra={p.discogsRealname && p.discogsRealname!==p.realName ? `as: ${p.discogsName}` : null} borderRight/>
          <MetaCell name="Wikidata" id={p.wikidataId} url={p.wikidataId?`https://www.wikidata.org/wiki/${p.wikidataId}`:null}
            editing={editing} onIdChange={v=>set('wikidataId',v)}/>
        </div>
        <div style={{display:'grid',gridTemplateColumns:'repeat(3,1fr)',borderBottom:'1px solid var(--rule)'}}>
          <MetaCell name="Genius" id={p.geniusId} url={p.geniusId?`https://genius.com/artists/${p.geniusId}`:null}
            editing={editing} onIdChange={v=>set('geniusId',v)}
            extra={p.geniusFollowers ? `${(p.geniusFollowers/1e3).toFixed(1)}K followers${p.geniusVerified?' · verified':''}` : null} borderRight/>
          <MetaCell name="Last.fm" id={p.lastfmListeners ? `${(p.lastfmListeners/1e3).toFixed(0)}K listeners`:null} url={p.lastfmUrl}
            editing={false}
            extra={p.lastfmPlaycount ? `${(p.lastfmPlaycount/1e6).toFixed(1)}M scrobbles` : null} borderRight/>
          <MetaCell name="AllMusic" id={p.allmusicId} url={p.allmusicId?`https://allmusic.com/artist/${p.allmusicId}`:null}
            editing={editing} onIdChange={v=>set('allmusicId',v)}/>
        </div>
        <div style={{display:'grid',gridTemplateColumns:'repeat(3,1fr)',borderBottom:'1px solid var(--rule)'}}>
          <MetaCell name="Google KG" id={p.gkgId} url={p.gkgWikipediaUrl}
            editing={editing} onIdChange={v=>set('gkgId',v)}
            extra={p.gkgWikipediaUrl ? 'Wikipedia ↗' : null} borderRight/>
          <MetaCell name="Rate Your Music" id={p.rymUrl?'linked':null} url={p.rymUrl}
            editing={editing} onIdChange={v=>set('rymUrl',v)} idKey="rymUrl" idAsUrl borderRight/>
          <div style={{padding:'14px 18px',display:'flex',alignItems:'center',justifyContent:'center',color:'var(--ink-4)',fontSize:11}} className="ff-mono upper">
            +18 more partners
          </div>
        </div>
      </div>

      {/* 11 · SOCIAL PRESENCE ──────────────────────────────────────────── */}
      <div style={{marginTop:32}}>
        <Section num={p.type==='Group'?'11':'10'} action={
          <span className="ff-mono upper" style={{fontSize:9,color:'var(--ink-3)',letterSpacing:'.08em'}}>
            {(()=>{
              const total = (p.instagramFollowers||0)+(p.tiktokFollowers||0)+(p.twitterFollowers||0)+(p.facebookFollowers||0)+(p.threadsFollowers||0)+(p.blueskyFollowers||0);
              return total ? `${(total/1e6).toFixed(1)}M TOTAL FOLLOWERS` : 'NO PLATFORMS LINKED';
            })()}
          </span>
        }>Social presence</Section>
        <div style={{display:'grid',gridTemplateColumns:'repeat(3,1fr)',borderTop:'1px solid var(--rule)',borderBottom:'1px solid var(--rule)'}}>
          <SocialCell name="Instagram" handle={p.instagramHandle} followers={p.instagramFollowers} verified={p.instagramVerified}
            url={p.instagramHandle?`https://instagram.com/${p.instagramHandle}`:null}
            editing={editing} onChange={v=>set('instagramHandle',v)} borderRight/>
          <SocialCell name="TikTok" handle={p.tiktokHandle} followers={p.tiktokFollowers}
            url={p.tiktokHandle?`https://tiktok.com/@${p.tiktokHandle}`:null}
            editing={editing} onChange={v=>set('tiktokHandle',v)} borderRight/>
          <SocialCell name="X / Twitter" handle={p.twitterHandle} followers={p.twitterFollowers}
            url={p.twitterHandle?`https://x.com/${p.twitterHandle}`:null}
            editing={editing} onChange={v=>set('twitterHandle',v)}/>
        </div>
        <div style={{display:'grid',gridTemplateColumns:'repeat(3,1fr)',borderBottom:'1px solid var(--rule)'}}>
          <SocialCell name="Facebook" handle={p.facebookUrl?(p.facebookUrl.split('/').filter(Boolean).pop()):null} followers={p.facebookFollowers}
            url={p.facebookUrl} editing={editing} onChange={v=>set('facebookUrl',v)} idAsUrl borderRight/>
          <SocialCell name="Threads" handle={p.threadsHandle} followers={p.threadsFollowers} verified={p.threadsVerified}
            url={p.threadsHandle?`https://threads.net/@${p.threadsHandle}`:null}
            editing={editing} onChange={v=>set('threadsHandle',v)} borderRight/>
          <SocialCell name="Bluesky" handle={p.blueskyHandle} followers={p.blueskyFollowers}
            url={p.blueskyHandle?`https://bsky.app/profile/${p.blueskyHandle}`:null}
            editing={editing} onChange={v=>set('blueskyHandle',v)}/>
        </div>
      </div>

      {/* 12 · LIVE / FAN / DISTRIBUTION ────────────────────────────────── */}
      <div style={{marginTop:32}}>
        <Section num={p.type==='Group'?'12':'11'}>Live, fan & distribution</Section>
        <div style={{display:'grid',gridTemplateColumns:'repeat(4,1fr)',gap:18,padding:'18px 0',borderTop:'1px solid var(--rule)',borderBottom:'1px solid var(--rule)'}}>
          <F label="BANDSINTOWN TRACKERS" value={p.bandsintownTrackers ? p.bandsintownTrackers.toLocaleString() : undefined}/>
          <F label="UPCOMING SHOWS" value={p.bandsintownUpcoming != null ? String(p.bandsintownUpcoming) : (p.songkickUpcoming != null ? String(p.songkickUpcoming) : undefined)}/>
          <F label="ON TOUR" value={p.songkickOnTour ? 'Yes' : (p.bandsintownUpcoming ? 'Yes' : 'No')}/>
          <F label="LINKTREE" k="linktreeUrl" mono/>
          <F label="LINKFIRE" k="linkfireUrl" mono/>
          <F label="PATREON" k="patreonUrl" mono/>
          <F label="DISTRIBUTOR" value={p.distrokidUrl ? 'DistroKid' : p.tunecoreUrl ? 'TuneCore' : p.cdbabyUrl ? 'CD Baby' : p.unitedmastersUrl ? 'UnitedMasters' : undefined}/>
          <F label="SMARTURL / FEATURE.FM" value={p.smarturlUrl || p.featureFmUrl}/>
        </div>
      </div>

      {/* 13 · OPERATIONAL ─────────────────────────────────────────────── */}
      <div style={{marginTop:32}}>
        <Section num={p.type==='Group'?'13':'12'}>Operational</Section>
        <div style={{display:'grid',gridTemplateColumns:'repeat(3,1fr)',gap:18,padding:'18px 0',borderTop:'1px solid var(--rule)',borderBottom:'1px solid var(--rule)'}}>
          <F label="TAGS"              k="tags" value={Array.isArray(p.tags)?p.tags.join(', '):p.tags}/>
          <F label="CREATED"           k="createdAt"/>
          <F label="LAST VERIFIED"     k="lastVerifiedAt"/>
          <F label="OPEN CONFLICTS"    value={String(p.conflictsOpen ?? 0)}/>
          <F label="RESOLVED CONFLICTS" value={String(p.conflictsResolved ?? 0)}/>
          <F label="PROFILE ID"        value={p.id}/>
        </div>
        <div style={{marginTop:14}}>
          <F label="INTERNAL NOTES" k="notes" mono={false} multiline/>
        </div>
      </div>

    </div>
  );
}

// ───────────────────────────────────────────────────────────── COMMAND PALETTE
function CommandPalette({ open, onClose, go }) {
  const [q, setQ] = useS2('');
  const inputRef = useR2();
  useE2(()=>{ if (open) setTimeout(()=>inputRef.current?.focus(),50); else setQ('') }, [open]);

  const all = useM2(()=>[
    ...WORKS.map(w=>({type:'work',label:w.title,sub:w.iswc+' · '+w.writers.join(', '),ref:w,go:()=>go('work',w)})),
    ...ARTISTS.map(p=>({type:'profile',label:p.name,sub:p.pro+' · IPI '+p.ipi+' · '+p.country,ref:p,go:()=>go('public',p)})),
    {type:'page',label:'Dashboard',sub:'Daily briefing',go:()=>go('dashboard')},
    {type:'page',label:'Catalog',sub:'Works · Recordings · Releases',go:()=>go('catalog')},
    {type:'page',label:'Directory',sub:'Profiles · Publishers · Labels',go:()=>go('directory')},
    {type:'page',label:'Claims & conflicts',sub:'Rights queue',go:()=>go('claims')},
    {type:'page',label:'Transmissions',sub:'CWR · DDEX · royalty in/out',go:()=>go('cwr')},
    {type:'page',label:'Royalties',sub:'Statement inbox · reconciliation',go:()=>go('royalties')},
    {type:'page',label:'Performance',sub:'Revenue · Audience · Geography (with map)',go:()=>go('performance')},
    {type:'page',label:'Audio FP',sub:'Fingerprinting · DSP/UGC scan · claims',go:()=>go('audio-fp')},
    {type:'page',label:'Audio FP · Identify',sub:'Match a sample against catalog',go:()=>go('audio-fp', { tab:'identify' })},
    {type:'page',label:'Audio FP · Monitor',sub:'Detection log across platforms',go:()=>go('audio-fp', { tab:'monitor' })},
    {type:'page',label:'Audio FP · Unauthorized',sub:'Recover royalties from unlicensed use',go:()=>go('audio-fp', { tab:'unauth' })},
    {type:'page',label:'Cultural & Market',sub:'Per-territory norms · calendar · sensitivity',go:()=>go('cultural')},
    {type:'page',label:'Cultural Calendar',sub:'Release-timing windows by market',go:()=>go('cultural', { tab:'calendar' })},
    {type:'page',label:'Content Sensitivity',sub:'Per-territory advisories before release',go:()=>go('cultural', { tab:'sensitivity' })},
    {type:'page',label:'Future-Proofing',sub:'Forward-looking signals · scenarios · roadmap',go:()=>go('future-proof')},
    {type:'page',label:'Future · Radar',sub:'Horizon × impact quadrant',go:()=>go('future-proof', { tab:'radar' })},
    {type:'page',label:'Future · Scenarios',sub:'5-year publishing economics outlook',go:()=>go('future-proof', { tab:'scenarios' })},
    {type:'page',label:'Future · Readiness',sub:'12-capability scorecard',go:()=>go('future-proof', { tab:'readiness' })},
    {type:'page',label:'Future · Roadmap',sub:'Quarter-anchored milestones',go:()=>go('future-proof', { tab:'roadmap' })},
    {type:'page',label:'Statement Parser',sub:'Universal ingest · 10 adapters · error inbox',go:()=>go('stmt-parser')},
    {type:'page',label:'Parser · Ingest',sub:'Drop a royalty statement file',go:()=>go('stmt-parser', { tab:'ingest' })},
    {type:'page',label:'Parser · Adapters',sub:'Vendor signature registry',go:()=>go('stmt-parser', { tab:'adapters' })},
    {type:'page',label:'Parser · Errors',sub:'Error taxonomy + recovery actions',go:()=>go('stmt-parser', { tab:'errors' })},
    {type:'page',label:'Parser · Schema',sub:'Canonical line schema reference',go:()=>go('stmt-parser', { tab:'schema' })},
    {type:'page',label:'Parser · Custom adapter',sub:'Map your own CSV columns to canonical fields',go:()=>go('stmt-custom')},
    {type:'page',label:'Fee Engine',sub:'Admin / sub-pub cascade · per source · per territory',go:()=>go('fee-engine')},
    {type:'page',label:'Fee · Simulator',sub:'Try a scenario, see which rule fires',go:()=>go('fee-engine', { tab:'simulator' })},
    {type:'page',label:'Writer Payments',sub:'Per-writer distribution statements with fee breakdown',go:()=>go('writer-payments')},
    {type:'page',label:'Writer Payments · Export',sub:'Bulk CSVs for accounting handoff',go:()=>go('writer-payments', { tab:'export' })},
    {type:'page',label:'Royalty Visualizer',sub:'9 cross-filtered charts · time/source/work/territory',go:()=>go('royalty-viz')},
    {type:'page',label:'Royalty Viz · Time',sub:'Time-series + work×period heatmap + cohort',go:()=>go('royalty-viz', { tab:'time' })},
    {type:'page',label:'Royalty Viz · Composition',sub:'Sankey: source → work → writer',go:()=>go('royalty-viz', { tab:'composition' })},
    {type:'page',label:'Royalty Viz · Geography',sub:'Territory choropleth',go:()=>go('royalty-viz', { tab:'geo' })},
    {type:'page',label:'Royalty Viz · Long-tail',sub:'Pareto + per-work sparkline grid',go:()=>go('royalty-viz', { tab:'longtail' })},
    {type:'page',label:'Catalog dedup',sub:'Find duplicate works · recordings · releases · parties',go:()=>go('dedup')},
    {type:'cmd',label:'Generate CWR batch',sub:'⌘ G',go:()=>go('cwr', { tab:'gen' })},
    {type:'cmd',label:'New work',sub:'⌘ N',go:()=>go('work')},
    {type:'cmd',label:'Resolve top hot claim',sub:'⌘ ⇧ R',go:()=>go('claims')},
    {type:'cmd',label:'Toggle dark mode',sub:'⌘ ⇧ D',go:()=>{document.documentElement.dataset.theme = document.documentElement.dataset.theme==='dark'?'light':'dark';onClose();}},
  ],[go,onClose]);

  const filtered = useM2(()=>{
    if (!q) return all.slice(0,12);
    const F = window.FuzzyEngine;
    const ql = q.toLowerCase();
    // Hybrid: substring hit ranks first, then fuzzy score for typo tolerance.
    const scored = all.map(i => {
      const hay = (i.label + ' ' + i.sub);
      const hayL = hay.toLowerCase();
      // Substring presence is strong signal — boost it.
      const sub = hayL.includes(ql) ? 1 : 0;
      let fz = 0;
      if (F) {
        // Compare query against label (most important), trim long-tail noise.
        try { fz = F.score(q, i.label); } catch (_) {}
      }
      return { i, score: sub * 1.0 + fz * 0.85 };
    }).filter(x => x.score >= 0.3)
      .sort((a, b) => b.score - a.score)
      .slice(0, 16);
    return scored.map(x => x.i);
  }, [q, all]);

  if (!open) return null;
  return (
    <div onClick={onClose} style={{position:'fixed',inset:0,background:'rgba(10,10,10,.42)',zIndex:1000,
      display:'flex',alignItems:'flex-start',justifyContent:'center',paddingTop:'12vh',backdropFilter:'blur(4px)'}}>
      <div data-cmdk onClick={e=>e.stopPropagation()} style={{width:'min(720px,92vw)',background:'var(--bg)',border:'1px solid var(--rule)',
        boxShadow:'8px 8px 0 var(--ink)',display:'flex',flexDirection:'column',maxHeight:'72vh'}}>
        <div style={{padding:'14px 18px',borderBottom:'1px solid var(--rule)',display:'flex',gap:12,alignItems:'center'}}>
          <Ic.Search width={18} height={18} style={{color:'var(--ink-3)'}}/>
          <input ref={inputRef} value={q} onChange={e=>setQ(e.target.value)} placeholder="Search works, profiles, commands…"
            style={{flex:1,border:'none',outline:'none',background:'transparent',fontSize:18,color:'var(--ink)',
              fontFamily:'Space Grotesk',letterSpacing:'-0.02em',fontWeight:500}}/>
          <Kbd>ESC</Kbd>
        </div>
        <div style={{overflow:'auto',padding:'8px 0'}}>
          {filtered.length===0 && <div style={{padding:'40px',textAlign:'center',color:'var(--ink-3)'}}>No matches.</div>}
          {filtered.map((it,i)=>(
            <div key={i} onClick={()=>{it.go();onClose()}} style={{display:'grid',gridTemplateColumns:'70px 1fr 30px',gap:12,
              padding:'12px 18px',cursor:'pointer',alignItems:'center'}}
              onMouseEnter={e=>e.currentTarget.style.background='var(--bg-2)'}
              onMouseLeave={e=>e.currentTarget.style.background='transparent'}>
              <span className="ff-mono upper" style={{fontSize:9,fontWeight:600,letterSpacing:'.08em',color:'var(--ink-3)',
                background:'var(--bg-2)',padding:'3px 6px',textAlign:'center'}}>{it.type}</span>
              <div>
                <div style={{fontSize:14,fontWeight:600}}>{it.label}</div>
                <div className="ff-mono" style={{fontSize:11,color:'var(--ink-3)',marginTop:2}}>{it.sub}</div>
              </div>
              <Ic.Right width={14} height={14} style={{color:'var(--ink-3)'}}/>
            </div>
          ))}
        </div>
        <div style={{padding:'10px 18px',borderTop:'1px solid var(--rule)',display:'flex',gap:14,fontSize:10}} className="ff-mono upper">
          <span style={{color:'var(--ink-3)'}}><Kbd>↑↓</Kbd> navigate</span>
          <span style={{color:'var(--ink-3)'}}><Kbd>↵</Kbd> open</span>
          <span style={{color:'var(--ink-3)'}}><Kbd>⌘ K</Kbd> toggle</span>
          <span style={{flex:1}}/>
          <span style={{color:'var(--ink-3)'}}>{filtered.length} results</span>
        </div>
      </div>
    </div>
  );
}

Object.assign(window, { ScreenClaims, ScreenCwr, ScreenAnalytics, ScreenPublic, CommandPalette });
