// MONEY FLOW, rewritten
// Story: Beat sent → Song recorded → Released on DSPs → splits into publishing + master pools
// Pools expand in place on click. Chip grid + plain-English explainer below.

// US per-1K rates — aligned with Ch.03 publishing + master calcs (career.jsx SX rate).
const FLOW_RATES_US = { perf: 0.35, mech: 0.35, master: 3.30, digital: 0.20 };
const FLOW_STREAM_STOPS = [10_000, 100_000, 1_000_000, 10_000_000, 100_000_000, 1_000_000_000];

function flowStreamChip(n) {
  if (n >= 1_000_000_000) return `${n / 1_000_000_000}B`;
  if (n >= 1_000_000) return `${n / 1_000_000}M`;
  return `${n / 1_000}K`;
}

function computeFlowAmounts(streams) {
  const k = streams / 1000;
  const perf = k * FLOW_RATES_US.perf;
  const mech = k * FLOW_RATES_US.mech;
  const master = k * FLOW_RATES_US.master;
  const digital = k * FLOW_RATES_US.digital;
  return {
    perf, mech, master, digital,
    pubTotal: perf + mech,
    masterTotal: master + digital,
    total: perf + mech + master + digital,
  };
}

function MoneyFlow() {
  const StreamScrubber = window.StreamScrubber;
  const [hovered, setHovered] = React.useState(null);
  const [expanded, setExpanded] = React.useState(null);
  const [streams, setStreams] = React.useState(1_000_000);
  return (
    <section className="section rule-top" id="flow">
      <div className="page">
        <div className="grid-12" style={{ alignItems: "end", marginBottom: 56 }}>
          <div style={{ gridColumn: "span 1" }} className="hide-mobile">
            <span className="vert">DIAGRAM · FIG. 02</span>
          </div>
          <div style={{ gridColumn: "span 7" }}>
            <span className="eyebrow">Industry Setup</span>
            <h2 className="h-section" style={{ marginTop: 16 }}>
              Where every<br/>
              <em>dollar</em> goes.
            </h2>
          </div>
          <div style={{ gridColumn: "span 4" }}>
            <p className="lede">
              One beat splits into four separate money streams. Most producers collect from one. The others are sitting somewhere, uncollected, unregistered, or signed away.
            </p>
          </div>
        </div>

        {StreamScrubber && (
          <StreamScrubber streams={streams} setStreams={setStreams} />
        )}

        <FlowDiagram
          hovered={hovered} setHovered={setHovered}
          expanded={expanded} setExpanded={setExpanded}
          streams={streams} setStreams={setStreams}
        />

        <div style={{ marginTop: 48, paddingTop: 32, borderTop: "1px solid var(--rule)", display: "flex", alignItems: "center", gap: 32, flexWrap: "wrap" }}>
          <p className="body-text" style={{ margin: 0, flex: 1, minWidth: 240, color: "var(--ink-2)" }}>
            Most producers collect from one of these four streams. The ones who collect all four did the paperwork before the record dropped.
          </p>
          <JoinCta href={PU_LINKS.free} sub="Free · No card needed">Join free on Skool</JoinCta>
        </div>
      </div>
    </section>
  );
}

// ---- Pool data, plain-English copy for beginner producers ----
const POOLS = {
  performance: {
    label: "Performance Royalties",
    side: "publishing",
    color: "gold",
    summary: "Paid when your song plays in public: radio, Spotify, TV, even a coffee shop. Register with one PRO in your country.",
    sources: [
      { name: "ASCAP",  desc: "US · most popular",  url: "https://www.ascap.com/music-creators/member-access",
        plain: "Most US producers join here. Free, ~15 minutes." },
      { name: "BMI",    desc: "US · alternative",   url: "https://www.bmi.com/join",
        plain: "Same job as ASCAP. Pick one, not both." },
      { name: "SOCAN",  desc: "Canada",             url: "https://www.socan.com/join/",
        plain: "Canadian producers. Free signup." },
      { name: "PRS",    desc: "UK",                 url: "https://www.prsformusic.com/joinprs",
        plain: "UK producers. Small one-time fee." },
    ],
    footnote: "Live somewhere else? Every country has one. Search '[your country] PRO', Germany has GEMA, Australia has APRA, Japan has JASRAC, etc.",
  },
  mechanicals: {
    label: "Mechanical Royalties",
    side: "publishing",
    color: "gold",
    summary: "A separate royalty per stream and download. Smaller per-play than performance, but it compounds at scale.",
    sources: [
      { name: "The MLC",   desc: "US · the must-do",         url: "https://www.themlc.com/",
        plain: "Do this first. Free. Catches US Spotify and Apple streams." },
      { name: "HFA",       desc: "US · older system",        url: "https://www.harryfox.com/",
        plain: "Mostly label deals and sample clearance. Skip as an indie." },
      { name: "MCPS",      desc: "UK · via PRS",             url: "https://www.prsformusic.com/",
        plain: "Comes with PRS signup in the UK." },
      { name: "Publisher admin", desc: "Songtrust, Sentric…", url: null,
        plain: "Global registration for ~15% cut. Wait until you're earning real money." },
    ],
    footnote: "Bottom line: get on The MLC. Everything else can wait.",
  },
  master: {
    label: "Master Royalties",
    side: "master",
    color: "purple",
    summary: "Biggest income when you own master share. Four setups. Pick one in writing before release.",
    sources: [
      { name: "Distro · you upload", desc: "Independent · you control", url: null,
        plain: "You upload via DistroKid, TuneCore, etc. and keep your master % each cycle." },
      { name: "Distro · split invite", desc: "Independent · artist uploads", url: null,
        plain: "Artist adds you as a split recipient in their distro. Your % pays direct, no invoicing." },
      { name: "Label · points + advance", desc: "Signed · recoupable", url: null,
        plain: "3–4 points plus fee-as-advance. Streaming pays back the advance before you see royalties." },
      { name: "Flat fee / buyout", desc: "No ongoing master share", url: null,
        plain: "All-in fee or buyout. Upfront cash, no ongoing master income. Only if that's the deal you wanted." },
    ],
    footnote: "Most placements land in A or B (indie distro) or C (signed points). If nothing is in writing, you're not in the pool, someone else collects your share.",
  },
  digital: {
    label: "Digital Performance",
    side: "master",
    color: "purple",
    summary: "Extra royalty from internet radio, SiriusXM, and non-interactive streams. Most producers never collect it.",
    sources: [
      { name: "SoundExchange", desc: "US · 10 min signup",         url: "https://www.soundexchange.com/artist-copyright-owner/join-sx/",
        plain: "US producers. Free, ~10 minutes. Money sits in escrow if you skip this." },
      { name: "PPL",           desc: "UK / Ireland",               url: "https://www.ppluk.com/i-want-to/join-ppl/",
        plain: "UK version. Worth it with BBC or UK streaming play." },
      { name: "GVL",           desc: "Germany",                    url: "https://gvl.de/",
        plain: "Germany. European neighbouring rights." },
      { name: "Neighbouring rights agent", desc: "Global · for a cut", url: null,
        plain: "Outside US/UK/DE? An agent collects globally for a cut. Only once you have real plays." },
    ],
    footnote: "Almost every producer skips this. Don't be that producer, SoundExchange takes 10 minutes and you can register from anywhere.",
  },
};

function FlowDiagram({ hovered, setHovered, expanded, setExpanded, streams, setStreams }) {
  const raw = computeFlowAmounts(streams);
  const pubTotal = useSmooth(raw.pubTotal);
  const masterTotal = useSmooth(raw.masterTotal);
  const perfAmt = useSmooth(raw.perf);
  const mechAmt = useSmooth(raw.mech);
  const masterAmt = useSmooth(raw.master);
  const digitalAmt = useSmooth(raw.digital);

  const streamIdx = FLOW_STREAM_STOPS.indexOf(streams);
  const safeIdx = streamIdx >= 0 ? streamIdx : FLOW_STREAM_STOPS.findIndex((s) => s >= streams) || 0;

  const VB_W = 1400;
  const CENTER = VB_W / 2;
  const MID_W = 320;
  const MID_H = 120;
  const LEAF_W = 300;
  const LEAF_H = 136;
  const MID_GAP = 64;
  const LEAF_GAP = 28;

  const midY = 392;
  const leafY = 632;
  const streamY = 228;
  const streamW = 520;
  const streamH = 96;

  const pubX = CENTER - MID_W / 2 - MID_GAP / 2;
  const masterX = CENTER + MID_W / 2 + MID_GAP / 2;
  const leafSpan = LEAF_W * 4 + LEAF_GAP * 3;
  const leafStartX = CENTER - leafSpan / 2 + LEAF_W / 2;
  const leafXs = [0, 1, 2, 3].map((i) => leafStartX + i * (LEAF_W + LEAF_GAP));

  const nodes = {
    phone:    { x: 100, y: 60,  w: 220, h: 220, kind: "phone" },
    song:     { x: 600, y: 90,  w: 200, h: 70, label: "Song Recorded", sub: "Your beat + their vocal" },
    dsp:      { x: 1000, y: 90, w: 220, h: 70, label: "Released on DSPs", sub: "Spotify · Apple · YouTube" },
    pub:      { x: pubX, y: midY, w: MID_W, h: MID_H, label: "Publishing Rights", sub: "The composition · melody · chords", tone: "gold" },
    master_rights: { x: masterX, y: midY, w: MID_W, h: MID_H, label: "Master Rights", sub: "The actual recording", tone: "purple" },
    performance: { x: leafXs[0], y: leafY, w: LEAF_W, h: LEAF_H, kind: "leaf", id: "performance" },
    mechanicals: { x: leafXs[1], y: leafY, w: LEAF_W, h: LEAF_H, kind: "leaf", id: "mechanicals" },
    master_pool: { x: leafXs[2], y: leafY, w: LEAF_W, h: LEAF_H, kind: "leaf", id: "master" },
    digital:     { x: leafXs[3], y: leafY, w: LEAF_W, h: LEAF_H, kind: "leaf", id: "digital" },
  };

  const edges = [
    { from: "song", to: "dsp", color: "ink", dotted: false, kind: "arrow" },
    { from: "dsp", to: "pub", color: "gold" },
    { from: "dsp", to: "master_rights", color: "purple" },
    { from: "pub", to: "performance", color: "gold" },
    { from: "pub", to: "mechanicals", color: "gold" },
    { from: "master_rights", to: "master_pool", color: "purple" },
    { from: "master_rights", to: "digital", color: "purple" },
  ];

  const colorOf = (c) => c === "gold" ? "#d4a64a" : c === "purple" ? "#b889ff" : "#cfc7b8";

  // Pool hover ids (master) vs SVG node keys (master_pool) must stay in sync for tracing.
  const POOL_HOVER_TO_NODE = { master: "master_pool" };
  const nodeKeyOf = (id) => POOL_HOVER_TO_NODE[id] || id;
  const poolIdOf = (key) => (key === "master_pool" ? "master" : key);

  const ancestors = {
    performance: ["pub", "dsp", "song"],
    mechanicals: ["pub", "dsp", "song"],
    master: ["master_rights", "dsp", "song"],
    digital: ["master_rights", "dsp", "song"],
    pub: ["dsp", "song"],
    master_rights: ["dsp", "song"],
  };

  const isHi = (id) => {
    if (!hovered) return false;
    const idKey = nodeKeyOf(id);
    const hoveredKey = nodeKeyOf(hovered);
    if (id === hovered || idKey === hoveredKey) return true;
    const chain = ancestors[hovered] || ancestors[poolIdOf(hovered)] || [];
    return chain.includes(id) || chain.includes(idKey) || chain.includes(poolIdOf(id));
  };

  const edgeHi = (e) => {
    if (!hovered) return false;
    return isHi(e.from) && isHi(e.to);
  };

  const togglePool = (id) => setExpanded(expanded === id ? null : id);

  const leafNodeKey = { performance: "performance", mechanicals: "mechanicals", master: "master_pool", digital: "digital" };
  const expandedPool = expanded ? POOLS[expanded] : null;
  const expandedLeaf = expanded ? nodes[leafNodeKey[expanded]] : null;
  const hasExpanded = !!expandedPool;

  const baseHeight = 800;
  const expandedExtra = 240;
  const vbHeight = hasExpanded ? baseHeight + expandedExtra : baseHeight;

  const traceLabel = hovered ? (
    POOLS[hovered]?.label
    || (hovered === "song" ? "Song Recorded"
      : hovered === "dsp" ? "Released on DSPs"
      : hovered === "pub" ? "Publishing Rights"
      : hovered === "master_rights" ? "Master Rights" : "")
  ) : null;

  return (
    <div className="flow-diagram-wrap" style={{ position: "relative", border: "1px solid var(--rule-strong)", background: "rgba(255,255,255,0.015)", padding: "24px 0 0", overflow: "hidden" }}>
      <div className="caption" style={{ position: "absolute", top: 20, left: 24 }}>Fig. 02 · The money topology</div>
      <div className="caption" style={{ position: "absolute", top: 20, right: 24 }}>
        {traceLabel ? `↳ ${traceLabel}` : "↳ Hover to trace the money"}
      </div>
      <div className="caption" style={{ position: "absolute", top: 44, right: 24, color: "var(--ink-3)", fontSize: 10 }}>
        US est. · ${FLOW_RATES_US.perf.toFixed(2)} perf + ${FLOW_RATES_US.mech.toFixed(2)} mech + ${FLOW_RATES_US.master.toFixed(2)} master + ${FLOW_RATES_US.digital.toFixed(2)} digital / 1K
      </div>

      <svg viewBox={`0 0 ${VB_W} ${vbHeight}`} className="flow-diagram-svg" style={{ width: "100%", height: "auto", display: "block", marginTop: 20, transition: "all 0.3s ease" }}>
        <defs>
          {/* Liquid-glass node treatment: white top sheen + soft float shadow */}
          <linearGradient id="glassSheen" x1="0" y1="0" x2="0" y2="1">
            <stop offset="0%" stopColor="#ffffff" stopOpacity="0.16" />
            <stop offset="55%" stopColor="#ffffff" stopOpacity="0" />
          </linearGradient>
          <filter id="glassFloat" x="-20%" y="-20%" width="140%" height="160%">
            <feDropShadow dx="0" dy="2" stdDeviation="6" floodColor="#000000" floodOpacity="0.45" />
          </filter>
          <pattern id="bgdots" width="24" height="24" patternUnits="userSpaceOnUse">
            <circle cx="2" cy="2" r="0.8" fill="rgba(255,255,255,0.04)" />
          </pattern>
          <radialGradient id="moneyDotGold">
            <stop offset="0%" stopColor="#fff3cf" />
            <stop offset="40%" stopColor="#d4a64a" />
            <stop offset="100%" stopColor="rgba(212,166,74,0)" />
          </radialGradient>
          <radialGradient id="moneyDotPurple">
            <stop offset="0%" stopColor="#e8d6ff" />
            <stop offset="40%" stopColor="#b889ff" />
            <stop offset="100%" stopColor="rgba(184,137,255,0)" />
          </radialGradient>
        </defs>

        <rect x="0" y="0" width={VB_W} height={vbHeight} fill="url(#bgdots)" />

        <Phone x={nodes.phone.x} y={nodes.phone.y} />
        <PhoneToSong from={{ x: nodes.phone.x + nodes.phone.w, y: nodes.phone.y + 130 }} to={{ x: nodes.song.x - nodes.song.w/2, y: nodes.song.y + 35 }} />

        {edges.map((e, i) => {
          const f = nodes[e.from], t = nodes[e.to];
          const fx = f.x, fy = f.y + (f.h || 0);
          const tx = t.x, ty = t.y;
          let path;
          if (e.from === "song" && e.to === "dsp") {
            const sy = f.y + f.h / 2;
            path = `M ${f.x + f.w/2} ${sy} L ${t.x - t.w/2} ${sy}`;
          } else {
            const midY = (fy + ty) / 2;
            path = `M ${fx} ${fy} L ${fx} ${midY} L ${tx} ${midY} L ${tx} ${ty}`;
          }
          const hi = edgeHi(e);
          const stroke = colorOf(e.color);
          return (
            <g key={i}>
              <path d={path}
                    fill="none"
                    stroke={stroke}
                    strokeOpacity={hi ? 0.95 : 0.32}
                    strokeWidth={hi ? 2.5 : 1.5}
                    strokeDasharray={hi ? "0" : "4 4"}
                    style={{ transition: "all 0.25s ease" }} />
              {e.kind === "arrow" && (
                <polygon
                  points={`${t.x - t.w/2 - 6},${f.y + f.h/2 - 5} ${t.x - t.w/2 - 6},${f.y + f.h/2 + 5} ${t.x - t.w/2},${f.y + f.h/2}`}
                  fill={stroke}
                  opacity={hi ? 1 : 0.6}
                />
              )}
              <circle r={hi ? 5 : 3.5} fill={`url(#${e.color === "gold" ? "moneyDotGold" : e.color === "purple" ? "moneyDotPurple" : "moneyDotGold"})`}>
                <animateMotion dur={`${3.5 + (i % 3) * 0.5}s`} repeatCount="indefinite" rotate="auto" begin={`${i * 0.4}s`}>
                  <mpath href={`#edge-path-${i}`} />
                </animateMotion>
              </circle>
              <path id={`edge-path-${i}`} d={path} fill="none" stroke="none" />
            </g>
          );
        })}

        <FlowNode x={nodes.song.x} y={nodes.song.y} w={nodes.song.w} h={nodes.song.h}
                  label={nodes.song.label} sub={nodes.song.sub} tone="ink"
                  hovered={isHi("song")}
                  onEnter={() => setHovered("song")} onLeave={() => setHovered(null)} />

        <FlowNode x={nodes.dsp.x} y={nodes.dsp.y} w={nodes.dsp.w} h={nodes.dsp.h}
                  label={nodes.dsp.label} sub={nodes.dsp.sub} tone="ink"
                  hovered={isHi("dsp")}
                  onEnter={() => setHovered("dsp")} onLeave={() => setHovered(null)} />

        <text x={CENTER} y={streamY + streamH / 2} textAnchor="middle" dominantBaseline="middle"
          style={{ fontFamily: "var(--mono)", fontSize: 13, fill: "var(--gold)", letterSpacing: "0.1em", fontWeight: 700 }}>
          {formatNum(streams)} STREAMS
        </text>

        <FlowNode x={nodes.pub.x} y={nodes.pub.y} w={nodes.pub.w} h={nodes.pub.h}
                  label={nodes.pub.label} sub={nodes.pub.sub} tone="gold" big
                  fullPool
                  amount={`$${formatNum(pubTotal)}`}
                  amountSub="US est."
                  hovered={isHi("pub")}
                  onEnter={() => setHovered("pub")} onLeave={() => setHovered(null)} />
        <FlowNode x={nodes.master_rights.x} y={nodes.master_rights.y} w={nodes.master_rights.w} h={nodes.master_rights.h}
                  label={nodes.master_rights.label} sub={nodes.master_rights.sub} tone="purple" big
                  fullPool
                  amount={`$${formatNum(masterTotal)}`}
                  amountSub="US est."
                  hovered={isHi("master_rights")}
                  onEnter={() => setHovered("master_rights")} onLeave={() => setHovered(null)} />

        {[
          { key: "performance", amount: perfAmt, owed: "→ PRO", fullPool: "100% publishing" },
          { key: "mechanicals", amount: mechAmt, owed: "→ MLC", fullPool: "100% publishing" },
          { key: "master_pool", amount: masterAmt, owed: "→ Distro / label", fullPool: "100% master" },
          { key: "digital", amount: digitalAmt, owed: "→ SoundExchange", fullPool: "100% master" },
        ].map(({ key, amount, owed, fullPool }) => {
          const n = nodes[key];
          const pool = POOLS[n.id];
          const isExp = expanded === n.id;
          const isDim = !!expanded && !isExp;
          return (
            <LeafNode
              key={key}
              x={n.x} y={n.y} w={n.w} h={n.h}
              pool={pool} id={n.id}
              amount={`$${formatNum(amount)}`}
              owedLabel={owed}
              fullPool={fullPool}
              hovered={isHi(n.id)}
              expanded={isExp}
              dimmed={isDim}
              onEnter={() => setHovered(n.id)}
              onLeave={() => setHovered(null)}
              onClick={() => togglePool(n.id)}
            />
          );
        })}

        {hasExpanded && (
          <ExpandedChildren
            pool={expandedPool}
            leaf={expandedLeaf}
            baseY={baseHeight - 20}
          />
        )}
      </svg>

      {/* HTML explainer panel, shown only when a pool is expanded */}
      <div style={{
        borderTop: hasExpanded ? "1px solid var(--rule-strong)" : "1px solid transparent",
        background: hasExpanded ? "rgba(255,255,255,0.02)" : "transparent",
        padding: hasExpanded ? "28px 32px 32px" : "0 32px",
        maxHeight: hasExpanded ? 1200 : 0,
        overflow: "hidden",
        transition: "max-height 0.35s ease, padding 0.25s ease, border-color 0.25s ease",
      }}>
        {hasExpanded && <PoolExplainer pool={expandedPool} />}
      </div>
    </div>
  );
}

// ---- Plain-English explainer panel ----
function PoolExplainer({ pool }) {
  const c = pool.color === "gold" ? "var(--gold)" : "#b889ff";
  return (
    <div className="grid-12" style={{ gap: 24, alignItems: "start" }}>
      <div style={{ gridColumn: "span 6" }}>
        <span className="caption" style={{ color: c }}>What this actually is</span>
        <div style={{
          fontFamily: "var(--sans)", fontWeight: 800,
          fontSize: "clamp(22px, 2.2vw, 28px)", letterSpacing: "-0.025em",
          marginTop: 6, color: "var(--ink)",
        }}>
          {pool.label}
        </div>
        <p style={{ fontSize: 15, lineHeight: 1.55, marginTop: 12, color: "var(--ink-2)" }}>
          {pool.summary}
        </p>
        {pool.footnote && (
          <p style={{
            fontSize: 13, lineHeight: 1.5, marginTop: 14,
            color: c, fontFamily: "var(--mono)",
            borderLeft: `2px solid ${c}`, paddingLeft: 12,
          }}>
            {pool.footnote}
          </p>
        )}
      </div>
      <div style={{ gridColumn: "span 6" }}>
        <span className="caption" style={{ color: c }}>Where to collect, what each one does</span>
        <ul style={{ listStyle: "none", padding: 0, margin: "12px 0 0", display: "flex", flexDirection: "column", gap: 14 }}>
          {pool.sources.map((s, i) => (
            <li key={i} style={{ display: "grid", gridTemplateColumns: "140px 1fr", gap: 16, alignItems: "baseline" }}>
              <div>
                {s.url ? (
                  <a href={s.url} target="_blank" rel="noopener noreferrer"
                     style={{ color: c, fontFamily: "var(--sans)", fontWeight: 700, fontSize: 15, textDecoration: "none", borderBottom: `1px solid ${c}66` }}>
                    {s.name} ↗
                  </a>
                ) : (
                  <span style={{ color: "var(--ink)", fontFamily: "var(--sans)", fontWeight: 700, fontSize: 15 }}>
                    {s.name}
                  </span>
                )}
              </div>
              <div style={{ fontSize: 14, lineHeight: 1.5, color: "var(--ink-2)" }}>
                {s.plain}
              </div>
            </li>
          ))}
        </ul>
      </div>
    </div>
  );
}

// ---- Phone ----
function Phone({ x, y }) {
  const w = 220, h = 220;
  return (
    <g>
      <rect x={x} y={y} width={w} height={h} rx="18" fill="#1a1814" stroke="var(--rule-strong)" strokeWidth="1.5" />
      <rect x={x + 4} y={y + 4} width={w - 8} height={h - 8} rx="14" fill="#0a0a0a" />
      <text x={x + 14} y={y + 22} fontFamily="var(--mono)" fontSize="9" fill="var(--ink-3)" letterSpacing="0.5">9:41</text>
      <text x={x + w - 14} y={y + 22} textAnchor="end" fontFamily="var(--mono)" fontSize="9" fill="var(--ink-3)" letterSpacing="0.5">●●● 4G</text>
      <text x={x + w/2} y={y + 44} textAnchor="middle" fontFamily="var(--sans)" fontWeight="600" fontSize="11" fill="var(--ink)">to: @theartist</text>
      <line x1={x + 12} y1={y + 52} x2={x + w - 12} y2={y + 52} stroke="var(--rule)" strokeWidth="0.6" />

      <g>
        <rect x={x + 14} y={y + 66} width="110" height="32" rx="14" fill="#1c1916" />
        <text x={x + 24} y={y + 86} fontFamily="var(--sans)" fontSize="10" fill="var(--ink-2)">yo got a beat for u 🔥</text>
      </g>

      <g>
        <rect x={x + 56} y={y + 108} width="148" height="32" rx="14" fill="var(--gold)" />
        <text x={x + 130} y={y + 128} textAnchor="middle" fontFamily="var(--sans)" fontSize="10" fontWeight="600" fill="#16110a">
          🎵 untitled_beat_v3.mp3
        </text>
      </g>

      <g>
        <rect x={x + 122} y={y + 148} width="82" height="28" rx="13" fill="var(--gold)" />
        <text x={x + 163} y={y + 166} textAnchor="middle" fontFamily="var(--sans)" fontSize="10" fontWeight="600" fill="#16110a">
          let me know
        </text>
        <text x={x + 198} y={y + 184} textAnchor="end" fontFamily="var(--mono)" fontSize="7" fill="var(--ink-3)">Delivered</text>
      </g>

      <text x={x + w/2} y={y + h + 18} textAnchor="middle" fontFamily="var(--mono)" fontSize="9" letterSpacing="2" fill="var(--ink-3)">
        STEP 01 · YOU SEND THE BEAT
      </text>
    </g>
  );
}

function PhoneToSong({ from, to }) {
  const path = `M ${from.x} ${from.y} C ${from.x + 80} ${from.y}, ${to.x - 80} ${to.y}, ${to.x} ${to.y}`;
  return (
    <g>
      <path d={path} stroke="var(--gold)" strokeWidth="1.5" strokeDasharray="4 4" fill="none" opacity="0.45" />
      <path id="phone-to-song-path" d={path} stroke="none" fill="none" />
      <text fontFamily="var(--mono)" fontSize="9" fill="var(--gold)" letterSpacing="1">
        <textPath href="#phone-to-song-path" startOffset="50%" textAnchor="middle">↳ beat sent</textPath>
      </text>
      <g>
        <circle r="5" fill="url(#moneyDotGold)">
          <animateMotion dur="3.5s" repeatCount="indefinite" rotate="auto">
            <mpath href="#phone-to-song-path" />
          </animateMotion>
        </circle>
      </g>
    </g>
  );
}

// ---- Nodes ----
function FlowNode({ x, y, w, h, label, sub, tone, hovered, big, fullPool, amount, amountSub, onEnter, onLeave }) {
  const colorMap = {
    ink:    { fill: "#181613", border: "var(--rule-strong)", text: "var(--ink)" },
    gold:   { fill: "rgba(212,166,74,0.10)", border: "var(--gold)", text: "var(--gold)" },
    purple: { fill: "rgba(184,137,255,0.10)", border: "#b889ff", text: "#b889ff" },
  };
  const c = colorMap[tone];
  const accent = tone === "purple" ? "#b889ff" : "var(--gold)";
  const hoverFill = tone === "purple" ? "rgba(184,137,255,0.18)" : tone === "ink" ? "rgba(255,255,255,0.04)" : "rgba(212,166,74,0.18)";
  const hoverStroke = tone === "purple" ? "#b889ff" : tone === "ink" ? "var(--rule-strong)" : "var(--gold)";
  const boxH = (fullPool && big) ? h : (amount ? (big ? 96 : 88) : h);
  return (
    <g onMouseEnter={onEnter} onMouseLeave={onLeave} style={{ cursor: "pointer" }}>
      <rect
        x={x - w/2} y={y}
        width={w} height={boxH}
        rx={14} ry={14}
        fill={hovered ? hoverFill : c.fill}
        stroke={hovered ? hoverStroke : c.border}
        strokeWidth={hovered ? 2 : 1}
        filter="url(#glassFloat)"
        style={{ transition: "all 0.2s ease" }}
      />
      <rect
        x={x - w/2} y={y}
        width={w} height={boxH}
        rx={14} ry={14}
        fill="url(#glassSheen)"
        pointerEvents="none"
      />
      <text x={x} y={y + (big ? 26 : 24)}
            textAnchor="middle"
            fontFamily="var(--sans)"
            fontWeight={big ? 800 : 700}
            fontSize={big ? "18" : "16"}
            letterSpacing="-0.015em"
            fill={hovered ? (tone === "purple" ? "#b889ff" : tone === "gold" ? "var(--gold)" : "var(--ink)") : c.text}>
        {fullPool ? `100% ${label}` : label}
      </text>
      {sub && (
        <text x={x} y={y + (big ? 44 : 44)}
              textAnchor="middle"
              fontFamily="var(--mono)"
              fontSize="8"
              letterSpacing="1.2"
              fill="var(--ink-3)">
          {sub.toUpperCase()}
        </text>
      )}
      {fullPool && (
        <text x={x} y={y + (big ? 58 : 56)}
              textAnchor="middle"
              fontFamily="var(--mono)"
              fontSize="8"
              letterSpacing="1.4"
              fill={hovered ? accent : "var(--ink-3)"}>
          TOTAL THE SONG GENERATED
        </text>
      )}
      {amount && (
        <>
          <text x={x} y={y + (big ? (fullPool ? 82 : 72) : 64)}
                textAnchor="middle"
                fontFamily="var(--sans)"
                fontWeight={800}
                fontSize="22"
                letterSpacing="-0.02em"
                fill={hovered ? accent : "var(--ink)"}
                style={{ fontVariantNumeric: "tabular-nums" }}>
            {amount}
          </text>
          {amountSub && (
            <text x={x} y={y + (big ? (fullPool ? 98 : 88) : 80)}
                  textAnchor="middle"
                  fontFamily="var(--mono)"
                  fontSize="7.5"
                  letterSpacing="1"
                  fill="var(--ink-3)">
              {amountSub.toUpperCase()}
            </text>
          )}
        </>
      )}
    </g>
  );
}

function LeafNode({ x, y, w, h, pool, id, amount, owedLabel, fullPool, hovered, expanded, dimmed, onEnter, onLeave, onClick }) {
  const c = pool.color === "gold" ? "#d4a64a" : "#b889ff";
  const fillBase = pool.color === "gold" ? "rgba(212,166,74,0.06)" : "rgba(184,137,255,0.06)";
  const fillHi   = pool.color === "gold" ? "rgba(212,166,74,0.18)" : "rgba(184,137,255,0.18)";
  const fillExp  = pool.color === "gold" ? "rgba(212,166,74,0.28)" : "rgba(184,137,255,0.28)";

  const fill = expanded ? fillExp : (hovered ? fillHi : fillBase);
  const stroke = (expanded || hovered) ? c : c + "88";
  const sw = expanded ? 2.5 : (hovered ? 2 : 1);
  const opacity = dimmed ? 0.4 : 1;

  return (
    <g
      onMouseEnter={onEnter}
      onMouseLeave={onLeave}
      onClick={onClick}
      style={{ cursor: "pointer", opacity, transition: "opacity 0.2s ease" }}
    >
      <rect
        x={x - w/2} y={y}
        width={w} height={h}
        rx={14} ry={14}
        fill={fill}
        stroke={stroke}
        strokeWidth={sw}
        filter="url(#glassFloat)"
        style={{ transition: "all 0.2s ease" }}
      />
      <rect
        x={x - w/2} y={y}
        width={w} height={h}
        rx={14} ry={14}
        fill="url(#glassSheen)"
        pointerEvents="none"
      />
      <text x={x - w/2 + 12} y={y + 20}
            fontFamily="var(--mono)"
            fontSize="9"
            letterSpacing="1.5"
            fill={c}>
        ⓘ POOL
      </text>
      <text x={x} y={y + 52}
            textAnchor="middle"
            fontFamily="var(--sans)"
            fontWeight="800"
            fontSize="23"
            letterSpacing="-0.02em"
            fill={(hovered || expanded) ? c : "var(--ink)"}>
        {pool.label.replace(" Royalties", "").replace(" Performance", " Perf.")}
      </text>
      {amount && (
        <>
          <text x={x} y={y + 78}
                textAnchor="middle"
                fontFamily="var(--sans)"
                fontWeight={800}
                fontSize="22"
                letterSpacing="-0.02em"
                fill={(hovered || expanded) ? c : "var(--ink)"}
                style={{ fontVariantNumeric: "tabular-nums" }}>
            {amount}
          </text>
          {fullPool && (
            <text x={x} y={y + 94}
                  textAnchor="middle"
                  fontFamily="var(--mono)"
                  fontSize="8"
                  letterSpacing="1.2"
                  fill="var(--ink-3)">
              {fullPool.toUpperCase()} · SONG TOTAL
            </text>
          )}
          <text x={x} y={y + (fullPool ? 110 : 100)}
                textAnchor="middle"
                fontFamily="var(--mono)"
                fontSize="9"
                letterSpacing="1"
                fill={c}>
            {owedLabel ? `OWED ${owedLabel.toUpperCase()}` : (pool.side === "publishing" ? "PUBLISHING SIDE" : "MASTER SIDE")}
          </text>
        </>
      )}
      <text x={x} y={y + (amount ? (fullPool ? 126 : 118) : 92)}
            textAnchor="middle"
            fontFamily="var(--mono)"
            fontSize="7.5"
            letterSpacing="1"
            fill={(hovered || expanded) ? c : "var(--ink-3)"}>
        {expanded ? "↳ CLICK TO COLLAPSE" : "↳ CLICK TO EXPAND"}
      </text>
    </g>
  );
}

// ---- Expanded chip row inside SVG (no connectors, clamped to viewBox) ----
function ExpandedChildren({ pool, leaf, baseY }) {
  const c = pool.color === "gold" ? "#d4a64a" : "#b889ff";
  const fillBase = pool.color === "gold" ? "rgba(212,166,74,0.08)" : "rgba(184,137,255,0.08)";

  const sources = pool.sources;
  const branched = pool.branched;
  const vbW = 1400;
  const safeMargin = 24;

  const chips = [];
  if (branched) {
    const chipW = 280, chipH = 90, gap = 24;
    const totalW = chipW * sources.length + gap * (sources.length - 1);
    const idealStart = leaf.x - totalW / 2;
    const startX = Math.max(safeMargin, Math.min(vbW - totalW - safeMargin, idealStart));
    sources.forEach((s, i) => {
      chips.push({
        ...s,
        x: startX + i * (chipW + gap),
        y: baseY + 50,
        w: chipW,
        h: chipH,
      });
    });
  } else {
    const chipW = 200, chipH = 64, gapX = 16, gapY = 12;
    const cols = 2;
    const totalW = chipW * cols + gapX * (cols - 1);
    const idealStart = leaf.x - totalW / 2;
    const startX = Math.max(safeMargin, Math.min(vbW - totalW - safeMargin, idealStart));
    sources.forEach((s, i) => {
      const col = i % cols;
      const row = Math.floor(i / cols);
      chips.push({
        ...s,
        x: startX + col * (chipW + gapX),
        y: baseY + 50 + row * (chipH + gapY),
        w: chipW,
        h: chipH,
      });
    });
  }

  // Center the eyebrow above the chip cluster (not strictly over the leaf, since chips can be clamped sideways)
  const eyebrowCenterX = chips.length
    ? (chips[0].x + chips[chips.length - 1].x + chips[chips.length - 1].w) / 2
    : leaf.x;

  return (
    <g>
      <text x={eyebrowCenterX} y={baseY + 28}
            textAnchor="middle"
            fontFamily="var(--mono)"
            fontSize="10"
            letterSpacing="2"
            fill={c}>
        ↳ WHERE TO COLLECT
      </text>

      {chips.map((chip, i) => (
        <SourceChip
          key={i}
          x={chip.x} y={chip.y}
          w={chip.w} h={chip.h}
          name={chip.name}
          desc={chip.desc}
          url={chip.url}
          color={c}
          fillBase={fillBase}
          big={chip.big}
        />
      ))}
    </g>
  );
}

function SourceChip({ x, y, w, h, name, desc, url, color, fillBase, big }) {
  const [hover, setHover] = React.useState(false);
  const clickable = !!url;

  const chipBody = (
    <g
      onMouseEnter={() => setHover(true)}
      onMouseLeave={() => setHover(false)}
      style={{ cursor: clickable ? "pointer" : "default" }}
    >
      <rect
        x={x} y={y}
        width={w} height={h}
        rx={10} ry={10}
        fill={hover && clickable ? color + "22" : fillBase}
        stroke={color}
        strokeOpacity={hover && clickable ? 1 : 0.55}
        strokeWidth={hover && clickable ? 2 : 1.2}
        filter="url(#glassFloat)"
        style={{ transition: "all 0.15s ease" }}
      />
      <rect
        x={x} y={y}
        width={w} height={h}
        rx={10} ry={10}
        fill="url(#glassSheen)"
        pointerEvents="none"
      />
      <text
        x={x + 14} y={y + (big ? 36 : 26)}
        fontFamily="var(--sans)"
        fontWeight="700"
        fontSize={big ? "16" : "13"}
        letterSpacing="-0.01em"
        fill={color}
      >
        {name}
      </text>
      <text
        x={x + 14} y={y + (big ? 60 : 46)}
        fontFamily="var(--mono)"
        fontSize={big ? "10" : "9"}
        letterSpacing="1"
        fill="var(--ink-2)"
      >
        {desc.toUpperCase()}
      </text>
      {clickable && (
        <text
          x={x + w - 12} y={y + 18}
          textAnchor="end"
          fontFamily="var(--mono)"
          fontSize="11"
          fill={color}
          opacity={hover ? 1 : 0.6}
        >
          ↗
        </text>
      )}
    </g>
  );

  if (clickable) {
    return (
      <a href={url} target="_blank" rel="noopener noreferrer">
        {chipBody}
      </a>
    );
  }
  return chipBody;
}

window.MoneyFlow = MoneyFlow;
