// page-briefing.jsx — 盤前簡報頁（每天 08:15 自動更新）
// 整合：美股訊號 + 台股題材 + 推薦 Top 5 + 自選股 alert + 三大法人 Top 10 + 總經事件
// 掛 window.BriefingPage；沿用 window.STOCK_TOKENS（T）+ window.fetchAPI
(function () {
  'use strict';

  const T = window.STOCK_TOKENS;

  // ── 小工具 ─────────────────────────────────────────────────────────────────

  function today() {
    return new Date().toISOString().slice(0, 10);
  }

  // 百分比顯示（帶符號）
  function pct(n, digits) {
    const d = digits != null ? digits : 2;
    const v = Number(n);
    if (!isFinite(v)) return '—';
    return (v > 0 ? '+' : '') + v.toFixed(d) + '%';
  }

  // 億元顯示
  function yi(n) {
    const v = Number(n);
    if (!isFinite(v)) return '—';
    return (v >= 0 ? '+' : '') + (v / 1e8).toFixed(1) + '億';
  }

  // ── 骨架 Loading 元件 ───────────────────────────────────────────────────────

  function Skeleton({ width, height, style }) {
    return (
      <div
        style={{
          width: width || '100%',
          height: height || 14,
          borderRadius: 4,
          background: 'linear-gradient(90deg, ' + T.hover + ' 25%, ' + T.border + ' 50%, ' + T.hover + ' 75%)',
          backgroundSize: '200% 100%',
          animation: 'briefing-shimmer 1.5s infinite',
          ...style,
        }}
      />
    );
  }

  // 注入 shimmer keyframe（只跑一次）
  if (typeof document !== 'undefined' && !document.getElementById('briefing-shimmer-style')) {
    const s = document.createElement('style');
    s.id = 'briefing-shimmer-style';
    s.textContent = '@keyframes briefing-shimmer{0%{background-position:200% 0}100%{background-position:-200% 0}}';
    document.head.appendChild(s);
  }

  // ── 空白佔位 ────────────────────────────────────────────────────────────────

  function Empty({ msg }) {
    return (
      <div style={{ color: T.text3, fontSize: 12, padding: '4px 0', fontStyle: 'italic' }}>
        {msg || '尚無資料（pipeline 下次跑會更新）'}
      </div>
    );
  }

  // ── PageHeader ──────────────────────────────────────────────────────────────

  function PageHeader({ date, loading }) {
    const dot = loading ? (
      <span style={{
        display: 'inline-block', width: 7, height: 7,
        borderRadius: '50%', background: T.warn,
        animation: 'briefing-pulse 1.5s ease-in-out infinite',
        verticalAlign: 'middle', marginLeft: 8,
      }} title="資料載入中" />
    ) : (
      <span style={{
        display: 'inline-block', width: 7, height: 7,
        borderRadius: '50%', background: T.down,
        verticalAlign: 'middle', marginLeft: 8,
      }} title="資料已就緒" />
    );

    // pulse keyframe
    if (typeof document !== 'undefined' && !document.getElementById('briefing-pulse-style')) {
      const s = document.createElement('style');
      s.id = 'briefing-pulse-style';
      s.textContent = '@keyframes briefing-pulse{0%,100%{opacity:1}50%{opacity:0.3}}';
      document.head.appendChild(s);
    }

    return (
      <div style={{ marginBottom: 20, borderBottom: '1px solid ' + T.border, paddingBottom: 16 }}>
        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', flexWrap: 'wrap', gap: 8 }}>
          <div>
            <h1 style={{ fontSize: 22, fontWeight: 700, color: T.text, margin: 0, letterSpacing: '-0.3px' }}>
              盤前簡報{dot}
            </h1>
            <div style={{ fontSize: 12, color: T.text3, marginTop: 4 }}>
              {date || today()} · 每日 08:15 自動更新
            </div>
          </div>
          <div style={{
            fontSize: 11, color: T.text3,
            padding: '4px 10px', background: T.surface2,
            borderRadius: 6, border: '1px solid ' + T.border,
          }}>
            一鍵總覽 · 開盤前必看
          </div>
        </div>
      </div>
    );
  }

  // ── Section 容器 ────────────────────────────────────────────────────────────

  function Section({ title, icon, children, accent }) {
    return (
      <div style={{
        background: T.surface,
        border: '1px solid ' + (accent || T.border),
        borderRadius: 8,
        padding: '14px 16px',
        marginBottom: 12,
      }}>
        <div style={{
          display: 'flex', alignItems: 'center', gap: 6,
          marginBottom: 12,
          paddingBottom: 10,
          borderBottom: '1px solid ' + T.border,
        }}>
          {icon && <span style={{ fontSize: 15 }}>{icon}</span>}
          <h2 style={{ fontSize: 13, fontWeight: 600, color: T.text, margin: 0, letterSpacing: '0.2px' }}>
            {title}
          </h2>
        </div>
        {children}
      </div>
    );
  }

  // ── 雙欄 Row ────────────────────────────────────────────────────────────────

  function TwoCol({ children }) {
    return (
      <div style={{
        display: 'grid',
        gridTemplateColumns: 'repeat(auto-fit, minmax(360px, 1fr))',
        gap: 12,
        marginBottom: 12,
      }}>
        {children}
      </div>
    );
  }

  // ── 1. 美股昨夜訊號 ─────────────────────────────────────────────────────────
  // 資料源：live.news?.us_signals（陣列，每項 {symbol, name?, change_pct, close?}）

  function USSignalsCard({ signals, loading }) {
    if (loading) {
      return (
        <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
          {[1, 2, 3, 4].map(i => <Skeleton key={i} height={22} />)}
        </div>
      );
    }
    if (!signals || signals.length === 0) {
      return <Empty msg="尚無美股訊號（pipeline 跑後自動更新）" />;
    }

    return (
      <div>
        {signals.slice(0, 7).map((s, i) => {
          const chg = Number(s.change_pct);
          const up = chg > 0;
          const flat = chg === 0;
          const color = flat ? T.flat : up ? T.up : T.down;
          const bgColor = flat ? T.surface2 : up ? T.upSoft : T.downSoft;

          return (
            <div key={s.symbol || i} style={{
              display: 'flex', alignItems: 'center', justifyContent: 'space-between',
              padding: '7px 0',
              borderBottom: i < Math.min(signals.length, 7) - 1 ? '1px solid ' + T.border : 'none',
            }}>
              <div style={{ display: 'flex', flexDirection: 'column', gap: 1 }}>
                <span style={{ fontSize: 13, fontWeight: 600, color: T.text, fontFamily: T.fontMono }}>
                  {s.symbol || '—'}
                </span>
                {s.name && (
                  <span style={{ fontSize: 11, color: T.text3 }}>{s.name}</span>
                )}
              </div>
              <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                {s.close != null && (
                  <span style={{ fontSize: 12, color: T.text2, fontFamily: T.fontMono }}>
                    {Number(s.close).toFixed(2)}
                  </span>
                )}
                <span style={{
                  fontSize: 12, fontWeight: 600, fontFamily: T.fontMono,
                  color: color,
                  background: bgColor,
                  padding: '2px 7px', borderRadius: 4,
                  minWidth: 60, textAlign: 'right',
                }}>
                  {pct(chg)}
                </span>
              </div>
            </div>
          );
        })}
      </div>
    );
  }

  // ── 2. 三大法人 Top 10 買超 ─────────────────────────────────────────────────
  // 資料源：live.flow_tw?.top10_buy（[{code, name, net_buy_amount}]）

  function TopBuyCard({ top10, loading }) {
    if (loading) {
      return (
        <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
          {[1, 2, 3, 4, 5].map(i => <Skeleton key={i} height={22} />)}
        </div>
      );
    }
    if (!top10 || top10.length === 0) {
      return <Empty msg="尚無三大法人資料" />;
    }

    const maxAmt = Math.max(...top10.slice(0, 10).map(s => Math.abs(Number(s.net_buy_amount) || 0))) || 1;

    return (
      <div>
        {top10.slice(0, 10).map((s, i) => {
          const amt = Number(s.net_buy_amount) || 0;
          const barPct = Math.min(100, (Math.abs(amt) / maxAmt) * 100);

          return (
            <div key={s.code || i} style={{
              padding: '6px 0',
              borderBottom: i < Math.min(top10.length, 10) - 1 ? '1px solid ' + T.border : 'none',
            }}>
              <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 3 }}>
                <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
                  <span style={{
                    fontSize: 10, color: T.text4, fontFamily: T.fontMono,
                    width: 18, textAlign: 'right', flexShrink: 0,
                  }}>
                    {i + 1}
                  </span>
                  <span style={{ fontSize: 13, fontWeight: 600, color: T.text, fontFamily: T.fontMono }}>
                    {s.code}
                  </span>
                  {s.name && (
                    <span style={{ fontSize: 12, color: T.text2 }}>{s.name}</span>
                  )}
                </div>
                <span style={{
                  fontSize: 12, fontWeight: 600, color: T.up,
                  fontFamily: T.fontMono, flexShrink: 0,
                }}>
                  {yi(amt)}
                </span>
              </div>
              {/* 水平 bar */}
              <div style={{
                height: 3, background: T.hover, borderRadius: 2, marginLeft: 24,
              }}>
                <div style={{
                  height: '100%', width: barPct + '%',
                  background: T.up, borderRadius: 2,
                  transition: 'width 0.6s ease',
                }} />
              </div>
            </div>
          );
        })}
      </div>
    );
  }

  // ── 3. 台股今日題材 Top 5 ───────────────────────────────────────────────────
  // 資料源：live.themes?.themes（[{name, heat, stocks?}]）

  const HEAT_COLORS = [
    { bg: 'oklch(0.95 0.06 25)',  fg: T ? T.up   : '#c00' },
    { bg: 'oklch(0.95 0.05 40)',  fg: 'oklch(0.58 0.18 40)' },
    { bg: 'oklch(0.96 0.04 65)',  fg: T ? T.warn : '#b80' },
    { bg: T ? T.surface2 : '#f4f4f4', fg: T ? T.text2 : '#555' },
    { bg: T ? T.surface2 : '#f4f4f4', fg: T ? T.text3 : '#888' },
  ];

  function ThemesCard({ themes, loading }) {
    if (loading) {
      return (
        <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
          {[1, 2, 3, 4, 5].map(i => <Skeleton key={i} height={20} />)}
        </div>
      );
    }
    if (!themes || themes.length === 0) {
      return <Empty msg="尚無題材資料（等待 tw-theme-scanner 結果）" />;
    }

    return (
      <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
        {themes.slice(0, 5).map((th, i) => {
          const hc = HEAT_COLORS[Math.min(i, HEAT_COLORS.length - 1)];
          const heat = th.heat != null ? Number(th.heat) : null;
          return (
            <div key={th.name || i} style={{
              display: 'flex', alignItems: 'center', gap: 10,
              padding: '8px 10px',
              background: hc.bg, borderRadius: 6,
            }}>
              <span style={{
                fontSize: 11, fontWeight: 700, color: hc.fg,
                fontFamily: T.fontMono, width: 18, flexShrink: 0, textAlign: 'center',
              }}>
                {i + 1}
              </span>
              <span style={{ fontSize: 13, color: T.text, flex: 1, fontWeight: i === 0 ? 600 : 400 }}>
                {th.name}
              </span>
              {heat != null && (
                <span style={{
                  fontSize: 11, color: hc.fg, fontFamily: T.fontMono,
                  background: 'rgba(255,255,255,0.6)',
                  padding: '1px 6px', borderRadius: 3,
                  flexShrink: 0,
                }}>
                  熱度 {heat}
                </span>
              )}
              {th.stocks && th.stocks.length > 0 && (
                <span style={{ fontSize: 11, color: T.text3, flexShrink: 0 }}>
                  {th.stocks.slice(0, 3).join(' ')}
                </span>
              )}
            </div>
          );
        })}
      </div>
    );
  }

  // ── 4. 自選股觸發 Alert ─────────────────────────────────────────────────────
  // 資料源：fetchAPI /api/notifications?unread_only=true&limit=20，filter type='alert'

  const ALERT_TYPE_LABELS = {
    volume_spike:    '量爆',
    breakout:        '突破',
    inst_buy_streak: '法人買',
    news:            '新聞',
    target_hit:      '目標價',
    stop_loss_hit:   '停損',
  };

  const ALERT_TYPE_ACCENT = {
    target_hit:   T ? T.up   : '#c00',
    stop_loss_hit:T ? T.down : '#090',
    volume_spike: T ? T.warn : '#b80',
    breakout:     T ? T.up   : '#c00',
    inst_buy_streak: T ? T.primary : '#33c',
    news:         T ? T.text3 : '#888',
  };

  function AlertsCard({ alerts, loading }) {
    if (loading) {
      return (
        <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
          {[1, 2, 3].map(i => <Skeleton key={i} height={44} />)}
        </div>
      );
    }
    if (!alerts || alerts.length === 0) {
      return <Empty msg="無觸發中的 alert（自選股設定 alert 後這裡會顯示）" />;
    }

    return (
      <div style={{ display: 'flex', flexDirection: 'column', gap: 0 }}>
        {alerts.slice(0, 6).map((a, i) => {
          // 嘗試從 title 推斷 type（notifications 存的是字串 title）
          const rawType = a.alert_type || '';
          const label = ALERT_TYPE_LABELS[rawType] || rawType || 'Alert';
          const accent = ALERT_TYPE_ACCENT[rawType] || (T ? T.text3 : '#888');

          return (
            <div key={a.id || i} style={{
              padding: '10px 0',
              borderBottom: i < Math.min(alerts.length, 6) - 1 ? '1px solid ' + T.border : 'none',
              display: 'flex', gap: 10, alignItems: 'flex-start',
            }}>
              {/* 左側標籤 */}
              <div style={{
                flexShrink: 0, minWidth: 44, textAlign: 'center',
                padding: '2px 6px', borderRadius: 4,
                background: accent + '18',
                border: '1px solid ' + accent + '40',
              }}>
                <span style={{ fontSize: 10, fontWeight: 700, color: accent, fontFamily: T.fontMono }}>
                  {label}
                </span>
              </div>
              {/* 右側文字 */}
              <div style={{ flex: 1 }}>
                <div style={{ fontSize: 13, color: T.text, marginBottom: 2, lineHeight: 1.4 }}>
                  {a.title}
                </div>
                {a.body && (
                  <div style={{ fontSize: 11, color: T.text3, lineHeight: 1.4 }}>{a.body}</div>
                )}
              </div>
            </div>
          );
        })}
      </div>
    );
  }

  // ── 5. 今日 AI 推薦 Top 5 ───────────────────────────────────────────────────
  // 資料源：live.picks?.picks（[{code, name, entry_low, entry_high, target_short,
  //          stop_loss, horizon, score, themes, llm_card}]）

  const HORIZON_LABEL = { short: '短線', mid: '中線', long: '長線' };
  const HORIZON_COLOR = {
    short: T ? T.up      : '#c00',
    mid:   T ? T.primary : '#33c',
    long:  T ? T.warn    : '#b80',
  };

  function PicksCard({ picks, loading }) {
    if (loading) {
      return (
        <div style={{
          display: 'grid',
          gridTemplateColumns: 'repeat(auto-fill, minmax(200px, 1fr))',
          gap: 10,
        }}>
          {[1, 2, 3, 4, 5].map(i => <Skeleton key={i} height={90} />)}
        </div>
      );
    }
    if (!picks || picks.length === 0) {
      return <Empty msg="今日無推薦（pipeline 尚未產出）" />;
    }

    return (
      <div style={{
        display: 'grid',
        gridTemplateColumns: 'repeat(auto-fill, minmax(200px, 1fr))',
        gap: 10,
      }}>
        {picks.slice(0, 5).map((p, i) => {
          const hz = p.horizon || 'short';
          const hzColor = HORIZON_COLOR[hz] || (T ? T.text3 : '#888');
          const hzLabel = HORIZON_LABEL[hz] || hz;

          return (
            <div key={p.code || i} style={{
              padding: '12px 14px',
              background: T.bg,
              borderRadius: 7,
              border: '1px solid ' + T.border,
              display: 'flex', flexDirection: 'column', gap: 6,
              transition: 'border-color 0.15s',
            }}
              onMouseEnter={e => e.currentTarget.style.borderColor = T.borderStrong}
              onMouseLeave={e => e.currentTarget.style.borderColor = T.border}
            >
              {/* 上排：代號 + 波段 */}
              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
                <span style={{
                  fontSize: 15, fontWeight: 700, color: T.text, fontFamily: T.fontMono,
                }}>
                  {p.code}
                </span>
                <span style={{
                  fontSize: 10, fontWeight: 600, color: hzColor,
                  background: hzColor + '18',
                  padding: '1px 6px', borderRadius: 3,
                  border: '1px solid ' + hzColor + '40',
                }}>
                  {hzLabel}
                </span>
              </div>
              {/* 名稱 */}
              {p.name && (
                <div style={{ fontSize: 12, color: T.text2 }}>{p.name}</div>
              )}
              {/* 進場 / 目標 */}
              <div style={{ fontSize: 11, color: T.text3, lineHeight: 1.6 }}>
                {p.entry_low != null && p.entry_high != null && (
                  <div>進場 <span style={{ color: T.text2, fontFamily: T.fontMono }}>
                    {p.entry_low}–{p.entry_high}
                  </span></div>
                )}
                {p.target_short != null && (
                  <div>目標 <span style={{ color: T.up, fontFamily: T.fontMono }}>
                    {p.target_short}
                  </span></div>
                )}
                {p.stop_loss != null && (
                  <div>停損 <span style={{ color: T.down, fontFamily: T.fontMono }}>
                    {p.stop_loss}
                  </span></div>
                )}
              </div>
              {/* 分數 */}
              {p.score != null && (
                <div style={{ fontSize: 11, color: T.text3, marginTop: 2 }}>
                  推薦分數 <span style={{
                    fontFamily: T.fontMono, fontWeight: 600,
                    color: Number(p.score) >= 70 ? T.up : T.text2,
                  }}>
                    {p.score}/100
                  </span>
                </div>
              )}
            </div>
          );
        })}
      </div>
    );
  }

  // ── 6. 未來 7 天總經事件 ─────────────────────────────────────────────────────
  // 資料源：live.macro_calendar?.events（[{date, title, importance, expected_time, region?}]）

  const IMPORTANCE_CONFIG = {
    critical: { label: '⚠ 重大',  accent: T ? T.up      : '#c00',   bg: T ? T.upSoft   : '#fee2e2' },
    high:     { label: '重要',    accent: T ? T.warn     : '#b80',   bg: T ? T.warnSoft : '#fef3cd' },
    medium:   { label: '中',      accent: T ? T.text3    : '#888',   bg: T ? T.surface2 : '#f4f4f4' },
    low:      { label: '低',      accent: T ? T.text4    : '#bbb',   bg: T ? T.bg       : '#fafafa' },
  };

  function MacroCard({ events, loading }) {
    if (loading) {
      return (
        <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
          {[1, 2, 3, 4, 5].map(i => <Skeleton key={i} height={24} />)}
        </div>
      );
    }
    if (!events || events.length === 0) {
      return <Empty msg="未來 14 天無重大總經事件" />;
    }

    // 依日期分組
    const byDate = {};
    events.forEach(e => {
      const d = e.date || '—';
      if (!byDate[d]) byDate[d] = [];
      byDate[d].push(e);
    });

    return (
      <div>
        {Object.entries(byDate).map(([date, items]) => (
          <div key={date} style={{ marginBottom: 12 }}>
            {/* 日期標題 */}
            <div style={{
              fontSize: 11, fontWeight: 600, color: T.text3, fontFamily: T.fontMono,
              marginBottom: 6, letterSpacing: '0.5px',
              paddingBottom: 4,
              borderBottom: '1px solid ' + T.border,
            }}>
              {date}
            </div>
            {items.map((e, ei) => {
              const ic = IMPORTANCE_CONFIG[e.importance] || IMPORTANCE_CONFIG.medium;
              return (
                <div key={ei} style={{
                  display: 'flex', alignItems: 'center', gap: 10,
                  padding: '6px 0',
                  borderBottom: ei < items.length - 1 ? '1px dashed ' + T.border : 'none',
                }}>
                  {/* 重要度標籤 */}
                  <span style={{
                    fontSize: 10, fontWeight: 600, color: ic.accent,
                    background: ic.bg,
                    padding: '1px 6px', borderRadius: 3,
                    flexShrink: 0, minWidth: 36, textAlign: 'center',
                  }}>
                    {ic.label}
                  </span>
                  {/* 事件名稱 */}
                  <span style={{ fontSize: 13, color: T.text, flex: 1 }}>
                    {e.title}
                  </span>
                  {/* 預期時間 */}
                  {e.expected_time && (
                    <span style={{ fontSize: 11, color: T.text3, flexShrink: 0, fontFamily: T.fontMono }}>
                      {e.expected_time}
                    </span>
                  )}
                  {/* 地區 */}
                  {e.region && (
                    <span style={{ fontSize: 10, color: T.text4, flexShrink: 0 }}>
                      {e.region}
                    </span>
                  )}
                </div>
              );
            })}
          </div>
        ))}
      </div>
    );
  }

  // ── 深問提示按鈕區 ───────────────────────────────────────────────────────────
  // 沿用 window.DeepPromptButton（template='briefing'）
  // data shape 對齊 formatBriefing：date / market_indices / top_picks / themes_top5 / self_watchlist_alerts

  function DeepPromptArea({ briefingData }) {
    const DeepBtn = window.DeepPromptButton;
    if (!DeepBtn) return null;

    // 把 briefingData 轉成 formatBriefing 期待的 shape
    const formatted = {
      date: briefingData.date,
      market_indices: briefingData.usSignals
        ? Object.fromEntries(
            (briefingData.usSignals || []).slice(0, 6).map(s => [
              s.symbol, pct(s.change_pct),
            ])
          )
        : null,
      top_picks: (briefingData.todayPicks || []).map(p => ({
        code: p.code,
        name: p.name || '',
        reason: p.llm_card
          ? p.llm_card.slice(0, 80) + (p.llm_card.length > 80 ? '…' : '')
          : (p.themes || []).join('、'),
      })),
      themes_top5: (briefingData.themesTop5 || []).map(t => t.name || t),
      self_watchlist_alerts: (briefingData.alerts || []).map(a => ({
        code: a.code || a.title || '—',
        name: a.name || '',
        message: a.body || '',
      })),
    };

    return (
      <div style={{
        marginTop: 20, padding: '14px 16px',
        background: T.aiSoft, border: '1px solid ' + T.aiBorder,
        borderRadius: 8, display: 'flex', alignItems: 'center', gap: 12,
        flexWrap: 'wrap',
      }}>
        <div style={{ flex: 1, minWidth: 200 }}>
          <div style={{ fontSize: 13, fontWeight: 600, color: T.ai, marginBottom: 2 }}>
            深問今日盤前
          </div>
          <div style={{ fontSize: 11, color: T.text3 }}>
            複製以上所有資料摘要，貼到 Claude / ChatGPT 進一步分析
          </div>
        </div>
        <DeepBtn
          template="briefing"
          data={formatted}
          buttonText="複製深問提示"
          size="md"
          variant="primary"
        />
      </div>
    );
  }

  // ── 主元件：BriefingPage ────────────────────────────────────────────────────

  function BriefingPage() {
    const live = window.STOCK_DATA_LIVE || {};

    // ── 靜態資料：直接從 live 讀，不需額外 fetch ─────
    const macroEvents   = (live.macro_calendar?.events  || []);
    const todayPicks    = (live.picks?.picks             || []).slice(0, 5);
    const flowTw        = live.flow_tw                   || {};
    const top10Buy      = flowTw.top10_buy               || [];
    const themesTop5    = (live.themes?.themes           || []).slice(0, 5);
    const usSignals     = live.news?.us_signals          || [];
    const picksDate     = live.picks?.date               || today();

    // ── 動態資料：需 fetchAPI 抓 notifications（alerts）────
    const [alerts,      setAlerts]      = React.useState([]);
    const [alertLoading, setAlertLoading] = React.useState(true);

    React.useEffect(() => {
      const fetchFn = window.fetchAPI;
      if (!fetchFn) {
        setAlertLoading(false);
        return;
      }
      fetchFn('/api/notifications?unread_only=true&limit=30')
        .then(data => {
          const alertNotifs = (data.notifications || []).filter(n => n.type === 'alert');
          setAlerts(alertNotifs);
        })
        .catch(() => {
          // 靜默失敗，顯示空狀態
        })
        .finally(() => {
          setAlertLoading(false);
        });
    }, []);

    // live 資料是否已載入（簡易判斷：picks 有沒有）
    const liveLoading = Object.keys(live).length === 0;

    // 組給 DeepPromptArea 的資料包
    const briefingData = {
      date:       picksDate,
      todayPicks,
      themesTop5,
      top10Buy,
      macroEvents,
      alerts,
      usSignals,
    };

    return (
      <div style={{
        padding: '20px 16px 40px',
        maxWidth: 1200,
        margin: '0 auto',
        fontFamily: T.fontSans,
        color: T.text,
      }}>
        {/* 頁首 */}
        <PageHeader date={picksDate} loading={liveLoading} />

        {/* 第一橫排：美股訊號 + 三大法人 Top 10 */}
        <TwoCol>
          <Section title="美股昨夜訊號" icon="🇺🇸">
            <USSignalsCard signals={usSignals} loading={liveLoading} />
          </Section>
          <Section title="三大法人 Top 10 買超" icon="📊">
            <TopBuyCard top10={top10Buy} loading={liveLoading} />
          </Section>
        </TwoCol>

        {/* 第二橫排：台股題材 + 自選股觸發 */}
        <TwoCol>
          <Section title="台股今日題材 Top 5" icon="🔥">
            <ThemesCard themes={themesTop5} loading={liveLoading} />
          </Section>
          <Section title="自選股觸發 Alert" icon="⚡">
            <AlertsCard alerts={alerts} loading={alertLoading} />
          </Section>
        </TwoCol>

        {/* 第三區：今日 AI 推薦 Top 5（全寬）*/}
        <Section title="今日 AI 推薦 Top 5" icon="🎯" accent={T.aiBorder}>
          <PicksCard picks={todayPicks} loading={liveLoading} />
        </Section>

        {/* 第四區：未來 7 天總經事件（全寬）*/}
        <Section title="未來 7 天總經事件" icon="📅">
          <MacroCard events={macroEvents} loading={liveLoading} />
        </Section>

        {/* 深問提示按鈕 */}
        <DeepPromptArea briefingData={briefingData} />
      </div>
    );
  }

  // 掛全域
  window.BriefingPage = BriefingPage;

})();
