const { useState: TUS, useEffect: TUE, useMemo: TUM } = React;
const fmtPct = window.fmtPct,
  fmtPrice = window.fmtPrice,
  sectorOrder = window.sectorOrder,
  Sparkline = window.Sparkline;

// ————————————————————————— Theme-level aggregate returns
function themeAggregate(themeId) {
  const theme = window.THEMES.find((t) => t.id === themeId);
  if (!theme) return { series: [], count: 0 };
  const tickers = (theme.tickers || []).filter((t) => window.PRICES[t]);
  if (!tickers.length) return { series: [], count: 0 };
  const len = Math.min(...tickers.map((t) => window.PRICES[t].length));
  const series = [];
  for (let i = 0; i < len; i++) {
    let s = 0;
    tickers.forEach((t) => {
      const ps = window.pcs(window.PRICES[t]);
      s += ps[0] ? ps[i] / ps[0] : 1;
    });
    series.push((s / tickers.length) * 100);
  }
  return { series, count: tickers.length };
}

// ————————————————————————— THESIS BLOCK
function ThemeThesis({ themeId, lang }) {
  const meta = window.themeMeta(themeId);
  const theme = window.THEMES.find((t) => t.id === themeId);
  if (!meta.thesis) return null;
  return (
    <section className="card theme-thesis" style={{ "--tc": theme.color }}>
      <div className="thesis-grid">
        <div className="thesis-main">
          <div className="thesis-eyebrow">
            <span className="tc-dot" />
            {lang === "jp" ? "投資テーゼ" : "Investment thesis"}
          </div>
          <p className="thesis-body">{meta.thesis}</p>
        </div>

        <div className="thesis-col">
          <div className="thesis-col-h">
            <svg
              width="12"
              height="12"
              viewBox="0 0 12 12"
              fill="none"
              stroke="currentColor"
              strokeWidth="1.5"
            >
              <path d="M2 9 L5 5 L7 7 L10 3" />
              <path d="M8 3 L10 3 L10 5" />
            </svg>
            {lang === "jp" ? "ドライバー" : "Drivers"}
          </div>
          <ul className="thesis-list">
            {meta.drivers?.map((d, i) => (
              <li key={i}>{d}</li>
            ))}
          </ul>
        </div>

        <div className="thesis-col">
          <div className="thesis-col-h">
            <svg
              width="12"
              height="12"
              viewBox="0 0 12 12"
              fill="none"
              stroke="currentColor"
              strokeWidth="1.5"
            >
              <circle cx="6" cy="6" r="4" />
              <path d="M6 3 L6 6 L8 7.5" />
            </svg>
            {lang === "jp" ? "カタリスト" : "Catalysts"}
          </div>
          <ul className="thesis-cat">
            {meta.catalysts?.map((c, i) => (
              <li key={i}>
                <span className="cat-d">{c.date}</span>
                <span className="cat-l">{c.label}</span>
              </li>
            ))}
          </ul>
        </div>

        <div className="thesis-col">
          <div className="thesis-col-h warn">
            <svg
              width="12"
              height="12"
              viewBox="0 0 12 12"
              fill="none"
              stroke="currentColor"
              strokeWidth="1.5"
            >
              <path d="M6 1 L11 10 L1 10 Z" />
              <path d="M6 5 L6 7.5" />
              <circle cx="6" cy="9" r="0.5" fill="currentColor" />
            </svg>
            {lang === "jp" ? "リスク" : "Risks"}
          </div>
          <ul className="thesis-list">
            {meta.risks?.map((r, i) => (
              <li key={i}>{r}</li>
            ))}
          </ul>
        </div>
      </div>
    </section>
  );
}

// ————————————————————————— RECENT UPDATES
function RecentUpdates({ themeId, lang }) {
  const theme = window.THEMES.find((t) => t.id === themeId);
  const updates = (theme && theme.recentUpdates) || [];
  if (!updates.length) return null;
  const TYPE_STYLE = {
    evolve: { bg: "oklch(72% 0.14 230 / 0.12)", color: "oklch(72% 0.14 230)" },
    explore: { bg: "oklch(72% 0.14 280 / 0.12)", color: "oklch(72% 0.14 280)" },
    new_narrative: {
      bg: "oklch(76% 0.14 70 / 0.12)",
      color: "oklch(76% 0.14 70)",
    },
  };
  return (
    <section className="card" style={{ padding: "16px 20px" }}>
      <div className="panel-t" style={{ marginBottom: 12 }}>
        {lang === "jp" ? "最近の更新" : "Recent Updates"}
      </div>
      <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
        {updates.slice(0, 5).map((u, i) => {
          const ts = TYPE_STYLE[u.type] || TYPE_STYLE.evolve;
          return (
            <div
              key={i}
              style={{
                display: "flex",
                flexDirection: "column",
                gap: 6,
                padding: "10px 14px",
                background: "var(--bg-1)",
                borderRadius: 8,
                border: "1px solid var(--line)",
                cursor: "pointer",
              }}
              onClick={() => {
                // Navigate to the report if SingleReportView is available
                if (
                  window.ALL_REPORTS &&
                  window.ALL_REPORTS.find((r) => r.slug === u.report)
                ) {
                  // Dispatch via URL
                  const p = new URLSearchParams();
                  p.set("view", "single-report");
                  p.set("slug", u.report);
                  history.pushState(
                    { view: "single-report", reportSlug: u.report },
                    "",
                    location.pathname + "?" + p.toString(),
                  );
                  window.dispatchEvent(
                    new PopStateEvent("popstate", {
                      state: { view: "single-report", reportSlug: u.report },
                    }),
                  );
                }
              }}
            >
              <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
                <span
                  style={{
                    fontFamily: "var(--mono)",
                    fontSize: 11,
                    color: "var(--fg-3)",
                  }}
                >
                  {u.date}
                </span>
                <span
                  style={{
                    fontSize: 10,
                    fontWeight: 600,
                    padding: "2px 7px",
                    borderRadius: 10,
                    background: ts.bg,
                    color: ts.color,
                    textTransform: "uppercase",
                    letterSpacing: "0.04em",
                  }}
                >
                  {u.type}
                </span>
                <span
                  style={{
                    marginLeft: "auto",
                    fontSize: 11,
                    color: "var(--accent)",
                  }}
                >
                  {lang === "jp" ? "レポート →" : "View report →"}
                </span>
              </div>
              <div
                style={{ fontSize: 13, color: "var(--fg-1)", lineHeight: 1.5 }}
              >
                {u.summary}
              </div>
              {u.stocks_updated && u.stocks_updated.length > 0 && (
                <div style={{ fontSize: 11, color: "var(--fg-3)" }}>
                  {lang === "jp" ? "更新:" : "Updated:"}{" "}
                  {u.stocks_updated.join(", ")}
                </div>
              )}
            </div>
          );
        })}
      </div>
    </section>
  );
}

// ————————————————————————— KPI DASHBOARD ROW
function ThemeKPIs({ themeId, lang }) {
  const meta = window.themeMeta(themeId);
  const k = meta.kpis;
  if (!k) return null;
  const row = [
    { k: "YTD", v: k.ytd, pct: true },
    { k: "1M", v: k.m1, pct: true },
    { k: "3M", v: k.m3, pct: true },
    { k: "1Y", v: k.y1, pct: true },
    { k: "Vol", sub: "annualized", v: k.vol, pct: true, neutral: true },
    { k: "vs TOPIX", v: k.vsTopix, pct: true },
    { k: "Sharpe", v: k.sharpe, pct: false, neutral: true },
  ];
  return (
    <section className="card kpi-card">
      <div className="kpi-row">
        {row.map((c, i) => (
          <div className="kpi-cell" key={c.k}>
            <div className="kpi-k">
              {c.k}
              {c.sub && <span className="kpi-sub"> · {c.sub}</span>}
            </div>
            <div
              className="kpi-v"
              style={{
                color: c.neutral
                  ? "var(--fg-0)"
                  : c.v >= 0
                    ? "var(--up)"
                    : "var(--down)",
              }}
            >
              {c.pct ? fmtPct(c.v ?? 0) : (c.v ?? 0).toFixed(2)}
            </div>
            <MiniBar
              v={c.v}
              max={c.k === "Sharpe" ? 2 : 70}
              neutral={c.neutral}
            />
          </div>
        ))}
      </div>
    </section>
  );
}
function MiniBar({ v, max, neutral }) {
  const pct = Math.min(100, (Math.abs(v) / max) * 100);
  const col = neutral ? "var(--fg-2)" : v >= 0 ? "var(--up)" : "var(--down)";
  return (
    <div className="kpi-bar">
      <div className="kpi-bar-center" />
      <div
        className="kpi-bar-fill"
        style={{
          width: pct + "%",
          background: col,
          left: v >= 0 ? "50%" : 50 - pct + "%",
        }}
      />
    </div>
  );
}

// ————————————————————————— COMPARISON TABLE
function ComparisonTable({ themeId, onOpen, lang }) {
  const theme = window.THEMES.find((t) => t.id === themeId);
  const [sortKey, setSortKey] = TUS("mcap");
  const [sortDir, setSortDir] = TUS("desc");
  const cols = [
    { k: "ticker", label: "Ticker", align: "left", w: 70 },
    {
      k: "name",
      label: lang === "jp" ? "銘柄" : "Name",
      align: "left",
      w: 160,
    },
    {
      k: "positioning",
      label: lang === "jp" ? "ポジショニング" : "Positioning",
      align: "left",
      w: 220,
    },
    { k: "conviction", label: "Conviction", align: "center", w: 90 },
    { k: "price", label: "Price", align: "right" },
    { k: "chg", label: "Chg %", align: "right" },
    { k: "mcap", label: "Mcap ¥T", align: "right" },
    { k: "pe", label: "P/E", align: "right" },
    { k: "roe", label: "ROE %", align: "right" },
    { k: "revGrowth", label: "Rev Δ %", align: "right" },
    { k: "opMargin", label: "Op Mgn %", align: "right" },
    { k: "divYield", label: "Div %", align: "right" },
    { k: "spark", label: "60d", align: "center", w: 80, nosort: true },
  ];
  const rows = TUM(() => {
    const data = theme.tickers
      .filter((t) => window.STOCKS[t])
      .map((t) => {
        const s = window.STOCKS[t] || {};
        const m = window.stockMeta(t);
        return {
          t,
          ticker: t.replace(".T", ""),
          name: lang === "jp" ? s.jp : s.name,
          positioning: m.positioning,
          price: s.price,
          chg: s.chg,
          mcap: (s.mcap || 0) / 1000,
          pe: m.pe,
          roe: m.roe,
          revGrowth: m.revGrowth,
          opMargin: m.opMargin,
          divYield: m.divYield,
          conviction: m.conviction,
        };
      });
    data.sort((a, b) => {
      let av = a[sortKey],
        bv = b[sortKey];
      if (sortKey === "conviction") {
        var convOrd = { HIGH: 3, MEDIUM: 2, LOW: 1 };
        av = convOrd[a.conviction] || 0;
        bv = convOrd[b.conviction] || 0;
      }
      if (typeof av === "string")
        return sortDir === "desc"
          ? (bv || "").localeCompare(av || "")
          : (av || "").localeCompare(bv || "");
      return sortDir === "desc" ? bv - av : av - bv;
    });
    return data;
  }, [themeId, sortKey, sortDir, lang]);

  function clickSort(k, nosort) {
    if (nosort) return;
    if (sortKey === k) setSortDir(sortDir === "desc" ? "asc" : "desc");
    else {
      setSortKey(k);
      setSortDir("desc");
    }
  }

  // compute min/max for gradient coloring
  const ranges = TUM(() => {
    const r = {};
    ["chg", "mcap", "pe", "roe", "revGrowth", "opMargin", "divYield"].forEach(
      (k) => {
        const vs = rows
          .map((x) => x[k])
          .filter((v) => typeof v === "number" && !isNaN(v));
        r[k] = { min: Math.min(...vs), max: Math.max(...vs) };
      },
    );
    return r;
  }, [rows]);

  function heatBG(k, v) {
    if (!ranges[k] || typeof v !== "number") return "transparent";
    const { min, max } = ranges[k];
    if (max === min) return "transparent";
    const norm = (v - min) / (max - min);
    // higher-better for most; pe lower-better; divYield higher-better
    const better = k === "pe" ? 1 - norm : norm;
    const alpha = 0.06 + better * 0.16;
    const hue = k === "divYield" ? 210 : 155;
    return `oklch(60% 0.14 ${hue} / ${alpha})`;
  }

  return (
    <section className="card">
      <div className="card-h">
        <div>
          <div className="panel-t">
            {lang === "jp" ? "銘柄比較" : "Cross-member comparison"}
          </div>
          <div className="panel-s">
            {lang === "jp"
              ? "指標をクリックしてソート"
              : "Click any header to sort"}
          </div>
        </div>
      </div>
      <div className="cmp-scroll">
        <table className="cmp">
          <thead>
            <tr>
              {cols.map((c) => (
                <th
                  key={c.k}
                  className={
                    "cmp-th t-" +
                    c.align +
                    (sortKey === c.k ? " on" : "") +
                    (c.nosort ? " nosort" : "")
                  }
                  style={c.w ? { width: c.w } : {}}
                  onClick={() => clickSort(c.k, c.nosort)}
                >
                  <span>{c.label}</span>
                  {sortKey === c.k && (
                    <span className="sort-i">
                      {sortDir === "desc" ? "▼" : "▲"}
                    </span>
                  )}
                </th>
              ))}
            </tr>
          </thead>
          <tbody>
            {rows.map((r, i) => {
              const upC = r.chg >= 0;
              const convColor =
                r.conviction === "HIGH"
                  ? "oklch(68% 0.18 25)"
                  : r.conviction === "MEDIUM"
                    ? "oklch(76% 0.14 70)"
                    : "var(--fg-3)";
              const convBg =
                r.conviction === "HIGH"
                  ? "oklch(68% 0.18 25 / 0.12)"
                  : r.conviction === "MEDIUM"
                    ? "oklch(76% 0.14 70 / 0.12)"
                    : "var(--bg-2)";
              return (
                <tr
                  key={r.t}
                  className="cmp-row"
                  onClick={() => onOpen(r.t)}
                  style={{ animationDelay: i * 30 + "ms" }}
                >
                  <td className="cmp-tk t-left">{r.ticker}</td>
                  <td className="cmp-n t-left">{r.name}</td>
                  <td className="cmp-pos t-left">{r.positioning}</td>
                  <td className="t-center">
                    <span
                      style={{
                        fontSize: 11,
                        fontWeight: 600,
                        padding: "2px 8px",
                        borderRadius: 4,
                        background: convBg,
                        color: convColor,
                      }}
                    >
                      {r.conviction}
                    </span>
                  </td>
                  <td className="t-right cmp-num">{fmtPrice(r.price)}</td>
                  <td
                    className="t-right cmp-num"
                    style={{ color: upC ? "var(--up)" : "var(--down)" }}
                  >
                    {fmtPct(r.chg)}
                  </td>
                  <td
                    className="t-right cmp-num"
                    style={{ background: heatBG("mcap", r.mcap) }}
                  >
                    {r.mcap.toFixed(2)}
                  </td>
                  <td
                    className="t-right cmp-num"
                    style={{ background: heatBG("pe", r.pe) }}
                  >
                    {r.pe ? r.pe.toFixed(1) : "—"}
                  </td>
                  <td
                    className="t-right cmp-num"
                    style={{ background: heatBG("roe", r.roe) }}
                  >
                    {r.roe ? r.roe.toFixed(1) : "—"}
                  </td>
                  <td
                    className="t-right cmp-num"
                    style={{ background: heatBG("revGrowth", r.revGrowth) }}
                  >
                    {(r.revGrowth || 0) >= 0 ? "+" : ""}
                    {(r.revGrowth ?? 0).toFixed(1)}
                  </td>
                  <td
                    className="t-right cmp-num"
                    style={{ background: heatBG("opMargin", r.opMargin) }}
                  >
                    {(r.opMargin ?? 0).toFixed(1)}
                  </td>
                  <td
                    className="t-right cmp-num"
                    style={{ background: heatBG("divYield", r.divYield) }}
                  >
                    {(r.divYield ?? 0).toFixed(1)}
                  </td>
                  <td className="t-center">
                    <Sparkline
                      data={window.pcs(window.PRICES[r.t])?.slice(-60)}
                      width={70}
                      height={22}
                      glow={false}
                    />
                  </td>
                </tr>
              );
            })}
          </tbody>
        </table>
      </div>
    </section>
  );
}
function AnalystBar({ a, total }) {
  const bw = (a.buy / total) * 100,
    hw = (a.hold / total) * 100,
    sw = (a.sell / total) * 100;
  return (
    <div
      className="ab"
      title={`Buy ${a.buy} · Hold ${a.hold} · Sell ${a.sell} · Target ¥${a.target?.toLocaleString()}`}
    >
      <div className="ab-bar">
        <div style={{ width: bw + "%", background: "var(--up)" }} />
        <div style={{ width: hw + "%", background: "var(--fg-3)" }} />
        <div style={{ width: sw + "%", background: "var(--down)" }} />
      </div>
      <div className="ab-nums">
        <span className="up">{a.buy}</span>
        <span>·</span>
        <span>{a.hold}</span>
        <span>·</span>
        <span className="down">{a.sell}</span>
      </div>
    </div>
  );
}

// ————————————————————————— MINI DOSSIER GRID
function DossierGrid({ themeId, onOpen, lang }) {
  const theme = window.THEMES.find((t) => t.id === themeId);
  return (
    <section className="card">
      <div className="card-h">
        <div>
          <div className="panel-t">
            {lang === "jp" ? "銘柄ドシエ" : "Member dossiers"}
          </div>
          <div className="panel-s">
            {lang === "jp"
              ? "クリックで詳細ドロワー"
              : "Click to open theme-contextual detail"}
          </div>
        </div>
      </div>
      <div className="dossier-grid">
        {theme.tickers.map((t, i) => (
          <Dossier key={t} t={t} onOpen={onOpen} lang={lang} i={i} />
        ))}
      </div>
    </section>
  );
}
function Dossier({ t, onOpen, lang, i }) {
  const s = window.STOCKS[t];
  const m = window.stockMeta(t);
  const news = TUM(
    () => window.NEWS.filter((n) => (n.tickers || []).includes(t)).slice(0, 3),
    [t],
  );
  if (!s) return null;
  const up = s.chg >= 0;
  const a = m.analyst;
  const total = a.buy + a.hold + a.sell || 1;
  const upside = a.target && s.price ? (a.target / s.price - 1) * 100 : 0;

  return (
    <div
      className="dossier"
      style={{ animationDelay: i * 40 + "ms" }}
      onClick={() => onOpen(t)}
    >
      <div className="dos-hd">
        <div className="dos-hd-l">
          <div className="dos-tk">{t.replace(".T", "")}</div>
          <div>
            <div className="dos-n">{lang === "jp" ? s.jp : s.name}</div>
            <div className="dos-pos">{m.positioning}</div>
          </div>
        </div>
        <div className="dos-hd-r">
          <div className="dos-px">{fmtPrice(s.price)}</div>
          <div
            className="dos-chg"
            style={{ color: up ? "var(--up)" : "var(--down)" }}
          >
            {fmtPct(s.chg)}
          </div>
        </div>
      </div>

      <div className="dos-thesis">{m.thesis}</div>

      <div className="dos-fin">
        <div>
          <span>Mcap</span>¥{((s.mcap || 0) / 1000).toFixed(2)}T
        </div>
        <div>
          <span>P/E</span>
          {m.pe ? m.pe.toFixed(1) : "—"}
        </div>
        <div>
          <span>ROE</span>
          {m.roe ? m.roe.toFixed(1) + "%" : "—"}
        </div>
        <div>
          <span>Rev Δ</span>
          <span
            style={{ color: m.revGrowth >= 0 ? "var(--up)" : "var(--down)" }}
          >
            {(m.revGrowth || 0) >= 0 ? "+" : ""}
            {(m.revGrowth ?? 0).toFixed(1)}%
          </span>
        </div>
        <div>
          <span>Op Mgn</span>
          {(m.opMargin ?? 0).toFixed(1)}%
        </div>
        <div>
          <span>Div</span>
          {(m.divYield ?? 0).toFixed(1)}%
        </div>
      </div>

      <div className="dos-analyst">
        <div className="dos-analyst-l">
          <div className="dos-analyst-h">
            {lang === "jp" ? "アナリスト" : "Analyst consensus"}
          </div>
          <AnalystBar a={a} total={total} />
        </div>
        <div className="dos-analyst-r">
          <div className="dos-target-l">
            {lang === "jp" ? "目標価格" : "Target"}
          </div>
          <div className="dos-target-v">{fmtPrice(a.target || 0)}</div>
          <div
            className="dos-target-up"
            style={{ color: upside >= 0 ? "var(--up)" : "var(--down)" }}
          >
            {upside >= 0 ? "+" : ""}
            {upside.toFixed(1)}% {lang === "jp" ? "上値余地" : "upside"}
          </div>
        </div>
      </div>

      <div className="dos-news">
        <div className="dos-news-h">
          {lang === "jp" ? "直近ニュース" : "Recent news"}
        </div>
        {news.length === 0 && <div className="dos-news-none">—</div>}
        {news.map((n) => (
          <div key={n.id} className={"dos-news-item imp-" + n.impact}>
            <span className="dos-news-d">{n.date.slice(5, 10)}</span>
            <span className="dos-news-h2">{n.headline}</span>
          </div>
        ))}
      </div>
    </div>
  );
}

// ————————————————————————— CONTEXT DRAWER (theme-contextual stock detail)
function ContextDrawer({ ticker, themeId, onClose, onOpenStock, lang }) {
  if (!ticker) return null;
  const s = window.STOCKS[ticker];
  if (!s) return null;
  const m = window.stockMeta(ticker);
  const theme = window.THEMES.find((t) => t.id === themeId);
  const a = m.analyst;
  const total = a.buy + a.hold + a.sell || 1;
  const up = s.chg >= 0;
  const upside = a.target && s.price ? (a.target / s.price - 1) * 100 : 0;

  // theme peers
  const peers = theme.tickers
    .filter((t) => t !== ticker && window.STOCKS[t])
    .map((t) => {
      const sp = window.STOCKS[t];
      const mp = window.stockMeta(t);
      return {
        t,
        name: lang === "jp" ? sp.jp : sp.name,
        chg: sp.chg,
        mcap: sp.mcap,
        pe: mp.pe,
        revGrowth: mp.revGrowth,
        sector: sp.sector,
      };
    });
  // how stock ranks on each axis
  const allWithMe = [
    ...peers,
    { t: ticker, chg: s.chg, mcap: s.mcap, pe: m.pe, revGrowth: m.revGrowth },
  ];
  function rank(key, higherBetter = true) {
    const sorted = allWithMe
      .slice()
      .sort((a, b) => (higherBetter ? b[key] - a[key] : a[key] - b[key]));
    return sorted.findIndex((x) => x.t === ticker) + 1;
  }
  const ranks = {
    perf: rank("chg", true),
    mcap: rank("mcap", true),
    pe: rank("pe", false),
    growth: rank("revGrowth", true),
  };
  const themeNews = window.NEWS.filter(
    (n) =>
      ((n.themes || []).includes(themeId) ||
        (n.tickers || []).includes(ticker)) &&
      (n.tickers || []).includes(ticker),
  );
  const otherThemeNews = window.NEWS.filter(
    (n) =>
      (n.themes || []).includes(themeId) && !(n.tickers || []).includes(ticker),
  ).slice(0, 4);

  // price series vs theme index
  const pxSeries = window.pcs(window.PRICES[ticker] || []);
  const themeSeries = themeAggregate(themeId).series;
  const len = Math.min(pxSeries.length, themeSeries.length);
  const sNorm = pxSeries
    .slice(0, len)
    .map((v) => (v / (pxSeries[0] || 1)) * 100);
  const tNorm = themeSeries
    .slice(0, len)
    .map((v) => (v / (themeSeries[0] || 1)) * 100);

  return (
    <div className="drawer-overlay" onClick={onClose}>
      <aside
        className="drawer"
        onClick={(e) => e.stopPropagation()}
        style={{ "--tc": theme.color }}
      >
        <div className="drawer-h">
          <div className="drawer-h-l">
            <div className="drawer-crumb">
              <span className="tc-dot" />
              {lang === "jp" ? theme.jp : theme.name}
              <span className="drawer-sep">›</span>
              <span>{lang === "jp" ? "ドシエ" : "Dossier"}</span>
            </div>
            <div className="drawer-title">
              <span className="drawer-tk">{ticker.replace(".T", "")}</span>
              <span className="drawer-n">{lang === "jp" ? s.jp : s.name}</span>
            </div>
            <div className="drawer-pos">{m.positioning}</div>
          </div>
          <div className="drawer-h-r">
            <div className="drawer-px">{fmtPrice(s.price)}</div>
            <div
              className="drawer-chg"
              style={{ color: up ? "var(--up)" : "var(--down)" }}
            >
              {fmtPct(s.chg)}
            </div>
            <button className="drawer-x" onClick={onClose}>
              ✕
            </button>
          </div>
        </div>

        <div className="drawer-body">
          <div className="drawer-sec">
            <div className="drawer-sec-h">
              {lang === "jp" ? "テーゼ" : "Thesis in theme context"}
            </div>
            <div className="drawer-thesis">{m.thesis}</div>
          </div>

          <div className="drawer-sec">
            <div className="drawer-sec-h">
              {lang === "jp" ? "テーマ内順位" : "Ranking within theme"}
            </div>
            <div className="rank-grid">
              <RankCell
                label={lang === "jp" ? "日次パフォ" : "Daily perf"}
                rank={ranks.perf}
                total={theme.tickers.length}
                v={fmtPct(s.chg)}
                better={up}
              />
              <RankCell
                label={lang === "jp" ? "時価総額" : "Market cap"}
                rank={ranks.mcap}
                total={theme.tickers.length}
                v={`¥${((s.mcap || 0) / 1000).toFixed(2)}T`}
                better={true}
              />
              <RankCell
                label="P/E"
                rank={ranks.pe}
                total={theme.tickers.length}
                v={m.pe ? m.pe.toFixed(1) : "—"}
                better={true}
              />
              <RankCell
                label={lang === "jp" ? "売上成長" : "Rev growth"}
                rank={ranks.growth}
                total={theme.tickers.length}
                v={`${(m.revGrowth || 0) >= 0 ? "+" : ""}${(m.revGrowth ?? 0).toFixed(1)}%`}
                better={(m.revGrowth || 0) >= 0}
              />
            </div>
          </div>

          <div className="drawer-sec">
            <div className="drawer-sec-h">
              {lang === "jp"
                ? `${lang === "jp" ? theme.jp : theme.name}指数との相関`
                : `vs ${theme.name} theme index (180d)`}
            </div>
            <RelPerfChart sNorm={sNorm} tNorm={tNorm} tc={theme.color} />
          </div>

          <div className="drawer-sec">
            <div className="drawer-sec-h">
              {lang === "jp"
                ? "バリューチェーン内の位置"
                : "Position in value chain"}
            </div>
            <div className="vc-position">
              {sectorOrder.map((sc, i) => {
                const members = theme.tickers.filter(
                  (t) => window.STOCKS[t]?.sector === sc,
                );
                const isMe = s.sector === sc;
                return (
                  <div key={sc} className={"vcp-col" + (isMe ? " me" : "")}>
                    <div className="vcp-i">{i + 1}</div>
                    <div className="vcp-n">{sc}</div>
                    <div className="vcp-members">
                      {members.map((tk) => (
                        <span
                          key={tk}
                          className={
                            "vcp-chip" + (tk === ticker ? " active" : "")
                          }
                          onClick={(e) => {
                            e.stopPropagation();
                            if (tk !== ticker) onOpenStock(tk);
                          }}
                        >
                          {tk.replace(".T", "")}
                        </span>
                      ))}
                      {members.length === 0 && (
                        <span className="vcp-empty">—</span>
                      )}
                    </div>
                    {i < sectorOrder.length - 1 && (
                      <div className="vcp-arr">›</div>
                    )}
                  </div>
                );
              })}
            </div>
          </div>

          <div className="drawer-sec drawer-sec-2col">
            <div>
              <div className="drawer-sec-h">
                {lang === "jp" ? "アナリスト" : "Analyst consensus"}
              </div>
              <div className="analyst-lg">
                <AnalystBar a={a} total={total} />
                <div className="analyst-tgt">
                  <div>
                    <span>Target</span>
                    {fmtPrice(a.target || 0)}
                  </div>
                  <div>
                    <span>Upside</span>
                    <b
                      style={{
                        color: upside >= 0 ? "var(--up)" : "var(--down)",
                      }}
                    >
                      {upside >= 0 ? "+" : ""}
                      {upside.toFixed(1)}%
                    </b>
                  </div>
                </div>
              </div>
            </div>
            <div>
              <div className="drawer-sec-h">
                {lang === "jp" ? "財務" : "Financials"}
              </div>
              <div className="fin-grid">
                <div>
                  <span>P/E</span>
                  {m.pe ? m.pe.toFixed(1) : "—"}
                </div>
                <div>
                  <span>PEG</span>
                  {m.peg ? m.peg.toFixed(1) : "—"}
                </div>
                <div>
                  <span>ROE</span>
                  {m.roe ? m.roe.toFixed(1) + "%" : "—"}
                </div>
                <div>
                  <span>Rev Δ</span>
                  <b
                    style={{
                      color: m.revGrowth >= 0 ? "var(--up)" : "var(--down)",
                    }}
                  >
                    {(m.revGrowth || 0) >= 0 ? "+" : ""}
                    {(m.revGrowth ?? 0).toFixed(1)}%
                  </b>
                </div>
                <div>
                  <span>Op Mgn</span>
                  {(m.opMargin ?? 0).toFixed(1)}%
                </div>
                <div>
                  <span>Div</span>
                  {(m.divYield ?? 0).toFixed(1)}%
                </div>
              </div>
            </div>
          </div>

          <div className="drawer-sec">
            <div className="drawer-sec-h">
              {lang === "jp" ? "ストック固有ニュース" : "Stock-specific news"}
            </div>
            {themeNews.length === 0 && <div className="dos-news-none">—</div>}
            {themeNews.map((n) => (
              <div key={n.id} className={"news-item imp-" + n.impact}>
                <div className="news-top">
                  <span className="news-time">{(n.date || "").slice(5)}</span>
                  <span className="news-src">{n.source}</span>
                </div>
                <div className="news-h">{n.headline}</div>
              </div>
            ))}
          </div>

          {otherThemeNews.length > 0 && (
            <div className="drawer-sec">
              <div className="drawer-sec-h">
                {lang === "jp"
                  ? "テーマ関連ニュース (他銘柄)"
                  : "Theme news (other members)"}
              </div>
              {otherThemeNews.map((n) => (
                <div key={n.id} className={"news-item imp-" + n.impact}>
                  <div className="news-top">
                    <span className="news-time">{(n.date || "").slice(5)}</span>
                    <span className="news-src">{n.source}</span>
                  </div>
                  <div className="news-h">{n.headline}</div>
                  <div className="news-bot">
                    {(n.tickers || []).slice(0, 4).map((t) => (
                      <span key={t} className="news-chip">
                        {t.replace(".T", "")}
                      </span>
                    ))}
                  </div>
                </div>
              ))}
            </div>
          )}

          {(m.irSources || []).length > 0 && (
            <div className="drawer-sec">
              <div className="drawer-sec-h">
                {lang === "jp" ? "公式IR資料" : "Primary IR documentation"}
              </div>
              <div className="ir-src-list">
                {m.irSources.map((src, i) => (
                  <a
                    key={i}
                    href={src.url}
                    target="_blank"
                    rel="noopener noreferrer"
                    className="ir-src-item"
                  >
                    <div className="ir-src-top">
                      {src.type && (
                        <span className={"ir-src-tag ir-src-tag-" + src.type}>
                          {src.type.replace(/_/g, " ")}
                        </span>
                      )}
                      <span className="ir-src-label">{src.label}</span>
                      <span className="ir-src-arr">↗</span>
                    </div>
                    {src.cites && (
                      <div className="ir-src-cites">
                        <span className="ir-src-cites-l">
                          {lang === "jp" ? "引用:" : "Cites:"}
                        </span>{" "}
                        {src.cites}
                      </div>
                    )}
                  </a>
                ))}
              </div>
            </div>
          )}

          <div className="drawer-actions">
            <button className="btn ghost" onClick={onClose}>
              {lang === "jp" ? "閉じる" : "Close"}
            </button>
            <button className="btn primary" onClick={() => onOpenStock(ticker)}>
              {lang === "jp"
                ? "個別銘柄ビューを開く"
                : "Open full stock workspace"}{" "}
              →
            </button>
          </div>
        </div>
      </aside>
    </div>
  );
}

function RankCell({ label, rank, total, v, better }) {
  return (
    <div className="rank-c">
      <div className="rank-l">{label}</div>
      <div className="rank-v">{v}</div>
      <div className="rank-r">
        <span className={rank === 1 ? "gold" : rank <= 3 ? "silver" : ""}>
          #{rank}
        </span>
        <span className="rank-of">of {total}</span>
      </div>
    </div>
  );
}

function RelPerfChart({ sNorm, tNorm, tc }) {
  if (!sNorm || !sNorm.length || !tNorm || !tNorm.length) return null;
  const W = 720,
    H = 160,
    pad = { l: 36, r: 10, t: 10, b: 20 };
  const all = [...sNorm, ...tNorm];
  const min = Math.min(...all, 94),
    max = Math.max(...all, 106);
  const range = max - min || 1;
  const x = (i) =>
    pad.l + (i / Math.max(1, sNorm.length - 1)) * (W - pad.l - pad.r);
  const y = (v) => pad.t + (1 - (v - min) / range) * (H - pad.t - pad.b);
  const pathS = sNorm
    .map(
      (v, i) => (i === 0 ? "M" : "L") + x(i).toFixed(1) + " " + y(v).toFixed(1),
    )
    .join(" ");
  const pathT = tNorm
    .map(
      (v, i) => (i === 0 ? "M" : "L") + x(i).toFixed(1) + " " + y(v).toFixed(1),
    )
    .join(" ");
  const lastS = sNorm[sNorm.length - 1],
    lastT = tNorm[tNorm.length - 1];
  return (
    <svg viewBox={`0 0 ${W} ${H}`} className="rel-chart">
      <line
        x1={pad.l}
        y1={y(100)}
        x2={W - pad.r}
        y2={y(100)}
        stroke="var(--line-faint)"
        strokeDasharray="2 3"
      />
      <text x={pad.l - 4} y={y(100) + 3} textAnchor="end" className="rel-yl">
        100
      </text>
      <path
        d={pathT}
        fill="none"
        stroke={tc}
        strokeWidth="1.5"
        strokeDasharray="3 3"
        opacity="0.7"
      />
      <path d={pathS} fill="none" stroke="var(--fg-0)" strokeWidth="2" />
      <circle cx={x(sNorm.length - 1)} cy={y(lastS)} r="3" fill="var(--fg-0)" />
      <circle cx={x(tNorm.length - 1)} cy={y(lastT)} r="2.5" fill={tc} />
      <text
        x={x(sNorm.length - 1) + 6}
        y={y(lastS) + 3}
        className="rel-lbl"
        fill="var(--fg-0)"
      >
        Stock {lastS.toFixed(0)}
      </text>
      <text
        x={x(tNorm.length - 1) + 6}
        y={y(lastT) + 3}
        className="rel-lbl"
        fill={tc}
      >
        Theme {lastT.toFixed(0)}
      </text>
    </svg>
  );
}

// ————————————————————————— MAIN THEME VIEW (orchestrates the above)
function ThemeView({
  themeId,
  onNode,
  onOpenTheme,
  lang,
  hoverNews,
  setHoverNews,
  onOpenReport,
}) {
  const theme = window.THEMES.find((t) => t.id === themeId);
  const [drawer, setDrawer] = TUS(null);
  if (!theme) return null;

  const stats = TUM(() => {
    const tickers = theme.tickers.filter((t) => window.STOCKS[t]);
    const chgs = tickers.map((t) => window.STOCKS[t].chg);
    const avg = chgs.length ? chgs.reduce((a, b) => a + b, 0) / chgs.length : 0;
    const up = chgs.filter((c) => c >= 0).length;
    const mcap = tickers.reduce((a, t) => a + (window.STOCKS[t].mcap || 0), 0);
    return { count: tickers.length, avg, up, down: tickers.length - up, mcap };
  }, [themeId]);
  const aggSeries = TUM(() => themeAggregate(themeId).series, [themeId]);

  // theme news for bottom panel
  const news = TUM(() => {
    return window.NEWS.filter(
      (n) =>
        (n.themes || []).includes(themeId) ||
        (n.tickers || []).some((tk) => (theme.tickers || []).includes(tk)),
    );
  }, [themeId]);

  return (
    <>
      {/* HERO */}
      <div className="theme-hero" style={{ "--tc": theme.color }}>
        <div className="theme-hero-l">
          <div className="theme-hero-label">
            <span className="tc-dot" />
            {lang === "jp" ? "テーマレポート" : "Theme report"}
          </div>
          <h1 className="theme-hero-n">
            {lang === "jp" ? theme.jp : theme.name}
          </h1>
          <div className="theme-hero-sub">
            {lang === "jp" ? theme.name : theme.jp}
            <span className="dot">·</span>
            {stats.count} {lang === "jp" ? "銘柄" : "stocks"}
            <span className="dot">·</span>¥
            {((stats.mcap || 0) / 1000).toFixed(1)}T{" "}
            {lang === "jp" ? "時価総額" : "aggregate mcap"}
          </div>
          <button className="rep-cta" onClick={onOpenReport}>
            <span className="rep-cta-i">📄</span>
            <span className="rep-cta-l">
              <span className="rep-cta-t">
                {lang === "jp" ? "完全レポートを読む" : "Read full report"}
              </span>
              <span className="rep-cta-s">
                {lang === "jp"
                  ? "DD・出典・銘柄分析"
                  : "Long-form DD · cited evidence · errata"}
              </span>
            </span>
            <span className="rep-cta-arr">→</span>
          </button>
        </div>
        <div className="theme-hero-r">
          <div className="theme-hero-agg">
            <div
              className="theme-hero-chg"
              style={{ color: stats.avg >= 0 ? "var(--up)" : "var(--down)" }}
            >
              {fmtPct(stats.avg)}
            </div>
            <div className="theme-hero-lbl">
              {lang === "jp" ? "平均変化" : "avg daily"}
            </div>
          </div>
          <div className="theme-hero-agg">
            <div className="theme-hero-br">
              <span className="up">{stats.up}</span>
              <span className="sep">/</span>
              <span className="down">{stats.down}</span>
            </div>
            <div className="theme-hero-lbl">
              {lang === "jp" ? "上昇/下降" : "advancers"}
            </div>
          </div>
          <div className="theme-hero-spark">
            <Sparkline
              data={aggSeries.slice(-90)}
              width={180}
              height={54}
              glow={true}
            />
            <div className="theme-hero-lbl">
              {lang === "jp" ? "90日指数" : "90d index"}
            </div>
          </div>
        </div>
      </div>

      {/* THESIS */}
      <ThemeThesis themeId={themeId} lang={lang} />

      {/* RECENT UPDATES */}
      <RecentUpdates themeId={themeId} lang={lang} />

      {/* KPI DASHBOARD */}
      <ThemeKPIs themeId={themeId} lang={lang} />

      {/* COMPARISON TABLE */}
      <ComparisonTable themeId={themeId} onOpen={setDrawer} lang={lang} />

      {/* DOSSIERS */}
      <DossierGrid themeId={themeId} onOpen={setDrawer} lang={lang} />

      {/* CROSS-THEME OVERLAP + NEWS */}
      <section className="bottom-grid">
        <div className="card">
          <div className="card-h">
            <div>
              <div className="panel-t">
                {lang === "jp" ? "クロステーマ露出" : "Cross-theme exposure"}
              </div>
              <div className="panel-s">
                {lang === "jp"
                  ? "他テーマへの共有銘柄"
                  : "Stocks shared with other themes"}
              </div>
            </div>
          </div>
          <div className="xtheme-grid">
            {window.THEMES.filter((t) => t.id !== themeId).map((ot) => {
              const shared = (theme.tickers || []).filter((t) =>
                (ot.tickers || []).includes(t),
              );
              return (
                <button
                  key={ot.id}
                  className="xtheme-row"
                  onClick={() => onOpenTheme(ot.id)}
                >
                  <div className="xtheme-l">
                    <span
                      className="tc-dot"
                      style={{
                        background: ot.color,
                        boxShadow: `0 0 10px ${ot.color}`,
                      }}
                    />
                    <span className="xtheme-n">
                      {lang === "jp" ? ot.jp : ot.name}
                    </span>
                  </div>
                  <div className="xtheme-count-bar">
                    <div
                      className="xtheme-count-fill"
                      style={{
                        width: `${Math.min(100, shared.length * 20)}%`,
                        background: ot.color,
                      }}
                    />
                  </div>
                  <div className="xtheme-shared">
                    {shared.slice(0, 4).map((t) => (
                      <span
                        key={t}
                        className="news-chip"
                        onClick={(e) => {
                          e.stopPropagation();
                          setDrawer(t);
                        }}
                      >
                        {t.replace(".T", "")}
                      </span>
                    ))}
                    {shared.length === 0 && (
                      <span className="xtheme-none">—</span>
                    )}
                  </div>
                  <div className="xtheme-val">{shared.length}</div>
                </button>
              );
            })}
          </div>
        </div>
        <div className="card">
          <div className="news-panel">
            <div className="panel-h">
              <div>
                <div className="panel-t">
                  {lang === "jp" ? "テーマ関連ニュース" : "Theme news feed"}
                </div>
                <div className="panel-s">
                  {news.length} {lang === "jp" ? "件" : "items"}
                </div>
              </div>
            </div>
            <div className="news-list">
              {news.map((n, i) => (
                <div
                  key={n.id}
                  className={"news-item imp-" + n.impact}
                  onMouseEnter={() => setHoverNews(n)}
                  onMouseLeave={() => setHoverNews(null)}
                >
                  <div className="news-top">
                    <span className="news-time">{(n.date || "").slice(5)}</span>
                    <span className="news-src">{n.source}</span>
                  </div>
                  <div className="news-h">{n.headline}</div>
                  <div className="news-bot">
                    {(n.tickers || []).slice(0, 5).map((t) => (
                      <span
                        key={t}
                        className="news-chip"
                        onClick={() => setDrawer(t)}
                        style={{ cursor: "pointer" }}
                      >
                        {t.replace(".T", "")}
                      </span>
                    ))}
                  </div>
                </div>
              ))}
            </div>
          </div>
        </div>
      </section>

      <ContextDrawer
        ticker={drawer}
        themeId={themeId}
        onClose={() => setDrawer(null)}
        onOpenStock={onNode}
        lang={lang}
      />
    </>
  );
}

// View switcher with tabs + searchable theme dropdown
function ViewSwitcher({ view, setView, themeId, setThemeId, lang }) {
  const [open, setOpen] = TUS(false);
  const [q, setQ] = TUS("");
  const wrapRef = React.useRef(null);
  TUE(() => {
    const onDoc = (e) => {
      if (wrapRef.current && !wrapRef.current.contains(e.target))
        setOpen(false);
    };
    document.addEventListener("mousedown", onDoc);
    return () => document.removeEventListener("mousedown", onDoc);
  }, []);
  const cur = window.THEMES.find((t) => t.id === themeId);
  const filtered = window.THEMES.filter((t) => {
    const s = (q || "").toLowerCase();
    return (
      !s ||
      (t.name || "").toLowerCase().includes(s) ||
      (t.jp || "").includes(q) ||
      t.id.includes(s)
    );
  });
  return (
    <div className="view-switcher">
      <div className="seg">
        <button
          className={view === "stock" ? "on" : ""}
          onClick={() => setView("stock")}
        >
          <svg
            width="12"
            height="12"
            viewBox="0 0 12 12"
            fill="none"
            stroke="currentColor"
            strokeWidth="1.5"
          >
            <circle cx="6" cy="6" r="3" />
            <circle cx="6" cy="6" r="5" />
          </svg>
          {lang === "jp" ? "銘柄" : "Stock"}
        </button>
        <button
          className={
            view === "theme" || view === "themes" || view === "report"
              ? "on"
              : ""
          }
          onClick={() => setView("themes")}
        >
          <svg
            width="12"
            height="12"
            viewBox="0 0 12 12"
            fill="none"
            stroke="currentColor"
            strokeWidth="1.5"
          >
            <circle cx="3" cy="3" r="1.5" />
            <circle cx="9" cy="3" r="1.5" />
            <circle cx="3" cy="9" r="1.5" />
            <circle cx="9" cy="9" r="1.5" />
            <line x1="3" y1="3" x2="9" y2="3" />
            <line x1="3" y1="3" x2="3" y2="9" />
            <line x1="9" y1="3" x2="9" y2="9" />
            <line x1="3" y1="9" x2="9" y2="9" />
          </svg>
          {lang === "jp" ? "テーマ" : "Theme"}
        </button>
        <button
          className={view === "reports" ? "on" : ""}
          onClick={() => setView("reports")}
        >
          <svg
            width="12"
            height="12"
            viewBox="0 0 12 12"
            fill="none"
            stroke="currentColor"
            strokeWidth="1.5"
          >
            <rect x="2" y="1.5" width="8" height="9" rx="1" />
            <line x1="3.5" y1="4" x2="8.5" y2="4" />
            <line x1="3.5" y1="6" x2="8.5" y2="6" />
            <line x1="3.5" y1="8" x2="6.5" y2="8" />
          </svg>
          {lang === "jp" ? "レポート" : "Reports"}
        </button>
        <button
          className={view === "news" ? "on" : ""}
          onClick={() => setView("news")}
        >
          <svg
            width="12"
            height="12"
            viewBox="0 0 12 12"
            fill="none"
            stroke="currentColor"
            strokeWidth="1.5"
          >
            <path d="M2 2.5 h6 v7 h-6 z" />
            <line x1="3.5" y1="4" x2="6.5" y2="4" />
            <line x1="3.5" y1="5.5" x2="6.5" y2="5.5" />
            <line x1="3.5" y1="7" x2="5" y2="7" />
            <path d="M8 4.5 h2 v4.5 a1 1 0 0 1 -1 1 h-0.5" />
          </svg>
          {lang === "jp" ? "ニュース" : "News"}
          <span className="seg-live-dot" />
        </button>
        <button
          className={view === "radar" ? "on" : ""}
          onClick={() => setView("radar")}
        >
          <svg
            width="12"
            height="12"
            viewBox="0 0 12 12"
            fill="none"
            stroke="currentColor"
            strokeWidth="1.5"
          >
            <circle cx="6" cy="6" r="5" />
            <circle cx="6" cy="6" r="3" />
            <circle cx="6" cy="6" r="1" />
            <line x1="6" y1="1" x2="6" y2="6" />
          </svg>
          {lang === "jp" ? "レーダー" : "Radar"}
        </button>
      </div>
      {(view === "theme" || view === "report") && cur && (
        <div className="theme-dd" ref={wrapRef}>
          <button
            className="theme-dd-btn"
            style={{ "--tc": cur.color }}
            onClick={() => setOpen((o) => !o)}
          >
            <span className="tc-dot" />
            <span className="theme-dd-cur">
              {lang === "jp" ? cur.jp : cur.name}
            </span>
            <span className="theme-dd-count">{cur.tickers.length}</span>
            <svg
              width="10"
              height="10"
              viewBox="0 0 10 10"
              fill="none"
              stroke="currentColor"
              strokeWidth="1.5"
              style={{
                transform: open ? "rotate(180deg)" : "none",
                transition: "transform .15s",
              }}
            >
              <polyline points="2,4 5,7 8,4" />
            </svg>
          </button>
          {open && (
            <div className="theme-dd-menu">
              <input
                className="theme-dd-search"
                autoFocus
                placeholder={lang === "jp" ? "テーマを検索…" : "Search themes…"}
                value={q}
                onChange={(e) => setQ(e.target.value)}
              />
              <div className="theme-dd-list">
                {filtered.map((th) => {
                  const hasReport = !!window.themeReport(th.id);
                  return (
                    <button
                      key={th.id}
                      className={
                        "theme-dd-item" + (themeId === th.id ? " on" : "")
                      }
                      style={{ "--tc": th.color }}
                      onClick={() => {
                        setThemeId(th.id);
                        setOpen(false);
                        setQ("");
                      }}
                    >
                      <span className="tc-dot" />
                      <span className="theme-dd-i-l">
                        <span className="theme-dd-i-n">
                          {lang === "jp" ? th.jp : th.name}
                        </span>
                        <span className="theme-dd-i-s">
                          {lang === "jp" ? th.name : th.jp} ·{" "}
                          {th.tickers.length}{" "}
                          {lang === "jp" ? "銘柄" : "stocks"}
                        </span>
                      </span>
                      {hasReport && (
                        <span
                          className="theme-dd-i-rep-dot"
                          title={
                            lang === "jp" ? "DDレポート有" : "has DD report"
                          }
                        />
                      )}
                    </button>
                  );
                })}
                {!filtered.length && (
                  <div className="theme-dd-empty">
                    {lang === "jp" ? "見つかりません" : "No themes match"}
                  </div>
                )}
              </div>
            </div>
          )}
        </div>
      )}
    </div>
  );
}

window.ThemeView = ThemeView;
window.ViewSwitcher = ViewSwitcher;
