// Helm — 個股健檢(台股)。前端直連 FinMind(window.HelmStockData)+ 規則引擎(window.HelmStockScore)。
//   誠實設計:不可靠不給綠燈、警語在上、每指標可點「這是什麼」、盲區免責在下。教材型、非投資建議。
(function () {
  const NS = window.HelmDesignSystem_9613a7;
  const { Field, Input, Button } = NS;

  const DOT = { green: "var(--value-positive)", yellow: "var(--accent-brass)", red: "var(--value-negative)", gray: "var(--text-tertiary)" };
  const CAT_LABEL = { valuation: "貴不貴?", profit: "會賺嗎?", growth: "在長大嗎?", health: "會倒嗎?" };
  // 每面向一句白話「所以呢」(回答 CAT_LABEL),依燈號——讓新手不必自己解讀數字
  const CAT_VERDICT = {
    valuation: { green: "看起來偏便宜", yellow: "估值中等", red: "偏貴,別追高" },
    profit: { green: "很會賺錢", yellow: "會賺、但普通", red: "賺錢能力偏弱" },
    growth: { green: "有在成長", yellow: "成長平平", red: "在縮水或沒成長" },
    health: { green: "財務穩、短期不會倒", yellow: "財務尚可", red: "財務有壓力,留意" },
  };
  // 同業綜合判讀:賺錢面贏幾項 + 估值面偏貴/便宜 → 一句話(positionTag 的精神、用同業實際數字講)
  function peerSynth(m, ind) {
    if (!ind) return null;
    var win = 0, tot = 0;
    [["gross", "medGross"], ["opm", "medOpm"], ["yield", "medYield"]].forEach(function (p) { if (m[p[0]] != null && ind[p[1]] != null) { tot++; if (m[p[0]] > ind[p[1]]) win++; } });
    var exp = 0, cheap = 0;
    [["pe", "medPE"], ["pb", "medPB"]].forEach(function (p) { if (m[p[0]] != null && ind[p[1]] != null) { if (m[p[0]] > ind[p[1]] * 1.1) exp++; else if (m[p[0]] < ind[p[1]] * 0.9) cheap++; } });
    var valTxt = exp > cheap ? "估值比同業貴" : cheap > exp ? "估值比同業便宜" : "估值跟同業差不多";
    var tail = (win >= 2 && exp > 0) ? " → 偏「好公司但不便宜」" : (win <= 1 && cheap > 0) ? " → 便宜可能有原因,先看為什麼" : (win >= 2 && cheap > 0) ? " → 偏「又好又相對便宜」(難得,先想市場在擔心什麼)" : "";
    return "跟同業比:" + (tot ? "賺錢能力 " + tot + " 項中贏 " + win + " 項、" : "") + valTxt + "。" + tail;
  }
  const CAT_SUB = { valuation: "估值", profit: "獲利", growth: "成長", health: "財務健康" };
  function fmt(n) { return Math.round(n || 0).toLocaleString("en-US"); }
  function Dot({ light }) { return <span style={{ display: "inline-block", width: 11, height: 11, borderRadius: "50%", background: DOT[light] || DOT.gray, flexShrink: 0 }} />; }

  // 同業比較:本檔 vs 產業中位數的白話判讀(估值:低=便宜;殖利率:高=配息多)。中性陳述,不暗示買賣。
  function peerCmp(v, med, kind) {
    if (v == null || med == null || med === 0) return "";
    var r = v / med;
    if (kind === "yield" || kind === "margin") return r >= 1.15 ? "比同業高" : r <= 0.85 ? "比同業低" : "和同業相當";   // 殖利率/毛利率/營益率:高=好
    return r >= 1.15 ? "比同業貴" : r <= 0.85 ? "比同業便宜" : "和同業相當";   // PE/PB:低=便宜
  }
  // 指標位階尺:把引擎的 good/ok 門檻帶到前端,讓新手知道這個數字落在「好 / 普通 / 偏弱」哪一段(不必自己背門檻)
  function ladder(it) {
    if (it.good == null || it.ok == null || it.dir === "bool" || it.value == null) return null;
    var u = it.unit || "", z = it.light === "green" ? "好" : it.light === "yellow" ? "普通" : "偏弱";
    var scale = it.dir === "low"
      ? "越低越好 · 好 ≤" + it.good + u + " ｜ 普通 ≤" + it.ok + u + " ｜ 再高就偏弱"
      : "越高越好 · 好 ≥" + it.good + u + " ｜ 普通 ≥" + it.ok + u + " ｜ 再低就偏弱";
    return scale + " —— 這檔 " + it.value + u + " → " + z;
  }
  // AI 分析文字 → 段落(【標題】粗體)
  function aiParas(text) {
    return String(text || "").split(/\n+/).map(function (l) { return l.trim(); }).filter(Boolean).map(function (line, i) {
      var mm = line.match(/^【(.+?)】([\s\S]*)$/);
      if (mm) return <p key={i} className="stk-ai__sec"><b>{mm[1]}</b>{mm[2] ? " " + mm[2] : ""}</p>;
      return <p key={i} className="stk-ai__foot">{line}</p>;
    });
  }
  // 本次花費估算(Sonnet $3/$15 每百萬 token,匯率約 31.5)
  function estCost(u) {
    if (!u || u.in == null || u.out == null) return "";
    var nt = (u.in / 1e6 * 3 + u.out / 1e6 * 15) * 31.5;
    return nt < 0.1 ? "<NT$0.1" : "約 NT$" + (Math.round(nt * 10) / 10);
  }

  function PriceTrend({ trend }) {
    const ref = React.useRef(null);
    const chart = React.useRef(null);
    React.useEffect(function () {
      if (!ref.current || !window.Chart || !trend || trend.length < 2) return;
      const css = getComputedStyle(document.documentElement);
      const closes = trend.map(function (p) { return p.c; });
      const rising = closes[closes.length - 1] >= closes[0];
      const col = ((rising ? css.getPropertyValue("--value-negative") : css.getPropertyValue("--value-positive")) || (rising ? "#c1503b" : "#3a8a5f")).trim();
      const tert = (css.getPropertyValue("--text-tertiary") || "#9aa").trim();
      const grid = (css.getPropertyValue("--line-faint") || "rgba(0,0,0,.06)").trim();
      var crosshair = {
        id: "crosshair",
        afterDraw: function (c) {
          if (!(c.tooltip && c.tooltip._active && c.tooltip._active.length)) return;
          var x = c.tooltip._active[0].element.x, cx = c.ctx;
          cx.save(); cx.beginPath(); cx.moveTo(x, c.chartArea.top); cx.lineTo(x, c.chartArea.bottom);
          cx.lineWidth = 1; cx.strokeStyle = tert; cx.stroke(); cx.restore();
        },
      };
      if (chart.current) chart.current.destroy();
      chart.current = new window.Chart(ref.current, {
        type: "line",
        plugins: [crosshair],
        data: { labels: trend.map(function (p) { return p.d; }), datasets: [{ data: closes, borderColor: col, backgroundColor: col + "22", fill: true, tension: 0.2, pointRadius: 0, pointHoverRadius: 4, pointHoverBackgroundColor: col, borderWidth: 2 }] },
        options: {
          responsive: true, maintainAspectRatio: false,
          interaction: { mode: "index", intersect: false },
          plugins: {
            legend: { display: false },
            tooltip: {
              displayColors: false,
              callbacks: {
                title: function (it) { return it && it.length ? it[0].label : ""; },
                label: function (c) { return "收盤 " + c.parsed.y; },
              },
            },
          },
          scales: {
            y: { ticks: { color: tert, maxTicksLimit: 5 }, grid: { color: grid } },
            x: { ticks: { color: tert, maxTicksLimit: 5, maxRotation: 0 }, grid: { display: false } },
          },
        },
      });
      return function () { if (chart.current) { chart.current.destroy(); chart.current = null; } };
    }, [trend]);
    return <div className="stk-chart"><canvas ref={ref} /></div>;
  }

  // 回測:「如果早幾年買?」(紀律回測,台股 only;FinMind 10年日線、已還原拆分;教材、非投資建議)
  function btWan(n) { return (Math.round((n || 0) / 1e3) / 10).toLocaleString("en-US", { maximumFractionDigits: 1 }); }
  function btHonest(r, mode) {
    var s = r.stock, b = r.bench, parts = [];
    // 倖存者偏誤(永遠先講)——回測只算得出「活下來」的股
    parts.push("這個回測只算得出『現在還在、查得到』的股;下市、減資歸零、爆雷的個股根本不會出現在這裡,所以個股回測天生偏向『活下來的贏家』,別當成『長期持有都會賺』。");
    // 贏/輸大盤:先講限制再講結果,避免「選股能贏」的錯覺
    if (b && !r.benchIsSelf) parts.push(s.ret > b.ret
      ? "就算這檔過去贏了 0050,你當年也無法事先知道是它——事後挑贏家容易、事前難,多數個股長期贏不了大盤。"
      : "這檔同期還輸給大盤 0050,選股要長期贏過大盤本來就很難。");
    // 給通用、非「對這檔的時機建議」的可行方向(教材、非投資建議)
    parts.push("報酬是過去、估值位階是現在的數據,都不預測未來。要不要進場是你的決定;真要進,『分批 / 定期定額』本來就是不用賭單一時點的通用做法。這裡是教材、幫你看懂數字,不是叫你買這一檔。");
    return parts.join(" ");
  }
  // 「怎麼看」本益比位階(白話教學,依個股旗標調整;不下買賣命令)
  function btPerNote(flags) {
    if (flags && flags.persistentLoss) return "這檔近期獲利不穩或虧損,本益比會嚴重失真、參考性很低,別拿它當主要依據。";
    if (flags && flags.cyclical) return "⚠️ 這檔是景氣循環股,本益比讀法跟一般『相反』:獲利高峰時本益比最低、谷底時最高——所以「低本益比」反而常是高點。重點看獲利在循環的哪一段,不能只看本益比。";
    var s = "本益比 = 股價 ÷ 每股盈餘,代表市場願意用幾倍的獲利價格買它。位階偏高 = 現在比它自己過去貴 / 市場對它成長期待較高(也可能一時過熱);偏低 = 相對便宜或市場較不看好。它反映「市場情緒 + 獲利」,同一檔不同時期高低都正常、也不單獨預測會漲會跌。";
    if (flags && flags.isFinance) s += " 金融股另常看股價淨值比(PBR)。";
    return s;
  }
  function Backtest({ code, flags }) {
    const B = window.HelmBacktest;
    const [open, setOpen] = React.useState(false);
    const [data, setData] = React.useState(null);
    const [err, setErr] = React.useState(null);
    const [loading, setLoading] = React.useState(false);
    const [years, setYears] = React.useState(10);
    const [mode, setMode] = React.useState("dca");
    const [amt, setAmt] = React.useState("10000");
    React.useEffect(function () { setData(null); setErr(null); setOpen(false); }, [code]);
    function load() {
      if (data || loading || !window.HelmStockData.getBacktest) return;
      setLoading(true); setErr(null);
      window.HelmStockData.getBacktest(code).then(function (d) {
        setLoading(false);
        if (!d.ok) { setErr("這檔歷史資料太少(可能上市不久),無法回測。"); return; }
        setData(d);
      }).catch(function (e) { setLoading(false); setErr(String((e && e.message) || e)); });
    }
    function toggle() { var n = !open; setOpen(n); if (n) load(); }

    var result = null;
    if (data && B && data.prices.length) {
      var last = data.prices[data.prices.length - 1].date;
      var startDate = (Number(last.slice(0, 4)) - years) + last.slice(4);
      var rawAmt = B.num(amt); var input = rawAmt > 0 ? rawAmt : (mode === "dca" ? 10000 : 100000);   // 負/空/0 → 退回預設(別讓負數靜默清空結果)
      var run = mode === "dca" ? B.dca : B.lump;
      var stock = run(data.prices, input, startDate);
      if (stock) {
        var vNow = B.valuationAt(data.per, last);
        result = { stock: stock, bench: run(data.bench, input, startDate), benchIsSelf: data.benchIsSelf,
          vThen: B.valuationAt(data.per, stock.startDate), vNow: vNow,
          perPos: B.perPercentile(data.per, vNow && vNow.per),
          yrs: Math.round(B.monthsBetween(stock.startDate, last) / 12 * 10) / 10 };
      }
    }
    return (
      <section className="fpage__card">
        <button type="button" className="stk-bt__head" onClick={toggle} aria-expanded={open}>
          <span className="t-overline"><i className="ph ph-clock-counter-clockwise" aria-hidden="true" /> 如果早幾年買?(回測)</span>
          <i className={"ph " + (open ? "ph-caret-up" : "ph-caret-down")} aria-hidden="true" />
        </button>
        {open && (
          <div className="stk-bt">
            {loading && <div className="fx-now"><span className="fx-now__unit">抓 10 年歷史中</span><span className="fx-now__rate">…</span></div>}
            {err && <div className="fx-advice fx-advice--mid"><span className="fx-advice__txt">⚠️ {err}</span></div>}
            {result && (
              <div>
                <div className="stk-bt__segs">
                  {[1, 3, 5, 10].map(function (y) { var dis = data.availYears >= 2 && y > data.availYears; return <button key={y} type="button" disabled={dis} aria-pressed={years === y} className={"stk-bt__seg" + (years === y ? " is-on" : "")} onClick={function () { setYears(y); }}>{y} 年前</button>; })}
                </div>
                <div className="stk-bt__segs">
                  <button type="button" aria-pressed={mode === "dca"} className={"stk-bt__seg" + (mode === "dca" ? " is-on" : "")} onClick={function () { setMode("dca"); }}>定期定額 / 月</button>
                  <button type="button" aria-pressed={mode === "lump"} className={"stk-bt__seg" + (mode === "lump" ? " is-on" : "")} onClick={function () { setMode("lump"); }}>一次買進</button>
                </div>
                <div className="stk-bt__in"><span className="stk-bt__sym">NT$</span><input type="text" inputMode="numeric" aria-label={mode === "dca" ? "每月投入金額" : "一次投入金額"} value={amt} onChange={function (e) { setAmt(e.target.value); }} /><span className="stk-bt__inhint">{mode === "dca" ? "每月投入" : "一次投入"}</span></div>

                <div className="fx-now"><span className="fx-now__unit">投入 {btWan(result.stock.invested)} 萬({result.yrs} 年)→ 現在約</span><span className={"fx-now__rate t-num " + (result.stock.profit >= 0 ? "fx-pl--up" : "fx-pl--down")}>{btWan(result.stock.value)} 萬</span></div>
                <p className="stk-bt__caveat">這是<b>過去 {result.yrs} 年的歷史結果、不是預測</b>,未來不會照抄、也不能重來。{result.yrs < years - 0.4 ? "(這檔只有 " + result.yrs + " 年資料,已用全部歷史)" : ""}</p>
                <div className="stk-bt__rows">
                  <div className="tax-row"><span>總報酬</span><b className={result.stock.ret >= 0 ? "fx-pl--up" : "fx-pl--down"}>{result.stock.ret >= 0 ? "+" : ""}{(result.stock.ret * 100).toFixed(0)}%</b></div>
                  {mode === "lump" && result.stock.cagr != null && <div className="tax-row"><span>年化報酬</span><b className="t-num">{(result.stock.cagr * 100).toFixed(1)}%</b></div>}
                  {mode === "dca" && <div className="tax-row"><span>平均成本 / 股</span><b className="t-num">{result.stock.avgCost.toFixed(1)}</b></div>}
                  {!result.benchIsSelf && result.bench && <div className="tax-row"><span>同期 0050 大盤(同方式)</span><b className={result.bench.ret >= 0 ? "fx-pl--up" : "fx-pl--down"}>{result.bench.ret >= 0 ? "+" : ""}{(result.bench.ret * 100).toFixed(0)}%</b></div>}
                </div>
                {!result.benchIsSelf && <p className="stk-bt__cap">兩邊都是<b>價格報酬、未含股息</b>;0050 與存股的殖利率較高,把股息算進去後差距會縮小——這條對照沒占到便宜。</p>}
                {data.anomaly && <div className="fx-advice fx-advice--mid" style={{ marginTop: 8 }}><span className="fx-advice__txt">⚠️ 這檔歷史中出現過單日 &gt;20% 的劇烈變動(可能是崩跌、處置股、或減資等公司行為)——回測數字看看就好、別當準。</span></div>}

                {result.vThen && result.vThen.per != null && (
                  <div className="stk-bt__val">
                    <div className="stk-bt__valh"><i className="ph ph-scales" aria-hidden="true" /> {mode === "dca" ? "第一次扣款時" : "當時"} → 現在(估值)</div>
                    <div className="tax-row"><span>本益比 PER</span><b className="t-num">{result.vThen.per} → {result.vNow && result.vNow.per != null ? result.vNow.per : "—"}</b></div>
                    {result.perPos && <div className="tax-row"><span>現在 PER 在自己過去約 10 年的位階</span><b className="t-num">第 {result.perPos.pct} 百分位 · {result.perPos.zone}</b></div>}
                    <div className="tax-row"><span>殖利率</span><b className="t-num">{result.vThen.yield != null ? result.vThen.yield + "%" : "—"} → {result.vNow && result.vNow.yield != null ? result.vNow.yield + "%" : "—"}</b></div>
                    {mode === "dca" && <p className="stk-bt__cap">定期定額是分批買的,「當時」只是第一個月的估值參考。</p>}
                    <p className="stk-bt__cap"><b>怎麼看:</b>{btPerNote(flags)}</p>
                  </div>
                )}

                <div className="fx-advice fx-advice--mid"><span className="fx-advice__tag">💡 誠實</span><span className="fx-advice__txt">{btHonest(result, mode)}</span></div>
                <p className="stk-note">回測=「如果當時這樣做」的歷史結果,<b>過去績效不代表未來</b>。資料來自 FinMind,拆分以官方事件還原。教材、非投資建議。</p>
              </div>
            )}
          </div>
        )}
      </section>
    );
  }

  function StockScreen({ onClose, initialId }) {
    const [id, setId] = React.useState(initialId || "");
    const [busy, setBusy] = React.useState(false);
    const [res, setRes] = React.useState(null);
    const [openKey, setOpenKey] = React.useState(null);
    const [flags, setFlags] = React.useState(null);
    const [watched, setWatched] = React.useState(false);
    const [news, setNews] = React.useState(null);
    const [ai, setAi] = React.useState(null);
    const [aiBusy, setAiBusy] = React.useState(false);
    const [aiArm, setAiArm] = React.useState(false);     // 防誤觸:按一下先「待命」,再確認才真的花錢呼叫
    const [aiErr, setAiErr] = React.useState(null);
    const [aiProf, setAiProf] = React.useState(null);       // 未收錄業務簡介時,AI 上網即時查的公司側寫
    const [aiProfBusy, setAiProfBusy] = React.useState(false);
    const [aiProfErr, setAiProfErr] = React.useState(null);
    const scrollRef = React.useRef(null);

    function doLookup(code) {   // 真正抓某「代號」的健檢
      if (scrollRef.current) scrollRef.current.scrollTop = 0;   // 查新股(含點同業/選名稱)→ 回到頂端看新標頭
      setId(code);
      setBusy(true); setRes(null); setOpenKey(null);
      window.HelmStockData.buildMetrics(code).then(function (r) {
        if (!r.metrics) { setRes({ etf: true, name: r._debug.name, code: code }); }
        else { setRes({ score: window.HelmStockScore.scoreStock(r.metrics), m: r.metrics, code: code }); }
        setBusy(false);
      }).catch(function (e) {
        var msg = String((e && e.message) || e);
        if (/Failed to fetch|NetworkError|fetch/i.test(msg)) msg = "抓取資料失敗——可能是網路不穩,或 FinMind 暫時限流(免費版有流量上限),請稍後再試。";
        setRes({ error: msg }); setBusy(false);
      });
    }

    function lookup(codeArg) {
      const raw = String(typeof codeArg === "string" ? codeArg : id).trim();
      if (/^\d{4,6}[A-Z]?$/.test(raw)) { doLookup(raw); return; }   // 純代號 → 直接查
      if (!raw) { setRes({ error: "請輸入股票代號或名稱(例:2330 或 台積電)" }); return; }
      // 中文名稱 → 先用對照表解析(朋友常只記得名字)
      if (scrollRef.current) scrollRef.current.scrollTop = 0;
      setBusy(true); setRes(null); setOpenKey(null);
      window.HelmStockData.searchByName(raw).then(function (matches) {
        setBusy(false);
        if (!matches.length) { setRes({ error: "找不到「" + raw + "」,試試完整名稱或股票代號(例:2330)" }); return; }
        if (matches.length === 1) { doLookup(matches[0].code); return; }
        setRes({ choose: matches.slice(0, 8) });
      }).catch(function () { setBusy(false); setRes({ error: "名稱對照載入失敗,請改用代號(例:2330)" }); });
    }

    React.useEffect(function () {
      window.HelmStockData.getFlags().then(setFlags);
      if (initialId) lookup(initialId);
    }, []);
    React.useEffect(function () { setWatched(!!(res && res.code && window.HelmWatch && window.HelmWatch.has(res.code))); }, [res && res.code]);

    // 換一檔(含點同業):清掉上一檔的新聞/AI 結果,並自動抓新聞(免費、公開、後端已快取)
    React.useEffect(function () {
      setNews(null); setAi(null); setAiArm(false); setAiErr(null);
      setAiProf(null); setAiProfBusy(false); setAiProfErr(null);
      if (res && res.m && res.code && window.HelmData && window.HelmData.stockNews) {
        window.HelmData.stockNews(res.code, res.m.name).then(function (d) { setNews(d || { items: [] }); }).catch(function () { setNews({ items: [] }); });
      }
    }, [res && res.code]);

    function toggleWatch() {
      if (!res || !res.m) return;
      if (watched) { window.HelmWatch.remove(res.code); setWatched(false); }
      else { window.HelmWatch.add(res.code, res.m.name, "tw"); setWatched(true); }
    }

    function runAI() {   // 真的花錢的那一步(確認後才會走到這)
      if (!res || !res.m || !(window.HelmData && window.HelmData.stockAI)) return;
      setAiArm(false); setAiBusy(true); setAiErr(null); setAi(null);
      var slim = Object.assign({}, res.m); delete slim.trend; delete slim.crossCheck;   // 不送 120 點走勢/比對細節,省流量
      window.HelmData.stockAI({ code: res.code, name: res.m.name, metrics: slim }).then(function (r) {
        setAiBusy(false);
        if (r && r.ok && r.data && r.data.analysis) setAi(r.data);
        else { var d = (r && r.data) || {}; setAiErr(d.error === "no_key" ? "後端尚未設定 AI 金鑰" : (r && r.error === "network") ? "網路問題,請稍後再試" : "分析失敗,請稍後再試"); }
      }).catch(function () { setAiBusy(false); setAiErr("分析失敗,請稍後再試"); });
    }

    function runProfile() {   // 未收錄業務簡介 → AI 上網查證整理(花 token、後端持久快取,查過就不再花)
      if (!res || !res.m || !(window.HelmData && window.HelmData.companyProfile)) return;
      setAiProfBusy(true); setAiProfErr(null);
      var mk = res.m.market || (res.m.isUs ? "us" : "tw");
      window.HelmData.companyProfile({ code: res.code, name: res.m.name, market: mk }).then(function (r) {
        setAiProfBusy(false);
        if (r && r.ok && r.data && r.data.profile && r.data.profile.biz) setAiProf(r.data.profile);
        else { var d = (r && r.data) || {}; setAiProfErr(d.error === "no_key" ? "後端尚未設定 AI 金鑰" : (r && r.error === "network") ? "網路問題,請稍後再試" : "查詢失敗,請稍後再試"); }
      }).catch(function () { setAiProfBusy(false); setAiProfErr("查詢失敗,請稍後再試"); });
    }

    function adviceCls(light) { return light === "red" ? "fx-advice--high" : light === "yellow" ? "fx-advice--mid" : "fx-advice--low"; }

    function report() {
      const s = res.score, m = res.m;
      const prof = m.profile || aiProf;   // 靜態側寫,沒收錄就用 AI 即時查到的
      const fi = flags && flags.flags && flags.flags[res.code];
      const staleTxt = m.staleMonths != null ? "(" + m.staleMonths + " 個月前)" : "";
      const px = m.price != null ? Math.round(m.price * 100) / 100 : "—";
      const chg = m.changePct;
      const chgCol = (chg > 0) ? "var(--value-negative)" : (chg < 0) ? "var(--value-positive)" : "var(--text-secondary)";
      const chgTxt = chg == null ? "" : (chg > 0 ? "▲ " : chg < 0 ? "▼ " : "持平 ") + Math.abs(chg) + "%";
      return (
        <React.Fragment>
          {fi && (
            <section className="fpage__card stk-flagcard">
              <div className="stk-flag">
                <i className="ph ph-warning-octagon" aria-hidden="true" />
                <div className="stk-flag__txt">
                  <b>⚠️ {fi.kinds.join("、")}股 · {fi.market}</b>
                  <span>{fi.note}</span>
                  <span className="stk-flag__cav">交易受限制或有異常,常是炒作、財務有狀況的標的——新手務必避開。資料截至 {flags.asOf},最新以證交所/櫃買公告為準。</span>
                </div>
              </div>
            </section>
          )}
          {/* 標頭 + 鮮度 */}
          <section className="fpage__card">
            <div className="stk-head">
              <div>
                <span className="stk-name">{m.name}</span>
                <span className="stk-code t-num">{res.code}</span>
              </div>
              <div className="stk-fresh">財報截至 {m.lq} {staleTxt}</div>
            </div>

            {/* 定位(最重要,擺最上)*/}
            <div className={"fx-advice " + adviceCls(s.overallLight)} style={{ marginTop: 12 }}>
              <span className="fx-advice__txt">{s.tag}</span>
            </div>

            {/* 總分(不可靠時不給綠燈)*/}
            <div className="stk-score">
              <span className="stk-score__num t-num" style={{ color: DOT[s.overallLight] }}>{(s.overall == null || s.suppressScore) ? "—" : s.overall}</span>
              <span className="stk-score__of">/ 100</span>
              <span className="stk-score__verdict"><Dot light={s.overallLight} /> {s.verdict}</span>
            </div>

            {s.unreliable && s.reliaNote && (
              <div className="fx-advice fx-advice--mid" style={{ marginTop: 8 }}>
                <span className="fx-advice__txt">⚠️ 這次分數的可靠度要打折:{s.reliaNote}。<b>別只看燈號和總分</b>,往下點開紅燈、黃燈的項目看細項才準。</span>
              </div>
            )}
            <p className="stk-howto"><b>新手可以這樣看:</b>{s.overallLight === "green" ? "體質這關過了,但「好公司」不等於「現在買便宜」——先看上面「貴不貴?」那格和同業比是不是偏貴,再決定要追還是等回檔。" : s.overallLight === "red" ? "先別因為「跌很多 / 看起來便宜」就急著進場——點開紅燈的那幾項,搞懂它為什麼弱,便宜常常有便宜的原因。" : "有好有壞,先點開紅燈、黃燈的那幾項,搞懂是哪裡不夠好、為什麼,而不是只看總分這個數字。"}</p>

            <button type="button" className={"stk-watch" + (watched ? " stk-watch--on" : "")} onClick={toggleWatch}>
              <i className={"ph " + (watched ? "ph-check-circle" : "ph-plus-circle")} aria-hidden="true" />
              {watched ? "已在觀察清單" : "加入觀察清單"}
            </button>
          </section>

          {/* 股價 + 趨勢圖 */}
          {m.trend && m.trend.length > 1 && (
            <section className="fpage__card">
              <div className="fpage__card-head"><span className="t-overline">股價走勢</span><span className="fpage__card-hint">收盤 {m.asOfPrice} · 紅漲綠跌</span></div>
              <div className="stk-px-row">
                <span className="stk-px t-num" style={{ color: chgCol }}>{px}</span>
                {chgTxt && <span className="stk-px-chg t-num" style={{ color: chgCol }}>{chgTxt}</span>}
              </div>
              <PriceTrend trend={m.trend} />
              <p className="stk-px-note">近 {m.trend.length} 個交易日收盤 · 只是價格走勢、不代表未來;搭配上面的體質一起看。</p>
              {(function () {
                var cs = m.trend.map(function (p) { return p.c; }).filter(function (x) { return x != null; });
                if (cs.length < 5) return null;
                var lo = Math.min.apply(null, cs), hi = Math.max.apply(null, cs), cur = cs[cs.length - 1];
                if (!(hi > lo)) return null;
                var pct = Math.round((cur - lo) / (hi - lo) * 100);
                var z = pct >= 75 ? "偏高" : pct <= 25 ? "偏低" : "中段";
                return <p className="stk-px-note" style={{ marginTop: 4 }}>這段區間約 {Math.round(lo)}~{Math.round(hi)},現價落在第 {pct}%(位階{z})。<b>位階高 ≠ 貴、低 ≠ 便宜</b>——這只是價格在這段期間的相對高低,真正貴不貴要看上面的「貴不貴?」那格。</p>;
              })()}
            </section>
          )}

          {/* 這家公司是什麼來頭(收錄就顯示靜態側寫;沒收錄就給「讓 AI 上網查」按鈕)*/}
          {prof ? (
            <section className="fpage__card stk-prof">
              <div className="fpage__card-head"><span className="t-overline"><i className="ph ph-buildings" aria-hidden="true" /> 這家公司是什麼來頭</span>{!m.profile && aiProf && <span className="fpage__card-hint"><i className="ph ph-globe-hemisphere-west" aria-hidden="true" /> AI 即時查</span>}</div>
              {prof.tags && prof.tags.length > 0 && (
                <div className="stk-prof__tags">{prof.tags.map(function (t, i) { return <span key={i} className="stk-prof__tag">{t}</span>; })}</div>
              )}
              <p className="stk-biz">{prof.biz}</p>
              {prof.founded && <p className="stk-prof__line"><i className="ph ph-flag" aria-hidden="true" /> <b>成立</b>{prof.founded}</p>}
              {prof.style && <p className="stk-prof__line"><i className="ph ph-user-focus" aria-hidden="true" /> <b>經營風格</b>{prof.style}</p>}
              {prof.edge && <p className="stk-prof__line stk-prof__edge"><i className="ph ph-shield-star" aria-hidden="true" /> <b>跟同業的差別</b>{prof.edge}</p>}
              {prof.rev && <p className="stk-biz__rev"><b>主要營收:</b>{prof.rev}</p>}
              <p className="cc-note">{(!m.profile && aiProf) ? "上面由 AI 即時上網查證整理,可能有誤或過時,請以公司官方資料(公開資訊觀測站 / 年報)為準、非投資建議。" : "公司側寫由 AI 整理、聚焦「跟同類公司的差異」幫你挑股有頭緒;為求精簡可能簡化,年份/數字請以公司官方資料為準、非投資建議。"}</p>
            </section>
          ) : (
            <section className="fpage__card stk-prof">
              <div className="fpage__card-head"><span className="t-overline"><i className="ph ph-buildings" aria-hidden="true" /> 這家公司是什麼來頭</span></div>
              {aiProfBusy ? (
                <div className="fx-now"><span className="fx-now__unit">AI 上網查證中(約 10–20 秒)</span><span className="fx-now__rate">…</span></div>
              ) : (
                <div>
                  <p className="stk-note" style={{ marginTop: 0 }}>這檔還沒收錄業務簡介。要不要讓 AI 上網查一下這家公司在做什麼?</p>
                  {aiProfErr && <p className="stk-ai__warn" style={{ marginBottom: 10 }}>⚠️ {aiProfErr}</p>}
                  <button type="button" className="stk-ai__trigger" onClick={runProfile}>
                    <i className="ph ph-globe-hemisphere-west" aria-hidden="true" />
                    <span className="stk-ai__trigger-l">
                      <b>讓 AI 查這家公司在做什麼</b>
                      <span>上網搜尋 + 整理成業務簡介 · 點一下約 NT$1~3,查過就記住、不再收費</span>
                    </span>
                    <i className="ph ph-caret-right" aria-hidden="true" />
                  </button>
                </div>
              )}
            </section>
          )}

          {/* 四大類紅黃綠燈 */}
          <section className="fpage__card">
            <div className="fpage__card-head"><span className="t-overline">體質四面向</span><span className="fpage__card-hint">點指標看「這是什麼」</span></div>
            <div className="stk-cats">
              {s.categories.map(function (c) {
                return (
                  <div key={c.key} className="stk-cat">
                    <div className="stk-cat__row">
                      <Dot light={c.light} />
                      <span className="stk-cat__label">{CAT_LABEL[c.key] || c.name}</span>
                      <span className="stk-cat__sub">{CAT_SUB[c.key] || ""}</span>
                      <span className="stk-cat__pct t-num">{c.pct == null ? "—" : c.pct + "%"}</span>
                    </div>
                    {c.light !== "gray" && CAT_VERDICT[c.key] && <div className="stk-cat__verdict">→ {CAT_VERDICT[c.key][c.light]}</div>}
                    {c.conflict && <div className="stk-cat__conflict"><i className="ph ph-trend-down" aria-hidden="true" /> {c.conflict}</div>}
                    {c.note && !c.conflict && <div className="stk-cat__note">{c.note}</div>}
                    <div className="stk-items">
                      {c.items.map(function (it) {
                        const k = c.key + ":" + it.key, open = openKey === k;
                        return (
                          <div key={it.key} className="stk-item">
                            <button type="button" className="stk-item__btn" onClick={function () { setOpenKey(open ? null : k); }}>
                              <Dot light={it.light} />
                              <span className="stk-item__name">{it.name}</span>
                              <span className="stk-item__val">{it.verdict}</span>
                              <i className={"ph " + (open ? "ph-caret-up" : "ph-question")} aria-hidden="true" />
                            </button>
                            {open && <div className="stk-item__desc">{ladder(it) && <div className="stk-ladder">{ladder(it)}</div>}{it.desc}</div>}
                          </div>
                        );
                      })}
                    </div>
                  </div>
                );
              })}
            </div>
          </section>

          {/* 同業比較:本檔 vs 全市場同產業中位數 + 知名同業(點看健檢)*/}
          {m.industryStats && (
            <section className="fpage__card">
              <div className="fpage__card-head"><span className="t-overline"><i className="ph ph-scales" aria-hidden="true" /> 同業比較 · {m.industryStats.industry}</span><span className="fpage__card-hint">產業中位數</span></div>
              {peerSynth(m, m.industryStats) && <div className="stk-peer__synth">{peerSynth(m, m.industryStats)}</div>}
              <div className="stk-peer">
                {[["本益比", "pe", "medPE", "val", "倍"], ["股價淨值比", "pb", "medPB", "val", "倍"], ["殖利率", "yield", "medYield", "yield", "%"], ["毛利率", "gross", "medGross", "margin", "%"], ["營業利益率", "opm", "medOpm", "margin", "%"]].map(function (row) {
                  var mv = m[row[1]], med = m.industryStats[row[2]], sfx = row[4], read = peerCmp(mv, med, row[3]);
                  return (
                    <div key={row[1]} className="stk-peer__row">
                      <span className="stk-peer__label">{row[0]}</span>
                      <span className="stk-peer__mine t-num">{mv == null ? "—" : mv + sfx}</span>
                      <span className="stk-peer__med t-num">同業 {med == null ? "—" : med + sfx}</span>
                      <span className="stk-peer__read">{read}</span>
                    </div>
                  );
                })}
              </div>
              {m.industryStats.peers && m.industryStats.peers.length > 0 && (
                <div className="stk-peers">
                  <span className="stk-peers__lbl">同業(點看健檢)</span>
                  {m.industryStats.peers.map(function (p) {
                    return <button key={p.code} type="button" className="stk-peer-chip" onClick={function () { lookup(p.code); }}>{p.name}</button>;
                  })}
                </div>
              )}
              <p className="cc-note">中位數=同業一半在上、一半在下的「中間值」,取自全市場同產業約 {m.industryStats.n} 檔(證交所+櫃買{m.industryStats.asOf ? "," + m.industryStats.asOf + " 更新" : ""})。便宜不一定好、貴不一定壞——要配合上面的體質一起看。</p>
            </section>
          )}

          {/* 本夢比:沒獲利/持續虧損時才出現的教學卡 */}
          {(m.pe == null || m.persistentLoss) && (
            <section className="fpage__card">
              <div className="fpage__card-head"><span className="t-overline"><i className="ph ph-cloud" aria-hidden="true" /> 什麼是「本夢比」?</span></div>
              <p className="stk-dream">本益比(股價 ÷ 每股盈餘)是「幾年能賺回股價」。但這家公司{m.persistentLoss ? "近年多數時間在虧損" : "目前算不出正的本益比(沒有穩定獲利)"},於是市場戲稱為「<b>本夢比</b>」——股價買的不是「現在賺多少」,而是「未來的夢」(想像中的成長)。夢做得成、股價海闊天空;<b>夢一旦破滅,可能大幅回跌</b>。沒有獲利支撐的高價股,想像空間與風險都特別大,新手務必謹慎。</p>
            </section>
          )}

          {/* 資料比對:FinMind vs 官方(證交所/櫃買)*/}
          {m.crossCheck && m.crossCheck.any && (
            <section className="fpage__card">
              <div className="fpage__card-head"><span className="t-overline">資料比對 · vs {m.crossCheck.market}官方</span></div>
              {m.crossCheck.allMatch ? (
                <p className="cc-ok"><i className="ph ph-check-circle" aria-hidden="true" /> 本益比、股價淨值比、殖利率都與{m.crossCheck.market}官方一致 —— 資料可信。</p>
              ) : (
                <div>
                  <p className="cc-warn"><i className="ph ph-warning" aria-hidden="true" /> 與官方有出入(請以官方為準):</p>
                  {[["pe", "本益比"], ["pb", "股價淨值比"], ["yield", "殖利率"]].map(function (kv) {
                    var c = m.crossCheck[kv[0]]; if (!c || c.match) return null;
                    return <div key={kv[0]} className="cc-row">{kv[1]}:本工具 <b>{c.fm}</b> / 官方 <b>{c.off}</b></div>;
                  })}
                </div>
              )}
              <p className="cc-note">官方資料來自證交所／櫃買(每日);本工具數據來自 FinMind。小差異多為價格時點不同,大差異才需留意。</p>
            </section>
          )}

          {/* 技術面(分開、不計分)*/}
          {s.technical.length > 0 && (
            <section className="fpage__card">
              <div className="fpage__card-head"><span className="t-overline">技術面 · 短線時機</span><span className="fpage__card-hint">不計分 · 點 ❓ 看說明</span></div>
              <p className="stk-tech-split">上面四面向看的是「<b>公司好不好</b>」(長期體質);這一區看的是「<b>現在這個價格的短線時機</b>」,而且<b>會失靈</b>。好公司也可能正買在高點。<b>這區的燈號不是買賣訊號</b>,只是市場情緒的參考。</p>
              {s.techSummary && <p className="stk-tech-sum"><i className="ph ph-chart-line" aria-hidden="true" /> {s.techSummary}</p>}
              <div className="stk-items">
                {s.technical.map(function (t, i) {
                  const k = "tech:" + i, open = openKey === k;
                  return (
                    <div key={i} className="stk-item">
                      <button type="button" className="stk-item__btn" onClick={function () { setOpenKey(open ? null : k); }}>
                        <Dot light={t.light} />
                        <span className="stk-item__name">{t.name}</span>
                        <span className="stk-item__val">{t.signal}</span>
                        <i className={"ph " + (open ? "ph-caret-up" : "ph-question")} aria-hidden="true" />
                      </button>
                      {t.detail && <div className="stk-tech-detail t-num">{t.detail}</div>}
                      {open && <div className="stk-item__desc">{t.desc}</div>}
                    </div>
                  );
                })}
              </div>
              <p className="cc-note">想搞懂這些指標怎麼用?到「理財工具 → <b>技術面入門</b>」有每個指標的白話講解,以及停損、部位、用閒錢等風險管理。技術面是短線時機參考、會失靈,搭配上面的體質一起看。</p>
            </section>
          )}

          {/* 最新新聞(Google 新聞,後端代抓+快取;外連原文)*/}
          <section className="fpage__card">
            <div className="fpage__card-head"><span className="t-overline"><i className="ph ph-newspaper" aria-hidden="true" /> 最新新聞</span><span className="fpage__card-hint">Google 新聞</span></div>
            {news == null ? (
              <div className="fx-now"><span className="fx-now__unit">載入新聞中</span><span className="fx-now__rate">…</span></div>
            ) : (news.items && news.items.length) ? (
              <div className="stk-news">
                {news.items.map(function (it, i) {
                  return (
                    <a key={i} className="stk-news__item" href={it.link} target="_blank" rel="noopener noreferrer">
                      <span className="stk-news__title">{it.title}</span>
                      <span className="stk-news__meta">{[it.source, it.date].filter(Boolean).join(" · ")}<i className="ph ph-arrow-square-out" aria-hidden="true" /></span>
                    </a>
                  );
                })}
              </div>
            ) : (
              <p className="stk-note" style={{ marginTop: 0 }}>查無相關新聞。</p>
            )}
            <p className="cc-note">新聞由 Google 新聞彙整,點擊外連原文出處。標題僅供參考、非投資建議,內容請以原文與公司公告為準。</p>
          </section>

          {/* AI 客觀分析:按鈕觸發(備注花費)+ 兩段式防誤觸 */}
          <section className="fpage__card stk-ai">
            <div className="fpage__card-head"><span className="t-overline"><i className="ph ph-sparkle" aria-hidden="true" /> AI 客觀分析</span><span className="fpage__card-hint">多空兩面 · 通常不到 NT$1/次</span></div>
            {ai ? (
              <div>
                {!m.profile && <p className="stk-note" style={{ marginTop: 0, marginBottom: 8 }}>(這檔未收錄業務簡介,以下由 AI 補上)</p>}
                <div className="stk-ai__body">{aiParas(ai.analysis)}</div>
                <div className="stk-ai__bar">
                  <span className="stk-ai__cost">{ai.model === "demo" ? "展示版示範" : ("模型 Sonnet" + (estCost(ai.usage) ? " · 本次" + estCost(ai.usage) : ""))}</span>
                  <button type="button" className="stk-ai__redo" onClick={function () { setAi(null); setAiArm(true); }}>重新分析</button>
                </div>
              </div>
            ) : aiBusy ? (
              <div className="fx-now"><span className="fx-now__unit">AI 分析中(約 5–10 秒)</span><span className="fx-now__rate">…</span></div>
            ) : aiArm ? (
              <div className="stk-ai__confirm">
                <p className="stk-ai__warn">確定要分析嗎?會呼叫一次 AI、<b>花費通常不到 NT$1</b>。</p>
                <div className="stk-ai__btns">
                  <button type="button" className="stk-ai__go" onClick={runAI}><i className="ph ph-check" aria-hidden="true" /> 確定分析</button>
                  <button type="button" className="stk-ai__cancel" onClick={function () { setAiArm(false); }}>取消</button>
                </div>
              </div>
            ) : (
              <div>
                {aiErr && <p className="stk-ai__warn" style={{ marginBottom: 10 }}>⚠️ {aiErr}</p>}
                <button type="button" className="stk-ai__trigger" onClick={function () { setAiArm(true); }}>
                  <i className="ph ph-sparkle" aria-hidden="true" />
                  <span className="stk-ai__trigger-l">
                    <b>用 AI 客觀解讀這檔</b>
                    <span>多空兩面分析,避免好公司只因近期表現差被分數低估 · 點一下,通常不到 NT$1</span>
                  </span>
                  <i className="ph ph-caret-right" aria-hidden="true" />
                </button>
              </div>
            )}
          </section>

          {/* 盲區 + 免責 */}
          <section className="fpage__card stk-disclaim">
            <div className="fpage__card-head"><span className="t-overline"><i className="ph ph-eye-slash" aria-hidden="true" /> 這工具看不到什麼</span></div>
            {s.notes.map(function (n, i) { return <p key={i} className="stk-note">{n}</p>; })}
          </section>
        </React.Fragment>
      );
    }

    return (
      <div className="fpage" role="dialog" aria-modal="true" aria-label="個股健檢">
        <div className="fpage__panel">
          <header className="fpage__bar">
            <button className="fpage__cancel" onClick={onClose}><i className="ph ph-arrow-left" aria-hidden="true" />返回</button>
            <span className="fpage__title">個股健檢</span>
            <span aria-hidden="true" />
          </header>
          <div className="fpage__scroll" ref={scrollRef}>
            <div className="fpage__body">
              <section className="fpage__card">
                <div className="fpage__card-head"><span className="t-overline">查台股</span><span className="fpage__card-hint">代號或名稱</span></div>
                <div className="stk-search">
                  <Input placeholder="例:2330 或 台積電" value={id}
                    onChange={function (e) { setId(e.target.value); }}
                    onKeyDown={function (e) { if (e.key === "Enter") lookup(); }} />
                  <Button variant="primary" onClick={function () { lookup(); }} loading={busy}>查健檢</Button>
                </div>
                <p className="prot-sum__note">用財報指標看一家公司的體質——<b>教材型、非投資建議</b>。<b>可打代號或中文名稱</b>(只記得名字也行)。資料來自 FinMind,免費、可能有延遲。</p>
              </section>

              {busy && <section className="fpage__card"><div className="fx-now"><span className="fx-now__unit">查詢中</span><span className="fx-now__rate">…</span></div></section>}
              {res && res.error && <section className="fpage__card"><div className="fx-advice fx-advice--mid"><span className="fx-advice__txt">⚠️ {res.error}</span></div></section>}
              {res && res.choose && (
                <section className="fpage__card">
                  <div className="fpage__card-head"><span className="t-overline"><i className="ph ph-list-magnifying-glass" aria-hidden="true" /> 找到多檔,請選一個</span></div>
                  <div className="stk-choose">
                    {res.choose.map(function (c) {
                      return (
                        <button key={c.code} type="button" className="stk-choose__row" onClick={function () { lookup(c.code); }}>
                          <span className="stk-choose__name">{c.name}</span>
                          <span className="stk-choose__code t-num">{c.code}</span>
                          <i className="ph ph-caret-right" aria-hidden="true" />
                        </button>
                      );
                    })}
                  </div>
                  <p className="stk-note" style={{ marginTop: 10 }}>沒看到要找的?輸入更完整的名稱,或直接打代號。</p>
                </section>
              )}
              {res && res.etf && (
                <section className="fpage__card">
                  <div className="stk-head"><div><span className="stk-name">{res.name}</span><span className="stk-code t-num">{res.code}</span></div></div>
                  <div className="fx-advice fx-advice--mid" style={{ marginTop: 12 }}><span className="fx-advice__txt">這是 ETF / 無個股財報,不適用個股基本面評分。ETF 請看淨值、折溢價、追蹤誤差、總費用率。</span></div>
                </section>
              )}
              {res && res.score && report()}
              {res && (res.score || res.etf) && <Backtest code={res.code} flags={res.m ? { cyclical: res.m.cyclical, persistentLoss: res.m.persistentLoss, isFinance: res.m.isFinance } : null} />}
            </div>
          </div>
        </div>
      </div>
    );
  }

  window.StockScreen = StockScreen;
})();
