// Charts for Pathway — pure SVG, no libraries.
const { useState: useStateC, useRef: useRefC, useEffect: useEffectC, useMemo: useMemoC } = React;

// Area/line chart (revenue or covers over time) with tooltip
function LineChart({ series, field = 'revenue', height = 240, format = CoverStory.fmtMoney, color = 'var(--pw-accent)' }) {
  const W = 800, H = height, P = { t: 16, r: 16, b: 28, l: 48 };
  const ref = useRefC(null);
  const [hover, setHover] = useStateC(null);

  const values = series.map(s => s[field]);
  const max = Math.max(...values, 1) * 1.1;
  const min = 0;
  const x = i => P.l + (W - P.l - P.r) * (i / (series.length - 1 || 1));
  const y = v => P.t + (H - P.t - P.b) * (1 - (v - min) / (max - min));

  const path = series.map((d, i) => `${i ? 'L' : 'M'}${x(i).toFixed(1)} ${y(d[field]).toFixed(1)}`).join(' ');
  const area = `${path} L ${x(series.length - 1).toFixed(1)} ${y(0).toFixed(1)} L ${x(0).toFixed(1)} ${y(0).toFixed(1)} Z`;

  // y-axis ticks
  const ticks = 4;
  const yTicks = Array.from({ length: ticks + 1 }, (_, i) => (max / ticks) * i);

  function handleMove(e) {
    const rect = ref.current.getBoundingClientRect();
    const px = (e.clientX - rect.left) / rect.width * W;
    const i = Math.round((px - P.l) / (W - P.l - P.r) * (series.length - 1));
    if (i >= 0 && i < series.length) setHover({ i, x: x(i), y: y(series[i][field]) });
  }

  return (
    <div style={{ position: 'relative' }}>
      <svg ref={ref} viewBox={`0 0 ${W} ${H}`} style={{ width: '100%', height, display: 'block' }}
        onMouseMove={handleMove} onMouseLeave={() => setHover(null)}>
        {yTicks.map((v, i) => (
          <g key={i}>
            <line x1={P.l} x2={W - P.r} y1={y(v)} y2={y(v)} stroke="var(--pw-hair)" />
            <text x={P.l - 8} y={y(v)} dy="0.32em" textAnchor="end" fill="var(--pw-fg-subtle)" fontSize="10" style={{ letterSpacing: '0.06em' }}>{format(v)}</text>
          </g>
        ))}
        <path d={area} fill={color} opacity="0.12" />
        <path d={path} fill="none" stroke={color} strokeWidth="1.5" />
        {hover && (
          <g>
            <line x1={hover.x} x2={hover.x} y1={P.t} y2={H - P.b} stroke="var(--pw-fg)" strokeDasharray="2 3" opacity="0.4" />
            <circle cx={hover.x} cy={hover.y} r="4" fill={color} stroke="var(--pw-bg)" strokeWidth="2" />
          </g>
        )}
        {/* x labels: first, mid, last */}
        {[0, Math.floor(series.length / 2), series.length - 1].map(i => (
          <text key={i} x={x(i)} y={H - 8} textAnchor="middle" fill="var(--pw-fg-subtle)" fontSize="10" style={{ letterSpacing: '0.06em' }}>Day {Math.round(series[i].t) + 1}</text>
        ))}
      </svg>
      {hover && (
        <div className="pw-tt" style={{ left: `${(hover.x / W) * 100}%`, top: `${(hover.y / H) * 100}%` }}>
          <div className="tt-k">Day {Math.round(series[hover.i].t) + 1}</div>
          <div>{format(series[hover.i][field])}</div>
        </div>
      )}
    </div>
  );
}

// Stacked area: revenue by channel
function StackedArea({ series, channelsOn, height = 280 }) {
  const W = 800, H = height, P = { t: 16, r: 16, b: 28, l: 48 };
  const enabled = CoverStory.CHANNELS.filter(c => channelsOn[c.id]);
  const ref = useRefC(null);
  const [hover, setHover] = useStateC(null);

  // Build stacked series
  const stacks = series.map(d => {
    let acc = 0;
    const layers = {};
    enabled.forEach(c => {
      const v = d.byChannel[c.id] || 0;
      layers[c.id] = { from: acc, to: acc + v, val: v };
      acc += v;
    });
    return { total: acc, layers };
  });
  const max = Math.max(...stacks.map(s => s.total), 1) * 1.1;
  const x = i => P.l + (W - P.l - P.r) * (i / (series.length - 1 || 1));
  const y = v => P.t + (H - P.t - P.b) * (1 - v / max);

  function pathFor(ch) {
    const top = stacks.map((s, i) => `${i ? 'L' : 'M'}${x(i).toFixed(1)} ${y(s.layers[ch].to).toFixed(1)}`).join(' ');
    const bot = stacks.slice().reverse().map((s, i) => `L${x(stacks.length - 1 - i).toFixed(1)} ${y(s.layers[ch].from).toFixed(1)}`).join(' ');
    return top + ' ' + bot + ' Z';
  }

  const ticks = 4;
  const yTicks = Array.from({ length: ticks + 1 }, (_, i) => (max / ticks) * i);

  function handleMove(e) {
    const rect = ref.current.getBoundingClientRect();
    const px = (e.clientX - rect.left) / rect.width * W;
    const i = Math.round((px - P.l) / (W - P.l - P.r) * (series.length - 1));
    if (i >= 0 && i < series.length) setHover({ i, x: x(i) });
  }

  return (
    <div style={{ position: 'relative' }}>
      <svg ref={ref} viewBox={`0 0 ${W} ${H}`} style={{ width: '100%', height, display: 'block' }}
        onMouseMove={handleMove} onMouseLeave={() => setHover(null)}>
        {yTicks.map((v, i) => (
          <g key={i}>
            <line x1={P.l} x2={W - P.r} y1={y(v)} y2={y(v)} stroke="var(--pw-hair)" />
            <text x={P.l - 8} y={y(v)} dy="0.32em" textAnchor="end" fill="var(--pw-fg-subtle)" fontSize="10">{CoverStory.fmtMoney(v)}</text>
          </g>
        ))}
        {enabled.map(c => (
          <path key={c.id} d={pathFor(c.id)} fill={c.color} opacity="0.85" />
        ))}
        {hover && (
          <line x1={hover.x} x2={hover.x} y1={P.t} y2={H - P.b} stroke="var(--pw-fg)" strokeDasharray="2 3" opacity="0.4" />
        )}
        {[0, Math.floor(series.length / 2), series.length - 1].map(i => (
          <text key={i} x={x(i)} y={H - 8} textAnchor="middle" fill="var(--pw-fg-subtle)" fontSize="10">Day {Math.round(series[i].t) + 1}</text>
        ))}
      </svg>
      {hover && (() => {
        const s = stacks[hover.i];
        return (
          <div className="pw-tt" style={{ left: `${(hover.x / W) * 100}%`, top: `${((y(s.total) - 8) / H) * 100}%` }}>
            <div className="tt-k">Day {Math.round(series[hover.i].t) + 1}</div>
            <div style={{ marginBottom: 4 }}>{CoverStory.fmtMoney(s.total)}</div>
            {enabled.map(c => (
              <div key={c.id} style={{ display: 'flex', justifyContent: 'space-between', gap: 14, fontSize: 11 }}>
                <span style={{ opacity: 0.7 }}>
                  <span style={{ display: 'inline-block', width: 8, height: 8, background: c.color, marginRight: 6 }}></span>
                  {c.name}
                </span>
                <span>{CoverStory.fmtMoney(s.layers[c.id].val)}</span>
              </div>
            ))}
          </div>
        );
      })()}
    </div>
  );
}

// Sparkline (small, no axis)
function Sparkline({ series, field = 'revenue', color = 'var(--pw-accent)', fill = true }) {
  const W = 160, H = 30;
  const values = series.map(s => s[field]);
  const max = Math.max(...values, 1);
  const min = Math.min(...values, 0);
  const x = i => (W) * (i / (series.length - 1 || 1));
  const y = v => H - (H - 2) * ((v - min) / (max - min || 1)) - 1;
  const path = series.map((d, i) => `${i ? 'L' : 'M'}${x(i).toFixed(1)} ${y(d[field]).toFixed(1)}`).join(' ');
  const area = `${path} L ${x(series.length - 1).toFixed(1)} ${H} L ${x(0).toFixed(1)} ${H} Z`;
  return (
    <svg viewBox={`0 0 ${W} ${H}`} preserveAspectRatio="none" style={{ width: '100%', height: H, display: 'block' }}>
      {fill && <path d={area} fill={color} opacity="0.18" />}
      <path d={path} fill="none" stroke={color} strokeWidth="1.2" />
    </svg>
  );
}

// Sankey: Spend → Channel → Covers → Revenue (simplified 3-column)
function RamSankey({ channelTotals, height = 420 }) {
  const W = 880, H = height, P = { t: 20, r: 140, b: 20, l: 140 };
  const innerW = W - P.l - P.r;
  const innerH = H - P.t - P.b;
  const nodeW = 14;

  const totalSpend = channelTotals.reduce((s, c) => s + c.spend, 0);
  const totalCovers = channelTotals.reduce((s, c) => s + c.covers, 0);
  const totalRevenue = channelTotals.reduce((s, c) => s + c.revenue, 0);

  // 3 columns: col0 single 'Spend' node; col1 per-channel; col2 single 'Covers'; col3 single 'Revenue'
  // Simpler: [Ad Spend] -> [Channel] -> [Revenue]
  const cols = {
    0: { x: P.l, nodes: [{ id: 'spend', name: 'Ad spend', value: totalSpend, format: CoverStory.fmtMoney }] },
    1: { x: P.l + innerW * 0.38, nodes: channelTotals.map(c => ({ id: c.id, name: c.name, value: c.revenue, color: c.color, glyph: c.glyph })) },
    2: { x: P.l + innerW * 0.76, nodes: [{ id: 'covers', name: 'Covers', value: totalCovers, format: CoverStory.fmtInt }] },
    3: { x: P.l + innerW + nodeW * 0.2, nodes: [{ id: 'revenue', name: 'Revenue', value: totalRevenue, format: CoverStory.fmtMoneyFull }] },
  };

  // Layout nodes vertically within each column proportional to value
  function layoutCol(col, total) {
    const sum = col.nodes.reduce((s, n) => s + n.value, 0) || 1;
    const pad = 8;
    const avail = innerH - pad * (col.nodes.length - 1);
    let y = P.t;
    col.nodes.forEach(n => {
      n.h = (n.value / sum) * avail;
      n.y0 = y;
      n.y1 = y + n.h;
      y = n.y1 + pad;
    });
  }
  layoutCol(cols[0]); layoutCol(cols[1]); layoutCol(cols[2]); layoutCol(cols[3]);

  // Links: spend -> channel (by channel spend share), channel -> covers (by covers), covers -> revenue single
  // We'll compute partial thicknesses using each channel's own share.
  function linkPath(sx, sy0, sy1, tx, ty0, ty1) {
    const mx = (sx + tx) / 2;
    return `M ${sx} ${sy0} C ${mx} ${sy0}, ${mx} ${ty0}, ${tx} ${ty0} L ${tx} ${ty1} C ${mx} ${ty1}, ${mx} ${sy1}, ${sx} ${sy1} Z`;
  }

  // For col0 → col1: allocate slices of col0 proportional to each channel's share of spend
  const spendNode = cols[0].nodes[0];
  let spendCursor = spendNode.y0;
  const links01 = cols[1].nodes.map((n, i) => {
    const ch = channelTotals[i];
    const share = ch.spend / totalSpend;
    const sliceH = spendNode.h * share;
    // match target proportionally — take share of target node equal to channel's share within col1
    const l = {
      source: spendNode, target: n, channel: ch,
      sy0: spendCursor, sy1: spendCursor + sliceH,
      ty0: n.y0, ty1: n.y1,
      kind: 'spend',
    };
    spendCursor += sliceH;
    return l;
  });

  // Col1 → Col2: channel -> covers, thickness ∝ covers share
  const coversNode = cols[2].nodes[0];
  let coversCursor = coversNode.y0;
  const links12 = cols[1].nodes.map((n, i) => {
    const ch = channelTotals[i];
    const share = ch.covers / totalCovers;
    const sliceH = coversNode.h * share;
    const l = {
      source: n, target: coversNode, channel: ch,
      sy0: n.y0, sy1: n.y1,
      ty0: coversCursor, ty1: coversCursor + sliceH,
      kind: 'covers',
    };
    coversCursor += sliceH;
    return l;
  });

  // Col2 → Col3: single flow (covers → revenue)
  const revenueNode = cols[3].nodes[0];
  const link23 = {
    source: coversNode, target: revenueNode,
    sy0: coversNode.y0, sy1: coversNode.y1,
    ty0: revenueNode.y0, ty1: revenueNode.y1,
    kind: 'revenue',
  };

  return (
    <div className="pw-sankey">
      <svg viewBox={`0 0 ${W} ${H}`} style={{ width: '100%', height: H }}>
        {/* Column labels */}
        {[
          { x: cols[0].x + nodeW / 2, label: 'Ad spend' },
          { x: cols[1].x + nodeW / 2, label: 'Channel' },
          { x: cols[2].x + nodeW / 2, label: 'Covers' },
          { x: cols[3].x + nodeW / 2, label: 'Revenue' },
        ].map((c, i) => (
          <text key={i} x={c.x} y={10} textAnchor="middle" fontSize="10" fill="var(--pw-fg-subtle)"
                style={{ letterSpacing: '0.14em', textTransform: 'uppercase' }}>{c.label}</text>
        ))}

        {/* Links */}
        {links01.map((l, i) => (
          <path key={'a' + i} className="link"
            d={linkPath(cols[0].x + nodeW, l.sy0, l.sy1, cols[1].x, l.ty0, l.ty1)}
            fill={l.channel.color} opacity="0.55" />
        ))}
        {links12.map((l, i) => (
          <path key={'b' + i} className="link"
            d={linkPath(cols[1].x + nodeW, l.sy0, l.sy1, cols[2].x, l.ty0, l.ty1)}
            fill={l.channel.color} opacity="0.55" />
        ))}
        <path className="link"
          d={linkPath(cols[2].x + nodeW, link23.sy0, link23.sy1, cols[3].x, link23.ty0, link23.ty1)}
          fill="var(--pw-accent)" opacity="0.45" />

        {/* Nodes */}
        {Object.entries(cols).map(([k, col]) => col.nodes.map((n, i) => (
          <g key={k + i} className="node">
            <rect x={col.x} y={n.y0} width={nodeW} height={Math.max(n.h, 2)} fill={n.color || 'var(--pw-fg)'} />
          </g>
        )))}

        {/* Labels: left of col0, right of col3, inside for col1 (to right of bar), inside for col2 */}
        <text x={cols[0].x - 10} y={spendNode.y0 + spendNode.h / 2} dy="0.32em" textAnchor="end" fontSize="13" fontWeight="500" fill="var(--pw-fg)">
          Ad spend
        </text>
        <text x={cols[0].x - 10} y={spendNode.y0 + spendNode.h / 2 + 16} dy="0.32em" textAnchor="end" fontSize="11" fill="var(--pw-fg-muted)">
          {CoverStory.fmtMoneyFull(totalSpend)}
        </text>

        {cols[1].nodes.map((n, i) => (
          <g key={'l1' + i}>
            <text x={cols[1].x + nodeW + 8} y={n.y0 + n.h / 2 - 4} dy="0.32em" fontSize="12" fontWeight="500" fill="var(--pw-fg)">{n.name}</text>
            <text x={cols[1].x + nodeW + 8} y={n.y0 + n.h / 2 + 10} dy="0.32em" fontSize="10" fill="var(--pw-fg-muted)">{CoverStory.fmtMoney(n.value)}</text>
          </g>
        ))}

        <text x={cols[2].x + nodeW + 8} y={coversNode.y0 + coversNode.h / 2 - 4} dy="0.32em" fontSize="13" fontWeight="500" fill="var(--pw-fg)">Covers</text>
        <text x={cols[2].x + nodeW + 8} y={coversNode.y0 + coversNode.h / 2 + 12} dy="0.32em" fontSize="11" fill="var(--pw-fg-muted)">{CoverStory.fmtInt(totalCovers)}</text>

        <text x={cols[3].x + nodeW + 8} y={revenueNode.y0 + revenueNode.h / 2 - 4} dy="0.32em" fontSize="13" fontWeight="500" fill="var(--pw-fg)">Revenue</text>
        <text x={cols[3].x + nodeW + 8} y={revenueNode.y0 + revenueNode.h / 2 + 12} dy="0.32em" fontSize="11" fill="var(--pw-fg-muted)">{CoverStory.fmtMoneyFull(totalRevenue)}</text>
      </svg>
    </div>
  );
}

// Horizontal bar list (used for channel mix, audience etc.)
function BarList({ items, max, fieldLabel, format = CoverStory.fmtMoney }) {
  const topMax = max || Math.max(...items.map(i => i.value));
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
      {items.map(it => (
        <div key={it.id} style={{ display: 'grid', gridTemplateColumns: '140px 1fr 80px', gap: 12, alignItems: 'center' }}>
          <div style={{ fontSize: 13, color: 'var(--pw-fg)', display: 'flex', alignItems: 'center', gap: 8 }}>
            <span style={{ display: 'inline-block', width: 8, height: 8, background: it.color || 'var(--pw-accent)' }} />
            {it.name}
          </div>
          <div className="pw-bar">
            <div className="pw-bar-fill" style={{ width: `${(it.value / topMax) * 100}%`, background: it.color || 'var(--pw-accent)' }} />
          </div>
          <div style={{ textAlign: 'right', fontSize: 13, fontVariantNumeric: 'tabular-nums', color: 'var(--pw-fg)' }}>{format(it.value)}</div>
        </div>
      ))}
    </div>
  );
}

// Donut (channel revenue share)
function Donut({ items, total }) {
  const r = 52, R = 72, cx = 80, cy = 80;
  let acc = 0;
  const arcs = items.map((it, i) => {
    const frac = it.value / total;
    const start = acc * Math.PI * 2 - Math.PI / 2;
    acc += frac;
    const end = acc * Math.PI * 2 - Math.PI / 2;
    const large = frac > 0.5 ? 1 : 0;
    const x1 = cx + R * Math.cos(start), y1 = cy + R * Math.sin(start);
    const x2 = cx + R * Math.cos(end),   y2 = cy + R * Math.sin(end);
    const x3 = cx + r * Math.cos(end),   y3 = cy + r * Math.sin(end);
    const x4 = cx + r * Math.cos(start), y4 = cy + r * Math.sin(start);
    const d = `M ${x1} ${y1} A ${R} ${R} 0 ${large} 1 ${x2} ${y2} L ${x3} ${y3} A ${r} ${r} 0 ${large} 0 ${x4} ${y4} Z`;
    return <path key={i} d={d} fill={it.color} />;
  });
  return (
    <svg viewBox="0 0 160 160" style={{ width: 160, height: 160 }}>
      {arcs}
    </svg>
  );
}

// Dual-metric chart: revenue area (primary) + bookings line (secondary).
// Designed to replace the noisy multi-channel stacked area. One clear story per axis.
function RevenueBookingsChart({ series, height = 340 }) {
  const W = 800, H = height, P = { t: 24, r: 56, b: 32, l: 56 };
  const ref = useRefC(null);
  const [hover, setHover] = useStateC(null);

  const rev = series.map(s => s.revenue);
  const bk  = series.map(s => s.covers);
  const maxRev = Math.max(...rev, 1) * 1.15;
  const maxBk  = Math.max(...bk,  1) * 1.25;

  const x  = i => P.l + (W - P.l - P.r) * (i / (series.length - 1 || 1));
  const yR = v => P.t + (H - P.t - P.b) * (1 - v / maxRev);
  const yB = v => P.t + (H - P.t - P.b) * (1 - v / maxBk);

  // Smooth path via Catmull-Rom to Bezier for softer look
  function smooth(points) {
    if (points.length < 2) return '';
    let d = `M${points[0][0].toFixed(1)} ${points[0][1].toFixed(1)}`;
    for (let i = 0; i < points.length - 1; i++) {
      const p0 = points[i - 1] || points[i];
      const p1 = points[i];
      const p2 = points[i + 1];
      const p3 = points[i + 2] || p2;
      const t = 0.22;
      const c1x = p1[0] + (p2[0] - p0[0]) * t;
      const c1y = p1[1] + (p2[1] - p0[1]) * t;
      const c2x = p2[0] - (p3[0] - p1[0]) * t;
      const c2y = p2[1] - (p3[1] - p1[1]) * t;
      d += ` C${c1x.toFixed(1)} ${c1y.toFixed(1)}, ${c2x.toFixed(1)} ${c2y.toFixed(1)}, ${p2[0].toFixed(1)} ${p2[1].toFixed(1)}`;
    }
    return d;
  }

  const revPts = series.map((d, i) => [x(i), yR(d.revenue)]);
  const bkPts  = series.map((d, i) => [x(i), yB(d.covers)]);
  const revPath = smooth(revPts);
  const bkPath  = smooth(bkPts);
  const revArea = `${revPath} L${x(series.length - 1).toFixed(1)} ${yR(0).toFixed(1)} L${x(0).toFixed(1)} ${yR(0).toFixed(1)} Z`;

  const ticks = 3;
  const yTicks = Array.from({ length: ticks + 1 }, (_, i) => (maxRev / ticks) * i);

  function handleMove(e) {
    const rect = ref.current.getBoundingClientRect();
    const px = (e.clientX - rect.left) / rect.width * W;
    const i = Math.round((px - P.l) / (W - P.l - P.r) * (series.length - 1));
    if (i >= 0 && i < series.length) setHover({ i });
  }

  const gradId = 'pw-rev-grad-' + (Math.random().toString(36).slice(2, 7));

  return (
    <div style={{ position: 'relative' }}>
      {/* Axis labels */}
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 6, padding: '0 4px' }}>
        <div style={{ display: 'flex', gap: 18 }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 11, color: 'var(--pw-fg-muted)' }}>
            <span style={{ width: 10, height: 2, background: 'var(--pw-accent)' }} />
            <span>Revenue</span>
          </div>
          <div style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 11, color: 'var(--pw-fg-muted)' }}>
            <span style={{ width: 10, height: 0, borderTop: '1.5px dashed var(--pw-fg)', opacity: 0.55 }} />
            <span>Bookings</span>
          </div>
        </div>
      </div>

      <svg ref={ref} viewBox={`0 0 ${W} ${H}`} style={{ width: '100%', height, display: 'block' }}
        onMouseMove={handleMove} onMouseLeave={() => setHover(null)}>
        <defs>
          <linearGradient id={gradId} x1="0" x2="0" y1="0" y2="1">
            <stop offset="0%" stopColor="var(--pw-accent)" stopOpacity="0.22" />
            <stop offset="100%" stopColor="var(--pw-accent)" stopOpacity="0" />
          </linearGradient>
        </defs>

        {/* Light baseline only */}
        <line x1={P.l} x2={W - P.r} y1={H - P.b} y2={H - P.b} stroke="var(--pw-hair)" />
        {/* Sparse y-labels (left: revenue) */}
        {yTicks.slice(1).map((v, i) => (
          <text key={i} x={P.l - 10} y={yR(v)} dy="0.32em" textAnchor="end"
                fill="var(--pw-fg-subtle)" fontSize="10" style={{ letterSpacing: '0.06em' }}>
            {CoverStory.fmtMoney(v)}
          </text>
        ))}
        {/* Sparse y-labels (right: bookings) */}
        {[0.5, 1].map((f, i) => (
          <text key={i} x={W - P.r + 10} y={yB(maxBk * f)} dy="0.32em" textAnchor="start"
                fill="var(--pw-fg-subtle)" fontSize="10" style={{ letterSpacing: '0.06em' }}>
            {CoverStory.fmtInt(maxBk * f)}
          </text>
        ))}

        {/* Revenue — soft area + line */}
        <path d={revArea} fill={`url(#${gradId})`} />
        <path d={revPath} fill="none" stroke="var(--pw-accent)" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />

        {/* Bookings — dashed secondary line */}
        <path d={bkPath} fill="none" stroke="var(--pw-fg)" strokeWidth="1.4" strokeLinecap="round" strokeLinejoin="round" strokeDasharray="4 4" opacity="0.55" />

        {hover && (() => {
          const i = hover.i;
          const px = x(i);
          return (
            <g>
              <line x1={px} x2={px} y1={P.t} y2={H - P.b} stroke="var(--pw-fg)" strokeDasharray="2 3" opacity="0.35" />
              <circle cx={px} cy={yR(series[i].revenue)} r="5" fill="var(--pw-accent)" stroke="var(--pw-bg)" strokeWidth="2" />
              <circle cx={px} cy={yB(series[i].covers)} r="3.5" fill="var(--pw-fg)" stroke="var(--pw-bg)" strokeWidth="2" />
            </g>
          );
        })()}

        {/* x labels: first, mid, last */}
        {[0, Math.floor(series.length / 2), series.length - 1].map(i => (
          <text key={i} x={x(i)} y={H - 10} textAnchor="middle" fill="var(--pw-fg-subtle)" fontSize="10" style={{ letterSpacing: '0.06em' }}>
            Day {Math.round(series[i].t) + 1}
          </text>
        ))}
      </svg>

      {hover && (() => {
        const s = series[hover.i];
        const px = x(hover.i);
        return (
          <div className="pw-tt" style={{ left: `${(px / W) * 100}%`, top: `${(yR(s.revenue) / H) * 100}%` }}>
            <div className="tt-k">Day {Math.round(s.t) + 1}</div>
            <div style={{ display: 'flex', justifyContent: 'space-between', gap: 18, fontSize: 12 }}>
              <span style={{ opacity: 0.7 }}>Revenue</span>
              <span style={{ fontWeight: 500 }}>{CoverStory.fmtMoney(s.revenue)}</span>
            </div>
            <div style={{ display: 'flex', justifyContent: 'space-between', gap: 18, fontSize: 12 }}>
              <span style={{ opacity: 0.7 }}>Bookings</span>
              <span style={{ fontWeight: 500 }}>{CoverStory.fmtInt(s.covers)}</span>
            </div>
          </div>
        );
      })()}
    </div>
  );
}

// Channel breakdown — grouped horizontal bars. Cleaner than stacking in the chart.
function ChannelBreakdown({ channelTotals }) {
  const enabled = channelTotals;
  const total = enabled.reduce((s, c) => s + c.revenue, 0) || 1;
  const KIND_LABELS = { paid: 'Paid', owned: 'Owned', organic: 'Organic', earned: 'Earned', direct: 'Direct' };
  const KIND_ORDER  = ['paid', 'owned', 'organic', 'earned', 'direct'];
  const groups = {};
  enabled.forEach(c => {
    const kind = (CoverStory.CHANNELS.find(x => x.id === c.id) || {}).kind || 'other';
    (groups[kind] = groups[kind] || []).push(c);
  });
  KIND_ORDER.forEach(k => groups[k] && groups[k].sort((a, b) => b.revenue - a.revenue));

  return (
    <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '20px 32px', paddingTop: 4 }}>
      {KIND_ORDER.filter(k => groups[k]).map(k => (
        <div key={k}>
          <div style={{ fontSize: 10, letterSpacing: '0.16em', textTransform: 'uppercase', color: 'var(--pw-fg-subtle)', marginBottom: 8, display: 'flex', justifyContent: 'space-between' }}>
            <span>{KIND_LABELS[k] || k}</span>
            <span style={{ color: 'var(--pw-fg-muted)' }}>
              {CoverStory.fmtMoney(groups[k].reduce((s, c) => s + c.revenue, 0))}
            </span>
          </div>
          {groups[k].map(c => {
            const pct = c.revenue / total;
            return (
              <div key={c.id} style={{ display: 'grid', gridTemplateColumns: '110px 1fr 64px', gap: 12, alignItems: 'center', padding: '6px 0' }}>
                <div style={{ fontSize: 12, color: 'var(--pw-fg)', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{c.name}</div>
                <div style={{ position: 'relative', height: 6, background: 'var(--pw-hair)' }}>
                  <div style={{ position: 'absolute', inset: 0, width: `${pct * 100}%`, background: c.color }} />
                </div>
                <div style={{ fontSize: 12, color: 'var(--pw-fg-muted)', textAlign: 'right', fontVariantNumeric: 'tabular-nums' }}>
                  {CoverStory.fmtMoney(c.revenue)}
                </div>
              </div>
            );
          })}
        </div>
      ))}
    </div>
  );
}

Object.assign(window, { LineChart, StackedArea, Sparkline, RamSankey, BarList, Donut, RevenueBookingsChart, ChannelBreakdown });
