// imprint-hierarchy.jsx — Publisher imprint hierarchy explorer.
// Renders the publisher catalog as a centerpiece tree (with list-view tweak),
// node cards with metadata, side detail panel, filters, and re-parent / add / archive actions.

(function () {
  const { useMemo, useState, useEffect, useRef } = React;

  // ── seed: extend window.PUBLISHERS to a richer hierarchy ───────────
  function ensureSeed() {
    const base = window.PUBLISHERS || [];
    if (window.__IMPRINT_TREE_SEEDED) return base;

    // Extra nodes to round out 25-imprint catalog with realistic depth
    const extras = [
      { id: 'pb_g1', name: 'Pluralis Music Group',     pro: 'ASCAP', territory: 'Worldwide',     works: 4820, parent: '—',                  parentPub: null,                 administrator: 'Self',           cae: '00210034801', submitterCode: 'PMG', since: '2002', tier: 'group',   adminPct: 100, status: 'active', revenue: 18.4, dealStart: '2002-01-01', dealEnd: null },
      { id: 'pb_g2', name: 'Sony Music Publishing',    pro: 'ASCAP', territory: 'Worldwide',     works: 4_900_000, parent: '—',             parentPub: null,                 administrator: 'Self',           cae: '00012410012', submitterCode: 'SNY', since: '1995', tier: 'group',   adminPct: 100, status: 'active', revenue: 1480.0, dealStart: '1995-01-01', dealEnd: null },
      { id: 'pb_g3', name: 'Concord Music',            pro: 'BMI',   territory: 'Worldwide',     works: 1_200_000, parent: '—',             parentPub: null,                 administrator: 'Self',           cae: '00041220078', submitterCode: 'CCD', since: '1973', tier: 'group',   adminPct: 100, status: 'active', revenue: 720.0, dealStart: '1973-01-01', dealEnd: null },
      { id: 'pb_g4', name: 'Domino Publ.',             pro: 'PRS',   territory: 'Worldwide',     works: 88_000, parent: '—',                parentPub: null,                 administrator: 'Self',           cae: '00088002201', submitterCode: 'DOM', since: '1993', tier: 'group',   adminPct: 100, status: 'active', revenue: 42.1, dealStart: '1993-01-01', dealEnd: null },
      { id: 'pb_g5', name: 'Beggars Music',            pro: 'PRS',   territory: 'Worldwide',     works: 56_400, parent: '—',                parentPub: null,                 administrator: 'Self',           cae: '00056140111', submitterCode: 'BGR', since: '1977', tier: 'group',   adminPct: 100, status: 'active', revenue: 31.7, dealStart: '1977-01-01', dealEnd: null },
      { id: 'pb_g6', name: 'Kobalt',                   pro: 'PRS',   territory: 'Worldwide',     works: 1_300_000, parent: '—',             parentPub: null,                 administrator: 'Self',           cae: '00130012244', submitterCode: 'KBT', since: '2000', tier: 'group',   adminPct: 100, status: 'active', revenue: 530.0, dealStart: '2000-01-01', dealEnd: null },
      // Sony sub-imprints
      { id: 'pb_s1', name: 'EMI Music Publishing',     pro: 'ASCAP', territory: 'Worldwide',     works: 1_400_000, parent: 'Sony Music Publishing', parentPub: 'Sony Music Publishing', administrator: 'Sony Music Publishing', cae: '00140012044', submitterCode: 'EMI', since: '2018', tier: 'imprint', adminPct: 100, status: 'active', revenue: 220.0, dealStart: '2018-04-01', dealEnd: null },
      { id: 'pb_s2', name: 'Stage Three Music',        pro: 'PRS',   territory: 'GB · EU',       works: 22_000, parent: 'EMI Music Publishing', parentPub: 'EMI Music Publishing', administrator: 'Sony Music Publishing', cae: '00220012088', submitterCode: 'ST3', since: '2003', tier: 'sub',     adminPct: 75, status: 'active', revenue: 8.9, dealStart: '2003-01-01', dealEnd: null },
      { id: 'pb_s3', name: 'Famous Music',             pro: 'BMI',   territory: 'Worldwide',     works: 125_000, parent: 'Sony Music Publishing', parentPub: 'Sony Music Publishing', administrator: 'Sony Music Publishing', cae: '00125014411', submitterCode: 'FAM', since: '2007', tier: 'imprint', adminPct: 100, status: 'active', revenue: 28.4, dealStart: '2007-06-01', dealEnd: null },
      // Concord sub-imprints
      { id: 'pb_c1', name: 'Pulse Music Publishing',   pro: 'ASCAP', territory: 'Worldwide',     works: 18_500, parent: 'Concord Music',    parentPub: 'Concord Music',      administrator: 'Concord Music',  cae: '00018510041', submitterCode: 'PLS', since: '2010', tier: 'imprint', adminPct: 100, status: 'active', revenue: 7.2, dealStart: '2010-01-01', dealEnd: null },
      { id: 'pb_c2', name: 'Rodgers & Hammerstein',    pro: 'ASCAP', territory: 'Worldwide',     works: 920, parent: 'Concord Music',       parentPub: 'Concord Music',      administrator: 'Concord Music',  cae: '00000920012', submitterCode: 'RNH', since: '2017', tier: 'imprint', adminPct: 100, status: 'active', revenue: 3.4, dealStart: '2017-09-01', dealEnd: null },
      // Pluralis sub-imprints
      { id: 'pb_p1', name: 'Pluralis Sync',            pro: 'ASCAP', territory: 'US · CA',       works: 1240, parent: 'Pluralis Music Group', parentPub: 'Pluralis Music Group', administrator: 'Self', cae: '00001240088', submitterCode: 'PSY', since: '2014', tier: 'imprint', adminPct: 100, status: 'active', revenue: 4.1, dealStart: '2014-03-01', dealEnd: null },
      { id: 'pb_p2', name: 'Pluralis JP',              pro: 'JASRAC',territory: 'JP',            works: 220, parent: 'Pluralis Music Group',  parentPub: 'Pluralis Music Group', administrator: 'Self', cae: '00000220011', submitterCode: 'PJP', since: '2019', tier: 'imprint', adminPct: 100, status: 'active', revenue: 0.62, dealStart: '2019-01-01', dealEnd: null },
      { id: 'pb_p3', name: 'Pluralis EU',              pro: 'GEMA',  territory: 'EU',            works: 410, parent: 'Pluralis Music Group',  parentPub: 'Pluralis Music Group', administrator: 'Self', cae: '00000410022', submitterCode: 'PEU', since: '2016', tier: 'imprint', adminPct: 100, status: 'active', revenue: 1.41, dealStart: '2016-04-01', dealEnd: null },
      { id: 'pb_p4', name: 'Pluralis Latin',           pro: 'SACM',  territory: 'LATAM',         works: 180, parent: 'Pluralis Music Group',  parentPub: 'Pluralis Music Group', administrator: 'Self', cae: '00000180044', submitterCode: 'PLA', since: '2021', tier: 'imprint', adminPct: 100, status: 'active', revenue: 0.21, dealStart: '2021-06-01', dealEnd: null },
      // Domino sub
      { id: 'pb_d1', name: 'Domino Publishing US',     pro: 'BMI',   territory: 'US',            works: 8200, parent: 'Domino Publ.',       parentPub: 'Domino Publ.',       administrator: 'Domino Publ.',   cae: '00008200044', submitterCode: 'DPU', since: '2008', tier: 'imprint', adminPct: 100, status: 'active', revenue: 3.8, dealStart: '2008-06-01', dealEnd: null },
      // Wixen
      { id: 'pb_w1', name: 'Wixen Music',              pro: 'BMI',   territory: 'Worldwide',     works: 50_000, parent: '—',                 parentPub: null,                 administrator: 'Self',           cae: '00050001244', submitterCode: 'WXM', since: '1978', tier: 'group',   adminPct: 100, status: 'active', revenue: 24.0, dealStart: '1978-01-01', dealEnd: null },
      // Some dormant / terminated examples
      { id: 'pb_t1', name: 'Saint Songs Old Cat',      pro: 'BMI',   territory: 'Worldwide',     works: 14, parent: 'Saint Music',           parentPub: 'Saint Music',        administrator: 'Sony Music Publishing', cae: '00000014022', submitterCode: 'SSO', since: '2009', tier: 'sub',     adminPct: 50, status: 'terminated', revenue: 0.04, dealStart: '2009-03-01', dealEnd: '2021-09-30' },
      { id: 'pb_t2', name: 'Helado Sub-Latin',         pro: 'SACM',  territory: 'LATAM',         works: 22, parent: 'Helado Pub',            parentPub: 'Helado Pub',         administrator: 'Beggars Music',  cae: '00000022011', submitterCode: 'HSL', since: '2020', tier: 'sub',     adminPct: 60, status: 'dormant',    revenue: 0.08, dealStart: '2020-01-01', dealEnd: null },
    ];

    // Patch existing 8 to add tier/status/adminPct/revenue and link orphans to groups
    const patches = {
      pb_01: { tier: 'imprint', status: 'active', adminPct: 100, revenue: 6.2,  dealStart: '2018-01-01', dealEnd: null },
      pb_02: { tier: 'imprint', status: 'active', adminPct: 100, revenue: 4.4,  dealStart: '2014-06-01', dealEnd: null },
      pb_03: { tier: 'imprint', status: 'active', adminPct: 100, revenue: 1.8,  dealStart: '2020-08-01', dealEnd: null, parent: 'Kobalt', parentPub: 'Kobalt' },
      pb_04: { tier: 'imprint', status: 'active', adminPct: 100, revenue: 0.41, dealStart: '2017-04-01', dealEnd: null },
      pb_05: { tier: 'imprint', status: 'active', adminPct: 100, revenue: 1.22, dealStart: '2016-02-01', dealEnd: null },
      pb_06: { tier: 'imprint', status: 'active', adminPct: 100, revenue: 2.6,  dealStart: '2019-11-01', dealEnd: null, parent: 'Wixen Music', parentPub: 'Wixen Music' },
      pb_07: { tier: 'imprint', status: 'dormant',adminPct: 100, revenue: 0.18, dealStart: '2015-05-01', dealEnd: null, parent: 'Wixen Music', parentPub: 'Wixen Music' },
      pb_08: { tier: 'imprint', status: 'active', adminPct: 100, revenue: 0.94, dealStart: '2018-07-01', dealEnd: null },
    };
    base.forEach(p => Object.assign(p, patches[p.id] || {}));

    // Append extras (avoid duplicates)
    const ids = new Set(base.map(p => p.id));
    extras.forEach(e => { if (!ids.has(e.id)) base.push(e); });

    window.PUBLISHERS = base;
    window.__PUBLISHERS = base;
    window.__IMPRINT_TREE_SEEDED = true;
    return base;
  }

  // ── tree builders ──────────────────────────────────────────────────
  function buildTree(nodes) {
    const byName = new Map(nodes.map(n => [n.name, n]));
    const children = new Map();
    const roots = [];
    nodes.forEach(n => children.set(n.id, []));
    nodes.forEach(n => {
      const parent = n.parent && n.parent !== '—' ? byName.get(n.parent) : null;
      if (parent) children.get(parent.id).push(n);
      else roots.push(n);
    });
    // sort siblings by works desc
    children.forEach(arr => arr.sort((a, b) => (b.works || 0) - (a.works || 0)));
    roots.sort((a, b) => (b.works || 0) - (a.works || 0));
    return { roots, children };
  }
  function descendants(tree, id, acc = new Set()) {
    const kids = tree.children.get(id) || [];
    kids.forEach(k => { acc.add(k.id); descendants(tree, k.id, acc); });
    return acc;
  }

  // ── ASTRO design tokens (match other screens) ──────────────────────
  const Pill = ({ tone, dot, children }) => {
    const c = tone === 'ok' ? 'oklch(0.55 0.16 145)' : tone === 'err' ? 'oklch(0.55 0.18 25)' : tone === 'warn' ? 'oklch(0.55 0.16 75)' : tone === 'accent' ? 'oklch(0.55 0.16 230)' : 'var(--ink-3)';
    return (
      <span className="ff-mono upper" style={{
        display: 'inline-flex', alignItems: 'center', gap: 6, padding: '3px 8px', fontSize: 9, letterSpacing: '.1em',
        border: '1px solid var(--rule)', color: c, background: 'transparent', whiteSpace: 'nowrap',
      }}>
        {dot && <span style={{ width: 5, height: 5, borderRadius: '50%', background: c }} />}
        {children}
      </span>
    );
  };
  const num = (v, dp = 2) => Number(v || 0).toFixed(dp);
  const fmtWorks = (n) => n >= 1e6 ? (n / 1e6).toFixed(1) + 'M' : n >= 1e3 ? (n / 1e3).toFixed(1) + 'k' : String(n);
  const fmtRev = (m) => m >= 1000 ? '$' + (m / 1000).toFixed(2) + 'B' : m >= 1 ? '$' + m.toFixed(1) + 'M' : '$' + (m * 1000).toFixed(0) + 'K';

  // color-by-mode hashing
  function colorFor(node, mode) {
    if (mode === 'status') {
      return node.status === 'active' ? 'oklch(0.55 0.16 145)' :
             node.status === 'dormant' ? 'oklch(0.55 0.16 75)' :
             node.status === 'terminated' ? 'oklch(0.55 0.18 25)' : 'var(--ink-3)';
    }
    if (mode === 'territory') {
      const map = { 'Worldwide': 'oklch(0.55 0.16 230)', 'US': 'oklch(0.6 0.15 25)', 'US · CA': 'oklch(0.6 0.15 30)', 'GB · EU': 'oklch(0.55 0.15 280)', 'EU': 'oklch(0.55 0.15 280)', 'JP': 'oklch(0.6 0.16 360)', 'LATAM': 'oklch(0.6 0.16 60)', 'Worldwide ex. JP': 'oklch(0.55 0.16 200)' };
      return map[node.territory] || 'var(--ink-3)';
    }
    if (mode === 'pro') {
      const map = { 'ASCAP': 'oklch(0.5 0.18 240)', 'BMI': 'oklch(0.55 0.16 25)', 'PRS': 'oklch(0.55 0.15 280)', 'GEMA': 'oklch(0.5 0.18 30)', 'JASRAC': 'oklch(0.5 0.18 360)', 'SACM': 'oklch(0.55 0.16 100)' };
      return map[node.pro] || 'var(--ink-3)';
    }
    if (mode === 'revenue') {
      const r = node.revenue || 0;
      return r >= 100 ? 'oklch(0.55 0.16 145)' : r >= 10 ? 'oklch(0.6 0.13 130)' : r >= 1 ? 'oklch(0.65 0.1 100)' : 'oklch(0.7 0.06 80)';
    }
    return 'var(--ink-3)';
  }

  // ── tweaks panel state ────────────────────────────────────────────
  const DEFAULTS = /*EDITMODE-BEGIN*/{
    "view": "tree",
    "colorBy": "status",
    "density": "comfortable",
    "showCounts": true,
    "showAdminPct": true,
    "rootOnly": false
  }/*EDITMODE-END*/;

  function useTweaks(initial) {
    const [t, setT] = useState(initial);
    function set(patch) {
      const next = { ...t, ...patch };
      setT(next);
      window.parent.postMessage({ type: '__edit_mode_set_keys', edits: patch }, '*');
    }
    return [t, set];
  }

  // ── filter UI atoms ───────────────────────────────────────────────
  const FilterBtn = ({ active, onClick, children }) => (
    <button onClick={onClick} className="ff-mono upper" style={{
      padding: '5px 10px', fontSize: 10, letterSpacing: '.08em',
      background: active ? 'var(--ink)' : 'transparent', color: active ? 'var(--bg)' : 'var(--ink)',
      border: '1px solid ' + (active ? 'var(--ink)' : 'var(--rule)'), cursor: 'pointer',
    }}>{children}</button>
  );

  // ── tree-view node row ────────────────────────────────────────────
  function TreeNode({ node, tree, depth, expanded, setExpanded, selected, setSelected, t, search, drag, setDrag, dropTarget, setDropTarget, onMove }) {
    const kids = tree.children.get(node.id) || [];
    const isOpen = expanded.has(node.id);
    const isSelected = selected === node.id;
    const matches = !search || node.name.toLowerCase().includes(search.toLowerCase()) || (node.aliases || '').toLowerCase().includes(search.toLowerCase());
    const dim = !matches && search;
    const c = colorFor(node, t.colorBy);
    const isDropTarget = dropTarget === node.id && drag && drag !== node.id;

    function handleDragStart(e) {
      e.dataTransfer.setData('text/plain', node.id);
      e.dataTransfer.effectAllowed = 'move';
      setDrag(node.id);
    }
    function handleDragOver(e) {
      if (drag && drag !== node.id) {
        e.preventDefault();
        e.dataTransfer.dropEffect = 'move';
        setDropTarget(node.id);
      }
    }
    function handleDragLeave() { if (dropTarget === node.id) setDropTarget(null); }
    function handleDrop(e) {
      e.preventDefault();
      const sourceId = e.dataTransfer.getData('text/plain') || drag;
      if (sourceId && sourceId !== node.id) onMove(sourceId, node.id);
      setDrag(null); setDropTarget(null);
    }

    const padY = t.density === 'compact' ? 6 : t.density === 'spacious' ? 16 : 11;

    return (
      <>
        <div
          draggable
          onDragStart={handleDragStart}
          onDragOver={handleDragOver}
          onDragLeave={handleDragLeave}
          onDrop={handleDrop}
          onClick={() => setSelected(node.id)}
          style={{
            display: 'grid',
            gridTemplateColumns: `${depth * 26 + 18}px 18px 1fr auto auto auto`,
            gap: 0, alignItems: 'center',
            padding: `${padY}px 14px ${padY}px 8px`,
            borderTop: '1px solid var(--rule-soft)',
            background: isSelected ? 'var(--bg-2)' : isDropTarget ? 'oklch(0.95 0.04 145)' : 'transparent',
            cursor: 'pointer',
            opacity: dim ? 0.35 : 1,
            transition: 'background 80ms',
          }}
          onMouseEnter={e => { if (!isSelected) e.currentTarget.style.background = 'var(--bg-2)'; }}
          onMouseLeave={e => { if (!isSelected && !isDropTarget) e.currentTarget.style.background = 'transparent'; }}
        >
          {/* indent + connector lines */}
          <div style={{ position: 'relative', height: '100%' }}>
            {Array.from({ length: depth }).map((_, i) => (
              <div key={i} style={{ position: 'absolute', left: i * 26 + 12, top: 0, bottom: 0, width: 1, background: 'var(--rule-soft)' }} />
            ))}
            {depth > 0 && <div style={{ position: 'absolute', left: (depth - 1) * 26 + 12, top: '50%', width: 14, height: 1, background: 'var(--rule)' }} />}
          </div>
          {/* expand caret */}
          {kids.length > 0 ? (
            <button onClick={e => { e.stopPropagation(); const next = new Set(expanded); next.has(node.id) ? next.delete(node.id) : next.add(node.id); setExpanded(next); }}
              style={{ background: 'transparent', border: 0, color: 'var(--ink-3)', cursor: 'pointer', fontSize: 10, padding: 0, transform: isOpen ? 'rotate(90deg)' : 'rotate(0deg)', transition: 'transform 120ms' }}>▶</button>
          ) : <span />}
          {/* name + meta */}
          <div style={{ minWidth: 0, display: 'flex', alignItems: 'center', gap: 10 }}>
            <span style={{ width: 6, height: 6, borderRadius: '50%', background: c, flex: 'none' }} />
            <div style={{ minWidth: 0, flex: 1 }}>
              <div style={{ display: 'flex', alignItems: 'baseline', gap: 8 }}>
                <span style={{ fontSize: 13.5, fontWeight: 500, letterSpacing: '-0.005em', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{node.name}</span>
                {node.tier === 'group' && <span className="ff-mono upper" style={{ fontSize: 8, color: 'var(--ink-4)', letterSpacing: '.12em' }}>GROUP</span>}
              </div>
              <div className="ff-mono" style={{ fontSize: 10, color: 'var(--ink-3)', marginTop: 2, display: 'flex', gap: 10, flexWrap: 'wrap' }}>
                <span>{node.pro}</span>
                <span>· {node.territory}</span>
                <span>· IPI {node.cae}</span>
                {node.administrator && node.administrator !== 'Self' && <span>· admin {node.administrator}</span>}
              </div>
            </div>
          </div>
          {/* counts */}
          {t.showCounts && (
            <div className="ff-mono num" style={{ fontSize: 11, color: 'var(--ink-2)', textAlign: 'right', minWidth: 70 }}>
              {fmtWorks(node.works || 0)} works
            </div>
          )}
          {/* admin pct */}
          {t.showAdminPct && (
            <div className="ff-mono num" style={{ fontSize: 11, color: 'var(--ink-3)', textAlign: 'right', minWidth: 60, paddingLeft: 14 }}>
              {node.adminPct || 100}%
            </div>
          )}
          {/* status pill */}
          <div style={{ paddingLeft: 14 }}>
            <Pill tone={node.status === 'active' ? 'ok' : node.status === 'dormant' ? 'warn' : node.status === 'terminated' ? 'err' : 'neutral'} dot>{node.status || 'active'}</Pill>
          </div>
        </div>
        {/* children */}
        {isOpen && kids.map(k => (
          <TreeNode key={k.id} node={k} tree={tree} depth={depth + 1}
            expanded={expanded} setExpanded={setExpanded}
            selected={selected} setSelected={setSelected}
            t={t} search={search}
            drag={drag} setDrag={setDrag} dropTarget={dropTarget} setDropTarget={setDropTarget} onMove={onMove} />
        ))}
      </>
    );
  }

  // ── side detail panel ─────────────────────────────────────────────
  function DetailPanel({ node, tree, allNodes, onClose, onAddChild, onArchive, onMove, go }) {
    if (!node) return (
      <div style={{ padding: 32, color: 'var(--ink-3)', textAlign: 'center' }}>
        <div className="ff-mono upper" style={{ fontSize: 10, letterSpacing: '.12em', marginBottom: 8 }}>NO IMPRINT SELECTED</div>
        <div style={{ fontSize: 12, lineHeight: 1.5, maxWidth: 240, margin: '0 auto' }}>Click any imprint in the tree to inspect its detail. Drag rows onto each other to re-parent.</div>
      </div>
    );
    const kids = tree.children.get(node.id) || [];
    const desc = descendants(tree, node.id);
    const totalSubWorks = [...desc].reduce((s, id) => s + ((allNodes.find(n => n.id === id)?.works) || 0), 0);
    const totalSubRev   = [...desc].reduce((s, id) => s + ((allNodes.find(n => n.id === id)?.revenue) || 0), 0);
    return (
      <div style={{ padding: '20px 22px', overflowY: 'auto', height: '100%' }}>
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 12 }}>
          <div className="ff-mono upper" style={{ fontSize: 9, letterSpacing: '.18em', color: 'var(--ink-3)' }}>IMPRINT · {node.id}</div>
          <button onClick={onClose} style={{ background: 'transparent', border: 0, color: 'var(--ink-3)', cursor: 'pointer', fontSize: 16 }}>×</button>
        </div>
        <h3 className="ff-display" style={{ fontSize: 26, fontWeight: 600, letterSpacing: '-0.025em', margin: 0, lineHeight: 1.05 }}>{node.name}</h3>
        {node.aliases && <div className="ff-mono" style={{ fontSize: 10, color: 'var(--ink-3)', marginTop: 4 }}>aka {node.aliases}</div>}
        <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap', marginTop: 10 }}>
          <Pill tone={node.status === 'active' ? 'ok' : node.status === 'dormant' ? 'warn' : 'err'} dot>{node.status}</Pill>
          {node.tier && <Pill>{node.tier}</Pill>}
          <Pill>{node.pro}</Pill>
          <Pill>{node.territory}</Pill>
        </div>

        {/* metric grid */}
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 0, marginTop: 18, border: '1px solid var(--rule)' }}>
          <Stat label="WORKS" value={fmtWorks(node.works || 0)} sub={totalSubWorks ? `+${fmtWorks(totalSubWorks)} below` : null} />
          <Stat label="REVENUE / YR" value={fmtRev(node.revenue || 0)} sub={totalSubRev ? `+${fmtRev(totalSubRev)} below` : null} />
          <Stat label="ADMIN %" value={(node.adminPct || 100) + '%'} />
          <Stat label="CHILDREN" value={kids.length} />
        </div>

        {/* deal terms */}
        <div className="ff-mono upper" style={{ fontSize: 9, letterSpacing: '.12em', color: 'var(--ink-3)', marginTop: 22, marginBottom: 8 }}>DEAL TERMS</div>
        <div style={{ display: 'grid', gridTemplateColumns: 'auto 1fr', gap: '6px 16px', fontSize: 12, alignItems: 'baseline' }}>
          <span className="ff-mono" style={{ fontSize: 10, color: 'var(--ink-3)' }}>SINCE</span><span className="ff-mono num">{node.since || '—'}</span>
          <span className="ff-mono" style={{ fontSize: 10, color: 'var(--ink-3)' }}>START</span><span className="ff-mono num">{node.dealStart || '—'}</span>
          <span className="ff-mono" style={{ fontSize: 10, color: 'var(--ink-3)' }}>END</span><span className="ff-mono num">{node.dealEnd || 'open'}</span>
          <span className="ff-mono" style={{ fontSize: 10, color: 'var(--ink-3)' }}>ADMIN</span><span style={{ fontSize: 12 }}>{node.administrator || 'Self'}</span>
          <span className="ff-mono" style={{ fontSize: 10, color: 'var(--ink-3)' }}>SUBMITTER</span><span className="ff-mono num">{node.submitterCode || '—'}</span>
          <span className="ff-mono" style={{ fontSize: 10, color: 'var(--ink-3)' }}>IPI</span><span className="ff-mono num">{node.cae || '—'}</span>
        </div>

        {/* parent */}
        <div className="ff-mono upper" style={{ fontSize: 9, letterSpacing: '.12em', color: 'var(--ink-3)', marginTop: 22, marginBottom: 8 }}>PARENT</div>
        <select value={node.parent || '—'} onChange={e => onMove(node.id, allNodes.find(n => n.name === e.target.value)?.id || null)}
          style={{ width: '100%', padding: '7px 8px', fontSize: 12, background: 'var(--bg)', color: 'var(--ink)', border: '1px solid var(--rule)' }}>
          <option value="—">— (top-level)</option>
          {allNodes.filter(n => n.id !== node.id && !descendants(tree, node.id).has(n.id)).map(n => (
            <option key={n.id} value={n.name}>{n.name}</option>
          ))}
        </select>

        {/* children list */}
        {kids.length > 0 && (
          <>
            <div className="ff-mono upper" style={{ fontSize: 9, letterSpacing: '.12em', color: 'var(--ink-3)', marginTop: 22, marginBottom: 8 }}>CHILDREN · {kids.length}</div>
            <div style={{ border: '1px solid var(--rule)' }}>
              {kids.map((k, i) => (
                <div key={k.id} style={{ display: 'flex', justifyContent: 'space-between', padding: '8px 12px', borderTop: i ? '1px solid var(--rule-soft)' : 'none', fontSize: 12 }}>
                  <span>{k.name}</span>
                  <span className="ff-mono num" style={{ color: 'var(--ink-3)' }}>{fmtWorks(k.works || 0)}</span>
                </div>
              ))}
            </div>
          </>
        )}

        {/* actions */}
        <div style={{ display: 'flex', gap: 8, marginTop: 22, flexWrap: 'wrap' }}>
          <button onClick={() => onAddChild(node.id)} className="ff-mono upper" style={btnGhost}>+ Add child</button>
          <button onClick={() => go && go('subpubs', { id: node.id })} className="ff-mono upper" style={btnGhost}>Open detail →</button>
          <button onClick={() => onArchive(node.id)} className="ff-mono upper" style={{ ...btnGhost, color: 'oklch(0.55 0.18 25)' }}>Archive</button>
        </div>
      </div>
    );
  }
  function Stat({ label, value, sub }) {
    return (
      <div style={{ padding: '10px 12px', border: '0px solid transparent', borderRight: '1px solid var(--rule-soft)', borderBottom: '1px solid var(--rule-soft)' }}>
        <div className="ff-mono upper" style={{ fontSize: 8, letterSpacing: '.14em', color: 'var(--ink-3)' }}>{label}</div>
        <div className="ff-display num" style={{ fontSize: 22, fontWeight: 600, letterSpacing: '-0.02em', marginTop: 4 }}>{value}</div>
        {sub && <div className="ff-mono" style={{ fontSize: 9, color: 'var(--ink-3)', marginTop: 2 }}>{sub}</div>}
      </div>
    );
  }
  const btnGhost = {
    padding: '6px 11px', fontSize: 10, letterSpacing: '.08em', background: 'transparent',
    color: 'var(--ink)', border: '1px solid var(--rule)', cursor: 'pointer',
  };

  // ── add-child modal ───────────────────────────────────────────────
  function AddChildModal({ parentId, allNodes, onCancel, onCreate }) {
    const parent = allNodes.find(n => n.id === parentId);
    const [name, setName] = useState('');
    const [pro, setPro] = useState('ASCAP');
    const [territory, setTerritory] = useState('Worldwide');
    const [adminPct, setAdminPct] = useState(100);
    if (!parent) return null;
    return (
      <div style={{ position: 'fixed', inset: 0, background: 'oklch(0 0 0 / .55)', zIndex: 9001, display: 'flex', justifyContent: 'center', alignItems: 'center' }} onClick={onCancel}>
        <div onClick={e => e.stopPropagation()} style={{ background: 'var(--bg)', width: 'min(440px, 92vw)', border: '1px solid var(--rule)' }}>
          <div style={{ padding: '14px 18px', borderBottom: '1px solid var(--rule)', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
            <div className="ff-mono upper" style={{ fontSize: 10, letterSpacing: '.12em' }}>ADD CHILD IMPRINT</div>
            <button onClick={onCancel} style={{ background: 'transparent', border: 0, color: 'var(--ink-3)', cursor: 'pointer' }}>×</button>
          </div>
          <div style={{ padding: 18, display: 'flex', flexDirection: 'column', gap: 12 }}>
            <div className="ff-mono" style={{ fontSize: 11, color: 'var(--ink-3)' }}>Parent: <strong style={{ color: 'var(--ink)' }}>{parent.name}</strong></div>
            <Field label="NAME"><input value={name} onChange={e => setName(e.target.value)} placeholder="e.g. New Imprint" style={inp} /></Field>
            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
              <Field label="PRO"><select value={pro} onChange={e => setPro(e.target.value)} style={inp}><option>ASCAP</option><option>BMI</option><option>SESAC</option><option>PRS</option><option>GEMA</option><option>SACEM</option><option>JASRAC</option><option>SACM</option></select></Field>
              <Field label="TERRITORY"><select value={territory} onChange={e => setTerritory(e.target.value)} style={inp}><option>Worldwide</option><option>US</option><option>US · CA</option><option>EU</option><option>GB · EU</option><option>JP</option><option>LATAM</option></select></Field>
            </div>
            <Field label="ADMIN %"><input type="number" min={0} max={100} value={adminPct} onChange={e => setAdminPct(+e.target.value)} style={inp} /></Field>
          </div>
          <div style={{ padding: '12px 18px', borderTop: '1px solid var(--rule)', display: 'flex', justifyContent: 'flex-end', gap: 8 }}>
            <button onClick={onCancel} className="ff-mono upper" style={btnGhost}>Cancel</button>
            <button onClick={() => onCreate({ name, pro, territory, adminPct, parentId })} disabled={!name} className="ff-mono upper" style={!name ? { ...btnGhost, opacity: 0.4, cursor: 'not-allowed' } : { padding: '6px 11px', fontSize: 10, letterSpacing: '.08em', background: 'var(--ink)', color: 'var(--bg)', border: 0, cursor: 'pointer', fontWeight: 600 }}>Create</button>
          </div>
        </div>
      </div>
    );
  }
  function Field({ label, children }) {
    return (
      <label style={{ display: 'block' }}>
        <div className="ff-mono upper" style={{ fontSize: 9, letterSpacing: '.12em', color: 'var(--ink-3)', marginBottom: 6 }}>{label}</div>
        {children}
      </label>
    );
  }
  const inp = { width: '100%', padding: '6px 8px', fontSize: 12, background: 'var(--bg)', color: 'var(--ink)', border: '1px solid var(--rule-soft)', fontFamily: 'inherit' };

  // ── tweaks panel ──────────────────────────────────────────────────
  function TweaksPanel({ t, set, onClose }) {
    return (
      <div style={{ position: 'fixed', right: 16, bottom: 16, width: 280, background: 'var(--bg)', border: '1px solid var(--rule)', boxShadow: '0 8px 28px oklch(0 0 0 / .22)', zIndex: 9000 }}>
        <div style={{ padding: '10px 14px', borderBottom: '1px solid var(--rule)', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
          <div className="ff-mono upper" style={{ fontSize: 10, letterSpacing: '.18em' }}>TWEAKS</div>
          <button onClick={onClose} style={{ background: 'transparent', border: 0, color: 'var(--ink-3)', cursor: 'pointer' }}>×</button>
        </div>
        <div style={{ padding: 14, display: 'flex', flexDirection: 'column', gap: 14 }}>
          <Tweak label="VIEW">
            {['tree', 'list', 'compact-list'].map(v => (
              <FilterBtn key={v} active={t.view === v} onClick={() => set({ view: v })}>{v}</FilterBtn>
            ))}
          </Tweak>
          <Tweak label="COLOR BY">
            {['status', 'territory', 'pro', 'revenue'].map(v => (
              <FilterBtn key={v} active={t.colorBy === v} onClick={() => set({ colorBy: v })}>{v}</FilterBtn>
            ))}
          </Tweak>
          <Tweak label="DENSITY">
            {['compact', 'comfortable', 'spacious'].map(v => (
              <FilterBtn key={v} active={t.density === v} onClick={() => set({ density: v })}>{v.slice(0, 4)}</FilterBtn>
            ))}
          </Tweak>
          <Tweak label="">
            <label className="ff-mono" style={{ fontSize: 10, display: 'flex', gap: 6, alignItems: 'center' }}>
              <input type="checkbox" checked={t.showCounts} onChange={e => set({ showCounts: e.target.checked })} />SHOW WORK COUNTS
            </label>
            <label className="ff-mono" style={{ fontSize: 10, display: 'flex', gap: 6, alignItems: 'center' }}>
              <input type="checkbox" checked={t.showAdminPct} onChange={e => set({ showAdminPct: e.target.checked })} />SHOW ADMIN %
            </label>
            <label className="ff-mono" style={{ fontSize: 10, display: 'flex', gap: 6, alignItems: 'center' }}>
              <input type="checkbox" checked={t.rootOnly} onChange={e => set({ rootOnly: e.target.checked })} />COLLAPSE ALL
            </label>
          </Tweak>
        </div>
      </div>
    );
  }
  function Tweak({ label, children }) {
    return (
      <div>
        {label && <div className="ff-mono upper" style={{ fontSize: 8, letterSpacing: '.16em', color: 'var(--ink-3)', marginBottom: 6 }}>{label}</div>}
        <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>{children}</div>
      </div>
    );
  }

  // ── main screen ───────────────────────────────────────────────────
  function ScreenImprintHierarchy({ go }) {
    const all = useMemo(() => ensureSeed(), []);
    const [, force] = useState(0);
    const refresh = () => force(x => x + 1);

    const [search, setSearch] = useState('');
    const [filterPro, setFilterPro] = useState('all');
    const [filterStatus, setFilterStatus] = useState('all');
    const [filterTerritory, setFilterTerritory] = useState('all');
    const [selected, setSelected] = useState(null);
    const [expanded, setExpanded] = useState(() => new Set(all.filter(n => n.tier === 'group' || !n.parent || n.parent === '—').map(n => n.id)));
    const [drag, setDrag] = useState(null);
    const [dropTarget, setDropTarget] = useState(null);
    const [addUnder, setAddUnder] = useState(null);
    const [tweaksOpen, setTweaksOpen] = useState(false);
    const [t, setT] = useTweaks(DEFAULTS);

    // edit mode coupling
    useEffect(() => {
      const onMsg = (e) => {
        if (e.data?.type === '__activate_edit_mode') setTweaksOpen(true);
        if (e.data?.type === '__deactivate_edit_mode') setTweaksOpen(false);
      };
      window.addEventListener('message', onMsg);
      window.parent.postMessage({ type: '__edit_mode_available' }, '*');
      return () => window.removeEventListener('message', onMsg);
    }, []);

    // collapse-all override
    useEffect(() => {
      if (t.rootOnly) setExpanded(new Set());
    }, [t.rootOnly]);

    // filter the visible set
    const filtered = useMemo(() => all.filter(n => {
      if (filterPro !== 'all' && n.pro !== filterPro) return false;
      if (filterStatus !== 'all' && (n.status || 'active') !== filterStatus) return false;
      if (filterTerritory !== 'all' && n.territory !== filterTerritory) return false;
      return true;
    }), [all, filterPro, filterStatus, filterTerritory]);

    // when filter is on, expand ancestors of any match so they're visible
    useEffect(() => {
      if (filterPro === 'all' && filterStatus === 'all' && filterTerritory === 'all') return;
      const tree = buildTree(all);
      const byName = new Map(all.map(n => [n.name, n]));
      const need = new Set();
      filtered.forEach(n => {
        let cur = n;
        while (cur && cur.parent && cur.parent !== '—') {
          const p = byName.get(cur.parent);
          if (!p) break;
          need.add(p.id);
          cur = p;
        }
      });
      setExpanded(prev => new Set([...prev, ...need]));
    }, [filterPro, filterStatus, filterTerritory]);

    const tree = useMemo(() => buildTree(all), [all]);
    const treeFiltered = useMemo(() => buildTree(filtered), [filtered]);

    const counts = useMemo(() => {
      const by = (k, fn = n => n[k]) => {
        const m = new Map();
        all.forEach(n => { const v = fn(n) || '—'; m.set(v, (m.get(v) || 0) + 1); });
        return m;
      };
      return {
        total: all.length,
        active: all.filter(n => (n.status || 'active') === 'active').length,
        groups: all.filter(n => n.tier === 'group').length,
        works: all.reduce((s, n) => s + (n.works || 0), 0),
        revenue: all.reduce((s, n) => s + (n.revenue || 0), 0),
        pros: by('pro'), territories: by('territory'), statuses: by('status'),
      };
    }, [all]);

    function moveNode(id, parentId) {
      const node = all.find(n => n.id === id);
      if (!node) return;
      // prevent cycles
      if (parentId) {
        const desc = descendants(buildTree(all), id);
        if (desc.has(parentId) || id === parentId) {
          window.toast && window.toast('Cannot move into self or descendant', 'soft');
          return;
        }
      }
      if (parentId) {
        const p = all.find(n => n.id === parentId);
        node.parent = p.name;
        node.parentPub = p.name;
      } else {
        node.parent = '—';
        node.parentPub = null;
      }
      window.toast && window.toast(`Moved ${node.name}`, 'soft');
      refresh();
    }
    function addChild({ name, pro, territory, adminPct, parentId }) {
      const p = all.find(n => n.id === parentId);
      const id = 'pb_' + Math.random().toString(36).slice(2, 8);
      all.push({
        id, name, pro, territory, adminPct, parent: p ? p.name : '—', parentPub: p ? p.name : null,
        works: 0, cae: '—', submitterCode: name.slice(0, 3).toUpperCase(), since: new Date().getFullYear() + '',
        administrator: 'Self', tier: 'sub', status: 'active', revenue: 0, dealStart: new Date().toISOString().slice(0, 10), dealEnd: null,
      });
      window.toast && window.toast(`Created ${name}`, 'soft');
      setAddUnder(null);
      const next = new Set(expanded); next.add(parentId); setExpanded(next);
      setSelected(id);
      refresh();
    }
    function archive(id) {
      const n = all.find(x => x.id === id); if (!n) return;
      n.status = n.status === 'terminated' ? 'active' : 'terminated';
      window.toast && window.toast(`${n.name} → ${n.status}`, 'soft');
      refresh();
    }
    function exportCsv() {
      const rows = [['id', 'name', 'parent', 'pro', 'territory', 'works', 'revenueM', 'status', 'adminPct', 'tier', 'cae', 'submitterCode', 'dealStart', 'dealEnd']];
      all.forEach(n => rows.push([n.id, n.name, n.parent || '', n.pro, n.territory, n.works || 0, n.revenue || 0, n.status || 'active', n.adminPct || 100, n.tier || '', n.cae || '', n.submitterCode || '', n.dealStart || '', n.dealEnd || '']));
      const csv = rows.map(r => r.map(c => `"${String(c).replace(/"/g, '""')}"`).join(',')).join('\n');
      const blob = new Blob([csv], { type: 'text/csv' });
      const url = URL.createObjectURL(blob);
      const a = document.createElement('a'); a.href = url; a.download = 'imprint-hierarchy.csv'; a.click();
      setTimeout(() => URL.revokeObjectURL(url), 1000);
    }
    function expandAll() { setExpanded(new Set(all.map(n => n.id))); }
    function collapseAll() { setExpanded(new Set()); }

    const proOpts = ['all', ...new Set(all.map(n => n.pro))];
    const statusOpts = ['all', 'active', 'dormant', 'terminated'];
    const terrOpts = ['all', ...new Set(all.map(n => n.territory))];

    return (
      <div style={{ background: 'var(--bg)', color: 'var(--ink)', minHeight: '100vh' }}>
        {/* hero */}
        <div style={{ padding: '32px 32px 22px', borderBottom: '1px solid var(--rule)' }}>
          <div className="ff-mono upper" style={{ fontSize: 10, letterSpacing: '.18em', color: 'var(--ink-3)', marginBottom: 8 }}>CATALOG · STRUCTURE</div>
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-end', gap: 24, flexWrap: 'wrap' }}>
            <div>
              <h1 className="ff-display" style={{ fontSize: 'clamp(34px, 4vw, 52px)', fontWeight: 600, letterSpacing: '-0.035em', lineHeight: 0.95, margin: 0 }}>Imprint hierarchy</h1>
              <div className="ff-mono" style={{ fontSize: 11, color: 'var(--ink-3)', marginTop: 10, maxWidth: 620, lineHeight: 1.6 }}>
                The full publisher tree across the catalog. Drag any imprint onto another to re-parent. Click a node to inspect; use filters and the tweaks panel to slice.
              </div>
            </div>
            <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
              <button onClick={expandAll} className="ff-mono upper" style={btnGhost}>Expand all</button>
              <button onClick={collapseAll} className="ff-mono upper" style={btnGhost}>Collapse all</button>
              <button onClick={exportCsv} className="ff-mono upper" style={btnGhost}>Export CSV</button>
              <button onClick={() => setAddUnder(selected || tree.roots[0]?.id)} className="ff-mono upper" style={{ padding: '7px 14px', fontSize: 11, letterSpacing: '.08em', background: 'var(--ink)', color: 'var(--bg)', border: 0, cursor: 'pointer', fontWeight: 600 }}>+ New imprint</button>
            </div>
          </div>

          {/* stats strip */}
          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(5, 1fr)', gap: 0, marginTop: 22, border: '1px solid var(--rule)' }}>
            <Stat label="IMPRINTS" value={counts.total} />
            <Stat label="ACTIVE" value={counts.active} sub={`${counts.total - counts.active} dormant/term.`} />
            <Stat label="GROUPS (TOP)" value={counts.groups} />
            <Stat label="WORKS UNDER MGMT" value={fmtWorks(counts.works)} />
            <Stat label="REVENUE / YR" value={fmtRev(counts.revenue)} />
          </div>
        </div>

        {/* filter bar */}
        <div style={{ padding: '14px 32px', borderBottom: '1px solid var(--rule)', display: 'flex', flexWrap: 'wrap', gap: 16, alignItems: 'center', background: 'var(--bg-2)' }}>
          <input value={search} onChange={e => setSearch(e.target.value)} placeholder="Search by name or alias…"
            style={{ flex: '0 1 280px', padding: '7px 10px', fontSize: 12, background: 'var(--bg)', color: 'var(--ink)', border: '1px solid var(--rule)' }} />
          <FilterGroup label="PRO">
            {proOpts.map(p => <FilterBtn key={p} active={filterPro === p} onClick={() => setFilterPro(p)}>{p}</FilterBtn>)}
          </FilterGroup>
          <FilterGroup label="STATUS">
            {statusOpts.map(s => <FilterBtn key={s} active={filterStatus === s} onClick={() => setFilterStatus(s)}>{s}</FilterBtn>)}
          </FilterGroup>
          <FilterGroup label="TERR.">
            {terrOpts.slice(0, 6).map(t => <FilterBtn key={t} active={filterTerritory === t} onClick={() => setFilterTerritory(t)}>{t}</FilterBtn>)}
          </FilterGroup>
          <div style={{ flex: 1 }} />
          <button onClick={() => setTweaksOpen(o => !o)} className="ff-mono upper" style={btnGhost}>⚙ Tweaks</button>
        </div>

        {/* main split */}
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 380px', minHeight: 'calc(100vh - 320px)' }}>
          <div style={{ borderRight: '1px solid var(--rule)', overflowX: 'auto' }}>
            {/* tree headers */}
            <div className="ff-mono upper" style={{ display: 'grid', gridTemplateColumns: '1fr auto auto auto', gap: 0, padding: '10px 14px', fontSize: 9, color: 'var(--ink-3)', letterSpacing: '.12em', background: 'var(--bg-2)', borderBottom: '1px solid var(--rule)' }}>
              <span>IMPRINT</span>
              {t.showCounts && <span style={{ minWidth: 70, textAlign: 'right' }}>WORKS</span>}
              {t.showAdminPct && <span style={{ minWidth: 60, textAlign: 'right', paddingLeft: 14 }}>ADMIN%</span>}
              <span style={{ paddingLeft: 14 }}>STATUS</span>
            </div>
            {/* render either tree or flat list */}
            {t.view === 'tree' ? (
              <>
                {treeFiltered.roots.map(r => (
                  <TreeNode key={r.id} node={r} tree={treeFiltered} depth={0}
                    expanded={expanded} setExpanded={setExpanded}
                    selected={selected} setSelected={setSelected}
                    t={t} search={search}
                    drag={drag} setDrag={setDrag} dropTarget={dropTarget} setDropTarget={setDropTarget}
                    onMove={moveNode}
                  />
                ))}
                {treeFiltered.roots.length === 0 && <div className="ff-mono" style={{ padding: 32, textAlign: 'center', color: 'var(--ink-3)', fontSize: 12 }}>No imprints match the current filters.</div>}
              </>
            ) : (
              <FlatList nodes={filtered} t={t} search={search} selected={selected} setSelected={setSelected} colorFor={colorFor} />
            )}
          </div>

          {/* detail panel */}
          <div style={{ background: 'var(--bg)', borderTop: '1px solid var(--rule-soft)' }}>
            <DetailPanel
              node={selected ? all.find(n => n.id === selected) : null}
              tree={tree} allNodes={all}
              onClose={() => setSelected(null)}
              onAddChild={(id) => setAddUnder(id)}
              onArchive={archive}
              onMove={moveNode}
              go={go}
            />
          </div>
        </div>

        {addUnder && <AddChildModal parentId={addUnder} allNodes={all} onCancel={() => setAddUnder(null)} onCreate={addChild} />}
        {tweaksOpen && <TweaksPanel t={t} set={setT} onClose={() => { setTweaksOpen(false); window.parent.postMessage({ type: '__edit_mode_dismissed' }, '*'); }} />}
      </div>
    );
  }

  function FlatList({ nodes, t, search, selected, setSelected, colorFor }) {
    const sorted = [...nodes]
      .filter(n => !search || n.name.toLowerCase().includes(search.toLowerCase()) || (n.aliases || '').toLowerCase().includes(search.toLowerCase()))
      .sort((a, b) => (b.works || 0) - (a.works || 0));
    const padY = t.density === 'compact' ? 6 : t.density === 'spacious' ? 16 : 11;
    return (
      <>
        {sorted.map(n => {
          const isSelected = selected === n.id;
          return (
            <div key={n.id} onClick={() => setSelected(n.id)} style={{
              display: 'grid', gridTemplateColumns: '1fr auto auto auto', gap: 0, alignItems: 'center',
              padding: `${padY}px 14px`, borderTop: '1px solid var(--rule-soft)',
              background: isSelected ? 'var(--bg-2)' : 'transparent', cursor: 'pointer',
            }}
              onMouseEnter={e => { if (!isSelected) e.currentTarget.style.background = 'var(--bg-2)'; }}
              onMouseLeave={e => { if (!isSelected) e.currentTarget.style.background = 'transparent'; }}>
              <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
                <span style={{ width: 6, height: 6, borderRadius: '50%', background: colorFor(n, t.colorBy), flex: 'none' }} />
                <div>
                  <div style={{ fontSize: 13.5, fontWeight: 500 }}>{n.name}</div>
                  <div className="ff-mono" style={{ fontSize: 10, color: 'var(--ink-3)', marginTop: 2 }}>
                    {n.parent && n.parent !== '—' ? `↳ ${n.parent}` : '— top-level'} · {n.pro} · {n.territory}
                  </div>
                </div>
              </div>
              <span className="ff-mono num" style={{ fontSize: 11, color: 'var(--ink-2)', minWidth: 70, textAlign: 'right' }}>{fmtWorks(n.works || 0)}</span>
              <span className="ff-mono num" style={{ fontSize: 11, color: 'var(--ink-3)', minWidth: 60, textAlign: 'right', paddingLeft: 14 }}>{n.adminPct || 100}%</span>
              <div style={{ paddingLeft: 14 }}><Pill tone={n.status === 'active' ? 'ok' : n.status === 'dormant' ? 'warn' : 'err'} dot>{n.status || 'active'}</Pill></div>
            </div>
          );
        })}
      </>
    );
  }

  function FilterGroup({ label, children }) {
    return (
      <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
        <span className="ff-mono upper" style={{ fontSize: 9, letterSpacing: '.12em', color: 'var(--ink-3)' }}>{label}</span>
        <div style={{ display: 'flex', gap: 4 }}>{children}</div>
      </div>
    );
  }

  // ── exports
  Object.assign(window, {
    ScreenImprintHierarchy,
  });
})();
