/* AIMSify Journal — diagram primitives
   Minimalistic, AIMSify-branded SVG diagrams used inside articles.
   Every diagram uses navy/cyan + a single accent and labels in plain English.
*/

const NAVY = '#0D1B3E';
const NAVY_2 = '#1B2E66';
const NAVY_3 = '#2A3F7C';
const CYAN = '#0891B2';
const CYAN_LIGHT = '#E0F7FA';
const CYAN_BRIGHT = '#00E5FF';
const INK = '#1F2E55';
const INK_3 = '#4A5577';
const INK_4 = '#6F7894';
const RULE = '#DFDCD2';
const PAPER = '#FAFAF7';
const PAPER_2 = '#F4F3EE';
const GOLD = '#B45309';

function FigureFrame({ title, caption, source, children }) {
  return (
    <figure style={{
      margin: '40px 0',
      padding: 0,
      fontFamily: 'var(--sans)',
    }}>
      <div style={{
        background: PAPER_2,
        border: `1px solid ${RULE}`,
        borderRadius: 12,
        padding: '24px 28px 18px',
      }}>
        {title && (
          <div style={{
            fontSize: 11,
            fontWeight: 700,
            letterSpacing: '0.14em',
            textTransform: 'uppercase',
            color: CYAN,
            marginBottom: 12,
            fontFamily: 'var(--sans)',
          }}>{title}</div>
        )}
        {children}
      </div>
      {(caption || source) && (
        <figcaption style={{
          fontSize: 13,
          color: INK_3,
          fontFamily: 'var(--sans)',
          marginTop: 10,
          paddingLeft: 4,
          fontStyle: 'normal',
          lineHeight: 1.5,
        }}>
          {caption && <span>{caption}</span>}
          {source && <span style={{ color: INK_4, marginLeft: 8 }}>· {source}</span>}
        </figcaption>
      )}
    </figure>
  );
}

// ---------- FLOW (horizontal stages with arrows) ----------
function FlowDiagram({ stages = [], title, caption }) {
  const N = stages.length;
  const PAD = 24;
  const GAP = 28;
  // Auto-widen for many stages so labels never overflow
  const cardW = Math.max(120, Math.min(180, 150));
  const W = PAD * 2 + N * cardW + (N - 1) * GAP;
  const H = 220;
  const cardH = 110;
  const y = (H - cardH) / 2;

  return (
    <FigureFrame title={title} caption={caption}>
      <svg viewBox={`0 0 ${W} ${H}`} width="100%" style={{ display: 'block' }}>
        {stages.map((s, i) => {
          const x = PAD + i * (cardW + GAP);
          const isHighlight = s.highlight;
          return (
            <g key={i}>
              <rect x={x} y={y} width={cardW} height={cardH} rx="8"
                fill={isHighlight ? NAVY : '#FFFFFF'}
                stroke={isHighlight ? NAVY : RULE}
                strokeWidth="1.5" />
              {s.tag && (
                <text x={x + 14} y={y + 22} fill={isHighlight ? CYAN_BRIGHT : CYAN}
                  style={{ font: '700 10px/1 "Inter", sans-serif', letterSpacing: '0.12em' }}>
                  {String(s.tag).toUpperCase()}
                </text>
              )}
              {/* Wrapped label via foreignObject */}
              <foreignObject x={x + 8} y={y + (s.tag ? 30 : 14)} width={cardW - 16} height={cardH - (s.tag ? 38 : 22)}>
                <div xmlns="http://www.w3.org/1999/xhtml" style={{
                  fontFamily: 'Sora, sans-serif',
                  fontSize: 12.5,
                  lineHeight: 1.35,
                  fontWeight: 600,
                  letterSpacing: '-0.005em',
                  color: isHighlight ? '#FFFFFF' : NAVY,
                  textAlign: 'center',
                  display: 'flex',
                  alignItems: 'center',
                  justifyContent: 'center',
                  height: '100%',
                  textWrap: 'balance',
                }}>{s.label}</div>
              </foreignObject>
              {/* Arrow to next */}
              {i < N - 1 && (
                <g>
                  <line x1={x + cardW + 4} y1={H / 2} x2={x + cardW + GAP - 6} y2={H / 2}
                    stroke={INK_4} strokeWidth="1.5" />
                  <path d={`M${x + cardW + GAP - 6} ${H / 2 - 4} L${x + cardW + GAP} ${H / 2} L${x + cardW + GAP - 6} ${H / 2 + 4}`}
                    fill={INK_4} />
                </g>
              )}
            </g>
          );
        })}
      </svg>
    </FigureFrame>
  );
}

// ---------- 2x2 MATRIX (e.g. risk impact x likelihood, or before/after) ----------
function MatrixDiagram({ title, caption, xAxis = ['', ''], yAxis = ['', ''], cells = [] }) {
  // cells: 4 entries in order: TL, TR, BL, BR ({ label, sub, tone: 'low'|'mid'|'high'|'critical' })
  const W = 640, H = 360;
  const PAD = 70;
  const cellW = (W - PAD - 30) / 2;
  const cellH = (H - PAD - 30) / 2;
  const tone = {
    low:      { fill: '#E0F7FA', stroke: CYAN,  text: NAVY },
    mid:      { fill: '#FBF1E2', stroke: GOLD,  text: NAVY },
    high:     { fill: '#FEE2E2', stroke: '#DC2626', text: NAVY },
    critical: { fill: '#FECACA', stroke: '#991B1B', text: '#7F1D1D' },
    neutral:  { fill: '#FFFFFF', stroke: RULE,  text: NAVY },
  };

  return (
    <FigureFrame title={title} caption={caption}>
      <svg viewBox={`0 0 ${W} ${H}`} width="100%" style={{ display: 'block' }}>
        {/* Y-axis label */}
        <text x="22" y={H / 2} fill={INK_3} textAnchor="middle"
          transform={`rotate(-90 22 ${H / 2})`}
          style={{ font: '700 10.5px/1 "Inter", sans-serif', letterSpacing: '0.14em', textTransform: 'uppercase' }}>
          {yAxis[0]}
        </text>
        {/* X-axis label */}
        <text x={W / 2} y={H - 14} fill={INK_3} textAnchor="middle"
          style={{ font: '700 10.5px/1 "Inter", sans-serif', letterSpacing: '0.14em', textTransform: 'uppercase' }}>
          {xAxis[0]}
        </text>

        {/* Y axis tick labels */}
        <text x={PAD - 12} y={PAD + 18} fill={INK_4} textAnchor="end"
          style={{ font: '500 11px/1 "Inter", sans-serif' }}>
          {yAxis[2] || 'High'}
        </text>
        <text x={PAD - 12} y={H - PAD + 4} fill={INK_4} textAnchor="end"
          style={{ font: '500 11px/1 "Inter", sans-serif' }}>
          {yAxis[1] || 'Low'}
        </text>
        {/* X axis tick labels */}
        <text x={PAD + 6} y={H - PAD + 24} fill={INK_4}
          style={{ font: '500 11px/1 "Inter", sans-serif' }}>
          {xAxis[1] || 'Low'}
        </text>
        <text x={W - 30} y={H - PAD + 24} fill={INK_4} textAnchor="end"
          style={{ font: '500 11px/1 "Inter", sans-serif' }}>
          {xAxis[2] || 'High'}
        </text>

        {/* Cells */}
        {cells.map((c, i) => {
          const col = i % 2, row = Math.floor(i / 2);
          const x = PAD + col * (cellW + 6);
          const y = PAD + row * (cellH + 6);
          const t = tone[c.tone || 'neutral'];
          return (
            <g key={i}>
              <rect x={x} y={y} width={cellW} height={cellH} rx="8"
                fill={t.fill} stroke={t.stroke} strokeWidth="1.4" />
              <text x={x + 16} y={y + 26} fill={t.text}
                style={{ font: '700 13.5px/1.3 "Sora", sans-serif', letterSpacing: '-0.01em' }}>
                {c.label}
              </text>
              {c.sub && (
                <foreignObject x={x + 14} y={y + 36} width={cellW - 28} height={cellH - 44}>
                  <div xmlns="http://www.w3.org/1999/xhtml" style={{
                    fontFamily: 'Inter, sans-serif',
                    fontSize: 11.5,
                    lineHeight: 1.45,
                    color: INK_3,
                  }}>{c.sub}</div>
                </foreignObject>
              )}
            </g>
          );
        })}
      </svg>
    </FigureFrame>
  );
}

// ---------- STACKED BARS / TIER COMPARISON ----------
function StackBarDiagram({ title, caption, items = [], maxLabel = '100%' }) {
  // items: [{ label, value (0-100), tone: 'low'|'mid'|'high', valueLabel }]
  const H = 60 + items.length * 44;
  const W = 680;
  const labelW = 200, barX = labelW + 12, barRight = W - 80;
  const barMax = barRight - barX;
  const tones = {
    low:  '#22C55E',
    mid:  GOLD,
    high: '#DC2626',
    cyan: CYAN,
    navy: NAVY,
  };

  return (
    <FigureFrame title={title} caption={caption}>
      <svg viewBox={`0 0 ${W} ${H}`} width="100%" style={{ display: 'block' }}>
        {/* Top axis */}
        <line x1={barX} y1="34" x2={barRight} y2="34" stroke={RULE} strokeWidth="1" />
        <text x={barX} y="22" fill={INK_4} style={{ font: '500 10px/1 "Inter", sans-serif' }}>0%</text>
        <text x={barRight} y="22" fill={INK_4} textAnchor="end" style={{ font: '500 10px/1 "Inter", sans-serif' }}>{maxLabel}</text>

        {items.map((it, i) => {
          const y = 50 + i * 44;
          const w = Math.max(2, (Math.min(100, it.value) / 100) * barMax);
          const fill = tones[it.tone || 'cyan'] || tones.cyan;
          return (
            <g key={i}>
              <text x={labelW} y={y + 18} fill={NAVY} textAnchor="end"
                style={{ font: '600 13px/1 "Sora", sans-serif', letterSpacing: '-0.005em' }}>
                {it.label}
              </text>
              <rect x={barX} y={y + 4} width={barMax} height="22" rx="4" fill={PAPER_2} />
              <rect x={barX} y={y + 4} width={w} height="22" rx="4" fill={fill} />
              <text x={barX + w + 8} y={y + 19} fill={INK_3}
                style={{ font: '500 12px/1 "Inter", sans-serif' }}>
                {it.valueLabel || `${it.value}%`}
              </text>
            </g>
          );
        })}
      </svg>
    </FigureFrame>
  );
}

// ---------- LAYERED PYRAMID (e.g. CTM tiers) ----------
function PyramidDiagram({ title, caption, layers = [] }) {
  // layers from BOTTOM to TOP
  const W = 680, H = 60 + layers.length * 56;
  const cx = W / 2;
  const baseW = 520;
  const topW = 140;
  const layerH = (H - 60) / layers.length;

  return (
    <FigureFrame title={title} caption={caption}>
      <svg viewBox={`0 0 ${W} ${H}`} width="100%" style={{ display: 'block' }}>
        {layers.map((l, i) => {
          // i=0 is bottom. Top layer is layers.length-1
          const fromBottom = i;
          const fromTop = layers.length - 1 - i;
          const yTop = 30 + fromTop * layerH;
          const wTop = topW + ((baseW - topW) * fromTop) / Math.max(1, layers.length - 1);
          const wBot = topW + ((baseW - topW) * (fromTop + 1)) / Math.max(1, layers.length - 1);
          const fill = l.fill || (i === layers.length - 1 ? CYAN : i === 0 ? NAVY : NAVY_3);
          const textColor = '#FFFFFF';
          const path = `M${cx - wTop / 2} ${yTop} L${cx + wTop / 2} ${yTop} L${cx + wBot / 2} ${yTop + layerH} L${cx - wBot / 2} ${yTop + layerH} Z`;
          return (
            <g key={i}>
              <path d={path} fill={fill} stroke="#FFFFFF" strokeWidth="2" />
              <text x={cx} y={yTop + layerH / 2 - 2} fill={textColor} textAnchor="middle"
                style={{ font: '700 13.5px/1 "Sora", sans-serif', letterSpacing: '-0.005em' }}>
                {l.label}
              </text>
              {l.sub && (
                <text x={cx} y={yTop + layerH / 2 + 14} fill="rgba(255,255,255,0.78)" textAnchor="middle"
                  style={{ font: '400 11px/1 "Inter", sans-serif' }}>
                  {l.sub}
                </text>
              )}
              {/* Right-side annotation */}
              {l.note && (
                <text x={cx + baseW / 2 + 14} y={yTop + layerH / 2 + 4} fill={INK_3}
                  style={{ font: '500 11.5px/1 "Inter", sans-serif' }}>
                  {l.note}
                </text>
              )}
            </g>
          );
        })}
      </svg>
    </FigureFrame>
  );
}

// ---------- DEPENDENCY / CONCENTRATION GRAPH (hub-and-spoke) ----------
function HubSpokeDiagram({ title, caption, hub = {}, spokes = [], note }) {
  const W = 680, H = 360;
  const cx = W / 2, cy = H / 2;
  const hubR = 60;
  const spokeR = 120;
  const N = spokes.length;

  return (
    <FigureFrame title={title} caption={caption}>
      <svg viewBox={`0 0 ${W} ${H}`} width="100%" style={{ display: 'block' }}>
        {/* Spokes (vendors) — arranged as a half-ring above and below */}
        {spokes.map((s, i) => {
          const angle = (Math.PI * (i + 0.5)) / N - Math.PI / 2; // top semi
          const x = cx + Math.cos(angle) * (hubR + spokeR);
          const y = cy + Math.sin(angle) * (hubR + spokeR);
          return (
            <g key={i}>
              <line x1={cx + Math.cos(angle) * hubR} y1={cy + Math.sin(angle) * hubR}
                x2={x - Math.cos(angle) * 18} y2={y - Math.sin(angle) * 14}
                stroke={s.tone === 'risk' ? '#DC2626' : INK_4} strokeWidth="1.2"
                strokeDasharray={s.tone === 'risk' ? '0' : '4 3'} />
              <rect x={x - 60} y={y - 16} width="120" height="32" rx="6"
                fill="#FFFFFF" stroke={s.tone === 'risk' ? '#DC2626' : RULE} strokeWidth="1.2" />
              <text x={x} y={y + 4} fill={NAVY} textAnchor="middle"
                style={{ font: '600 12px/1 "Sora", sans-serif', letterSpacing: '-0.005em' }}>
                {s.label}
              </text>
            </g>
          );
        })}
        {/* Hub */}
        <circle cx={cx} cy={cy} r={hubR} fill={NAVY} stroke={CYAN_BRIGHT} strokeWidth="2" />
        <text x={cx} y={cy - 4} fill="#FFFFFF" textAnchor="middle"
          style={{ font: '700 13px/1 "Sora", sans-serif', letterSpacing: '-0.005em' }}>
          {hub.label || 'Shared'}
        </text>
        <text x={cx} y={cy + 14} fill={CYAN_BRIGHT} textAnchor="middle"
          style={{ font: '700 11px/1 "Inter", sans-serif', letterSpacing: '0.06em' }}>
          {hub.sub || 'INFRASTRUCTURE'}
        </text>
        {note && (
          <text x={cx} y={H - 16} fill={INK_3} textAnchor="middle"
            style={{ font: '500 12px/1 "Inter", sans-serif' }}>
            {note}
          </text>
        )}
      </svg>
    </FigureFrame>
  );
}

// ---------- COMPARE (before / after, two-column with bullets) ----------
function CompareDiagram({ title, caption, left = {}, right = {} }) {
  const W = 680, H = 260;
  const colW = 296, colH = 220;
  const gap = 8;
  const lx = (W - 2 * colW - gap) / 2, ry = 22;
  const rx = lx + colW + gap;

  const renderCol = (col, x, isAfter) => {
    const fill = isAfter ? NAVY : '#FFFFFF';
    const stroke = isAfter ? NAVY : RULE;
    const titleColor = isAfter ? CYAN_BRIGHT : CYAN;
    const headColor = isAfter ? '#FFFFFF' : NAVY;
    const bodyColor = isAfter ? 'rgba(255,255,255,0.78)' : INK_3;
    return (
      <g>
        <rect x={x} y={ry} width={colW} height={colH} rx="10" fill={fill} stroke={stroke} strokeWidth="1.5" />
        <text x={x + 20} y={ry + 26} fill={titleColor}
          style={{ font: '700 10.5px/1 "Inter", sans-serif', letterSpacing: '0.14em' }}>
          {(col.label || (isAfter ? 'AFTER' : 'BEFORE')).toUpperCase()}
        </text>
        <text x={x + 20} y={ry + 50} fill={headColor}
          style={{ font: '700 16px/1.2 "Sora", sans-serif', letterSpacing: '-0.01em' }}>
          {col.headline}
        </text>
        <foreignObject x={x + 20} y={ry + 68} width={colW - 40} height={colH - 80}>
          <ul xmlns="http://www.w3.org/1999/xhtml" style={{
            margin: 0, padding: '0 0 0 18px', listStyle: 'disc',
            fontFamily: 'Inter, sans-serif', fontSize: 12.5, lineHeight: 1.55,
            color: bodyColor,
          }}>
            {(col.items || []).map((it, i) => <li key={i} style={{ marginBottom: 6 }}>{it}</li>)}
          </ul>
        </foreignObject>
      </g>
    );
  };

  return (
    <FigureFrame title={title} caption={caption}>
      <svg viewBox={`0 0 ${W} ${H}`} width="100%" style={{ display: 'block' }}>
        {renderCol(left, lx, false)}
        {/* Arrow */}
        <g>
          <line x1={lx + colW + 1} y1={H / 2} x2={rx - 1} y2={H / 2} stroke={INK_4} strokeWidth="1.4" />
          <path d={`M${rx - 6} ${H / 2 - 5} L${rx} ${H / 2} L${rx - 6} ${H / 2 + 5}`} fill={INK_4} />
        </g>
        {renderCol(right, rx, true)}
      </svg>
    </FigureFrame>
  );
}

// ---------- TIMELINE (90-day plan) ----------
function TimelineDiagram({ title, caption, milestones = [] }) {
  const W = 720, H = 220;
  const PAD = 40;
  const trackY = 110;
  const N = milestones.length;
  const step = (W - PAD * 2) / Math.max(1, N - 1);

  return (
    <FigureFrame title={title} caption={caption}>
      <svg viewBox={`0 0 ${W} ${H}`} width="100%" style={{ display: 'block' }}>
        {/* Track */}
        <line x1={PAD} y1={trackY} x2={W - PAD} y2={trackY} stroke={RULE} strokeWidth="2" />
        {milestones.map((m, i) => {
          const x = PAD + i * step;
          const above = i % 2 === 0;
          const labelY = above ? trackY - 22 : trackY + 36;
          const subY = above ? trackY - 60 : trackY + 56;
          return (
            <g key={i}>
              <circle cx={x} cy={trackY} r="9" fill={i === 0 ? CYAN : NAVY} stroke="#FFFFFF" strokeWidth="2.5" />
              <text x={x} y={labelY} fill={CYAN} textAnchor="middle"
                style={{ font: '700 10.5px/1 "Inter", sans-serif', letterSpacing: '0.10em' }}>
                {(m.tag || `T+${i*30}D`).toUpperCase()}
              </text>
              <foreignObject x={x - 75} y={above ? subY - 36 : subY - 10} width="150" height="60">
                <div xmlns="http://www.w3.org/1999/xhtml" style={{
                  fontFamily: 'Sora, sans-serif',
                  fontSize: 12,
                  lineHeight: 1.35,
                  color: NAVY,
                  fontWeight: 600,
                  textAlign: 'center',
                  letterSpacing: '-0.005em',
                }}>{m.label}</div>
              </foreignObject>
            </g>
          );
        })}
      </svg>
    </FigureFrame>
  );
}

// ---------- ROLE STACK (RACI / oversight responsibility) ----------
function RoleStackDiagram({ title, caption, layers = [] }) {
  // Vertical stacked bands; each shows: layer label + role(s) + responsibility
  const W = 680;
  const layerH = 64;
  const H = 30 + layers.length * (layerH + 8);

  return (
    <FigureFrame title={title} caption={caption}>
      <svg viewBox={`0 0 ${W} ${H}`} width="100%" style={{ display: 'block' }}>
        {layers.map((l, i) => {
          const y = 20 + i * (layerH + 8);
          const fill = l.fill || (i === 0 ? NAVY : '#FFFFFF');
          const stroke = i === 0 ? NAVY : RULE;
          const isDark = i === 0;
          const titleColor = isDark ? '#FFFFFF' : NAVY;
          const tagColor = isDark ? CYAN_BRIGHT : CYAN;
          const subColor = isDark ? 'rgba(255,255,255,0.75)' : INK_3;
          return (
            <g key={i}>
              <rect x="20" y={y} width={W - 40} height={layerH} rx="8" fill={fill} stroke={stroke} strokeWidth="1.5" />
              <text x="40" y={y + 22} fill={tagColor}
                style={{ font: '700 10.5px/1 "Inter", sans-serif', letterSpacing: '0.14em' }}>
                {(l.tag || '').toUpperCase()}
              </text>
              <text x="40" y={y + 42} fill={titleColor}
                style={{ font: '700 14px/1.2 "Sora", sans-serif', letterSpacing: '-0.01em' }}>
                {l.label}
              </text>
              <text x={W - 40} y={y + 22} fill={tagColor} textAnchor="end"
                style={{ font: '700 10.5px/1 "Inter", sans-serif', letterSpacing: '0.10em' }}>
                {(l.role || '').toUpperCase()}
              </text>
              <text x={W - 40} y={y + 42} fill={subColor} textAnchor="end"
                style={{ font: '500 12px/1.3 "Inter", sans-serif' }}>
                {l.sub}
              </text>
            </g>
          );
        })}
      </svg>
    </FigureFrame>
  );
}

// ---------- QUADRANT (2x2 grid w/ free-form items per cell) ----------
function QuadrantDiagram({ title, subtitle, caption, xAxis = '', yAxis = '', quadrants = [] }) {
  // quadrants: [{ x:'low|high', y:'low|high', label, items }]
  const W = 680, H = 420;
  const PAD = 64;
  const cellW = (W - PAD - 30) / 2;
  const cellH = (H - PAD - 40) / 2;
  const find = (xv, yv) => quadrants.find(q => q.x === xv && q.y === yv) || { items: [] };

  // Map: TL = low x, high y ; TR = high x, high y ; BL = low x, low y ; BR = high x, low y
  const layout = [
    { q: find('low','high'),  col:0, row:0, tone:'#F4F3EE' },
    { q: find('high','high'), col:1, row:0, tone:CYAN_LIGHT },
    { q: find('low','low'),   col:0, row:1, tone:'#FFFFFF' },
    { q: find('high','low'),  col:1, row:1, tone:'#F8FAFB' },
  ];

  return (
    <FigureFrame title={title || subtitle} caption={caption || subtitle}>
      <svg viewBox={`0 0 ${W} ${H}`} width="100%" style={{ display: 'block' }}>
        {/* Y axis label */}
        <text x="22" y={H / 2} fill={INK_3} textAnchor="middle"
          transform={`rotate(-90 22 ${H / 2})`}
          style={{ font: '700 10.5px/1 "Inter", sans-serif', letterSpacing: '0.14em', textTransform: 'uppercase' }}>
          {yAxis}
        </text>
        {/* X axis label */}
        <text x={W / 2} y={H - 14} fill={INK_3} textAnchor="middle"
          style={{ font: '700 10.5px/1 "Inter", sans-serif', letterSpacing: '0.14em', textTransform: 'uppercase' }}>
          {xAxis}
        </text>

        {layout.map(({ q, col, row, tone }, i) => {
          const x = PAD + col * (cellW + 6);
          const y = PAD - 20 + row * (cellH + 6);
          return (
            <g key={i}>
              <rect x={x} y={y} width={cellW} height={cellH} rx="8"
                fill={tone} stroke={RULE} strokeWidth="1.4" />
              <text x={x + 18} y={y + 26} fill={NAVY}
                style={{ font: '700 13.5px/1.3 "Sora", sans-serif', letterSpacing: '-0.005em' }}>
                {q.label}
              </text>
              <foreignObject x={x + 18} y={y + 36} width={cellW - 32} height={cellH - 44}>
                <ul xmlns="http://www.w3.org/1999/xhtml" style={{
                  margin: 0, padding: '0 0 0 16px', listStyle: 'disc',
                  fontFamily: 'Inter, sans-serif', fontSize: 11.5, lineHeight: 1.5,
                  color: INK_3,
                }}>
                  {(q.items || []).map((it, j) => <li key={j} style={{ marginBottom: 3 }}>{it}</li>)}
                </ul>
              </foreignObject>
            </g>
          );
        })}
      </svg>
    </FigureFrame>
  );
}

// ---------- VENN (two-circle overlap with labelled lists) ----------
function VennDiagram({ title, subtitle, caption, left = {}, right = {}, overlap = {} }) {
  const W = 760, H = 480;
  const r = 145;
  const cxL = W / 2 - r * 0.55;
  const cxR = W / 2 + r * 0.55;
  const cy  = 290;

  const headY     = 22;
  const listTop   = 40;
  const listW     = 260;
  const listH     = 110;

  const lensCx = W / 2;
  const itemStyle = {
    margin: 0, padding: 0, listStyle: 'none',
    fontFamily: 'Inter, sans-serif', fontSize: 12, lineHeight: 1.45,
    color: NAVY, textAlign: 'center', fontWeight: 500,
    overflowWrap: 'break-word', wordBreak: 'normal',
  };

  return (
    <FigureFrame title={title || subtitle} caption={caption || subtitle}>
      <svg viewBox={`0 0 ${W} ${H}`} width="100%" style={{ display: 'block' }}>

        <text x={cxL} y={headY} fill={NAVY} textAnchor="middle"
          style={{ font: '700 13px/1 "Sora", sans-serif', letterSpacing: '-0.005em' }}>
          {left.label}
        </text>
        <foreignObject x={cxL - listW / 2} y={listTop} width={listW} height={listH}>
          <ul xmlns="http://www.w3.org/1999/xhtml" style={itemStyle}>
            {(left.items || []).map((it, i) => <li key={i} style={{ marginBottom: 3 }}>{it}</li>)}
          </ul>
        </foreignObject>

        <text x={cxR} y={headY} fill={NAVY} textAnchor="middle"
          style={{ font: '700 13px/1 "Sora", sans-serif', letterSpacing: '-0.005em' }}>
          {right.label}
        </text>
        <foreignObject x={cxR - listW / 2} y={listTop} width={listW} height={listH}>
          <ul xmlns="http://www.w3.org/1999/xhtml" style={itemStyle}>
            {(right.items || []).map((it, i) => <li key={i} style={{ marginBottom: 3 }}>{it}</li>)}
          </ul>
        </foreignObject>

        <line x1={cxL} y1={listTop + listH + 4} x2={cxL} y2={cy - r - 4}
          stroke={CYAN} strokeOpacity="0.35" strokeWidth="1" strokeDasharray="2 3"/>
        <line x1={cxR} y1={listTop + listH + 4} x2={cxR} y2={cy - r - 4}
          stroke={NAVY_3} strokeOpacity="0.35" strokeWidth="1" strokeDasharray="2 3"/>

        <circle cx={cxL} cy={cy} r={r}
          fill={CYAN_LIGHT} stroke={CYAN} strokeOpacity="0.45" strokeWidth="1.5" fillOpacity="0.55" />
        <circle cx={cxR} cy={cy} r={r}
          fill="#E8E5DA" stroke={NAVY_3} strokeOpacity="0.45" strokeWidth="1.5" fillOpacity="0.55" />

        <text x={lensCx} y={cy - 8} fill={CYAN} textAnchor="middle"
          style={{ font: '700 11px/1 "Inter", sans-serif', letterSpacing: '0.14em' }}>
          {(overlap.label || 'Shared').toUpperCase()}
        </text>
        <foreignObject x={lensCx - 90} y={cy} width={180} height={80}>
          <div xmlns="http://www.w3.org/1999/xhtml" style={{
            fontFamily: 'Inter, sans-serif', fontSize: 11, lineHeight: 1.45,
            color: NAVY, textAlign: 'center', fontWeight: 500,
            overflowWrap: 'break-word',
          }}>
            {(overlap.items || []).join(' \u00b7 ')}
          </div>
        </foreignObject>
      </svg>
    </FigureFrame>
  );
}

// ---------- TREE (hierarchical folders / taxonomy) ----------
function TreeDiagram({ title, subtitle, caption, root }) {
  const items = [];
  const walk = (node, depth) => {
    items.push({ label: node.label, depth });
    (node.children || []).forEach(c => walk(c, depth + 1));
  };
  walk(root, 0);

  const W = 680;
  const rowH = 30;
  const H = 30 + items.length * rowH;

  return (
    <FigureFrame title={title || subtitle} caption={caption || subtitle}>
      <svg viewBox={`0 0 ${W} ${H}`} width="100%" style={{ display: 'block' }}>
        {items.map((it, i) => {
          const y = 20 + i * rowH;
          const x = 30 + it.depth * 28;
          const isRoot = it.depth === 0;
          return (
            <g key={i}>
              {!isRoot && (
                <line x1={x - 14} y1={y - 5} x2={x - 14} y2={y + 6}
                  stroke={INK_4} strokeOpacity="0.45" strokeWidth="1" />
              )}
              {!isRoot && (
                <line x1={x - 14} y1={y + 6} x2={x - 4} y2={y + 6}
                  stroke={INK_4} strokeOpacity="0.45" strokeWidth="1" />
              )}
              <rect x={x} y={y - 10} width={W - x - 30} height="22" rx="4"
                fill={isRoot ? NAVY : '#FFFFFF'} stroke={isRoot ? NAVY : RULE} strokeWidth="1" />
              <text x={x + 12} y={y + 5} fill={isRoot ? '#FFFFFF' : NAVY}
                style={{ font: `${isRoot ? 700 : 600} 12.5px/1 "Sora", sans-serif`, letterSpacing: '-0.005em' }}>
                {it.label}
              </text>
            </g>
          );
        })}
      </svg>
    </FigureFrame>
  );
}

// ---------- ADAPTERS (translate the article-author shape into primitive props) ----------

// nodes/edges -> stages (FlowDiagram expects a linear stage list)
function adaptFlow({ title, subtitle, nodes = [], edges = [] }) {
  const stages = nodes.map(n => ({
    label: n.label,
    highlight: n.tone === 'highlight',
    sub: n.tone === 'warn' ? '(exit path)' : undefined,
  }));
  return <FlowDiagram title={title || subtitle} caption={subtitle && title ? subtitle : undefined} stages={stages} />;
}

// rows/cols/cells -> 2x2 matrix; or fall back to a simple table-style layout for n×m
function adaptMatrix({ title, subtitle, rows = [], cols = [], cells = [] }) {
  // Always render as a simple table because article matrices are 3x3 or 4x3.
  const W = 720;
  const C = cols.length;
  const R = rows.length;
  const headerH = 36;
  const rowH = 56;
  const labelW = 150;
  const cellW = (W - labelW - 20) / C;
  const H = 20 + headerH + R * rowH + 20;

  return (
    <FigureFrame title={title || subtitle} caption={subtitle && title ? subtitle : undefined}>
      <svg viewBox={`0 0 ${W} ${H}`} width="100%" style={{ display: 'block' }}>
        {/* Column headers */}
        {cols.map((c, j) => (
          <g key={`h${j}`}>
            <text x={labelW + 12 + j * cellW} y={32} fill={CYAN}
              style={{ font: '700 10.5px/1 "Inter", sans-serif', letterSpacing: '0.14em' }}>
              {String(c).toUpperCase()}
            </text>
          </g>
        ))}
        {/* Header underline */}
        <line x1="20" y1="42" x2={W - 20} y2="42" stroke={RULE} strokeWidth="1" />
        {/* Rows */}
        {rows.map((rl, i) => {
          const y = 42 + i * rowH;
          return (
            <g key={`r${i}`}>
              <rect x="20" y={y + 6} width={W - 40} height={rowH - 8} rx="6"
                fill={i % 2 === 0 ? '#FFFFFF' : PAPER_2} stroke={RULE} strokeWidth="1" />
              <text x="32" y={y + rowH / 2 + 4} fill={NAVY}
                style={{ font: '700 13px/1.3 "Sora", sans-serif', letterSpacing: '-0.005em' }}>
                {rl}
              </text>
              {(cells[i] || []).map((cell, j) => (
                <foreignObject key={j}
                  x={labelW + 8 + j * cellW} y={y + 14}
                  width={cellW - 12} height={rowH - 22}>
                  <div xmlns="http://www.w3.org/1999/xhtml" style={{
                    fontFamily: 'Inter, sans-serif', fontSize: 12, lineHeight: 1.4,
                    color: INK_3,
                  }}>{cell}</div>
                </foreignObject>
              ))}
            </g>
          );
        })}
      </svg>
    </FigureFrame>
  );
}

// levels -> layers (PyramidDiagram expects bottom-to-top in `layers` with sub/note)
function adaptPyramid({ title, subtitle, levels = [] }) {
  // article author lists levels TOP-to-BOTTOM; PyramidDiagram wants bottom-to-top
  const layers = [...levels].reverse().map(l => ({
    label: l.label,
    sub: l.tag,
  }));
  return <PyramidDiagram title={title || subtitle} caption={subtitle && title ? subtitle : undefined} layers={layers} />;
}

// ---------- THE DISPATCHER ----------
function Diagram(props) {
  const kind = props.kind || props.variant;
  switch (kind) {
    case 'flow':       return adaptFlow(props);
    case 'matrix':     return adaptMatrix(props);
    case 'stackbar':   return <StackBarDiagram {...props} />;
    case 'pyramid':    return adaptPyramid(props);
    case 'hubspoke':   return <HubSpokeDiagram {...props} />;
    case 'compare':    return <CompareDiagram {...props} />;
    case 'timeline':   return <TimelineDiagram {...props} />;
    case 'rolestack':  return <RoleStackDiagram {...props} />;
    case 'quadrant':   return <QuadrantDiagram {...props} />;
    case 'venn':       return <VennDiagram {...props} />;
    case 'tree':       return <TreeDiagram {...props} />;
    default:           return null;
  }
}

Object.assign(window, {
  Diagram, FigureFrame,
  FlowDiagram, MatrixDiagram, StackBarDiagram, PyramidDiagram,
  HubSpokeDiagram, CompareDiagram, TimelineDiagram, RoleStackDiagram,
  QuadrantDiagram, VennDiagram, TreeDiagram,
});
