// Pages for the stock app. Depends on globals from components.jsx + data.js.

const { useState, useMemo, useEffect } = React;

// ─── Sparkline path for portfolio curve ──────────────────────
function sparkPath(points, w, h, pad=4) {
  const min = Math.min(...points), max = Math.max(...points);
  const range = max - min || 1;
  const sx = (w - pad*2) / (points.length - 1);
  return points.map((p, i) => {
    const x = pad + i * sx;
    const y = pad + (h - pad*2) * (1 - (p - min) / range);
    return (i === 0 ? 'M' : 'L') + x.toFixed(1) + ' ' + y.toFixed(1);
  }).join(' ');
}

// ─── Candlestick mock chart (SVG) ────────────────────────────
function MockCandles({ width=340, height=200, indicators }) {
  const data = useMemo(() => {
    const rnd = (() => { let s = 42; return () => { s = (s*9301 + 49297) % 233280; return s/233280; }; })();
    const out = [];
    let close = 100;
    for (let i = 0; i < 60; i++) {
      const open = close;
      const ch = (rnd() - 0.48) * 6;
      close = Math.max(60, open + ch);
      const high = Math.max(open, close) + rnd() * 3;
      const low = Math.min(open, close) - rnd() * 3;
      out.push({ open, close, high, low });
    }
    return out;
  }, []);
  const min = Math.min(...data.map(d => d.low));
  const max = Math.max(...data.map(d => d.high));
  const range = max - min || 1;
  const cw = width / data.length;
  const Y = (v) => 8 + (height - 50) * (1 - (v - min) / range);
  const ma = data.map((_, i) => {
    const w = data.slice(Math.max(0, i-9), i+1);
    return w.reduce((s, x) => s + x.close, 0) / w.length;
  });
  return (
    <svg width="100%" height={height} viewBox={`0 0 ${width} ${height}`} preserveAspectRatio="none">
      {[0,1,2,3].map(i => (
        <line key={i} x1="0" x2={width} y1={8 + (height-50)*i/3} y2={8 + (height-50)*i/3}
              stroke="var(--border)" strokeDasharray="2 4" opacity="0.5"/>
      ))}
      {data.map((d, i) => {
        const x = i * cw + cw/2;
        const up = d.close >= d.open;
        const color = up ? 'var(--up)' : 'var(--down)';
        return (
          <g key={i}>
            <line x1={x} x2={x} y1={Y(d.high)} y2={Y(d.low)} stroke={color} strokeWidth="1"/>
            <rect x={x - cw*0.35} y={Y(Math.max(d.open, d.close))}
                  width={cw*0.7} height={Math.max(1, Math.abs(Y(d.open) - Y(d.close)))}
                  fill={color}/>
          </g>
        );
      })}
      {indicators.ma && <path d={ma.map((v, i) => (i===0?'M':'L') + (i*cw+cw/2) + ' ' + Y(v)).join(' ')}
                              fill="none" stroke="var(--accent)" strokeWidth="1.5"/>}
      {indicators.rsi && (
        <g transform={`translate(0, ${height - 36})`}>
          <rect x="0" y="0" width={width} height="32" fill="var(--bg-2)" opacity="0.5"/>
          <text x="4" y="11" style={{ fontSize: 9, fill: 'var(--src-ai)' }}>RSI 58.2</text>
          <path d={data.map((_, i) => {
            const v = 40 + Math.sin(i/4)*15 + Math.cos(i/2.3)*8;
            return (i===0?'M':'L') + (i*cw + cw/2) + ' ' + (16 + (1 - v/100) * 14);
          }).join(' ')} fill="none" stroke="var(--src-ai)" strokeWidth="1.2"/>
        </g>
      )}
    </svg>
  );
}

// ─── Dashboard ───────────────────────────────────────────────
function DashboardPage({ market, setMarket, term, setTerm, onOpen, style='editorial', tweaks={} }) {
  const [refreshing, setRefreshing] = useState(false);
  const [swipedOut, setSwipedOut] = useState({});
  const recs = window.MOCK_RECS.filter(r => r.market === market && r.term === term && !swipedOut[r.symbol]);
  const lang = window.__LANG || 'zh';
  const modOn = (id) => tweaks[`mod_dashboard_${id}`] !== false;

  const handleRefresh = () => {
    setRefreshing(true);
    setTimeout(() => { setRefreshing(false); setSwipedOut({}); }, 900);
  };

  const now = new Date();
  const dateline = lang === 'zh'
    ? `${now.getFullYear()}年 ${now.getMonth()+1}月 ${now.getDate()}日 · 卷 XII · 第 ${42} 期`
    : `${now.toLocaleDateString('en-US',{ month:'short', day:'numeric', year:'numeric' })} · Vol. XII · No. ${42}`;

  const CardVariant = style === 'dossier' ? DossierCard : style === 'poster' ? PosterCard : style === 'editorial' ? EditorialCard : UnionCard;

  return (
    <div>
      {modOn('breaker') && <CircuitBreakerBanner show={false} />}
      {modOn('weekend') && window.MarketClosedBanner && <window.MarketClosedBanner market={market} />}
      <div className="hdr">
        <div className="hdr-top">
          <h1 className="hdr-title">{style === 'dossier' ? '// ' + (lang === 'zh' ? '今日推荐' : 'DAILY QUEUE').toUpperCase() + ' //' : t('dashboard.title')}</h1>
          <div className="hdr-sub" style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
            <button className={'refresh-btn' + (refreshing ? ' spin' : '')} onClick={handleRefresh} aria-label={t('dashboard.refresh')}>
              <Icon.refresh />
            </button>
            {window.TermPulse ? <window.TermPulse term={term} minutes={7} /> : <PulseDot status="ok" />}
            <span>{t('dashboard.data_fresh', { minutes: 7 })}</span>
          </div>
        </div>
        {style === 'editorial' && <div className="dateline"><span>{dateline}</span><span className="vol">— STOCK BRIEFING —</span></div>}
        {style === 'union' && <div className="dateline"><span>{dateline}</span><span className="vol">FELIX · PRIVATE EDITION</span></div>}
        <MarketTabs value={market} onChange={setMarket} />
        <TermTabs value={term} onChange={setTerm} />
      </div>

      {modOn('radar') && <MarketRadar currentMarket={market} />}
      {modOn('continuity') && window.MarketContinuity && <window.MarketContinuity market={market} />}
      {modOn('verdesk') !== false && window.VerificationDesk && <window.VerificationDesk market={market} onOpen={onOpen} />}
      {modOn('alerts') !== false && window.AlertsRail && <window.AlertsRail market={market} onOpen={onOpen} />}
      {modOn('bullpen') !== false && window.AnalystBullpen && <window.AnalystBullpen market={market} />}

      {refreshing ? <LoadingSkeleton /> :
        recs.length === 0 ? <EmptyState market={market} /> :
        <div>
          {recs.map((r, i) => {
            if (window.markSeen) window.markSeen(market, r.symbol);
            let lpTimer = null;
            const onPressDown = () => {
              lpTimer = setTimeout(() => {
                lpTimer = null;
                window.haptic?.medium?.();
                window.compareToggle?.(r.symbol);
              }, 520);
            };
            const onPressUp = () => { if (lpTimer) { clearTimeout(lpTimer); lpTimer = null; } };
            const cardEl = (
            <div key={r.symbol}
              onPointerDown={onPressDown}
              onPointerUp={onPressUp}
              onPointerLeave={onPressUp}
              onPointerCancel={onPressUp}
              onContextMenu={(e) => { e.preventDefault(); window.compareToggle?.(r.symbol); }}>
            <FlippableCard rec={r}>
              <CardVariant
                rec={r}
                index={i}
                onClick={() => onOpen(r.symbol)}
                onSwipe={style === 'poster' ? (dir) => setSwipedOut(s => ({ ...s, [r.symbol]: dir })) : undefined}
              />
            </FlippableCard>
            </div>
            );
            // Wrap union/editorial/dossier in Swipeable for left/right gestures
            if (style !== 'poster' && window.Swipeable) {
              return (
                <window.Swipeable key={r.symbol}
                  leftActions={[{ label: lang==='zh'?'加自选':'Watch', icon: '★ ', color: 'color-mix(in oklab, var(--up-strong) 80%, #000)' }]}
                  rightActions={[{ label: lang==='zh'?'忽略':'Skip', icon: '✕ ', color: 'color-mix(in oklab, var(--text-3) 50%, #000)' }]}
                  onCommit={(dir) => {
                    if (dir === 'right') setSwipedOut(s => ({ ...s, [r.symbol]: dir }));
                    window.toast?.({
                      msg: dir === 'right' ? (lang==='zh'?`已忽略 ${r.symbol}`:`Skipped ${r.symbol}`) : (lang==='zh'?`已加入自选 · ${r.symbol}`:`Watching · ${r.symbol}`),
                      kind: dir === 'right' ? 'info' : 'success',
                      action: dir === 'right' ? { label: lang==='zh'?'撤销':'Undo', run: () => setSwipedOut(s => { const c = { ...s }; delete c[r.symbol]; return c; }) } : null
                    });
                  }}>
                  {cardEl}
                </window.Swipeable>
              );
            }
            return cardEl;
          })}
          {style === 'poster' && recs.length > 0 && (
            <div style={{ textAlign: 'center', fontSize: 10, color: 'var(--ink-3, var(--text-3))', padding: '4px 0 12px', letterSpacing: '0.1em', textTransform: 'uppercase' }}>
              ← {lang === 'zh' ? '左滑忽略' : 'swipe to ignore'} · {lang === 'zh' ? '右滑买入' : 'swipe to buy'} →
            </div>
          )}
        </div>
      }

      <div className="disclaimer">
        <PulseDot status="ok" /> &nbsp; {t('common.synced_to_cc')} · {t('dashboard.data_fresh', { minutes: 7 })}<br/>
        {t('disclaimer')}
      </div>
    </div>
  );
}

// ─── Detail page ─────────────────────────────────────────────
function DetailPage({ symbol, onBack, style='editorial', tweaks={} }) {
  const rec = window.MOCK_RECS.find(r => r.symbol === symbol) || window.MOCK_RECS[0];
  const [indicators, setIndicators] = useState({ ma: true, rsi: false, macd: false });
  const [expanded, setExpanded] = useState(false);

  const toggle = (k) => setIndicators({ ...indicators, [k]: !indicators[k] });

  const modOn = (id) => tweaks[`mod_detail_${id}`] !== false;
  const DEFAULT_ORDER = ['analysts','causality','chart','backtest','regret','reasoning'];
  const order = Array.isArray(tweaks.order_detail) && tweaks.order_detail.length ? tweaks.order_detail : DEFAULT_ORDER;

  const sections = {
    analysts: modOn('analysts') && (
      <div className="section" key="analysts">
        <h2 className="section-title">{(window.__LANG||'zh')==='zh'?'三源对话 · Three Analysts':'Three Analysts'}</h2>
        <AnalystBubbles scores={rec.scores} reasoning={rec.reasoning} />
        {modOn('opposite') && window.OppositeAnalyst && <window.OppositeAnalyst rec={rec} />}
      </div>
    ),
    causality: modOn('causality') && (
      <div className="section" key="causality">
        <h2 className="section-title">{(window.__LANG||'zh')==='zh'?'因果链 · Why This Score':'Why This Score'}</h2>
        {window.DissentBadge && <window.DissentBadge rec={rec} />}
        {window.CausalityChain && <window.CausalityChain rec={rec} />}
      </div>
    ),
    chart: modOn('chart') && (
      <div className="section" key="chart">
        <h2 className="section-title">
          <span>K线 · Price Chart</span>
          <span style={{ fontSize: 10, color: 'var(--text-3)', textTransform: 'none', letterSpacing: 0 }}>60 D</span>
        </h2>
        <div className="chart-panel"><MockCandles indicators={indicators} /></div>
        <div className="chart-controls">
          {['ma','rsi','macd'].map(k => (
            <button key={k} className={'indicator-toggle' + (indicators[k] ? ' on' : '')} onClick={() => toggle(k)}>
              {t('chart.' + k)}
            </button>
          ))}
        </div>
        {modOn('timeline') && <>
          <h3 className="section-title" style={{ marginTop: 18, fontSize: 11 }}>{(window.__LANG||'zh')==='zh'?'决策时间线 · Decision Timeline':'Decision Timeline'}</h3>
          <DecisionTimeline rec={rec} />
        </>}
      </div>
    ),
    backtest: modOn('backtest') && (
      <div className="section" key="backtest">
        <h2 className="section-title">
          <span>{t('backtest.title')}</span>
          {rec.backtest.sample_size < 30 && <WinRateBadge winRate={0} sampleSize={rec.backtest.sample_size} />}
        </h2>
        <div className="stat-grid">
          <div className="stat-cell"><div className="lab">{t('backtest.win_rate')}</div><div className="v">{fmt.pctFromUnit(rec.backtest.win_rate,{dp:0})}</div><div className="sub">n = {rec.backtest.sample_size}</div></div>
          <div className="stat-cell"><div className="lab">{t('backtest.avg_return')}</div><div className={'v ' + (rec.backtest.avg_return_pct>=0?'up':'down')}>{fmt.pct(rec.backtest.avg_return_pct,{signed:true})}</div><div className="sub">每次推荐</div></div>
          <div className="stat-cell"><div className="lab">{t('risk.max_drawdown')}</div><div className="v down">−{rec.risk.max_drawdown_pct.toFixed(1)}%</div><div className="sub">历史峰谷</div></div>
          <div className="stat-cell"><div className="lab">{t('recommendation.take_profit')}</div><div className="v">{fmt.price(rec.recommendation.take_profit,rec.price.currency)}</div><div className="sub">+{(((rec.recommendation.take_profit/rec.price.current)-1)*100).toFixed(1)}%</div></div>
        </div>
      </div>
    ),
    regret: modOn('regret') && (
      <div className="section" key="regret">
        <h2 className="section-title">{t('regret.title')}</h2>
        <ContractRegret rec={rec} />
        <button className="act-btn primary" style={{ marginTop: 12, width: '100%' }} onClick={() => window.openSignModal && window.openSignModal(rec)}>
          <Icon.check width="14" height="14"/> {(window.__LANG||'zh')==='zh'?'签署承诺书 · Acknowledge & Sign':'Acknowledge & Sign'}
        </button>
      </div>
    ),
    reasoning: modOn('reasoning') && (
      <div className="section" key="reasoning">
        <div className="expand-card">
          <div className="expand-head" onClick={() => setExpanded(!expanded)}>
            <span style={{ fontWeight: 600 }}>{t('recommendation.reasoning')}</span>
            <Icon.chevD width="14" height="14" style={{ transform: expanded ? 'rotate(180deg)' : 'none', transition: 'transform .2s' }} />
          </div>
          {expanded && (
            <div className="expand-body">
              {window.ReasoningWithReceipts && <window.ReasoningWithReceipts rec={rec} />}
              <div style={{ marginTop: 10, fontSize: 11, color: 'var(--text-3)', fontFamily: 'var(--font-mono)', letterSpacing: '0.04em' }}>
                {(window.__LANG||'zh')==='zh'?'点击任一来源查看证据 · 置信度 ':'Tap a source for receipts · confidence '}{fmt.pctFromUnit(rec.recommendation.confidence,{dp:0})} · {rec.recommendation.hold_period_days}d · SL {fmt.price(rec.recommendation.stop_loss,rec.price.currency)} · TP {fmt.price(rec.recommendation.take_profit,rec.price.currency)}
              </div>
            </div>
          )}
        </div>
      </div>
    ),
  };

  return (
    <div>
      <div className="detail-hero">
        <button className="detail-back" onClick={onBack}>
          <Icon.back /> {t('actions.back')}
        </button>
        <div className="detail-symline">
          <span className="symbol">{rec.symbol}</span>
          <span className="badge">{t('dashboard.market_' + rec.market)}</span>
          <span className="badge" style={{ background: 'var(--accent-soft)', color: 'var(--accent-strong)', borderColor: 'rgba(245,158,11,0.3)' }}>
            {t('dashboard.' + rec.term + '_term')}
          </span>
        </div>
        <div className="detail-name">{rec.name}</div>
        <div className="detail-priceblock">
          <span className="px">{fmt.price(rec.price.current, rec.price.currency)}</span>
          <ChangePct value={rec.price.change_pct} large />
        </div>

        <div className="fusion-ring-wrap">
          <FusionRing score={rec.scores.fusion} />
          <div className="info">
            <div className="lab">{t('recommendation.fusion_score')}</div>
            <div className="ttl">
              {rec.scores.fusion >= 80 ? '强烈推荐 · Strong' : rec.scores.fusion >= 65 ? '推荐 · Buy' : '观察 · Watch'}
            </div>
            <div className="why">
              {t('recommendation.confidence')} {fmt.pctFromUnit(rec.recommendation.confidence, { dp: 0 })} · {t('recommendation.hold_period')} {t('recommendation.hold_days', { days: rec.recommendation.hold_period_days })}
            </div>
          </div>
        </div>
      </div>

      {order.map(id => sections[id]).filter(Boolean)}
      {/* legacy sections removed — driven by tweaks order
      _LEGACY_START_

      <div className="section">
        <h2 className="section-title">
          <span>K线 · Price Chart</span>
          <span style={{ fontSize: 10, color: 'var(--text-3)', textTransform: 'none', letterSpacing: 0 }}>60 D</span>
        </h2>
        <div className="chart-panel">
          <MockCandles indicators={indicators} />
        </div>
        <div className="chart-controls">
          {['ma','rsi','macd'].map(k => (
            <button key={k} className={'indicator-toggle' + (indicators[k] ? ' on' : '')} onClick={() => toggle(k)}>
              {t('chart.' + k)}
            </button>
          ))}
        </div>
        <h3 className="section-title" style={{ marginTop: 18, fontSize: 11 }}>{(window.__LANG||'zh')==='zh'?'决策时间线 · Decision Timeline':'Decision Timeline'}</h3>
        <DecisionTimeline rec={rec} />
      </div>

      <div className="section">
        <h2 className="section-title">
          <span>{t('backtest.title')}</span>
          {rec.backtest.sample_size < 30 && <WinRateBadge winRate={0} sampleSize={rec.backtest.sample_size} />}
        </h2>
        <div className="stat-grid">
          <div className="stat-cell">
            <div className="lab">{t('backtest.win_rate')}</div>
            <div className="v">{fmt.pctFromUnit(rec.backtest.win_rate, { dp: 0 })}</div>
            <div className="sub">n = {rec.backtest.sample_size}</div>
          </div>
          <div className="stat-cell">
            <div className="lab">{t('backtest.avg_return')}</div>
            <div className={'v ' + (rec.backtest.avg_return_pct >= 0 ? 'up' : 'down')}>
              {fmt.pct(rec.backtest.avg_return_pct, { signed: true })}
            </div>
            <div className="sub">每次推荐</div>
          </div>
          <div className="stat-cell">
            <div className="lab">{t('risk.max_drawdown')}</div>
            <div className="v down">−{rec.risk.max_drawdown_pct.toFixed(1)}%</div>
            <div className="sub">历史峰谷</div>
          </div>
          <div className="stat-cell">
            <div className="lab">{t('recommendation.take_profit')}</div>
            <div className="v">{fmt.price(rec.recommendation.take_profit, rec.price.currency)}</div>
            <div className="sub">+{(((rec.recommendation.take_profit/rec.price.current) - 1)*100).toFixed(1)}%</div>
          </div>
        </div>
      </div>

      <div className="section">
        <h2 className="section-title">{t('regret.title')}</h2>
        <ContractRegret rec={rec} />
        <button className="act-btn primary" style={{ marginTop: 12, width: '100%' }} onClick={() => window.openSignModal && window.openSignModal(rec)}>
          <Icon.check width="14" height="14"/> {(window.__LANG||'zh')==='zh'?'签署承诺书 · Acknowledge & Sign':'Acknowledge & Sign'}
        </button>
        <div className="regret-panel" style={{ display: 'none' }}>
          <div className="regret-head">
            <div className="regret-icon"><Icon.alert width="16" height="16"/></div>
            <div className="ttl">{t('regret.subtitle')}</div>
          </div>
          <div className="stat-grid">
            <div className="stat-cell">
              <div className="lab">{t('regret.loss_prob')}</div>
              <div className="v down">{fmt.pctFromUnit(rec.risk.loss_probability, { dp: 0 })}</div>
              <div className="sub">基于历史样本</div>
            </div>
            <div className="stat-cell">
              <div className="lab">{t('regret.max_loss')}</div>
              <div className="v down">−{((rec.price.current - rec.recommendation.stop_loss)/rec.price.current*100).toFixed(1)}%</div>
              <div className="sub">至止损位</div>
            </div>
            <div className="stat-cell" style={{ gridColumn: '1 / -1' }}>
              <div className="lab">{t('regret.worst_loss')}</div>
              <div className="v down">−{rec.risk.max_drawdown_pct.toFixed(1)}%</div>
              <div className="sub">{rec.backtest.sample_size} 次历史推荐中最差一次</div>
            </div>
          </div>
        </div>
      </div>

      <div className="section">
        <div className="expand-card">
          <div className="expand-head" onClick={() => setExpanded(!expanded)}>
            <span style={{ fontWeight: 600 }}>{t('recommendation.reasoning')}</span>
            <Icon.chevD width="14" height="14" style={{ transform: expanded ? 'rotate(180deg)' : 'none', transition: 'transform .2s' }} />
          </div>
          {expanded && (
            <div className="expand-body">
              基于三源融合算法,本次推荐综合了技术指标({rec.reasoning.technical})、AI 分析({rec.reasoning.ai})与外部专家共识({rec.reasoning.expert})。
              <br/><br/>
              当前置信度为 {fmt.pctFromUnit(rec.recommendation.confidence, { dp: 0 })},建议持有周期 {rec.recommendation.hold_period_days} 天。
              止损位设于 {fmt.price(rec.recommendation.stop_loss, rec.price.currency)},距现价约 {((rec.price.current - rec.recommendation.stop_loss)/rec.price.current*100).toFixed(1)}%。
              目标位 {fmt.price(rec.recommendation.take_profit, rec.price.currency)},风险收益比约 1:{(((rec.recommendation.take_profit-rec.price.current)/(rec.price.current-rec.recommendation.stop_loss))).toFixed(2)}。
            </div>
          )}
        </div>
      </div>

      _LEGACY_END_ */}

      <div className="action-bar">
        <button className="act-btn primary"><Icon.zap width="14" height="14"/> {t('actions.buy')}</button>
        <button className="act-btn" aria-label={t('actions.favorite')}><Icon.star width="16" height="16"/></button>
        <button className="act-btn" aria-label={t('actions.remind')}><Icon.bell width="16" height="16"/></button>
        <button className="act-btn" aria-label={t('actions.ignore')}><Icon.x width="16" height="16"/></button>
      </div>
      {window.StickyDetailBar && <window.StickyDetailBar rec={rec} onBack={onBack} />}
    </div>
  );
}

// ─── Portfolio page ──────────────────────────────────────────
function PortfolioPage() {
  const p = window.MOCK_POSITIONS;
  const curvePts = useMemo(() => {
    const rnd = (() => { let s = 7; return () => { s = (s*9301 + 49297) % 233280; return s/233280; }; })();
    const out = [100000];
    for (let i = 0; i < 60; i++) out.push(out[out.length-1] * (1 + (rnd() - 0.45) * 0.018));
    return out;
  }, []);
  const total = curvePts[curvePts.length - 1];
  const pnl = total - 100000;
  const pnlPct = pnl / 100000 * 100;

  return (
    <div>
      <div className="hdr">
        <div className="hdr-top">
          <h1 className="hdr-title">{t('portfolio.title')}</h1>
          <div className="hdr-sub"><span className="dot" /> {t('dashboard.synced')}</div>
        </div>
      </div>

      <div className="kpi-row">
        <div className="kpi">
          <div className="lab">{t('portfolio.total_value')}</div>
          <div className="v">¥{Math.round(total).toLocaleString()}</div>
          <div className="delta" style={{ color: 'var(--text-3)' }}>NAV</div>
        </div>
        <div className="kpi">
          <div className="lab">{t('portfolio.cumulative_return')}</div>
          <div className={'v ' + (pnl >= 0 ? 'up' : 'down')}>{pnl >= 0 ? '+' : ''}{fmt.pct(pnlPct, { dp: 1 })}</div>
          <div className="delta" style={{ color: pnl >= 0 ? 'var(--up)' : 'var(--down)' }}>
            {pnl >= 0 ? '+' : ''}¥{Math.round(pnl).toLocaleString()}
          </div>
        </div>
        <div className="kpi">
          <div className="lab">{t('portfolio.win_count')}</div>
          <div className="v">{p.closed.wins}/{p.closed.losses}</div>
          <div className="delta" style={{ color: 'var(--text-3)' }}>{fmt.pctFromUnit(p.closed.wins/p.closed.total, { dp: 0 })}</div>
        </div>
      </div>

      <div className="curve-card">
        <div className="head">
          <div>
            <div style={{ fontSize: 11, color: 'var(--text-3)', textTransform: 'uppercase', letterSpacing: '0.05em' }}>60 日曲线</div>
            <div className="total">¥{Math.round(total).toLocaleString()}</div>
          </div>
          <div style={{ textAlign: 'right' }}>
            <div className={pnl >= 0 ? 'ch up' : 'ch down'} style={{ fontSize: 14, justifyContent: 'flex-end' }}>
              {pnl >= 0 ? <Icon.arrowUp width="14" height="14"/> : <Icon.arrowDown width="14" height="14"/>}
              {fmt.pct(Math.abs(pnlPct), { dp: 2 })}
            </div>
          </div>
        </div>
        <svg width="0" height="0" style={{ display: 'none' }}/>
        <window.InteractiveCurve points={curvePts} base={100000} currency="¥"/>
      </div>

      <div style={{ padding: '0 16px 8px' }}>
        <h2 className="section-title" style={{ margin: '6px 0 6px' }}>{t('portfolio.open_positions')}</h2>
      </div>
      <div className="pos-row head">
        <div>Symbol / {t('portfolio.entry')}</div>
        <div className="right">{t('portfolio.now')} / {t('portfolio.pnl')}</div>
        <div className="right">{t('portfolio.held', { days: '' })}/ {t('portfolio.to_sl')}</div>
      </div>
      {p.open.map(pos => {
        const ret = (pos.current - pos.entry) / pos.entry * 100;
        const slDist = (pos.current - pos.stop_loss) / pos.current * 100;
        return (
          <div key={pos.symbol} className="pos-row">
            <div>
              <div className="sym">{pos.symbol}</div>
              <div className="meta">@ {fmt.price(pos.entry, pos.market === 'cn' ? 'CNY' : pos.market === 'crypto' ? 'USDT' : 'USD')}</div>
            </div>
            <div className="right">
              <div className="sym">{fmt.price(pos.current, pos.market === 'cn' ? 'CNY' : pos.market === 'crypto' ? 'USDT' : 'USD')}</div>
              <div className={'meta ' + (ret >= 0 ? 'up' : 'down')} style={{ color: ret >= 0 ? 'var(--up)' : 'var(--down)' }}>
                {ret >= 0 ? '+' : ''}{ret.toFixed(2)}%
              </div>
            </div>
            <div className="right">
              <div className="sym">{pos.days}d</div>
              <div className="meta">−{slDist.toFixed(1)}%</div>
            </div>
          </div>
        );
      })}

      <div style={{ padding: '16px 16px 8px' }}>
        <h2 className="section-title" style={{ margin: 0 }}>市场分布</h2>
      </div>
      <div style={{ padding: '0 16px 16px' }}>
        <window.DonutAllocation
          currency="¥"
          segments={[
            { lab: t('dashboard.market_us'),     v: Math.round(total * 0.42), c: 'var(--src-tech)' },
            { lab: t('dashboard.market_cn'),     v: Math.round(total * 0.36), c: 'var(--up)' },
            { lab: t('dashboard.market_crypto'), v: Math.round(total * 0.22), c: 'var(--accent)' },
          ]}
        />
      </div>
      {window.SignedLedger && <window.SignedLedger />}
    </div>
  );
}

// ─── Strategy page ───────────────────────────────────────────
function StrategyPage() {
  const [strats, setStrats] = useState(window.MOCK_STRATEGIES);
  const cbActive = strats.some(s => s.win_30d < 0.30 && s.status === 'paused');

  const toggleStrat = (id) => {
    setStrats(strats.map(s => s.id === id ? {
      ...s,
      status: s.status === 'enabled' ? 'paused' : 'enabled'
    } : s));
  };

  return (
    <div>
      <div className="hdr">
        <div className="hdr-top">
          <h1 className="hdr-title">{t('strategy.title')}</h1>
          <div className="hdr-sub"><span className="dot" /> 4 active</div>
        </div>
      </div>

      <CircuitBreakerBanner show={cbActive} />

      {strats.map(s => {
        const pillCls = s.status === 'enabled' ? 'on' : s.status === 'degraded' ? 'deg' : 'off';
        const lang = window.__LANG || 'zh';
        return (
          <div key={s.id} className="strat-card">
            <div className="strat-head">
              <div>
                <div className="name">{lang === 'zh' ? s.name_zh : s.name}</div>
                <div style={{ fontSize: 11, color: 'var(--text-3)', marginTop: 2, fontFamily: 'var(--font-mono)' }}>{s.id}</div>
              </div>
              <span className={'status-pill ' + pillCls}>{t('strategy.' + s.status)}</span>
            </div>
            <div className="strat-stat-row">
              <div className="strat-stat">
                <div className="lab">{t('strategy.win_30d')}</div>
                <div className="v" style={{ color: s.win_30d >= 0.60 ? 'var(--up)' : s.win_30d >= 0.40 ? 'var(--accent)' : 'var(--down)' }}>
                  {fmt.pctFromUnit(s.win_30d, { dp: 0 })}
                </div>
              </div>
              <div className="strat-stat">
                <div className="lab">{t('strategy.total_recs')}</div>
                <div className="v">{s.total}</div>
              </div>
              <div className="strat-stat">
                <div className="lab">{t('strategy.avg_return')}</div>
                <div className="v" style={{ color: s.avg_return >= 0 ? 'var(--up)' : 'var(--down)' }}>
                  {s.avg_return >= 0 ? '+' : ''}{s.avg_return.toFixed(1)}%
                </div>
              </div>
            </div>
            {/* mini bar showing win rate */}
            <div style={{ height: 4, background: 'var(--bg-3)', borderRadius: 999, overflow: 'hidden', marginBottom: 8 }}>
              <div style={{ width: (s.win_30d*100) + '%', height: '100%',
                background: s.win_30d >= 0.60 ? 'var(--up)' : s.win_30d >= 0.40 ? 'var(--accent)' : 'var(--down)' }}/>
            </div>
            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
              <div style={{ fontSize: 11, color: 'var(--text-3)' }}>{s.status === 'paused' ? '已暂停推送' : '正在生成推荐'}</div>
              <button className={'toggle' + (s.status === 'enabled' ? ' on' : '')} onClick={() => toggleStrat(s.id)} aria-label={'toggle ' + s.id}/>
            </div>
          </div>
        );
      })}
    </div>
  );
}

// ─── Settings page ───────────────────────────────────────────
function SettingsPage({ lang, theme, tweaks, setTweak }) {
  const PALETTES = [
    { id: 'midnight',  name: '深蓝墨水 · Midnight Ink',  swatches: ['#11141b','#e9b04c','#e0444a','#4cb87a'] },
    { id: 'graphite',  name: '石墨极简 · Graphite',      swatches: ['#131313','#d9b25c','#d9434a','#2ea876'] },
    { id: 'jade',      name: '翡翠终端 · Jade Terminal', swatches: ['#0f161c','#d68a4a','#d94f4f','#3fb89a'] },
    { id: 'ember',     name: '余烬暖调 · Ember',         swatches: ['#1a1612','#d49a3a','#cf4a3a','#6b9d5e'] },
    { id: 'neon',      name: '霓虹交易桌 · Neon Desk',   swatches: ['#10131c','#ffb340','#ec3f4d','#1fc78c'] },
    { id: 'parchment', name: '羊皮纸 · Parchment',       swatches: ['#f8f4eb','#b87a1e','#c63d3d','#3f8a5a'] },
    { id: 'highcontrast', name: '高对比 · High Contrast', swatches: ['#000000','#ffdf3a','#ff3838','#00ff7a'] },
  ];

  // module configuration — each entry: id, label, scope, defaultOn
  const DASHBOARD_MODULES = [
    { id: 'radar',      label: '今日大局 · Market Pulse' },
    { id: 'verdesk',    label: '核对桌 · Verification Desk' },
    { id: 'alerts',     label: '需要关注 · Alerts Rail' },
    { id: 'bullpen',    label: '今日值班分析师 · On Duty' },
    { id: 'continuity', label: '市场续读提示 · Continuity Banner' },
    { id: 'breaker',    label: '熔断横幅 · Circuit Breaker' },
    { id: 'weekend',    label: '休市周报 · Weekend Edition' },
  ];
  const DETAIL_MODULES = [
    { id: 'analysts',  label: '三源对话 · Three Analysts' },
    { id: 'opposite',  label: '反方分析师 · Opposite Side' },
    { id: 'causality', label: '因果链 · Why This Score' },
    { id: 'chart',     label: 'K 线图 · Price Chart' },
    { id: 'timeline',  label: '决策时间线 · Decision Timeline' },
    { id: 'backtest',  label: '回测面板 · Backtest' },
    { id: 'regret',    label: '后悔合约 · Regret Contract' },
    { id: 'reasoning', label: 'AI 全文 · Full Reasoning' },
  ];
  const PORTFOLIO_MODULES = [
    { id: 'kpi',        label: 'KPI 指标 · Key Metrics' },
    { id: 'curve',      label: '收益曲线 · Equity Curve' },
    { id: 'holdings',   label: '当前持仓 · Holdings' },
    { id: 'closed',     label: '已平仓历史 · Closed Trades' },
    { id: 'pie',        label: '市场分布饼图 · Allocation Pie' },
  ];

  const getMod = (scope, id, def=true) => {
    const k = `mod_${scope}_${id}`;
    return tweaks[k] !== undefined ? tweaks[k] : def;
  };
  const setMod = (scope, id, val) => setTweak(`mod_${scope}_${id}`, val);

  const getOrder = (scope, defaults) => {
    const k = `order_${scope}`;
    if (tweaks[k] && Array.isArray(tweaks[k])) return tweaks[k];
    return defaults.map(m => m.id);
  };
  const setOrder = (scope, arr) => setTweak(`order_${scope}`, arr);
  const moveItem = (scope, defaults, id, dir) => {
    const cur = [...getOrder(scope, defaults)];
    const i = cur.indexOf(id);
    const j = i + dir;
    if (i < 0 || j < 0 || j >= cur.length) return;
    [cur[i], cur[j]] = [cur[j], cur[i]];
    setOrder(scope, cur);
  };

  const renderModuleGroup = (scope, modules, title) => {
    const order = getOrder(scope, modules);
    const ordered = order.map(id => modules.find(m => m.id === id)).filter(Boolean);
    return (
      <div className="set-group">
        <div className="group-ttl">{title}</div>
        <div className="set-sub">点击 ⇅ 调整顺序 · 开关控制模块显示</div>
        {ordered.map((m, idx) => {
          const on = getMod(scope, m.id, true);
          return (
            <div key={m.id} className="set-row mod-row">
              <div className="mod-order">
                <button className="ord-btn" disabled={idx===0} onClick={() => moveItem(scope, modules, m.id, -1)} aria-label="up">↑</button>
                <button className="ord-btn" disabled={idx===ordered.length-1} onClick={() => moveItem(scope, modules, m.id, 1)} aria-label="down">↓</button>
              </div>
              <div className="mod-rank">{String(idx+1).padStart(2,'0')}</div>
              <div className="mod-label">
                <div className="lab">{m.label}</div>
                <div className="sub">{on ? '已启用 · enabled' : '已隐藏 · hidden'}</div>
              </div>
              <button className={'toggle ' + (on ? 'on' : '')} onClick={() => setMod(scope, m.id, !on)} aria-pressed={on}/>
            </div>
          );
        })}
      </div>
    );
  };

  return (
    <div>
      <div className="hdr">
        <div className="hdr-top">
          <h1 className="hdr-title">{t('settings.title')}</h1>
        </div>
      </div>

      <div className="set-group">
        <div className="group-ttl">主题配色 · Color Palette</div>
        <div className="set-sub">6 套配色 · 即时切换 · 含 dark / light 双版本</div>
        <div className="palette-grid">
          {PALETTES.map(p => {
            const active = (tweaks.palette || 'midnight') === p.id;
            return (
              <button key={p.id} className={'palette-card' + (active ? ' active' : '')}
                      onClick={() => setTweak('palette', p.id)} aria-pressed={active}>
                <div className="palette-swatches">
                  {p.swatches.map((sw, i) => (
                    <span key={i} className="sw" style={{ background: sw }}/>
                  ))}
                </div>
                <div className="palette-name">{p.name}</div>
                {active && <div className="palette-check">✓</div>}
              </button>
            );
          })}
        </div>
      </div>

      <div className="set-group">
        <div className="group-ttl">视觉风格 · Visual Style</div>
        <div className="set-sub">推荐卡的版式语言 · 影响首页 / 详情</div>
        <div className="style-grid">
          {[
            { id: 'union',     name: 'Union',     desc: '编辑层 + 卷宗按需展开' },
            { id: 'editorial', name: 'Editorial', desc: 'FT 报刊版式' },
            { id: 'dossier',   name: 'Dossier',   desc: '分析师卷宗' },
            { id: 'poster',    name: 'Poster',    desc: '交易桌海报' },
          ].map(s => {
            const active = (tweaks.style || 'union') === s.id;
            return (
              <button key={s.id} className={'style-card' + (active ? ' active' : '')}
                      onClick={() => setTweak('style', s.id)} aria-pressed={active}>
                <div className="style-name">{s.name}</div>
                <div className="style-desc">{s.desc}</div>
              </button>
            );
          })}
        </div>
      </div>

      {renderModuleGroup('dashboard', DASHBOARD_MODULES, '首页模块 · Dashboard Modules')}
      {renderModuleGroup('detail',    DETAIL_MODULES,    '详情模块 · Detail Modules')}
      {renderModuleGroup('portfolio', PORTFOLIO_MODULES, '组合模块 · Portfolio Modules')}

      <div className="set-group">
        <div className="group-ttl">动画 & 密度 · Motion & Density</div>
        <div className="set-row">
          <div>
            <div className="lab">动画效果 · Animations</div>
            <div className="sub">入场过渡 / 微交互</div>
          </div>
          <button className={'toggle ' + (tweaks.animations !== false ? 'on' : '')}
                  onClick={() => setTweak('animations', tweaks.animations === false ? true : false)}/>
        </div>
        <div className="set-row">
          <div className="lab">显示密度 · Density</div>
          <div className="seg" style={{ width: 200 }}>
            {[
              { v: 'compact',     label: '紧凑' },
              { v: 'normal',      label: '标准' },
              { v: 'comfortable', label: '宽松' },
            ].map(d => (
              <button key={d.v} className={'seg-btn' + ((tweaks.density || 'normal') === d.v ? ' active' : '')}
                      onClick={() => setTweak('density', d.v)}>{d.label}</button>
            ))}
          </div>
        </div>
      </div>

      <div className="set-group">
        <div className="group-ttl">{t('settings.notifications')}</div>
        {[
          { k: 'pwa', label: t('settings.pwa_push'), sub: 'iOS / Android 主屏', on: true },
          { k: 'macos', label: t('settings.macos_notif'), sub: '桌面横幅 + 声音', on: true },
          { k: 'cc', label: t('settings.status_line'), sub: 'Claude Code statusLine', on: true },
        ].map(r => (
          <div key={r.k} className="set-row">
            <div>
              <div className="lab">{r.label}</div>
              <div className="sub">{r.sub}</div>
            </div>
            <button className={'toggle ' + (r.on ? 'on' : '')}/>
          </div>
        ))}
      </div>

      <div className="set-group">
        <div className="group-ttl">{t('settings.language')} / {t('settings.theme')}</div>
        <div className="set-row">
          <div className="lab">{t('settings.language')}</div>
          <div className="seg" style={{ width: 160 }}>
            <button className={'seg-btn' + (lang === 'zh' ? ' active' : '')} onClick={() => setTweak('lang', 'zh')}>中文</button>
            <button className={'seg-btn' + (lang === 'en' ? ' active' : '')} onClick={() => setTweak('lang', 'en')}>English</button>
          </div>
        </div>
        <div className="set-row">
          <div className="lab">{t('settings.theme')}</div>
          <div className="seg" style={{ width: 200 }}>
            {['dark','light','system'].map(th => (
              <button key={th} className={'seg-btn' + (theme === th ? ' active' : '')} onClick={() => setTweak('theme', th)}>
                {t('settings.theme_' + th)}
              </button>
            ))}
          </div>
        </div>
      </div>

      <div className="set-group">
        <div className="group-ttl">{t('settings.markets')}</div>
        {['us','cn','crypto','th'].map(m => (
          <div key={m} className="set-row">
            <div>
              <div className="lab">{t('dashboard.market_' + m)}</div>
              <div className="sub">默认开启</div>
            </div>
            <button className="toggle on"/>
          </div>
        ))}
      </div>

      <div className="set-group">
        <div className="group-ttl">{t('settings.data_status')}</div>
        {window.MOCK_SOURCES.map(s => (
          <div key={s.id} className="set-row">
            <div style={{ display: 'flex', alignItems: 'center', gap: 8, flex: 1 }}>
              <span className={'dot ' + (s.status === 'ok' ? '' : s.status === 'degraded' ? 'warn' : 'danger')}/>
              <div>
                <div className="lab" style={{ fontFamily: 'var(--font-mono)', fontSize: 13 }}>{s.name}</div>
                <div className="sub">{t('dashboard.market_' + s.market)} · {s.latency}ms</div>
              </div>
            </div>
            <span style={{ fontSize: 11, color: s.status === 'ok' ? 'var(--ok)' : s.status === 'degraded' ? 'var(--warn)' : 'var(--danger)',
                           textTransform: 'uppercase', letterSpacing: '0.05em', fontWeight: 600 }}>{s.status}</span>
          </div>
        ))}
      </div>

      <div className="disclaimer">{t('disclaimer')}</div>
    </div>
  );
}

Object.assign(window, {
  DashboardPage, DetailPage, PortfolioPage, StrategyPage, SettingsPage,
});
