/** @jsx React.createElement */
/* ActiveProjectsBoard — data model, live state hook and card primitives for
   the interactive Active Projects Overview (Ipsen RFP §2.5).
   All companies/assets are fictive. Every count on the board derives from
   this state, so totals stay accurate as opportunities move. */

const APO_PHASES = {
  platform: { label: 'Platform / Discovery', fill: '#FFFFFF', border: '#C2CCDA', text: '#33415A' },
  pc:       { label: 'PC',                    fill: '#DCEAFB', border: '#A9CBF0', text: '#1E3A6B' },
  ph1:      { label: 'Ph 1',                  fill: '#7FB2E6', border: '#5C97D8', text: '#0B2D5E' },
  ph2:      { label: 'Ph 2',                  fill: '#2C44D0', border: '#2235AB', text: '#FFFFFF' },
  ph3:      { label: 'Ph 3',                  fill: '#CFC9A8', border: '#B0A981', text: '#3A3620' },
  approved: { label: 'Approved Marketed',     fill: '#9A8C5A', border: '#7C7043', text: '#FFFFFF' },
};
const APO_PHASE_ORDER = ['platform', 'pc', 'ph1', 'ph2', 'ph3', 'approved'];

const APO_TAS = [
  { key: 'onc',   name: 'Oncology',     accent: '#0B2D5E' },
  { key: 'neuro', name: 'Neuroscience', accent: '#3F7BC4' },
  { key: 'rare',  name: 'Rare Disease', accent: '#7FB2E6' },
  { key: 'other', name: 'Others',       accent: '#8DA2BC' },
];

const APO_STAGES = [
  { key: 'early',  label: 'Early Eval.',  sub: 'Pre CDA',              tint: '#F5F6F8', head: '#E7E9ED', headText: '#3A4250' },
  { key: 'deeper', label: 'Deeper Eval.', sub: 'CDA',                  tint: '#FEF7E6', head: '#F7C948', headText: '#5A4708' },
  { key: 'dd',     label: 'DD / NBO',     sub: 'Negotiation',          tint: '#FCEEEA', head: '#F4A48C', headText: '#7A2E16' },
  { key: 'final',  label: 'Final DD',     sub: 'Contract negotiation', tint: '#EEF6E9', head: '#8FCB76', headText: '#2C4A1C' },
];

const APO_UPCOMING = {
  onc:   { platform: 21, pc: 44, ph1: 50, ph2: 19, ph3: 5, approved: 0 },
  neuro: { platform: 10, pc: 17, ph1: 8,  ph2: 7,  ph3: 2, approved: 3 },
  rare:  { platform: 12, pc: 30, ph1: 7,  ph2: 11, ph3: 2, approved: 0 },
  other: { platform: 0,  pc: 0,  ph1: 0,  ph2: 0,  ph3: 0, approved: 0 },
};

const APO_STATUS = {
  nbo:        'NBO Submitted',
  new:        'New Project',
  damGreen:   'Presented in DAM',
  damBlue:    'Ready to go / Will be presented in DAM',
  moved:      'Just moved to the next step',
  onHold:     'On hold vs last week',
  terminated: 'Terminated vs last week',
  monitoring: 'Under monitoring vs last week',
  priority:   'Top priority projects',
};
const APO_FLAG_ORDER = ['priority', 'nbo', 'new', 'damGreen', 'damBlue', 'moved', 'onHold', 'terminated', 'monitoring'];
const APO_DEALS = ['In-licensing (global)', 'In-licensing (ex-US)', 'In-licensing', 'Acquisition', 'Option-to-license', 'Co-development', 'Co-development & co-commercialization', 'Co-promotion', 'Research collaboration', 'Sponsored research agreement', 'Strategic partnership', 'Technology license'];
// Canonical company/asset catalogue sourced from the Ipsen demo data (used by the drawer combo-boxes).
const APO_XLSX_ASSETS = [
  { company: 'Helixar Therapeutics', asset: 'HLX-201' },
  { company: 'Neurovance Biosciences', asset: 'NVB-114' },
  { company: 'Orphalux Pharma', asset: 'ORP-330' },
  { company: 'Calyx Oncology', asset: 'CLX-7' },
  { company: 'Synaptiq Bio', asset: 'SYN-040' },
  { company: 'RarePath Genomics', asset: 'RPG-AAV-12' },
  { company: 'Lakeshore University Cancer Center', asset: 'LU-TIL-2' },
  { company: 'Meridian Institute of Molecular Medicine', asset: 'MRD-Tau Program' },
  { company: 'Universit\u00e9 Lumi\u00e8re de Lyon \u2014 Institut de Neuroth\u00e9rapeutique', asset: 'ULL-NeuroX' },
  { company: 'Westgate University School of Medicine', asset: 'WSM-RareDx' },
];
const APO_LEADS = ['Andrew Thomson', 'Matthew Beard', 'Jean-Baptiste de Peretti', 'Alexander Thys', 'Jon Travers', 'Mehdi Zouaoui'];

const P = (id, ta, stage, phase, flags, company, asset, ind, mod, deal, lead, next, up, notes) =>
  ({ id, ta, stage, phase, flags, company, asset, ind, mod, deal, lead, next, up, notes: notes || '' });

const APO_SEED = [
  // ── Oncology · Early Eval. (above line) ──
  P('p01','onc','early','pc',['damBlue'],'Veltrion Bio','VLT-214','Pancreatic cancer (KRAS G12D)','Small molecule','In-licensing (global)','Jean-Baptiste de Peretti','DAM presentation Jul 29','Jul 21'),
  P('p02','onc','early','pc',['damBlue'],'Kaidos Onc','KDS-88','Gastric cancer (CLDN18.2)','ADC','In-licensing (ex-US)','Alexander Thys','DAM presentation Jul 29','Jul 21'),
  P('p03','onc','early','platform',['new'],'Lakeshore University Cancer Center','LU-TIL-2','Solid tumors (NY-ESO-1)','Cell therapy (TIL)','Sponsored research agreement','Jon Travers','Scope sponsored research','Jul 22','Sponsored research to advance a NY-ESO-1 TIL cell therapy toward IND.'),
  P('p04','onc','early','ph1',[],'Helixar Therapeutics','HLX-201','Solid tumors (TROP2)','Antibody-drug conjugate (ADC)','Co-development & co-commercialization','Alexander Thys','Ph 1 dose-escalation review','Jul 18','Co-development & co-commercialization of a TROP2 ADC in solid tumors; partner to lead ex-US.'),
  P('p05','onc','early','ph1',['damBlue'],'Quantia Bio','QTB-301','Multiple myeloma (GPRC5D)','T-cell engager','In-licensing (ex-US)','Alexander Thys','DAM presentation Aug 5','Jul 21'),
  P('p06','onc','early','ph2',['monitoring'],'Auralis Onc','AUR-509','NSCLC (HER3)','Bispecific mAb','Co-development','Matthew Beard','Awaiting Ph 2 interim','Jul 15'),
  // ── Oncology · Early Eval. (below line — Ph 3 / Approved) ──
  P('p07','onc','early','ph3',[],'Calyx Oncology','CLX-7','NSCLC (TIGIT)','Monoclonal antibody (IO)','Co-promotion','Jean-Baptiste de Peretti','Ph 3 readout tracking','Jul 18','Co-promotion of a Phase 3 anti-TIGIT antibody in NSCLC; shared US commercial effort.'),
  P('p08','onc','early','ph3',['terminated'],'Silvane Bio','SVB-77','CRC (WRN)','Small molecule','Acquisition','Mehdi Zouaoui','Passed \u2014 competitive','Jul 11'),
  P('p09','onc','early','ph3',[],'Vantor Bio','VNT-220','mCRPC (PSMA)','Radioligand','Acquisition','Andrew Thomson','Ph 3 readout Q1 2027','Jul 22'),
  // ── Oncology · Deeper Eval. ──
  P('p10','onc','deeper','ph2',['nbo'],'Kestrel Bio','KST-616','GEP-NET (SSTR2)','Radioligand','Acquisition','Jean-Baptiste de Peretti','NBO response due Aug 8','Jul 21','NBO submitted Jul 18. Banker: Ridgeline Partners.'),
  P('p11','onc','deeper','ph3',['damGreen','priority'],'Peralta Bio','PRL-777','CRC (KRAS G12C combo)','Small molecule','Acquisition','Mehdi Zouaoui','DD scope sign-off','Jul 20'),
  P('p12','onc','deeper','approved',['damGreen'],'Casseia Pharma','CSP-410','HCC (GPC3)','mAb','Co-development','Matthew Beard','Data room review','Jul 19'),
  // ── Oncology · DD / NBO ──
  P('p13','onc','dd','ph2',['damGreen'],'Oriona Tx','ORN-350','NSCLC (DLL3)','Radioligand','In-licensing (global)','Alexander Thys','Data room access granted','Jul 17'),
  P('p14','onc','dd','approved',['damGreen'],'Tavena Bio','TVN-902','Gastric cancer (FGFR2b)','ADC','In-licensing (global)','Alexander Thys','NBO drafting','Jul 17'),
  // ── Oncology · Final DD ──
  P('p15','onc','final','pc',[],'Lucerna Onc','LCN-121','Uveal melanoma','Small molecule','In-licensing (global)','Jean-Baptiste de Peretti','Term sheet redlines','Jul 20'),
  P('p16','onc','final','ph1',[],'Zephyra Onc','ZPH-233','AML (CD123)','ADC','In-licensing (ex-US)','Andrew Thomson','Contract negotiation','Jul 18'),
  // ── Neuroscience · Early Eval. ──
  P('p17','neuro','early','platform',[],'Synaptiq Bio','SYN-040','FTD (GRN)','Gene therapy platform','Research collaboration','Matthew Beard','Platform vs single-target scope','Jul 16','Research collaboration on an AAV gene-therapy platform for GRN-related neurodegeneration.'),
  P('p18','neuro','early','pc',[],'Meridian Institute of Molecular Medicine','MRD-Tau Program','Tauopathies (Tau)','Small molecule','Sponsored research agreement','Matthew Beard','Draft sponsored-research scope','Jul 15','Sponsored research on tau-targeting small molecules; access to HTS platform.'),
  P('p19','neuro','early','ph1',['new'],'Thalora Bio','THL-201','Essential tremor (T-type Ca)','Small molecule','In-licensing (global)','Jon Travers','Kick-off review Jul 27','Jul 22'),
  P('p20','neuro','early','ph3',['terminated'],'Nuvexa Bio','NVX-77','Neuropathic pain','Small molecule','In-licensing (ex-US)','Mehdi Zouaoui','Passed \u2014 crowded space','Jul 20'),
  // ── Neuroscience · Deeper Eval. ──
  P('p21','neuro','deeper','pc',['moved'],'Universit\u00e9 Lumi\u00e8re de Lyon \u2014 Institut de Neuroth\u00e9rapeutique','ULL-NeuroX','Neurodev. (mGluR5)','Small molecule','Technology license','Matthew Beard','Licensing proposal prep','Jul 21','Exclusive license to a preclinical mGluR5 modulator series from a Lyon spin-out.'),
  P('p22','neuro','deeper','ph2',['priority'],'Neurovance Biosciences','NVB-114','Narcolepsy (OX2R)','Small molecule (CNS)','In-licensing','Jon Travers','Ph 2 interim under review','Jul 19','In-license a Phase 2 OX2R agonist for narcolepsy; global rights, competitive process.'),
  // ── Neuroscience · DD / NBO ──
  P('p23','neuro','dd','pc',['priority'],'Elowen Bio','ELW-212','Refractory epilepsy','Small molecule','In-licensing (global)','Jon Travers','Full DD scope sign-off','Jul 19'),
  // ── Neuroscience · Final DD ──
  P('p24','neuro','final','pc',[],'Neralia Neuro','NRL-450','Focal epilepsy (Kv7)','Small molecule','In-licensing (global)','Mehdi Zouaoui','Term sheet redlines w/c Jul 27','Jul 21','Exclusivity through Aug 31.'),
  // ── Rare Disease · Early Eval. ──
  P('p25','rare','early','platform',[],'Westgate University School of Medicine','WSM-RareDx','Rare-disease Dx panel','Companion diagnostic','Technology license','Jon Travers','Assess strategic fit','Jul 16','License a companion-diagnostic platform for rare-disease trial stratification.'),
  P('p26','rare','early','ph1',[],'Fabrion Tx','FBR-101','Fabry disease','Gene therapy','In-licensing (global)','Andrew Thomson','Ph 1 6-mo data Q4 2026','Jul 15'),
  P('p27','rare','early','ph2',[],'Cuprida Tx','CPR-330','Wilson disease','Small molecule','In-licensing (ex-US)','Alexander Thys','Awaiting Ph 2 full data','Jul 15'),
  P('p28','rare','early','ph3',['onHold'],'Solvena Pharma','SLV-88','Phenylketonuria (PKU)','Enzyme therapy','Acquisition','Andrew Thomson','On hold \u2014 financing round','Jul 20','Paused pending the seller Series D close.'),
  // ── Rare Disease · Deeper Eval. ──
  P('p29','rare','deeper','pc',['moved'],'RarePath Genomics','RPG-AAV-12','Wilson disease (ATP7B)','AAV gene therapy','Option-to-license','Andrew Thomson','Option-to-license terms','Jul 22','Option-to-license an IND-enabling AAV gene therapy for Wilson disease.'),
  P('p30','rare','deeper','ph2',['priority','damGreen'],'Orphalux Pharma','ORP-330','Fabry disease (GLA)','Enzyme replacement therapy','Strategic partnership','Andrew Thomson','Heads of terms before pivotal','Jul 19','Strategic partnership to co-develop/commercialize an ERT for Fabry; term sheet exchanged.'),
  // ── Rare Disease · DD / NBO ──
  P('p31','rare','dd','ph2',['priority'],'Amyra Bio','AMY-15','ATTR amyloidosis','siRNA','Co-development','Mehdi Zouaoui','NBO preparation','Jul 14'),
  // ── Rare Disease · Final DD ──
  P('p32','rare','final','pc',[],'Halden Tx','HLD-90','MS (remyelination)','mAb','In-licensing (global)','Jon Travers','Contract negotiation','Jul 17'),
  // ── Others · Early Eval. ──
  P('p33','other','early','platform',[],'Boreas Immuno','BRS-40','IBD (TL1A)','mAb','In-licensing (global)','Jean-Baptiste de Peretti','Non-conf. deck received','Jul 16'),
  P('p34','other','early','pc',[],'Selencia Bio','SLN-22','Chronic kidney disease','Small molecule','In-licensing (ex-US)','Alexander Thys','Sci. diligence call Aug 4','Jul 15'),
  P('p35','other','early','ph2',[],'Marnix Tx','MNX-360','Systemic sclerosis','mAb','Co-development','Matthew Beard','Awaiting Ph 2 interim','Jul 14'),
  // ── Others · Deeper Eval. ──
  P('p36','other','deeper','pc',['moved'],'Corveon Bio','CVN-88','Primary biliary cholangitis','Small molecule','In-licensing (global)','Jon Travers','CDA signed Jul 20','Jul 20'),
  P('p37','other','deeper','ph2',['priority'],'Delphia Tx','DLP-14','Endometriosis','Small molecule','Acquisition','Mehdi Zouaoui','DD scope sign-off','Jul 19'),
  // ── Others · DD / NBO ──
  P('p38','other','dd','ph2',[],'Ravenna Bio','RVN-501','NASH (THR-\u03b2)','Small molecule','In-licensing (global)','Andrew Thomson','Data room access','Jul 18'),
];

// ── Live state (persists across refresh) ────────────────────────────────
const APO_LS_KEY = 'ab.apo.projects.v4';
function useApoProjects() {
  const [projects, setProjects] = React.useState(() => {
    try {
      const raw = localStorage.getItem(APO_LS_KEY);
      if (raw) { const arr = JSON.parse(raw); if (Array.isArray(arr) && arr.length && arr[0].id && arr[0].company) return arr; }
    } catch (e) {}
    return APO_SEED;
  });
  React.useEffect(() => { try { localStorage.setItem(APO_LS_KEY, JSON.stringify(projects)); } catch (e) {} }, [projects]);
  const update = (id, patch) => setProjects(ps => ps.map(p => p.id === id ? { ...p, ...patch, up: 'Just now' } : p));
  const move = (id, patch) => setProjects(ps => ps.map(p => {
    if (p.id !== id) return p;
    const next = { ...p, ...patch, up: 'Just now' };
    if (patch.stage && patch.stage !== p.stage) {
      const from = APO_STAGES.findIndex(s => s.key === p.stage);
      const to = APO_STAGES.findIndex(s => s.key === patch.stage);
      let flags = p.flags.filter(f => f !== 'moved');
      if (to > from) flags = flags.concat('moved');
      next.flags = flags;
    }
    return next;
  }));
  const toggleFlag = (id, flag) => setProjects(ps => ps.map(p => p.id === id
    ? { ...p, up: 'Just now', flags: p.flags.includes(flag) ? p.flags.filter(f => f !== flag) : [...p.flags, flag] }
    : p));
  const add = () => {
    const id = 'p' + Date.now();
    setProjects(ps => [...ps, P(id, 'onc', 'early', 'platform', ['new'], 'New company', 'TBD', '', '', 'In-licensing (global)', '', '', 'Just now')]);
    return id;
  };
  const addProject = (data) => {
    const id = 'p' + Date.now();
    setProjects(ps => [...ps, { ...data, id, up: 'Just now' }]);
    return id;
  };
  const replace = (id, project) => setProjects(ps => ps.map(p => p.id === id ? { ...project } : p));
  const remove = (id) => setProjects(ps => ps.filter(p => p.id !== id));
  const reset = () => setProjects(APO_SEED);
  return { projects, update, move, toggleFlag, add, addProject, replace, remove, reset };
}

const apoIsBelow = (p) => p.phase === 'ph3' || p.phase === 'approved';
const apoStageCount = (projects, key) => projects.filter(p => p.stage === key).length;
const apoTaCount = (projects, key) => projects.filter(p => p.ta === key && !p.flags.includes('terminated')).length;
function apoDescribeMove(p, patch) {
  const parts = [];
  if (patch.stage && patch.stage !== p.stage) parts.push(APO_STAGES.find(s => s.key === patch.stage).label);
  if (patch.ta && patch.ta !== p.ta) parts.push(APO_TAS.find(t => t.key === patch.ta).name);
  if (!parts.length) return null;
  return `${p.company} \u2013 ${p.asset} \u2192 ${parts.join(' \u00b7 ')}`;
}

// ── Badge primitives ────────────────────────────────────────────────────
function MiniTag({ bg, text = '#fff', children, title }) {
  return (
    <span title={title} style={{
      display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
      height: 13, padding: '0 4px', borderRadius: 3,
      background: bg, color: text,
      fontSize: 8, fontWeight: 800, letterSpacing: '.04em',
      lineHeight: 1, flexShrink: 0,
    }}>{children}</span>
  );
}

const apoBadgeChip = { display: 'inline-flex', alignItems: 'center', justifyContent: 'center', width: 15, height: 14, borderRadius: 4, background: '#fff', border: '1px solid rgba(15,23,42,.16)', flexShrink: 0 };

function StatusBadge({ flag }) {
  const title = APO_STATUS[flag];
  switch (flag) {
    case 'new':      return <MiniTag bg="#E5484D" title={title}>NEW</MiniTag>;
    case 'damGreen': return <MiniTag bg="#16A34A" title={title}>DAM</MiniTag>;
    case 'damBlue':  return <MiniTag bg="#3057D6" title={title}>DAM</MiniTag>;
    case 'nbo':      return (
      <span title={title} style={apoBadgeChip}>
        <svg width="11" height="11" viewBox="0 0 24 24" fill="#F5B312" stroke="#C98A06" strokeWidth="1"><path d="M12 2l2.9 6.3 6.9.7-5.1 4.6 1.4 6.8L12 17.8 5.9 21l1.4-6.8L2.2 9.6l6.9-.7z"/></svg>
      </span>
    );
    case 'onHold':   return (
      <span title={title} style={apoBadgeChip}>
        <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="#E08A0B" strokeWidth="2.4" strokeLinecap="round"><path d="M9 6v12M15 6v12"/></svg>
      </span>
    );
    case 'monitoring': return (
      <span title={title} style={apoBadgeChip}>
        <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#7C3AED" strokeWidth="2"><path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7z"/><circle cx="12" cy="12" r="2.6" fill="#7C3AED" stroke="none"/></svg>
      </span>
    );
    default: return null;
  }
}

function FlagGlyph({ flag }) {
  if (flag === 'priority') return <span style={{ width: 15, height: 13, borderRadius: 4, border: '2px solid #E5484D', flexShrink: 0, display: 'inline-block', boxSizing: 'border-box' }} />;
  if (flag === 'terminated') return (
    <span style={{ position: 'relative', width: 15, height: 13, borderRadius: 3, border: '1px solid var(--ab-border-strong)', background: '#fff', flexShrink: 0, overflow: 'hidden', display: 'inline-block', boxSizing: 'border-box' }}>
      <span style={{ position: 'absolute', left: -1, right: -1, top: '50%', height: 2, background: '#E5484D', transform: 'rotate(-20deg)' }} />
    </span>
  );
  if (flag === 'moved') return (
    <span style={{ display: 'inline-flex', flexShrink: 0 }}>
      <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="#16A34A" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round"><path d="M5 12h13M13 6l6 6-6 6"/></svg>
    </span>
  );
  return <StatusBadge flag={flag} />;
}

// ── Project chip (draggable, hoverable, clickable) ──────────────────────
function ProjectChip({ project, onOpen, onHover, highlight }) {
  const ph = APO_PHASES[project.phase];
  const flags = project.flags || [];
  const priority = flags.includes('priority');
  const terminated = flags.includes('terminated');
  const moved = flags.includes('moved');
  const badges = flags.filter(f => !['priority', 'terminated', 'moved'].includes(f));
  const hlOn = !!highlight;
  const hlMatch = hlOn && (highlight.type === 'phase' ? project.phase === highlight.key : flags.includes(highlight.key));
  return (
    <div className="apo-chip" draggable
      onDragStart={(e) => {
        e.dataTransfer.effectAllowed = 'move';
        try { e.dataTransfer.setData('text/plain', project.id); } catch (_) {}
        window.__apoDragId = project.id;
        if (onHover) onHover(null);
      }}
      onDragEnd={() => { window.__apoDragId = null; }}
      onMouseEnter={(e) => onHover && onHover(project, e.currentTarget.getBoundingClientRect())}
      onMouseLeave={() => onHover && onHover(null)}
      onClick={() => onOpen && onOpen(project.id)}
      style={{
        position: 'relative',
        display: 'flex', alignItems: 'center', gap: 5,
        padding: moved ? '5px 7px 5px 4px' : '5px 7px',
        borderRadius: 7,
        background: ph.fill,
        border: `1px solid ${ph.border}`,
        outline: priority ? '2px solid #E5484D' : 'none',
        opacity: hlOn && !hlMatch ? 0.24 : (terminated ? 0.78 : 1),
        boxShadow: hlMatch ? '0 0 0 2px #2C44D0, 0 6px 14px rgba(15,23,42,.22)' : 'none',
        transform: hlMatch ? 'translateY(-1px)' : 'none',
        zIndex: hlMatch ? 3 : 'auto',
        transition: 'opacity 130ms var(--ab-ease), box-shadow 130ms var(--ab-ease), transform 130ms var(--ab-ease)',
        overflow: 'hidden',
        minWidth: 0,
      }}>
      {moved && (
        <span style={{ display: 'inline-flex', flexShrink: 0 }}>
          <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="#16A34A" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round"><path d="M5 12h13M13 6l6 6-6 6"/></svg>
        </span>
      )}
      <span style={{ flex: 1, minWidth: 0, display: 'flex', flexDirection: 'column', gap: 1, lineHeight: 1.15 }}>
        <span style={{ fontSize: 11, fontWeight: 700, color: ph.text, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{project.company}</span>
        <span style={{ fontSize: 10, fontWeight: 500, color: ph.text, opacity: .78, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{project.asset}</span>
      </span>
      {badges.length > 0 && (
        <span style={{ display: 'grid', gridAutoFlow: 'column', gridTemplateRows: 'repeat(2, auto)', gap: 3, justifyItems: 'end', alignContent: 'center', flexShrink: 0 }}>
          {badges.map((f, i) => <StatusBadge key={i} flag={f} />)}
        </span>
      )}
      {terminated && (
        <span style={{
          position: 'absolute', left: 0, right: 0, top: '50%',
          height: 2, background: '#E5484D', transform: 'rotate(-9deg)',
          transformOrigin: 'center', pointerEvents: 'none',
        }} />
      )}
    </div>
  );
}

// ── Status multi-toggle (drawer + table popover) ────────────────────────
function StatusPicker({ flags, onToggle }) {
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 5 }}>
      {APO_FLAG_ORDER.map(f => {
        const on = flags.includes(f);
        return (
          <button key={f} type="button" className="apo-flag" data-on={String(on)} onClick={() => onToggle(f)}>
            <FlagGlyph flag={f} />
            <span style={{ flex: 1, textAlign: 'left', lineHeight: 1.25 }}>{APO_STATUS[f]}</span>
            <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke={on ? 'var(--ab-blue-500)' : 'transparent'} strokeWidth="2.8" strokeLinecap="round" strokeLinejoin="round" style={{ flexShrink: 0 }}><path d="M5 12l5 5L20 7"/></svg>
          </button>
        );
      })}
    </div>
  );
}

// ── Hover tooltip ───────────────────────────────────────────────────────
function APOTooltip({ tip }) {
  if (!tip) return null;
  const { p, rect } = tip;
  const ph = APO_PHASES[p.phase];
  const st = APO_STAGES.find(s => s.key === p.stage);
  const ta = APO_TAS.find(t => t.key === p.ta);
  const W = 300;
  const z = document.documentElement.currentCSSZoom || 1;
  const R = { left: rect.left / z, width: rect.width / z, top: rect.top / z, bottom: rect.bottom / z };
  const left = Math.max(10, Math.min(R.left + R.width / 2 - W / 2, window.innerWidth / z - W - 10));
  const below = R.bottom + 300 < window.innerHeight / z;
  const pos = below ? { left, top: R.bottom + 8 } : { left, top: R.top - 8, transform: 'translateY(-100%)' };
  const pill = { display: 'inline-flex', alignItems: 'center', gap: 5, padding: '2px 8px', borderRadius: 999, fontSize: 11, fontWeight: 600, border: '1px solid', whiteSpace: 'nowrap' };
  const metaRows = [['Indication', p.ind], ['Modality', p.mod], ['Deal type', p.deal], ['BD lead', p.lead], ['Next step', p.next]].filter(r => r[1]);
  return (
    <div className="apo-tip" style={{ ...pos, width: W, boxSizing: 'border-box' }}>
      <div style={{ fontSize: 13.5, fontWeight: 600, color: 'var(--ab-navy-700)', marginBottom: 7 }}>{p.company + ' \u2013 ' + p.asset}</div>
      <div style={{ display: 'flex', flexWrap: 'wrap', gap: 5, marginBottom: metaRows.length ? 10 : 0 }}>
        <span style={{ ...pill, background: ph.fill, borderColor: ph.border, color: ph.text }}>{ph.label}</span>
        <span style={{ ...pill, background: st.tint, borderColor: st.head, color: st.headText }}>{st.label}</span>
        <span style={{ ...pill, background: '#fff', borderColor: 'var(--ab-border)', color: 'var(--ab-slate-600)' }}>
          <span style={{ width: 7, height: 7, borderRadius: '50%', background: ta.accent }} />{ta.name}
        </span>
      </div>
      {metaRows.length > 0 && (
        <div style={{ display: 'grid', gridTemplateColumns: '80px 1fr', gap: '4px 10px' }}>
          {metaRows.map(([k, v]) => (
            <React.Fragment key={k}>
              <span style={{ fontSize: 10, fontWeight: 700, letterSpacing: '.05em', textTransform: 'uppercase', color: 'var(--ab-slate-400)', paddingTop: 1 }}>{k}</span>
              <span style={{ fontSize: 12, color: 'var(--ab-slate-700)', lineHeight: 1.35 }}>{v}</span>
            </React.Fragment>
          ))}
        </div>
      )}
      {p.flags.length > 0 && (
        <div style={{ display: 'flex', flexDirection: 'column', gap: 4, marginTop: 10, paddingTop: 9, borderTop: '1px solid var(--ab-slate-100)' }}>
          {p.flags.map(f => (
            <span key={f} style={{ display: 'flex', alignItems: 'center', gap: 7, fontSize: 11.5, color: 'var(--ab-slate-600)' }}>
              <FlagGlyph flag={f} />{APO_STATUS[f]}
            </span>
          ))}
        </div>
      )}
      {p.notes && <div style={{ marginTop: 9, fontSize: 11.5, fontStyle: 'italic', color: 'var(--ab-slate-500)', lineHeight: 1.4 }}>{p.notes}</div>}
      <div style={{ marginTop: 10, paddingTop: 8, borderTop: '1px solid var(--ab-slate-100)', fontSize: 10.5, color: 'var(--ab-slate-400)' }}>
        {`Updated ${p.up} \u00b7 Click to edit \u00b7 Drag to change stage`}
      </div>
    </div>
  );
}

Object.assign(window, {
  APO_PHASES, APO_PHASE_ORDER, APO_TAS, APO_STAGES, APO_UPCOMING, APO_STATUS,
  APO_FLAG_ORDER, APO_DEALS, APO_LEADS, APO_XLSX_ASSETS, APO_SEED,
  useApoProjects, apoStageCount, apoTaCount, apoDescribeMove, apoIsBelow,
  MiniTag, StatusBadge, FlagGlyph, ProjectChip, StatusPicker, APOTooltip,
});
