/* global React, window */
// Radar table screen — row-level actions differentiated by recType.

const { useState, useMemo, useRef, useEffect } = React;

function Radar({ ads, iterationsByAd, onOpenAd, runningTasksByAd, onShowHistory, onPause, onScale, alertsByAd, onOpenPulse, onCloneAd, onCreateAd, newAdScrollTrigger }) {
  const t = window.useT();
  const [tab, setTab] = useState("all");
  const [viewPreset, setViewPreset] = useState("strategist"); // M2 (C-3): role presets
  // Client-side filter state. Each is a Set; empty Set = "no constraint".
  const [filters, setFilters] = useState({ grade: new Set(), type: new Set(), recType: new Set() });
  const tabs = window.radarTabs(ads);

  // Helper that applies filters on top of the tab filter
  function applyFilters(list) {
    return list.filter(a => {
      if (filters.grade.size && !filters.grade.has(a.grade)) return false;
      if (filters.type.size && !filters.type.has(a.type)) return false;
      if (filters.recType.size) {
        const rt = a.type === "video" ? "Video" : a.recType;
        if (!filters.recType.has(rt)) return false;
      }
      return true;
    });
  }
  const activeFilterCount = filters.grade.size + filters.type.size + filters.recType.size;

  const counts = tabs.reduce((m, tt) => (m[tt.id] = applyFilters(ads.filter(tt.filter)).length, m), {});
  const current = tabs.find(tt => tt.id === tab) || tabs[3];
  const filtered = applyFilters(ads.filter(current.filter));

  // Round 4: when a new ad is created via the Raya panel, parent bumps
  // `newAdScrollTrigger`. We switch the bucket to "all" (so the new ad
  // is visible regardless of current filter) and scroll its row into
  // view. The row's just-created-row CSS class handles the fade glow.
  const newRowRef = useRef(null);
  useEffect(() => {
    if (!newAdScrollTrigger) return;
    setTab("all");
    setFilters({ grade: new Set(), type: new Set(), recType: new Set() });
    // Wait a frame for the re-render + class application before scrolling.
    const id = setTimeout(() => {
      if (newRowRef.current && typeof newRowRef.current.scrollIntoView === "function") {
        newRowRef.current.scrollIntoView({ behavior: "smooth", block: "center" });
      }
    }, 80);
    return () => clearTimeout(id);
  }, [newAdScrollTrigger]);

  const totalSpend = filtered.reduce((s, a) => s + a.spend, 0);
  const avgRoas = filtered.length
    ? filtered.reduce((s, a) => s + a.roas, 0) / filtered.length
    : 0;

  return (
    <div style={{ padding: "0 24px 60px" }}>
      {/* Title block — editorial */}
      <div style={{ padding: "32px 0 20px", display: "flex", alignItems: "flex-end", justifyContent: "space-between", gap: 32, flexWrap: "wrap" }}>
        <div style={{ minWidth: 0, flex: "1 1 320px" }}>
          <div className="eyebrow">{t("Performance · Last 14 days", "投放表现 · 最近 14 天")}</div>
          <h1 className="t-display" style={{ margin: "6px 0 0" }}>
            <em>Radar.</em> {t("15 active creatives, 10 ready to act on.", "15 条在跑的创意,10 条该动手了。")}
          </h1>
          <div style={{ marginTop: 10, color: "var(--ink-3)", fontSize: 13, maxWidth: 640 }}>
            {t(
              "Atria scored every active ad against its cohort and surfaced where iteration is highest-leverage. Each row carries its own next action — click a verb to do it, or open the row for the full diagnosis.",
              "Atria 把每一条在跑的广告都跟同类样本对比打了分,挑出最该迭代的那几条。每一行都带着「下一步该做什么」按钮 —— 直接点动词执行,或者展开行看完整诊断。"
            )}
          </div>
        </div>
        <div style={{ display: "flex", gap: 28, alignItems: "flex-end", flexShrink: 0 }}>
          <Stat label={t("Total spend", "总花费")} value={window.fmtMoney(totalSpend)} sub={t("14d", "14 天")} />
          <Stat label={t("Avg ROAS", "平均 ROAS")} value={avgRoas.toFixed(2)} sub={avgRoas >= 1 ? t("▲ profitable", "▲ 盈利") : t("▼ deficit", "▼ 亏损")} subTone={avgRoas >= 1 ? "good" : "bad"} />
          <Stat label={t("Iterations queued", "排队的迭代任务")} value={Object.values(iterationsByAd).reduce((a, b) => a + b, 0)} sub={t("across 2 ads", "分布在 2 条广告上")} />
          {onCreateAd && (
            <button
              data-tour="create-ad-btn"
              className="btn primary"
              onClick={onCreateAd}
              title={t(
                "Create a new ad creative — Raya can do it from one sentence",
                "新建一条广告创意 —— Raya 一句话就能做完"
              )}
              style={{
                alignSelf: "flex-end",
                paddingTop: 9, paddingBottom: 9,
                fontSize: 13,
                marginBottom: 2,
              }}
            >
              <window.Icon name="plus" size={13} />
              {t("Create ad creative", "创建广告创意")}
            </button>
          )}
        </div>
      </div>

      <div className="hr" />

      {/* Tabs */}
      <div data-tour="bucket-tabs" style={{ display: "flex", alignItems: "center", padding: "4px 0", gap: 8, flexWrap: "wrap" }}>
        <div className="tabs" style={{ flex: "1 1 auto", minWidth: 0, flexWrap: "wrap", alignItems: "center" }}>
          {tabs.map(tt => (
            <div key={tt.id} onClick={() => setTab(tt.id)}
              className={"tab " + (tt.id === tab ? "active" : "")}>
              {tt.glyph && <span style={{ color: "var(--accent)" }}>{tt.glyph}</span>}
              <span>{t(tt.label, tt.labelZh || tt.label)}</span>
              <span className="tab-count">{counts[tt.id]}</span>
            </div>
          ))}
          <BucketHelp />
        </div>
        <div style={{ display: "flex", gap: 8, alignItems: "center", flexWrap: "wrap" }}>
          {/* M2 (C-3): role presets replace «Customize columns» as primary control. */}
          <ViewPresetSwitcher value={viewPreset} onChange={setViewPreset} />
          <FilterMenu filters={filters} setFilters={setFilters} activeCount={activeFilterCount} />
        </div>
      </div>

      {/* Table — columns adapt to view preset (M2 / C-3). Wraps in its own
          horizontal scroll container so the table can scroll sideways without
          dragging the whole page along with it on narrow viewports. */}
      <div style={{ marginTop: 12, overflowX: "auto", overflowY: "hidden" }}>
        {(() => {
          const showSubScores = viewPreset === "analyst" || viewPreset === "strategist";
          const showSpend = viewPreset !== "strategist";
          const showRoas = true; // always
          const showCohort = viewPreset === "brand"; // brand mgr cares about cohort spend
          const summaryColSpan = 2 + (showSubScores ? 1 : 0) + 1; // Creative, Diagnosis, [Sub], Grade
          return (
            <table className="radar">
              <thead>
                <tr>
                  <th style={{ width: 240 }}>{t("Creative", "创意")}</th>
                  <th style={{ minWidth: 240 }}>{t("Diagnosis", "诊断")}</th>
                  <th data-tour="grade-col" style={{ width: 90 }}>
                    <window.HelpHeader label={t("Grade", "评级")}>
                      <div className="label" style={{ marginBottom: 6 }}>{t("Overall grade · how it's computed", "总评级 · 怎么算出来的")}</div>
                      <div style={{ marginBottom: 8, lineHeight: 1.55 }}>
                        {t(
                          "A single A–D letter combining four sub-metrics into a percentile-vs-cohort score. \"Cohort\" = your other ads in the same vertical, format, and ~14-day window.",
                          "一个 A–D 字母,把下面 4 项子指标合并成「在同类样本里的百分位」分数。「同类样本」 = 你账号下同类目、同格式、最近 14 天左右的其他广告。"
                        )}
                      </div>
                      <div className="label" style={{ marginBottom: 4 }}>{t("Algorithm", "算法")}</div>
                      <div style={{ fontFamily: "IBM Plex Mono, monospace", fontSize: 10, padding: "6px 8px", background: "var(--bg-deep)", borderRadius: 3, marginBottom: 8, color: "var(--ink-2)", lineHeight: 1.5 }}>
                        score = 0.40·z(ROAS) + 0.25·z(HOOK)<br/>
                        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+ 0.20·z(RET) + 0.15·z(CTR)
                      </div>
                      <div style={{ fontSize: 11, marginBottom: 10, color: "var(--ink-3)", lineHeight: 1.5 }}>
                        {t(
                          "z = (metric − cohort mean) / cohort stdev. Weights are tuned per vertical; ROAS dominates because it's the only metric tied directly to revenue.",
                          "z = (本广告指标 − 同类均值) / 同类标准差。各项权重按类目自动调,ROAS 占最大,因为只有它直接挂钩营收。"
                        )}
                      </div>
                      <div className="label" style={{ marginBottom: 4 }}>{t("Mapping", "等级映射")}</div>
                      <div style={{ display: "flex", flexDirection: "column", gap: 5, fontSize: 11, marginBottom: 10 }}>
                        <span><strong style={{ color: "var(--grade-a)" }}>A</strong> — {t("score ≥ +1.0σ · top decile · scale spend", "得分 ≥ +1.0σ · 前 10% · 该加预算")}</span>
                        <span><strong style={{ color: "var(--grade-b)" }}>B</strong> — {t("0 ≤ score < +1.0σ · top quartile · keep running", "0 ≤ 得分 < +1.0σ · 前 25% · 维持投放")}</span>
                        <span><strong style={{ color: "var(--grade-c)" }}>C</strong> — {t("−1.0σ ≤ score < 0 · below median · iterate soon", "−1.0σ ≤ 得分 < 0 · 中位以下 · 尽快迭代")}</span>
                        <span><strong style={{ color: "var(--grade-d)" }}>D</strong> — {t("score < −1.0σ · bottom quartile · pause or rework", "得分 < −1.0σ · 后 25% · 暂停或重做")}</span>
                      </div>
                      <div style={{ fontSize: 10, color: "var(--ink-3)", lineHeight: 1.5, borderTop: "1px solid var(--rule)", paddingTop: 6 }}>
                        {t(
                          "Industry refs: Meta Quality / Engagement / Conversion Rate Ranking · Google Ads Quality Score (1–10) · creative-analytics tools like Motion, Pencil, Marpipe.",
                          "行业参照:Meta 的 Quality / Engagement / Conversion Rate Ranking · Google Ads Quality Score(1–10)· 创意分析工具 Motion / Pencil / Marpipe 的打分体系。"
                        )}
                      </div>
                    </window.HelpHeader>
                  </th>
                  {showSubScores && (
                    <th style={{ width: 200 }}>
                      <window.HelpHeader label={t("Sub-scores", "子评分")}>
                        <div className="label" style={{ marginBottom: 6 }}>{t("The four sub-metrics", "4 项子指标")}</div>
                        <div style={{ marginBottom: 10, lineHeight: 1.5 }}>
                          {t(
                            "Each metric is graded A–D against your cohort (not absolute thresholds). Same statistical machinery as Meta's three-axis Ad Ranking system — top decile is A, top quartile is B, below-median is C, bottom quartile is D.",
                            "每一项都是跟「同类样本」比出来的 A–D,不是绝对阈值。和 Meta 的三轴 Ad Ranking 一个套路 —— 前 10% 是 A,前 25% 是 B,中位以下是 C,后 25% 是 D。"
                          )}
                        </div>
                        <div className="label" style={{ marginBottom: 6, color: "var(--ink-3)" }}>{t("For image ads", "图片广告")}</div>
                        <div style={{ display: "flex", flexDirection: "column", gap: 6, fontSize: 11, marginBottom: 10 }}>
                          <span><strong>ROAS</strong> — {t("Return on Ad Spend · revenue ÷ spend · 14-day attribution window.", "广告投资回报率 = 营收 ÷ 花费 · 14 天归因窗口。")}</span>
                          <span><strong>HOOK</strong> — {t("Hook rate · % who didn't scroll past in the first impression. Cohort median ≈ 28–35%.", "钩住率 · 首次展示没被划走的比例。同类中位数大约 28–35%。")}</span>
                          <span><strong>RET</strong> — {t("Retention · landing-page time-on-page ÷ cohort median. Click survives ≥ 3s.", "停留率 · 落地页停留时间 ÷ 同类中位数。点击后停留 ≥ 3 秒才算。")}</span>
                          <span><strong>CTR</strong> — {t("Click-through rate = clicks ÷ impressions.", "点击率 = 点击数 ÷ 曝光数。")}</span>
                        </div>
                        <div className="label" style={{ marginBottom: 6, color: "var(--ink-3)" }}>{t("For video ads", "视频广告")}</div>
                        <div style={{ display: "flex", flexDirection: "column", gap: 6, fontSize: 11, marginBottom: 8 }}>
                          <span><strong>HOOK</strong> — {t("3-sec view rate (Meta's standard).", "3 秒播放率(Meta 的标准定义)。")}</span>
                          <span><strong>RET</strong> — {t("Average watch time as % of total length; 50% mark is common cohort median.", "平均观看时长 ÷ 视频总长。50% 完播是常见的同类中位数。")}</span>
                          <span><strong>CTR</strong> — {t("Link clicks ÷ impressions. Lower base rate than image — cohort-relative scoring still applies.", "链接点击 ÷ 曝光数。绝对值通常低于图片,但是因为是「跟同类比」所以一样能区分好坏。")}</span>
                          <span><strong>ROAS</strong> — {t("Same as image; attribution still runs through the click + landing page.", "和图片一样,归因还是从点击 + 落地页走。")}</span>
                        </div>
                        <div style={{ fontSize: 10, color: "var(--ink-3)", lineHeight: 1.5, borderTop: "1px solid var(--rule)", paddingTop: 6 }}>
                          {t(
                            "Industry refs: Motion's Hook + Hold + CTR triad · Pencil's creative quality score · Meta video objective metrics.",
                            "行业参照:Motion 的 Hook + Hold + CTR 三件套 · Pencil 的创意质量分 · Meta 视频目标的标准指标。"
                          )}
                        </div>
                      </window.HelpHeader>
                    </th>
                  )}
                  {showSpend && (
                    <th style={{ width: 90 }} className="num">{t("Spend", "花费")}</th>
                  )}
                  {showRoas && (
                    <th style={{ width: 60 }} className="num">
                      <window.HelpHeader label="ROAS">
                        <div className="label" style={{ marginBottom: 6 }}>{t("ROAS · Return on Ad Spend", "ROAS · 广告投资回报率")}</div>
                        <div style={{ marginBottom: 8, lineHeight: 1.6 }}>
                          {t(
                            "For every $1 you spend on this ad, how much revenue comes back. ROAS = 3.53 means $1 in → $3.53 out → $2.53 profit.",
                            "每花 1 元广告费,能挣回多少营收。ROAS = 3.53 表示「花 1 元 → 挣回 3.53 元 → 净赚 2.53 元」。"
                          )}
                        </div>
                        <div className="label" style={{ marginBottom: 4 }}>{t("Rule of thumb", "经验值")}</div>
                        <div style={{ display: "flex", flexDirection: "column", gap: 4, fontSize: 11, marginBottom: 8 }}>
                          <span>{t("≥ 2.0 — healthy, profitable", "≥ 2.0 —— 健康,盈利")}</span>
                          <span>{t("1.0 – 2.0 — breaking even, marginally profitable", "1.0 – 2.0 —— 刚回本,微利")}</span>
                          <span>{t("< 1.0 — losing money on each conversion", "< 1.0 —— 每次转化都在亏")}</span>
                        </div>
                        <div style={{ fontSize: 10, color: "var(--ink-3)", lineHeight: 1.55, borderTop: "1px solid var(--rule)", paddingTop: 6 }}>
                          {t(
                            "Formula: revenue ÷ spend, using a 14-day attribution window (any conversion within 14 days of the click counts toward this ad).",
                            "算法:营收 ÷ 花费;14 天归因窗口(用户点了广告之后 14 天内的转化都算给这条广告)。"
                          )}
                        </div>
                      </window.HelpHeader>
                    </th>
                  )}
                  {showCohort && (
                    <th style={{ width: 90 }} className="num">
                      <window.HelpHeader label={t("Cohort %", "同类占比")}>
                        <div className="label" style={{ marginBottom: 6 }}>{t("Cohort % · share of peer-group budget", "同类占比 · 这条广告吃掉了多少同类预算")}</div>
                        <div style={{ marginBottom: 8, lineHeight: 1.6 }}>
                          {t(
                            "«Cohort» = your other ads in the same vertical and format. 17% means this single ad is eating 17% of the budget being spent on its peer group.",
                            "「同类」 = 你账号下同类目、同形式的其他广告。17% = 这条广告吃掉了同类预算的 17%。"
                          )}
                        </div>
                        <div className="label" style={{ marginBottom: 4 }}>{t("Why it matters", "为什么要看这个")}</div>
                        <div style={{ marginBottom: 8, fontSize: 11, lineHeight: 1.55 }}>
                          {t(
                            "Useful for brand managers: helps you see if one ad has become a single point of failure. If a winner takes >30% of cohort spend and starts slipping, the whole brand line slips with it.",
                            "品牌经理重点看 —— 帮你看「鸡蛋是不是都放在一个篮子里」。某条赢家占了同类预算 >30%、又开始失速时,整盘品牌投放都会被它拖下去。"
                          )}
                        </div>
                        <div style={{ fontSize: 10, color: "var(--ink-3)", lineHeight: 1.55, borderTop: "1px solid var(--rule)", paddingTop: 6 }}>
                          {t(
                            "Only shown in the «Brand manager» view preset (top-right view switcher).",
                            "只在「品牌经理」视图下显示(右上角视图切换器)。"
                          )}
                        </div>
                      </window.HelpHeader>
                    </th>
                  )}
                  <th data-tour="action-col" style={{ width: 200, textAlign: "right" }}>
                    <span style={{ display: "inline-flex", alignItems: "center", gap: 4 }}>
                      <window.HelpHeader label={t("Next action", "下一步动作")} align="right">
                        <div className="label" style={{ marginBottom: 6 }}>{t("Next action · what Atria suggests doing", "下一步动作 · Atria 建议你做什么")}</div>
                        <div style={{ marginBottom: 8, lineHeight: 1.6 }}>
                          {t(
                            "The button on each row is Atria's recommendation, derived from the row's Grade + Diagnosis. Click to do it. Don't agree? Ignore it — nothing happens automatically.",
                            "每一行的按钮 = Atria 根据这一行的「评级 + 诊断」推出来的建议动作。同意?点按钮就执行。不同意?不点就行,系统不会自动跑。"
                          )}
                        </div>
                        <div className="label" style={{ marginBottom: 4 }}>{t("Five possible verbs", "5 种可能的动作")}</div>
                        <div style={{ display: "flex", flexDirection: "column", gap: 5, fontSize: 11, marginBottom: 8, lineHeight: 1.55 }}>
                          <span><strong style={{ color: "var(--grade-d)" }}>{t("Pause", "暂停")}</strong> — {t("when Grade = D. Stop spending on a losing ad.", "评 D 时建议。别再给亏钱的广告烧预算。")}</span>
                          <span><strong style={{ color: "var(--grade-a)" }}>{t("Scale +30%", "扩量 +30%")}</strong> — {t("when Grade = A. Pour more budget into a winner.", "评 A 时建议。给赢家多 30% 预算。")}</span>
                          <span><strong style={{ color: "var(--grade-b)" }}>{t("Clone", "克隆")}</strong> — {t("for A / B winners. Copy as a new test before it decays.", "A / B 级赢家适用。趁势退之前拷一份做新测试。")}</span>
                          <span><strong style={{ color: "var(--accent)" }}>{t("Iterate", "迭代")}</strong> — {t("when Grade = C. AI generates 4 variants for you to compare.", "评 C 时建议。AI 出 4 个改版给你挑。")}</span>
                          <span><strong style={{ color: "var(--accent)" }}>{t("Refresh", "换素材")}</strong> — {t("when the visual itself looks fatigued. Swap the image / video entirely.", "图/视频本身审美疲劳时建议。整个换掉。")}</span>
                        </div>
                        <div style={{ fontSize: 10, color: "var(--ink-3)", lineHeight: 1.55, borderTop: "1px solid var(--rule)", paddingTop: 6 }}>
                          {t(
                            "Click «Why?» on any row to see Atria's reasoning before you act — it opens the detail page on the «Performance» tab.",
                            "点每行的「为什么?」可以先看 Atria 的推导依据再决定 —— 它打开详情页并落在「投放表现」tab。"
                          )}
                        </div>
                      </window.HelpHeader>
                    </span>
                  </th>
                </tr>
              </thead>
              <tbody>
                {(() => {
                  // The most-recent Raya-created ad gets a ref so we can
                  // scroll it into view when newAdScrollTrigger bumps.
                  const newestCreatedId = filtered.find(a => a._isRayaCreated)?.id;
                  return filtered.map(a => (
                  <RadarRow key={a.id} ad={a}
                    rowRef={a.id === newestCreatedId ? newRowRef : null}
                    iterations={iterationsByAd[a.id] || 0}
                    runningCount={runningTasksByAd[a.id] || 0}
                    alerts={alertsByAd ? alertsByAd[a.id] : null}
                    onOpen={(tab) => onOpenAd(a.id, tab)}
                    onShowHistory={() => onShowHistory(a.id)}
                    onPause={() => onPause(a.id)}
                    onScale={() => onScale(a.id)}
                    onClone={() => onCloneAd && onCloneAd(a.id)}
                    onOpenPulse={onOpenPulse}
                    showSubScores={showSubScores}
                    showSpend={showSpend}
                    showRoas={showRoas}
                    showCohort={showCohort}
                  />
                  ));
                })()}
                <tr className="summary">
                  <td colSpan={summaryColSpan}>
                    <span className="label">{t("Net results", "汇总")} · {t(current.label, current.labelZh || current.label)}</span>
                  </td>
                  {showSpend && (
                    <td className="num mono">{window.fmtMoney(totalSpend)}</td>
                  )}
                  {showRoas && (
                    <td className="num mono">{avgRoas.toFixed(2)}</td>
                  )}
                  {showCohort && <td className="num mono">—</td>}
                  <td />
                </tr>
              </tbody>
            </table>
          );
        })()}
      </div>
    </div>
  );
}

// Tab bucket help — small ? next to the tab strip explaining how rows are
// bucketed and what the recommended «next move» is on each tab.
function BucketHelp() {
  const t = window.useT();
  return (
    <span style={{ display: "inline-flex", alignItems: "center", marginLeft: 2 }}>
      <window.HelpHeader label="">
        <div className="label" style={{ marginBottom: 6 }}>{t("How rows are bucketed", "这几个分组怎么分出来的")}</div>
        <div style={{ marginBottom: 10, lineHeight: 1.55, fontSize: 12, color: "var(--ink-2)" }}>
          {t(
            "Buckets are derived from the overall Grade column — same A–D as that ? popover explains. Use these tabs to filter the table to a specific health band.",
            "分组直接来自上面那一列「评级」—— 跟那个 ? 解释的是同一套 A–D 分数。这几个 tab 就是按健康度区间快筛表格。"
          )}
        </div>
        <div style={{ display: "flex", flexDirection: "column", gap: 8, fontSize: 11 }}>
          <div>
            <strong style={{ color: "var(--accent)" }}>★ {t("Winners", "赢家")}</strong>
            <span style={{ color: "var(--ink-3)" }}> · Grade A</span>
            <div style={{ color: "var(--ink-2)", marginTop: 2, lineHeight: 1.5 }}>
              {t(
                "Top decile, profitable. Next action: scale spend +30% on the row, or «Clone as new test» to spin off a variant before the trend slips.",
                "前 10%,在赚钱。下一步:在行内直接加 30% 预算,或者点「Clone as new test(克隆做新测试)」拷一个变体出去试,趁势还没退之前再吃一波。"
              )}
            </div>
          </div>
          <div>
            <strong style={{ color: "var(--grade-b)" }}>▴ {t("High iteration potential", "迭代潜力大")}</strong>
            <span style={{ color: "var(--ink-3)" }}> · Grade B</span>
            <div style={{ color: "var(--ink-2)", marginTop: 2, lineHeight: 1.5 }}>
              {t(
                "Above median but not winning yet. One targeted iteration usually flips them to A. Best ROI on your time.",
                "中位以上但还没真的赢。通常一次有的放矢的迭代就能让它们翻到 A。这一类对你时间的回报率最高。"
              )}
            </div>
          </div>
          <div>
            <strong style={{ color: "var(--grade-c)" }}>· {t("Iteration candidates", "待迭代")}</strong>
            <span style={{ color: "var(--ink-3)" }}> · Grade C + D</span>
            <div style={{ color: "var(--ink-2)", marginTop: 2, lineHeight: 1.5 }}>
              {t(
                "Below median. Iterate or pause — leaving them alone burns budget without learning anything.",
                "中位以下。要么迭代要么暂停 —— 放着不动只是白花钱,也学不到东西。"
              )}
            </div>
          </div>
          <div>
            <strong style={{ color: "var(--ink-2)" }}>{t("All ads", "全部广告")}</strong>
            <div style={{ color: "var(--ink-2)", marginTop: 2, lineHeight: 1.5 }}>
              {t(
                "Unfiltered list. Useful with the Filter button next to the View switcher.",
                "不分组的全列表。配合右边的「筛选」按钮做多维筛选最好用。"
              )}
            </div>
          </div>
        </div>
      </window.HelpHeader>
    </span>
  );
}

// Filter menu — client-side checkbox popover. Grade / Type / Diagnosis kind.
function FilterMenu({ filters, setFilters, activeCount }) {
  const t = window.useT();
  const [open, setOpen] = useState(false);
  const ref = useRef(null);
  useEffect(() => {
    if (!open) return;
    const close = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener("mousedown", close);
    return () => document.removeEventListener("mousedown", close);
  }, [open]);

  function toggle(group, value) {
    setFilters(f => {
      const next = new Set(f[group]);
      next.has(value) ? next.delete(value) : next.add(value);
      return { ...f, [group]: next };
    });
  }
  function clearAll() {
    setFilters({ grade: new Set(), type: new Set(), recType: new Set() });
  }

  const groups = [
    {
      key: "grade",
      label: t("Grade", "评级"),
      items: [
        { v: "A", label: "A" },
        { v: "B", label: "B" },
        { v: "C", label: "C" },
        { v: "D", label: "D" },
      ],
    },
    {
      key: "type",
      label: t("Ad format", "广告类型"),
      items: [
        { v: "image", label: t("Image", "图片") },
        { v: "video", label: t("Video", "视频") },
      ],
    },
    {
      key: "recType",
      label: t("Diagnosis", "诊断类型"),
      items: [
        { v: "Iterate", label: t("Iterate", "迭代") },
        { v: "Refresh", label: t("Refresh", "换素材") },
        { v: "Pause",   label: t("Pause", "暂停") },
        { v: "Scale",   label: t("Scale", "扩量") },
        { v: "Video",   label: t("Video · advisory", "视频 · 仅建议") },
      ],
    },
  ];

  return (
    <span ref={ref} style={{ position: "relative" }}>
      <button className={"btn sm " + (activeCount > 0 ? "primary" : "")}
        onClick={() => setOpen(o => !o)}
        style={activeCount > 0 ? {} : null}>
        <window.Icon name="filter" size={12} />
        {t("Filter", "筛选")}
        {activeCount > 0 && (
          <span style={{
            marginLeft: 2,
            minWidth: 16, height: 16, padding: "0 4px",
            display: "inline-grid", placeItems: "center",
            borderRadius: 999,
            background: activeCount > 0 ? "var(--accent-ink)" : "var(--ink)",
            color: activeCount > 0 ? "var(--accent)" : "var(--bg)",
            fontFamily: "IBM Plex Mono, monospace",
            fontSize: 9, fontWeight: 600,
          }}>{activeCount}</span>
        )}
      </button>
      {open && (
        <div className="popover" style={{
          top: "calc(100% + 6px)", right: 0, minWidth: 240, padding: 12,
          fontFamily: "IBM Plex Sans, sans-serif", letterSpacing: 0, textTransform: "none",
          fontSize: 12, fontWeight: 400, color: "var(--ink)", zIndex: 30,
        }}>
          {groups.map((g, gi) => (
            <div key={g.key} style={{
              marginBottom: gi < groups.length - 1 ? 12 : 0,
              paddingBottom: gi < groups.length - 1 ? 10 : 0,
              borderBottom: gi < groups.length - 1 ? "1px solid var(--rule)" : "0",
            }}>
              <div className="label" style={{ marginBottom: 8 }}>{g.label}</div>
              <div style={{ display: "flex", flexWrap: "wrap", gap: 6 }}>
                {g.items.map(it => {
                  const isOn = filters[g.key].has(it.v);
                  return (
                    <button key={it.v}
                      onClick={() => toggle(g.key, it.v)}
                      style={{
                        padding: "4px 9px",
                        border: isOn ? "1.5px solid var(--accent)" : "1px solid var(--rule-strong)",
                        background: isOn ? "var(--accent-tint)" : "var(--card)",
                        color: isOn ? "var(--accent)" : "var(--ink-2)",
                        fontSize: 11,
                        fontWeight: isOn ? 600 : 500,
                        borderRadius: 3,
                        cursor: "pointer",
                      }}
                    >{it.label}</button>
                  );
                })}
              </div>
            </div>
          ))}
          <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginTop: 12, paddingTop: 10, borderTop: "1px solid var(--rule)" }}>
            <span className="mono" style={{ fontSize: 10, color: "var(--ink-3)" }}>
              {activeCount > 0 ? t(`${activeCount} active`, `已启用 ${activeCount} 项`) : t("No filters", "未启用筛选")}
            </span>
            <button
              onClick={clearAll}
              disabled={activeCount === 0}
              style={{
                fontSize: 11, padding: "4px 10px",
                background: "transparent",
                color: activeCount === 0 ? "var(--ink-4)" : "var(--accent)",
                border: "0", cursor: activeCount === 0 ? "default" : "pointer",
              }}
            >
              {t("Clear all", "全部清空")}
            </button>
          </div>
        </div>
      )}
    </span>
  );
}

// Sub-score cell — calm 2-column layout: label on the left, filled chip on the right.
// Replaces the old pale-letter-in-line treatment that wrapped unpredictably.
function SubScoreCell({ label, value }) {
  const pending = !value;
  const tone =
    value === "A" ? "var(--grade-a)"
    : value === "B" ? "var(--grade-b)"
    : value === "C" ? "var(--grade-c)"
    : value === "D" ? "var(--grade-d)"
    : "var(--ink-3)";
  return (
    <div style={{ display: "flex", alignItems: "center", gap: 6, minWidth: 0 }}>
      <span
        className="mono"
        style={{
          fontSize: 9,
          color: "var(--ink-3)",
          letterSpacing: ".08em",
          flex: "0 0 36px",
        }}
      >
        {label}
      </span>
      <span
        style={{
          display: "inline-grid",
          placeItems: "center",
          width: 18,
          height: 18,
          background: pending ? "var(--bg-deep)" : tone,
          color: pending ? "var(--ink-3)" : "var(--card)",
          border: pending ? "1px dashed var(--rule-strong)" : "0",
          fontFamily: "Source Serif 4, Georgia, serif",
          fontWeight: pending ? 400 : 600,
          fontSize: 11,
          borderRadius: 3,
          lineHeight: 1,
        }}
      >
        {value || "—"}
      </span>
    </div>
  );
}

function Stat({ label, value, sub, subTone }) {
  return (
    <div style={{ minWidth: 120 }}>
      <div className="label">{label}</div>
      <div className="t-h1" style={{ marginTop: 2, fontSize: 32, lineHeight: 1.05 }}>{value}</div>
      <div className="mono" style={{ fontSize: 11, color: subTone === "good" ? "var(--grade-a)" : subTone === "bad" ? "var(--grade-d)" : "var(--ink-3)", marginTop: 2 }}>{sub}</div>
    </div>
  );
}

function RadarRow({ ad, iterations, runningCount, alerts, onOpen, onShowHistory, onPause, onScale, onClone, onOpenPulse, showSubScores = true, showSpend = true, showRoas = true, showCohort = false, rowRef }) {
  const { Thumb, Grade, SubGrade, Pill, Icon, fmtMoney, fmtRoas } = window;
  const [pendingAction, setPendingAction] = useState(null); // null | "pause-confirm" | "scale-confirm" | "paused" | "scaled"
  const isVideo = ad.type === "video";
  // Mock cohort spend % — derived deterministically from ad code
  const cohortPct = (((ad.code || "").charCodeAt(0) || 0) % 18) + 2;
  const isWinner = ad.grade === "A";
  const isJustCreated = !!ad._isRayaCreated;
  const className = [
    isWinner ? "winner-row" : "",
    isJustCreated ? "just-created-row" : "",
  ].filter(Boolean).join(" ") || undefined;
  return (
    <tr
      ref={rowRef}
      onClick={() => onOpen()}
      className={className}
      style={pendingAction === "paused" || pendingAction === "scaled" ? { opacity: 0.62 } : null}>
      {/* Creative */}
      <td>
        <div style={{ display: "flex", gap: 12, alignItems: "center" }}>
          <Thumb ad={ad} w={48} h={48} />
          <div style={{ minWidth: 0 }}>
            <div style={{ display: "flex", alignItems: "center", gap: 6 }}>
              <Icon name={ad.type === "video" ? "video" : "image"} size={12} />
              <span style={{ fontSize: 11, color: "var(--ink-3)", fontFamily: "IBM Plex Mono, monospace", letterSpacing: ".06em" }}>{ad.code}</span>
            </div>
            <div className="t-h3" style={{
              marginTop: 2,
              fontSize: 14,
              lineHeight: 1.3,
              maxWidth: 220,
              display: "-webkit-box",
              WebkitLineClamp: 2,
              WebkitBoxOrient: "vertical",
              overflow: "hidden",
              wordBreak: "break-word",
            }}>
              {window.getLang() === "zh" && ad.headlineZh ? ad.headlineZh : ad.headline}
            </div>
          </div>
        </div>
      </td>

      {/* Diagnosis */}
      <td>
        <div style={{ display: "flex", flexDirection: "column", gap: 4 }}>
          <div style={{ display: "flex", alignItems: "center", gap: 6, flexWrap: "wrap" }}>
            {isJustCreated ? (
              <span
                style={{
                  display: "inline-flex", alignItems: "center", gap: 4,
                  padding: "2px 8px",
                  background: "var(--accent)", color: "var(--accent-ink)",
                  fontFamily: "IBM Plex Mono, monospace",
                  fontSize: 10, fontWeight: 600, letterSpacing: ".06em",
                  borderRadius: 3,
                }}
              >
                <Icon name="sparkle" size={9} />
                {window.useT()("JUST CREATED · By Raya", "刚刚创建 · Raya")}
              </span>
            ) : (
              <DiagnosisPill recType={ad.recType} isVideo={isVideo} />
            )}
            {iterations > 0 && (
              <IterationsBadge ad={ad} count={iterations} onClick={(e) => { e.stopPropagation(); onShowHistory(); }} />
            )}
            {alerts && alerts.length > 0 && (
              <PulseAlertBadge
                alerts={alerts}
                onClick={(e) => { e.stopPropagation(); onOpenPulse && onOpenPulse(alerts[0].id); }}
              />
            )}
            {runningCount > 0 && (
              <span style={{ display: "inline-flex", alignItems: "center", gap: 4, fontSize: 10, color: "var(--status-running)", fontFamily: "IBM Plex Mono, monospace" }}>
                <span className="spinner" style={{ width: 10, height: 10 }} /> {runningCount} running
              </span>
            )}
          </div>
          <div style={{ fontSize: 12, color: "var(--ink-2)", lineHeight: 1.35, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis", maxWidth: 320 }}>
            {topDiagnosisLine(ad)}
          </div>
        </div>
      </td>
      <td><Grade value={ad.grade} /></td>
      {showSubScores && (
        <td>
          <div style={{
            display: "grid",
            gridTemplateColumns: "1fr 1fr",
            columnGap: 10,
            rowGap: 6,
            maxWidth: 180,
          }}>
            <SubScoreCell label="ROAS" value={ad.subGrades.roas} />
            <SubScoreCell label="HOOK" value={ad.subGrades.hook} />
            <SubScoreCell label="RET"  value={ad.subGrades.retention} />
            <SubScoreCell label="CTR"  value={ad.subGrades.ctr} />
          </div>
        </td>
      )}
      {showSpend && <td className="num mono">{fmtMoney(ad.spend)}</td>}
      {showRoas && (
        <td className="num mono" style={{ fontWeight: 600, color: ad.roas >= 2 ? "var(--grade-a)" : ad.roas >= 1 ? "var(--ink)" : "var(--grade-d)" }}>
          {fmtRoas(ad.roas)}
        </td>
      )}
      {showCohort && (
        <td className="num mono" style={{ color: "var(--ink-2)" }}>{cohortPct}%</td>
      )}

      {/* Next action — verb-as-button (the single AI action surface, M1/C-2).
          onOpen takes an optional initial tab; the ghost "诊断 / 为什么?"
          button passes "perf" so the drawer lands on Performance instead of
          duplicating the primary verb's behaviour. */}
      <td style={{ textAlign: "right" }} onClick={(e) => e.stopPropagation()}>
        <RowAction ad={ad} pending={pendingAction} setPending={setPendingAction}
          onOpen={(tab) => onOpen(tab)} onPause={onPause} onScale={onScale} onClone={onClone} />
      </td>
    </tr>
  );
}

// M2 (C-3): segmented control for role presets
function ViewPresetSwitcher({ value, onChange }) {
  const t = window.useT();
  const presets = [
    { id: "strategist", label: t("Strategist", "策略师"), title: t("Creative-led columns", "创意为主的列组合") },
    { id: "analyst", label: t("Analyst", "分析师"), title: t("Metrics-heavy columns", "数据为主的列组合") },
    { id: "brand", label: t("Brand mgr", "品牌经理"), title: t("Spend + cohort columns", "花费 + 同类占比") },
  ];
  return (
    <div style={{
      display: "inline-flex",
      border: "1px solid var(--rule-strong)",
      borderRadius: 4,
      overflow: "hidden",
      fontSize: 11,
      background: "var(--card)",
    }}>
      <span className="mono" style={{
        padding: "5px 8px 5px 10px",
        color: "var(--ink-3)",
        fontSize: 9,
        letterSpacing: ".1em",
        textTransform: "uppercase",
        borderRight: "1px solid var(--rule)",
        background: "var(--bg-deep)",
        display: "inline-flex",
        alignItems: "center",
      }}>{t("View", "视图")}</span>
      {presets.map((p, i) => (
        <button key={p.id}
          title={p.title}
          onClick={() => onChange(p.id)}
          style={{
            padding: "5px 10px",
            fontSize: 11,
            background: value === p.id ? "var(--accent)" : "transparent",
            color: value === p.id ? "var(--accent-ink)" : "var(--ink-2)",
            borderLeft: i > 0 ? "1px solid var(--rule)" : "0",
            fontWeight: value === p.id ? 600 : 500,
            cursor: "pointer",
          }}>{p.label}</button>
      ))}
    </div>
  );
}

// M1 (C-2): a single, clearly-informational categorical tag.
// No hover-tooltip duplication, no separate AI Rec button — the verb
// in the Next action column is the only action surface.
// Adds a ✦ glyph for AI-driven verbs so users see at a glance which
// rows involve AI vs. which are platform switches.
function DiagnosisPill({ recType, isVideo }) {
  const t = window.useT();
  const tag = (label, ai) => (
    <span
      className="mono"
      style={{
        display: "inline-flex",
        alignItems: "center",
        gap: 4,
        padding: "1px 7px",
        fontSize: 10,
        letterSpacing: ".08em",
        textTransform: "uppercase",
        color: "var(--ink-3)",
        background: "var(--bg-deep)",
        border: "1px solid var(--rule)",
        borderRadius: 3,
        fontWeight: 500,
      }}
      title={t("Diagnosis type — informational. Action is in the Next action column.", "诊断类型 —— 只是信息标签。动作在右边的「下一步动作」列里。")}
    >
      {ai && (
        <span style={{ color: "var(--accent)", fontSize: 9, lineHeight: 1 }}>
          ✦
        </span>
      )}
      <span>{label}</span>
    </span>
  );
  if (isVideo) return tag(t("Video · advisory", "视频 · 仅建议"), false);
  if (recType === "Scale") return tag(t("Scale", "扩量"), false);
  if (recType === "Pause") return tag(t("Pause", "暂停"), false);
  if (recType === "Refresh") return tag(t("Refresh", "换素材"), true);
  return tag(t("Iterate", "迭代"), true);
}

function topDiagnosisLine(ad) {
  const t = window.t;
  if (ad._isRayaCreated) return t("Just pushed to Meta · data lands after the first delivery window (~2h).", "刚推到 Meta · 等第一个投放窗口跑完(约 2 小时)就有数据。");
  if (ad.type === "video") return t("Advisory only — text recs available · video iteration is on the roadmap.", "仅文字建议 · 视频版本迭代在路线图上。");
  if (ad.recType === "Scale") return t("Cohort converting > ROAS 2.0 · scale spend.", "同类账户 ROAS > 2.0 · 该加预算。");
  if (ad.recType === "Pause") return t("Fatigue + CTR decay > 22%. Pause within 48h.", "审美疲劳 + CTR 衰减 >22% · 48 小时内暂停。");
  if (ad.recType === "Refresh") return t("Frequency ≥ 4.8 on top audience — visual refresh recommended.", "核心受众接触频次 ≥ 4.8 · 建议换视觉。");
  return ((ad.recs || [])[0]?.shortZh && window.getLang() === "zh" ? ad.recs[0].shortZh : (ad.recs || [])[0]?.short)
    || t("Strengthen CTA — passive verbs are leaving CTR on the table.", "强化 CTA —— 被动动词在白白丢失点击率。");
}

function RowAction({ ad, pending, setPending, onOpen, onPause, onScale, onClone }) {
  const { Icon } = window;
  const t = window.useT();

  if (pending === "paused") {
    return <span className="mono" style={{ fontSize: 11, color: "var(--grade-a)" }}>{t("✓ Paused on Meta", "✓ 已在 Meta 暂停")}</span>;
  }
  if (pending === "scaled") {
    return <span className="mono" style={{ fontSize: 11, color: "var(--grade-a)" }}>{t("✓ Budget +30%", "✓ 预算 +30%")}</span>;
  }

  // Round 4: newly-created ads have no recs yet; show a single primary
  // "View on Meta" verb until data lands.
  if (ad._isRayaCreated) {
    return (
      <button
        className="btn sm primary"
        onClick={(e) => { e.stopPropagation(); window.__pushToast && window.__pushToast(t("Opened ad in Meta Ads Manager (demo).", "已在 Meta 广告管理后台打开(演示)"), "success"); }}
        title={t("Open this ad in Meta Ads Manager — data lands here once the first delivery window closes.", "在 Meta 广告管理后台打开这条广告 —— 第一个投放窗口结束后数据会上来。")}
      >
        {t("View on Meta", "在 Meta 查看")} <Icon name="arrow" size={11} />
      </button>
    );
  }

  if (ad.type === "video") {
    return <button className="btn sm" onClick={onOpen}>{t("Read advisory", "查看建议")}<Icon name="arrow" size={11} /></button>;
  }

  if (ad.recType === "Pause") {
    if (pending === "pause-confirm") {
      return (
        <span style={{ display: "inline-flex", gap: 6, alignItems: "center" }}>
          <span className="mono" style={{ fontSize: 10, color: "var(--ink-3)" }}>{t("Sure?", "确认?")}</span>
          <button className="btn sm" onClick={() => setPending(null)}>{t("Cancel", "取消")}</button>
          <button className="btn sm" style={{ background: "var(--grade-d)", color: "white", borderColor: "var(--grade-d)" }}
            onClick={() => { onPause(); setPending("paused"); }}>{t("Yes, pause", "是,暂停")}</button>
        </span>
      );
    }
    return (
      <span style={{ display: "inline-flex", gap: 6 }}>
        <button
          className="btn sm ghost"
          onClick={() => onOpen("perf")}
          title={t("Open the drawer on the Performance tab — see CTR / Hook / RET decay that triggered the Pause recommendation.", "打开抽屉的「投放表现」tab —— 看一下触发暂停建议的 CTR / Hook / RET 数据。")}
        >{t("Why?", "为什么?")}</button>
        <button className="btn sm" onClick={() => setPending("pause-confirm")}>{t("Pause ad", "暂停广告")}</button>
      </span>
    );
  }

  if (ad.recType === "Scale") {
    if (pending === "scale-confirm") {
      return (
        <span style={{ display: "inline-flex", gap: 6, alignItems: "center" }}>
          <span className="mono" style={{ fontSize: 10, color: "var(--ink-3)" }}>{t("+30%?", "+30%?")}</span>
          <button className="btn sm" onClick={() => setPending(null)}>{t("Cancel", "取消")}</button>
          <button className="btn primary sm"
            onClick={() => { onScale(); setPending("scaled"); }}>{t("Confirm", "确认")}</button>
        </span>
      );
    }
    // Clone is a one-shot action (you can only clone the same winner once
    // per session) — but Scale should stay reachable even after cloning.
    // Earlier this branch replaced the whole action area with a success
    // banner, which killed the Scale button. Now we just swap the Clone
    // slot for a small "✓ Cloned" confirmation and keep Scale primary.
    return (
      <span style={{ display: "inline-flex", gap: 6, flexWrap: "wrap", justifyContent: "flex-end", alignItems: "center" }}>
        {pending === "cloned" ? (
          <span className="mono" style={{ fontSize: 10, color: "var(--grade-a)", whiteSpace: "nowrap" }}>
            {t("✓ Cloned", "✓ 已克隆")}
          </span>
        ) : (
          <button className="btn sm ghost"
            onClick={() => { if (onClone) { onClone(); setPending("cloned"); } }}
            title={t("Clone as a new test — spin off a variant before this winner decays", "克隆做新测试 —— 趁这个赢家还没退势,拷一个变体出去试")}>
            <Icon name="refresh" size={11} />{t("Clone as new test", "克隆做新测试")}
          </button>
        )}
        <button className="btn primary sm" onClick={() => setPending("scale-confirm")}>{t("Scale +30%", "扩量 +30%")}</button>
      </span>
    );
  }

  // Iterate / Refresh — open the drawer to choose direction.
  // The ghost "诊断" button lands on the Performance tab (why this ad is
  // failing); the primary verb stays on AI Recommendation (what to do).
  return (
    <span style={{ display: "inline-flex", gap: 6 }}>
      <button
        className="btn sm ghost"
        onClick={() => onOpen("perf")}
        title={t("Open the drawer on the Performance tab — see the data behind this recommendation.", "打开抽屉的「投放表现」tab —— 看一下这条建议背后的数据。")}
      >{t("Diagnose", "诊断")}</button>
      <button
        className="btn primary sm"
        onClick={() => onOpen("airec")}
        title={t("Open AI Recommendation and start the action.", "打开「AI 建议」面板,直接开干。")}
      >
        <Icon name="bolt" size={11} />
        {ad.recType === "Refresh" ? t("Refresh", "换素材") : t("Iterate", "迭代")}
      </button>
    </span>
  );
}

function IterationsBadge({ ad, count, onClick }) {
  const [hover, setHover] = useState(false);
  const { Thumb, Pill } = window;
  const t = window.useT();
  return (
    <span style={{ position: "relative", display: "inline-block" }}
      onMouseEnter={() => setHover(true)} onMouseLeave={() => setHover(false)}>
      <button onClick={onClick}
        style={{
          display: "inline-flex", alignItems: "center", gap: 4,
          padding: "2px 8px", borderRadius: 999,
          background: "var(--ink)", color: "var(--bg)", border: "1px solid var(--ink)",
          fontFamily: "IBM Plex Mono, monospace", fontSize: 9, letterSpacing: ".08em", textTransform: "uppercase",
          cursor: "pointer",
        }}>{count} {t("iterations →", "次迭代 →")}</button>
      {hover && (
        <div className="popover" style={{ left: 0, top: "calc(100% + 6px)", minWidth: 200 }}>
          <div className="label" style={{ marginBottom: 8 }}>{count} {t("iterations · click to compare", "次迭代 · 点击对比")}</div>
          <div style={{ display: "flex", gap: 6 }}>
            {Array.from({ length: Math.min(count, 4) }).map((_, i) => (
              <div key={i} style={{ display: "flex", flexDirection: "column", gap: 4 }}>
                <Thumb ad={{ code: "IT-" + (i+1), type: "image" }} w={48} h={64} />
                <div className="mono" style={{ fontSize: 9, color: "var(--ink-3)", textAlign: "center" }}>{i === 0 ? t("PUSHED", "已发布") : t("DRAFT", "草稿")}</div>
              </div>
            ))}
          </div>
        </div>
      )}
    </span>
  );
}

// Pulse alert indicator on Radar rows. Click → jump to Pulse + focus that alert.
function PulseAlertBadge({ alerts, onClick }) {
  const [hover, setHover] = useState(false);
  const t = window.useT();
  const topSev = alerts.find(a => a.severity === "critical")
    || alerts.find(a => a.severity === "warn")
    || alerts[0];
  const color =
    topSev.severity === "critical" ? "var(--grade-d)"
    : topSev.severity === "warn" ? "var(--grade-b)"
    : "var(--status-running)";
  return (
    <span style={{ position: "relative", display: "inline-block" }}
      onMouseEnter={() => setHover(true)} onMouseLeave={() => setHover(false)}>
      <button onClick={onClick} title={t("Open in Pulse", "在 Pulse 中打开")}
        style={{
          display: "inline-flex", alignItems: "center", gap: 4,
          padding: "2px 7px", borderRadius: 999,
          background: color, color: "var(--bg)", border: `1px solid ${color}`,
          fontFamily: "IBM Plex Mono, monospace", fontSize: 9, fontWeight: 600,
          letterSpacing: ".08em", textTransform: "uppercase",
          cursor: "pointer",
        }}>
        <span>⚡</span> {alerts.length} {t(alerts.length === 1 ? "alert" : "alerts", "条告警")}
      </button>
      {hover && (
        <div className="popover" style={{ left: 0, top: "calc(100% + 6px)", minWidth: 260, padding: 10 }}>
          <div className="label" style={{ marginBottom: 6 }}>
            {alerts.length} {t(alerts.length > 1 ? "active Pulse alerts" : "active Pulse alert", "条 Pulse 告警在跑")}
          </div>
          <div style={{ display: "flex", flexDirection: "column", gap: 6 }}>
            {alerts.slice(0, 3).map(a => (
              <div key={a.id} style={{
                fontSize: 11, color: "var(--ink-2)",
                display: "flex", alignItems: "flex-start", gap: 6,
                lineHeight: 1.4,
              }}>
                <span style={{
                  marginTop: 4, width: 6, height: 6, borderRadius: "50%",
                  background: a.severity === "critical" ? "var(--grade-d)"
                    : a.severity === "warn" ? "var(--grade-b)"
                    : "var(--status-running)",
                  flexShrink: 0,
                }} />
                <span>{a.title}</span>
              </div>
            ))}
          </div>
          <div className="mono" style={{ fontSize: 9, color: "var(--accent)", marginTop: 8, letterSpacing: ".08em" }}>
            Click to open in Pulse →
          </div>
        </div>
      )}
    </span>
  );
}

Object.assign(window, { Radar });
