// CALCULATOR, the crown jewel
// Tabs: Producer Fee / Publishing / Master Royalty
// Live animation, side-by-side "what producers usually get" vs "what you should get"

const TIERS = [
  { id: "indie", label: "Independent", listeners: "0–100K monthly listeners", share: "~99%", shareOf: "Spotify artists", shareSource: "Dynamoi · Chartmetric", desc: "Bedroom artists, local acts, early-stage indies. Rates are low but credits compound." },
  { id: "rising", label: "Rising", listeners: "100K–500K monthly listeners", share: "~0.3%", shareOf: "Spotify artists", shareSource: "Dynamoi · MBW", desc: "Building a fanbase. Real placements, real budgets, but still negotiable." },
  { id: "established", label: "Established", listeners: "500K–5M monthly listeners", share: "~0.15%", shareOf: "Spotify artists", shareSource: "Spotify Loud & Clear", desc: "Career artist. Working with managers and labels. Standardized rates start applying." },
  { id: "major", label: "Major", listeners: "5M–50M monthly listeners", share: "~0.04%", shareOf: "Spotify artists", shareSource: "Spotify Loud & Clear", desc: "Major-label priority act. Real budgets, real teams, real leverage on both sides." },
  { id: "superstar", label: "Superstar", listeners: "50M+ monthly listeners", share: "<0.01%", shareOf: "Spotify artists", shareSource: "Spotify · Billboard", desc: "Global arena act. Stadium tours, brand deals. The room works around their schedule." },
];

const CREDITS = [
  { id: "building", label: "Building", stat: "0–5 real placements", share: "~85%", shareOf: "producers", shareSource: "Muso.AI · industry est.", who: "Early career. Still building a placement history.", desc: "No major label placements yet. You are establishing your sound and working your way into industry circles. Rates are lower but credits are the currency." },
  { id: "emerging", label: "Emerging", stat: "5–25 placements · a couple plaques", share: "~10%", shareOf: "producers", shareSource: "Muso.AI · RIAA est.", who: "A growing catalog. Managers and A&Rs are starting to know your name.", desc: "A few real placements. You can quote a real fee with a straight face. Reputation is building inside the industry, even if the public doesn't know your name yet." },
  { id: "established", label: "Established", stat: "25–50 placements · Plaques · Billboard", share: "~4%", shareOf: "producers", shareSource: "Billboard · Muso.AI", who: "Repeat placements. Credits that open doors in the room.", desc: "Plaques on the wall. Credits people recognize. Now the rate is the conversation, not the credit." },
  { id: "hitmaker", label: "Hitmaker", stat: "1B+ stream singles · Top #10 records · Executive producer", share: "~2%", shareOf: "producers", shareSource: "Billboard · Grammy · Muso.AI", who: "Executive producer. Top-tier track record. Your name sets the floor for the conversation.", desc: "Chart-defining records at the highest level. You run the session, not just the beat. The rate isn't a question, your name is the floor for the room." },
];

// Producer fee matrix [credit][artistTierIdx] -> [low, high] in USD
// BASE rates = non-exclusive beat lease equivalent. Hand-tuned ladder; all values end on clean increments.
const FEE_MATRIX = {
  building:    [[50, 150],    [150, 400],    [350, 1000],   [600, 2000],   [1000, 3500]],
  emerging:    [[150, 400],   [400, 1200],   [1200, 3500],  [2000, 5500],  [3500, 8500]],
  established: [[300, 800],   [800, 2000],   [2000, 5500],  [4000, 10000], [7000, 14000]],
  hitmaker:    [[400, 1000],  [1000, 3000],  [3000, 6500],  [6000, 15000], [15625, 31250]],
};
const USUAL_RATIO = 0.45; // what producers typically settle for, as a fraction of midpoint

// Snap displayed fees to readable increments after deal-type math
function roundDisplayFee(n) {
  if (n < 200) return Math.round(n / 25) * 25;
  if (n < 1000) return Math.round(n / 50) * 50;
  if (n < 5000) return Math.round(n / 100) * 100;
  if (n < 20000) return Math.round(n / 250) * 250;
  return Math.round(n / 500) * 500;
}

// Publishing splits, "usual" vs "you"
const PUB_MATRIX = {
  building:    { usualLow: 25, usualHigh: 50, youLow: 50, youHigh: 50 },
  emerging:    { usualLow: 30, usualHigh: 50, youLow: 50, youHigh: 50 },
  established: { usualLow: 35, usualHigh: 50, youLow: 50, youHigh: 50 },
  hitmaker:    { usualLow: 38, usualHigh: 50, youLow: 50, youHigh: 50 },
};

const DEAL_TYPES = [
  { id: "lease-ne",  label: "Beat Lease (Non-exclusive)", desc: "Non-exclusive. Artist can use the beat; you keep rights to sell it again. Always cap commercial copies and streaming numbers in the contract." },
  { id: "lease-ex",  label: "Beat Lease (Exclusive)",     desc: "One artist gets exclusive use. You give up the right to resell. Price reflects scarcity." },
  { id: "buyout",    label: "Buyout / Work-for-Hire",     desc: "You hand over everything for a flat fee. No backend. Higher number up front, and that's all." },
  { id: "standard",  label: "Standard Producer Deal",     desc: "Producer fee + 3-5 points + 50% publishing. The industry default for major placements." },
];

// Multipliers per deal type on the fee range
const DEAL_MULT = { "lease-ne": 1, "lease-ex": 2.0, "buyout": 3.2, "standard": 1.6 };

// Beat leases don't apply at career-artist scale, session deals only
const NO_LEASE_TIERS = new Set(["established", "major", "superstar"]);

function dealOptionsForTier(tierId) {
  if (NO_LEASE_TIERS.has(tierId)) {
    return DEAL_TYPES.filter((d) => d.id !== "lease-ne" && d.id !== "lease-ex");
  }
  return DEAL_TYPES;
}

function dealGroupsForTier(tierId) {
  const session = DEAL_TYPES.filter((d) => d.id === "standard" || d.id === "buyout");
  if (NO_LEASE_TIERS.has(tierId)) {
    return [{ label: "Placement", options: session }];
  }
  return [
    { label: "Beat market", options: DEAL_TYPES.filter((d) => d.id === "lease-ne" || d.id === "lease-ex") },
    { label: "Placement", options: session },
  ];
}

const CALC_TABS = [
  { id: "fee",        num: "01", color: "#d4a64a", title: "Producer Fee",      sub: "Upfront check" },
  { id: "publishing", num: "02", color: "#6ab8e8", title: "Publishing Split",  sub: "Songwriter split" },
  { id: "master",     num: "03", color: "#b889ff", title: "Master Royalty",    sub: "Backend points" },
];

function Calculator({ embedded, calcTab, onCalcTabChange }) {
  const [tab, setTab] = React.useState(calcTab || "fee");

  React.useEffect(() => {
    if (calcTab) setTab(calcTab);
  }, [calcTab]);

  const setTabAndNotify = (next) => {
    setTab(next);
    onCalcTabChange?.(next);
  };
  const [tier, setTier] = React.useState("indie");
  const [credit, setCredit] = React.useState("building");
  const [deal, setDeal] = React.useState("lease-ne");
  const [streams, setStreams] = React.useState(1_000_000);
  const [territory, setTerritory] = React.useState("us");
  const [platform, setPlatform] = React.useState("spotify");
  const [distro, setDistro] = React.useState("distrokid");
  const [masterStructure, setMasterStructure] = React.useState("independent");

  React.useEffect(() => {
    if (NO_LEASE_TIERS.has(tier) && (deal === "lease-ne" || deal === "lease-ex")) {
      setDeal("standard");
    }
  }, [tier, deal]);

  const tierIdx = TIERS.findIndex((t) => t.id === tier);
  const tierObj = TIERS[tierIdx];
  const creditObj = CREDITS.find((c) => c.id === credit);
  const dealOptions = dealOptionsForTier(tier);
  const dealGroups = dealGroupsForTier(tier);
  const dealObj = dealOptions.find((d) => d.id === deal) || dealOptions[0];
  const dealMult = DEAL_MULT[deal];

  const range = FEE_MATRIX[credit][tierIdx];
  const youLow = roundDisplayFee(range[0] * dealMult);
  const youHigh = roundDisplayFee(range[1] * dealMult);
  const youMid = (youLow + youHigh) / 2;
  const usual = roundDisplayFee(youMid * USUAL_RATIO);

  const aYouLow = useSmooth(youLow);
  const aYouHigh = useSmooth(youHigh);
  const aUsual = useSmooth(usual);

  const pub = PUB_MATRIX[credit];
  const aPubUsualLow = useSmooth(pub.usualLow);
  const aPubUsualHigh = useSmooth(pub.usualHigh);
  const aPubYouLow = useSmooth(pub.youLow);

  const activeTab = CALC_TABS.find((t) => t.id === tab);

  return (
    <section className={`section rule-top${embedded ? " chapter-embedded" : ""}`} id="calculator">
      <div className="page">
        <div style={{ marginBottom: 32 }}>
          <h2 className="h-section" style={{ margin: 0 }}>
            Know what you're <em>worth</em>
          </h2>
        </div>

        {/* Full-width step tab strip */}
        <div className="calc-tab-strip" style={{ marginBottom: 28 }}>
          {CALC_TABS.map((t) => {
            const active = tab === t.id;
            return (
              <button
                key={t.id}
                type="button"
                onClick={() => setTabAndNotify(t.id)}
                className={`calc-tab-btn ${active ? "active" : ""}`}
                style={{ "--tab-color": t.color }}
              >
                <span className="calc-tab-num">{t.num}</span>
                <span className="calc-tab-title">{t.title}</span>
                <span className="calc-tab-sub">{t.sub}</span>
              </button>
            );
          })}
        </div>

        {/* Body */}
        <div className="calc-shell" style={{ marginTop: 0 }}>
          {tab === "fee" && (
          <div className="calc-context">
            <div className="calc-context-note">
              <p className="calc-context-note-main">
                ★ Share % = estimated slice of Spotify artists (artist) or producers (producer).
              </p>
              <p className="calc-context-note-sources">
                Sources: Spotify Loud &amp; Clear, Chartmetric, Dynamoi, Muso.AI, Billboard, RIAA. Rounded.
              </p>
            </div>
            <div className="calc-context-grid calc-context-grid-pair">
              <Field label="Artist size" options={TIERS} value={tier} onChange={setTier}
                     tooltip={tierObj.desc}
                     share={`${tierObj.share} of ${tierObj.shareOf}`}
                     hint={tierObj.listeners} />
              <Field label="Producer size" options={CREDITS} value={credit} onChange={setCredit}
                     tooltip={creditObj.desc}
                     share={`${creditObj.share} of ${creditObj.shareOf}`}
                     hint={creditObj.stat} />
            </div>
            <div className="calc-context-deal">
              <Field label="Deal type" groups={dealGroups} options={dealOptions} value={deal} onChange={setDeal} tooltip={dealObj.desc} hint={dealObj.desc} />
            </div>
          </div>
          )}

          {tab === "fee" && (
          <div className="calc-benchmark-row">
            <span className="caption">Benchmark · usual vs. standard</span>
            <BenchmarkPanel
              tab={tab}
              layout="horizontal"
              usual={aUsual} youLow={aYouLow} youHigh={aYouHigh}
              usualRaw={usual} youLowRaw={youLow} youHighRaw={youHigh}
              pub={{ uLow: aPubUsualLow, uHigh: aPubUsualHigh, yLow: aPubYouLow }}
            />
          </div>
          )}

          {(tab === "publishing" || tab === "master") && (
          <div className="calc-panel calc-panel-workspace">
            {tab === "publishing" && (
              <PubSplitEditor
                streams={streams}
                setStreams={setStreams}
                territory={territory}
                onTerritoryChange={setTerritory}
                platform={platform}
                onPlatformChange={setPlatform}
              />
            )}
            {tab === "master" && (
              <MasterCalc
                streams={streams}
                setStreams={setStreams}
                territory={territory}
                onTerritoryChange={setTerritory}
                platform={platform}
                onPlatformChange={setPlatform}
                distro={distro}
                setDistro={setDistro}
                structure={masterStructure}
                setStructure={setMasterStructure}
              />
            )}
          </div>
          )}

          {tab === "publishing" && (
            <CollapsibleAppendix title="Regional publishing breakdown" sub="Performance vs. mechanical detail">
              <RegionalPublishingBreakdown embedded streams={streams} territory={territory} platform={platform} />
            </CollapsibleAppendix>
          )}
          {tab === "master" && (
            <>
              <CollapsibleAppendix title="Platform comparison" sub="Same territory · same stream count">
                <PlatformMasterBreakdown embedded streams={streams} territory={territory} platform={platform} onPlatformChange={setPlatform} />
              </CollapsibleAppendix>
              {masterStructure === "independent" && (
                <CollapsibleAppendix title="Distro comparison" sub="17 distributors · producer reference">
                  <DistroComparisonMatrix selectedId={distro} onSelect={setDistro} />
                </CollapsibleAppendix>
              )}
            </>
          )}
        </div>
      </div>
    </section>
  );
}

function CollapsibleAppendix({ title, sub, children }) {
  const [open, setOpen] = React.useState(false);
  return (
    <div className="calc-appendix">
      <button
        type="button"
        onClick={() => setOpen((v) => !v)}
        style={{
          width: "100%",
          display: "flex",
          justifyContent: "space-between",
          alignItems: "center",
          gap: 16,
          padding: "18px 0",
          background: "transparent",
          border: "none",
          cursor: "pointer",
          textAlign: "left",
          color: "var(--ink)",
        }}
      >
        <div>
          <span className="eyebrow no-mark" style={{ color: "var(--gold)" }}>{title}</span>
          {sub && <p className="caption" style={{ marginTop: 6 }}>{sub}</p>}
        </div>
        <span style={{ fontFamily: "var(--mono)", fontSize: 11, letterSpacing: "0.14em", color: "var(--gold)" }}>
          {open ? "HIDE ↑" : "EXPAND ↓"}
        </span>
      </button>
      {open && <div style={{ paddingBottom: 8 }}>{children}</div>}
    </div>
  );
}

function BenchmarkPanel({ tab, layout = "vertical", usual, youLow, youHigh, usualRaw, youLowRaw, youHighRaw, pub, master }) {
  const barsClass = layout === "horizontal" ? "calc-benchmark-bars" : "";
  const barsStyle = layout === "horizontal"
    ? undefined
    : { marginTop: 20, display: "flex", flexDirection: "column", gap: 20 };

  if (tab === "fee") {
    const max = Math.max(youHighRaw, usualRaw) * 1.1 || 1;
    return (
      <div className={barsClass} style={barsStyle}>
        <BarBlock tone="noise" label="What producers usually settle for" rangeLabel="Mid-range" value={`$${formatNum(usual)}`} tag="USUAL" pct={(usualRaw / max) * 100} />
        <BarBlock tone="gold" label="What you should quote" rangeLabel="Floor → ceiling" value={`$${formatNum(youLow)} – $${formatNum(youHigh)}`} tag="STANDARD" pct={(youHighRaw / max) * 100} secondaryPct={(youLowRaw / max) * 100} />
      </div>
    );
  }
  if (tab === "publishing") {
    const max = 50;
    return (
      <div className={barsClass} style={barsStyle}>
        <BarBlock tone="noise" label="What producers usually get" rangeLabel="Writer share" value={`${Math.round(pub.uLow)}–${Math.round(pub.uHigh)}%`} tag="USUAL" pct={(pub.uHigh / max) * 100} />
        <BarBlock tone="gold" label="What you should get" rangeLabel="If you wrote it" value={`${Math.round(pub.yLow)}%`} tag="STANDARD" pct={(pub.yLow / max) * 100} />
        {layout !== "horizontal" && (
          <p className="body-text" style={{ fontSize: 13, lineHeight: 1.5, margin: 0, color: "var(--ink-3)" }}>
            Use the workspace → to model splits with co-producers and the artist.
          </p>
        )}
      </div>
    );
  }
  const max = Math.max(master.yHigh, master.uHigh, 1) || 6;
  return (
    <div style={layout === "horizontal" ? { marginTop: 16 } : undefined}>
      <div className={barsClass} style={barsStyle}>
        <BarBlock tone="noise" label="What producers usually get" rangeLabel="Master points" value={`${Math.round(master.uLow)}–${Math.round(master.uHigh)} pts`} tag="USUAL" pct={(master.uHigh / max) * 100} />
        <BarBlock tone="gold" label="What you should target" rangeLabel="Points on master" value={`${Math.round(master.yLow)}–${Math.round(master.yHigh)} pts`} tag="STANDARD" pct={(master.yHigh / max) * 100} secondaryPct={(master.yLow / max) * 100} />
      </div>
      <p className="body-text" style={{ fontSize: 13, lineHeight: 1.5, margin: layout === "horizontal" ? "14px 0 0" : 0, color: "var(--ink-3)" }}>
        Indie = ownership % via distro. Signed = points + recoup. Workspace ↓ for the math.
      </p>
    </div>
  );
}


function FieldTooltip({ text }) {
  const [open, setOpen] = React.useState(false);
  if (!text) return null;
  return (
    <span className="field-tooltip-wrap">
      <button
        type="button"
        className="field-tooltip-trigger"
        aria-label="More info"
        aria-expanded={open}
        onMouseEnter={() => setOpen(true)}
        onMouseLeave={() => setOpen(false)}
        onFocus={() => setOpen(true)}
        onBlur={() => setOpen(false)}
      >
        ⓘ
      </button>
      {open && (
        <span className="field-tooltip-popover" role="tooltip">
          {text}
        </span>
      )}
    </span>
  );
}

function Field({ label, options, groups, value, onChange, desc, tooltip, share, hint }) {
  const selected = options.find((o) => o.id === value);
  const tipText = tooltip ?? selected?.desc ?? desc;
  return (
    <div className="field-block">
      <div className="field-label-row">
        <span className="field-label">
          {label}
          <FieldTooltip text={tipText} />
        </span>
      </div>
      {share && (
        <p className="field-share-subline">
          <span className="field-label-share-key">Share:</span> {share}
        </p>
      )}
      <div className="field-select-wrap">
        <select className="field" value={value} onChange={(e) => onChange(e.target.value)}>
          {groups
            ? groups.map((g) => (
                <optgroup key={g.label} label={g.label}>
                  {g.options.map((o) => (
                    <option key={o.id} value={o.id} style={{ background: "var(--bg)", color: "var(--ink)" }}>
                      {o.label}
                    </option>
                  ))}
                </optgroup>
              ))
            : options.map((o) => (
                <option key={o.id} value={o.id} style={{ background: "var(--bg)", color: "var(--ink)" }}>
                  {o.label}
                </option>
              ))}
        </select>
        <span className="field-select-chevron">▼</span>
      </div>
      {hint && <p className="field-hint">{hint}</p>}
    </div>
  );
}

function BarBlock({ tone, label, rangeLabel, value, tag, pct, secondaryPct, compact }) {
  const accent = tone === "gold" ? "var(--gold)" : tone === "noise" ? "rgba(192, 57, 43, 0.95)" : "var(--ink-3)";
  const valueColor = tone === "gold" ? "var(--gold)" : tone === "noise" ? "rgba(192, 57, 43, 0.95)" : "var(--ink-2)";
  const barFill = tone === "gold"
    ? "linear-gradient(90deg, var(--gold-dim), var(--gold))"
    : tone === "noise"
    ? "linear-gradient(90deg, #6a1a10, #c0392b)"
    : "linear-gradient(90deg, #2a2620, #4a4234)";
  const barSecondary = tone === "gold" ? "rgba(212,166,74,0.35)" : tone === "noise" ? "rgba(192, 57, 43, 0.28)" : "var(--ink-4)";

  return (
    <div>
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "baseline", marginBottom: compact ? 8 : 10 }}>
        <div>
          <span style={{ fontFamily: "var(--mono)", fontSize: 9, letterSpacing: "0.16em", color: accent }}>
            {tag}
          </span>
          <div style={{ fontFamily: "var(--sans)", fontWeight: 600, fontSize: compact ? 14 : 16, color: tone === "gold" ? "var(--ink)" : "var(--ink-2)", marginTop: 2, letterSpacing: "-0.01em" }}>
            {label}
          </div>
        </div>
        <div style={{ textAlign: "right" }}>
          <div className="caption" style={{ fontSize: compact ? 9 : undefined }}>{rangeLabel}</div>
          <div style={{ fontFamily: "var(--sans)", fontWeight: 800, fontSize: compact ? "clamp(22px, 2.4vw, 32px)" : "clamp(28px, 3.6vw, 48px)", letterSpacing: "-0.03em", color: valueColor, lineHeight: 1, fontVariantNumeric: "tabular-nums" }}>
            {value}
          </div>
        </div>
      </div>
      <div style={{ position: "relative", height: compact ? 14 : 18, background: "rgba(255,255,255,0.04)", overflow: "hidden" }}>
        {secondaryPct !== undefined && (
          <div style={{
            position: "absolute", inset: 0,
            width: `${secondaryPct}%`,
            background: barSecondary,
            transition: "width 0.5s cubic-bezier(0.2, 0.8, 0.2, 1)",
          }}></div>
        )}
        <div style={{
          position: "absolute", inset: 0,
          width: `${pct}%`,
          background: barFill,
          transition: "width 0.5s cubic-bezier(0.2, 0.8, 0.2, 1)",
        }}></div>
        {secondaryPct !== undefined && (
          <>
            <div style={{ position: "absolute", left: `${secondaryPct}%`, top: -3, bottom: -3, width: 1, background: "var(--ink-2)" }}></div>
            <div style={{ position: "absolute", left: `${pct}%`, top: -3, bottom: -3, width: 1, background: "var(--gold)" }}></div>
          </>
        )}
      </div>
    </div>
  );
}

// Regional royalty data, comprehensive breakdown
// US all-in publishing (performance + mechanical) ≈ $600–800 per million streams.
const US_PUB_PER_1K = 0.70;

const REGIONAL_PUBLISHING = {
  us: { label: "United States", perf: 0.35, mech: 0.35, total: US_PUB_PER_1K, mechSociety: "MLC", perfSociety: "PRO", confidence: "defensible", source: "MLC/ASCAP/BMI collateral + distributor data" },
  uk: { label: "United Kingdom", perf: 0.41, mech: 0.44, total: 0.85, mechSociety: "MCPS", perfSociety: "PRS", confidence: "consensus", source: "PRS/MCPS + distributor data" },
  global: { label: "Global (blended)", perf: 0.28, mech: 0.32, total: 0.60, mechSociety: "local societies", perfSociety: "local PROs", confidence: "extrapolated", source: "IFPI weighted average" },
  eu: { label: "Europe (high-ARPU)", perf: 0.43, mech: 0.47, total: 0.90, mechSociety: "MCPS", perfSociety: "PRS", confidence: "defensible", source: "SoundExchange EU + MCPS/PRS data" },
  mexico: { label: "Mexico", perf: 0.20, mech: 0.25, total: 0.45, mechSociety: "SACM", perfSociety: "SACM", confidence: "consensus", source: "Distributor reports + SACM" },
  asia: { label: "Asia ex-Japan", perf: 0.10, mech: 0.10, total: 0.20, mechSociety: "local societies", perfSociety: "local PROs", confidence: "extrapolated", source: "Low ARPU regions; sparse data" },
  japan: { label: "Japan", perf: 0.48, mech: 0.52, total: 1.00, mechSociety: "JASRAC", perfSociety: "JASRAC", confidence: "consensus", source: "High-ARPU market + local societies" },
};

const TERRITORY_PAYOUT_MAP = {
  us: { publishingKey: "us", masterRegionKey: "us" },
  uk: { publishingKey: "uk", masterRegionKey: "uk" },
  global: { publishingKey: "global", masterRegionKey: "global" },
};

function publishingRegionForTerritory(territory) {
  const key = TERRITORY_PAYOUT_MAP[territory]?.publishingKey || "us";
  return REGIONAL_PUBLISHING[key];
}

function publishingRatesForPlatform(territory, platform = "spotify") {
  const region = publishingRegionForTerritory(territory);
  if (!window.isNonInteractiveStream?.(platform)) {
    return { ...region, streamType: "interactive" };
  }
  const mechMult = window.NON_INTERACTIVE_MECH_MULT ?? 0.12;
  const mech = region.mech * mechMult;
  const perf = region.perf;
  return {
    ...region,
    perf,
    mech,
    total: perf + mech,
    mechSociety: "webcast · minimal",
    streamType: "non-interactive",
  };
}

function masterRateForTerritoryPlatform(territory, platform) {
  const regionKey = TERRITORY_PAYOUT_MAP[territory]?.masterRegionKey || "us";
  if (window.isNonInteractiveStream?.(platform)) {
    return (window.sxPer1kForRegion?.(regionKey) ?? 0.20) / 1000;
  }
  const ratesTable = window.PLATFORM_MASTER_RATES || {};
  const rates = ratesTable[platform] || ratesTable.spotify;
  return rates?.[regionKey] ?? rates?.us ?? 0.004;
}

function masterPer1kForTerritoryPlatform(territory, platform) {
  return masterRateForTerritoryPlatform(territory, platform) * 1000;
}

function DspPickerGrouped({ platform, onPlatformChange }) {
  const platforms = window.DSP_PLATFORMS || [];
  const interactive = platforms.filter((d) => d.streamType !== "non-interactive");
  const nonInteractive = platforms.filter((d) => d.streamType === "non-interactive");

  const renderBtn = (dsp) => (
    <button
      key={dsp.id}
      type="button"
      className={`pub-ns-dsp-btn${platform === dsp.id ? " active" : ""}`}
      onClick={() => onPlatformChange(dsp.id)}
      title={dsp.label}
      style={{ "--dsp-color": dsp.color }}
    >
      <DspIcon id={dsp.id} size={18} />
      <span>{dsp.label}</span>
    </button>
  );

  const streamTypes = window.STREAM_TYPES || {};

  return (
    <div className="pub-ns-dsp-groups">
      <div className="pub-ns-dsp-group">
        <span className="pub-ns-dsp-group-label">
          Interactive · on-demand
          <FieldTooltip text={streamTypes.interactive?.tooltip} />
        </span>
        <div className="pub-ns-dsp">{interactive.map(renderBtn)}</div>
      </div>
      <div className="pub-ns-dsp-group">
        <span className="pub-ns-dsp-group-label">
          Non-interactive · radio &amp; webcast
          <FieldTooltip text={streamTypes["non-interactive"]?.tooltip} />
        </span>
        <div className="pub-ns-dsp">{nonInteractive.map(renderBtn)}</div>
      </div>
    </div>
  );
}

function DspIconFallback({ id, size }) {
  const s = size;
  if (id === "amazon") {
    return (
      <svg width={s} height={s} viewBox="0 0 24 24" aria-hidden="true" className="dsp-icon-fallback">
        <rect width="24" height="24" rx="6" fill="#232f3e" />
        <path fill="#25d1da" d="M6.5 16.5c3.2 1.8 7.4 1.8 10.6 0 .2-.1.4 0 .5.2.1.2 0 .4-.2.5-3.6 2.1-8.2 2.1-11.8 0-.2-.1-.3-.3-.2-.5.1-.2.3 0 .4z" />
        <path fill="#fff" d="M12.5 6.5c-2.4 0-4.3 1.9-4.3 4.3s1.9 4.3 4.3 4.3 4.3-1.9 4.3-4.3-1.9-4.3-4.3-4.3z" />
      </svg>
    );
  }
  if (id === "tidal") {
    return (
      <svg width={s} height={s} viewBox="0 0 24 24" aria-hidden="true" className="dsp-icon-fallback">
        <rect width="24" height="24" rx="6" fill="#111" />
        <path fill="#fff" d="M6 16l3-4-3-4h2.5l3 4-3 4H6zm5.5 0l3-4-3-4H11l3 4-3 4h.5zm5.5 0l3-4-3-4H16l3 4-3 4h1z" />
      </svg>
    );
  }
  return (
    <span className="dsp-icon-fallback dsp-icon-fallback--text" style={{ width: s, height: s, fontSize: Math.max(9, s * 0.45) }}>
      {(window.getDspPlatform?.(id)?.label || id).charAt(0)}
    </span>
  );
}

function DspIcon({ id, size = 16 }) {
  const platform = window.getDspPlatform?.(id);
  const base = window.DSP_LOGO_BASE || "assets/dsp";
  if (platform?.logo) {
    return (
      <img
        src={`${base}/${platform.logo}`}
        alt=""
        className="dsp-icon-img"
        style={{ width: size, height: size }}
        loading="lazy"
        decoding="async"
      />
    );
  }
  return <DspIconFallback id={id} size={size} />;
}

// Confidence badge colors
const CONFIDENCE_COLORS = {
  defensible: { bg: "rgba(106,184,232,0.12)", border: "#6ab8e8", text: "#7bc4f0", label: "Defensible" },
  consensus: { bg: "rgba(122,207,153,0.12)", border: "#7acf99", text: "#8acaa0", label: "Industry consensus" },
  "industry-consensus": { bg: "rgba(122,207,153,0.12)", border: "#7acf99", text: "#8acaa0", label: "Industry consensus" },
  extrapolated: { bg: "rgba(184,137,255,0.12)", border: "#b889ff", text: "#c5a3ff", label: "Extrapolated" },
};

const ROLE_COLORS = {
  Producer: { fg: "#d4a64a", bg: "rgba(212,166,74,0.18)", border: "var(--gold)" },
  "Co-producer": { fg: "#e8c878", bg: "rgba(232,200,120,0.14)", border: "#e8c878" },
  Songwriter: { fg: "#b889ff", bg: "rgba(184,137,255,0.14)", border: "#b889ff" },
  Artist: { fg: "#6ab8e8", bg: "rgba(106,184,232,0.14)", border: "#6ab8e8" },
};

const PUB_PERF_COLOR = "var(--gold)";
const PUB_MECH_COLOR = "#7acf99";

const PUB_SIDE_CAP = 50;
const PUB_STREAM_STOPS = [10_000, 100_000, 1_000_000, 10_000_000, 100_000_000, 1_000_000_000];

function pubStreamStopIndex(value) {
  let best = 0;
  let bestDiff = Infinity;
  PUB_STREAM_STOPS.forEach((v, i) => {
    const diff = Math.abs(v - value);
    if (diff < bestDiff) {
      bestDiff = diff;
      best = i;
    }
  });
  return best;
}

function pubStreamChipLabel(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 pubRoyaltyBreakdown(streams, writerSharePct = 100, territory = "us", platform = "spotify") {
  const { perf, mech } = publishingRatesForPlatform(territory, platform);
  const perfPool = (streams / 1000) * perf;
  const mechPool = (streams / 1000) * mech;
  const mult = writerSharePct / 100;
  return {
    perfPool,
    mechPool,
    yourPerf: perfPool * mult,
    yourMech: mechPool * mult,
  };
}
const isProducerSide = (role) => role === "Producer" || role === "Co-producer";
const isArtistSide = (role) => role === "Artist" || role === "Songwriter";

function splitSideEvenly(count, sideCap = PUB_SIDE_CAP) {
  if (!count) return [];
  const base = Math.floor((sideCap / count) * 10) / 10;
  const shares = Array.from({ length: count }, () => base);
  shares[0] = +(shares[0] + (sideCap - shares.reduce((a, s) => a + s, 0))).toFixed(1);
  return shares;
}

function rebalanceSide(list, sideFilter) {
  const sideMembers = list.filter((c) => sideFilter(c));
  const shares = splitSideEvenly(sideMembers.length);
  let i = 0;
  return list.map((c) => {
    if (!sideFilter(c)) return c;
    return { ...c, share: shares[i++] };
  });
}

function rebalanceBothSides(list) {
  return rebalanceSide(rebalanceSide(list, (c) => isProducerSide(c.role)), (c) => isArtistSide(c.role));
}

function clampSideShare(value, sideCap = PUB_SIDE_CAP) {
  return Math.max(0, Math.min(sideCap, Number(value) || 0));
}

function pubStreamHeadline(streams) {
  if (streams >= 1_000_000_000) {
    return `${(streams / 1_000_000_000).toFixed(1).replace(/\.0$/, "")}B streams`;
  }
  if (streams >= 1_000_000) return `${Math.round(streams / 1_000_000)}M streams`;
  return `${Math.round(streams / 1_000)}K streams`;
}

function pubStreamCoherence(streams) {
  return Math.min(1, Math.max(0, (Math.log10(Math.max(streams, 10_000)) - 4) / 5));
}

function PubNoiseWaveform({ streams }) {
  const canvasRef = React.useRef(null);
  const rafRef = React.useRef(0);
  const reduceRef = React.useRef(
    typeof window !== "undefined" && window.matchMedia("(prefers-reduced-motion: reduce)").matches
  );

  React.useEffect(() => {
    const canvas = canvasRef.current;
    if (!canvas) return;
    const ctx = canvas.getContext("2d");
    let W = 0;
    let H = 0;
    let dpr = 1;
    const seeds = Array.from({ length: 48 }, () => Math.random() * 2 - 1);

    function cohAt(xN, target) {
      const pivot = 0.46;
      const width = 0.14 + target * 0.08;
      if (xN <= pivot - width) return 0;
      if (xN >= pivot + width) return 1;
      const t = (xN - (pivot - width)) / (width * 2);
      return t * t * (3 - 2 * t);
    }

    function signalAt(xN, t) {
      const env = Math.sin(xN * Math.PI);
      const a = Math.sin(xN * Math.PI * 6 + t * 1.05);
      const b = Math.sin(xN * Math.PI * 13 - t * 0.7) * 0.36;
      const c = Math.sin(xN * Math.PI * 2.5 + t * 0.45) * 0.55;
      return ((a + b + c) / 2) * env;
    }

    function resize() {
      const rect = canvas.getBoundingClientRect();
      W = Math.max(1, Math.round(rect.width));
      H = Math.max(1, Math.round(rect.height));
      dpr = Math.min(window.devicePixelRatio || 1, 2);
      canvas.width = Math.round(W * dpr);
      canvas.height = Math.round(H * dpr);
      ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
    }

    function draw(t) {
      ctx.clearRect(0, 0, W, H);
      if (W < 2 || H < 2) return;
      const mid = H * 0.52;
      const target = pubStreamCoherence(streams);
      const amp = H * 0.16 * (0.82 + 0.18 * Math.sin(t * 0.55));
      const step = Math.max(2, Math.round(W / 480));
      const pts = [];

      for (let x = 0; x <= W; x += step) {
        const xN = x / W;
        const coh = cohAt(xN, target);
        const si = Math.min(seeds.length - 1, Math.floor(xN * (seeds.length - 1)));
        const sig = signalAt(xN, t);
        const jitter = reduceRef.current
          ? seeds[si] * 0.35
          : seeds[si] * 0.65 + Math.sin(xN * 120 + t * 6.5) * 0.42;
        const v = sig * coh + jitter * (1 - coh);
        pts.push(x, mid - v * amp);
      }

      const stroke = ctx.createLinearGradient(0, 0, W, 0);
      stroke.addColorStop(0, "rgba(192,57,43,0.82)");
      stroke.addColorStop(0.42, "rgba(176,86,52,0.75)");
      stroke.addColorStop(0.58, "rgba(212,166,74,0.88)");
      stroke.addColorStop(1, "rgba(232,200,120,0.95)");

      ctx.beginPath();
      ctx.moveTo(pts[0], pts[1]);
      for (let i = 2; i < pts.length; i += 2) ctx.lineTo(pts[i], pts[i + 1]);
      ctx.strokeStyle = stroke;
      ctx.lineWidth = 2;
      ctx.stroke();
    }

    function loop(t) {
      draw(reduceRef.current ? 0 : t * 0.001);
      if (!reduceRef.current) rafRef.current = requestAnimationFrame(loop);
    }

    resize();
    draw(0);
    if (!reduceRef.current) rafRef.current = requestAnimationFrame(loop);

    const ro = new ResizeObserver(() => { resize(); draw(0); });
    ro.observe(canvas.parentElement);
    return () => {
      cancelAnimationFrame(rafRef.current);
      ro.disconnect();
    };
  }, [streams]);

  return <canvas ref={canvasRef} className="pub-ns-wave-canvas" aria-hidden="true" />;
}

function PubNoiseSignalDeck({
  streams,
  setStreams,
  variant = "publishing",
  yourShare,
  onShareChange,
  pubPool,
  yourEarned,
  breakdown,
  poolBar,
  children,
  territory: territoryProp,
  onTerritoryChange,
  platform: platformProp,
  onPlatformChange,
  showDspPicker = false,
  defaultTerritory = "us",
  defaultPlatform = "spotify",
  className = "",
}) {
  const isStreamsOnly = variant === "streams";
  const [internalTerritory, setInternalTerritory] = React.useState(defaultTerritory);
  const [internalPlatform, setInternalPlatform] = React.useState(defaultPlatform);
  const territory = territoryProp ?? internalTerritory;
  const setTerritory = onTerritoryChange ?? setInternalTerritory;
  const platform = platformProp ?? internalPlatform;
  const setPlatform = onPlatformChange ?? setInternalPlatform;
  const pubRegion = publishingRatesForPlatform(territory, platform);
  const streamTypeMeta = window.getStreamTypeMeta?.(platform) || {};
  const [customOpen, setCustomOpen] = React.useState(false);
  const [customDraft, setCustomDraft] = React.useState("");
  const tdata = TERRITORY_CERTS[territory];
  const allStops = [...BASE_STREAM_STOPS, ...tdata.tiers.map((t) => t.streams)].sort((a, b) => a - b);
  const currentPlaque = tdata.tiers.find((t) => t.streams === streams);
  const isCustomStop = !allStops.includes(streams);

  const openCustom = () => {
    setCustomDraft(formatNum(streams));
    setCustomOpen(true);
  };

  const applyCustom = () => {
    const parsed = Math.round(Number(String(customDraft).replace(/,/g, "")) || 0);
    const clamped = Math.min(MAX_CUSTOM_STREAMS, Math.max(1, parsed || 10_000));
    setStreams(clamped);
    setCustomOpen(false);
  };
  const restPct = Math.max(0, 100 - (yourShare || 0));
  const restEarned = (pubPool || 0) - (yourEarned || 0);
  const leftFr = Math.max(18, restPct);
  const rightFr = Math.max(18, yourShare || 0);

  function stopLabel(s) {
    const p = tdata.tiers.find((t) => t.streams === s);
    if (p) return p.name;
    if (s >= 1_000_000_000) return `${s / 1_000_000_000}B`;
    if (s >= 1_000_000) return `${s / 1_000_000}M`;
    return `${s / 1_000}K`;
  }

  function stopColor(s) {
    return tdata.tiers.find((t) => t.streams === s)?.color || null;
  }

  const unitsLabel = (n) => {
    if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(0)}M`;
    if (n >= 1_000) return `${(n / 1_000).toFixed(0)}K`;
    return n;
  };

  return (
    <div className={`pub-ns-deck${className ? ` ${className}` : ""}`}>
      <div className="pub-ns-wave-zone">
        <PubNoiseWaveform streams={streams} />
        <div className="pub-ns-wave-content">
          <div className="pub-ns-stream-display">
            <span className="caption">If this song does</span>
            <h3 className="pub-ns-stream-num">{pubStreamHeadline(streams)}</h3>
            <div className="pub-ns-plaque" style={{ color: currentPlaque ? currentPlaque.color : "transparent" }}>
              {currentPlaque
                ? `${tdata.body} ${currentPlaque.name.toUpperCase()} · ${tdata.flag} ${tdata.label} · ${unitsLabel(currentPlaque.units)} certified units`
                : "\u00a0"}
            </div>
          </div>
          <div className="pub-ns-territory pub-ns-territory--inline">
            {Object.entries(TERRITORY_CERTS).map(([key, td]) => (
              <button
                key={key}
                type="button"
                className={territory === key ? "active" : ""}
                onClick={() => setTerritory(key)}
              >
                {td.flag} {td.label}
              </button>
            ))}
            <span className="pub-ns-territory-meta">{tdata.body} · {tdata.type}</span>
          </div>
          <div className="pub-ns-chips">
            {allStops.map((s) => {
              const pc = stopColor(s);
              const active = streams === s;
              return (
                <button
                  key={s}
                  type="button"
                  className={`pub-ns-chip${active ? " active" : ""}`}
                  onClick={() => {
                    setStreams(s);
                    setCustomOpen(false);
                  }}
                  style={active && pc ? { borderColor: pc, background: `${pc}22`, color: pc } : undefined}
                >
                  {stopLabel(s)}
                </button>
              );
            })}
            {customOpen ? (
              <div className="pub-ns-chip-custom">
                <input
                  autoFocus
                  type="text"
                  inputMode="numeric"
                  value={customDraft}
                  placeholder="e.g. 2500000"
                  aria-label="Custom stream count"
                  onChange={(e) => setCustomDraft(e.target.value)}
                  onBlur={applyCustom}
                  onKeyDown={(e) => {
                    if (e.key === "Enter") applyCustom();
                    if (e.key === "Escape") setCustomOpen(false);
                  }}
                />
                <span className="pub-ns-chip-custom-cap">max 5B</span>
                <button type="button" className="pub-ns-chip-custom-set" onMouseDown={(e) => e.preventDefault()} onClick={applyCustom}>
                  Set
                </button>
                <button type="button" className="pub-ns-chip-custom-cancel" onClick={() => setCustomOpen(false)} aria-label="Cancel">
                  ×
                </button>
              </div>
            ) : (
              <button
                type="button"
                className={`pub-ns-chip pub-ns-chip--custom${isCustomStop ? " active" : ""}`}
                onClick={openCustom}
                title="Enter a custom stream count (max 5 billion)"
              >
                {isCustomStop ? pubStreamChipLabel(streams) : "Custom"}
              </button>
            )}
          </div>
          {showDspPicker && (
            <DspPickerGrouped platform={platform} onPlatformChange={setPlatform} />
          )}
        </div>
      </div>

      {isStreamsOnly ? (
        poolBar && (
          <div className="pub-ns-pool-bar">
            <span className="caption">{poolBar.label}</span>
            <span style={{ fontFamily: "var(--sans)", fontWeight: 700, fontSize: 15, color: "var(--ink-2)", fontVariantNumeric: "tabular-nums" }}>
              {poolBar.value}
            </span>
          </div>
        )
      ) : (
        <>
          <div className="pub-ns-pool-bar">
            <span className="caption">
              Publishing pool · {pubRegion.label} · {streamTypeMeta.label || "Interactive"} · ${pubRegion.total.toFixed(2)} / 1K
            </span>
            <span style={{ fontFamily: "var(--sans)", fontWeight: 700, fontSize: 15, color: "var(--ink-2)", fontVariantNumeric: "tabular-nums" }}>
              ${formatNum(pubPool)} total
            </span>
          </div>

          <div className="pub-ns-split" style={{ gridTemplateColumns: `${leftFr}fr ${rightFr}fr` }}>
            <div className="pub-ns-side pub-ns-side--noise">
              <span className="pub-ns-side-label">Rest of pool</span>
              <div className="pub-ns-side-val">${formatNum(restEarned)}</div>
              <span className="pub-ns-side-sub">{restPct.toFixed(1)}% · artist + co-writers</span>
            </div>
            <div className="pub-ns-side pub-ns-side--signal">
              <span className="pub-ns-side-label">Your writer's cut</span>
              <div className="pub-ns-side-val">${formatNum(yourEarned)}</div>
              <span className="pub-ns-side-sub">{yourShare}% of publishing pool</span>
            </div>
          </div>

          <div className="pub-ns-breakdown">
            <div className="pub-ns-breakdown-cell">
              <span className="caption">Performance → {pubRegion.perfSociety}</span>
              <div style={{ fontFamily: "var(--sans)", fontWeight: 700, fontSize: 16, color: "var(--gold)", marginTop: 4, fontVariantNumeric: "tabular-nums" }}>
                ${formatNum(breakdown.yourPerf)}
              </div>
            </div>
            <div className="pub-ns-breakdown-cell">
              <span className="caption">Mechanical → {pubRegion.mechSociety}</span>
              <div style={{ fontFamily: "var(--sans)", fontWeight: 700, fontSize: 16, color: "#6ab8e8", marginTop: 4, fontVariantNumeric: "tabular-nums" }}>
                ${formatNum(breakdown.yourMech)}
              </div>
            </div>
          </div>

        </>
      )}

      {children}
    </div>
  );
}

function PubSplitEditor({ streams, setStreams, territory, onTerritoryChange, platform, onPlatformChange }) {
  const [contributors, setContributors] = React.useState([
    { id: 1, role: "Producer", name: "You", share: 50 },
    { id: 2, role: "Artist", name: "The Artist", share: 50 },
  ]);

  const producerSide = contributors.filter((c) => isProducerSide(c.role));
  const artistSide = contributors.filter((c) => isArtistSide(c.role));
  const producerTotal = producerSide.reduce((a, c) => a + Number(c.share || 0), 0);
  const artistTotal = artistSide.reduce((a, c) => a + Number(c.share || 0), 0);
  const pubRegion = publishingRatesForPlatform(territory, platform);
  const pubPool = (streams / 1000) * pubRegion.total;

  // Derive your share directly from contributors, stays in sync with rebalancing
  const yourShare = Number(contributors.find((c) => c.id === 1)?.share || 0);
  const yourEarned = pubPool * (yourShare / 100);
  const breakdown = pubRoyaltyBreakdown(streams, yourShare, territory, platform);

  const update = (id, patch) => {
    setContributors((cs) => {
      const prev = cs.find((c) => c.id === id);
      let next = cs.map((c) => (c.id === id ? { ...c, ...patch } : c));
      if (patch.role && prev && ((isProducerSide(prev.role) && isArtistSide(patch.role)) || (isArtistSide(prev.role) && isProducerSide(patch.role)))) {
        next = rebalanceBothSides(next);
      } else if (patch.share !== undefined) {
        next = next.map((c) => (c.id === id ? { ...c, share: clampSideShare(patch.share) } : c));
      }
      return next;
    });
  };

  const add = (role) => {
    const newId = Math.max(...contributors.map((c) => c.id), 0) + 1;
    const defaultName = role === "Co-producer" ? "Co-producer" : role === "Songwriter" ? "Songwriter" : role;
    setContributors((cs) => {
      const next = [...cs, { id: newId, role, name: defaultName, share: 0 }];
      if (isProducerSide(role)) return rebalanceSide(next, (c) => isProducerSide(c.role));
      if (isArtistSide(role)) return rebalanceSide(next, (c) => isArtistSide(c.role));
      return next;
    });
  };

  const remove = (id) => {
    setContributors((cs) => {
      if (cs[0].id === id) return cs;
      const removed = cs.find((c) => c.id === id);
      const next = cs.filter((c) => c.id !== id);
      if (!next.some((c) => isProducerSide(c.role)) || !next.some((c) => isArtistSide(c.role))) return cs;
      if (removed && isProducerSide(removed.role)) return rebalanceSide(next, (c) => isProducerSide(c.role));
      return rebalanceSide(next, (c) => isArtistSide(c.role));
    });
  };

  const rebalanceProducerSide = () => setContributors((cs) => rebalanceSide(cs, (c) => isProducerSide(c.role)));
  const rebalanceArtistSide = () => setContributors((cs) => rebalanceSide(cs, (c) => isArtistSide(c.role)));

  return (
    <div className="pub-split-editor">
      <PubNoiseSignalDeck
        streams={streams}
        setStreams={setStreams}
        territory={territory}
        onTerritoryChange={onTerritoryChange}
        platform={platform}
        onPlatformChange={onPlatformChange}
        showDspPicker
        yourShare={yourShare}
        onShareChange={(v) => update(1, { share: v })}
        pubPool={pubPool}
        yourEarned={yourEarned}
        breakdown={breakdown}
      />

      <div className="pub-split-editor-body" style={{ marginTop: 28, borderTop: "1px solid var(--rule)", paddingTop: 20 }}>
        <SideSplitBar producerSide={producerSide} artistSide={artistSide} />
        <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(min(100%, 220px), 1fr))", gap: 0, marginTop: 16, border: "1px solid var(--rule)", borderRadius: 6, overflow: "hidden" }}>
          <div style={{ padding: "16px 18px", borderRight: "1px solid var(--rule)" }}>
            <PubSideGroup
              label="Producer side"
              color="var(--gold)"
              total={producerTotal}
              sideCap={PUB_SIDE_CAP}
              actions={(
                <>
                  <AddBtn label="+ Co-producer" onClick={() => add("Co-producer")} />
                  <AddBtn label="↺ Even" onClick={rebalanceProducerSide} />
                </>
              )}
            >
              {producerSide.map((c) => (
                <PubContributorRow key={c.id} c={c} isYou={c.id === 1} sideCap={PUB_SIDE_CAP}
                  onChange={(patch) => update(c.id, patch)} onRemove={() => remove(c.id)} pubPool={pubPool} />
              ))}
            </PubSideGroup>
          </div>
          <div style={{ padding: "16px 18px" }}>
            <PubSideGroup
              label="Artist side"
              color="#6ab8e8"
              total={artistTotal}
              sideCap={PUB_SIDE_CAP}
              actions={(
                <>
                  <AddBtn label="+ Songwriter" onClick={() => add("Songwriter")} />
                  <AddBtn label="+ Artist" onClick={() => add("Artist")} />
                  <AddBtn label="↺ Even" onClick={rebalanceArtistSide} />
                </>
              )}
            >
              {artistSide.map((c) => (
                <PubContributorRow key={c.id} c={c} isYou={false} sideCap={PUB_SIDE_CAP}
                  onChange={(patch) => update(c.id, patch)} onRemove={() => remove(c.id)} pubPool={pubPool} />
              ))}
            </PubSideGroup>
          </div>
        </div>
      </div>
    </div>
  );
}

function PubBlock({ label, children, first }) {
  return (
    <div style={{ paddingTop: first ? 0 : 18, borderTop: first ? "none" : "1px solid var(--rule)" }}>
      <span style={{ display: "block", fontFamily: "var(--mono)", fontSize: 10, letterSpacing: "0.16em", color: "var(--gold)", textTransform: "uppercase", marginBottom: 12 }}>{label}</span>
      {children}
    </div>
  );
}

function PubSideGroup({ label, color, total, sideCap = PUB_SIDE_CAP, actions, children }) {
  const balanced = Math.abs(total - sideCap) < 0.05;
  return (
    <div style={{ marginTop: 14, padding: "10px 0 2px", borderTop: `1px solid ${color === "var(--gold)" ? "rgba(212,166,74,0.18)" : "rgba(106,184,232,0.18)"}` }}>
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "baseline", gap: 8, marginBottom: 8 }}>
        <span className="caption" style={{ color: balanced ? color : "#e88a7d" }}>
          {label} · {total.toFixed(total % 1 ? 1 : 0)}%{balanced ? "" : ` · must equal ${sideCap}%`}
        </span>
        <div style={{ display: "flex", gap: 6, flexWrap: "wrap", justifyContent: "flex-end" }}>{actions}</div>
      </div>
      <div style={{ display: "flex", flexDirection: "column", gap: 6 }}>{children}</div>
    </div>
  );
}

function SideSplitBar({ producerSide, artistSide }) {
  const prodTotal = Math.max(1, producerSide.reduce((a, c) => a + Number(c.share || 0), 0));
  const artTotal = Math.max(1, artistSide.reduce((a, c) => a + Number(c.share || 0), 0));
  return (
    <div style={{ display: "flex", height: 8, overflow: "hidden", borderRadius: 2, background: "var(--rule-strong)" }}>
      <div style={{ width: "50%", display: "flex" }}>
        {producerSide.map((c, i) => {
          const col = ROLE_COLORS[c.role] || ROLE_COLORS.Producer;
          const pct = (Number(c.share) / prodTotal) * 100;
          return (
            <div key={c.id} title={`${c.name} · ${c.share}%`}
              style={{ width: `${pct}%`, background: col.bg, borderRight: i < producerSide.length - 1 ? "1px solid var(--rule-strong)" : "none", transition: "width 0.35s ease", minWidth: 0 }}
            />
          );
        })}
      </div>
      <div style={{ width: "50%", display: "flex" }}>
        {artistSide.map((c, i) => {
          const col = ROLE_COLORS[c.role] || ROLE_COLORS.Artist;
          const pct = (Number(c.share) / artTotal) * 100;
          return (
            <div key={c.id} title={`${c.name} · ${c.share}%`}
              style={{ width: `${pct}%`, background: col.bg, borderRight: i < artistSide.length - 1 ? "1px solid var(--rule-strong)" : "none", transition: "width 0.35s ease", minWidth: 0 }}
            />
          );
        })}
      </div>
    </div>
  );
}

function PubStreamsInput({ streams, setStreams }) {
  const maxIdx = PUB_STREAM_STOPS.length - 1;
  const stopIndex = pubStreamStopIndex(streams);
  const pct = maxIdx > 0 ? (stopIndex / maxIdx) * 100 : 0;
  const [editing, setEditing] = React.useState(false);
  const [draft, setDraft] = React.useState("");

  const beginEdit = () => {
    setDraft(formatNum(streams));
    setEditing(true);
  };

  const applyDraft = () => {
    const parsed = Math.max(0, Math.round(Number(String(draft).replace(/,/g, "")) || 0));
    setStreams(parsed || PUB_STREAM_STOPS[0]);
    setEditing(false);
  };

  return (
    <div style={{ minWidth: 0 }}>
      <div style={{ display: "flex", alignItems: "baseline", justifyContent: "flex-end", marginBottom: 12 }}>
        {editing ? (
          <input
            autoFocus
            type="text"
            inputMode="numeric"
            value={draft}
            onChange={(e) => setDraft(e.target.value)}
            onBlur={applyDraft}
            onKeyDown={(e) => {
              if (e.key === "Enter") applyDraft();
              if (e.key === "Escape") setEditing(false);
            }}
            style={{
              background: "transparent",
              border: "none",
              borderBottom: "1px solid rgba(212,166,74,0.4)",
              color: "var(--gold)",
              fontFamily: "var(--sans)",
              fontWeight: 800,
              fontSize: "clamp(28px, 3.4vw, 44px)",
              letterSpacing: "-0.025em",
              fontVariantNumeric: "tabular-nums",
              textAlign: "right",
              width: "min(100%, 240px)",
              outline: "none",
              padding: "2px 4px",
            }}
          />
        ) : (
          <button
            type="button"
            onClick={beginEdit}
            title="Click to type a custom stream count"
            style={{
              background: "transparent",
              border: "none",
              borderBottom: "1px solid rgba(212,166,74,0.4)",
              color: "var(--gold)",
              fontFamily: "var(--sans)",
              fontWeight: 800,
              fontSize: "clamp(28px, 3.4vw, 44px)",
              letterSpacing: "-0.025em",
              fontVariantNumeric: "tabular-nums",
              textAlign: "right",
              cursor: "text",
              outline: "none",
              padding: "2px 4px",
              lineHeight: 1.1,
            }}
          >
            {formatNum(streams)}
          </button>
        )}
      </div>

      <input
        type="range"
        min={0}
        max={maxIdx}
        step={1}
        value={stopIndex}
        onChange={(e) => setStreams(PUB_STREAM_STOPS[Number(e.target.value)])}
        className="pu-slider"
        style={{ width: "100%", display: "block", "--pu-pct": `${pct}%` }}
      />

      <div style={{ position: "relative", height: 22, marginTop: 10 }}>
        {PUB_STREAM_STOPS.map((s, i) => {
          const left = maxIdx > 0 ? (i / maxIdx) * 100 : 0;
          return (
            <span
              key={s}
              className="caption"
              style={{
                position: "absolute",
                left: `${left}%`,
                transform: i === 0 ? "none" : i === maxIdx ? "translateX(-100%)" : "translateX(-50%)",
                whiteSpace: "nowrap",
              }}
            >
              {pubStreamChipLabel(s)}
            </span>
          );
        })}
      </div>
    </div>
  );
}

function PubContributorRow({ c, isYou, sideCap, onChange, onRemove, pubPool }) {
  const col = ROLE_COLORS[c.role] || ROLE_COLORS.Producer;
  const roleOptions = isProducerSide(c.role)
    ? ["Producer", "Co-producer"]
    : ["Artist", "Songwriter"];
  const earned = pubPool != null ? (Number(c.share) / 100) * pubPool : null;
  return (
    <div style={{
      display: "grid",
      gridTemplateColumns: "auto 1fr auto auto auto",
      gap: 8,
      alignItems: "center",
      padding: "8px 0",
    }}>
      <select
        value={c.role}
        disabled={isYou}
        onChange={(e) => onChange({ role: e.target.value })}
        style={{
          background: col.bg,
          color: col.fg,
          border: `1px solid ${col.border}`,
          fontFamily: "var(--mono)",
          fontSize: 10,
          letterSpacing: "0.14em",
          padding: "6px 10px",
          appearance: "none",
          cursor: isYou ? "default" : "pointer",
          fontWeight: 600,
          opacity: isYou ? 0.85 : 1,
        }}
      >
        {roleOptions.map((r) => (
          <option key={r} value={r} style={{ background: "var(--bg)", color: "var(--ink)" }}>{r.toUpperCase()}</option>
        ))}
      </select>
      <input
        value={c.name}
        disabled={isYou}
        onChange={(e) => onChange({ name: e.target.value })}
        style={{
          background: "transparent",
          border: "none",
          color: "var(--ink)",
          fontFamily: "var(--sans)",
          fontWeight: 600,
          fontSize: 14,
          padding: "4px 0",
          outline: "none",
          width: "100%",
          letterSpacing: "-0.01em",
        }}
      />
      <div style={{ display: "flex", alignItems: "baseline", gap: 2 }}>
        <input
          type="number"
          value={c.share}
          min="0"
          max={sideCap}
          step="0.5"
          onChange={(e) => onChange({ share: Math.max(0, Math.min(sideCap, Number(e.target.value) || 0)) })}
          style={{
            background: "transparent",
            border: "none",
            borderBottom: `1px solid ${col.fg}`,
            color: col.fg,
            fontFamily: "var(--sans)",
            fontWeight: 700,
            fontSize: 14,
          width: 52,
          textAlign: "right",
          padding: "2px 2px",
          outline: "none",
          fontVariantNumeric: "tabular-nums",
          }}
        />
        <span style={{ color: col.fg, fontFamily: "var(--sans)", fontWeight: 700, fontSize: 14 }}>%</span>
      </div>
      {earned != null && (
        <span style={{ fontFamily: "var(--sans)", fontWeight: 700, fontSize: 13, color: col.fg, fontVariantNumeric: "tabular-nums", whiteSpace: "nowrap", opacity: 0.85 }}>
          ${formatNum(earned)}
        </span>
      )}
      {isYou ? <span style={{ width: 24 }}></span> : (
        <button type="button" onClick={onRemove} style={{ background: "transparent", border: "1px solid var(--rule)", color: "var(--ink-3)", fontFamily: "var(--mono)", fontSize: 12, width: 24, height: 24, cursor: "pointer" }}>×</button>
      )}
    </div>
  );
}

function PubRoyaltySubRows({ streams, writerSharePct }) {
  const { yourPerf, yourMech } = pubRoyaltyBreakdown(streams, writerSharePct);
  return (
    <div style={{ paddingLeft: 10, marginTop: 6, display: "flex", flexDirection: "column", gap: 4 }}>
      <div style={{ display: "grid", gridTemplateColumns: "1fr auto", gap: 12, alignItems: "baseline" }}>
        <span className="caption" style={{ color: "var(--ink-3)" }}>Performance · PRO</span>
        <span style={{ fontFamily: "var(--sans)", fontWeight: 600, fontSize: 12, color: PUB_PERF_COLOR, fontVariantNumeric: "tabular-nums" }}>${formatNum(yourPerf)}</span>
      </div>
      <div style={{ display: "grid", gridTemplateColumns: "1fr auto", gap: 12, alignItems: "baseline" }}>
        <span className="caption" style={{ color: "var(--ink-3)" }}>Mechanical · MLC</span>
        <span style={{ fontFamily: "var(--sans)", fontWeight: 600, fontSize: 12, color: PUB_MECH_COLOR, fontVariantNumeric: "tabular-nums" }}>${formatNum(yourMech)}</span>
      </div>
    </div>
  );
}

function PubWaterfall({ pubPool, streams, producerSide, artistSide }) {
  const renderProducerRow = (c) => {
    const earned = pubPool * (Number(c.share) / 100);
    const col = ROLE_COLORS[c.role] || ROLE_COLORS.Producer;
    const shareLabel = Number(c.share).toFixed(c.share % 1 ? 1 : 0);
    return (
      <div key={c.id} style={{ padding: "6px 0" }}>
        <div style={{ display: "grid", gridTemplateColumns: "1fr auto", gap: 16, alignItems: "baseline" }}>
          <div style={{ fontFamily: "var(--sans)", fontWeight: 600, fontSize: 14, color: "var(--ink-2)" }}>
            {c.name} · <span style={{ color: col.fg }}>{shareLabel}%</span>
          </div>
          <div style={{ fontFamily: "var(--sans)", fontWeight: 700, fontSize: 16, color: col.fg, fontVariantNumeric: "tabular-nums" }}>${formatNum(earned)}</div>
        </div>
        <PubRoyaltySubRows streams={streams} writerSharePct={Number(c.share)} />
      </div>
    );
  };

  const renderArtistRow = (c) => {
    const earned = pubPool * (Number(c.share) / 100);
    const col = ROLE_COLORS[c.role] || ROLE_COLORS.Artist;
    const shareLabel = Number(c.share).toFixed(c.share % 1 ? 1 : 0);
    return (
      <div key={c.id} style={{ display: "grid", gridTemplateColumns: "1fr auto", gap: 16, alignItems: "baseline", padding: "6px 0" }}>
        <div style={{ fontFamily: "var(--sans)", fontWeight: 600, fontSize: 14, color: "var(--ink-2)" }}>
          {c.name} · <span style={{ color: col.fg }}>{shareLabel}%</span>
        </div>
        <div style={{ fontFamily: "var(--sans)", fontWeight: 700, fontSize: 16, color: col.fg, fontVariantNumeric: "tabular-nums" }}>${formatNum(earned)}</div>
      </div>
    );
  };

  const renderSide = (label, color, members, renderRow) => (
    <div style={{ marginTop: 16, paddingTop: 12, borderTop: "1px solid var(--rule)" }}>
      <span className="caption" style={{ color, display: "block", marginBottom: 4 }}>{label}</span>
      {label === "Producer side" && (
        <p className="caption" style={{ margin: "0 0 8px", color: "var(--ink-3)", lineHeight: 1.4 }}>
          Register PRO + MLC to collect · until then it sits at the societies
        </p>
      )}
      {members.map(renderRow)}
    </div>
  );

  return (
    <div style={{ marginTop: 24, paddingTop: 24, borderTop: "1px solid var(--rule-gold)" }}>
      <div style={{ display: "grid", gridTemplateColumns: "1fr auto", gap: 16, alignItems: "baseline", padding: "6px 0" }}>
        <div style={{ fontFamily: "var(--sans)", fontWeight: 600, fontSize: 14, color: "var(--ink)" }}>Pool</div>
        <div style={{ fontFamily: "var(--sans)", fontWeight: 700, fontSize: 22, color: "var(--gold)", fontVariantNumeric: "tabular-nums" }}>${formatNum(pubPool)}</div>
      </div>
      {renderSide("Producer side", "var(--gold)", producerSide, renderProducerRow)}
      {renderSide("Artist side", "#6ab8e8", artistSide, renderArtistRow)}
    </div>
  );
}

function AddBtn({ label, onClick }) {
  return (
    <button onClick={onClick} style={{
      background: "transparent",
      border: "1px dashed var(--rule-strong)",
      color: "var(--ink-2)",
      fontFamily: "var(--mono)",
      fontSize: 11,
      letterSpacing: "0.14em",
      padding: "8px 12px",
      cursor: "pointer",
      textTransform: "uppercase",
      transition: "all 0.18s ease",
    }}
    onMouseEnter={(e) => { e.currentTarget.style.borderColor = "var(--gold)"; e.currentTarget.style.color = "var(--gold)"; }}
    onMouseLeave={(e) => { e.currentTarget.style.borderColor = "var(--rule-strong)"; e.currentTarget.style.color = "var(--ink-2)"; }}
    >
      {label}
    </button>
  );
}

const MASTER_VALUE = "clamp(22px, 2.4vw, 32px)";
const MASTER_HERO = "clamp(32px, 3.6vw, 44px)";
const MASTER_ROW_PAD = "14px 20px";
const MASTER_PANEL_PAD = "18px 20px";
const MASTER_GOLD = "#d4a64a";

const MASTER_TONES = {
  benchmark: { color: "#9a9283", bg: "rgba(0,0,0,0.14)", border: "rgba(255,255,255,0.08)" },
  streams: { color: "#6ab8e8", bg: "rgba(106,184,232,0.07)", border: "rgba(106,184,232,0.2)" },
  advance: { color: "#b889ff", bg: "rgba(184,137,255,0.07)", border: "rgba(184,137,255,0.2)" },
  deal: { color: "#d4a64a", bg: "rgba(212,166,74,0.06)", border: "rgba(212,166,74,0.22)" },
  distro: { color: "#7acf99", bg: "rgba(122,207,153,0.06)", border: "rgba(122,207,153,0.2)" },
  result: { color: "#d4a64a", bg: "rgba(212,166,74,0.08)", border: "rgba(212,166,74,0.32)" },
};

function masterTone(tone) {
  return MASTER_TONES[tone] || MASTER_TONES.deal;
}

function MasterLayoutToggle({ layout, setLayout }) {
  return (
    <div className="master-layout-toggle">
      <span className="caption">Preview</span>
      <GlassSegmented
        items={[
          { id: "a", num: "A", label: "Two columns" },
          { id: "b", num: "B", label: "Step strips" },
        ]}
        active={layout}
        onChange={setLayout}
        ariaLabel="Master preview layout"
      />
    </div>
  );
}

function MasterFieldA({ label, children, last, deal }) {
  return (
    <div className={`master-field-a${last ? " last" : ""}${deal ? " is-deal" : ""}`}>
      <span className="master-field-label">{label}</span>
      {children}
    </div>
  );
}

function MasterFieldB({ label, children, last }) {
  return (
    <div className={`master-field-b${last ? " last" : ""}`}>
      <span className="master-field-label" style={{ color: "var(--gold)" }}>{label}</span>
      {children}
    </div>
  );
}

function MasterStripB({ tone, label, labelExtra, children }) {
  const t = masterTone(tone);
  return (
    <div className="master-strip-b" style={{ "--strip-color": t.color }}>
      <span className="master-field-label" style={{ display: "inline-flex", alignItems: "center", gap: 6 }}>{label}{labelExtra}</span>
      {children}
    </div>
  );
}

function InfoTip({ text }) {
  const [show, setShow] = React.useState(false);
  return (
    <span style={{ position: "relative", display: "inline-block" }}>
      <span
        onMouseEnter={() => setShow(true)}
        onMouseLeave={() => setShow(false)}
        style={{ fontFamily: "var(--mono)", fontSize: 9, letterSpacing: "0.1em", color: "var(--ink-4)", cursor: "help", border: "1px solid var(--ink-4)", padding: "1px 5px", lineHeight: 1.2 }}
      >?</span>
      {show && (
        <div style={{
          position: "absolute",
          left: "calc(100% + 8px)",
          top: "50%",
          transform: "translateY(-50%)",
          zIndex: 200,
          background: "#181410",
          border: "1px solid var(--rule-strong)",
          padding: "10px 14px",
          width: 240,
          fontSize: 13,
          color: "var(--ink-2)",
          fontFamily: "var(--sans)",
          lineHeight: 1.5,
          pointerEvents: "none",
          boxShadow: "0 4px 24px rgba(0,0,0,0.6)",
        }}>
          {text}
        </div>
      )}
    </span>
  );
}


function MasterSection({ tone, label, hint, children }) {
  const t = masterTone(tone);
  return (
    <div style={{ padding: MASTER_ROW_PAD, borderBottom: `1px solid ${t.border}`, background: t.bg, borderLeft: `3px solid ${t.color}` }}>
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "baseline", marginBottom: children ? 8 : 0, gap: 8, flexWrap: "wrap" }}>
        <span style={{ fontFamily: "var(--mono)", fontSize: 9, letterSpacing: "0.16em", color: t.color, textTransform: "uppercase" }}>{label}</span>
        {hint && <span className="caption" style={{ fontSize: 9, color: "var(--ink-3)", lineHeight: 1.4 }}>{hint}</span>}
      </div>
      {children}
    </div>
  );
}

function MasterInputBlock({ tone, label, children }) {
  const t = masterTone(tone);
  return (
    <div style={{ padding: "14px 16px", background: t.bg, border: `1px solid ${t.border}`, borderLeft: `3px solid ${t.color}` }}>
      <span style={{ display: "block", fontFamily: "var(--mono)", fontSize: 9, letterSpacing: "0.16em", color: t.color, textTransform: "uppercase", marginBottom: 10 }}>{label}</span>
      {children}
    </div>
  );
}

function MasterSliderControl({ tone, hint, valueDisplay, unit, min, max, step, value, onChange, pct, sliderClass, ticks, tickLabels, layout = "b" }) {
  const t = masterTone(tone);
  const accent = layout === "a" ? MASTER_GOLD : t.color;
  const sliderToneClass = layout === "a"
    ? "tone-custom"
    : sliderClass || (tone === "streams" ? "role-artist" : tone === "advance" ? "role-songwriter" : "tone-custom");
  const valueClass = layout === "a" ? "master-value-a" : "";
  const valueStyle = layout === "a" ? undefined : { fontFamily: "var(--sans)", fontWeight: 800, fontSize: MASTER_VALUE, color: accent, fontVariantNumeric: "tabular-nums", letterSpacing: "-0.025em", lineHeight: 1 };

  return (
    <>
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "baseline", marginBottom: 8, flexWrap: "wrap", gap: 6 }}>
        {hint && <span className="caption" style={{ color: "var(--ink-2)", fontSize: 10 }}>{hint}</span>}
        <div style={{ display: "flex", alignItems: "baseline", gap: 4, marginLeft: hint ? "auto" : 0, width: hint ? undefined : "100%", justifyContent: hint ? undefined : "flex-end" }}>
          {layout === "a" ? (
            <span className={valueClass}>{valueDisplay}{unit ? ` ${unit}` : ""}</span>
          ) : (
            <>
              <span style={valueStyle}>{valueDisplay}</span>
              {unit && <span style={{ fontFamily: "var(--sans)", fontWeight: 600, fontSize: 14, color: "var(--ink-2)" }}>{unit}</span>}
            </>
          )}
        </div>
      </div>
      <input
        type="range"
        min={min}
        max={max}
        step={step}
        value={value}
        onChange={onChange}
        className={`pu-slider ${sliderToneClass}`}
        style={{ width: "100%", "--pu-pct": `${pct}%`, "--slider-color": accent }}
      />
      {tickLabels ? (
        <div style={{ position: "relative", height: 14, marginTop: 4 }}>
          {tickLabels.map(({ left, label, active }) => (
            <span key={label} className="caption" style={{
              position: "absolute",
              left: `${left}%`,
              transform: left === 0 ? "none" : left === 100 ? "translateX(-100%)" : "translateX(-50%)",
              whiteSpace: "nowrap",
              fontSize: 9,
              color: active ? accent : "var(--ink-3)",
              fontWeight: active ? 600 : 400,
            }}>{label}</span>
          ))}
        </div>
      ) : ticks ? (
        <div style={{ display: "flex", justifyContent: "space-between", marginTop: 6, fontFamily: "var(--mono)", fontSize: 9, letterSpacing: "0.12em", color: "var(--ink-3)" }}>
          {ticks.map((tick) => <span key={tick}>{tick}</span>)}
        </div>
      ) : null}
    </>
  );
}

function MasterYouKeepPanel({ headline, subline, rows, footnote, layout = "b", flat }) {
  const t = masterTone("result");
  return (
    <div style={{ position: flat ? "static" : "sticky", top: flat ? undefined : 24, padding: flat ? MASTER_PANEL_PAD : layout === "a" ? "16px 20px" : MASTER_PANEL_PAD, alignSelf: "start", minWidth: 0, background: flat ? t.bg : undefined }}>
      <div style={{ padding: flat ? 0 : layout === "a" ? "20px" : "18px 20px", background: flat ? "transparent" : t.bg, border: flat ? "none" : `1px solid ${t.border}`, borderTop: flat ? "none" : `2px solid ${t.color}` }}>
        <span className="caption" style={{ color: t.color }}>You keep</span>
        {layout === "a" ? (
          <div className="master-hero-a" style={{ color: t.color, marginTop: 6 }}>{headline}</div>
        ) : (
          <div style={{ fontFamily: "var(--sans)", fontWeight: 800, fontSize: MASTER_HERO, letterSpacing: "-0.035em", color: t.color, lineHeight: 1, fontVariantNumeric: "tabular-nums", marginTop: 6 }}>
            {headline}
          </div>
        )}
        {subline && <p className="caption" style={{ marginTop: 8, color: "var(--ink-3)", fontSize: 10, lineHeight: 1.45 }}>{subline}</p>}
        {rows?.length > 0 && (
          <div style={{ marginTop: 16, paddingTop: 14, borderTop: `1px solid ${t.border}` }}>
            {rows.map((row, i) => (
              <div key={i} style={{ display: "grid", gridTemplateColumns: "1fr auto", gap: 10, padding: "7px 0", borderBottom: i < rows.length - 1 ? "1px dashed var(--rule)" : "none" }}>
                <span style={{ fontFamily: "var(--sans)", fontWeight: row.emphasis ? 600 : 500, fontSize: 13, color: row.emphasis ? "var(--ink-2)" : "var(--ink-3)" }}>{row.label}</span>
                <span style={{ fontFamily: "var(--sans)", fontWeight: row.emphasis ? 700 : 600, fontSize: row.emphasis ? 15 : 14, color: row.color || t.color, fontVariantNumeric: "tabular-nums" }}>{row.value}</span>
              </div>
            ))}
          </div>
        )}
        {footnote && <p className="caption" style={{ marginTop: 12, color: "var(--ink-3)", lineHeight: 1.45, fontSize: 10 }}>{footnote}</p>}
      </div>
    </div>
  );
}

const TERRITORY_CERTS = {
  us: {
    label: "US", flag: "🇺🇸", body: "RIAA", type: "Single",
    tiers: [
      { name: "Gold",     color: "#d4a64a", units: 500_000,    streams: 75_000_000   },
      { name: "Platinum", color: "#c0c8d8", units: 1_000_000,  streams: 150_000_000  },
      { name: "Diamond",  color: "#a8d4e8", units: 10_000_000, streams: 1_500_000_000 },
    ],
  },
  uk: {
    label: "UK", flag: "🇬🇧", body: "BPI", type: "Single",
    tiers: [
      { name: "Silver",      color: "#b0b8c8", units: 200_000,   streams: 20_000_000  },
      { name: "Gold",        color: "#d4a64a", units: 400_000,   streams: 40_000_000  },
      { name: "Platinum",    color: "#c0c8d8", units: 600_000,   streams: 60_000_000  },
      { name: "3× Platinum", color: "#e8c878", units: 1_800_000, streams: 180_000_000 },
    ],
  },
  global: {
    label: "Global", flag: "🌍", body: "IFPI", type: "Single",
    tiers: [
      { name: "Platinum",       color: "#c0c8d8", units: 1_000_000,  streams: 150_000_000  },
      { name: "Multi-Platinum", color: "#e8c878", units: 3_000_000,  streams: 450_000_000  },
      { name: "Diamond",        color: "#a8d4e8", units: 10_000_000, streams: 1_500_000_000 },
    ],
  },
};

const BASE_STREAM_STOPS = [10_000, 100_000, 1_000_000, 10_000_000];
const MAX_CUSTOM_STREAMS = 5_000_000_000;

function StreamScrubber(props) {
  return <PubNoiseSignalDeck variant="streams" {...props} />;
}

function MasterStreamsSlider({ streams, setStreams, layout = "b" }) {
  const maxIdx = PUB_STREAM_STOPS.length - 1;
  const stopIndex = pubStreamStopIndex(streams);
  const pct = maxIdx > 0 ? (stopIndex / maxIdx) * 100 : 0;
  const tickLabels = PUB_STREAM_STOPS.map((s, i) => ({
    left: maxIdx > 0 ? (i / maxIdx) * 100 : 0,
    label: pubStreamChipLabel(s),
    active: stopIndex === i,
  }));

  return (
    <MasterSliderControl
      layout={layout}
      tone="streams"
      hint={layout === "b" ? "If this song does" : undefined}
      valueDisplay={formatNum(streams)}
      min={0}
      max={maxIdx}
      step={1}
      value={stopIndex}
      onChange={(e) => setStreams(PUB_STREAM_STOPS[Number(e.target.value)])}
      pct={pct}
      tickLabels={tickLabels}
    />
  );
}

function MasterStreamsRow({ streams, setStreams, layout = "b" }) {
  if (layout === "a") return null;
  return (
    <MasterStripB tone="streams" label="Streams">
      <MasterStreamsSlider streams={streams} setStreams={setStreams} layout="b" />
    </MasterStripB>
  );
}

const MASTER_BENCH = {
  independent: { usualLo: 0, usualHi: 10, targetLo: 25, targetHi: 50, usualRange: "Ownership %", targetRange: "Distro split", max: 50 },
  signed: { usualLo: 0, usualHi: 2, targetLo: 3, targetHi: 5, usualRange: "Master points", targetRange: "Points on master", max: 6 },
};

function MasterUsualBar({ structure }) {
  const b = MASTER_BENCH[structure];
  return (
    <BarBlock
      compact
      tone="noise"
      label="What producers usually settle for"
      rangeLabel={b.usualRange}
      value={`${b.usualLo}–${b.usualHi}${structure === "independent" ? "%" : " pts"}`}
      tag="USUAL"
      pct={(b.usualHi / b.max) * 100}
    />
  );
}

function MasterTargetBar({ structure }) {
  const b = MASTER_BENCH[structure];
  return (
    <BarBlock
      compact
      tone="gold"
      label="What you should target"
      rangeLabel={b.targetRange}
      value={`${b.targetLo}–${b.targetHi}${structure === "independent" ? "%" : " pts"}`}
      tag="STANDARD"
      pct={(b.targetHi / b.max) * 100}
      secondaryPct={(b.targetLo / b.max) * 100}
    />
  );
}

function MasterBenchmarkPanel({ structure }) {
  return (
    <div className="calc-benchmark-bars master-benchmark-bars">
      <MasterUsualBar structure={structure} />
      <MasterTargetBar structure={structure} />
    </div>
  );
}

function MasterCalc({ streams, setStreams, territory, onTerritoryChange, platform, onPlatformChange, distro, setDistro, structure, setStructure }) {
  return (
    <div>
      <div style={{ display: "flex", flexDirection: "column", border: "1px solid var(--rule-strong)" }}>
        <StepStrip
          step="01"
          active={structure === "independent"}
          onClick={() => setStructure("independent")}
          label="Independent artist"
          sub="Distro splits · ownership %"
          tag="OWNERSHIP %"
          accent="#d4a64a"
        />
        <StepStrip
          step="02"
          active={structure === "signed"}
          onClick={() => setStructure("signed")}
          label="Signed artist"
          sub="Label deal · points + recoup"
          tag="POINTS"
          accent="#b889ff"
        />
      </div>

      {structure === "independent"
        ? (
          <IndependentMaster
            layout="b"
            streams={streams}
            setStreams={setStreams}
            territory={territory}
            onTerritoryChange={onTerritoryChange}
            platform={platform}
            onPlatformChange={onPlatformChange}
            distro={distro}
            setDistro={setDistro}
          />
        )
        : (
          <SignedMaster
            layout="b"
            streams={streams}
            setStreams={setStreams}
            territory={territory}
            onTerritoryChange={onTerritoryChange}
            platform={platform}
            onPlatformChange={onPlatformChange}
          />
        )
      }
    </div>
  );
}

function StructBtn({ active, onClick, label, sub, right, last, accent = "#d4a64a", activeBg = "rgba(212,166,74,0.10)" }) {
  return (
    <button onClick={onClick} style={{
      flex: 1,
      background: active ? activeBg : "transparent",
      border: "none",
      borderRight: last ? "none" : "1px solid var(--rule-strong)",
      cursor: "pointer",
      padding: "16px 20px",
      textAlign: "left",
      color: active ? "var(--ink)" : "var(--ink-3)",
      position: "relative",
      transition: "all 0.18s ease",
    }}>
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "baseline", marginBottom: 6 }}>
        <span style={{ fontFamily: "var(--mono)", fontSize: 10, letterSpacing: "0.14em", color: active ? accent : "var(--ink-3)" }}>{active ? "ACTIVE" : "SETUP"}</span>
        <span style={{ fontFamily: "var(--mono)", fontSize: 9, letterSpacing: "0.14em", color: active ? accent : "var(--ink-3)" }}>{right}</span>
      </div>
      <div style={{ fontFamily: "var(--sans)", fontWeight: active ? 800 : 600, fontSize: 17, letterSpacing: "-0.015em" }}>{label}</div>
      <div className="caption" style={{ marginTop: 4 }}>{sub}</div>
      {active && <span style={{ position: "absolute", left: 0, right: 0, bottom: 0, height: 2, background: accent }}></span>}
    </button>
  );
}

function StepStrip({ step, active, onClick, label, sub, tag, accent = "#d4a64a" }) {
  return (
    <button type="button" onClick={onClick} style={{
      display: "flex",
      alignItems: "center",
      gap: 20,
      width: "100%",
      background: active ? `rgba(${accent === "#b889ff" ? "184,137,255" : "212,166,74"},0.06)` : "transparent",
      border: "none",
      borderBottom: "1px solid var(--rule-strong)",
      borderLeft: `3px solid ${active ? accent : "transparent"}`,
      cursor: "pointer",
      padding: "16px 22px",
      textAlign: "left",
      color: active ? "var(--ink)" : "var(--ink-3)",
      transition: "all 0.18s ease",
    }}>
      <span style={{ fontFamily: "var(--mono)", fontSize: 11, letterSpacing: "0.18em", color: active ? accent : "var(--ink-4)", minWidth: 24, fontWeight: 600 }}>
        {step}
      </span>
      <div style={{ flex: 1 }}>
        <div style={{ fontFamily: "var(--sans)", fontWeight: active ? 800 : 600, fontSize: 16, letterSpacing: "-0.015em" }}>{label}</div>
        <div className="caption" style={{ marginTop: 2 }}>{sub}</div>
      </div>
      <span style={{ fontFamily: "var(--mono)", fontSize: 9, letterSpacing: "0.14em", color: active ? accent : "var(--ink-4)", textAlign: "right" }}>{tag}</span>
    </button>
  );
}

// =========== INDEPENDENT ARTIST FLOW ===========

function IndependentMaster({ layout = "a", streams, setStreams, territory = "us", onTerritoryChange, platform = "spotify", onPlatformChange, distro = "distrokid", setDistro }) {
  const [ownership, setOwnership] = React.useState(25);
  const [tierId, setTierId] = React.useState(null);

  const distroObj = window.getDistroProfile(distro);
  const dspObj = window.getDspPlatform?.(platform) || (window.DSP_PLATFORMS || [])[0];
  const streamTypeMeta = window.getStreamTypeMeta?.(platform) || {};
  const isNonInteractive = window.isNonInteractiveStream?.(platform);
  const MASTER_PER_1K = masterPer1kForTerritoryPlatform(territory, platform);
  const grossMasterPool = (streams / 1000) * MASTER_PER_1K;

  React.useEffect(() => {
    const p = window.getDistroProfile(distro);
    if (p?.commission?.tiers?.length) {
      setTierId(p.commission.tiers[0].id);
    } else {
      setTierId(null);
    }
  }, [distro]);

  const commissionPct = isNonInteractive ? 0 : window.getDistroCommissionPct(distroObj, tierId);
  const commissionAmount = grossMasterPool * (commissionPct / 100);
  const netMasterPool = grossMasterPool - commissionAmount;
  const yourCut = netMasterPool * (ownership / 100);
  const artistCut = netMasterPool - yourCut;

  const poolLabel = isNonInteractive ? "Digital perf pool (SoundExchange)" : "Master pool (gross)";
  const rows = [
    { label: poolLabel, value: `$${formatNum(grossMasterPool)}`, emphasis: true },
  ];
  if (commissionPct > 0) {
    rows.push({ label: `Distro commission (−${commissionPct}%)`, value: `−$${formatNum(commissionAmount)}`, color: "#e88a7d" });
    rows.push({ label: "Net pool", value: `$${formatNum(netMasterPool)}`, emphasis: true });
  }
  rows.push(
    { label: `Artist · ${100 - ownership}%`, value: `$${formatNum(artistCut)}`, color: "var(--ink-2)" },
    { label: `You · ${ownership}%`, value: `$${formatNum(yourCut)}` },
  );

  let footnote = isNonInteractive
    ? "Non-interactive streams pay master-side via SoundExchange, not through a distributor. Register as sound recording owner."
    : distroObj.splits
      ? "No recoupment on indie splits. Paid direct each cycle if you're on their distro."
      : "No native splits on this distro. Fix the chain before release or you'll chase payments manually.";
  if (commissionPct > 0) {
    footnote += " Commission comes out before your split on most distros, already deducted above.";
  }

  const youKeep = {
    headline: `$${formatNum(yourCut)}`,
    subline: `${ownership}% ownership · ${formatNum(streams)} streams · ${dspObj.label} · ${streamTypeMeta.label || "Interactive"} · ${TERRITORY_CERTS[territory]?.label || territory} · ~$${MASTER_PER_1K.toFixed(2)}/1K`,
    rows,
    footnote,
  };

  if (layout === "a") {
    return (
      <div style={{ marginTop: 16 }}>
        <div className="master-card master-layout-a">
          <div className="tool-split" style={{ gap: 0 }}>
            <div className="master-layout-a-inputs">
              <MasterFieldA label="Benchmark">
                <MasterBenchmarkPanel structure="independent" />
                <p className="master-bench-note">Indie artists pay via their distributor. Get on splits before release.</p>
              </MasterFieldA>
              <MasterFieldA label="Streams">
                <MasterStreamsSlider streams={streams} setStreams={setStreams} layout="a" />
              </MasterFieldA>
              <MasterFieldA label="Your ownership" deal>
                <MasterSliderControl
                  layout="a"
                  tone="deal"
                  hint="Target · 25–50%"
                  valueDisplay={ownership}
                  unit="%"
                  min={0}
                  max={50}
                  step={1}
                  value={ownership}
                  onChange={(e) => setOwnership(Number(e.target.value))}
                  pct={(ownership / 50) * 100}
                  ticks={["0%", "25%", "50%"]}
                />
              </MasterFieldA>
              <MasterFieldA label={isNonInteractive ? "Payout path" : "Their distributor"} last>
                {isNonInteractive ? (
                  <p className="master-bench-note" style={{ margin: 0 }}>
                    SoundExchange · digital performance royalties · no distributor in this path
                  </p>
                ) : (
                  <DistroPickerBlock
                    distro={distro}
                    setDistro={setDistro}
                    profile={distroObj}
                    tierId={tierId}
                    setTierId={setTierId}
                    commissionPct={commissionPct}
                  />
                )}
              </MasterFieldA>
            </div>
            <MasterYouKeepPanel layout="a" {...youKeep} />
          </div>
        </div>
        <DistroDiagram distro={distroObj} ownership={ownership} artistShare={100 - ownership} />
        <SplitsHowTo distro={distroObj} />
      </div>
    );
  }

  const [showDiagram, setShowDiagram] = React.useState(false);
  const [showHowTo, setShowHowTo] = React.useState(false);

  return (
    <div style={{ marginTop: 16 }}>
      <div className="master-card master-layout-b">
        <PubNoiseSignalDeck
          variant="streams"
          streams={streams}
          setStreams={setStreams}
          territory={territory}
          onTerritoryChange={onTerritoryChange}
          platform={platform}
          onPlatformChange={onPlatformChange}
          showDspPicker
          className="pub-ns-deck--in-master"
        />
        <div style={{ display: "flex", flexWrap: "wrap", borderTop: "1px solid var(--rule-strong)", borderBottom: "1px solid var(--rule-strong)" }}>
          <div style={{ flex: "1 1 200px", padding: "14px 20px", borderRight: "1px solid var(--rule-strong)" }}>
            <span className="master-field-label">Your ownership</span>
            <MasterSliderControl
              layout="b"
              tone="deal"
              hint="Target · 25–50%"
              valueDisplay={ownership}
              unit="%"
              min={0}
              max={50}
              step={1}
              value={ownership}
              onChange={(e) => setOwnership(Number(e.target.value))}
              pct={(ownership / 50) * 100}
              ticks={["0%", "25%", "50%"]}
            />
          </div>
          <div style={{ flex: "1 1 200px", padding: "14px 20px" }}>
            <span className="master-field-label">{isNonInteractive ? "Payout path" : "Their distributor"}</span>
            {isNonInteractive ? (
              <p className="caption" style={{ marginTop: 8, lineHeight: 1.5, color: "var(--ink-2)" }}>
                SoundExchange · digital performance · no distributor
              </p>
            ) : (
              <DistroPickerBlock
                distro={distro}
                setDistro={setDistro}
                profile={distroObj}
                tierId={tierId}
                setTierId={setTierId}
                commissionPct={commissionPct}
                selectedColor={MASTER_TONES.distro.color}
                showSheet={false}
              />
            )}
          </div>
        </div>
        {!isNonInteractive && (
          <div style={{ padding: "14px 20px", borderBottom: "1px solid var(--rule-strong)" }}>
            <DistroProducerSheet profile={distroObj} />
          </div>
        )}
        <div style={{ borderTop: "1px solid var(--rule-strong)" }}>
          <MasterYouKeepPanel layout="b" flat {...youKeep} />
        </div>
        {/* Distro chain: collapsible */}
        <div style={{ borderTop: "1px solid var(--rule-strong)" }}>
          <button type="button" onClick={() => setShowDiagram(v => !v)}
            style={{ display: "flex", justifyContent: "space-between", alignItems: "center", width: "100%", background: "transparent", border: "none", padding: "12px 20px", cursor: "pointer" }}>
            <span style={{ fontFamily: "var(--mono)", fontSize: 10, letterSpacing: "0.12em", color: "var(--ink-3)" }}>
              HOW MONEY REACHES YOU - {showDiagram ? "hide" : "see the distro chain"}
            </span>
            <span style={{ fontFamily: "var(--mono)", fontSize: 10, color: "var(--ink-4)" }}>{showDiagram ? "▲" : "▼"}</span>
          </button>
          {showDiagram && <DistroDiagram distro={distroObj} ownership={ownership} artistShare={100 - ownership} embedded />}
        </div>
        {/* How to get on splits: collapsible */}
        <div style={{ borderTop: "1px solid var(--rule-strong)" }}>
          <button type="button" onClick={() => setShowHowTo(v => !v)}
            style={{ display: "flex", justifyContent: "space-between", alignItems: "center", width: "100%", background: "transparent", border: "none", padding: "12px 20px", cursor: "pointer" }}>
            <span style={{ fontFamily: "var(--mono)", fontSize: 10, letterSpacing: "0.12em", color: "var(--ink-3)" }}>
              GETTING ON THE SPLITS - {showHowTo ? "hide" : "how to set this up"}
            </span>
            <span style={{ fontFamily: "var(--mono)", fontSize: 10, color: "var(--ink-4)" }}>{showHowTo ? "▲" : "▼"}</span>
          </button>
          {showHowTo && (
            <div style={{ padding: "0 20px 20px" }}>
              <SplitsHowTo distro={distroObj} inline />
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

function DistroDiagram({ distro, ownership, artistShare, embedded }) {
  return (
    <div style={embedded ? { padding: "16px 20px" } : { marginTop: 16, padding: "24px 26px", border: "1px solid var(--rule-strong)", background: "rgba(255,255,255,0.015)" }}>
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "baseline", marginBottom: 16, flexWrap: "wrap", gap: 8 }}>
        <span className="caption">How money reaches you (independent setup)</span>
        <span className="caption">Fig. 03 · The distro chain</span>
      </div>

      <svg viewBox="0 0 800 280" style={{ width: "100%", height: "auto", display: "block" }}>
        <defs>
          <radialGradient id="distroDot">
            <stop offset="0%" stopColor="#fff3cf" />
            <stop offset="40%" stopColor="#d4a64a" />
            <stop offset="100%" stopColor="rgba(212,166,74,0)" />
          </radialGradient>
        </defs>

        {/* DSPs (top) */}
        <DistroNode x={400} y={20} w={300} h={50} label="Streaming Platforms" sub="Spotify · Apple · SoundCloud · 10 more" />

        {/* Distributor (mid) */}
        <DistroNode x={400} y={110} w={260} h={56} label={distro.label} sub={distro.splits ? (distro.commission?.default > 0 ? `Splits · ${distro.commission.raw} commission` : "Splits configured → paid direct") : "No native splits → manual payout"} accent />

        {/* Lines */}
        <path d="M 400 70 L 400 110" stroke="#d4a64a" strokeWidth="1.5" strokeDasharray="3 3" opacity="0.5" />

        {/* Split, artist vs you */}
        <path d={`M ${400} ${166} L ${400} ${200} L ${220} ${200} L ${220} ${220}`} stroke="#cfc7b8" strokeWidth="1.5" strokeDasharray="3 3" opacity="0.45" />
        <path d={`M ${400} ${166} L ${400} ${200} L ${580} ${200} L ${580} ${220}`} stroke="#d4a64a" strokeWidth="1.5" strokeDasharray="3 3" opacity={0.7} />

        {/* Particles */}
        <circle r="3.5" fill="url(#distroDot)">
          <animateMotion dur="3s" repeatCount="indefinite" path={`M 400 70 L 400 200 L 220 200 L 220 220`} />
        </circle>
        <circle r="3.5" fill="url(#distroDot)">
          <animateMotion dur="3s" repeatCount="indefinite" begin="1.2s" path={`M 400 70 L 400 200 L 580 200 L 580 220`} />
        </circle>

        {/* Artist node */}
        <DistroNode x={220} y={220} w={220} h={50} label="The Artist" sub={`${artistShare}% of master`} muted />

        {/* You node */}
        <DistroNode x={580} y={220} w={220} h={50} label="You (Producer)" sub={`${ownership}% · paid each cycle`} accent />
      </svg>

      <p className="body-text" style={{ fontSize: 13, lineHeight: 1.55, marginTop: 14, color: "var(--ink-2)" }}>
        The artist's distributor account is what pays out streaming revenue. <span className="gold">You only collect master royalties if you're configured as a split recipient inside that account.</span> Otherwise the full pool lands in the artist's bank, and you're chasing it.
      </p>
    </div>
  );
}

function DistroNode({ x, y, w, h, label, sub, accent, muted }) {
  const fill = accent ? "rgba(212,166,74,0.10)" : muted ? "rgba(255,255,255,0.025)" : "#181613";
  const border = accent ? "#d4a64a" : muted ? "rgba(255,255,255,0.15)" : "rgba(255,255,255,0.18)";
  const labelColor = accent ? "#d4a64a" : "#f5f1e8";
  return (
    <g>
      <rect x={x - w/2} y={y} width={w} height={h} fill={fill} stroke={border} strokeWidth="1" />
      <text x={x} y={y + 22} textAnchor="middle" fontFamily="var(--sans)" fontWeight="700" fontSize="14" letterSpacing="-0.01em" fill={labelColor}>{label}</text>
      <text x={x} y={y + 40} textAnchor="middle" fontFamily="var(--mono)" fontSize="9" letterSpacing="1.3" fill="#8c8576">{sub.toUpperCase()}</text>
    </g>
  );
}

function SplitsHowTo({ distro }) {
  const steps = distro.splits ? [
    `The artist opens their ${distro.label} dashboard and finds the song.`,
    "Adds you as a split recipient, your email, your ownership %.",
    "You accept the split request in your inbox / create a free account.",
    "From this release forward, your share is paid direct to you each cycle. No invoicing, no chasing.",
  ] : [
    "Splits aren't supported natively here, you have two options.",
    "Option A: insist the artist switch to a distro with splits before release (Stem, DistroKid, UnitedMasters).",
    "Option B: get a signed master share / co-ownership agreement and invoice them quarterly based on their statements.",
    "Option A is better in almost every case. Manual payouts are where producers go unpaid.",
  ];
  return (
    <div style={{ marginTop: 22, padding: "24px 26px", border: "1px solid var(--rule-strong)", background: "rgba(255,255,255,0.015)" }}>
      <span className="caption">Getting on the splits</span>
      <div className="pull-serif" style={{ fontSize: 19, lineHeight: 1.3, marginTop: 4, color: "var(--ink)" }}>
        Master ownership without distro splits = <span className="gold">unpaid royalties.</span> Fix the chain before the song goes up.
      </div>
      <ol style={{ listStyle: "none", padding: 0, margin: "18px 0 0", display: "flex", flexDirection: "column", gap: 10 }}>
        {steps.map((s, i) => (
          <li key={i} style={{ display: "flex", gap: 14, fontSize: 15, lineHeight: 1.45, color: "var(--ink-2)" }}>
            <span style={{ fontFamily: "var(--mono)", color: "var(--gold)", minWidth: 24, fontWeight: 600 }}>{String(i + 1).padStart(2, "0")}</span>
            <span>{s}</span>
          </li>
        ))}
      </ol>
    </div>
  );
}

// =========== SIGNED ARTIST FLOW (existing points logic) ===========

const RECOUP_LINE_ITEMS = [
  { id: "recording",   label: "Recording costs (studio, mix, master)", amount: 30_000, note: "100% recoupable. The baseline in every deal" },
  { id: "artist_adv",  label: "Artist advance (their signing bonus)",   amount: 50_000, note: "100% recoupable, charged to artist's royalty account" },
  { id: "video",       label: "Music video (50% recoupable)",           amount: 12_500, note: "Label standard: only 50% of video budget charged back" },
  { id: "tour",        label: "Tour support",                           amount: 10_000, note: "100% recoupable - label advance to cover touring losses" },
  { id: "indie_promo", label: "Independent radio promotion",            amount: 10_000, note: "100% recoupable, can run $50K–$150K per single" },
  { id: "net_profit",  label: "Marketing / advertising (360 deal only)",amount: 50_000, note: "Only recoupable in net-profit/360 deals, NOT standard" },
];

function SignedMaster({ layout = "a", streams, setStreams, territory = "us", onTerritoryChange, platform = "spotify", onPlatformChange }) {
  const [points, setPoints] = React.useState(4);
  const [advance, setAdvance] = React.useState(10_000);
  const [recoupToggles, setRecoupToggles] = React.useState({});
  const [showRecoupWall, setShowRecoupWall] = React.useState(false);

  const dspObj = window.getDspPlatform?.(platform) || (window.DSP_PLATFORMS || [])[0];
  const streamTypeMeta = window.getStreamTypeMeta?.(platform) || {};
  const isNonInteractive = window.isNonInteractiveStream?.(platform);
  const MASTER_PER_1K = masterPer1kForTerritoryPlatform(territory, platform);
  const grossMasterPool = (streams / 1000) * MASTER_PER_1K;
  const yourGross = grossMasterPool * (points / 100);
  const labelCut = grossMasterPool - yourGross;
  const artistRoyaltyFromLabel = labelCut * 0.15;

  const extraRecoup = RECOUP_LINE_ITEMS
    .filter((i) => recoupToggles[i.id])
    .reduce((sum, i) => sum + i.amount, 0);
  const totalDebt = advance + extraRecoup;
  const totalRemainingDebt = Math.max(0, totalDebt - yourGross);
  const totalRecoupedPct = totalDebt > 0 ? Math.min(100, (yourGross / totalDebt) * 100) : 100;
  const totalFree = totalDebt === 0 || totalRecoupedPct >= 100;
  const ratePerStream = streams > 0 ? yourGross / streams : 0;
  const streamsToBreakEven = ratePerStream > 0 ? Math.ceil(totalDebt / ratePerStream) : 0;

  const remainingDebt = Math.max(0, advance - yourGross);
  const yourNet = Math.max(0, yourGross - advance);
  const recoupedPct = advance > 0 ? Math.min(100, (yourGross / advance) * 100) : 100;

  const youKeep = {
    headline: `$${formatNum(advance > 0 ? yourNet : yourGross)}`,
    subline: `${points} pts · ${formatNum(streams)} streams · ${dspObj.label} · ${streamTypeMeta.label || "Interactive"} · ${TERRITORY_CERTS[territory]?.label || territory} · ~$${MASTER_PER_1K.toFixed(2)}/1K`,
    rows: [
      { label: isNonInteractive ? "Digital perf pool (SoundExchange)" : "Master pool", value: `$${formatNum(grossMasterPool)}`, emphasis: true },
      { label: `Your ${points} pts (gross)`, value: `$${formatNum(yourGross)}` },
      {
        label: advance > 0 ? (remainingDebt > 0 ? "After recoup" : "Fully recouped") : "Take-home",
        value: `$${formatNum(advance > 0 ? yourNet : yourGross)}`,
        color: remainingDebt > 0 ? "var(--ink-2)" : "#8acaa0",
      },
    ],
    footnote: advance > 0 && remainingDebt > 0 ? `Still ${formatMoney(remainingDebt)} to recoup from your master share.` : undefined,
  };

  const pointsBlock = (
    <>
      <MasterSliderControl
        layout={layout}
        tone="deal"
        hint="Target · 3–5 pts"
        valueDisplay={points}
        unit="pts"
        min={0}
        max={10}
        step={0.5}
        value={points}
        onChange={(e) => setPoints(Number(e.target.value))}
        pct={(points / 10) * 100}
        ticks={["0", "5", "10 pts"]}
      />
    </>
  );

  if (layout === "a") {
    return (
      <div style={{ marginTop: 16 }}>
        <div className="master-card master-layout-a">
          <div className="tool-split" style={{ gap: 0 }}>
            <div className="master-layout-a-inputs">
              <MasterFieldA label="Benchmark">
                <MasterBenchmarkPanel structure="signed" />
                <p className="master-bench-note">Signed deals pay points from the label's master pool. Fee is separate.</p>
              </MasterFieldA>
              <MasterFieldA label="Streams">
                <MasterStreamsSlider streams={streams} setStreams={setStreams} layout="a" />
              </MasterFieldA>
              <MasterFieldA label="Advance / recoup">
                <RecoupmentBlock
                  advance={advance}
                  setAdvance={setAdvance}
                  yourGross={yourGross}
                  remainingDebt={remainingDebt}
                  recoupedPct={recoupedPct}
                  slim
                  layout="a"
                />
              </MasterFieldA>
              <MasterFieldA label="Your points" deal last>
                {pointsBlock}
              </MasterFieldA>
            </div>
            <MasterYouKeepPanel layout="a" {...youKeep} />
          </div>
        </div>
      </div>
    );
  }

  return (
    <div style={{ marginTop: 16 }}>
      <div className="master-card master-layout-b">
        <PubNoiseSignalDeck
          variant="streams"
          streams={streams}
          setStreams={setStreams}
          territory={territory}
          onTerritoryChange={onTerritoryChange}
          platform={platform}
          onPlatformChange={onPlatformChange}
          showDspPicker
          className="pub-ns-deck--in-master"
        />
        <div style={{ display: "flex", flexWrap: "wrap", borderTop: "1px solid var(--rule-strong)", borderBottom: "1px solid var(--rule-strong)" }}>
          <div style={{ flex: "1 1 200px", padding: "14px 20px", borderRight: "1px solid var(--rule-strong)" }}>
            <span className="master-field-label" style={{ display: "inline-flex", alignItems: "center", gap: 6 }}>
              Your points
              <InfoTip text="1 point = 1% of the master royalty. 4 points on a 10M-stream record = 4% of every dollar that master earns, forever, on every play of that recording." />
            </span>
            {pointsBlock}
          </div>
          <div style={{ flex: "1 1 200px", padding: "14px 20px" }}>
            <span className="master-field-label">Advance / recoup</span>
            <RecoupmentBlock
              advance={advance}
              setAdvance={setAdvance}
              yourGross={yourGross}
              remainingDebt={remainingDebt}
              recoupedPct={recoupedPct}
              compact
              layout="b"
            />
          </div>
        </div>
        <div style={{ borderTop: "1px solid var(--rule-strong)" }}>
          <MasterYouKeepPanel layout="b" flat {...youKeep} />
        </div>
        {/* Deal receipt */}
        <div style={{ borderTop: "1px solid var(--rule-strong)", borderBottom: "1px solid var(--rule-strong)", padding: "18px 20px" }}>
          {/* Always-visible: master split */}
          <div style={{ display: "flex", justifyContent: "space-between", alignItems: "baseline", marginBottom: 12 }}>
            <span className="caption">Deal receipt · who takes what</span>
            <span className="caption" style={{ color: "var(--ink-4)" }}>Example only. Every deal is different</span>
          </div>

          <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(min(100%, 130px), 1fr))", gap: 8, marginBottom: 14 }}>
            <div style={{ padding: "10px 12px", border: "1px solid var(--rule-strong)", background: "rgba(255,255,255,0.02)" }}>
              <div className="caption" style={{ color: "var(--ink-3)" }}>Label keeps</div>
              <div style={{ fontFamily: "var(--sans)", fontWeight: 800, fontSize: 18, color: "var(--ink-2)", marginTop: 4, fontVariantNumeric: "tabular-nums" }}>
                ${formatNum(labelCut)}
              </div>
              <div className="caption" style={{ marginTop: 3 }}>{(100 - points).toFixed(0)}% of pool</div>
            </div>
            <div style={{ padding: "10px 12px", border: "1px solid rgba(200,180,160,0.3)", background: "rgba(200,180,160,0.04)" }}>
              <div className="caption" style={{ color: "#c8b4a0" }}>Artist royalty</div>
              <div style={{ fontFamily: "var(--sans)", fontWeight: 800, fontSize: 18, color: "#c8b4a0", marginTop: 4, fontVariantNumeric: "tabular-nums" }}>
                ~${formatNum(artistRoyaltyFromLabel)}
              </div>
              <div className="caption" style={{ marginTop: 3 }}>~15% from label</div>
            </div>
            <div style={{ padding: "10px 12px", border: "1px solid var(--rule-gold)", background: "rgba(212,166,74,0.06)" }}>
              <div className="caption" style={{ color: "var(--gold)" }}>Your {points} pts</div>
              <div style={{ fontFamily: "var(--sans)", fontWeight: 800, fontSize: 18, color: "var(--gold)", marginTop: 4, fontVariantNumeric: "tabular-nums" }}>
                ${formatNum(yourGross)}
              </div>
              <div className="caption" style={{ marginTop: 3 }}>{points}% of pool</div>
            </div>
          </div>

          {/* Recoup wall toggle */}
          <button
            type="button"
            onClick={() => setShowRecoupWall((v) => !v)}
            style={{ display: "flex", justifyContent: "space-between", alignItems: "center", width: "100%", background: showRecoupWall ? "rgba(224,80,64,0.05)" : "rgba(255,255,255,0.02)", border: "1px solid var(--rule-strong)", padding: "9px 14px", cursor: "pointer", transition: "background 0.15s" }}
          >
            <span style={{ fontFamily: "var(--mono)", fontSize: 10, letterSpacing: "0.12em", color: showRecoupWall ? "#e05040" : "var(--ink-3)" }}>
              RECOUP WALL · {showRecoupWall ? "hide example" : "see what hides your money"}
            </span>
            <span style={{ fontFamily: "var(--mono)", fontSize: 10, color: showRecoupWall ? "#e05040" : "var(--ink-4)" }}>
              {showRecoupWall ? "▲" : "▼"}
            </span>
          </button>

          {showRecoupWall && (
            <div style={{ border: "1px solid var(--rule-strong)", borderTop: "none", background: "rgba(255,255,255,0.01)" }}>
              {/* Disclaimer */}
              <div style={{ padding: "10px 14px", borderBottom: "1px solid var(--rule-strong)", background: "rgba(184,137,255,0.06)" }}>
                <p style={{ margin: 0, fontFamily: "var(--mono)", fontSize: 9, letterSpacing: "0.08em", color: "#c5a3ff", lineHeight: 1.55 }}>
                  ★ This is an illustrative example, not your deal. Every contract is negotiated differently. A good entertainment lawyer can isolate you to recording recoupment only, cap album costs, and in some cases negotiate a <strong>half-recoupable advance</strong> (only 50% charged back). Without one, labels use their boilerplate, and their boilerplate is not written for you.
                </p>
              </div>
              {/* Advance row */}
              <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", padding: "8px 14px", borderBottom: "1px solid var(--rule)" }}>
                <span style={{ fontFamily: "var(--mono)", fontSize: 11, color: "var(--ink-2)" }}>Your advance</span>
                <span style={{ fontFamily: "var(--mono)", fontSize: 11, color: "var(--ink)", fontVariantNumeric: "tabular-nums" }}>${formatNum(advance)}</span>
              </div>
              {/* Toggleable items */}
              {RECOUP_LINE_ITEMS.map((item) => {
                const active = !!recoupToggles[item.id];
                const isWarning = item.id === "net_profit";
                const accentColor = isWarning ? "#b889ff" : "#e05040";
                return (
                  <div key={item.id} style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start", padding: "7px 14px", borderBottom: "1px solid var(--rule)", background: active ? `rgba(${isWarning ? "184,137,255" : "224,80,64"},0.04)` : "transparent", transition: "background 0.15s" }}>
                    <div style={{ display: "flex", flexDirection: "column", gap: 2, flex: 1, minWidth: 0 }}>
                      <button
                        type="button"
                        onClick={() => setRecoupToggles((t) => ({ ...t, [item.id]: !t[item.id] }))}
                        style={{ display: "flex", alignItems: "center", gap: 8, background: "none", border: "none", cursor: "pointer", padding: 0 }}
                      >
                        <span style={{ width: 14, height: 14, border: `1px solid ${active ? accentColor : "var(--rule-strong)"}`, background: active ? `rgba(${isWarning ? "184,137,255" : "224,80,64"},0.18)` : "transparent", display: "inline-flex", alignItems: "center", justifyContent: "center", fontSize: 10, color: active ? accentColor : "var(--ink-4)", transition: "all 0.15s", flexShrink: 0 }}>
                          {active ? "×" : "+"}
                        </span>
                        <span style={{ fontFamily: "var(--mono)", fontSize: 11, color: active ? "var(--ink-2)" : "var(--ink-4)" }}>{item.label}</span>
                      </button>
                      {active && item.note && (
                        <span style={{ fontFamily: "var(--mono)", fontSize: 9, letterSpacing: "0.08em", color: "var(--ink-4)", paddingLeft: 22 }}>{item.note}</span>
                      )}
                    </div>
                    <span style={{ fontFamily: "var(--mono)", fontSize: 11, color: active ? accentColor : "var(--ink-4)", fontVariantNumeric: "tabular-nums", transition: "color 0.15s", flexShrink: 0, marginLeft: 8 }}>
                      {active ? `$${formatNum(item.amount)}` : "N/A"}
                    </span>
                  </div>
                );
              })}
              {/* Total + bar */}
              <div style={{ padding: "10px 14px", borderTop: "1px solid var(--rule-strong)", background: "rgba(255,255,255,0.02)" }}>
                <div style={{ display: "flex", justifyContent: "space-between", marginBottom: 8, fontFamily: "var(--mono)", fontSize: 10, letterSpacing: "0.1em" }}>
                  <span style={{ color: "var(--ink-3)" }}>TOTAL TO RECOUP</span>
                  <span style={{ color: totalFree && totalDebt > 0 ? "#8acaa0" : totalDebt > yourGross ? "#e05040" : "var(--ink)", transition: "color 0.3s", fontVariantNumeric: "tabular-nums" }}>
                    ${formatNum(totalDebt)}
                    {totalDebt > 0 && !totalFree && ` · ${formatNum(streamsToBreakEven)} streams to clear`}
                  </span>
                </div>
                <div style={{ position: "relative", height: 16, background: "rgba(255,255,255,0.04)", overflow: "hidden", border: "1px solid var(--rule-strong)" }}>
                  <div style={{
                    position: "absolute", inset: 0,
                    width: `${Math.min(100, totalRecoupedPct)}%`,
                    background: totalFree && totalDebt > 0
                      ? "linear-gradient(90deg, #2a5a3a, #7acf99)"
                      : "linear-gradient(90deg, #6a1a10, #c0392b)",
                    transition: "width 0.5s cubic-bezier(0.2, 0.8, 0.2, 1), background 0.4s ease",
                  }} />
                  <div style={{ position: "absolute", inset: 0, display: "flex", alignItems: "center", justifyContent: "center", fontFamily: "var(--mono)", fontSize: 9, letterSpacing: "0.12em", color: totalRecoupedPct >= 50 ? "#16110a" : "var(--ink-2)", fontWeight: 600 }}>
                    {totalFree && totalDebt > 0 ? "CLEARED" : `${formatMoney(totalRemainingDebt)} LEFT`}
                  </div>
                </div>
              </div>
              {/* Recoupment trigger note */}
              <div style={{ padding: "8px 14px", borderTop: "1px solid var(--rule)", background: "rgba(255,255,255,0.01)" }}>
                <p style={{ margin: 0, fontFamily: "var(--mono)", fontSize: 9, letterSpacing: "0.08em", color: "var(--ink-4)", lineHeight: 1.5 }}>
                  ★ <strong style={{ color: "var(--ink-3)" }}>Release recoupment</strong> (standard): royalties frozen until the entire album recoups all recording costs, then your advance clears as a second hurdle. Once both clear, royalties pay out retroactively to stream 1. <strong style={{ color: "var(--ink-3)" }}>Recording recoupment</strong> (negotiate for this): only your track's costs need to clear. The difference is years of unpaid royalties, or never seeing a check at all.
                </p>
              </div>
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

// ---- POINTS DIAGRAM, 100 squares ----
function PointsDiagram({ points, compact }) {
  const total = 100;
  const yourSquares = Math.min(total, Math.round(points));
  const cells = Array.from({ length: total });

  return (
    <div style={{ marginTop: compact ? 10 : 24, padding: compact ? "12px 0 0" : "22px 24px", border: compact ? "none" : "1px solid var(--rule-strong)", background: compact ? "transparent" : "rgba(255,255,255,0.015)" }}>
      {!compact && (
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "baseline", marginBottom: 16, flexWrap: "wrap", gap: 12 }}>
          <div>
            <span className="caption">What are points, exactly?</span>
            <div className="pull-serif" style={{ fontSize: 18, lineHeight: 1.3, marginTop: 4, color: "var(--ink-2)" }}>
              One point = one percent of the master. <span className="gold">100 squares, 100 points, 100% of the master royalty.</span>
            </div>
          </div>
          <div style={{ display: "flex", gap: 14, fontFamily: "var(--mono)", fontSize: 10, letterSpacing: "0.14em" }}>
            <span style={{ display: "flex", alignItems: "center", gap: 6 }}>
              <span style={{ width: 10, height: 10, background: "var(--gold)", display: "inline-block" }}></span>
              <span style={{ color: "var(--gold)" }}>YOU · {points}%</span>
            </span>
            <span style={{ display: "flex", alignItems: "center", gap: 6 }}>
              <span style={{ width: 10, height: 10, background: "var(--ink-4)", display: "inline-block" }}></span>
              <span style={{ color: "var(--ink-3)" }}>REST · LABEL + ARTIST</span>
            </span>
          </div>
        </div>
      )}
      <div style={{ display: "grid", gridTemplateColumns: compact ? "repeat(10, 1fr)" : "repeat(20, 1fr)", gap: 3 }}>
        {cells.map((_, i) => {
          const isYou = i < yourSquares;
          return (
            <div key={i} style={{
              aspectRatio: "1",
              background: isYou ? "var(--gold)" : "rgba(255,255,255,0.045)",
              border: isYou ? "1px solid var(--gold-2)" : "1px solid rgba(255,255,255,0.04)",
              transition: "background 0.3s ease, border-color 0.3s ease",
            }}></div>
          );
        })}
      </div>
      <p className="caption" style={{ marginTop: compact ? 8 : 14 }}>
        {compact
          ? `${points} pts = ${points}% of every master dollar, forever.`
          : "★ Each square is 1% (\"1 point\") of the master royalty. \"5 points\" means you collect 5 of these squares' worth of every dollar the master earns, forever, on every play of that recording."}
      </p>
    </div>
  );
}

function RecoupmentBlock({ advance, setAdvance, yourGross, remainingDebt, recoupedPct, compact, slim, layout = "b" }) {
  const isCompact = compact || slim;
  const free = advance === 0 || recoupedPct >= 100;
  const inDebt = !free && advance > 0;

  // Debt-state color shift: red when in debt, green when free, neutral when advance = 0
  const stateColor = free && advance > 0 ? "#8acaa0" : inDebt ? "#e05040" : masterTone("advance").color;
  const stateBorder = free && advance > 0 ? "rgba(74,138,90,0.45)" : inDebt ? "rgba(192,57,43,0.4)" : masterTone("advance").border;
  const stateBg = free && advance > 0 ? "rgba(74,138,90,0.06)" : inDebt ? "rgba(192,57,43,0.06)" : masterTone("advance").bg;

  return (
    <div style={{
      marginTop: slim ? 0 : compact ? 0 : 24,
      padding: slim ? 0 : compact ? 0 : "24px 26px",
      border: isCompact ? "none" : `1px solid ${stateBorder}`,
      background: isCompact ? "transparent" : stateBg,
      transition: "border-color 0.4s ease, background 0.4s ease",
    }}>
      {!isCompact && (
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "baseline", marginBottom: 18 }}>
          <span className="caption" style={{ color: stateColor, transition: "color 0.4s ease" }}>The advance trap</span>
          <span className="caption">Drag the advance amount →</span>
        </div>
      )}

      {slim ? (
        <MasterSliderControl
          layout={layout}
          tone={layout === "a" ? "deal" : "advance"}
          hint={layout === "b" ? "Advance amount" : undefined}
          valueDisplay={`$${formatNum(advance)}`}
          min={0}
          max={50000}
          step={500}
          value={advance}
          onChange={(e) => setAdvance(Number(e.target.value))}
          pct={(advance / 50000) * 100}
          ticks={["$0", "$25K", "$50K"]}
        />
      ) : (
        <div style={{ display: "grid", gridTemplateColumns: "auto 1fr auto", gap: 16, alignItems: "center" }}>
          <span className="caption" style={{ minWidth: 0 }}>$0</span>
          <input
            type="range"
            min="0"
            max="50000"
            step="500"
            value={advance}
            onChange={(e) => setAdvance(Number(e.target.value))}
            className={`pu-slider${inDebt ? " debt-track" : free && advance > 0 ? " green-track" : " role-songwriter"}`}
            style={{ width: "100%", "--pu-pct": `${(advance / 50000) * 100}%` }}
          />
          <div style={{ display: "flex", alignItems: "baseline", gap: 4, minWidth: 100, justifyContent: "flex-end" }}>
            <span style={{ fontFamily: "var(--sans)", fontWeight: 800, fontSize: 24, color: stateColor, letterSpacing: "-0.02em", fontVariantNumeric: "tabular-nums", transition: "color 0.4s ease" }}>${formatNum(advance)}</span>
            <span className="caption">advance</span>
          </div>
        </div>
      )}

      <div style={{ marginTop: slim ? 8 : 20 }}>
        <div style={{ display: "flex", justifyContent: "space-between", marginBottom: slim ? 4 : 6, fontFamily: "var(--mono)", fontSize: slim ? 9 : 10, letterSpacing: "0.14em" }}>
          <span style={{ color: "var(--ink-3)" }}>RECOUPMENT</span>
          <span style={{ color: free && advance > 0 ? "#8acaa0" : stateColor, transition: "color 0.4s ease" }}>
            {free && advance > 0 ? "100% RECOUPED ✓" : `${Math.round(recoupedPct)}% PAID BACK`}
          </span>
        </div>
        <div style={{ position: "relative", height: slim ? 14 : 22, background: "rgba(255,255,255,0.05)", overflow: "hidden", border: `1px solid ${stateBorder}` }}>
          <div style={{
            position: "absolute", inset: 0,
            width: `${Math.min(100, recoupedPct)}%`,
            background: free && advance > 0
              ? "linear-gradient(90deg, #2a5a3a, #7acf99)"
              : inDebt
              ? "linear-gradient(90deg, #6a1a10, #c0392b)"
              : `linear-gradient(90deg, rgba(184,137,255,0.45), ${masterTone("advance").color})`,
            transition: "width 0.5s cubic-bezier(0.2, 0.8, 0.2, 1), background 0.4s ease",
          }}></div>
          <div style={{ position: "absolute", inset: 0, display: "flex", alignItems: "center", justifyContent: "center", fontFamily: "var(--mono)", fontSize: slim ? 9 : 11, letterSpacing: "0.12em", color: recoupedPct >= 50 ? "#16110a" : "var(--ink-2)", fontWeight: 600 }}>
            {free && advance > 0 ? "RECOUPED - ROYALTIES NOW FLOWING" : `${formatMoney(remainingDebt)} LEFT`}
          </div>
        </div>
      </div>
    </div>
  );
}

function MasterAdvanceRow({ advance, setAdvance, yourGross, remainingDebt, recoupedPct, layout = "b" }) {
  if (layout === "a") return null;
  return (
    <MasterStripB tone="advance" label="Advance / recoup">
      <RecoupmentBlock
        advance={advance}
        setAdvance={setAdvance}
        yourGross={yourGross}
        remainingDebt={remainingDebt}
        recoupedPct={recoupedPct}
        slim
        layout="b"
      />
    </MasterStripB>
  );
}

function Tile({ label, value, sub, gold, flag }) {
  return (
    <div style={{ padding: "14px 16px", border: "1px solid var(--rule)", background: "rgba(255,255,255,0.02)" }}>
      <div className="caption">{label}</div>
      <div style={{
        fontFamily: "var(--sans)",
        fontWeight: 800,
        fontSize: "clamp(20px, 2.4vw, 28px)",
        letterSpacing: "-0.02em",
        color: flag ? "#8acaa0" : gold ? "var(--gold)" : "var(--ink)",
        marginTop: 4,
        fontVariantNumeric: "tabular-nums",
      }}>
        {value}
      </div>
      {sub && <div className="caption" style={{ marginTop: 6 }}>{sub}</div>}
    </div>
  );
}

// Regional publishing breakdown: detail panel synced to stream counter territory
function RegionalPublishingBreakdown({ embedded, streams, territory = "us", platform = "spotify" }) {
  const region = publishingRatesForPlatform(territory, platform);
  const streamTypeMeta = window.getStreamTypeMeta?.(platform) || {};
  const dspObj = window.getDspPlatform?.(platform);
  const confStyle = CONFIDENCE_COLORS[region.confidence] || CONFIDENCE_COLORS.extrapolated;
  const totalEarned = streams * region.total / 1000;
  const perfEarned = streams * region.perf / 1000;
  const mechEarned = streams * region.mech / 1000;
  const cert = TERRITORY_CERTS[territory];

  return (
    <div style={embedded ? { marginTop: 8 } : { marginTop: 48, paddingTop: 40, borderTop: "1px solid var(--rule)" }}>
      {!embedded && (
        <div style={{ marginBottom: 28 }}>
          <span className="caption">Deep dive: regional publishing breakdown</span>
          <h3 className="pull-serif" style={{ fontSize: 24, lineHeight: 1.3, margin: "10px 0 0", color: "var(--ink)" }}>
            Performance royalties (radio, streaming) + mechanical royalties (reproductions) vary dramatically by region.
          </h3>
        </div>
      )}

      <div style={{ padding: "20px 22px", background: "rgba(212,166,74,0.03)", border: "1px solid var(--rule-gold)", marginBottom: 24 }}>
        <span className="caption" style={{ color: "var(--gold)" }}>Why the difference?</span>
        <ul className="body-text" style={{ marginTop: 8, paddingLeft: 20, fontSize: 14, lineHeight: 1.5, color: "var(--ink-2)" }}>
          <li><strong>Interactive streams</strong> (Spotify, Apple, etc.) pay full performance + mechanical via PRO and MLC (or local societies).</li>
          <li><strong>Non-interactive streams</strong> (Pandora, iHeartRadio) pay performance via PRO; mechanicals are statutory and minimal on webcast.</li>
          <li><strong>Regional variation</strong> reflects ARPU and local society rates. Change territory and DSP in the stream counter above.</li>
        </ul>
      </div>

      <div style={{ padding: "24px 28px", border: "1px solid var(--rule-strong)", background: "rgba(255,255,255,0.015)", marginBottom: 24 }}>
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "baseline", marginBottom: 20, flexWrap: "wrap", gap: 12 }}>
          <div>
            <span className="caption">{dspObj?.label} · {streamTypeMeta.label} · {cert?.flag} {cert?.label} · per 1,000 streams</span>
            <div className="pull-serif" style={{ fontSize: 20, lineHeight: 1.3, margin: "8px 0 0", color: "var(--ink)" }}>
              ${region.total.toFixed(2)} total publishing pool
            </div>
          </div>
          <div style={{ background: confStyle.bg, border: `1px solid ${confStyle.border}`, borderRadius: "4px", padding: "6px 10px", fontFamily: "var(--mono)", fontSize: 10, letterSpacing: "0.12em", color: confStyle.text }}>
            ★ {confStyle.label}
          </div>
        </div>

        <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(min(100%, 180px), 1fr))", gap: 14 }}>
          <div style={{ padding: "14px 16px", border: "1px solid rgba(212,166,74,0.3)", background: "rgba(212,166,74,0.06)" }}>
            <div className="caption" style={{ color: "var(--gold)" }}>Performance → {region.perfSociety}</div>
            <div style={{ fontFamily: "var(--sans)", fontWeight: 800, fontSize: 24, color: "var(--gold)", marginTop: 6, letterSpacing: "-0.02em" }}>
              ${region.perf.toFixed(2)} / 1K
            </div>
            <div className="caption" style={{ marginTop: 4, color: "var(--ink-3)" }}>${formatNum(perfEarned)} at {formatNum(streams)} streams</div>
          </div>
          <div style={{ padding: "14px 16px", border: "1px solid rgba(122,207,153,0.3)", background: "rgba(122,207,153,0.06)" }}>
            <div className="caption" style={{ color: PUB_MECH_COLOR }}>Mechanical → {region.mechSociety}</div>
            <div style={{ fontFamily: "var(--sans)", fontWeight: 800, fontSize: 24, color: PUB_MECH_COLOR, marginTop: 6, letterSpacing: "-0.02em" }}>
              ${region.mech.toFixed(2)} / 1K
            </div>
            <div className="caption" style={{ marginTop: 4, color: "var(--ink-3)" }}>${formatNum(mechEarned)} at {formatNum(streams)} streams</div>
          </div>
        </div>

        <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(min(100%, 140px), 1fr))", gap: 16, marginTop: 20 }}>
          <Tile label="Publishing pool" value={`$${formatNum(totalEarned)}`} sub={`${formatNum(streams)} streams × $${region.total.toFixed(2)}/1K`} gold />
          <Tile label="Performance split" value={`$${formatNum(perfEarned)}`} sub={`${(region.perf / region.total * 100).toFixed(0)}% of pool`} />
          <Tile label="Mechanical split" value={`$${formatNum(mechEarned)}`} sub={`${(region.mech / region.total * 100).toFixed(0)}% of pool`} />
        </div>

        <div className="caption" style={{ marginTop: 12, color: "var(--ink-3)" }}>
          Source: {region.source}
        </div>
      </div>
    </div>
  );
}

// Platform comparison: synced to stream counter territory + DSP
function PlatformMasterBreakdown({ embedded, streams, territory = "us", platform = "spotify", onPlatformChange }) {
  const regionKey = TERRITORY_PAYOUT_MAP[territory]?.masterRegionKey || "us";
  const dspObj = window.getDspPlatform?.(platform) || (window.DSP_PLATFORMS || [])[0];
  const streamTypeMeta = window.getStreamTypeMeta?.(platform) || {};
  const isNonInteractive = window.isNonInteractiveStream?.(platform);
  const ratePerStream = masterRateForTerritoryPlatform(territory, platform);
  const totalEarned = streams * ratePerStream;
  const confStyle = CONFIDENCE_COLORS[window.PLATFORM_MASTER_RATES?.[platform]?.confidence] || CONFIDENCE_COLORS.extrapolated;
  const cert = TERRITORY_CERTS[territory];

  return (
    <div style={embedded ? { marginTop: 8 } : { marginTop: 48, paddingTop: 40, borderTop: "1px solid var(--rule)" }}>
      {!embedded && (
        <div style={{ marginBottom: 28 }}>
          <span className="caption">Deep dive: master royalties by platform</span>
          <h3 className="pull-serif" style={{ fontSize: 24, lineHeight: 1.3, margin: "10px 0 0", color: "var(--ink)" }}>
            Per-stream rates to the master rights holder vary wildly by platform and listener location.
          </h3>
        </div>
      )}

      <p className="caption" style={{ marginBottom: 14, color: "var(--ink-3)", lineHeight: 1.5 }}>
        Interactive pays via distributor. Non-interactive pays via SoundExchange. Change territory and DSP in the stream counter above, all math updates live.
      </p>

      <div style={{ display: "flex", alignItems: "center", gap: 12, marginBottom: 20, flexWrap: "wrap", padding: "12px 14px", border: "1px solid var(--rule-strong)", background: "rgba(255,255,255,0.015)" }}>
        <DspIcon id={platform} size={24} />
        <span className="caption" style={{ color: "var(--ink-2)" }}>
          {dspObj.label} · {streamTypeMeta.label} · {cert?.flag} {cert?.label} · ${(ratePerStream * 1000).toFixed(2)}/1K · ${formatNum(totalEarned)} gross pool
          {isNonInteractive ? " · SoundExchange" : ""}
        </span>
        <div style={{ background: confStyle.bg, border: `1px solid ${confStyle.border}`, borderRadius: "4px", padding: "4px 8px", fontFamily: "var(--mono)", fontSize: 9, letterSpacing: "0.12em", color: confStyle.text }}>
          ★ {confStyle.label}
        </div>
      </div>

      <PlatformComparison territory={territory} streams={streams} highlightPlatform={platform} onPlatformChange={onPlatformChange} />
    </div>
  );
}

function platformRateForDsp(dsp, regionKey) {
  if (dsp.streamType === "non-interactive") return (window.sxPer1kForRegion?.(regionKey) ?? 0.20) / 1000;
  return window.PLATFORM_MASTER_RATES?.[dsp.id]?.[regionKey] ?? 0;
}

function PlatformBarChart({ dsps, regionKey, streams, highlightPlatform, maxRate, variant, onPlatformChange }) {
  const isSx = variant === "non-interactive";

  return (
    <div className={`platform-bar-chart platform-bar-chart--${variant}`}>
      <div className="platform-bar-chart-cols" role="list">
        {dsps.map((dsp) => {
          const rate = platformRateForDsp(dsp, regionKey);
          const earned = streams * rate;
          const pct = maxRate > 0 ? Math.max(6, (rate / maxRate) * 100) : 0;
          const active = dsp.id === highlightPlatform;

          return (
            <button
              key={dsp.id}
              type="button"
              role="listitem"
              className={`platform-bar-chart-col${active ? " active" : ""}`}
              onClick={() => onPlatformChange?.(dsp.id)}
              title={`${dsp.label} · $${(rate * 1000).toFixed(2)} per 1K · $${formatNum(earned)} total`}
            >
              <span className="platform-bar-chart-earned">${formatNum(earned)}</span>
              <div className="platform-bar-chart-track">
                <div className="platform-bar-chart-fill" style={{ height: `${pct}%` }} />
              </div>
              <span className="platform-bar-chart-rate">
                {(rate * 1000).toFixed(2)}/1K{isSx ? " · SX" : ""}
              </span>
              <span className="platform-bar-chart-icon">
                <DspIcon id={dsp.id} size={20} />
              </span>
              <span className="platform-bar-chart-label">{dsp.label}</span>
            </button>
          );
        })}
      </div>
    </div>
  );
}

function PlatformComparison({ territory, streams, highlightPlatform, onPlatformChange }) {
  const regionKey = TERRITORY_PAYOUT_MAP[territory]?.masterRegionKey || "us";
  const platforms = window.DSP_PLATFORMS || [];
  const interactive = platforms.filter((d) => d.streamType !== "non-interactive");
  const nonInteractive = platforms.filter((d) => d.streamType === "non-interactive");
  const maxInteractive = Math.max(...interactive.map((d) => platformRateForDsp(d, regionKey)), 0.0001);
  const maxNonInteractive = Math.max(...nonInteractive.map((d) => platformRateForDsp(d, regionKey)), 0.0001);

  return (
    <div>
      <span className="caption">How platforms compare · {TERRITORY_CERTS[territory]?.label} listeners · {formatNum(streams)} streams</span>
      <div style={{ marginTop: 16, display: "flex", flexDirection: "column", gap: 24 }}>
        <div>
          <span className="caption pub-ns-dsp-group-label" style={{ display: "inline-flex", marginBottom: 12, color: "var(--gold)" }}>
            Interactive · distributor master pool
            <FieldTooltip text={window.STREAM_TYPES?.interactive?.tooltip} />
          </span>
          <PlatformBarChart
            dsps={interactive}
            regionKey={regionKey}
            streams={streams}
            highlightPlatform={highlightPlatform}
            maxRate={maxInteractive}
            variant="interactive"
            onPlatformChange={onPlatformChange}
          />
        </div>
        <div>
          <span className="caption pub-ns-dsp-group-label" style={{ display: "inline-flex", marginBottom: 12, color: "#b889ff" }}>
            Non-interactive · SoundExchange digital perf
            <FieldTooltip text={window.STREAM_TYPES?.["non-interactive"]?.tooltip} />
          </span>
          <PlatformBarChart
            dsps={nonInteractive}
            regionKey={regionKey}
            streams={streams}
            highlightPlatform={highlightPlatform}
            maxRate={maxNonInteractive}
            variant="non-interactive"
            onPlatformChange={onPlatformChange}
          />
        </div>
      </div>
      <p className="caption" style={{ marginTop: 14, color: "var(--ink-3)" }}>
        ★ Bar height = rate per 1K streams. Click a bar to sync with the DSP picker above.
      </p>
    </div>
  );
}

window.Calculator = Calculator;
window.PubNoiseSignalDeck = PubNoiseSignalDeck;
window.StreamScrubber = StreamScrubber;
window.TERRITORY_CERTS = TERRITORY_CERTS;
