/* ============================================================
   EMM — widget library for PRD v2 screens
   Donut, NSS card, Emotion cards, Velocity meter, Misinfo rows,
   Status pill, Hashtags, Word cloud, Report card, Live feed,
   Share/linguistic bars, dual-line trend.
   Depends on window.* (Panel, SentChip, ChannelLogo, Sparkline…)
   ============================================================ */
(function () {
  "use strict";
  const { useState, useRef, useEffect } = React;
  const E = window.EMM, C = E.fmt, L = window.emmL;
  const SENT = { pos: "var(--pos)", neu: "var(--neu)", neg: "var(--neg)" };
  const SENT_EMOJI = { pos: "🙂", neu: "😐", neg: "🙁" };
  const SENT_NAME = { pos: "positive", neu: "neutral", neg: "negative" };

  // ---------- Donut chart — ECharts doughnut ------------------
  function Donut({ segments, size = 132, thickness = 16, center, centerSub, gap = 2, onClick }) {
    const th = window.chartTheme();
    const resolve = (c) => !c ? th.accent
      : c.indexOf("var(") === 0 ? ({ "var(--pos)": th.pos, "var(--neu)": th.neu, "var(--neg)": th.neg, "var(--accent)": th.accent }[c] || th.accent)
      : c;
    const segs = segments.filter((s) => s.v > 0);
    const data = segs.map((s) => ({ value: s.v, name: s.label || "", itemStyle: { color: resolve(s.c) } }));
    const inner = Math.max(0, size / 2 - thickness), outer = size / 2 - 2;
    const base = window.baseOption(th);
    const option = {
      ...base,
      tooltip: { ...base.tooltip, trigger: "item", formatter: (p) => `${p.name}<br/><b>${C.compact(p.value)}</b> · ${p.percent}%` },
      title: center !== undefined ? { text: String(center), subtext: centerSub || "", left: "center", top: "center",
        textStyle: { color: th.ink, fontFamily: th.mono, fontSize: Math.round(size * 0.2), fontWeight: 600 },
        subtextStyle: { color: th.muted, fontFamily: th.mono, fontSize: 10 }, itemGap: 2 } : undefined,
      series: [{ type: "pie", radius: [inner, outer], center: ["50%", "50%"], avoidLabelOverlap: false,
        padAngle: gap, label: { show: false }, labelLine: { show: false },
        cursor: onClick ? "pointer" : "default",
        itemStyle: { borderColor: th.surface, borderWidth: 2 }, data }],
    };
    const onEvents = onClick ? { click: (p) => { const s = segs[p.dataIndex]; if (s) onClick(s); } } : undefined;
    return <div className="emm-donut" style={{ width: size, height: size, flex: "0 0 auto" }}><EChart option={option} height={size} onEvents={onEvents} /></div>;
  }

  // legend rows for donut
  function DonutLegend({ items }) {
    const total = items.reduce((s, x) => s + x.v, 0) || 1;
    return (
      <div className="emm-donut-legend">
        {items.map((it, i) => (
          <div key={i} className="emm-donut-leg-row">
            <span className="emm-dot" style={{ background: it.c }} />
            <span className="emm-donut-leg-lab">{it.label}</span>
            <span className="mono emm-donut-leg-val">{Math.round((it.v / total) * 100)}%</span>
          </div>
        ))}
      </div>
    );
  }

  // ---------- NSS card (−100 … +100 scale w/ marker) ---------
  function NSSCard({ value, delta, onInfo }) {
    const { t } = window.useEmmT();
    const pos = ((value + 100) / 200) * 100; // 0..100
    const band = value >= 25 ? "good" : value <= -10 ? "bad" : "mid";
    const up = delta >= 0;
    return (
      <div className="emm-kpi emm-nss">
        <div className="emm-kpi-label" title={t("nssFormula")}>{t("kpiNSS")} <span className="emm-nss-info mono">ⓘ</span></div>
        <div className="emm-kpi-row">
          <div className={"emm-kpi-value mono emm-nss-val " + band}>{value > 0 ? "+" : ""}{value}</div>
          {delta !== undefined && delta !== null && (
            <div className={"emm-kpi-delta mono " + (up ? "good" : "bad")} style={{ marginTop: 0 }}>
              <span className="arrow">{up ? "▲" : "▼"}</span> {Math.abs(delta)}
            </div>
          )}
        </div>
        <div className="emm-nss-scale">
          <div className="emm-nss-track">
            <span className="emm-nss-mid" />
            <span className={"emm-nss-marker " + band} style={{ left: pos + "%" }} />
          </div>
          <div className="emm-nss-ends mono"><span>−100</span><span>0</span><span>+100</span></div>
        </div>
      </div>
    );
  }

  // ---------- Active Alerts card (severity bars) -------------
  function ActiveAlertsCard({ critical, warning, flag, onClick }) {
    const { t } = window.useEmmT();
    const total = critical + warning + flag;
    return (
      <div className="emm-kpi emm-alerts-kpi clickable" onClick={onClick}>
        <div className="emm-kpi-label">{t("kpiAlerts")}</div>
        <div className="emm-kpi-row">
          <div className="emm-kpi-value mono" style={{ color: "var(--neg)" }}>{total}</div>
          <span className="emm-alerts-go mono">{t("goToCrisis")} →</span>
        </div>
        <div className="emm-alerts-bars">
          <div className="emm-alerts-seg crit" style={{ flex: Math.max(1, critical) }} title="critical">{critical}</div>
          <div className="emm-alerts-seg warn" style={{ flex: Math.max(1, warning) }} title="warning">{warning}</div>
          <div className="emm-alerts-seg flag" style={{ flex: Math.max(1, flag) }} title="flagged">{flag}</div>
        </div>
        <div className="emm-alerts-leg mono">
          <span><i className="crit" />{t("stCritical")}</span>
          <span><i className="warn" />{t("stFlagged")}</span>
          <span><i className="flag" />{t("stReview")}</span>
        </div>
      </div>
    );
  }

  // ---------- Status pill ------------------------------------
  function StatusPill({ status }) {
    const { t } = window.useEmmT();
    const map = { critical: "stCritical", flagged: "stFlagged", review: "stReview", resolved: "stResolved" };
    return <span className={"emm-status-pill " + status}>{t(map[status] || status)}</span>;
  }

  // ---------- Emotion card -----------------------------------
  const EMO_META = {
    anger:   { glyph: "△", cls: "anger" },
    anxiety: { glyph: "◇", cls: "anxiety" },
    trust:   { glyph: "○", cls: "trust" },
    hope:    { glyph: "✦", cls: "hope" },
  };
  function EmotionCard({ emo }) {
    const { t } = window.useEmmT();
    const m = EMO_META[emo.id];
    return (
      <div className={"emm-emocard " + m.cls}>
        <div className="emm-emocard-top">
          <span className="emm-emocard-glyph">{m.glyph}</span>
          <span className="emm-emocard-label">{t(emo.id)}</span>
        </div>
        <div className="emm-emocard-val mono">{emo.pct}<span className="pct">%</span></div>
        <div className="emm-emocard-track"><div className="emm-emocard-fill" style={{ width: emo.pct + "%" }} /></div>
      </div>
    );
  }

  // ---------- Share / linguistic bars (% w/ label) -----------
  function ShareBars({ items, lang, accent = "var(--accent)", onClick }) {
    const th = window.chartTheme();
    const acc = accent.indexOf("var(") === 0 ? th.accent : accent;
    const cats = items.map((it) => L(it.label, lang));
    const data = items.map((it) => ({ value: it.pct, itemStyle: { color: it.c ? th.rgb(it.c) : acc } }));
    const base = window.baseOption(th);
    const option = {
      ...base,
      tooltip: { ...base.tooltip, trigger: "axis", axisPointer: { type: "shadow" }, formatter: (ps) => `${cats[ps[0].dataIndex]}: <b>${ps[0].value}%</b>` },
      grid: { left: 4, right: 34, top: 4, bottom: 4, containLabel: true },
      xAxis: { type: "value", show: false, max: Math.max(...items.map((i) => i.pct), 1) },
      yAxis: { type: "category", inverse: true, data: cats, axisLine: { show: false }, axisTick: { show: false }, axisLabel: { color: th.ink2, fontSize: 12, width: 140, overflow: "truncate" } },
      series: [{ type: "bar", barMaxWidth: 13, data, cursor: onClick ? "pointer" : "default", label: { show: true, position: "right", formatter: (p) => p.value + "%", color: th.ink, fontFamily: th.mono, fontSize: 11 } }],
    };
    const onEvents = onClick ? { click: (p) => { const it = items[p.dataIndex]; if (it) onClick(it); } } : undefined;
    return <EChart option={option} height={Math.max(70, items.length * 30 + 10)} onEvents={onEvents} />;
  }

  // ---------- Sentiment-by-topic stack — ECharts -------------
  function TopicSentStack({ rows, lang, onClick }) {
    const th = window.chartTheme();
    const cats = rows.map((r) => L(r, lang));
    const pct = (r, k) => { const tot = r.pos + r.neu + r.neg || 1; return Math.round(r[k] / tot * 100); };
    const mk = (k, c) => ({ name: k, type: "bar", stack: "s", barMaxWidth: 18, itemStyle: { color: c },
      data: rows.map((r) => pct(r, k)),
      label: k !== "neu" ? { show: true, position: "inside", formatter: (p) => p.value >= 12 ? p.value + "%" : "", color: "#fff", fontFamily: th.mono, fontSize: 10 } : { show: false } });
    const base = window.baseOption(th);
    const option = {
      ...base,
      tooltip: { ...base.tooltip, trigger: "axis", axisPointer: { type: "shadow" },
        formatter: (ps) => { const r = rows[ps[0].dataIndex]; return `${L(r, lang)}<br/><span style="color:${th.pos}">${pct(r, "pos")}%</span> · <span style="color:${th.neu}">${pct(r, "neu")}%</span> · <span style="color:${th.neg}">${pct(r, "neg")}%</span>`; } },
      grid: { left: 4, right: 8, top: 4, bottom: 4, containLabel: true },
      xAxis: { type: "value", show: false, max: 100 },
      yAxis: { type: "category", inverse: true, data: cats, axisLine: { show: false }, axisTick: { show: false }, axisLabel: { color: th.ink2, fontSize: 12, width: 150, overflow: "truncate" } },
      series: [mk("pos", th.pos), mk("neu", th.neu), mk("neg", th.neg)],
    };
    const onEvents = onClick ? { click: (p) => onClick(rows[p.dataIndex]) } : null;
    return <EChart option={option} height={Math.max(80, rows.length * 34 + 10)} onEvents={onEvents} />;
  }

  // ---------- Hashtag row ------------------------------------
  function HashtagList({ items }) {
    return (
      <div className="emm-hashtags">
        {items.map((h) => (
          <div key={h.rank} className="emm-hashtag-row">
            <span className={"emm-hashtag-rank mono" + (h.rank <= 3 ? " top" : "")}>{String(h.rank).padStart(2, "0")}</span>
            <span className="emm-hashtag-tag">{h.tag}</span>
            <span className="mono emm-hashtag-vol">{C.compact(h.vol)}</span>
            <span className={"emm-hashtag-trend mono " + (h.trend > 0 ? "up" : "down")}>
              {h.trend > 0 ? "▲" : "▼"}{h.delta}%
            </span>
          </div>
        ))}
      </div>
    );
  }

  // ---------- Word cloud — ECharts wordCloud plugin ----------
  function WordCloud({ words }) {
    const th = window.chartTheme();
    const col = { pos: th.pos, neu: th.neu, neg: th.neg };
    const data = words.map((w) => ({ name: w.w, value: Math.round(20 + w.wt * 80), textStyle: { color: col[w.s] || th.accent } }));
    const option = {
      tooltip: { show: true, backgroundColor: th.surface, borderColor: th.line2, textStyle: { color: th.ink, fontFamily: th.font } },
      series: [{ type: "wordCloud", shape: "circle", left: "center", top: "center", width: "98%", height: "96%",
        sizeRange: [13, 40], rotationRange: [0, 0], gridSize: 7, drawOutOfBound: false,
        textStyle: { fontFamily: th.font, fontWeight: 600 }, emphasis: { textStyle: { fontWeight: 700 } }, data }],
    };
    return <EChart option={option} height={236} />;
  }

  // ---------- Live media feed --------------------------------
  function LiveFeed({ items, lang, onOpen }) {
    const { t } = window.useEmmT();
    return (
      <div className="emm-livefeed">
        {items.map((it) => (
          <button key={it.id} className="emm-feed-row" onClick={() => onOpen(it.story)}>
            <ChannelLogo ch={it.ch} size={26} />
            <div className="emm-feed-main">
              <div className="emm-feed-head">{L(it.headline, lang)}</div>
              <div className="emm-feed-meta mono">{it.ch.name} · {L(it.topicName, lang)}</div>
            </div>
            <div className="emm-feed-right">
              <span className="emm-feed-emoji" title={t(SENT_NAME[it.sent])} aria-label={t(SENT_NAME[it.sent])}>{SENT_EMOJI[it.sent]}</span>
              <span className="emm-feed-time mono">{it.live ? <span className="emm-feed-live"><span className="emm-live-dot" />{t("now")}</span> : it.mins + " " + t("minsAgo")}</span>
            </div>
          </button>
        ))}
      </div>
    );
  }

  // ---------- Dual-line trend (TV vs Online) — ECharts --------
  function DualTrend({ series, height = 200, labelEvery = 3 }) {
    const { t } = window.useEmmT();
    const th = window.chartTheme();
    const x = series.map((s) => "D" + (s.day + 1));
    const base = window.baseOption(th);
    const option = {
      ...base,
      tooltip: { ...base.tooltip, trigger: "axis",
        formatter: (ps) => `${ps[0].axisValue}<br/>` + ps.map((p) => `${p.marker}${p.seriesName}: <b>${C.compact(p.data)}</b>`).join("<br/>") },
      legend: { data: [t("tv"), t("online")], top: 0, right: 0, icon: "roundRect", itemWidth: 16, itemHeight: 7,
        textStyle: { color: th.muted, fontFamily: th.mono, fontSize: 10 } },
      grid: { left: 4, right: 10, top: 26, bottom: 22, containLabel: true },
      xAxis: { type: "category", data: x, boundaryGap: false, axisTick: { show: false },
        axisLine: { lineStyle: { color: th.line2 } },
        axisLabel: { color: th.muted, fontFamily: th.mono, fontSize: 10, interval: (i) => i % labelEvery === 0 || i === x.length - 1 } },
      yAxis: { type: "value", splitLine: { lineStyle: { color: th.line } },
        axisLabel: { color: th.muted, fontFamily: th.mono, fontSize: 10, formatter: (v) => C.compact(v) } },
      series: [
        { name: t("tv"), type: "line", data: series.map((s) => s.tv), showSymbol: false, lineStyle: { color: th.accent, width: 2 }, itemStyle: { color: th.accent } },
        { name: t("online"), type: "line", data: series.map((s) => s.online), showSymbol: false, lineStyle: { color: th.neg, width: 2, type: "dashed" }, itemStyle: { color: th.neg } },
      ],
    };
    return <EChart option={option} height={height} />;
  }

  // ---------- Velocity meter (pulsing crisis hero) -----------
  function VelocityMeter({ spike, source, series, lang }) {
    const { t } = window.useEmmT();
    const spark = series.map((s) => s.v);
    const st = window.EMM_SETTINGS || {};
    const vp = st.velocityPct != null ? st.velocityPct : 200;
    const wh = st.windowH != null ? st.windowH : 2;
    const autoTxt = t("autoTriggerTpl").replace("%p%", vp).replace("%h%", wh);
    return (
      <div className="emm-velocity">
        <div className="emm-velocity-main">
          <div className="emm-velocity-pulse"><span className="emm-velocity-ring" /><span className="emm-velocity-core">!</span></div>
          <div className="emm-velocity-body">
            <div className="emm-velocity-spike mono">+{spike}%</div>
            <div className="emm-velocity-cap mono">{t("velocitySpike")} · {t("last6h")}</div>
            <div className="emm-velocity-gz">
              <span className="emm-velocity-gz-cap mono">{t("groundZero")}:</span>
              <span className="emm-velocity-gz-val">{L(source, lang)}</span>
            </div>
          </div>
        </div>
        <div className="emm-velocity-spark">
          <Sparkline values={spark} width={240} height={56} color="var(--neg)" fill={true} />
          <div className="emm-velocity-auto mono">{autoTxt}</div>
        </div>
      </div>
    );
  }

  // ---------- Misinformation row -----------------------------
  function MisinfoRow({ row, lang, onOpen }) {
    const { t } = window.useEmmT();
    return (
      <div className="emm-misinfo" onClick={onOpen ? () => onOpen(row) : undefined}>
        <span className={"emm-misinfo-stripe " + row.sev} />
        <div className="emm-misinfo-body">
          <div className="emm-misinfo-head">{L(row.head, lang)}</div>
          <div className="emm-misinfo-meta mono">
            <span>{L(row.source, lang)}</span>
            <span className="dot">·</span>
            <span>{C.compact(row.reach)} {t("reach")}</span>
            <span className="dot">·</span>
            <span>{row.mins}m {t("detectedAgo")}</span>
          </div>
        </div>
        <div className="emm-misinfo-vel mono">
          <span className="emm-misinfo-vel-val">+{row.velocity}%</span>
          <StatusPill status={row.status} />
        </div>
      </div>
    );
  }

  // ---------- Fake-news / deepfake detector ------------------
  const FN_KIND = { deepfake: "kindDeepfake", doctored: "kindDoctored", falseclaim: "kindFalseclaim", miscontext: "kindMiscontext", rumour: "kindRumour" };
  const FN_VERDICT = { false: "verdictFalse", disputed: "verdictDisputed", unverified: "verdictUnverified", debunked: "verdictDebunked" };

  function FakeNewsDetector({ rows, summary, lang, onSelect, selectedId }) {
    const { t } = window.useEmmT();
    const tiles = [
      { k: "active", label: t("fnDetected"),      val: summary.active,        tone: "neg" },
      { k: "deep",   label: t("fnDeepfakes"),     val: summary.deepfakes,     tone: "warn" },
      { k: "false",  label: t("fnVerifiedFalse"), val: summary.verifiedFalse, tone: "neg" },
      { k: "clar",   label: t("fnClarified"),     val: summary.clarified,     tone: "pos" },
      { k: "pend",   label: t("fnPending"),       val: summary.pendingClar,   tone: "warn" },
      { k: "sat",    label: t("fnAvgSat"),        val: summary.avgSatMins ? summary.avgSatMins + "m" : "—", tone: "" },
    ];
    return (
      <div className="emm-fnd">
        <div className="emm-fnd-tiles">
          {tiles.map((ti) => (
            <div key={ti.k} className={"emm-fnd-tile " + ti.tone}>
              <div className="emm-fnd-tile-val mono">{ti.val}</div>
              <div className="emm-fnd-tile-lab mono">{ti.label}</div>
            </div>
          ))}
        </div>
        <div className="emm-fnd-list">
          {rows.map((r) => <FakeNewsRow key={r.id} row={r} lang={lang} onSelect={onSelect} selected={selectedId === r.id} />)}
        </div>
      </div>
    );
  }

  function FakeNewsRow({ row, lang, onSelect, selected }) {
    const { t } = window.useEmmT();
    const synth = row.synthetic && row.synthetic.flag;
    const clarCls = row.clar.issued ? (row.clar.status === "live" ? "live" : "drafting") : "none";
    const clarLabel = row.clar.issued
      ? (row.clar.status === "live" ? t("clarLive") : t("clarDrafting"))
      : t("clarNone");
    return (
      <div className={"emm-fnrow " + row.sev + (onSelect ? " selectable" : "") + (selected ? " selected" : "")}
        onClick={onSelect ? () => onSelect(row) : undefined}>
        <span className={"emm-misinfo-stripe " + row.sev} />
        <div className="emm-fnrow-body">
          <div className="emm-fnrow-tags">
            <span className={"emm-fn-kind " + row.kind}>{t(FN_KIND[row.kind] || "kindRumour")}</span>
            {synth && (
              <span className="emm-fn-deepfake mono" title={t("syntheticFlag")}>
                ◈ {t("kindDeepfake")} · {row.synthetic.confidence}% {t("confidence")}
              </span>
            )}
            <span className={"emm-fn-verdict " + row.verdict}>{t(FN_VERDICT[row.verdict] || "verdictUnverified")}</span>
          </div>
          <div className="emm-fnrow-head">{L(row.head, lang)}</div>
          <div className="emm-fnrow-meta mono">
            <span className="emm-fn-gz" title={t("groundZero")}>⌖ {row.ground.handle}</span>
            <span className="dot">·</span>
            <span>{L(row.ground.network, lang)}</span>
            <span className="dot">·</span>
            <span>{C.compact(row.reach)} {t("reach")}</span>
            <span className="dot">·</span>
            <span>{row.mins}m {t("detectedAgo")}</span>
          </div>
        </div>
        <div className="emm-fnrow-right">
          <span className="emm-fnrow-vel mono">+{row.velocity}%</span>
          <span className={"emm-fn-clar mono " + clarCls}>{clarLabel}{row.clar.satMins ? " · " + row.clar.satMins + "m" : ""}</span>
          <StatusPill status={row.status} />
        </div>
      </div>
    );
  }

  // ---------- Spokesperson credibility & prominence ----------
  function SpokespersonTracker({ rows, lang, highlightId }) {
    const { t } = window.useEmmT();
    const maxQ = Math.max(...rows.map((r) => r.quotes), 1);
    const focusRef = useRef(null);
    useEffect(() => { if (highlightId && focusRef.current) focusRef.current.scrollIntoView({ block: "nearest", behavior: "smooth" }); }, [highlightId]);
    return (
      <div className="emm-spox">
        {rows.map((s) => {
          const initials = (s.name.en || "").split(" ").map((w) => w[0]).join("").slice(0, 2);
          return (
            <div key={s.id} ref={s.id === highlightId ? focusRef : null}
              className={"emm-spox-row" + (s.id === highlightId ? " is-focus" : "")}>
              <span className="emm-spox-avatar mono">{initials}</span>
              <div className="emm-spox-main">
                <div className="emm-spox-name">{L(s.name, lang)}<span className="emm-spox-role mono">{L(s.role, lang)}</span></div>
                <div className="emm-spox-tone" title={t("surroundingTone")}>
                  <div className="seg pos" style={{ width: s.pos + "%" }} />
                  <div className="seg neu" style={{ width: s.neu + "%" }} />
                  <div className="seg neg" style={{ width: s.neg + "%" }} />
                </div>
              </div>
              <div className="emm-spox-stats mono">
                <div className="emm-spox-quotes"><b>{s.quotes}</b> {t("quotes")}</div>
                <div className="emm-spox-share">{s.share}% {t("shareOfQuotes")}
                  <span className={"emm-spox-trend " + (s.trend >= 0 ? "up" : "down")}>{s.trend >= 0 ? "▲" : "▼"}{Math.abs(s.trend)}</span>
                </div>
              </div>
            </div>
          );
        })}
      </div>
    );
  }

  // ---------- Raw mention log (analytics bottom tier) --------
  function RawMentionLog({ rows, lang, onExport }) {
    const { t } = window.useEmmT();
    if (!rows.length) return null;
    return (
      <div className="emm-rawlog">
        <div className="emm-rawlog-row head mono">
          <span>{t("time")}</span><span>{t("channel")}</span><span>{t("colProgramme")}</span>
          <span>{t("colExcerpt")}</span><span>{t("topic")}</span><span>{t("sentiment")}</span><span>{t("reach")}</span>
        </div>
        {rows.map((r) => {
          const ch = E.channel(r.ch);
          return (
            <div key={r.id} className="emm-rawlog-row">
              <span className="mono emm-rawlog-dim">D{r.day + 1} · {r.time}</span>
              <span className="emm-rawlog-ch"><ChannelLogo ch={ch} size={18} /><span className="mono">{ch.name}</span></span>
              <span className="mono emm-rawlog-dim">{r.daypart}</span>
              <span className="emm-rawlog-excerpt">{L(r.snippet, lang)}{r.matched && <span className="emm-rawlog-match mono">PR✓</span>}</span>
              <span className="emm-rawlog-topic">{L(r.topicName, lang)}</span>
              <span><SentDot s={r.sent} /></span>
              <span className="mono emm-rawlog-dim">{C.compact(r.reach)}</span>
            </div>
          );
        })}
      </div>
    );
  }

  // ---------- Reach / airtime split by sentiment — ECharts ---
  function SentMetricSplit({ pos, neu, neg, fmt }) {
    const { t } = window.useEmmT();
    const th = window.chartTheme();
    const total = pos + neu + neg || 1;
    const rows = [
      { k: "pos", label: t("positive"), v: pos, c: th.pos },
      { k: "neu", label: t("neutral"),  v: neu, c: th.neu },
      { k: "neg", label: t("negative"), v: neg, c: th.neg },
    ];
    const base = window.baseOption(th);
    const option = {
      ...base,
      tooltip: { ...base.tooltip, trigger: "item", formatter: (p) => `${p.seriesName}<br/><b>${fmt(p.value)}</b> · ${Math.round((p.value / total) * 100)}%` },
      grid: { left: 0, right: 0, top: 0, bottom: 0 },
      xAxis: { type: "value", show: false, max: total },
      yAxis: { type: "category", show: false, data: ["s"] },
      series: rows.map((r) => ({ name: r.label, type: "bar", stack: "s", barWidth: 14, itemStyle: { color: r.c }, data: [r.v] })),
    };
    return (
      <div className="emm-sentsplit">
        <div style={{ height: 16 }}><EChart option={option} height={16} /></div>
        <div className="emm-sentsplit-rows">
          {rows.map((r) => (
            <div key={r.k} className="emm-sentsplit-row">
              <span className="emm-dot" style={{ background: r.c }} />
              <span className="emm-sentsplit-lab">{r.label}</span>
              <span className="mono emm-sentsplit-val">{fmt(r.v)}</span>
              <span className="mono emm-sentsplit-pct">{Math.round((r.v / total) * 100)}%</span>
            </div>
          ))}
        </div>
      </div>
    );
  }

  // ---------- Channel's own digital & social presence --------
  function ChannelSocial({ rows }) {
    const { t } = window.useEmmT();
    const P = E.PLATFORMS || {};
    return (
      <div className="emm-chsocial">
        <div className="emm-chsocial-row head mono">
          <span>{t("channel")}</span><span>{t("handle")}</span><span>{t("audience")}</span><span>{t("dprViews")}</span>
        </div>
        {rows.map((r) => {
          const pm = P[r.platform] || { abbr: "—", label: r.platform, hue: 260 };
          return (
            <div key={r.platform} className="emm-chsocial-row">
              <span className="emm-chsocial-plat">
                <span className="emm-htcard-plat mono" style={{ background: `oklch(0.94 0.05 ${pm.hue})`, color: `oklch(0.42 0.13 ${pm.hue})` }}>{pm.abbr}</span>
                {pm.label}
              </span>
              <span className="mono emm-chsocial-handle">{r.handle}</span>
              <span className="mono emm-chsocial-aud">{r.followers != null ? C.compact(r.followers) : "—"}</span>
              <span className="mono emm-chsocial-views">{C.compact(r.views)}</span>
            </div>
          );
        })}
      </div>
    );
  }

  // ---------- Ad-spend bars (reach per rupee) — ECharts ------
  function SpendBars({ items, lang }) {
    const th = window.chartTheme();
    const cats = items.map((s) => L(s.label, lang));
    const max = Math.max(...items.map((s) => s.rpr), 1);
    const base = window.baseOption(th);
    const option = {
      ...base,
      tooltip: { ...base.tooltip, trigger: "axis", axisPointer: { type: "shadow" },
        formatter: (ps) => { const s = items[ps[0].dataIndex]; return `${L(s.label, lang)}<br/><b>${s.rpr.toFixed(1)}</b> reach/₹ · ${s.spend}% of budget`; } },
      grid: { left: 4, right: 52, top: 4, bottom: 4, containLabel: true },
      xAxis: { type: "value", show: false, max },
      yAxis: { type: "category", inverse: true, data: cats, axisLine: { show: false }, axisTick: { show: false }, axisLabel: { color: th.ink2, fontSize: 12, width: 132, overflow: "truncate" } },
      series: [{ type: "bar", barMaxWidth: 14, itemStyle: { color: th.accent },
        data: items.map((s) => s.rpr),
        label: { show: true, position: "right", formatter: (p) => items[p.dataIndex].rpr.toFixed(1), color: th.ink, fontFamily: th.mono, fontSize: 11 } }],
    };
    return <EChart option={option} height={Math.max(70, items.length * 32 + 10)} />;
  }

  // ---------- Spreader list (propagation accounts) -----------
  function SpreaderList({ nodes }) {
    const { t } = window.useEmmT();
    const P = E.PLATFORMS || {};
    const TIER = { 0: t("groundZeroNode"), 1: t("amplifierTier"), 2: t("secondaryTier") };
    const rows = nodes.slice().sort((a, b) => b.reach - a.reach);
    return (
      <div className="emm-spreaders">
        <div className="emm-spreader-row head mono">
          <span>{t("account")}</span><span>{t("platform")}</span><span>{t("tierLabel")}</span><span>{t("followers")}</span><span>{t("reach")}</span><span>{t("joinedAt")}</span>
        </div>
        {rows.map((n) => {
          const pm = P[n.platform] || { abbr: "—", hue: 260 };
          return (
            <div key={n.id} className={"emm-spreader-row" + (n.tier === 0 ? " gz" : "")}>
              <span className="emm-spreader-acc">{n.tier === 0 && <span className="emm-gz-dot" title={t("groundZeroNode")}>⌖</span>}{n.handle}</span>
              <span><span className="emm-htcard-plat mono" style={{ background: `oklch(0.94 0.05 ${pm.hue})`, color: `oklch(0.42 0.13 ${pm.hue})` }}>{pm.abbr}</span></span>
              <span className="mono emm-spreader-dim">{TIER[n.tier]}</span>
              <span className="mono">{C.compact(n.followers)}</span>
              <span className="mono emm-spreader-reach">{C.compact(n.reach)}</span>
              <span className="mono emm-spreader-dim">{n.joinMin === 0 ? "—" : "+" + n.joinMin + "m"}</span>
            </div>
          );
        })}
      </div>
    );
  }

  // ---------- Relationship strip (pivot chain) ---------------
  function RelationshipStrip({ item, lang, onPivot }) {
    const { t } = window.useEmmT();
    const ch = item.channel && E.channel(item.channel);
    const an = item.anchor && E.journalist(item.anchor);
    const tp = item.topic && E.topic(item.topic);
    const cat = item.category && E.category(item.category);
    const SENT = { pos: t("positive"), neu: t("neutral"), neg: t("negative") };
    const AUTHL = { genuine: t("authGenuine"), unverified: t("authUnverified"), disputed: t("authDisputed"), fake: t("authFake") };
    const chips = [
      ch && { k: "channel", dim: t("dimChannel"), val: ch.name, id: item.channel },
      an && { k: "anchor", dim: t("dimAnchor"), val: L({ en: an.name, hi: an.hi }, lang), id: item.anchor },
      tp && { k: "topic", dim: t("dimTopic"), val: L(tp, lang), id: item.topic },
      cat && { k: "category", dim: t("dimCategory"), val: L(cat, lang), id: item.category },
      item.sentiment && { k: "sentiment", dim: t("dimSentiment"), val: SENT[item.sentiment], id: item.sentiment },
      item.authenticity && { k: "authenticity", dim: t("dimAuthenticity"), val: AUTHL[item.authenticity], id: item.authenticity },
    ].filter(Boolean);
    return (
      <div className="emm-relstrip">
        <span className="emm-relstrip-cap mono">{t("relationships")}</span>
        {chips.map((c, i) => (
          <React.Fragment key={c.k}>
            {i > 0 && <span className="emm-relstrip-sep">→</span>}
            <button className="emm-relchip" onClick={onPivot ? () => onPivot(c.k, c.id) : undefined} title={t("pivotHint")}>
              <span className="emm-relchip-dim mono">{c.dim}</span>
              <span className="emm-relchip-val">{c.val}</span>
            </button>
          </React.Fragment>
        ))}
      </div>
    );
  }

  // ---------- Report card ------------------------------------
  function ReportCard({ report, lang, onGenerate }) {
    const { t } = window.useEmmT();
    return (
      <button className="emm-report-card" onClick={onGenerate}>
        <span className="emm-report-icon">{report.icon}</span>
        <div className="emm-report-title">{L(report.title, lang)}</div>
        <div className="emm-report-desc">{L(report.desc, lang)}</div>
        <span className="emm-report-action mono">{t("generate")} ↓</span>
      </button>
    );
  }

  // ---------- helpers ----------------------------------------
  function niceCeil(v) {
    if (v <= 0) return 1;
    const mag = Math.pow(10, Math.floor(Math.log10(v)));
    const f = v / mag;
    const nice = f <= 1 ? 1 : f <= 2 ? 2 : f <= 5 ? 5 : 10;
    return nice * mag;
  }
  function ticks(max, n) { const out = []; for (let i = 1; i <= n; i++) out.push(Math.round((max / n) * i)); return out; }

  // ---------- Hashtag trending cards (by platform) -----------
  function HashtagCards({ items }) {
    const P = E.PLATFORMS || {};
    return (
      <div className="emm-htcards">
        {items.map((h) => {
          const pm = P[h.platform] || { abbr: "—", label: h.platform, hue: 260 };
          return (
            <div key={h.rank} className="emm-htcard">
              <div className="emm-htcard-top">
                <span className="emm-htcard-plat mono" style={{ background: `oklch(0.94 0.05 ${pm.hue})`, color: `oklch(0.42 0.13 ${pm.hue})` }}>{pm.abbr}</span>
                <span className={"emm-htcard-rank mono" + (h.rank <= 3 ? " top" : "")}>{String(h.rank).padStart(2, "0")}</span>
              </div>
              <div className="emm-htcard-tag">{h.tag}</div>
              <div className="emm-htcard-spark"><Sparkline values={h.spark} width={120} height={26} color={h.trend > 0 ? "var(--pos)" : "var(--neg)"} /></div>
              <div className="emm-htcard-foot">
                <span className="mono emm-htcard-vol">{C.compact(h.vol)}</span>
                <span className={"emm-htcard-trend mono " + (h.trend > 0 ? "up" : "down")}>{h.trend > 0 ? "▲" : "▼"}{h.delta}%</span>
              </div>
            </div>
          );
        })}
      </div>
    );
  }

  Object.assign(window, {
    Donut, DonutLegend, NSSCard, ActiveAlertsCard, StatusPill, EmotionCard,
    ShareBars, TopicSentStack, HashtagList, HashtagCards, WordCloud, LiveFeed, DualTrend,
    VelocityMeter, MisinfoRow, ReportCard,
    FakeNewsDetector, SpokespersonTracker, RawMentionLog,
    SentMetricSplit, ChannelSocial, SpreaderList, RelationshipStrip, SpendBars,
  });
})();
