// screens3.jsx — Recordings, Releases, Publishers, Labels, Agreements, Settings
const { useState: useS3, useEffect: useE3 } = React;

// ───────────────────────────────────────────────────────────── RECORDINGS
// Recording list pulled from the rich graph (songs.jsx). Falls back if absent.
const RECORDINGS = (typeof RECORDING_GRAPH !== 'undefined')
  ? RECORDING_GRAPH.filter(r => !r.external)
  : [];

function RecordingsView({ go, q='', sort='edited', view='table', labelFilter='', bulkScope='' }) {
  const [hover, setHover] = useS3(null);
  const lf = (labelFilter||'').trim().toLowerCase();
  const list = RECORDINGS.filter(r => {
    if (lf && !(r.label||'').toLowerCase().includes(lf)) return false;
    if (!q) return true;
    const blob = `${r.title} ${r.isrc} ${r.artist} ${r.album} ${r.label}`.toLowerCase();
    return blob.includes(q.toLowerCase());
  });

  // ─── Predict score memo: scored once per render of the filtered list.
  // Lazy: only compute when sort==='predicted' or when we render the column.
  const predictMap = React.useMemo(() => {
    if (!window.PredictEngine) return null;
    const E = window.PredictEngine;
    const out = {};
    for (const r of list) {
      const f = E.getFeatures(r);
      out[r.id] = E.songSuccess(f).score;
    }
    return out;
  }, [list.length, sort]);

  list.sort((a,b) => {
    switch (sort) {
      case 'title':       return a.title.localeCompare(b.title);
      case 'title-desc':  return b.title.localeCompare(a.title);
      case 'plays':       return (b.plays||0) - (a.plays||0);
      case 'royalties':   return (b.plays||0) - (a.plays||0); // proxy
      case 'predicted':   return (predictMap?.[b.id]||0) - (predictMap?.[a.id]||0);
      case 'created':     return (b.year||0) - (a.year||0);
      case 'edited':
      default:            return (a.id||'').localeCompare(b.id||'');
    }
  });

  if (view === 'grid') {
    return (
      <div style={{borderTop:'1px solid var(--rule)',marginTop:0,
        display:'grid',gridTemplateColumns:'repeat(auto-fill,minmax(200px,1fr))',gap:0}}>
        {list.map(rec => (
          <div key={rec.id} data-rec-row={rec.id} onClick={()=>window.dispatchEvent(new CustomEvent('astro-open-recording',{detail:{id:rec.id}}))}
            style={{cursor:'pointer',background:'var(--bg)',
              borderRight:'1px solid var(--rule-soft)',borderBottom:'1px solid var(--rule-soft)',
              padding:14,display:'flex',flexDirection:'column',gap:10,position:'relative'}}
            onMouseEnter={e=>e.currentTarget.style.background='var(--bg-2)'}
            onMouseLeave={e=>e.currentTarget.style.background='var(--bg)'}>
            {bulkScope && (
              <div style={{position:'absolute',top:8,left:8,zIndex:2,padding:3,background:'rgba(255,255,255,.92)',border:'1px solid var(--rule)'}}
                onClick={e=>e.stopPropagation()}>
                <BulkCheckbox scope={bulkScope} id={rec.id} allIds={list.map(x=>x.id)}/>
              </div>
            )}
            <div style={{aspectRatio:'1/1',background:rec.art,position:'relative',width:'100%'}}>
              <Ic.Disc width={28} height={28} style={{position:'absolute',top:'50%',left:'50%',transform:'translate(-50%,-50%)',color:'rgba(0,0,0,.5)'}}/>
              {rec.explicit && <span className="ff-mono" style={{position:'absolute',top:6,right:6,fontSize:9,padding:'1px 5px',background:'var(--ink)',color:'var(--bg)',fontWeight:600}}>E</span>}
            </div>
            <div style={{flex:1,display:'flex',flexDirection:'column',gap:4,minHeight:0}}>
              <div className="heading-swap ff-display" style={{fontSize:14,fontWeight:600,letterSpacing:'-0.01em',lineHeight:1.2,
                overflow:'hidden',textOverflow:'ellipsis',display:'-webkit-box',WebkitLineClamp:2,WebkitBoxOrient:'vertical'}}>{rec.title}</div>
              <div style={{fontSize:12,color:'var(--ink-2)',overflow:'hidden',textOverflow:'ellipsis',whiteSpace:'nowrap'}}>{rec.artist}</div>
              <div className="ff-mono" style={{fontSize:10,color:'var(--ink-3)',overflow:'hidden',textOverflow:'ellipsis',whiteSpace:'nowrap'}}>{rec.album} · {rec.year}</div>
            </div>
            <div style={{display:'flex',justifyContent:'space-between',paddingTop:8,borderTop:'1px solid var(--rule-soft)'}}>
              <span className="ff-mono" style={{fontSize:10,color:'var(--ink-3)'}}>{rec.label}</span>
              <span className="ff-mono num" style={{fontSize:11,color:'var(--ink)'}}>{rec.plays}M</span>
            </div>
          </div>
        ))}
      </div>
    );
  }

  return (
    <div style={{borderTop:'1px solid var(--rule)',marginTop:0,position:'relative'}}>
      <div className="ff-mono upper" style={{display:'grid',gridTemplateColumns:'28px 56px 1fr 220px 180px 130px 70px 90px 70px',gap:14,
        padding:'10px 14px',fontSize:10,color:'var(--ink-3)',background:'var(--bg-2)',borderBottom:'1px solid var(--rule)'}}>
        <span style={{display:'flex',alignItems:'center'}}>{bulkScope ? <BulkHeaderCheckbox scope={bulkScope} allIds={list.map(r=>r.id)}/> : null}</span>
        <span/><span>TITLE / ISRC</span><span>ARTIST</span><span>RELEASE</span><span>LABEL</span><span style={{textAlign:'right'}}>DUR</span><span style={{textAlign:'right'}}>PLAYS</span><span style={{textAlign:'right'}} title="Predicted hit score (0–100)">PRED</span>
      </div>
      {list.map((rec,i)=>(
        <div key={rec.id} data-rec-row={rec.id} onClick={()=>window.dispatchEvent(new CustomEvent('astro-open-recording',{detail:{id:rec.id}}))} style={{display:'grid',gridTemplateColumns:'28px 56px 1fr 220px 180px 130px 70px 90px 70px',gap:14,
          padding:'12px 14px',borderBottom:'1px solid var(--rule-soft)',alignItems:'center',cursor:'pointer',
          background: hover===rec.id ? 'var(--bg-2)' : 'transparent'}}
          onMouseEnter={()=>setHover(rec.id)}
          onMouseLeave={()=>setHover(null)}>
          <span style={{display:'flex',alignItems:'center'}}>{bulkScope ? <BulkCheckbox scope={bulkScope} id={rec.id} allIds={list.map(x=>x.id)}/> : null}</span>
          <div style={{width:44,height:44,background:rec.art,position:'relative'}}>
            <Ic.Disc width={18} height={18} style={{position:'absolute',top:13,left:13,color:'rgba(0,0,0,.55)'}}/>
          </div>
          <div>
            <div style={{display:'flex',alignItems:'center',gap:8,flexWrap:'wrap'}}>
              <span style={{fontSize:14,fontWeight:600,letterSpacing:'-0.01em'}}>{rec.title}</span>
              {rec.explicit && <span className="ff-mono" style={{fontSize:9,padding:'1px 4px',background:'var(--ink)',color:'var(--bg)',fontWeight:600}}>E</span>}
              {(()=>{
                const dsp = window.RECORDING_DSP_DETAILS?.[rec.id];
                if (!dsp) return null;
                const chips = [];
                if (dsp.spotify?.popularity >= 70) chips.push({l:`◉ ${dsp.spotify.popularity}`,c:'#1ed760',t:`Spotify popularity ${dsp.spotify.popularity}/100`});
                if (dsp.tiktok?.videoCount >= 10000) chips.push({l:`TT ${dsp.tiktok.videoCount>=1e6?(dsp.tiktok.videoCount/1e6).toFixed(1)+'M':(dsp.tiktok.videoCount/1e3).toFixed(0)+'K'}`,c:'#000',t:`${dsp.tiktok.videoCount.toLocaleString()} TikToks using sound`});
                if (dsp.shazam?.count >= 100000) chips.push({l:`SZ ${dsp.shazam.count>=1e6?(dsp.shazam.count/1e6).toFixed(1)+'M':(dsp.shazam.count/1e3).toFixed(0)+'K'}`,c:'#0066ff',t:`${dsp.shazam.count.toLocaleString()} Shazams`});
                if (dsp.appleExtras?.hasAtmos || dsp.tidal?.audioModes?.includes?.('DOLBY_ATMOS')) chips.push({l:'ATMOS',c:'#fa233b',t:'Dolby Atmos delivered'});
                if (dsp.versionFlags?.isRemix) chips.push({l:'REMIX',c:'var(--ink-3)',t:dsp.versionFlags.remixerName?`Remix by ${dsp.versionFlags.remixerName}`:'Remix'});
                if (dsp.versionFlags?.isCover) chips.push({l:'COVER',c:'var(--ink-3)',t:dsp.versionFlags.originalArtist?`Cover of ${dsp.versionFlags.originalArtist}`:'Cover'});
                if (dsp.versionFlags?.isLive) chips.push({l:'LIVE',c:'var(--ink-3)',t:dsp.versionFlags.liveVenue||'Live recording'});
                return chips.slice(0,3).map((ch,i)=>(
                  <span key={i} title={ch.t} className="ff-mono" onClick={e=>e.stopPropagation()}
                    style={{fontSize:9,padding:'1px 5px',border:`1px solid ${ch.c}`,color:ch.c,fontWeight:600,letterSpacing:'.04em',whiteSpace:'nowrap'}}>{ch.l}</span>
                ));
              })()}
            </div>
            <div className="ff-mono" style={{fontSize:10,color:'var(--ink-3)',marginTop:2}}>{(window.isrcDisplay ? window.isrcDisplay(rec.isrc) : rec.isrc)}</div>
          </div>
          <span style={{fontSize:13}}>{rec.artist}</span>
          <span style={{fontSize:12,color:'var(--ink-2)'}}>{rec.album} · {rec.year}</span>
          <span className="ff-mono" style={{fontSize:11,color:'var(--ink-3)'}}>{rec.label}</span>
          <span className="ff-mono num" style={{fontSize:12,textAlign:'right'}}>{Math.floor(rec.duration/60)}:{String(rec.duration%60).padStart(2,'0')}</span>
          <span className="ff-mono num" style={{fontSize:13,textAlign:'right'}}>{rec.plays}M</span>
          {(() => {
            const sc = predictMap?.[rec.id];
            if (sc == null) return <span/>;
            const c = sc > 75 ? '#0a8754' : sc > 60 ? '#0070d6' : sc > 45 ? 'var(--ink)' : sc > 30 ? '#a35418' : '#a32a18';
            return <span className="ff-mono num" title={`Predicted hit score · ${sc}/100`} style={{fontSize:12,textAlign:'right',color:c,fontWeight:500}}>{sc}</span>;
          })()}
        </div>
      ))}
    </div>
  );
}

function RecordingActionModal({ kind, title, children, onClose, onConfirm, confirmLabel, danger }) {
  return (
    <>
      <div onClick={onClose} style={{position:'fixed',inset:0,background:'rgba(0,0,0,.42)',zIndex:80}}/>
      <div style={{
        position:'fixed', top:'50%', left:'50%', transform:'translate(-50%, -50%)',
        width:'min(540px, 92vw)', maxHeight:'86vh', overflow:'auto',
        background:'var(--bg)', border:'1px solid var(--ink)', boxShadow:'8px 8px 0 rgba(0,0,0,.18)',
        zIndex:81, color:'var(--ink)'
      }}>
        <div style={{padding:'16px 20px',borderBottom:'1px solid var(--rule)'}}>
          <div className="ff-mono upper" style={{fontSize:9,letterSpacing:'.14em',color:'var(--ink-3)',marginBottom:6}}>{kind}</div>
          <div className="ff-display" style={{fontSize:22,fontWeight:700,letterSpacing:'-0.01em'}}>{title}</div>
        </div>
        <div style={{padding:'18px 20px'}}>{children}</div>
        <div style={{display:'flex',gap:8,justifyContent:'flex-end',padding:'12px 16px',borderTop:'1px solid var(--rule)',background:'var(--bg-2)'}}>
          <button onClick={onClose}
            style={{padding:'8px 14px',background:'transparent',color:'var(--ink)',border:'1px solid var(--rule)',fontSize:12,fontWeight:500,cursor:'pointer'}}>Cancel</button>
          <button onClick={onConfirm}
            style={{padding:'8px 14px',background: danger ? '#c0392b' : 'var(--ink)',color: danger ? '#fff' : 'var(--bg)',border:0,fontSize:12,fontWeight:600,cursor:'pointer',letterSpacing:'.02em'}}>{confirmLabel}</button>
        </div>
      </div>
    </>
  );
}

function RecordingDrawer({ r, onClose, go }) {
  const [menuOpen, setMenuOpen] = React.useState(false);
  const [modal, setModal] = React.useState(null); // null | 'addToRelease' | 'linkWork' | 'ddex' | 'archive' | 'flag'
  const [archived, setArchived] = React.useState(false);
  const [flagged, setFlagged] = React.useState(false);
  const [linkedReleaseIds, setLinkedReleaseIds] = React.useState(r.releaseIds || []);
  const [linkedWorkIds, setLinkedWorkIds] = React.useState([r.workId, ...(r.alsoWorks||[]).map(a=>a.workId)].filter(Boolean));
  React.useEffect(() => {
    if (!menuOpen) return;
    const close = () => setMenuOpen(false);
    window.addEventListener('click', close);
    return () => window.removeEventListener('click', close);
  }, [menuOpen]);
  const toast = (msg) => window.dispatchEvent(new CustomEvent('astro-toast',{detail:{msg,tone:'ok'}}));
  // Pull rich graph data
  const primaryWork = (typeof WORK_BY_ID !== 'undefined') ? WORK_BY_ID[r.workId] : null;
  // Fallback: title-match to plain WORKS list (legacy data)
  const matched = primaryWork || ((typeof WORKS !== 'undefined') ? WORKS.find(w => w.title.toLowerCase() === r.title.toLowerCase()) : null);
  const sourceWorks = (r.alsoWorks || []).map(aw => ({...aw, work: WORK_BY_ID?.[aw.workId]})).filter(x=>x.work);
  const derives = (r.derivesFrom || []).map(d => ({...d, parent: d.ofRecordingId ? REC_BY_ID?.[d.ofRecordingId] : null}));
  const releases = (r.releaseIds || []).map(id => REL_BY_ID?.[id]).filter(Boolean);
  // Other recordings of the same primary work
  const siblings = primaryWork ? (recordingsForWork?.(primaryWork.id) || []).filter(x => x.id !== r.id) : [];
  const dsps = [
    {n:'Spotify',     plays:Math.round(r.plays*0.42*10)/10,share:42},
    {n:'Apple Music', plays:Math.round(r.plays*0.21*10)/10,share:21},
    {n:'YouTube',     plays:Math.round(r.plays*0.18*10)/10,share:18},
    {n:'Amazon',      plays:Math.round(r.plays*0.09*10)/10,share:9},
    {n:'Tidal',       plays:Math.round(r.plays*0.05*10)/10,share:5},
    {n:'Other',       plays:Math.round(r.plays*0.05*10)/10,share:5},
  ];
  return (
    <>
      <div onClick={onClose} style={{position:'fixed',inset:0,background:'rgba(0,0,0,.34)',zIndex:60}}/>
      <aside style={{position:'fixed',top:0,right:0,bottom:0,width:'min(560px,92vw)',background:'var(--bg)',borderLeft:'1px solid var(--rule)',
        zIndex:61,overflowY:'auto',boxShadow:'-20px 0 40px rgba(0,0,0,.18)'}}>
        {/* Hero */}
        <div style={{position:'relative',padding:'24px 28px 20px',background:r.art,color:'#0b0b0b'}}>
          <button onClick={onClose} aria-label="Close"
            style={{position:'absolute',top:14,right:14,width:30,height:30,display:'inline-flex',alignItems:'center',justifyContent:'center',
              background:'rgba(0,0,0,.08)',border:0,cursor:'pointer'}}>
            <span style={{fontSize:18,lineHeight:1}}>×</span>
          </button>
          <div className="ff-mono upper" style={{fontSize:9,letterSpacing:'.14em',opacity:.7,marginBottom:14}}>RECORDING · {r.isrc}</div>
          <div style={{display:'flex',gap:18,alignItems:'flex-end'}}>
            <div style={{width:96,height:96,background:'rgba(0,0,0,.12)',display:'flex',alignItems:'center',justifyContent:'center',flexShrink:0}}>
              <Ic.Disc width={42} height={42} style={{color:'rgba(0,0,0,.65)'}}/>
            </div>
            <div style={{flex:1,minWidth:0}}>
              <div className="ff-display" style={{fontSize:30,fontWeight:700,letterSpacing:'-0.03em',lineHeight:1.0}}>{r.title}</div>
              <div style={{fontSize:14,marginTop:6,fontWeight:500}}>{r.artist}</div>
              <div className="ff-mono" style={{fontSize:11,marginTop:4,opacity:.75}}>{r.album} · {r.year} · {Math.floor(r.duration/60)}:{String(r.duration%60).padStart(2,'0')}</div>
            </div>
          </div>
          <div style={{display:'flex',gap:8,marginTop:16}}>
            <button onClick={()=>window.dispatchEvent(new CustomEvent('astro-toast',{detail:{msg:`Preview opened for ${r.title}`,tone:'ok'}}))}
              style={{display:'flex',alignItems:'center',gap:6,padding:'7px 12px',background:'#0b0b0b',color:r.art,border:0,fontSize:12,fontWeight:600,cursor:'pointer'}}>
              <Ic.Play width={11} height={11}/> Preview
            </button>
            <button onClick={()=>window.dispatchEvent(new CustomEvent('astro-toast',{detail:{msg:'Metadata editor opened',tone:'ok'}}))}
              style={{padding:'7px 12px',background:'transparent',color:'#0b0b0b',border:'1px solid rgba(0,0,0,.3)',fontSize:12,fontWeight:500,cursor:'pointer'}}>Edit metadata</button>
            <div style={{position:'relative'}} onClick={(e)=>e.stopPropagation()}>
              <button onClick={()=>setMenuOpen(o=>!o)} aria-label="More actions" aria-expanded={menuOpen}
                style={{padding:'7px 10px',background: menuOpen ? '#0b0b0b' : 'transparent',color: menuOpen ? r.art : '#0b0b0b',border:'1px solid rgba(0,0,0,.3)',fontSize:12,cursor:'pointer',fontWeight:600,letterSpacing:'.04em'}}>···</button>
              {menuOpen && (
                <div style={{
                  position:'absolute', top:'calc(100% + 4px)', right:0, minWidth:240, zIndex:5,
                  background:'var(--bg)', border:'1px solid var(--ink)', boxShadow:'4px 4px 0 rgba(0,0,0,.18)',
                  color:'var(--ink)'
                }}>
                  {[
                    { l:'Open full page',          go:()=>{ setMenuOpen(false); onClose && onClose(); go && go('recording', { id:r.id }); } },
                    { l:'Open in catalog',         go:()=>{ setMenuOpen(false); onClose && onClose(); go && go('catalog', { tab:'recordings', focus:r.id }); } },
                    { l:'Copy ISRC',               go:()=>{ navigator.clipboard?.writeText(r.isrc); setMenuOpen(false); toast(`ISRC ${r.isrc} copied`); } },
                    { l:'Copy share link',         go:()=>{ navigator.clipboard?.writeText(`astro://recording/${r.id}`); setMenuOpen(false); toast('Share link copied'); } },
                    { divider:true },
                    { l:'Generate DDEX delivery',  go:()=>{ setMenuOpen(false); setModal('ddex'); } },
                    { l:'Add to release...',       go:()=>{ setMenuOpen(false); setModal('addToRelease'); } },
                    { l:'Link new work...',        go:()=>{ setMenuOpen(false); setModal('linkWork'); } },
                    { divider:true },
                    { l: flagged ? 'Unflag' : 'Flag for review', go:()=>{ setMenuOpen(false); setModal('flag'); } },
                    { l: archived ? 'Restore from archive' : 'Archive recording', danger: !archived, go:()=>{ setMenuOpen(false); setModal('archive'); } },
                  ].map((it, i) => it.divider ? (
                    <div key={`d-${i}`} style={{height:1,background:'var(--rule)'}}/>
                  ) : (
                    <button key={i} onClick={it.go}
                      style={{
                        display:'block', width:'100%', textAlign:'left', padding:'9px 14px',
                        background:'transparent', border:0, cursor:'pointer',
                        fontSize:12, color: it.danger ? '#c0392b' : 'var(--ink)',
                        fontWeight: it.danger ? 600 : 500, letterSpacing:'.01em'
                      }}
                      onMouseEnter={e=>e.currentTarget.style.background='var(--bg-2)'}
                      onMouseLeave={e=>e.currentTarget.style.background='transparent'}>
                      {it.l}
                    </button>
                  ))}
                </div>
              )}
            </div>
          </div>
        </div>

        {/* IDs / metadata grid */}
        <div style={{padding:'20px 28px',borderBottom:'1px solid var(--rule)'}}>
          <div className="ff-mono upper" style={{fontSize:9,color:'var(--ink-3)',letterSpacing:'.12em',marginBottom:12}}>IDENTIFIERS</div>
          <div style={{display:'grid',gridTemplateColumns:'1fr 1fr',gap:'14px 24px'}}>
            {[
              ['ISRC', r.isrc],
              ['Label', r.label],
              ['Duration', `${Math.floor(r.duration/60)}:${String(r.duration%60).padStart(2,'0')}`],
              ['Year', r.year],
              ['Explicit', r.explicit ? 'Yes' : 'No'],
              ['P-line', `℗ ${r.year} ${r.label}`],
            ].map(([k,v])=>(
              <div key={k}>
                <div className="ff-mono upper" style={{fontSize:9,color:'var(--ink-3)',letterSpacing:'.1em',marginBottom:3}}>{k}</div>
                <div style={{fontSize:13}}>{v}</div>
              </div>
            ))}
          </div>
        </div>

        {/* Linked work + Source works + Derivations + Other recordings */}
        <div style={{padding:'20px 28px',borderBottom:'1px solid var(--rule)'}}>
          <div className="ff-mono upper" style={{fontSize:9,color:'var(--ink-3)',letterSpacing:'.12em',marginBottom:12}}>UNDERLYING WORK</div>
          {matched ? (
            <button onClick={()=>{ onClose(); go && go('work', matched); }}
              style={{display:'flex',alignItems:'center',gap:14,width:'100%',padding:'12px 14px',border:'1px solid var(--rule)',background:'transparent',cursor:'pointer',textAlign:'left'}}>
              <span style={{width:8,height:36,background:'var(--accent)'}}/>
              <div style={{flex:1,minWidth:0}}>
                <div style={{fontSize:13,fontWeight:600}}>{matched.title}</div>
                <div className="ff-mono" style={{fontSize:10,color:'var(--ink-3)',marginTop:2}}>ISWC {window.iswcDisplay ? window.iswcDisplay(matched.iswc) : matched.iswc} · <SocietyLink code={matched.pro} style={{fontSize:10}}/> · shares {matched.shares}</div>
              </div>
              <Ic.Right width={14} height={14} style={{color:'var(--ink-3)'}}/>
            </button>
          ) : (
            <div className="ff-mono" style={{fontSize:11,color:'var(--ink-3)'}}>No work matched. <button style={{textDecoration:'underline',background:'transparent',border:0,padding:0,fontSize:11,fontFamily:'inherit',cursor:'pointer',color:'var(--ink-2)'}}>Match manually</button></div>
          )}

          {sourceWorks.length>0 && (
            <>
              <div className="ff-mono upper" style={{fontSize:9,color:'var(--ink-3)',letterSpacing:'.12em',marginTop:18,marginBottom:10}}>
                ALSO INCORPORATES · {sourceWorks.length} SOURCE WORK{sourceWorks.length>1?'S':''}
              </div>
              {sourceWorks.map((sw,i)=>{
                const cColor = sw.clearance==='cleared' ? '#5b8a3a' : sw.clearance==='pending' ? '#b78113' : '#c0392b';
                return (
                  <button key={i} onClick={()=>{ onClose(); go && go('work', sw.work); }}
                    style={{display:'grid',gridTemplateColumns:'8px 1fr 80px 80px 14px',alignItems:'center',gap:12,
                      width:'100%',padding:'10px 12px',marginBottom:6,
                      border:'1px dashed var(--rule)',background:'transparent',cursor:'pointer',textAlign:'left'}}>
                    <span style={{width:8,height:30,background:'#9b6a18'}}/>
                    <div style={{minWidth:0}}>
                      <div style={{fontSize:13,fontWeight:500}}>{sw.work.title}</div>
                      <div className="ff-mono" style={{fontSize:10,color:'var(--ink-3)',marginTop:2}}>
                        ISWC {window.iswcDisplay ? window.iswcDisplay(sw.work.iswc) : sw.work.iswc} · {sw.work.writers.join(', ')}
                      </div>
                    </div>
                    <span className="ff-mono upper" style={{fontSize:9,letterSpacing:'.1em',color:'#9b6a18',fontWeight:600,textAlign:'right'}}>{sw.type}</span>
                    <span className="ff-mono num" style={{fontSize:12,textAlign:'right'}}>{sw.share}%</span>
                    <Ic.Right width={12} height={12} style={{color:'var(--ink-3)'}}/>
                  </button>
                );
              })}
              <div className="ff-mono" style={{fontSize:10,color:'var(--ink-3)',marginTop:4,paddingLeft:2}}>
                Clearance status: {sourceWorks.map((sw,i)=>(
                  <span key={i} style={{color: sw.clearance==='cleared'?'#5b8a3a':sw.clearance==='pending'?'#b78113':'#c0392b',fontWeight:600,marginRight:8}}>
                    {sw.work.title} — {sw.clearance.toUpperCase()}
                  </span>
                ))}
              </div>
            </>
          )}

          {derives.length>0 && (
            <>
              <div className="ff-mono upper" style={{fontSize:9,color:'var(--ink-3)',letterSpacing:'.12em',marginTop:18,marginBottom:10}}>
                DERIVES FROM
              </div>
              {derives.map((d,i)=>{
                if (d.parent) return (
                  <button key={i} onClick={()=>{ window.dispatchEvent(new CustomEvent('astro-open-recording',{detail:{id:d.parent.id}})); }}
                    style={{display:'grid',gridTemplateColumns:'40px 1fr 80px 14px',alignItems:'center',gap:12,
                      width:'100%',padding:'8px 10px',marginBottom:6,
                      border:'1px solid var(--rule)',background:'transparent',cursor:'pointer',textAlign:'left'}}>
                    <div style={{width:36,height:36,background:d.parent.art,position:'relative'}}>
                      <Ic.Disc width={14} height={14} style={{position:'absolute',top:11,left:11,color:'rgba(0,0,0,.55)'}}/>
                    </div>
                    <div style={{minWidth:0}}>
                      <div style={{fontSize:12,fontWeight:500,whiteSpace:'nowrap',overflow:'hidden',textOverflow:'ellipsis'}}>{d.parent.title}</div>
                      <div className="ff-mono" style={{fontSize:10,color:'var(--ink-3)',marginTop:2}}>{d.parent.artist} · {d.parent.year}</div>
                    </div>
                    <span className="ff-mono upper" style={{fontSize:9,letterSpacing:'.1em',color:'#7d3da8',fontWeight:600,textAlign:'right'}}>{d.kind}</span>
                    <Ic.Right width={12} height={12} style={{color:'var(--ink-3)'}}/>
                  </button>
                );
                return (
                  <div key={i} className="ff-mono" style={{fontSize:11,color:'var(--ink-2)',padding:'6px 10px',border:'1px dashed var(--rule)',marginBottom:6}}>
                    {d.kind.toUpperCase()} · external source
                  </div>
                );
              })}
            </>
          )}

          {siblings.length>0 && (
            <>
              <div className="ff-mono upper" style={{fontSize:9,color:'var(--ink-3)',letterSpacing:'.12em',marginTop:18,marginBottom:10}}>
                OTHER RECORDINGS OF THIS WORK · {siblings.length}
              </div>
              {siblings.map(sib=>(
                <button key={sib.id} onClick={()=>{ window.dispatchEvent(new CustomEvent('astro-open-recording',{detail:{id:sib.id}})); }}
                  style={{display:'grid',gridTemplateColumns:'32px 1fr 60px',alignItems:'center',gap:10,
                    width:'100%',padding:'7px 10px',marginBottom:4,
                    border:'1px solid var(--rule-soft)',background:'transparent',cursor:'pointer',textAlign:'left'}}>
                  <div style={{width:28,height:28,background:sib.art}}/>
                  <div style={{minWidth:0,fontSize:12}}>{sib.title} <span style={{color:'var(--ink-3)'}}>· {sib.artist}</span></div>
                  <span className="ff-mono num" style={{fontSize:10,color:'var(--ink-3)',textAlign:'right'}}>{sib.year}</span>
                </button>
              ))}
            </>
          )}
        </div>

        {/* Releases this recording appears on */}
        {releases.length>0 && (
          <div style={{padding:'20px 28px',borderBottom:'1px solid var(--rule)'}}>
            <div className="ff-mono upper" style={{fontSize:9,color:'var(--ink-3)',letterSpacing:'.12em',marginBottom:12}}>APPEARS ON · {releases.length} RELEASE{releases.length>1?'S':''}</div>
            {releases.map(rl => (
              <div key={rl.id} style={{display:'grid',gridTemplateColumns:'1fr 80px',gap:12,padding:'8px 0',borderTop:'1px solid var(--rule-soft)',alignItems:'center'}}>
                <div>
                  <div style={{fontSize:13}}>{rl.title}</div>
                  <div className="ff-mono" style={{fontSize:10,color:'var(--ink-3)',marginTop:2}}>{rl.artist}</div>
                </div>
                <span className="ff-mono upper" style={{fontSize:9,letterSpacing:'.1em',color:'var(--ink-3)',textAlign:'right'}}>{rl.kind}</span>
              </div>
            ))}
          </div>
        )}

        {/* Platform popularity */}
        {(() => {
          const pop = window.recordingPlatformDetails ? window.recordingPlatformDetails(r) : null;
          if (!pop) return null;
          const tiles = [
            {l:'SPOTIFY', c:'#1ed760', v:pop.spotifyPopularity},
            {l:'TIDAL',   c:'#000000', v:pop.tidalPopularity, sub:pop.tidalQuality},
            {l:'QOBUZ',   c:'#0070d8', v:pop.qobuzPopularity, sub:pop.qobuzHires?'Hi-Res':null},
            {l:'AM',      c:'#fa233b', v:pop.appleMusicScore, sub:pop.appleHasAtmos?'Atmos':null},
          ].filter(t => typeof t.v === 'number');
          if (tiles.length === 0) return null;
          return (
            <div style={{padding:'20px 28px',borderBottom:'1px solid var(--rule)'}}>
              <div style={{display:'flex',justifyContent:'space-between',alignItems:'baseline',marginBottom:12}}>
                <div className="ff-mono upper" style={{fontSize:9,color:'var(--ink-3)',letterSpacing:'.12em'}}>
                  PLATFORM POPULARITY{pop.fromRelease ? ' · INHERITED FROM RELEASE' : ''}
                </div>
                {pop.lastUpdated && <div className="ff-mono" style={{fontSize:9,color:'var(--ink-4)'}}>updated {pop.lastUpdated}</div>}
              </div>
              <div style={{display:'grid',gridTemplateColumns:`repeat(${tiles.length},1fr)`,gap:0,border:'1px solid var(--rule-soft)'}}>
                {tiles.map((t,i)=>(
                  <div key={t.l} style={{padding:'12px 14px',borderRight:i<tiles.length-1?'1px solid var(--rule-soft)':'none'}}>
                    <div style={{display:'flex',alignItems:'center',gap:6,marginBottom:6}}>
                      <span style={{width:7,height:7,background:t.c,display:'inline-block',borderRadius:1}}/>
                      <span className="ff-mono upper" style={{fontSize:9,fontWeight:600,letterSpacing:'.08em'}}>{t.l}</span>
                    </div>
                    <div className="ff-display num" style={{fontSize:22,fontWeight:600,letterSpacing:'-0.03em',lineHeight:1}}>
                      {t.v}<span style={{fontSize:11,color:'var(--ink-3)',fontWeight:400}}>/100</span>
                    </div>
                    <div style={{height:3,background:'var(--bg-2)',marginTop:6,position:'relative',overflow:'hidden'}}>
                      <div style={{height:'100%',width:`${t.v}%`,background:t.c}}/>
                    </div>
                    {t.sub && <div className="ff-mono upper" style={{fontSize:8,letterSpacing:'.1em',color:'var(--ink-3)',marginTop:6}}>{t.sub}</div>}
                  </div>
                ))}
              </div>
              {(pop.qobuzMaximumBitDepth || pop.spotifyExplicit) && (
                <div className="ff-mono" style={{fontSize:10,color:'var(--ink-3)',marginTop:8,display:'flex',gap:18,flexWrap:'wrap'}}>
                  {pop.qobuzMaximumBitDepth && <span>{pop.qobuzMaximumBitDepth}-bit / {pop.qobuzMaximumSamplingRate} kHz on Qobuz</span>}
                  {pop.spotifyExplicit && <span style={{color:'var(--ink)'}}>Explicit on Spotify</span>}
                </div>
              )}
            </div>
          );
        })()}

        {/* Linked music videos */}
        {window.RecordingVideosBlock ? (
          <div style={{ padding: '20px 28px', borderBottom: '1px solid var(--rule)' }}>
            <window.RecordingVideosBlock recordingTitle={r.title} recordingId={r.id} />
          </div>
        ) : null}

        {/* DSP distribution */}
        <div style={{padding:'20px 28px',borderBottom:'1px solid var(--rule)'}}>
          <div className="ff-mono upper" style={{fontSize:9,color:'var(--ink-3)',letterSpacing:'.12em',marginBottom:12}}>STREAMS BY DSP · LAST 30 DAYS</div>
          {dsps.map(d=>(
            <div key={d.n} style={{display:'grid',gridTemplateColumns:'90px 1fr 50px',alignItems:'center',gap:12,padding:'5px 0'}}>
              <span style={{fontSize:12}}>{d.n}</span>
              <div style={{height:6,background:'var(--bg-2)',position:'relative'}}>
                <div style={{position:'absolute',inset:0,width:`${d.share*1.8}%`,maxWidth:'100%',background:'var(--ink)'}}/>
              </div>
              <span className="ff-mono num" style={{fontSize:11,textAlign:'right',color:'var(--ink-2)'}}>{d.plays}M</span>
            </div>
          ))}
        </div>

        {/* Activity */}
        <div style={{padding:'20px 28px 28px'}}>
          <div className="ff-mono upper" style={{fontSize:9,color:'var(--ink-3)',letterSpacing:'.12em',marginBottom:12}}>RECENT ACTIVITY</div>
          {[
            {when:'2 days ago', what:'DDEX ERN 4.3 delivered to 14 DSPs', who:'system'},
            {when:'8 days ago', what:'Linked to work T-308.901.522-0', who:'a.cohen'},
            {when:'14 days ago', what:'ISRC validated · IFPI registry', who:'system'},
            {when:'1 month ago', what:'Imported from label feed (Saint / Columbia)', who:'feed.sc'},
          ].map((ev,i)=>(
            <div key={i} style={{display:'grid',gridTemplateColumns:'90px 1fr 80px',gap:12,padding:'7px 0',borderTop: i?'1px solid var(--rule-soft)':'none',fontSize:12}}>
              <span className="ff-mono" style={{color:'var(--ink-3)',fontSize:10}}>{ev.when}</span>
              <span>{ev.what}</span>
              <span className="ff-mono" style={{color:'var(--ink-3)',fontSize:10,textAlign:'right'}}>{ev.who}</span>
            </div>
          ))}
        </div>
      </aside>

      {/* DDEX preview modal */}
      {modal === 'ddex' && (
        <RecordingActionModal kind="DDEX" title="Generate DDEX delivery" onClose={()=>setModal(null)}
          confirmLabel="Queue ERN delivery" onConfirm={()=>{ setModal(null); toast(`DDEX ERN draft created for "${r.title}"`); }}>
          <div style={{display:'grid',gridTemplateColumns:'auto 1fr',gap:'4px 14px',fontSize:12,marginBottom:14}}>
            <span style={{color:'var(--ink-3)'}}>Recording</span><span className="ff-mono" style={{color:'var(--ink)'}}>{r.title} · {r.artist}</span>
            <span style={{color:'var(--ink-3)'}}>ISRC</span><span className="ff-mono" style={{color:'var(--ink)'}}>{(window.isrcDisplay ? window.isrcDisplay(r.isrc) : r.isrc)}</span>
            <span style={{color:'var(--ink-3)'}}>Format</span><span className="ff-mono" style={{color:'var(--ink)'}}>DDEX ERN 4.3</span>
            <span style={{color:'var(--ink-3)'}}>Filename</span><span className="ff-mono" style={{color:'var(--ink)'}}>ERN_{r.isrc.replace(/-/g,'')}_{new Date().toISOString().slice(0,10).replace(/-/g,'')}.xml</span>
            <span style={{color:'var(--ink-3)'}}>DSPs</span><span className="ff-mono" style={{color:'var(--ink)'}}>14 destinations · current routing</span>
          </div>
          <div style={{padding:'10px 12px',background:'var(--bg-2)',border:'1px solid var(--rule-soft)',fontSize:11,color:'var(--ink-2)',lineHeight:1.5}}>
            Drafts a DDEX ERN message with this recording's metadata, audio reference, and current release linkage. Review the draft in <span className="ff-mono" style={{color:'var(--ink)'}}>Transmissions → DDEX</span> before sending.
          </div>
        </RecordingActionModal>
      )}

      {/* Add to release */}
      {modal === 'addToRelease' && (
        <RecordingActionModal kind="RELEASES" title="Add to release" onClose={()=>setModal(null)}
          confirmLabel="Save linkage" onConfirm={()=>{ setModal(null); toast(`"${r.title}" added to ${linkedReleaseIds.length} release${linkedReleaseIds.length===1?'':'s'}`); }}>
          <div className="ff-mono upper" style={{fontSize:9,letterSpacing:'.12em',color:'var(--ink-3)',marginBottom:8}}>{linkedReleaseIds.length} LINKED · {(typeof RELEASES_X !== 'undefined' ? RELEASES_X.length : 0)} AVAILABLE</div>
          <div style={{border:'1px solid var(--rule)',maxHeight:300,overflow:'auto'}}>
            {(typeof RELEASES_X !== 'undefined' ? RELEASES_X : []).slice(0, 12).map((rel, i) => {
              const checked = linkedReleaseIds.includes(rel.id);
              return (
                <label key={rel.id} style={{display:'grid',gridTemplateColumns:'24px 36px 1fr auto',gap:12,alignItems:'center',padding:'10px 12px',borderBottom:i<11?'1px solid var(--rule-soft)':'none',cursor:'pointer'}}>
                  <input type="checkbox" checked={checked} onChange={()=>setLinkedReleaseIds(s => s.includes(rel.id) ? s.filter(x=>x!==rel.id) : [...s, rel.id])} style={{margin:0}}/>
                  <div style={{width:32,height:32,background:rel.art||rel.color||'var(--ink-3)'}}/>
                  <div>
                    <div style={{fontSize:13,fontWeight:600}}>{rel.title}</div>
                    <div className="ff-mono" style={{fontSize:10,color:'var(--ink-3)'}}>{rel.artist} · {rel.year || (rel.date||'').slice(0,4)} · {rel.upc}</div>
                  </div>
                  {checked && <span className="ff-mono upper" style={{fontSize:9,color:'#5b8a3a',letterSpacing:'.08em',fontWeight:600}}>✓ LINKED</span>}
                </label>
              );
            })}
          </div>
        </RecordingActionModal>
      )}

      {/* Link work */}
      {modal === 'linkWork' && (
        <RecordingActionModal kind="WORKS" title="Link to work" onClose={()=>setModal(null)}
          confirmLabel="Save linkage" onConfirm={()=>{ setModal(null); toast(`"${r.title}" linked to ${linkedWorkIds.length} work${linkedWorkIds.length===1?'':'s'}`); }}>
          <div className="ff-mono upper" style={{fontSize:9,letterSpacing:'.12em',color:'var(--ink-3)',marginBottom:8}}>{linkedWorkIds.length} LINKED · primary work + samples / interpolations</div>
          <div style={{border:'1px solid var(--rule)',maxHeight:300,overflow:'auto'}}>
            {(typeof ALL_WORKS !== 'undefined' ? ALL_WORKS : []).slice(0, 14).map((w, i, arr) => {
              const checked = linkedWorkIds.includes(w.id);
              const isPrimary = w.id === r.workId;
              return (
                <label key={w.id} style={{display:'grid',gridTemplateColumns:'24px 1fr auto',gap:12,alignItems:'center',padding:'10px 12px',borderBottom:i<arr.length-1?'1px solid var(--rule-soft)':'none',cursor:'pointer'}}>
                  <input type="checkbox" checked={checked} disabled={isPrimary} onChange={()=>setLinkedWorkIds(s => s.includes(w.id) ? s.filter(x=>x!==w.id) : [...s, w.id])} style={{margin:0}}/>
                  <div>
                    <div style={{fontSize:13,fontWeight:600}}>{w.title}</div>
                    <div className="ff-mono" style={{fontSize:10,color:'var(--ink-3)'}}>{(w.writers||[]).map(x=>x.name).slice(0,2).join(', ')} · ISWC {window.iswcDisplay ? window.iswcDisplay(w.iswc) : w.iswc || '—'}</div>
                  </div>
                  {isPrimary ? (
                    <span className="ff-mono upper" style={{fontSize:9,color:'var(--ink-2)',letterSpacing:'.08em',fontWeight:600}}>PRIMARY</span>
                  ) : checked ? (
                    <span className="ff-mono upper" style={{fontSize:9,color:'#5b8a3a',letterSpacing:'.08em',fontWeight:600}}>✓ SAMPLE</span>
                  ) : null}
                </label>
              );
            })}
          </div>
        </RecordingActionModal>
      )}

      {/* Flag */}
      {modal === 'flag' && (
        <RecordingActionModal kind="REVIEW" title={flagged ? 'Remove review flag' : 'Flag for review'} onClose={()=>setModal(null)}
          confirmLabel={flagged ? 'Remove flag' : 'Flag recording'}
          onConfirm={()=>{ setFlagged(!flagged); setModal(null); toast(flagged ? `"${r.title}" unflagged` : `"${r.title}" flagged for review`); }}>
          {flagged ? (
            <p style={{fontSize:13,color:'var(--ink-2)',lineHeight:1.55,marginBottom:0}}>
              This recording is currently flagged. Removing the flag closes the review item and re-enables CWR / DDEX transmissions.
            </p>
          ) : (
            <>
              <p style={{fontSize:13,color:'var(--ink-2)',lineHeight:1.55,marginBottom:14}}>
                Flagging pauses outbound transmissions for this recording until a rights admin clears the flag. The recording stays visible in the catalog and statements continue to ingest as normal.
              </p>
              <div className="ff-mono upper" style={{fontSize:9,letterSpacing:'.12em',color:'var(--ink-3)',marginBottom:6}}>REASON</div>
              <select defaultValue="splits-conflict" className="ff-mono"
                style={{width:'100%',padding:'9px 10px',fontSize:12,background:'var(--bg)',color:'var(--ink)',border:'1px solid var(--rule)'}}>
                <option value="splits-conflict">Splits conflict / dispute</option>
                <option value="metadata">Metadata accuracy</option>
                <option value="rights">Rights ownership unclear</option>
                <option value="sample">Uncleared sample / interpolation</option>
                <option value="other">Other</option>
              </select>
            </>
          )}
        </RecordingActionModal>
      )}

      {/* Archive */}
      {modal === 'archive' && (
        <RecordingActionModal kind="ARCHIVE" title={archived ? 'Restore from archive' : 'Archive recording'}
          onClose={()=>setModal(null)} danger={!archived}
          confirmLabel={archived ? 'Restore recording' : 'Archive recording'}
          onConfirm={()=>{ setArchived(!archived); setModal(null); toast(archived ? `"${r.title}" restored` : `"${r.title}" archived`); }}>
          <p style={{fontSize:13,color:'var(--ink-2)',lineHeight:1.55,marginBottom:14}}>
            {archived
              ? 'Restoring this recording re-enables outbound CWR / DDEX transmissions and surfaces it in catalog filters again. Linked works, releases, and royalty history are unchanged.'
              : 'Archiving hides this recording from default catalog views and pauses outbound transmissions. The ISRC and metadata remain queryable, and royalty history is preserved. You can restore at any time.'}
          </p>
          <div style={{padding:'10px 12px',background:'var(--bg-2)',border:'1px solid var(--rule-soft)',fontSize:11,color:'var(--ink-2)',display:'grid',gridTemplateColumns:'auto 1fr',gap:'4px 12px'}}>
            <span style={{color:'var(--ink-3)'}}>ISRC</span><span className="ff-mono" style={{color:'var(--ink)'}}>{(window.isrcDisplay ? window.isrcDisplay(r.isrc) : r.isrc)}</span>
            <span style={{color:'var(--ink-3)'}}>Linked works</span><span className="ff-mono" style={{color:'var(--ink)'}}>{linkedWorkIds.length}</span>
            <span style={{color:'var(--ink-3)'}}>Linked releases</span><span className="ff-mono" style={{color:'var(--ink)'}}>{linkedReleaseIds.length}</span>
          </div>
        </RecordingActionModal>
      )}
    </>
  );
}

// ───────────────────────────────────────────────────────────── RELEASES + RELEASE GROUPS
//
// Two-level model (matches DDEX ERN's parent work + child release pattern):
//
//   RELEASE_GROUPS — the parent "Release" — title, primary artist, genre, baseline ©
//   RELEASES       — each issued Edition of the parent (Digital Original, CD, Vinyl LP,
//                    Deluxe, Japan Edition, etc.) with its own UPC, format, territory,
//                    release date, retail price, ℗/© line, label, distribution, status.
//
// One Release Group can have many Editions. Tracklist lives on the group; each edition
// stores {includedTrackIds[], bonusTracks[], order[]} as overrides. ISRCs are shared by
// default (same recording), with per-edition override allowed for remasters / re-records.
//
// On the catalog list, we render ONE row per Release Group with a "5 EDITIONS" chip.
// Editions are listed inside the release detail page.

const RELEASE_GROUPS = [
  {id:'rg_01', title:'A Seat at the Table', artist:'Solange', label:'Saint / Columbia', marketingLabel:'Saint Records',
    type:'Album', genre:'R&B/Soul', subGenre:'Neo-Soul', language:'English', parental:'Explicit',
    pLineYear:2016, cLineYear:2016, art:'#f4d34a',
    originalDate:'2016-09-30'},
  {id:'rg_02', title:'Crush', artist:'Floating Points', label:'Ninja Tune', marketingLabel:'Ninja Tune',
    type:'Album', genre:'Electronic', subGenre:'Ambient', language:'English', parental:'NotExplicit',
    pLineYear:2019, cLineYear:2019, art:'#2a4d8f',
    originalDate:'2019-10-18'},
  {id:'rg_03', title:'Desire, I Want To Turn Into You', artist:'Caroline Polachek', label:'Perpetual Novice', marketingLabel:'Perpetual Novice',
    type:'Album', genre:'Pop', subGenre:'Art Pop', language:'English', parental:'NotExplicit',
    pLineYear:2023, cLineYear:2023, art:'#e8693a',
    originalDate:'2023-02-14'},
  {id:'rg_04', title:'Devotion', artist:'Tirzah', label:'Domino', marketingLabel:'Domino',
    type:'Album', genre:'R&B/Soul', subGenre:'Alt R&B', language:'English', parental:'NotExplicit',
    pLineYear:2018, cLineYear:2018, art:'#5a3d1c',
    originalDate:'2018-08-10'},
  {id:'rg_05', title:'Far In', artist:'Helado Negro', label:'4AD', marketingLabel:'4AD',
    type:'Album', genre:'Indie', subGenre:'Latin Indie', language:'Spanish', parental:'NotExplicit',
    pLineYear:2021, cLineYear:2021, art:'#b04a3a',
    originalDate:'2021-10-22'},
  {id:'rg_06', title:'BUBBA', artist:'KAYTRANADA', label:'RCA', marketingLabel:'RCA',
    type:'Album', genre:'Electronic', subGenre:'House', language:'English', parental:'Explicit',
    pLineYear:2019, cLineYear:2019, art:'#5c2a8a',
    originalDate:'2019-12-13'},
  {id:'rg_07', title:'EP2', artist:'Yaeji', label:'Godmode', marketingLabel:'Godmode',
    type:'EP', genre:'Electronic', subGenre:'House', language:'English', parental:'NotExplicit',
    pLineYear:2017, cLineYear:2017, art:'#0f4c2a',
    originalDate:'2017-11-17'},
  {id:'rg_08', title:'I AM', artist:'Sault', label:'Forever Living Originals', marketingLabel:'Forever Living',
    type:'Album', genre:'R&B/Soul', subGenre:'Funk', language:'English', parental:'NotExplicit',
    pLineYear:2024, cLineYear:2024, art:'#d4a02a',
    originalDate:'2024-11-01'},
  // Compilations & VA collections — single-edition groups
  {id:'rg_comp_qro', title:'Quarantine Mixtape Vol. 4', artist:'Various Artists', label:'Saint / Columbia', marketingLabel:'Saint Records',
    type:'Compilation', genre:'R&B/Soul', subGenre:'Various', language:'English', parental:'NotExplicit',
    pLineYear:2020, cLineYear:2020, art:'#a35d3a',
    originalDate:'2020-06-12'},
  {id:'rg_kexp_v3', title:'On the Roof, Vol. 3', artist:'Various Artists', label:'Nonesuch', marketingLabel:'KEXP',
    type:'Live comp', genre:'Indie', subGenre:'Various', language:'English', parental:'NotExplicit',
    pLineYear:2022, cLineYear:2022, art:'#5a3d1c',
    originalDate:'2022-04-08'},
  {id:'rg_summer22', title:'Summer Mix \u201922', artist:'Various Artists', label:'RCA', marketingLabel:'RCA',
    type:'Compilation', genre:'Electronic', subGenre:'Various', language:'English', parental:'Explicit',
    pLineYear:2022, cLineYear:2022, art:'#e8693a',
    originalDate:'2022-06-21'},
  // Companion EPs / remix EPs that share a parent group
  {id:'rg_kicki', title:'KiCk i', artist:'Arca', label:'XL Recordings', marketingLabel:'XL Recordings',
    type:'Album', genre:'Electronic', subGenre:'Experimental', language:'Spanish', parental:'Explicit',
    pLineYear:2020, cLineYear:2020, art:'#2a2a2a',
    originalDate:'2020-06-26'},
  {id:'rg_blackis', title:'UNTITLED (Black Is)', artist:'Sault', label:'Forever Living Originals', marketingLabel:'Forever Living',
    type:'Album', genre:'R&B/Soul', subGenre:'Funk', language:'English', parental:'NotExplicit',
    pLineYear:2020, cLineYear:2020, art:'#0f4c2a',
    originalDate:'2020-06-19'},
  {id:'rg_fatigue', title:'Fatigue', artist:'L\u2019Rain', label:'Mexican Summer', marketingLabel:'Mexican Summer',
    type:'Album', genre:'Indie', subGenre:'Experimental', language:'English', parental:'NotExplicit',
    pLineYear:2021, cLineYear:2021, art:'#3a6b3f',
    originalDate:'2021-06-25'},
];
window.RELEASE_GROUPS = RELEASE_GROUPS;

// Each release row is now an EDITION of a release group.
// The original 8 rows become the "Digital Original" edition; a few groups get
// extra editions to demo CD / Vinyl / Deluxe / regional variants.
const RELEASES = [
  // ── A Seat at the Table — 5 editions
  {id:'rl_01', groupId:'rg_01', editionLabel:'Digital Original', upc:'886447988104', title:'A Seat at the Table', artist:'Solange', label:'Saint / Columbia', type:'Album', format:'Digital', territory:'Worldwide', tracks:21, date:'2016-09-30', retailPrice:9.99, pLine:'℗ 2016 Saint Records / Columbia', cLine:'© 2016 Saint Records', status:'live', art:'#f4d34a', ddex:'delivered', dsps:14, dspList:['Spotify','Apple','YouTube','Amazon','Tidal','Deezer']},
  {id:'rl_01b', groupId:'rg_01', editionLabel:'CD',                upc:'886447988111', title:'A Seat at the Table', artist:'Solange', label:'Saint / Columbia', type:'Album', format:'CD',       territory:'Worldwide', tracks:21, date:'2016-10-07', retailPrice:14.99, pLine:'℗ 2016 Saint Records / Columbia', cLine:'© 2016 Saint Records', status:'live', art:'#f4d34a', ddex:'delivered', dsps:0, dspList:[]},
  {id:'rl_01c', groupId:'rg_01', editionLabel:'Vinyl 2LP',         upc:'886447988128', title:'A Seat at the Table', artist:'Solange', label:'Saint / Columbia', type:'Album', format:'Vinyl',    territory:'Worldwide', tracks:21, date:'2017-01-20', retailPrice:34.99, pLine:'℗ 2017 Saint Records / Columbia', cLine:'© 2017 Saint Records', status:'live', art:'#f4d34a', ddex:'delivered', dsps:0, dspList:[]},
  {id:'rl_01d', groupId:'rg_01', editionLabel:'Deluxe (Digital)',  upc:'886447988135', title:'A Seat at the Table', artist:'Solange', label:'Saint / Columbia', type:'Album', format:'Digital', territory:'Worldwide', tracks:23, date:'2017-09-30', retailPrice:12.99, pLine:'℗ 2017 Saint Records / Columbia', cLine:'© 2017 Saint Records', status:'live', art:'#f4d34a', ddex:'delivered', dsps:14, dspList:['Spotify','Apple','YouTube','Amazon','Tidal','Deezer'], bonusTracks:2},
  {id:'rl_01e', groupId:'rg_01', editionLabel:'Japan Edition',     upc:'4988001112079', title:'A Seat at the Table', artist:'Solange', label:'Sony Japan',          type:'Album', format:'CD',       territory:'Japan',    tracks:22, date:'2016-11-23', retailPrice:22.99, pLine:'℗ 2016 Sony Music Japan',          cLine:'© 2016 Sony Music Japan',         status:'live', art:'#f4d34a', ddex:'delivered', dsps:0, dspList:[], bonusTracks:1},

  // ── Crush — 2 editions
  {id:'rl_02',  groupId:'rg_02', editionLabel:'Digital Original', upc:'5054429143966', title:'Crush', artist:'Floating Points', label:'Ninja Tune', type:'Album', format:'Digital', territory:'Worldwide', tracks:9, date:'2019-10-18', retailPrice:9.99, pLine:'℗ 2019 Ninja Tune', cLine:'© 2019 Ninja Tune', status:'live', art:'#2a4d8f', ddex:'delivered', dsps:14, dspList:['Spotify','Apple','YouTube','Amazon','Tidal','Deezer']},
  {id:'rl_02b', groupId:'rg_02', editionLabel:'Vinyl LP',         upc:'5054429143973', title:'Crush', artist:'Floating Points', label:'Ninja Tune', type:'Album', format:'Vinyl',   territory:'Worldwide', tracks:9, date:'2019-11-01', retailPrice:27.99, pLine:'℗ 2019 Ninja Tune', cLine:'© 2019 Ninja Tune', status:'live', art:'#2a4d8f', ddex:'delivered', dsps:0, dspList:[]},

  // ── Singles & remaining albums — single edition each
  {id:'rl_03', groupId:'rg_03', editionLabel:'Digital Original', upc:'196292142457', title:'Desire, I Want To Turn Into You', artist:'Caroline Polachek', label:'Perpetual Novice', type:'Album', format:'Digital', territory:'Worldwide', tracks:12, date:'2023-02-14', retailPrice:9.99, pLine:'℗ 2023 Perpetual Novice', cLine:'© 2023 Perpetual Novice', status:'live', art:'#e8693a', ddex:'delivered', dsps:14, dspList:['Spotify','Apple','YouTube','Amazon','Tidal','Deezer']},
  {id:'rl_04', groupId:'rg_04', editionLabel:'Digital Original', upc:'5060766761886', title:'Devotion',                       artist:'Tirzah',            label:'Domino',           type:'Album', format:'Digital', territory:'Worldwide', tracks:11, date:'2018-08-10', retailPrice:9.99, pLine:'℗ 2018 Domino', cLine:'© 2018 Domino', status:'live', art:'#5a3d1c', ddex:'delivered', dsps:13, dspList:['Spotify','Apple','YouTube','Amazon','Tidal','Deezer']},
  {id:'rl_05', groupId:'rg_05', editionLabel:'Digital Original', upc:'191404110034', title:'Far In',                          artist:'Helado Negro',      label:'4AD',              type:'Album', format:'Digital', territory:'Worldwide', tracks:14, date:'2021-10-22', retailPrice:9.99, pLine:'℗ 2021 4AD', cLine:'© 2021 4AD', status:'live', art:'#b04a3a', ddex:'delivered', dsps:14, dspList:['Spotify','Apple','YouTube','Amazon','Tidal','Deezer']},
  {id:'rl_06', groupId:'rg_06', editionLabel:'Digital Original', upc:'196925100229', title:'BUBBA',                           artist:'KAYTRANADA',        label:'RCA',              type:'Album', format:'Digital', territory:'Worldwide', tracks:17, date:'2019-12-13', retailPrice:9.99, pLine:'℗ 2019 RCA', cLine:'© 2019 RCA', status:'scheduled', art:'#5c2a8a', ddex:'queued',    dsps:0,  dspList:[]},
  {id:'rl_07', groupId:'rg_07', editionLabel:'Digital Original', upc:'190296994265', title:'EP2',                             artist:'Yaeji',             label:'Godmode',          type:'EP',    format:'Digital', territory:'Worldwide', tracks:7,  date:'2017-11-17', retailPrice:5.99, pLine:'℗ 2017 Godmode', cLine:'© 2017 Godmode', status:'live', art:'#0f4c2a', ddex:'delivered', dsps:14, dspList:['Spotify','Apple','YouTube','Amazon','Tidal','Deezer']},
  {id:'rl_08', groupId:'rg_08', editionLabel:'Digital Original', upc:'5060854191625', title:'I AM',                           artist:'Sault',             label:'Forever Living Originals', type:'Album', format:'Digital', territory:'Worldwide', tracks:18, date:'2024-11-01', retailPrice:9.99, pLine:'℗ 2024 Forever Living', cLine:'© 2024 Forever Living', status:'live', art:'#d4a02a', ddex:'delivered', dsps:11, dspList:['Spotify','Apple','YouTube','Tidal','Deezer']},

  // ── Bubba / KAYTRANADA — extra editions (Deluxe + Remixes EP)
  {id:'rl_bubba_dlx', groupId:'rg_06', editionLabel:'Deluxe',  upc:'196925100236', title:'BUBBA (Deluxe)',  artist:'KAYTRANADA', label:'RCA', type:'Album', format:'Digital', territory:'Worldwide', tracks:19, date:'2020-06-19', retailPrice:11.99, pLine:'℗ 2020 RCA', cLine:'© 2020 RCA', status:'live', art:'#5c2a8a', ddex:'delivered', dsps:14, dspList:['Spotify','Apple','YouTube','Amazon','Tidal','Deezer'], bonusTracks:2},
  {id:'rl_bubba_rmx', groupId:'rg_06', editionLabel:'Remixes', upc:'196925100243', title:'BUBBA Remixes',   artist:'KAYTRANADA', label:'RCA', type:'EP',    format:'Digital', territory:'Worldwide', tracks:6,  date:'2020-12-04', retailPrice:5.99,  pLine:'℗ 2020 RCA', cLine:'© 2020 RCA', status:'live', art:'#7d3da8', ddex:'delivered', dsps:14, dspList:['Spotify','Apple','YouTube','Amazon','Tidal','Deezer']},

  // ── Desire / Caroline Polachek — Bunny Remixes EP
  {id:'rl_bunny_rmx', groupId:'rg_03', editionLabel:'Remixes', upc:'196292142464', title:'Bunny Remixes',   artist:'Caroline Polachek', label:'Perpetual Novice', type:'EP', format:'Digital', territory:'Worldwide', tracks:5, date:'2022-08-19', retailPrice:5.99, pLine:'℗ 2022 Perpetual Novice', cLine:'© 2022 Perpetual Novice', status:'live', art:'#cc4d2a', ddex:'delivered', dsps:14, dspList:['Spotify','Apple','YouTube','Amazon','Tidal','Deezer']},

  // ── Various-artists compilations — each its own group
  {id:'rl_comp_qro',     groupId:'rg_comp_qro',  editionLabel:'Digital Original', upc:'886447988142', title:'Quarantine Mixtape Vol. 4', artist:'Various Artists', label:'Saint / Columbia', type:'Compilation', format:'Digital', territory:'Worldwide', tracks:14, date:'2020-06-12', retailPrice:7.99, pLine:'℗ 2020 Saint Records', cLine:'© 2020 Saint Records', status:'live', art:'#a35d3a', ddex:'delivered', dsps:13, dspList:['Spotify','Apple','YouTube','Amazon','Tidal','Deezer']},
  {id:'rl_kexp_v3',      groupId:'rg_kexp_v3',   editionLabel:'Digital Original', upc:'075597913040', title:'On the Roof, Vol. 3',       artist:'Various Artists', label:'Nonesuch',         type:'Live comp',   format:'Digital', territory:'Worldwide', tracks:11, date:'2022-04-08', retailPrice:8.99, pLine:'℗ 2022 Nonesuch / KEXP', cLine:'© 2022 Nonesuch / KEXP', status:'live', art:'#5a3d1c', ddex:'delivered', dsps:13, dspList:['Spotify','Apple','YouTube','Amazon','Tidal','Deezer']},
  {id:'rl_summer_mix_22',groupId:'rg_summer22',  editionLabel:'Digital Original', upc:'196925100250', title:'Summer Mix \u201922',       artist:'Various Artists', label:'RCA',              type:'Compilation', format:'Digital', territory:'Worldwide', tracks:18, date:'2022-06-21', retailPrice:8.99, pLine:'℗ 2022 RCA', cLine:'© 2022 RCA', status:'live', art:'#e8693a', ddex:'delivered', dsps:14, dspList:['Spotify','Apple','YouTube','Amazon','Tidal','Deezer']},

  // ── Albums missing from the original 8: Arca, Sault (Black Is), L'Rain
  {id:'rl_kicki',   groupId:'rg_kicki',   editionLabel:'Digital Original', upc:'634904086428', title:'KiCk i',                artist:'Arca',     label:'XL Recordings',           type:'Album', format:'Digital', territory:'Worldwide', tracks:12, date:'2020-06-26', retailPrice:9.99, pLine:'℗ 2020 XL Recordings',           cLine:'© 2020 XL Recordings',           status:'live', art:'#2a2a2a', ddex:'delivered', dsps:14, dspList:['Spotify','Apple','YouTube','Amazon','Tidal','Deezer']},
  {id:'rl_blackis', groupId:'rg_blackis', editionLabel:'Digital Original', upc:'5060854190017', title:'UNTITLED (Black Is)',    artist:'Sault',    label:'Forever Living Originals', type:'Album', format:'Digital', territory:'Worldwide', tracks:20, date:'2020-06-19', retailPrice:9.99, pLine:'℗ 2020 Forever Living',          cLine:'© 2020 Forever Living',          status:'live', art:'#0f4c2a', ddex:'delivered', dsps:11, dspList:['Spotify','Apple','YouTube','Tidal','Deezer']},
  {id:'rl_fatigue', groupId:'rg_fatigue', editionLabel:'Digital Original', upc:'184923011029', title:'Fatigue',                artist:'L\u2019Rain', label:'Mexican Summer',          type:'Album', format:'Digital', territory:'Worldwide', tracks:11, date:'2021-06-25', retailPrice:9.99, pLine:'℗ 2021 Mexican Summer',         cLine:'© 2021 Mexican Summer',         status:'live', art:'#3a6b3f', ddex:'delivered', dsps:14, dspList:['Spotify','Apple','YouTube','Amazon','Tidal','Deezer']},
];
window.RELEASES = RELEASES;

// Helper — get all editions of a release group, sorted by date
window.editionsOfGroup = (groupId) => RELEASES.filter(r => r.groupId === groupId)
  .sort((a,b) => (a.date||'').localeCompare(b.date||''));

// Helper — for a release row, find its parent group's metadata
window.releaseGroupOf = (release) => RELEASE_GROUPS.find(g => g.id === (release && release.groupId));

function ReleasesView({ go, labelFilter='' }) {
  const lf = (labelFilter||'').trim().toLowerCase();
  const [sortBy, setSortBy] = React.useState('default'); // default | popularity | trending | date | tracks

  // popularity scorer (composite avg) with safe fallback
  const popOf = (r) => {
    const fn = window.releasePopularityScore;
    if (!fn) return null;
    return fn(r.id);
  };

  // week-over-week delta from the trend series (composite avg of all 4 platforms)
  const trendDelta = (r) => {
    const series = (window.RELEASE_POPULARITY_TREND || {})[r.id];
    if (!series) return null;
    const pickLast = (arr) => arr && arr.length ? arr[arr.length-1] : null;
    const pickPrev = (arr) => arr && arr.length > 1 ? arr[arr.length-2] : null;
    const platforms = ['spotify','tidal','qobuz','apple'];
    const lastVals = platforms.map(k => pickLast(series[k])).filter(v => typeof v === 'number');
    const prevVals = platforms.map(k => pickPrev(series[k])).filter(v => typeof v === 'number');
    if (!lastVals.length || !prevVals.length) return null;
    const last = lastVals.reduce((a,b)=>a+b,0)/lastVals.length;
    const prev = prevVals.reduce((a,b)=>a+b,0)/prevVals.length;
    return Math.round((last - prev) * 10) / 10;
  };

  const filtered = lf ? RELEASES.filter(r => (r.label||'').toLowerCase().includes(lf)) : RELEASES;

  // Collapse to ONE row per release group — pick the earliest LIVE edition as the
  // canonical row (its date becomes the "original release" date shown on the card).
  // Stash {editionCount, totalDsps} so the card can show a chip + DSPs across all editions.
  const grouped = React.useMemo(() => {
    const byGroup = new Map();
    for (const r of filtered) {
      const k = r.groupId || r.id;
      if (!byGroup.has(k)) byGroup.set(k, []);
      byGroup.get(k).push(r);
    }
    const rows = [];
    for (const [k, eds] of byGroup) {
      const live = eds.filter(e => e.status === 'live').sort((a,b) => (a.date||'').localeCompare(b.date||''));
      const sorted = (live.length ? live : eds.slice().sort((a,b) => (a.date||'').localeCompare(b.date||'')));
      const primary = sorted[0];
      const totalDsps = Math.max(...eds.map(e => e.dsps || 0), 0);
      const anyDelivered = eds.some(e => e.ddex === 'delivered');
      rows.push({...primary, editionCount: eds.length, totalDsps, ddexAggregate: anyDelivered ? 'delivered' : (primary.ddex || 'queued')});
    }
    return rows;
  }, [filtered]);

  const list = React.useMemo(() => {
    const out = grouped.slice();
    if (sortBy === 'popularity') out.sort((a,b)=>(popOf(b)??-1)-(popOf(a)??-1));
    if (sortBy === 'trending')   out.sort((a,b)=>(trendDelta(b)??-99)-(trendDelta(a)??-99));
    if (sortBy === 'date')       out.sort((a,b)=>(b.date||'').localeCompare(a.date||''));
    if (sortBy === 'tracks')     out.sort((a,b)=>(b.tracks||0)-(a.tracks||0));
    return out;
  }, [grouped, sortBy]);

  const SortBtn = ({ k, children }) => (
    <button onClick={()=>setSortBy(k)} className="ff-mono upper"
      style={{padding:'6px 10px',fontSize:10,letterSpacing:'.1em',
        background: sortBy===k ? 'var(--ink)' : 'transparent',
        color: sortBy===k ? 'var(--bg)' : 'var(--ink-2)',
        border:'1px solid var(--rule)',borderLeft:0,cursor:'pointer',fontWeight:600}}>
      {children}
    </button>
  );

  return (
    <>
      {/* Sort bar */}
      <div style={{display:'flex',alignItems:'center',justifyContent:'space-between',
        padding:'10px 0',borderTop:'1px solid var(--rule)',borderBottom:'1px solid var(--rule)',
        marginTop:0}}>
        <span className="ff-mono upper" style={{fontSize:10,letterSpacing:'.12em',color:'var(--ink-3)'}}>
          {list.length} RELEASE{list.length===1?'':'S'}
        </span>
        <div style={{display:'flex'}}>
          <span className="ff-mono upper" style={{fontSize:9,letterSpacing:'.12em',color:'var(--ink-3)',
            padding:'8px 12px',borderTop:'1px solid var(--rule)',borderBottom:'1px solid var(--rule)',
            borderLeft:'1px solid var(--rule)'}}>SORT</span>
          <SortBtn k="default">DEFAULT</SortBtn>
          <SortBtn k="popularity">POPULARITY</SortBtn>
          <SortBtn k="trending">TRENDING</SortBtn>
          <SortBtn k="date">DATE</SortBtn>
          <SortBtn k="tracks">TRACKS</SortBtn>
        </div>
      </div>

      <div style={{display:'grid',gridTemplateColumns:'repeat(auto-fill,minmax(260px,1fr))',gap:0,
        borderLeft:'1px solid var(--rule)',marginTop:0}}>
        {list.length === 0 && (
          <div style={{padding:'48px 24px',textAlign:'center',color:'var(--ink-3)',gridColumn:'1 / -1'}}>
            <div className="ff-mono upper" style={{fontSize:10,letterSpacing:'.1em'}}>NO RELEASES MATCH “{labelFilter}”</div>
          </div>
        )}
        {list.map((r,i)=>{
          const pop = popOf(r);
          const delta = trendDelta(r);
          // when trending sort is active, show the delta instead of the popularity badge
          const showTrending = sortBy === 'trending';
          return (
          <div key={r.id} onClick={()=>window.dispatchEvent(new CustomEvent('astro-open-release',{detail:{id:r.id}}))}
            style={{borderRight:'1px solid var(--rule)',borderBottom:'1px solid var(--rule)',cursor:'pointer'}}
            onMouseEnter={e=>e.currentTarget.style.background='var(--bg-2)'}
            onMouseLeave={e=>e.currentTarget.style.background='transparent'}>
            <div style={{aspectRatio:'1',background:r.art,position:'relative',overflow:'hidden'}}>
              <div style={{position:'absolute',inset:0,display:'flex',alignItems:'flex-end',padding:14}}>
                <span className="ff-display" style={{fontSize:34,fontWeight:700,letterSpacing:'-0.04em',
                  color:'rgba(0,0,0,.78)',lineHeight:.95}}>{r.title}</span>
              </div>
              <div style={{position:'absolute',top:10,left:14,right:14,display:'flex',justifyContent:'space-between',color:'rgba(0,0,0,.55)'}}>
                <span className="ff-mono num" style={{fontSize:9,fontWeight:600}}>№ {String(i+1).padStart(3,'0')}</span>
                <span className="ff-mono upper" style={{fontSize:9,fontWeight:600}}>{r.type}</span>
              </div>
              {/* Editions chip — bottom-left of cover */}
              {r.editionCount > 1 && (
                <div style={{position:'absolute',bottom:10,left:12}}>
                  <span className="ff-mono upper" style={{fontSize:9,fontWeight:600,padding:'3px 7px',
                    background:'rgba(0,0,0,.78)',color:'#fff',letterSpacing:'.08em'}}>
                    {r.editionCount} EDITIONS
                  </span>
                </div>
              )}
              {/* Trending delta badge — shown when trending sort active */}
              {showTrending && delta != null && (
                <div style={{position:'absolute',top:36,right:12,
                  background:'rgba(0,0,0,.78)',color:'#fff',
                  padding:'3px 7px',display:'flex',alignItems:'center',gap:5}}>
                  <span style={{color: delta > 0 ? '#5cd87a' : delta < 0 ? '#f5876b' : '#aaa',fontSize:11,lineHeight:1}}>
                    {delta > 0 ? '▲' : delta < 0 ? '▼' : '·'}
                  </span>
                  <span className="ff-mono num" style={{fontSize:10,fontWeight:600,letterSpacing:'.02em'}}>
                    {delta > 0 ? '+' : ''}{delta} WoW
                  </span>
                </div>
              )}
              {/* Popularity badge — top-right (default) */}
              {!showTrending && pop != null && (
                <div style={{position:'absolute',top:36,right:12,
                  background:'rgba(0,0,0,.78)',color:'#fff',
                  padding:'3px 7px',display:'flex',alignItems:'center',gap:5}}>
                  <span style={{width:5,height:5,background: pop>=75 ? '#5cd87a' : pop>=50 ? '#f5b942' : '#999',
                    display:'inline-block',borderRadius:'50%'}}/>
                  <span className="ff-mono num" style={{fontSize:10,fontWeight:600,letterSpacing:'.02em'}}>POP {pop}</span>
                </div>
              )}
              <div style={{position:'absolute',bottom:10,right:12}}>
                <Pill tone={r.ddexAggregate==='delivered'?'ok':'accent'} dot>{r.ddexAggregate}</Pill>
              </div>
            </div>
            <div style={{padding:'12px 14px'}}>
              <div style={{fontSize:14,fontWeight:600,marginBottom:2}}>{r.artist}</div>
              <div className="ff-mono" style={{fontSize:10,color:'var(--ink-3)'}}>{r.upc} · {r.tracks} tracks · {r.date}</div>
              <div style={{display:'flex',justifyContent:'space-between',marginTop:10,paddingTop:10,borderTop:'1px solid var(--rule-soft)'}}>
                <span className="ff-mono" style={{fontSize:10,color:'var(--ink-3)'}}>{r.label}</span>
                <span className="ff-mono num" style={{fontSize:11}}>{r.totalDsps}/14 DSPs</span>
              </div>
            </div>
          </div>
        );})}
      </div>
    </>
  );
}

// ───────────────────────────────────────────────────────────── PUBLISHERS
const PUBLISHERS = [
  {id:'pb_01',name:'PluralPub',aliases:'Pluralis Music · PluralPublishing LLC',cae:'00721998400',submitterCode:'PLP',pro:'ASCAP',territory:'Worldwide',works:1422,parent:'Pluralis Music Group',parentPub:'Pluralis Music Group',administrator:'Self',since:'2018'},
  {id:'pb_02',name:'Saint Music',aliases:'Saint Records Publ.',cae:'00891204771',submitterCode:'SNT',pro:'BMI',territory:'Worldwide ex. JP',works:884,parent:'Sony Music Publishing',parentPub:'Sony Music Publishing',administrator:'Sony Music Publishing',since:'2014'},
  {id:'pb_03',name:'Perpetual Novice',aliases:'PN Music',cae:'00644201339',submitterCode:'PPN',pro:'ASCAP',territory:'Worldwide',works:312,parent:'—',parentPub:null,administrator:'Kobalt',since:'2020'},
  {id:'pb_04',name:'TM Works',aliases:'Tirzah Music',cae:'00901881224',submitterCode:'TMW',pro:'PRS',territory:'GB · EU',works:62,parent:'Domino Publ.',parentPub:'Domino Publ.',administrator:'Domino Publ.',since:'2017'},
  {id:'pb_05',name:'KAY Publishing',aliases:'KAY Pub Inc',cae:'00733821244',submitterCode:'KAY',pro:'ASCAP',territory:'Worldwide',works:241,parent:'Concord Music',parentPub:'Concord Music',administrator:'Concord Music',since:'2016'},
  {id:'pb_06',name:'Forever Living',aliases:'FL Originals Publ.',cae:'00892100401',submitterCode:'FLV',pro:'BMI',territory:'Worldwide',works:521,parent:'—',parentPub:null,administrator:'Self',since:'2019'},
  {id:'pb_07',name:'Godmode Pub',aliases:'GM Music',cae:'00501228810',submitterCode:'GMD',pro:'BMI',territory:'US · CA',works:88,parent:'—',parentPub:null,administrator:'Wixen Music',since:'2015'},
  {id:'pb_08',name:'Helado Pub',aliases:'HN Pub LLC',cae:'00204418820',submitterCode:'HLD',pro:'ASCAP',territory:'Worldwide',works:184,parent:'4AD Publishing',parentPub:'4AD Publishing',administrator:'Beggars Music',since:'2018'},
];
window.__PUBLISHERS = PUBLISHERS;
window.PUBLISHERS = PUBLISHERS;

function PublishersView({ go, q='', filters }) {
  const f = filters || {};
  const ql = (q||'').trim().toLowerCase();
  const list = PUBLISHERS.filter(p => {
    if (ql) {
      const blob = `${p.name} ${p.aliases||''} ${p.cae||''} ${p.pro||''} ${p.territory||''} ${p.parent||''}`.toLowerCase();
      if (!blob.includes(ql)) return false;
    }
    // verified — derive: anyone with a CAE listed counts as verified
    if (f.verified === 'verified'   && !p.cae) return false;
    if (f.verified === 'unverified' && p.cae) return false;
    if (f.pro && f.pro !== 'any' && p.pro !== f.pro) return false;
    if (f.territory && f.territory !== 'any') {
      const t = (p.territory||'').toLowerCase();
      if (f.territory === 'WW' && !t.includes('worldwide')) return false;
      if (f.territory === 'US' && !(t.includes('us') || t.includes('worldwide'))) return false;
      if (f.territory === 'GB' && !(t.includes('gb') || t.includes('uk') || t.includes('worldwide'))) return false;
      if (f.territory === 'EU' && !(t.includes('eu') || t.includes('worldwide'))) return false;
    }
    return true;
  });
  if (list.length === 0) {
    return (
      <div style={{padding:'48px 24px',borderTop:'1px solid var(--rule)',borderLeft:'1px solid var(--rule)',borderRight:'1px solid var(--rule)',borderBottom:'1px solid var(--rule)',textAlign:'center',color:'var(--ink-3)'}}>
        <div className="ff-mono upper" style={{fontSize:10,letterSpacing:'.1em'}}>NO PUBLISHERS MATCH THE CURRENT FILTERS</div>
      </div>
    );
  }
  return (
    <div style={{display:'grid',gridTemplateColumns:'repeat(2,1fr)',gap:0,borderTop:'1px solid var(--rule)',
      borderLeft:'1px solid var(--rule)'}}>
      {list.map((p,i)=>(
        <div key={p.id} onClick={()=>window.dispatchEvent(new CustomEvent('astro-open-publisher',{detail:{id:p.id}}))}
          style={{padding:'20px 22px',borderRight:'1px solid var(--rule)',borderBottom:'1px solid var(--rule)',
          cursor:'pointer',display:'grid',gridTemplateColumns:'1fr auto',gap:18,alignItems:'flex-start'}}
          onMouseEnter={e=>e.currentTarget.style.background='var(--bg-2)'}
          onMouseLeave={e=>e.currentTarget.style.background='transparent'}>
          <div>
            <div style={{display:'flex',alignItems:'baseline',gap:10,marginBottom:6}}>
              <span className="ff-mono num" style={{fontSize:10,color:'var(--ink-4)'}}>№ {String(i+1).padStart(2,'0')}</span>
              <Pill tone="soft">PUBLISHER</Pill>
            </div>
            <div className="heading-swap ff-display" style={{fontSize:28,fontWeight:600,letterSpacing:'-0.03em',lineHeight:1,marginBottom:8}}>
              {window.PartyIndicator && <window.PartyIndicator party={p}/>}{p.name}
            </div>
            <div className="ff-mono" style={{fontSize:10,color:'var(--ink-3)',marginBottom:10}}>a.k.a. {p.aliases}</div>
            <div style={{display:'grid',gridTemplateColumns:'80px 1fr',rowGap:6,fontSize:11}} className="ff-mono">
              <span style={{color:'var(--ink-3)'}}>IPI</span><span>{p.cae}</span>
              <span style={{color:'var(--ink-3)'}}>TERRITORY</span><span>{p.territory}</span>
              <span style={{color:'var(--ink-3)'}}>PARENT</span><span>{p.parent}</span>
              <span style={{color:'var(--ink-3)'}}>SINCE</span><span>{p.since}</span>
            </div>
          </div>
          <div style={{textAlign:'right',borderLeft:'1px solid var(--rule-soft)',paddingLeft:18}}>
            <div className="ff-mono upper" style={{fontSize:9,color:'var(--ink-3)'}}>WORKS</div>
            <div className="ff-display num" style={{fontSize:42,fontWeight:600,letterSpacing:'-0.04em',lineHeight:1,marginBottom:8}}>
              {p.works.toLocaleString()}
            </div>
            <Spark data={sparkRoster.slice(i*2,i*2+18)} w={120} h={26} stroke="var(--ink-2)"/>
          </div>
        </div>
      ))}
    </div>
  );
}

// ───────────────────────────────────────────────────────────── LABELS
const LABELS = [
  {id:'lb_01',name:'Columbia',code:'COL',releases:842,artists:412,parent:'Sony Music',kind:'major',deals:'Distribution · Worldwide',color:'#0f1f4f'},
  {id:'lb_02',name:'Ninja Tune',code:'ZEN',releases:621,artists:188,parent:'Independent',kind:'indie',deals:'Distribution · Ex-NA via [PIAS]',color:'#1a1a1a'},
  {id:'lb_03',name:'Domino',code:'DNO',releases:512,artists:142,parent:'Independent',kind:'indie',deals:'Distribution · WW',color:'#5a3d1c'},
  {id:'lb_04',name:'4AD',code:'AAD',releases:401,artists:98,parent:'Beggars Group',kind:'indie',deals:'Distribution · WW',color:'#b04a3a'},
  {id:'lb_05',name:'RCA',code:'RCA',releases:1248,artists:622,parent:'Sony Music',kind:'major',deals:'Distribution · Worldwide',color:'#a02a4d'},
  {id:'lb_06',name:'XL Recordings',code:'XL',releases:288,artists:74,parent:'Beggars Group',kind:'indie',deals:'Distribution · WW',color:'#000'},
  {id:'lb_07',name:'Forever Living Originals',code:'FLO',releases:42,artists:8,parent:'Independent',kind:'indie',deals:'Direct · WW',color:'#d4a02a'},
  {id:'lb_08',name:'Godmode',code:'GMD',releases:88,artists:24,parent:'Independent',kind:'indie',deals:'Distribution · NA',color:'#0f4c2a'},
];
window.LABELS = LABELS;

function LabelsView({ go, q='', filters }) {
  const f = filters || {};
  const ql = (q||'').trim().toLowerCase();
  const isMajor = (l) => {
    // Explicit classification on the label record. Heuristic fallback only if
    // a label predates the `kind` field — known major-owned parent companies.
    if (l.kind === 'major') return true;
    if (l.kind === 'indie') return false;
    return /sony music|universal music|warner music|umg|wmg/i.test(l.parent||'');
  };
  const list = LABELS.filter(l => {
    if (ql) {
      const blob = `${l.name} ${l.code||''} ${l.parent||''} ${l.deals||''}`.toLowerCase();
      if (!blob.includes(ql)) return false;
    }
    // verified — labels with releases > 0 count as verified
    if (f.verified === 'verified'   && !((l.releases||0) > 0)) return false;
    if (f.verified === 'unverified' && (l.releases||0) > 0) return false;
    if (f.territory && f.territory !== 'any') {
      const d = (l.deals||'').toLowerCase();
      if (f.territory === 'WW' && !(d.includes('worldwide') || d.includes('ww'))) return false;
      if (f.territory === 'US' && !(d.includes('us') || d.includes('na') || d.includes('worldwide') || d.includes('ww'))) return false;
      if (f.territory === 'GB' && !(d.includes('gb') || d.includes('uk') || d.includes('worldwide') || d.includes('ww'))) return false;
      if (f.territory === 'EU' && !(d.includes('eu') || d.includes('ex-na') || d.includes('worldwide') || d.includes('ww'))) return false;
    }
    if (f.parentKind === 'major' && !isMajor(l)) return false;
    if (f.parentKind === 'indie' && isMajor(l)) return false;
    return true;
  });
  if (list.length === 0) {
    return (
      <div style={{padding:'48px 24px',borderTop:'1px solid var(--rule)',borderLeft:'1px solid var(--rule)',borderRight:'1px solid var(--rule)',borderBottom:'1px solid var(--rule)',textAlign:'center',color:'var(--ink-3)'}}>
        <div className="ff-mono upper" style={{fontSize:10,letterSpacing:'.1em'}}>NO LABELS MATCH THE CURRENT FILTERS</div>
      </div>
    );
  }
  return (
    <div style={{display:'grid',gridTemplateColumns:'repeat(4,1fr)',gap:0,borderTop:'1px solid var(--rule)',borderLeft:'1px solid var(--rule)'}}>
      {list.map((l,i)=>(
        <div key={l.id} onClick={()=>window.dispatchEvent(new CustomEvent('astro-open-label',{detail:{id:l.id}}))}
          style={{borderRight:'1px solid var(--rule)',borderBottom:'1px solid var(--rule)',cursor:'pointer',display:'flex',flexDirection:'column'}}
          onMouseEnter={e=>e.currentTarget.style.background='var(--bg-2)'}
          onMouseLeave={e=>e.currentTarget.style.background='transparent'}>
          <div style={{height:120,background:l.color,position:'relative',overflow:'hidden',borderBottom:'1px solid var(--rule)'}}>
            <span className="ff-display" style={{position:'absolute',bottom:6,left:14,fontSize:74,fontWeight:800,
              color:'rgba(255,255,255,.18)',letterSpacing:'-0.06em',lineHeight:1}}>{l.code}</span>
            <Pill tone="accent" className="absolute" /* placeholder */ />
            <span className="ff-mono num" style={{position:'absolute',top:10,right:12,color:'rgba(255,255,255,.7)',fontSize:10,fontWeight:600}}>№ {String(i+1).padStart(2,'0')}</span>
          </div>
          <div style={{padding:'14px',flex:1,display:'flex',flexDirection:'column'}}>
            <div className="heading-swap ff-display" style={{fontSize:18,fontWeight:600,letterSpacing:'-0.02em',lineHeight:1.05,marginBottom:4}}>{window.PartyIndicator && <window.PartyIndicator party={l}/>}{l.name}</div>
            <div style={{display:'flex',alignItems:'center',gap:8,marginBottom:12,flexWrap:'wrap'}}>
              <div className="ff-mono" style={{fontSize:10,color:'var(--ink-3)'}}>{l.parent}</div>
              {(l.kind === 'major' || l.kind === 'indie') && (
                <span className="ff-mono upper" style={{fontSize:8,letterSpacing:'.12em',padding:'1px 5px',
                  background: l.kind === 'major' ? 'var(--ink)' : 'transparent',
                  color: l.kind === 'major' ? 'var(--bg)' : 'var(--ink-2)',
                  border: l.kind === 'major' ? '1px solid var(--ink)' : '1px solid var(--rule)',
                  fontWeight:600}}>
                  {l.kind === 'major' ? 'MAJOR' : 'INDIE'}
                </span>
              )}
            </div>
            <div style={{display:'flex',justifyContent:'space-between',marginTop:'auto',paddingTop:10,borderTop:'1px solid var(--rule-soft)'}}>
              <div>
                <div className="ff-mono upper" style={{fontSize:9,color:'var(--ink-4)'}}>RELEASES</div>
                <div className="ff-mono num" style={{fontSize:14,fontWeight:600}}>{l.releases.toLocaleString()}</div>
              </div>
              <div style={{textAlign:'right'}}>
                <div className="ff-mono upper" style={{fontSize:9,color:'var(--ink-4)'}}>ARTISTS</div>
                <div className="ff-mono num" style={{fontSize:14,fontWeight:600}}>{l.artists}</div>
              </div>
            </div>
            <div className="ff-mono" style={{fontSize:10,color:'var(--ink-3)',marginTop:8}}>{l.deals}</div>
          </div>
        </div>
      ))}
    </div>
  );
}

// ───────────────────────────────────────────────────────────── AGREEMENTS
// ───────────────────────────────────────────────────────────── AGREEMENTS · DATA
// Schema-aligned fixtures. Each agreement carries the full nested model so the
// detail page can render parties (agreement_parties), territories (agreement_territories
// w/ rights_type), works covered (work_agreements w/ collection/ownership shares),
// versions (agreement_versions), signatures (agreement_signatures), assets
// (agreement_assets), advance schedule, retention/post-term flags, and an audit
// trail. The list view only reads the flat top-level fields.
//
// NOTE: hardcoded mock agreements removed — real agreements now flow in
// exclusively from the Rocket-Science CSV import via rs-bridge.jsx (which
// prepends them to window.AGREEMENTS) and from sub-pub deals mirrored in by
// subpubs.jsx::mirrorSubPubDealsIntoAgreements. Keep the array shape so the
// modal and downstream consumers continue to work.
const AGREEMENTS = [];
const __AGREEMENTS_LEGACY_MOCKS_REMOVED = [
  {
    id:'AG-2024-0042', kind:'Publishing · Co-pub',
    a:'PluralPub', b:'Floating Points',
    territory:'World', share:'50/50',
    start:'2024-03-01', end:'2029-02-28', term:'5y',
    status:'active', value:'$220K adv.',
    // Rich fields
    typeCwr:'OS', // Original
    refNumber:'PP-FP-2024-COPUB-01',
    societyAgreementNumber:'PRS-882-441-330',
    autoRenew:true, renewNoticeMonths:6,
    retentionYears:3, retentionEndDate:'2032-02-28',
    priorRoyaltyStatus:'A', // All royalties retained
    sharesCanChange:false, advanceGiven:true,
    salesOrManufacture:'S',
    jurisdiction:'England & Wales', disputeResolution:'Arbitration · LCIA',
    parties:[
      {role:'Assignor', name:'Sam Shepherd p/k/a Floating Points', kind:'writer', ipi:'00578913241', share:50, isControlled:true},
      {role:'Assignee', name:'PluralPub Ltd.', kind:'publisher', ipi:'00231887442', share:50, isControlled:true},
      {role:'Witness',  name:'Ninja Tune Ltd.',  kind:'company',   ipi:null,           share:null, isControlled:false},
    ],
    territories:[
      {code:'WW', label:'World', rights:'Mechanical + Performance', collection:50, ownership:50, excluded:[]},
    ],
    works:[
      {id:'W-9821', title:'Birth4000', iswc:'T-300.451.221-9', collection:50, ownership:50, status:'registered'},
      {id:'W-9822', title:'Vocoder',  iswc:'T-300.451.222-7', collection:50, ownership:50, status:'registered'},
      {id:'W-9824', title:'Del Oro',  iswc:'T-300.451.224-2', collection:50, ownership:50, status:'pending'},
      {id:'W-9831', title:'Promenade', iswc:'T-300.451.231-8', collection:50, ownership:50, status:'registered'},
      {id:'W-9844', title:'Ratio (Deluxe)', iswc:'T-300.451.244-5', collection:50, ownership:50, status:'registered'},
    ],
    versions:[
      {n:3, date:'2024-04-12', author:'Avery Cohen', note:'Final · counter-signed PDF replacing v2', current:true},
      {n:2, date:'2024-03-22', author:'Sam Carter (FP team)', note:'Counter-revision · advance schedule restructured 60/40'},
      {n:1, date:'2024-03-08', author:'Avery Cohen', note:'Draft sent to counter-party'},
    ],
    signatures:[
      {party:'PluralPub Ltd.',                   signer:'Avery Cohen',  role:'Director',          method:'DocuSign · email',       at:'2024-04-12T11:42:00Z', verified:true},
      {party:'Sam Shepherd p/k/a Floating Points', signer:'Sam Shepherd', role:'Self',              method:'DocuSign · email',       at:'2024-04-12T15:08:00Z', verified:true},
      {party:'Ninja Tune Ltd.',                    signer:'P. Hunt',      role:'Witness · GC',      method:'Wet ink · scanned',      at:'2024-04-15T09:00:00Z', verified:true},
    ],
    assets:[
      {kind:'PDF · Final',           name:'PP-FP-2024-COPUB-01_v3_signed.pdf', size:'1.2 MB', uploaded:'2024-04-15', by:'Avery Cohen'},
      {kind:'PDF · Working',         name:'PP-FP-2024-COPUB-01_v2_redline.pdf', size:'1.4 MB', uploaded:'2024-03-22', by:'Sam Carter'},
      {kind:'Schedule A · Works',    name:'schedule-a-works-list.xlsx',         size:'88 KB',  uploaded:'2024-04-01', by:'Avery Cohen'},
      {kind:'Schedule B · Advances', name:'schedule-b-advance-recoupment.pdf',  size:'310 KB', uploaded:'2024-04-01', by:'Avery Cohen'},
      {kind:'Side letter',           name:'side-letter-vinyl-mech-rate.pdf',    size:'140 KB', uploaded:'2024-04-08', by:'Sam Carter'},
    ],
    advanceSchedule:[
      {n:1, label:'On signing',           amount:88000,  due:'2024-04-15', paid:true,  paidOn:'2024-04-18'},
      {n:2, label:'On delivery · LP1',    amount:66000,  due:'2025-09-30', paid:true,  paidOn:'2025-09-29'},
      {n:3, label:'On delivery · LP2',    amount:66000,  due:'2027-09-30', paid:false, paidOn:null},
    ],
    audit:[
      {at:'2024-04-15T09:00:00Z', who:'Avery Cohen',  what:'Witness signature countersigned · status → ACTIVE'},
      {at:'2024-04-12T15:08:00Z', who:'Sam Shepherd', what:'Signed v3 via DocuSign'},
      {at:'2024-04-12T11:42:00Z', who:'Avery Cohen',  what:'Counter-signed v3'},
      {at:'2024-04-12T11:38:00Z', who:'System',       what:'v3 generated from v2 + redline acceptance'},
      {at:'2024-03-22T18:00:00Z', who:'Sam Carter',   what:'Returned redline · advance restructured 60/40'},
      {at:'2024-03-08T10:14:00Z', who:'Avery Cohen',  what:'Draft v1 sent to counter-party'},
    ],
  },
  {
    id:'AG-2023-0871', kind:'Admin · Sub-publishing',
    a:'Saint Music', b:'GEMA Territory Co.',
    territory:'DE · AT · CH', share:'15% admin',
    start:'2023-09-01', end:'2026-08-31', term:'3y',
    status:'expiring', value:'admin only',
    typeCwr:'PG', refNumber:'SM-GTC-2023-SUB-DACH',
    societyAgreementNumber:'GEMA-44-118-201',
    autoRenew:false, renewNoticeMonths:3,
    retentionYears:2, retentionEndDate:'2028-08-31',
    priorRoyaltyStatus:'N', sharesCanChange:false, advanceGiven:false,
    salesOrManufacture:null,
    jurisdiction:'Germany · Munich', disputeResolution:'GEMA Schiedsgericht',
    parties:[
      {role:'Original Publisher', name:'Saint Music GmbH',    kind:'publisher', ipi:'00712054088', share:100, isControlled:true},
      {role:'Sub-Publisher',      name:'GEMA Territory Co.',  kind:'publisher', ipi:'00919223144', share:15,  isControlled:false},
    ],
    territories:[
      {code:'DE', label:'Germany',    rights:'Mech + Perf · all uses',  collection:15, ownership:0, excluded:[]},
      {code:'AT', label:'Austria',    rights:'Mech + Perf · all uses',  collection:15, ownership:0, excluded:[]},
      {code:'CH', label:'Switzerland',rights:'Mech + Perf · all uses',  collection:15, ownership:0, excluded:[]},
    ],
    works:[
      {id:'W-7711', title:'Stadtlicht',     iswc:'T-901.221.001-3', collection:15, ownership:0, status:'registered'},
      {id:'W-7712', title:'Bahnhof',        iswc:'T-901.221.002-1', collection:15, ownership:0, status:'registered'},
      {id:'W-7720', title:'Erinnerung',     iswc:'T-901.221.020-9', collection:15, ownership:0, status:'pending'},
    ],
    versions:[
      {n:2, date:'2023-09-12', author:'M. Becker', note:'Final · executed', current:true},
      {n:1, date:'2023-08-22', author:'Avery Cohen', note:'Draft from template GEMA-SUB-2022'},
    ],
    signatures:[
      {party:'Saint Music GmbH',   signer:'M. Becker',   role:'Geschäftsführer', method:'Wet ink · scanned', at:'2023-09-12T10:00:00Z', verified:true},
      {party:'GEMA Territory Co.', signer:'H. Steinmann',role:'Direktor',         method:'Wet ink · scanned', at:'2023-09-14T14:30:00Z', verified:true},
    ],
    assets:[
      {kind:'PDF · Final',  name:'SM-GTC-2023-SUB-DACH_signed.pdf', size:'880 KB', uploaded:'2023-09-15', by:'M. Becker'},
      {kind:'Schedule A',   name:'works-schedule-2023.xlsx',         size:'44 KB',  uploaded:'2023-09-01', by:'Avery Cohen'},
    ],
    advanceSchedule:[],
    audit:[
      {at:'2026-04-15T09:00:00Z', who:'System',        what:'Renewal alert · 90 days to expiry'},
      {at:'2023-09-15T11:00:00Z', who:'Avery Cohen',   what:'Status → ACTIVE · all signatures verified'},
      {at:'2023-09-12T10:00:00Z', who:'M. Becker',     what:'Signed v2'},
    ],
  },
  {
    id:'AG-2022-0188', kind:'Recording · Distribution',
    a:'Pluralis Music', b:'4AD',
    territory:'World', share:'80/20 net',
    start:'2022-01-01', end:'2027-12-31', term:'5y',
    status:'active', value:'recoupable',
    typeCwr:null, refNumber:'PM-4AD-2022-DIST-WW',
    societyAgreementNumber:null,
    autoRenew:false, renewNoticeMonths:6,
    retentionYears:5, retentionEndDate:'2032-12-31',
    priorRoyaltyStatus:'A', sharesCanChange:false, advanceGiven:true,
    salesOrManufacture:'S',
    jurisdiction:'England & Wales', disputeResolution:'Court · London',
    parties:[
      {role:'Licensor',   name:'Pluralis Music Ltd.', kind:'label',     ipi:null,           share:80, isControlled:true},
      {role:'Distributor',name:'4AD Limited',         kind:'label',     ipi:null,           share:20, isControlled:false},
    ],
    territories:[
      {code:'WW', label:'World', rights:'Phonographic · all formats', collection:80, ownership:100, excluded:[]},
    ],
    works:[],
    versions:[
      {n:1, date:'2021-12-14', author:'Avery Cohen', note:'Final', current:true},
    ],
    signatures:[
      {party:'Pluralis Music Ltd.', signer:'Avery Cohen', role:'Director', method:'DocuSign', at:'2021-12-14T16:00:00Z', verified:true},
      {party:'4AD Limited',         signer:'C. Drummond', role:'MD',        method:'DocuSign', at:'2021-12-15T11:00:00Z', verified:true},
    ],
    assets:[
      {kind:'PDF · Final',     name:'PM-4AD-2022-DIST-WW.pdf',  size:'1.6 MB', uploaded:'2021-12-15', by:'Avery Cohen'},
      {kind:'Schedule · Recordings', name:'recordings-schedule.xlsx', size:'212 KB', uploaded:'2021-12-10', by:'Avery Cohen'},
    ],
    advanceSchedule:[
      {n:1, label:'Marketing fund · Year 1', amount:150000, due:'2022-01-15', paid:true, paidOn:'2022-01-14'},
      {n:2, label:'Marketing fund · Year 3', amount:120000, due:'2024-01-15', paid:true, paidOn:'2024-01-12'},
      {n:3, label:'Marketing fund · Year 5', amount:120000, due:'2026-01-15', paid:false, paidOn:null},
    ],
    audit:[
      {at:'2021-12-15T11:00:00Z', who:'C. Drummond', what:'Signed v1'},
      {at:'2021-12-14T16:00:00Z', who:'Avery Cohen', what:'Signed v1'},
    ],
  },
  {
    id:'AG-2024-0309', kind:'License · Sync',
    a:'PluralPub', b:'A24 Films',
    territory:'World', share:'one-time',
    start:'2024-11-22', end:'2031-11-22', term:'7y',
    status:'active', value:'$58K',
    typeCwr:null, refNumber:'PP-A24-SYNC-IDOL-2024',
    societyAgreementNumber:null,
    autoRenew:false, renewNoticeMonths:0,
    retentionYears:0, retentionEndDate:null,
    priorRoyaltyStatus:'N', sharesCanChange:false, advanceGiven:false,
    salesOrManufacture:null,
    jurisdiction:'New York · USA', disputeResolution:'Court · NY County',
    parties:[
      {role:'Licensor', name:'PluralPub Ltd.',   kind:'publisher', ipi:'00231887442', share:100, isControlled:true},
      {role:'Licensee', name:'A24 Films LLC',    kind:'company',   ipi:null,          share:0,   isControlled:false},
    ],
    territories:[
      {code:'WW', label:'World', rights:'Sync · feature film + trailer + marketing', collection:0, ownership:100, excluded:[]},
    ],
    works:[
      {id:'W-9824', title:'Del Oro', iswc:'T-300.451.224-2', collection:100, ownership:50, status:'registered'},
    ],
    versions:[
      {n:1, date:'2024-11-18', author:'Avery Cohen', note:'Final · negotiated MFN', current:true},
    ],
    signatures:[
      {party:'PluralPub Ltd.', signer:'Avery Cohen', role:'Director',          method:'DocuSign', at:'2024-11-22T14:00:00Z', verified:true},
      {party:'A24 Films LLC',  signer:'J. Hofstetter', role:'Music Supervision', method:'DocuSign', at:'2024-11-22T16:30:00Z', verified:true},
    ],
    assets:[
      {kind:'PDF · Final',  name:'PP-A24-SYNC-IDOL-2024.pdf',     size:'480 KB', uploaded:'2024-11-22', by:'Avery Cohen'},
      {kind:'Cue sheet',    name:'idol-s2-ep03-cuesheet.pdf',      size:'88 KB',  uploaded:'2024-11-25', by:'A24 Music Sup.'},
      {kind:'Sync request', name:'a24-sync-request-original.pdf',  size:'140 KB', uploaded:'2024-10-30', by:'J. Hofstetter'},
    ],
    advanceSchedule:[
      {n:1, label:'Sync fee · all-in', amount:58000, due:'2024-11-22', paid:true, paidOn:'2024-12-04'},
    ],
    audit:[
      {at:'2024-12-04T10:00:00Z', who:'System',       what:'Sync fee received · $58,000 · cleared'},
      {at:'2024-11-22T16:30:00Z', who:'J. Hofstetter',what:'Signed v1'},
      {at:'2024-11-22T14:00:00Z', who:'Avery Cohen', what:'Signed v1'},
    ],
  },
  {
    id:'AG-2025-0014', kind:'Producer · Royalty',
    a:'KAY Publishing', b:'Roberto Lange',
    territory:'World', share:'3% net',
    start:'2025-01-15', end:'—', term:'perpetual',
    status:'active', value:'royalty only',
    typeCwr:null, refNumber:'KAY-RL-PROD-2025',
    societyAgreementNumber:null,
    autoRenew:false, renewNoticeMonths:0,
    retentionYears:0, retentionEndDate:null,
    priorRoyaltyStatus:'N', sharesCanChange:false, advanceGiven:false,
    salesOrManufacture:null,
    jurisdiction:'New York · USA', disputeResolution:'AAA Arbitration',
    parties:[
      {role:'Artist/Owner', name:'KAY Publishing LLC', kind:'publisher', ipi:'00892331140', share:97, isControlled:true},
      {role:'Producer',     name:'Roberto Carlos Lange (Helado Negro)', kind:'producer',  ipi:'00554122089', share:3,  isControlled:false},
    ],
    territories:[
      {code:'WW', label:'World', rights:'Phonographic · all formats', collection:3, ownership:0, excluded:[]},
    ],
    works:[],
    versions:[
      {n:1, date:'2025-01-10', author:'Avery Cohen', note:'Final · executed', current:true},
    ],
    signatures:[
      {party:'KAY Publishing LLC',  signer:'Avery Cohen',  role:'Director', method:'DocuSign', at:'2025-01-15T10:00:00Z', verified:true},
      {party:'Roberto Carlos Lange', signer:'Roberto Lange',role:'Self',     method:'DocuSign', at:'2025-01-15T11:30:00Z', verified:true},
    ],
    assets:[
      {kind:'PDF · Final', name:'KAY-RL-PROD-2025_signed.pdf', size:'420 KB', uploaded:'2025-01-15', by:'Avery Cohen'},
    ],
    advanceSchedule:[],
    audit:[
      {at:'2025-01-15T11:30:00Z', who:'Roberto Lange', what:'Signed v1'},
      {at:'2025-01-15T10:00:00Z', who:'Avery Cohen',  what:'Signed v1'},
    ],
  },
  {
    id:'AG-2021-0922', kind:'Society · Mandate',
    a:'Pluralis Music', b:'PRS for Music',
    territory:'GB', share:'mech + perf',
    start:'2021-04-01', end:'2026-04-01', term:'5y',
    status:'expiring', value:'mandate',
    typeCwr:'PG', refNumber:'PM-PRS-MANDATE-2021',
    societyAgreementNumber:'PRS-882-441-330',
    autoRenew:true, renewNoticeMonths:3,
    retentionYears:0, retentionEndDate:null,
    priorRoyaltyStatus:'N', sharesCanChange:false, advanceGiven:false,
    salesOrManufacture:null,
    jurisdiction:'England & Wales', disputeResolution:'PRS Members Council',
    parties:[
      {role:'Member',  name:'Pluralis Music Ltd.', kind:'publisher', ipi:'00231887442', share:100, isControlled:true},
      {role:'Society', name:'PRS for Music',       kind:'society',   ipi:null,          share:null,isControlled:false},
    ],
    territories:[
      {code:'GB', label:'United Kingdom', rights:'Mechanical + Performance', collection:100, ownership:0, excluded:[]},
    ],
    works:[],
    versions:[
      {n:1, date:'2021-03-22', author:'PRS Operations', note:'Standard member mandate', current:true},
    ],
    signatures:[
      {party:'Pluralis Music Ltd.', signer:'Avery Cohen', role:'Director', method:'PRS portal · digital', at:'2021-03-25T14:00:00Z', verified:true},
      {party:'PRS for Music',       signer:'Operations',   role:'System',   method:'PRS portal · digital', at:'2021-03-25T14:00:01Z', verified:true},
    ],
    assets:[
      {kind:'PDF · Mandate', name:'PM-PRS-MANDATE-2021.pdf', size:'620 KB', uploaded:'2021-03-25', by:'Avery Cohen'},
    ],
    advanceSchedule:[],
    audit:[
      {at:'2026-01-01T00:00:00Z', who:'System',       what:'Renewal alert · 90 days to expiry'},
      {at:'2021-03-25T14:00:00Z', who:'Avery Cohen',  what:'Mandate executed via PRS portal'},
    ],
  },
  {
    id:'AG-2023-0445', kind:'Master · Buy-out',
    a:'Saint Music', b:'Solange Knowles',
    territory:'World', share:'100%',
    start:'2023-06-15', end:'—', term:'perpetual',
    status:'active', value:'$1.4M',
    typeCwr:null, refNumber:'SM-SK-MASTER-BUYOUT-2023',
    societyAgreementNumber:null,
    autoRenew:false, renewNoticeMonths:0,
    retentionYears:0, retentionEndDate:null,
    priorRoyaltyStatus:'A', sharesCanChange:false, advanceGiven:true,
    salesOrManufacture:'S',
    jurisdiction:'New York · USA', disputeResolution:'AAA Arbitration',
    parties:[
      {role:'Buyer',  name:'Saint Music GmbH',     kind:'publisher', ipi:'00712054088', share:100, isControlled:true},
      {role:'Seller', name:'Solange Knowles',      kind:'writer',    ipi:'00578913241', share:0,   isControlled:false},
    ],
    territories:[
      {code:'WW', label:'World', rights:'Phonographic · all rights · perpetual', collection:100, ownership:100, excluded:[]},
    ],
    works:[],
    versions:[
      {n:2, date:'2023-06-15', author:'Avery Cohen', note:'Final · executed', current:true},
      {n:1, date:'2023-05-20', author:'Avery Cohen', note:'Initial draft'},
    ],
    signatures:[
      {party:'Saint Music GmbH', signer:'M. Becker',     role:'Geschäftsführer', method:'Wet ink · notarized', at:'2023-06-15T10:00:00Z', verified:true},
      {party:'Solange Knowles',  signer:'Solange Knowles',role:'Self',           method:'Wet ink · notarized', at:'2023-06-15T11:00:00Z', verified:true},
    ],
    assets:[
      {kind:'PDF · Final · notarized', name:'SM-SK-BUYOUT-2023_notarized.pdf', size:'2.1 MB', uploaded:'2023-06-16', by:'Avery Cohen'},
      {kind:'Catalog inventory',       name:'inventory-204-recordings.xlsx',    size:'180 KB', uploaded:'2023-05-22', by:'Avery Cohen'},
      {kind:'Tax · IRS forms',         name:'w9-1099-package.pdf',              size:'310 KB', uploaded:'2023-06-20', by:'Tax Counsel'},
    ],
    advanceSchedule:[
      {n:1, label:'On signing',         amount:700000, due:'2023-06-15', paid:true, paidOn:'2023-06-16'},
      {n:2, label:'12 months post-close',amount:700000, due:'2024-06-15', paid:true, paidOn:'2024-06-14'},
    ],
    audit:[
      {at:'2024-06-14T16:00:00Z', who:'System',       what:'Final payment · $700,000 · cleared'},
      {at:'2023-06-16T11:00:00Z', who:'System',       what:'204 recordings transferred · catalog inventory updated'},
      {at:'2023-06-15T11:00:00Z', who:'Solange Knowles', what:'Signed v2'},
      {at:'2023-06-15T10:00:00Z', who:'M. Becker',    what:'Signed v2'},
    ],
  },
  {
    id:'AG-2025-0101', kind:'Publishing · Admin',
    a:'Forever Living', b:'TM Works',
    territory:'WW ex. JP', share:'80/20 net',
    start:'2025-02-01', end:'2030-01-31', term:'5y',
    status:'pending', value:'$120K adv.',
    typeCwr:'PA', refNumber:'FL-TM-ADMIN-2025',
    societyAgreementNumber:null,
    autoRenew:true, renewNoticeMonths:6,
    retentionYears:2, retentionEndDate:'2032-01-31',
    priorRoyaltyStatus:'N', sharesCanChange:false, advanceGiven:true,
    salesOrManufacture:'S',
    jurisdiction:'England & Wales', disputeResolution:'Arbitration · LCIA',
    parties:[
      {role:'Original Publisher', name:'Forever Living Music', kind:'publisher', ipi:'00118229447', share:100, isControlled:false},
      {role:'Administrator',      name:'TM Works Ltd.',         kind:'publisher', ipi:'00229107551', share:20,  isControlled:true},
    ],
    territories:[
      {code:'WW', label:'World', rights:'Mech + Perf · ex. Japan', collection:20, ownership:0, excluded:['JP']},
    ],
    works:[
      {id:'W-8101', title:'Dancing Forever',   iswc:'T-455.221.001-9', collection:20, ownership:0, status:'registered'},
      {id:'W-8102', title:'Honey Don\'t Stop', iswc:'T-455.221.002-7', collection:20, ownership:0, status:'pending'},
    ],
    versions:[
      {n:2, date:'2026-04-22', author:'Avery Cohen', note:'Pending counter-signature', current:true},
      {n:1, date:'2026-04-12', author:'Forever Living', note:'Initial draft'},
    ],
    signatures:[
      {party:'TM Works Ltd.', signer:'Avery Cohen', role:'Director', method:'DocuSign', at:'2026-04-22T11:00:00Z', verified:true},
      {party:'Forever Living Music', signer:null, role:null, method:null, at:null, verified:false},
    ],
    assets:[
      {kind:'PDF · Working', name:'FL-TM-ADMIN-2025_v2.pdf', size:'920 KB', uploaded:'2026-04-22', by:'Avery Cohen'},
      {kind:'Schedule A',    name:'forever-living-works.xlsx', size:'52 KB',  uploaded:'2026-04-12', by:'Forever Living'},
    ],
    advanceSchedule:[
      {n:1, label:'On signing',          amount:60000, due:'2026-05-01', paid:false, paidOn:null},
      {n:2, label:'On delivery · LP1',   amount:60000, due:'2027-05-01', paid:false, paidOn:null},
    ],
    audit:[
      {at:'2026-04-22T11:00:00Z', who:'Avery Cohen',     what:'Signed v2 · awaiting counter-party'},
      {at:'2026-04-15T14:00:00Z', who:'System',          what:'Reminder sent to Forever Living'},
      {at:'2026-04-12T09:00:00Z', who:'Forever Living',  what:'Sent draft v1'},
    ],
  },
];
window.AGREEMENTS = AGREEMENTS;

function AgreementsView({ go }) {
  // Derive KPI strip values live from window.AGREEMENTS so the strip stays in
  // sync with whatever data the CSV import produced. No mock fallbacks — if a
  // bucket is empty, show 0.
  const ags = Array.isArray(window.AGREEMENTS) ? window.AGREEMENTS : [];
  const isActive   = ag => ag.status === 'live' || ag.status === 'active';
  const isExpiring = ag => ag.status === 'expiring';
  const isPending  = ag => ag.status === 'pending';

  // Expiring < 90d: any active agreement whose end date falls in the next 90 days.
  const today = new Date();
  const ninetyDays = 1000 * 60 * 60 * 24 * 90;
  const expiringSoon = ags.filter(a => {
    if (!isActive(a) || !a.end) return false;
    const end = new Date(a.end);
    if (isNaN(end)) return false;
    const delta = end - today;
    return delta > 0 && delta <= ninetyDays;
  }).length + ags.filter(isExpiring).length;

  // Gross committed: sum any advance figures present on agreements. Real RS
  // agreements carry advance amounts in advanceSchedule[]; mock fallbacks may
  // stash a number in `value` like '$220K adv.' — try both.
  const parseMoney = s => {
    if (typeof s !== 'string') return 0;
    const m = s.match(/\$([\d.,]+)\s*([KMB])?/i);
    if (!m) return 0;
    const n = parseFloat(m[1].replace(/,/g,''));
    const mult = (m[2]||'').toUpperCase() === 'M' ? 1e6 : (m[2]||'').toUpperCase() === 'B' ? 1e9 : (m[2]||'').toUpperCase() === 'K' ? 1e3 : 1;
    return n * mult;
  };
  const totalAdvance = ags.reduce((sum, a) => {
    if (Array.isArray(a.advanceSchedule) && a.advanceSchedule.length) {
      return sum + a.advanceSchedule.reduce((s, row) => s + (Number(row.amount) || 0), 0);
    }
    return sum + parseMoney(a.value);
  }, 0);
  const fmtMoney = n => {
    if (!n) return '$0';
    if (n >= 1e6) return `$${(n/1e6).toFixed(n >= 1e7 ? 0 : 1)}M`;
    if (n >= 1e3) return `$${(n/1e3).toFixed(0)}K`;
    return `$${n.toFixed(0)}`;
  };

  return (
    <div>
      {/* Quick actions row */}
      <div style={{display:'flex',gap:10,marginBottom:16,flexWrap:'wrap'}}>
        <button onClick={()=>go && go('agreement-renewals')} className="ff-mono upper"
          style={{padding:'8px 14px',fontSize:11,letterSpacing:'.08em',background:expiringSoon?'var(--accent)':'transparent',color:expiringSoon?'var(--accent-ink)':'var(--ink)',border:'1px solid '+(expiringSoon?'var(--accent)':'var(--rule)'),cursor:'pointer'}}>
          ⌖ RENEWALS &amp; REVERSIONS{expiringSoon?` · ${expiringSoon} ACTIVE`:''}
        </button>
        <button onClick={()=>go && go('agreement-templates')} className="ff-mono upper"
          style={{padding:'8px 14px',fontSize:11,letterSpacing:'.08em',background:'transparent',color:'var(--ink)',border:'1px solid var(--rule)',cursor:'pointer'}}>
          ◇ TEMPLATE LIBRARY
        </button>
      </div>
      {/* Stats strip — derived from window.AGREEMENTS */}
      <div style={{display:'grid',gridTemplateColumns:'repeat(4,1fr)',borderTop:'1px solid var(--rule)',borderBottom:'1px solid var(--rule)',marginTop:0}}>
        {[
          {l:'ACTIVE', v: ags.filter(isActive).length, sub: ags.length ? 'across catalog' : 'no agreements'},
          {l:'EXPIRING · 90D', v: expiringSoon, sub: expiringSoon ? 'review needed' : 'none upcoming', tone: expiringSoon ? 'accent' : null},
          {l:'PENDING SIGN', v: ags.filter(isPending).length, sub:'awaiting counter-party'},
          {l:'GROSS COMMITTED', v: fmtMoney(totalAdvance), sub:'all advances'},
        ].map((s,i,arr)=>(
          <div key={i} style={{padding:'18px 16px',borderRight:i<arr.length-1?'1px solid var(--rule)':'none',
            background:s.tone==='accent'?'var(--accent)':'transparent',color:s.tone==='accent'?'var(--accent-ink)':'var(--ink)'}}>
            <div className="ff-mono upper" style={{fontSize:10,fontWeight:600,marginBottom:6}}>{s.l}</div>
            <div className="ff-display num" style={{fontSize:36,fontWeight:600,letterSpacing:'-0.04em',lineHeight:1}}>{s.v}</div>
            <div className="ff-mono" style={{fontSize:10,color:s.tone==='accent'?'var(--accent-ink)':'var(--ink-3)',marginTop:6}}>{s.sub}</div>
          </div>
        ))}
      </div>

      {/* Table */}
      <div style={{marginTop:0}}>
        <div className="ff-mono upper" style={{display:'grid',gridTemplateColumns:'120px 1fr 1.4fr 1fr 110px 90px 90px',gap:14,
          padding:'10px 14px',fontSize:10,color:'var(--ink-3)',background:'var(--bg-2)',borderBottom:'1px solid var(--rule)'}}>
          <span>ID</span><span>KIND</span><span>PARTIES</span><span>TERRITORY · SHARE</span><span>TERM</span><span>STATUS</span><span style={{textAlign:'right'}}>VALUE</span>
        </div>
        {AGREEMENTS.map(ag=>(
          <div key={ag.id} onClick={()=>go && go('agreement', ag)}
            style={{display:'grid',gridTemplateColumns:'120px 1fr 1.4fr 1fr 110px 90px 90px',gap:14,
            padding:'14px',borderBottom:'1px solid var(--rule-soft)',alignItems:'center',cursor:'pointer'}}
            onMouseEnter={e=>e.currentTarget.style.background='var(--bg-2)'}
            onMouseLeave={e=>e.currentTarget.style.background='transparent'}>
            <span className="ff-mono" style={{fontSize:11,fontWeight:600}}>{ag.id}</span>
            <span style={{fontSize:13}}>{ag.kind}</span>
            <div>
              <span style={{fontSize:13,fontWeight:600}}>{ag.a}</span>
              <span className="ff-mono" style={{color:'var(--ink-4)',margin:'0 6px',fontSize:10}}>×</span>
              <span style={{fontSize:13,fontWeight:600}}>{ag.b}</span>
              <div style={{display:'flex',gap:6,marginTop:4,alignItems:'center'}} onClick={e=>e.stopPropagation()}>
                {window.AgCounterpartyChip && <window.AgCounterpartyChip name={ag.a} go={go} kind="publisher" />}
                {window.AgCounterpartyChip && <window.AgCounterpartyChip name={ag.b} go={go} />}
              </div>
              <div className="ff-mono" style={{fontSize:10,color:'var(--ink-3)',marginTop:2}}>{ag.start} → {ag.end}</div>
            </div>
            <div>
              <span className="ff-mono" style={{fontSize:11}}>{ag.territory}</span>
              <div className="ff-mono" style={{fontSize:10,color:'var(--ink-3)',marginTop:2}}>{ag.share}</div>
            </div>
            <span className="ff-mono" style={{fontSize:11}}>{ag.term}</span>
            <Pill tone={ag.status==='active'?'ok':ag.status==='expiring'?'accent':'soft'} dot>{ag.status}</Pill>
            <span className="ff-mono num" style={{fontSize:12,textAlign:'right',fontWeight:600}}>{ag.value}</span>
          </div>
        ))}
      </div>
    </div>
  );
}

// ───────────────────────────────────────────────────────────── SETTINGS
function ScreenSettings({ go, payload }) {
  // Map shorthand tab keys (from header user menu) onto canonical section keys
  const tabAlias = { profile:'workspace', notifications:'workspace', security:'identity' };
  const initial = (payload && payload.tab) ? (tabAlias[payload.tab] || payload.tab) : 'workspace';
  const advancedKeys = new Set(['identity', 'rights', 'cwr', 'royalty', 'api', 'audit', 'danger']);
  const [section, setSection] = useS3(initial);
  const [showAdvanced, setShowAdvanced] = useS3(() => advancedKeys.has(initial));
  React.useEffect(() => {
    if (payload && payload.tab) {
      const t = tabAlias[payload.tab] || payload.tab;
      setSection(t);
      if (advancedKeys.has(t)) setShowAdvanced(true);
    }
  }, [payload && payload.tab]);
  const sections = [
    {k:'workspace',l:'Account basics',sub:'Name, region, profile',group:'Basics'},
    {k:'appearance',l:'Look & feel',sub:'Theme, accent, density',group:'Basics'},
    {k:'team',     l:'People & access',sub:'Members, roles, invites',group:'Basics'},
    {k:'billing',  l:'Billing',sub:'Plan, invoices',group:'Basics'},
    {k:'rights',   l:'Rights setup',sub:'Splits, territories, retention',group:'Operations'},
    {k:'cwr',      l:'Registrations',sub:'CWR, DDEX, schedules',group:'Operations'},
    {k:'royalty',  l:'Royalties import',sub:'Statements, parsers, FX',group:'Operations'},
    {k:'identity', l:'Sign-in security',sub:'Clerk, OIDC, SAML',group:'Developer'},
    {k:'api',      l:'Developer API',sub:'Tokens, webhooks, IPs',group:'Developer'},
    {k:'audit',    l:'Activity log',sub:'Who did what, when',group:'Developer'},
    {k:'danger',   l:'Delete & export',sub:'Workspace data controls',group:'Advanced'},
  ];
  const groups = ['Basics', 'Operations', 'Developer', 'Advanced'];
  const visibleSections = sections.filter((s) => s.group === 'Basics' || showAdvanced);
  const toggleAdvanced = () => {
    const next = !showAdvanced;
    setShowAdvanced(next);
    if (!next && advancedKeys.has(section)) setSection('workspace');
  };

  return (
    <div>
      <div style={{marginBottom:32}}>
        <PageHeader
          eyebrow="WORKSPACE"
          title="settings"
          highlight="settings"
          sub="Start with account basics. Advanced registration, royalty, API, and delete controls stay tucked away until needed."
        />
      </div>

      <div style={{display:'grid',gridTemplateColumns:'260px 1fr',gap:0,borderTop:'1px solid var(--rule)'}}>
        {/* Sidebar */}
        <nav style={{borderRight:'1px solid var(--rule)',padding:'14px 0',position:'sticky',top:80,alignSelf:'flex-start'}}>
          {groups.map((group) => {
            const items = visibleSections.filter((s) => s.group === group);
            if (!items.length) return null;
            return (
              <div key={group} style={{marginBottom: group === 'Basics' ? 12 : 8}}>
                <div className="ff-mono upper" style={{fontSize:9,color:'var(--ink-4)',letterSpacing:'.1em',fontWeight:700,padding:'8px 18px 6px'}}>
                  {group}
                </div>
                {items.map((s)=>(
                  <button key={s.k} onClick={()=>setSection(s.k)} style={{
                    width:'100%',textAlign:'left',padding:'12px 18px',display:'flex',gap:12,alignItems:'flex-start',
                    background:section===s.k?'var(--bg-2)':'transparent',
                    borderLeft:section===s.k?'3px solid var(--ink)':'3px solid transparent'}}>
                    <span style={{width:8,height:8,marginTop:5,background:section===s.k?'var(--ink)':'var(--rule)',display:'inline-block',flex:'0 0 auto'}}/>
                    <div>
                      <div style={{fontSize:13,fontWeight:600}}>{s.l}</div>
                      <div className="ff-mono" style={{fontSize:10,color:'var(--ink-3)',marginTop:2}}>{s.sub}</div>
                    </div>
                  </button>
                ))}
              </div>
            );
          })}
          <div style={{padding:'8px 18px 4px',borderTop:'1px solid var(--rule-soft)',marginTop:6}}>
            <button
              className="ff-mono upper"
              onClick={toggleAdvanced}
              style={{fontSize:10,fontWeight:700,letterSpacing:'.08em',color:'var(--ink)',display:'inline-flex',alignItems:'center',gap:8,padding:'8px 0'}}>
              {showAdvanced ? 'Hide advanced' : 'Show advanced'}
              <span style={{width:6,height:6,borderRight:'1px solid currentColor',borderBottom:'1px solid currentColor',transform:showAdvanced?'rotate(225deg)':'rotate(45deg)',marginTop:showAdvanced?3:-3}}/>
            </button>
          </div>
        </nav>

        {/* Pane */}
        <div style={{padding:'14px 32px 32px'}}>
          {section==='workspace' && <SettingsWorkspace/>}
          {section==='appearance' && <SettingsAppearance/>}
          {section==='team'      && <SettingsTeam/>}
          {section==='identity'  && <SettingsIdentity/>}
          {section==='rights'    && <SettingsRights/>}
          {section==='cwr'       && <SettingsCwr/>}
          {section==='royalty'   && <SettingsRoyalty/>}
          {section==='api'       && <SettingsApi/>}
          {section==='audit'     && <SettingsAudit/>}
          {section==='billing'   && <SettingsBilling/>}
          {section==='danger'    && <SettingsDanger/>}
        </div>
      </div>
    </div>
  );
}

function Field({ label, children, hint, full }) {
  return (
    <div style={{padding:'16px 0',borderBottom:'1px solid var(--rule-soft)',display:'grid',
      gridTemplateColumns: full?'1fr':'200px 1fr',gap:18,alignItems:'flex-start'}}>
      <div>
        <div className="ff-mono upper" style={{fontSize:10,fontWeight:600,marginBottom:4}}>{label}</div>
        {hint && <div style={{fontSize:11,color:'var(--ink-3)',lineHeight:1.4,maxWidth:200}}>{hint}</div>}
      </div>
      <div>{children}</div>
    </div>
  );
}

function SInp({ value, placeholder, mono, ...rest }) {
  return <input defaultValue={value} placeholder={placeholder} className={mono?'ff-mono':''}
    style={{width:'100%',padding:'8px 10px',background:'var(--paper)',border:'1px solid var(--rule)',fontSize:13,outline:'none'}} {...rest}/>;
}

function Tog({ value, label }) {
  const [on, setOn] = useS3(value);
  return (
    <button onClick={()=>setOn(!on)} style={{display:'inline-flex',alignItems:'center',gap:10}}>
      <span style={{width:32,height:18,background:on?'var(--ink)':'var(--rule-soft)',position:'relative',transition:'background .15s'}}>
        <span style={{position:'absolute',top:2,left:on?16:2,width:14,height:14,background:'var(--bg)',transition:'left .15s'}}/>
      </span>
      <span style={{fontSize:13}}>{label}</span>
    </button>
  );
}

// ───────────────────────────────────────────────────────────── APPEARANCE
function SettingsAppearance() {
  const [theme, setTheme] = useS3(()=>{ try{return localStorage.getItem('astro_theme')||'light'}catch{return'light'} });
  const [accent, setAccent] = useS3(()=>{ try{return localStorage.getItem('astro_accent')||'sodium'}catch{return'sodium'} });
  const [density, setDensity] = useS3(()=>{ try{return localStorage.getItem('astro_density')||'regular'}catch{return'regular'} });
  const [heading, setHeading] = useS3(()=>{ try{return localStorage.getItem('astro_heading')||'display'}catch{return'display'} });

  const apply = (k,v) => {
    try{ localStorage.setItem('astro_'+k, v); }catch{}
    window.dispatchEvent(new CustomEvent('astro-prefs', {detail:{[k]:v}}));
  };

  const themes = [
    {k:'light',l:'Light',d:'Newsprint paper, dark ink'},
    {k:'dark', l:'Dark', d:'Inverted — for low-light work'},
    {k:'auto', l:'Auto', d:'Follows your operating system'},
  ];
  const accents = [
    {k:'sodium',l:'Sodium',c:'#f0d028'},
    {k:'ember', l:'Ember', c:'#ff5b1f'},
    {k:'signal',l:'Signal',c:'#33d97a'},
    {k:'ultra', l:'Ultra', c:'#3344ff'},
    {k:'mono',  l:'Mono',  c:'#0e0e0c'},
  ];

  return (
    <div>
      <Section num="01">Theme</Section>
      <div style={{display:'grid',gridTemplateColumns:'repeat(3,1fr)',gap:0,borderTop:'1px solid var(--rule-soft)'}}>
        {themes.map(th=>{
          const active = theme===th.k;
          return (
            <button key={th.k} onClick={()=>{setTheme(th.k);apply('theme',th.k);}}
              style={{padding:'18px',border:'1px solid var(--rule)',borderLeft:'1px solid var(--rule)',
                background:active?'var(--bg-2)':'var(--paper)',textAlign:'left',display:'flex',flexDirection:'column',gap:10,
                outline:active?'2px solid var(--ink)':'none',outlineOffset:'-2px'}}>
              <div style={{display:'flex',alignItems:'center',justifyContent:'space-between'}}>
                <span style={{fontSize:14,fontWeight:600}}>{th.l}</span>
                {active && <Ic.Check width={14} height={14}/>}
              </div>
              {/* Mini preview */}
              <div style={{height:60,border:'1px solid var(--rule-soft)',display:'flex',overflow:'hidden'}}>
                {th.k==='auto' ? (
                  <>
                    <div style={{flex:1,background:'#f4efe6',padding:8,display:'flex',flexDirection:'column',gap:4}}>
                      <span style={{height:3,background:'#0e0e0c',width:'60%'}}/>
                      <span style={{height:2,background:'#6e6a60',width:'80%'}}/>
                      <span style={{height:2,background:'#6e6a60',width:'40%'}}/>
                    </div>
                    <div style={{flex:1,background:'#0c0c0a',padding:8,display:'flex',flexDirection:'column',gap:4}}>
                      <span style={{height:3,background:'#f4efe6',width:'60%'}}/>
                      <span style={{height:2,background:'#8c887d',width:'80%'}}/>
                      <span style={{height:2,background:'#8c887d',width:'40%'}}/>
                    </div>
                  </>
                ) : (
                  <div style={{flex:1,background:th.k==='dark'?'#0c0c0a':'#f4efe6',padding:8,display:'flex',flexDirection:'column',gap:4}}>
                    <span style={{height:3,background:th.k==='dark'?'#f4efe6':'#0e0e0c',width:'60%'}}/>
                    <span style={{height:2,background:th.k==='dark'?'#8c887d':'#6e6a60',width:'80%'}}/>
                    <span style={{height:2,background:th.k==='dark'?'#8c887d':'#6e6a60',width:'40%'}}/>
                  </div>
                )}
              </div>
              <div className="ff-mono" style={{fontSize:10,color:'var(--ink-3)',lineHeight:1.4}}>{th.d}</div>
            </button>
          );
        })}
      </div>

      <div style={{height:24}}/>
      <Section num="02">Accent color</Section>
      <div style={{display:'flex',gap:0,borderTop:'1px solid var(--rule-soft)',flexWrap:'wrap'}}>
        {accents.map(a=>{
          const active = accent===a.k;
          return (
            <button key={a.k} onClick={()=>{setAccent(a.k);apply('accent',a.k);}}
              style={{flex:'1 1 120px',padding:'14px 16px',border:'1px solid var(--rule)',
                background:active?'var(--bg-2)':'transparent',display:'flex',alignItems:'center',gap:10,
                outline:active?'2px solid var(--ink)':'none',outlineOffset:'-2px'}}>
              <span style={{width:20,height:20,background:a.c,border:'1px solid var(--rule)'}}/>
              <span style={{fontSize:13,fontWeight:500}}>{a.l}</span>
              {active && <Ic.Check width={12} height={12} style={{marginLeft:'auto'}}/>}
            </button>
          );
        })}
      </div>

      <div style={{height:24}}/>
      <Section num="03">Density</Section>
      <Field label="Layout density" hint="More or less air around content. Affects every screen.">
        <div style={{display:'flex',gap:0,border:'1px solid var(--rule)'}}>
          {['compact','regular','cozy'].map(d=>(
            <button key={d} onClick={()=>{setDensity(d);apply('density',d);}}
              className="ff-mono upper"
              style={{flex:1,padding:'10px 14px',fontSize:11,fontWeight:500,letterSpacing:'.06em',
                background:density===d?'var(--ink)':'transparent',
                color:density===d?'var(--bg)':'var(--ink)',
                borderRight: d!=='cozy'?'1px solid var(--rule)':'none'}}>
              {d}
            </button>
          ))}
        </div>
      </Field>
      <Field label="Heading style" hint="The typeface used for big titles.">
        <div style={{display:'flex',gap:0,border:'1px solid var(--rule)'}}>
          {[{k:'display',l:'Display'},{k:'serif',l:'Serif'},{k:'mono',l:'Mono'}].map(h=>(
            <button key={h.k} onClick={()=>{setHeading(h.k);apply('heading',h.k);}}
              style={{flex:1,padding:'10px 14px',fontSize:14,fontWeight:600,
                fontFamily:h.k==='serif'?'Fraunces, serif':h.k==='mono'?'IBM Plex Mono, monospace':'Space Grotesk, sans-serif',
                background:heading===h.k?'var(--ink)':'transparent',
                color:heading===h.k?'var(--bg)':'var(--ink)',
                borderRight: h.k!=='mono'?'1px solid var(--rule)':'none'}}>
              {h.l}
            </button>
          ))}
        </div>
      </Field>

      <div style={{marginTop:18,padding:'12px 14px',background:'var(--bg-2)',border:'1px solid var(--rule)',fontSize:12,color:'var(--ink-2)',display:'flex',alignItems:'center',gap:10}}>
        <Ic.Check width={14} height={14} style={{color:'var(--ok)'}}/>
        Changes save automatically and apply across all your sessions.
      </div>
    </div>
  );
}

function SettingsWorkspace() {
  return (
    <div>
      <Section num="01">Organization</Section>
      <Field label="Workspace name" hint="Visible across CWR, DDEX, royalty statements"><SInp value="Pluralis Music"/></Field>
      <Field label="Legal entity"><SInp value="Pluralis Music Group LLC"/></Field>
      <Field label="ASTRO ID" hint="Immutable identifier"><SInp value="ws_pl_8821" mono/></Field>
      <Field label="Region" hint="Where data is stored. Affects compliance posture.">
        <select className="ff-mono" style={{padding:'8px 10px',background:'var(--paper)',color:'var(--ink)',border:'1px solid var(--rule)',fontSize:13,minWidth:240}}>
          <option>us-east-1 · Virginia (default)</option><option>eu-west-1 · Ireland</option><option>ap-northeast-1 · Tokyo</option>
        </select>
      </Field>

      <Section num="02">Branding</Section>
      <Field label="Public profile slug" hint="Public artist pages live at /artist/[slug]"><SInp value="pluralis" mono/></Field>
      <Field label="Display style"><Tog value={true} label="Show ASTRO 'verified' badge on public pages"/></Field>
      <Field label="Logo"><div style={{width:80,height:80,border:'1px dashed var(--rule)',display:'flex',alignItems:'center',justifyContent:'center',color:'var(--ink-3)',fontSize:11}} className="ff-mono upper">Drop SVG</div></Field>

      <div style={{marginTop:24,display:'flex',gap:8,justifyContent:'flex-end'}}>
        <Btn variant="secondary" onClick={() => window.toast && window.toast('Changes discarded', 'soft')}>Discard</Btn>
        <Btn variant="primary" icon={<Ic.Check/>} onClick={() => window.toast && window.toast('Workspace settings saved', 'ok')}>Save changes</Btn>
      </div>
    </div>
  );
}

const TEAM = [
  {name:'Anya Cohen',email:'a.cohen@pluralis.com',role:'Owner',mfa:true,last:'2m ago'},
  {name:'Kim Davis',email:'k.davis@pluralis.com',role:'Rights Admin',mfa:true,last:'14m ago'},
  {name:'Marcus Lee',email:'m.lee@pluralis.com',role:'A&R',mfa:true,last:'1h ago'},
  {name:'Rachel Peters',email:'r.peters@pluralis.com',role:'Royalty Analyst',mfa:false,last:'2d ago'},
  {name:'Jorge Núñez',email:'j.nunez@pluralis.com',role:'Read-only',mfa:true,last:'5d ago'},
  {name:'Aisha Patel',email:'a.patel@pluralis.com',role:'Rights Admin',mfa:true,last:'invited'},
];
function SettingsTeam() {
  return (
    <div>
      <Section num="01" action={<Btn variant="primary" size="sm" icon={<Ic.Plus/>} onClick={() => window.toast && window.toast('Invite flow opens · paste an email and a role', 'soft')}>Invite member</Btn>}>Members · 6 active</Section>
      <div className="ff-mono upper" style={{display:'grid',gridTemplateColumns:'1fr 1fr 140px 80px 100px 30px',gap:14,
        padding:'10px 0',fontSize:10,color:'var(--ink-3)',borderBottom:'1px solid var(--rule)'}}>
        <span>NAME</span><span>EMAIL</span><span>ROLE</span><span>MFA</span><span style={{textAlign:'right'}}>LAST SEEN</span><span/>
      </div>
      {TEAM.map((m,i)=>(
        <div key={i} style={{display:'grid',gridTemplateColumns:'1fr 1fr 140px 80px 100px 30px',gap:14,
          padding:'12px 0',borderBottom:'1px solid var(--rule-soft)',alignItems:'center'}}>
          <div style={{display:'flex',alignItems:'center',gap:10}}>
            <span style={{width:28,height:28,background:'var(--bg-2)',display:'inline-flex',alignItems:'center',justifyContent:'center',fontSize:11,fontWeight:600}}>
              {m.name.split(' ').map(n=>n[0]).join('')}
            </span>
            <span style={{fontSize:13,fontWeight:600}}>{m.name}</span>
          </div>
          <span className="ff-mono" style={{fontSize:11,color:'var(--ink-3)'}}>{m.email}</span>
          <Pill tone={m.role==='Owner'?'accent':'soft'}>{m.role}</Pill>
          <span>{m.mfa ? <Pill tone="ok" dot>ON</Pill> : <Pill tone="danger" dot>OFF</Pill>}</span>
          <span className="ff-mono" style={{fontSize:11,color:'var(--ink-3)',textAlign:'right'}}>{m.last}</span>
          <Ic.More width={16} height={16} style={{color:'var(--ink-3)'}}/>
        </div>
      ))}

      <Section num="02">Roles</Section>
      <div style={{display:'grid',gridTemplateColumns:'repeat(2,1fr)',gap:0,borderLeft:'1px solid var(--rule)'}}>
        {[
          {r:'Owner',d:'Full control. Billing, dangerous ops.'},
          {r:'Rights Admin',d:'Catalog, claims, CWR, DDEX. No billing.'},
          {r:'Royalty Analyst',d:'Statements, analytics, exports.'},
          {r:'A&R',d:'Profiles, releases, agreements (read).'},
          {r:'Read-only',d:'Browse everything; can\u2019t edit.'},
          {r:'Custom\u2026',d:'Build a role from 38 atomic permissions.'},
        ].map((r,i)=>(
          <div key={i} style={{padding:'14px 16px',borderRight:'1px solid var(--rule)',borderBottom:'1px solid var(--rule)'}}>
            <div style={{fontSize:14,fontWeight:600,marginBottom:4}}>{r.r}</div>
            <div className="ff-mono" style={{fontSize:11,color:'var(--ink-3)'}}>{r.d}</div>
          </div>
        ))}
      </div>
    </div>
  );
}

function SettingsIdentity() {
  return (
    <div>
      <Section num="01">Authentication</Section>
      <Field label="Provider" hint="Currently bridged through Clerk"><SInp value="Clerk · clerk.pluralis.com" mono/></Field>
      <Field label="MFA required" hint="Block sign-in without MFA"><Tog value={true} label="Enforce for all members"/></Field>
      <Field label="Session length" hint="Idle timeout">
        <select style={{padding:'8px 10px',background:'var(--paper)',color:'var(--ink)',border:'1px solid var(--rule)',fontSize:13}} className="ff-mono">
          <option>30 minutes</option><option>2 hours</option><option>8 hours (default)</option><option>24 hours</option>
        </select>
      </Field>
      <Section num="02">SSO · Enterprise</Section>
      <Field label="Connection" hint="SAML / OIDC for your IdP">
        <div style={{display:'flex',gap:8,alignItems:'center'}}>
          <Pill tone="accent" dot>NOT CONFIGURED</Pill>
          <Btn variant="secondary" size="sm" onClick={() => window.toast && window.toast('SAML config wizard opens — needs IdP metadata XML', 'soft')}>Configure SAML</Btn>
          <Btn variant="ghost" size="sm" onClick={() => window.toast && window.toast('OIDC config wizard opens — needs issuer URL + client credentials', 'soft')}>Configure OIDC</Btn>
        </div>
      </Field>
      <Field label="Domain capture" hint="Auto-route @pluralis.com sign-ins through your IdP"><SInp value="pluralis.com" mono/></Field>
    </div>
  );
}

function SettingsRights() {
  return (
    <div>
      <Section num="01">Default rights</Section>
      <Field label="Default territory" hint="Applied when creating new works"><SInp value="Worldwide"/></Field>
      <Field label="Default split" hint="Even split if multiple writers"><Tog value={true} label="Auto-equalize when adding writer"/></Field>
      <Field label="Mechanical rate" hint="Statutory rate floor for US"><SInp value="9.1¢ / 5:00 max" mono/></Field>
      <Section num="02">Catalog hygiene</Section>
      <Field label="Duplicate detection" hint="Threshold for suggesting merges"><SInp value="0.92 (cosine of normalized title)" mono/></Field>
      <Field label="Auto-match recordings" hint="Match unmatched ISRCs nightly to works by metadata"><Tog value={true} label="Run nightly at 03:15 UTC"/></Field>
      <Field label="Conflict policy">
        <select style={{padding:'8px 10px',background:'var(--paper)',color:'var(--ink)',border:'1px solid var(--rule)',fontSize:13,minWidth:300}} className="ff-mono">
          <option>OPEN — claim auto-opened, both parties notified</option>
          <option>HOLD — payouts paused on contested share</option>
          <option>FREEZE — no CWR re-send until resolved</option>
        </select>
      </Field>
    </div>
  );
}

function SettingsCwr() {
  return (
    <div>
      <Section num="01">Schedule</Section>
      <Field label="Send cadence">
        <select style={{padding:'8px 10px',background:'var(--paper)',color:'var(--ink)',border:'1px solid var(--rule)',fontSize:13}} className="ff-mono">
          <option>Daily · 14:22 UTC (default)</option><option>Weekly · Mon 09:00 UTC</option><option>On-demand only</option>
        </select>
      </Field>
      <Field label="CWR version"><SInp value="v2.1 default · v3.0 for GEMA, PRS" mono/></Field>
      <Field label="DDEX version"><SInp value="ERN 4.3" mono/></Field>
      <Section num="02">Society endpoints</Section>
      <div className="ff-mono" style={{fontSize:11,borderTop:'1px solid var(--rule-soft)'}}>
        {SOCIETIES.map(s=>(
          <div key={s.acronym} style={{display:'grid',gridTemplateColumns:'80px 1fr 100px 90px',gap:14,padding:'12px 0',borderBottom:'1px solid var(--rule-soft)',alignItems:'center'}}>
            <span style={{fontWeight:600}}>{s.acronym}</span>
            <span style={{color:'var(--ink-3)'}}>cwr.{s.acronym.toLowerCase()}.{s.country.toLowerCase()}/v3/inbox</span>
            <span style={{color:'var(--ink-3)'}}>{s.country}</span>
            <Pill tone={s.ackRate>98?'ok':'soft'} dot>{s.ackRate>98?'connected':'partial'}</Pill>
          </div>
        ))}
      </div>
    </div>
  );
}

function SettingsRoyalty() {
  return (
    <div>
      <Section num="01">Statement parsers</Section>
      <div style={{display:'grid',gridTemplateColumns:'repeat(2,1fr)',gap:0,borderLeft:'1px solid var(--rule)'}}>
        {[
          {n:'Spotify CSV · v3', s:'active'},
          {n:'Apple Music TSV', s:'active'},
          {n:'YouTube CMS XML', s:'active'},
          {n:'Amazon Music JSON', s:'active'},
          {n:'TikTok Sound CSV', s:'active'},
          {n:'SoundExchange XLSX', s:'active'},
          {n:'BMI quarterly PDF', s:'beta'},
          {n:'ASCAP quarterly PDF', s:'beta'},
        ].map((p,i)=>(
          <div key={i} style={{padding:'14px',borderRight:'1px solid var(--rule)',borderBottom:'1px solid var(--rule)',display:'flex',justifyContent:'space-between',alignItems:'center'}}>
            <span style={{fontSize:13,fontWeight:600}}>{p.n}</span>
            <Pill tone={p.s==='active'?'ok':'accent'} dot>{p.s}</Pill>
          </div>
        ))}
      </div>
      <Section num="02">FX & accounting</Section>
      <Field label="Reporting currency">
        <select className="ff-mono" defaultValue="USD"
          style={{padding:'8px 10px',background:'var(--paper)',color:'var(--ink)',border:'1px solid var(--rule)',fontSize:13,minWidth:240}}>
          {(() => {
            const REF = window.REF;
            const list = (REF && REF.ready && Array.isArray(REF.currencies) && REF.currencies.length)
              ? REF.currencies.filter(c => c && c.iso_4217).slice().sort((a,b) => String(a.iso_4217).localeCompare(String(b.iso_4217)))
              : [{iso_4217:'USD',name:'US Dollar'},{iso_4217:'EUR',name:'Euro'},{iso_4217:'GBP',name:'British Pound'},{iso_4217:'JPY',name:'Japanese Yen'},{iso_4217:'CAD',name:'Canadian Dollar'},{iso_4217:'AUD',name:'Australian Dollar'}];
            return list.map(c => <option key={c.iso_4217} value={c.iso_4217}>{c.iso_4217} · {c.name||''}</option>);
          })()}
        </select>
      </Field>
      <Field label="FX source"><SInp value="ECB · daily fix · 16:00 CET" mono/></Field>
      <Field label="Recoupment"><Tog value={true} label="Apply unrecouped balance against new earnings"/></Field>
    </div>
  );
}

function SettingsApi() {
  return (
    <div>
      <Section num="01" action={<Btn variant="primary" size="sm" icon={<Ic.Plus/>} onClick={() => window.toast && window.toast('New API token · sk_live_… (copied to clipboard)', 'ok')}>New token</Btn>}>API tokens</Section>
      <div className="ff-mono" style={{fontSize:12,borderTop:'1px solid var(--rule)'}}>
        {[
          {name:'production-ingest', token:'astro_pk_live_2f4a…b91d', scopes:'works:write recordings:write', last:'14m ago'},
          {name:'analytics-readonly', token:'astro_pk_live_8b3e…44c2', scopes:'analytics:read works:read', last:'2h ago'},
          {name:'cwr-bot', token:'astro_pk_live_44e1…ff09', scopes:'cwr:write societies:read', last:'14:22 today'},
        ].map((t,i)=>(
          <div key={i} style={{display:'grid',gridTemplateColumns:'1fr 1fr 1fr 100px 30px',gap:14,padding:'14px 0',borderBottom:'1px solid var(--rule-soft)',alignItems:'center'}}>
            <span style={{fontWeight:600}}>{t.name}</span>
            <span style={{color:'var(--ink-3)'}}>{t.token}</span>
            <span style={{color:'var(--ink-3)',fontSize:11}}>{t.scopes}</span>
            <span style={{color:'var(--ink-3)',textAlign:'right'}}>{t.last}</span>
            <Ic.More width={16} height={16}/>
          </div>
        ))}
      </div>
      <Section num="02">Webhooks</Section>
      <Field label="Endpoint URL"><SInp value="https://hooks.pluralis.com/astro/v3" mono/></Field>
      <Field label="Events" hint="Fire-and-forget POST">
        <div style={{display:'flex',flexWrap:'wrap',gap:6}}>
          {['work.created','work.updated','claim.opened','claim.resolved','cwr.acknowledged','cwr.rejected','royalty.parsed'].map(e=>(
            <Pill key={e} tone="soft">{e}</Pill>
          ))}
        </div>
      </Field>
      <Field label="IP allowlist" hint="CIDR ranges, one per line">
        <textarea defaultValue={'34.96.44.0/24\n52.20.0.0/14'} className="ff-mono"
          style={{width:'100%',minHeight:80,padding:'8px 10px',background:'var(--paper)',border:'1px solid var(--rule)',fontSize:12,outline:'none',resize:'vertical'}}/>
      </Field>
    </div>
  );
}

function SettingsAudit() {
  const log = [
    {t:'14:48', who:'a.cohen', act:'updated split', tgt:'Cranes In The Sky · 62/38 → 62/38', ip:'73.21.x.x'},
    {t:'14:22', who:'system',  act:'CWR generated', tgt:'CW260418ASCAP.V21 · 144 works', ip:'-'},
    {t:'13:55', who:'k.davis', act:'resolved claim', tgt:'C-2026-04-1864 · Far In', ip:'88.10.x.x'},
    {t:'12:18', who:'m.lee',   act:'created agreement', tgt:'AG-2025-0101 · Forever Living × TM Works', ip:'73.21.x.x'},
    {t:'11:42', who:'a.cohen', act:'invited member', tgt:'a.patel@pluralis.com (Rights Admin)', ip:'73.21.x.x'},
    {t:'10:09', who:'r.peters',act:'imported statement', tgt:'SoundExchange Q3 2025 · 84,221 rows', ip:'162.4.x.x'},
    {t:'09:48', who:'system',  act:'webhook fired', tgt:'royalty.parsed → hooks.pluralis.com (200)', ip:'-'},
    {t:'08:22', who:'k.davis', act:'flagged duplicate', tgt:'Profile · Solange (× 3 candidates)', ip:'88.10.x.x'},
  ];
  return (
    <div>
      <Section num="01" action={<Btn variant="secondary" size="sm" icon={<Ic.File/>} onClick={() => window.toast && window.toast('Activity log exported · audit-' + new Date().toISOString().slice(0,10) + '.csv', 'ok')}>Export CSV</Btn>}>Activity · last 24h</Section>
      <div className="ff-mono" style={{fontSize:12,borderTop:'1px solid var(--rule)'}}>
        {log.map((l,i)=>(
          <div key={i} style={{display:'grid',gridTemplateColumns:'60px 100px 140px 1fr 90px',gap:14,padding:'12px 0',borderBottom:'1px solid var(--rule-soft)'}}>
            <span style={{color:'var(--ink-4)'}}>{l.t}</span>
            <span style={{color:'var(--ink-3)'}}>{l.who}</span>
            <span style={{fontWeight:600}}>{l.act}</span>
            <span>{l.tgt}</span>
            <span style={{color:'var(--ink-4)',textAlign:'right'}}>{l.ip}</span>
          </div>
        ))}
      </div>
    </div>
  );
}

function SettingsBilling() {
  return (
    <div>
      <Section num="01">Plan</Section>
      <div style={{padding:'24px',border:'2px solid var(--ink)',display:'flex',justifyContent:'space-between',alignItems:'center',marginBottom:24,background:'var(--accent)',color:'var(--accent-ink)'}}>
        <div>
          <div className="ff-mono upper" style={{fontSize:11,fontWeight:600,marginBottom:4}}>CURRENT PLAN</div>
          <div className="heading-swap ff-display" style={{fontSize:36,fontWeight:700,letterSpacing:'-0.03em',lineHeight:1}}>Catalog · Pro</div>
          <div className="ff-mono" style={{fontSize:11,marginTop:8}}>{(window.CATALOG_STATS?.works?.total || 18422).toLocaleString()} / 25,000 works · 6 / 10 seats · all DSPs · all societies</div>
        </div>
        <div style={{textAlign:'right'}}>
          <div className="ff-display num" style={{fontSize:42,fontWeight:700,letterSpacing:'-0.03em',lineHeight:1}}>$1,490</div>
          <div className="ff-mono upper" style={{fontSize:10,fontWeight:600}}>/ MONTH · BILLED ANNUALLY</div>
        </div>
      </div>
      <Section num="02">Recent invoices</Section>
      <div className="ff-mono" style={{fontSize:12,borderTop:'1px solid var(--rule)'}}>
        {['2026-04-01','2026-03-01','2026-02-01','2026-01-01','2025-12-01'].map((d,i)=>(
          <div key={d} style={{display:'grid',gridTemplateColumns:'120px 1fr 120px 100px 80px',gap:14,padding:'12px 0',borderBottom:'1px solid var(--rule-soft)',alignItems:'center'}}>
            <span>{d}</span>
            <span>Catalog Pro · monthly</span>
            <span className="num">$1,490.00</span>
            <Pill tone="ok" dot>paid</Pill>
            <a onClick={(e)=>{ e.stopPropagation(); window.dispatchEvent(new CustomEvent('astro-toast',{detail:{msg:'Invoice PDF downloaded',tone:'ok'}})); }} style={{textAlign:'right',borderBottom:'1px solid var(--ink)',cursor:'pointer'}}>PDF →</a>
          </div>
        ))}
      </div>
    </div>
  );
}

function SettingsDanger() {
  return (
    <div>
      <Section num="01">Export</Section>
      <Field label="Full catalog export" hint="ZIP of CSV + DDEX ERN 4.3 + CWR v3 + raw JSON. Takes ~20m for this catalog size.">
        <Btn variant="secondary" icon={<Ic.File/>} onClick={() => window.toast && window.toast('Catalog export queued · you\u2019ll get an email when it\u2019s ready', 'ok')}>Generate export</Btn>
      </Field>

      <Section num="02">Transfer ownership</Section>
      <Field label="New owner email" hint="Will receive a 7-day approval link.">
        <div style={{display:'flex',gap:8,alignItems:'flex-start'}}>
          <div style={{flex:1}}>
            <window.EmailField
              value={(window.__transferEmail || '')}
              onChange={v => { window.__transferEmail = v; }}
              style={{width:'100%',height:32,padding:'0 10px',background:'var(--bg)',border:'1px solid var(--rule)',fontSize:13,color:'var(--ink)',outline:'none',boxSizing:'border-box'}}/>
          </div>
          <Btn variant="secondary" onClick={() => window.toast && window.toast('Ownership transfer link sent · expires in 7 days', 'ok')}>Initiate</Btn>
        </div>
      </Field>

      <div style={{marginTop:32,padding:'24px',border:'2px solid var(--danger)',background:'transparent'}}>
        <div className="ff-mono upper" style={{fontSize:11,fontWeight:700,letterSpacing:'.08em',color:'var(--danger)',marginBottom:6}}>DELETE WORKSPACE</div>
        <p style={{fontSize:13,color:'var(--ink-2)',maxWidth:520,lineHeight:1.5,marginBottom:16}}>
          Permanently deletes all works, recordings, releases, agreements, royalty history, and audit log.
          Society registrations remain — they live with the PRO. <b>This cannot be undone.</b>
        </p>
        <Btn variant="danger" icon={<Ic.X/>} onClick={() => { if (window.confirm && window.confirm('This permanently deletes the workspace. Are you absolutely sure?')) { window.toast && window.toast('Deletion request submitted \u2014 7-day grace period before final delete', 'soft'); } }}>Delete Pluralis Music</Btn>
      </div>
    </div>
  );
}

Object.assign(window, { ScreenSettings, RecordingsView, ReleasesView, PublishersView, LabelsView, AgreementsView });
