/* Macro, sentiment & cycle widgets */

// === Macro events calendar =============================================
function W_Calendar() {
  const m = window.MOCK;
  return (
    <div className="card" style={{ gridColumn: 'span 7' }}>
      <div className="card-h">
        <CardTitle topic="calendar">Macro calendar — next 14 days</CardTitle>
        <window.DataTag src="fallback" />
      </div>
      <div className="card-b" style={{ paddingTop: 6 }}>
        {(!m.MACRO_EVENTS || m.MACRO_EVENTS.length === 0) && (
          <div style={{ padding: '18px 0', textAlign: 'center', color: 'var(--text-4)', fontSize: 12 }}>
            No high-impact USD events in the next 14 days
          </div>
        )}
        {m.MACRO_EVENTS.map((e, i) => {
          const impactColor = e.impact === 'high' ? 'var(--bear)' : e.impact === 'med' ? 'var(--gold)' : 'var(--text-4)';
          return (
            <div key={i} style={{
              display: 'grid',
              gridTemplateColumns: '94px 50px 1fr 90px 90px 14px',
              gap: 10, padding: '8px 0',
              borderBottom: i < m.MACRO_EVENTS.length - 1 ? '1px solid var(--border-soft)' : 'none',
              alignItems: 'center'
            }}>
              <span className="mono" style={{ fontSize: 11.5, color: 'var(--text-3)' }}>{e.date}</span>
              <span className="mono" style={{ fontSize: 11, color: 'var(--text-4)' }}>{e.time}</span>
              <span style={{ fontSize: 12.5 }}>{e.name}</span>
              <span className="mono" style={{ fontSize: 11.5, color: 'var(--text-3)', textAlign: 'right' }}>
                fcst <span style={{ color: 'var(--text)' }}>{e.forecast}</span>
              </span>
              <span className="mono" style={{ fontSize: 11, color: 'var(--text-4)', textAlign: 'right' }}>
                prior {e.prior}
              </span>
              <span style={{ width: 8, height: 8, borderRadius: 2, background: impactColor, justifySelf: 'end' }} title={e.impact} />
            </div>
          );
        })}
      </div>
    </div>
  );
}

// === DXY — reads from window.MOCK.DXY (real or fallback series) ========
function W_DXY() {
  const m   = window.MOCK;
  const dxy = m.DXY || [];
  const cur = m.DXY_CURRENT || (dxy.length ? dxy[dxy.length - 1] : 102.18);
  const chg = m.DXY_CHG_30D ?? -0.34;
  const n   = dxy.length;
  const dxyTimes = (Array.isArray(m.DXY_TIMES) && m.DXY_TIMES.length === n)
    ? m.DXY_TIMES
    : dxy.map((_, i) => Date.now() - (n - 1 - i) * 86400000);

  return (
    <div className="card" style={{ gridColumn: 'span 5' }}>
      <div className="card-h">
        <CardTitle topic="dxy">DXY · Dollar Index</CardTitle>
        <span className="chip neutral">Dollar Index</span>
      </div>
      <div className="card-b">
        <div style={{ display: 'flex', alignItems: 'baseline', gap: 10 }}>
          <span className="mono" style={{ fontSize: 22, fontWeight: 600 }}>{parseFloat(cur).toFixed(2)}</span>
          <span className={"num " + (chg >= 0 ? 'down' : 'up')} style={{ fontSize: 12 }}>
            {window.fmtSigned(chg, 2)}% · 30D
          </span>
        </div>
        {dxy.length > 0 && <Sparkline values={dxy} times={dxyTimes} height={70} color="var(--violet)" />}
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8, marginTop: 10 }}>
          <Stat label="Fed funds" value={m.FED_FUNDS || '—'} note="implied" color="var(--text)" />
          <Stat label="10Y yield" value={m.US10Y || '—'}    note="latest"  color="var(--text)" />
        </div>
        <div style={{ fontSize: 11.5, color: 'var(--text-2)', marginTop: 10 }}>
          {chg <= -0.3 ? 'Dollar weakening — tailwind for risk assets (inverse to BTC).'
            : chg >= 0.3 ? 'Dollar strengthening — headwind for risk assets (inverse to BTC).'
            : 'Dollar broadly flat — neutral for risk assets.'}
        </div>
      </div>
    </div>
  );
}

// === Inflation — reads from window.MOCK.INFLATION =====================
function W_Inflation() {
  const m = window.MOCK;
  const inf = m.INFLATION || {
    cpi: { vals: [2.8, 2.7, 2.7, 2.6, 2.5, 2.5, 2.4, 2.4, 2.3, 2.3], target: 2 },
    pce: { vals: [2.5, 2.5, 2.4, 2.3, 2.3, 2.2, 2.2, 2.1, 2.1, 2.0], target: 2 },
    ppi: { vals: [2.4, 2.3, 2.2, 2.3, 2.1, 2.0, 2.0, 2.1, 1.9, 1.9], target: 2 },
  };
  const series = [
    { name: 'CPI YoY', ...inf.cpi },
    { name: 'PCE YoY', ...inf.pce },
    { name: 'PPI YoY', ...inf.ppi },
  ];

  return (
    <div className="card" style={{ gridColumn: 'span 6' }}>
      <div className="card-h">
        <CardTitle topic="inflation">US inflation trackers</CardTitle>
        <window.DataTag src="fallback" />
      </div>
      <div className="card-b">
        {series.map((s, i) => {
          const cur  = s.vals[s.vals.length - 1];
          const prev = s.vals[s.vals.length - 2];
          const chg  = cur - prev;
          return (
            <div key={i} style={{
              display: 'grid', gridTemplateColumns: '80px 70px 1fr 70px',
              gap: 12, alignItems: 'center', padding: '10px 0',
              borderBottom: i < series.length - 1 ? '1px solid var(--border-soft)' : 'none'
            }}>
              <span style={{ fontSize: 12.5, color: 'var(--text-2)' }}>{s.name}</span>
              <span className="mono" style={{ fontSize: 16, fontWeight: 500 }}>{cur.toFixed(1)}%</span>
              <Sparkline values={s.vals} height={32} color="var(--info)" baseline={s.target} />
              <span className={"num " + (chg <= 0 ? 'up' : 'down')} style={{ textAlign: 'right', fontSize: 12 }}>
                {window.fmtSigned(chg, 1)}pp
              </span>
            </div>
          );
        })}
        <div style={{ marginTop: 10, padding: '8px 10px', background: 'var(--bg-elev)', borderRadius: 6,
                      fontSize: 11.5, color: 'var(--text-2)' }}>
          {(() => {
            const dirs = series.map(s => s.vals[s.vals.length - 1] - s.vals[s.vals.length - 2]);
            const rising = dirs.filter(d => d > 0.05).length;
            const falling = dirs.filter(d => d < -0.05).length;
            if (rising >= 2) return 'Inflation re-accelerating across trackers — complicates the path to Fed cuts.';
            if (falling >= 2) return 'Disinflation intact across trackers — supports the case for Fed cuts.';
            return 'Mixed inflation trend across the three trackers.';
          })()}
        </div>
      </div>
    </div>
  );
}

// === FOMC countdown — live countdown from window.MOCK.FOMC.nextDate ====
function W_FOMC() {
  const m    = window.MOCK;
  const fomc = m.FOMC || {};
  const nextDate = fomc.nextDate ? new Date(fomc.nextDate) : new Date('2026-06-17T18:00:00Z');

  const [remaining, setRemaining] = React.useState(() => calcRemaining(nextDate));

  React.useEffect(() => {
    const id = setInterval(() => setRemaining(calcRemaining(nextDate)), 60000);
    return () => clearInterval(id);
  }, [nextDate.getTime()]);

  const fmtDate = nextDate.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: '2-digit' });
  const fmtTime = nextDate.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit', hour12: false, timeZone: 'America/New_York' });

  return (
    <div className="card" style={{ gridColumn: 'span 4' }}>
      <div className="card-h">
        <CardTitle topic="fomc">Next FOMC</CardTitle>
        <span className="chip bear"><span className="chip-dot" /> high impact</span>
      </div>
      <div className="card-b">
        <div style={{ display: 'flex', gap: 6, marginBottom: 4 }}>
          {[
            { n: String(remaining.days).padStart(2,'0'),  l: 'days' },
            { n: String(remaining.hours).padStart(2,'0'), l: 'hours' },
            { n: String(remaining.mins).padStart(2,'0'),  l: 'min' },
          ].map((b, i) => (
            <div key={i} style={{
              flex: 1, padding: '10px 0', textAlign: 'center',
              background: 'var(--bg-elev)', borderRadius: 6, fontFamily: 'var(--font-mono)'
            }}>
              <div style={{ fontSize: 24, fontWeight: 600 }}>{b.n}</div>
              <div style={{ fontSize: 9.5, color: 'var(--text-4)', letterSpacing: '0.08em', textTransform: 'uppercase' }}>{b.l}</div>
            </div>
          ))}
        </div>
        <div style={{ fontSize: 11.5, color: 'var(--text-3)', marginTop: 8 }}>{fmtDate} · {fmtTime} ET</div>
        <div style={{ marginTop: 12, display: 'flex', justifyContent: 'space-between', alignItems: 'baseline',
                      padding: '10px 12px', background: 'var(--bg-elev)', borderRadius: 6 }}>
          <span style={{ fontSize: 11, color: 'var(--text-3)' }}>Current target rate</span>
          <span className="mono" style={{ color: 'var(--text)', fontSize: 15, fontWeight: 500 }}>{m.FED_FUNDS || '—'}</span>
        </div>
      </div>
    </div>
  );
}

function calcRemaining(target) {
  const ms = Math.max(0, target.getTime() - Date.now());
  const totalMins = Math.floor(ms / 60000);
  return {
    days:  Math.floor(totalMins / 1440),
    hours: Math.floor((totalMins % 1440) / 60),
    mins:  totalMins % 60,
  };
}

// === Fear & Greed — reads from window.MOCK.FEAR_GREED ==================
function W_FearGreed() {
  const m  = window.MOCK;
  const fg = m.FEAR_GREED || { value: 56, label: 'Greed' };
  const v  = fg.value;
  const segments = [
    { from: 0,  to: 25,  color: 'var(--bear)' },
    { from: 25, to: 50,  color: 'oklch(0.60 0.16 50)' },
    { from: 50, to: 75,  color: 'oklch(0.65 0.15 130)' },
    { from: 75, to: 100, color: 'var(--bull)' },
  ];
  let zoneCopy = 'Neither panic nor euphoria — wait for an extreme before sizing up.';
  if (v <= 25) zoneCopy = 'Extreme fear — historically a zone where forced sellers dominate.';
  else if (v < 50) zoneCopy = 'Fear zone — caution still elevated; wait for an extreme before sizing up.';
  else if (v >= 75) zoneCopy = 'Extreme greed — historically a zone of crowded risk.';
  else if (v >= 50) zoneCopy = 'Greed zone — optimism rising; wait for an extreme before sizing up.';
  return (
    <div className="card" style={{ gridColumn: 'span 4' }}>
      <div className="card-h">
        <CardTitle topic="fear-greed">Fear & Greed Index</CardTitle>
        <span className="chip neutral"><span className="chip-dot" /> {fg.label}</span>
      </div>
      <div className="card-b" style={{ display: 'flex', justifyContent: 'center' }}>
        <div>
          <Gauge value={v} segments={segments} label="MARKET SENTIMENT" />
          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 4, marginTop: 4, fontFamily: 'var(--font-mono)', fontSize: 9, color: 'var(--text-4)', textAlign: 'center' }}>
            <span>EXTR FEAR</span><span>FEAR</span><span>GREED</span><span>EXTR GREED</span>
          </div>
          <div style={{ fontSize: 11.5, color: 'var(--text-2)', marginTop: 10 }}>
            {zoneCopy}
          </div>
        </div>
      </div>
    </div>
  );
}

// === Shared confidence chip (cycle theory widgets) ==================
function ConfidenceChip({ confidence, missRisk, style }) {
  const c = Number.isFinite(Number(confidence)) ? Math.round(Number(confidence)) : 0;
  const risk = missRisk || (c >= 60 ? 'low' : c >= 35 ? 'med' : 'high');
  const label = risk === 'low' ? 'Low' : risk === 'med' ? 'Med' : 'High';
  const kind = risk === 'low' ? 'bull' : risk === 'med' ? 'gold' : 'bear';
  return (
    <span
      className={'chip ' + kind}
      title={'Model fit 0-100. Miss-risk: >=60 Low, 35-59 Med, <35 High.'}
      style={Object.assign({ textTransform: 'none', letterSpacing: '0.02em', fontWeight: 500 }, style || {})}
    >
      Confidence {c}% · {label} miss risk
    </span>
  );
}

// === Halving cycle progress =========================================
function W_Halving() {
  const m = window.MOCK;
  const h = m.HALVING;

  // 1064 / 364 cycle model — anchored on the real ATL/ATH pivots (bottom → top → bottom).
  const PREV_BOTTOM = new Date('2022-11-21T00:00:00Z').getTime(); // 2022 ATL
  const DOWN_DAYS = 364;                                          // top → next bottom (fixed each cycle)
  const halvingMs = new Date((h.lastHalving || '2024-04-19') + 'T00:00:00Z').getTime();
  const ath = h.cycleATH || {};
  const topMs = ath.dayOfCycle ? halvingMs + ath.dayOfCycle * 86400000 : PREV_BOTTOM + 1064 * 86400000;
  const nextBottomMs = topMs + DOWN_DAYS * 86400000;
  const now = Date.now();

  const upDays = Math.round((topMs - PREV_BOTTOM) / 86400000);
  const totalMs = nextBottomMs - PREV_BOTTOM;
  const xOf = (ms) => Math.max(0, Math.min(100, (ms - PREV_BOTTOM) / totalMs * 100));
  const topX = xOf(topMs);
  const nowX = xOf(now);
  const inDowntrend = now >= topMs;
  const daysToBottom = Math.max(0, Math.round((nextBottomMs - now) / 86400000));
  const daysToTop = Math.max(0, Math.round((topMs - now) / 86400000));
  const downProgress = Math.max(0, Math.min(100, Math.round((now - topMs) / (DOWN_DAYS * 86400000) * 100)));

  const fmtMY   = (ms) => new Date(ms).toLocaleDateString('en-US', { month: 'short', year: '2-digit' });
  const fmtFull = (ms) => new Date(ms).toLocaleDateString('en-US', { month: 'short', year: 'numeric' });

  // Projected bottom price — prior cycle bottoms ran ~77% below the cycle top on average.
  const DRAWDOWN = 0.77;
  const bottomPrice = ath.price ? Math.round(ath.price * (1 - DRAWDOWN)) : null;
  const fmtK = (v) => (v >= 1000 ? '$' + (v / 1000).toFixed(1) + 'k' : '$' + v);

  return (
    <div className="card" style={{ gridColumn: 'span 8' }}>
      <div className="card-h">
        <CardTitle topic="halving">Cycle position · 1064 / 364 model</CardTitle>
        <span className="tag">bottom → top → bottom</span>
      </div>
      <div className="card-b">
        <div style={{ display: 'flex', alignItems: 'baseline', gap: 20, marginBottom: 22 }}>
          <div>
            <div className="mono" style={{ fontSize: 22, fontWeight: 600 }}>{inDowntrend ? 'Downtrend' : 'Uptrend'}</div>
            <div style={{ fontSize: 11, color: 'var(--text-3)', marginTop: 1 }}>
              {inDowntrend
                ? `${downProgress}% through the 364-day downtrend · projected bottom ${fmtFull(nextBottomMs)}${bottomPrice ? ' · ~' + fmtK(bottomPrice) : ''}`
                : `${upDays}-day uptrend from the 2022 bottom · top projected ${fmtFull(topMs)}`}
            </div>
          </div>
          <div style={{ marginLeft: 'auto', textAlign: 'right' }}>
            <div className="mono" style={{ fontSize: 22, fontWeight: 600, color: 'var(--gold)' }}>{inDowntrend ? daysToBottom : daysToTop}d</div>
            <div style={{ fontSize: 11, color: 'var(--text-3)' }}>to projected {inDowntrend ? 'bottom' : 'top'}{inDowntrend && bottomPrice ? ' · ~' + fmtK(bottomPrice) : ''}</div>
            <div style={{ marginTop: 6, display: 'flex', justifyContent: 'flex-end' }}>
              <ConfidenceChip confidence={h.confidence} missRisk={h.missRisk} />
            </div>
          </div>
        </div>

        <div style={{ position: 'relative' }}>
          <div style={{ position: 'relative', height: 38, background: 'var(--bg-elev)', borderRadius: 6, overflow: 'hidden' }}>
            <div style={{ position: 'absolute', left: 0, width: topX + '%', top: 0, bottom: 0, background: 'var(--bull)', opacity: 0.28, display: 'flex', alignItems: 'center', paddingLeft: 8 }}>
              <span style={{ fontSize: 10, color: 'var(--text)', fontFamily: 'var(--font-mono)', letterSpacing: '0.04em', textTransform: 'uppercase' }}>Uptrend · {upDays}d</span>
            </div>
            <div style={{ position: 'absolute', left: topX + '%', right: 0, top: 0, bottom: 0, background: 'var(--bear)', opacity: 0.28, display: 'flex', alignItems: 'center', paddingLeft: 8 }}>
              <span style={{ fontSize: 10, color: 'var(--text)', fontFamily: 'var(--font-mono)', letterSpacing: '0.04em', textTransform: 'uppercase' }}>Downtrend · {DOWN_DAYS}d</span>
            </div>
            <div style={{ position: 'absolute', left: topX + '%', top: 0, bottom: 0, width: 2, background: 'var(--gold)', opacity: 0.9 }} />
            <div style={{ position: 'absolute', left: nowX + '%', top: -6, bottom: -6, width: 2, background: 'var(--text)', boxShadow: '0 0 8px oklch(0.9 0 0 / 0.5)' }}>
              <div style={{ position: 'absolute', top: -16, left: '50%', transform: 'translateX(-50%)', fontFamily: 'var(--font-mono)', fontSize: 9.5, color: 'var(--text)', whiteSpace: 'nowrap' }}>you are here</div>
            </div>
          </div>
          {/* Projected cycle-bottom target — radar ping + glow (rendered outside the clip) */}
          <div style={{ position: 'absolute', right: 0, top: 19, transform: 'translate(50%, -50%)', width: 12, height: 12, pointerEvents: 'none' }}>
            <span style={{ position: 'absolute', inset: 0, borderRadius: '50%', border: '2px solid var(--bear)', animation: 'ping 1.8s cubic-bezier(0,0,0.2,1) infinite' }} />
            <span style={{ position: 'absolute', inset: 3, borderRadius: '50%', background: 'var(--bear)', animation: 'targetGlow 1.8s ease-in-out infinite' }} />
          </div>
        </div>

        <div style={{ position: 'relative', height: 26, marginTop: 6 }}>
          <span style={{ position: 'absolute', left: '0%', fontSize: 9, color: 'var(--info)', fontFamily: 'var(--font-mono)', fontWeight: 600, whiteSpace: 'nowrap' }}>▼ Bottom {fmtMY(PREV_BOTTOM)}</span>
          <span style={{ position: 'absolute', left: topX + '%', transform: 'translateX(-50%)', fontSize: 9, color: 'var(--gold)', fontFamily: 'var(--font-mono)', fontWeight: 600, whiteSpace: 'nowrap' }}>▲ Top {fmtMY(topMs)}</span>
          <span style={{ position: 'absolute', left: '100%', transform: 'translateX(-100%)', fontSize: 9, color: 'var(--info)', fontFamily: 'var(--font-mono)', fontWeight: 600, whiteSpace: 'nowrap' }}>▼ Bottom {fmtMY(nextBottomMs)} (proj)</span>
        </div>

        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 10, marginTop: 14 }}>
          <Stat label="Cycle top (ATH)"  value={ath.price ? '$' + window.fmtUSD(ath.price) : '—'} note={fmtFull(topMs)} color="var(--gold)" />
          <Stat label="Uptrend length"   value={upDays + 'd'}          note="2022 bottom → top" />
          <Stat label="Downtrend length" value={DOWN_DAYS + 'd'}       note="fixed each cycle" color="var(--bear)" />
          <Stat label="Projected bottom" value={bottomPrice ? '~' + fmtK(bottomPrice) : fmtFull(nextBottomMs)} note={fmtFull(nextBottomMs) + ' · ' + daysToBottom + 'd · est'} color="var(--bear)" />
        </div>

        <div style={{ marginTop: 10, fontSize: 10.5, color: 'var(--text-4)', lineHeight: 1.5 }}>
          Model: ~1,050–1,071 days up (bottom→top), then 364 days down (top→bottom). Bottom price is an estimate from the ~77% average drawdown off the cycle top — a rough zone, not a target. Not financial advice.
        </div>
      </div>
    </div>
  );
}

// === Wyckoff phase model ================================================
function W_Wyckoff() {
  const m = window.MOCK;
  const w = m.WYCKOFF || { phase: 'Accumulation', confidence: 0, daysInPhase: 0, signals: {}, flag: null };
  const s = w.signals || {};
  const phases = [
    { name: 'Accumulation', color: 'var(--info)', note: 'absorption' },
    { name: 'Markup', color: 'var(--bull)', note: 'demand' },
    { name: 'Distribution', color: 'var(--gold)', note: 'supply' },
    { name: 'Markdown', color: 'var(--bear)', note: 'markdown' },
  ];
  const active = Math.max(0, phases.findIndex(p => p.name === w.phase));
  const fmt = v => v == null || !Number.isFinite(Number(v)) ? '—' : window.fmtUSD(Number(v));
  const funding = s.fundingPct == null ? '—' : window.fmtSigned(s.fundingPct, 3) + '%';
  const crowding = s.longs == null ? '—' : s.longs.toFixed(0) + '/' + (s.shorts == null ? '—' : s.shorts.toFixed(0));
  const range = s.range30d == null ? '—' : s.range30d.toFixed(1) + '% / ' + (s.range180d == null ? '—' : s.range180d.toFixed(1) + '%');
  return (
    <div className="card" style={{ gridColumn: 'span 7' }}>
      <div className="card-h">
        <CardTitle topic="wyckoff">Wyckoff market phase</CardTitle>
        <window.DataTag src="live" />
      </div>
      <div className="card-b">
        <div style={{ display: 'flex', alignItems: 'baseline', gap: 12, marginBottom: 22 }}>
          <div>
            <div className="mono" style={{ fontSize: 22, fontWeight: 600, color: phases[active].color }}>{w.phase}</div>
            <div style={{ color: 'var(--text-3)', fontSize: 11, marginTop: 2 }}>{w.daysInPhase || 0}d in phase</div>
            <div style={{ marginTop: 6 }}><ConfidenceChip confidence={w.confidence} missRisk={w.missRisk} /></div>
          </div>
          {w.flag && <span className="chip" style={{ marginLeft: 'auto', color: w.flag === 'UTAD' ? 'var(--gold)' : 'var(--info)', borderColor: w.flag === 'UTAD' ? 'var(--gold-dim)' : 'var(--info)' }}>{w.flag}</span>}
        </div>
        <div style={{ position: 'relative', paddingTop: 14 }}>
          <div style={{ display: 'flex', height: 38, gap: 3, borderRadius: 6, overflow: 'hidden', background: 'var(--bg-elev)' }}>
            {phases.map((p, i) => (
              <div key={p.name} style={{ flex: 1, background: p.color, opacity: i === active ? 0.82 : 0.25, padding: '7px 8px', border: i === active ? '1px solid ' + p.color : '1px solid transparent' }}>
                <div style={{ fontFamily: 'var(--font-mono)', fontSize: 10, color: 'var(--text)', whiteSpace: 'nowrap' }}>{p.name}</div>
                <div style={{ fontSize: 9, color: 'var(--text-2)', marginTop: 2 }}>{p.note}</div>
              </div>
            ))}
          </div>
          <div style={{ position: 'absolute', top: 5, left: ((active + 0.5) / 4 * 100) + '%', transform: 'translateX(-50%)', fontFamily: 'var(--font-mono)', fontSize: 9, color: 'var(--text)', whiteSpace: 'nowrap' }}>you are here</div>
        </div>
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(5, 1fr)', gap: 7, marginTop: 18 }}>
          <Stat label="Price" value={'$' + fmt(s.price)} note="BTC spot" />
          <Stat label="EMA stack" value={s.price > s.ema50 && s.ema50 > s.ema200 ? 'bull' : s.price < s.ema50 && s.ema50 < s.ema200 ? 'bear' : 'mixed'} note="price · 50 · 200" color={s.price > s.ema50 && s.ema50 > s.ema200 ? 'var(--bull)' : s.price < s.ema50 && s.ema50 < s.ema200 ? 'var(--bear)' : 'var(--text)'} />
          <Stat label="30d / 180d range" value={range} note="compression / expansion" />
          <Stat label="Funding" value={funding} note="latest · 8h" />
          <Stat label="L/S crowding" value={crowding} note={s.mvrv == null ? 'long / short %' : 'MVRV ' + Number(s.mvrv).toFixed(2)} />
        </div>
        <div style={{ marginTop: 12, padding: '8px 10px', background: 'var(--bg-elev)', borderRadius: 6, color: 'var(--text-2)', fontSize: 11, lineHeight: 1.45 }}>
          {w.flagDetail ? w.flagDetail + ' — watch the next range test.' : 'Phase read combines EMA stack, range regime, funding / L-S crowding and MVRV zone.'}
        </div>
        <div style={{ marginTop: 10, fontSize: 10.5, color: 'var(--text-4)', lineHeight: 1.45 }}>{w.disclaimer || 'Rules-based context only; not financial advice.'}</div>
      </div>
    </div>
  );
}

// === Elliott cycle-degree count =========================================
function W_Elliott() {
  const m = window.MOCK;
  const e = m.ELLIOTT || { activeWave: '3', confidence: 0, points: [], nextTarget: {}, alternate: {} };
  const points = e.points || [];
  const prices = points.map(p => Number(p.price)).filter(Number.isFinite);
  const lo = Math.min(...prices, 0), hi = Math.max(...prices, 1), span = Math.max(1, hi - lo);
  const xy = points.map(p => ({ ...p, y: 88 - ((p.price - lo) / span) * 70 }));
  const line = xy.map(p => p.x.toFixed(2) + ',' + p.y.toFixed(2)).join(' ');
  const money = v => v == null || !Number.isFinite(Number(v)) ? '—' : '$' + window.fmtUSD(Number(v));
  return (
    <div className="card" style={{ gridColumn: 'span 5' }}>
      <div className="card-h">
        <CardTitle topic="elliott">Elliott cycle-degree count</CardTitle>
        <span className="chip neutral">Nov 2022 ATL</span>
      </div>
      <div className="card-b">
        <div style={{ display: 'flex', alignItems: 'center', gap: 10, flexWrap: 'wrap' }}>
          <div className="mono" style={{ fontSize: 22, fontWeight: 600, color: 'var(--violet)' }}>Wave {e.activeWave}</div>
          <ConfidenceChip confidence={e.confidence} missRisk={e.missRisk} />
        </div>
        <div style={{ position: 'relative', marginTop: 12, height: 126, background: 'var(--bg-elev)', borderRadius: 6, overflow: 'hidden' }}>
          <svg viewBox="0 0 100 100" preserveAspectRatio="none" style={{ width: '100%', height: '100%', display: 'block' }} aria-label="Elliott wave labeled skeleton">
            <line x1={e.youAreHereX || 0} x2={e.youAreHereX || 0} y1="4" y2="94" stroke="var(--text)" strokeWidth="0.7" strokeDasharray="2 2" opacity="0.9" />
            <polyline points={line} fill="none" stroke="var(--violet)" strokeWidth="1.4" vectorEffect="non-scaling-stroke" />
            {xy.map((p, i) => <g key={p.label + i}><circle cx={p.x} cy={p.y} r={p.label === e.activeWave ? 2.8 : 1.7} fill={p.label === e.activeWave ? 'var(--gold)' : 'var(--violet)'} /><text x={p.x} y={Math.max(9, p.y - 5)} fill="var(--text-2)" fontSize="7" textAnchor="middle">{p.label}</text></g>)}
          </svg>
          <span style={{ position: 'absolute', left: (e.youAreHereX || 0) + '%', top: 3, transform: 'translateX(-50%)', fontFamily: 'var(--font-mono)', fontSize: 8.5, color: 'var(--text)', whiteSpace: 'nowrap' }}>you are here</span>
        </div>
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 7, marginTop: 12 }}>
          <Stat label="Invalidation" value={money(e.invalidationPrice)} note="count fails below / above" color="var(--bear)" />
          <Stat label="Next target zone" value={money(e.nextTarget && e.nextTarget.low) + '–' + money(e.nextTarget && e.nextTarget.high)} note="scenario zone" color="var(--gold)" />
        </div>
        <div style={{ display: 'flex', gap: 12, marginTop: 10, fontSize: 10.5, color: 'var(--text-3)' }}>
          <span style={{ color: e.wave3NotShortest ? 'var(--bull)' : 'var(--bear)' }}>● wave 3 not shortest</span>
          <span style={{ color: e.wave4NoOverlap ? 'var(--bull)' : 'var(--bear)' }}>● wave 4 no overlap</span>
        </div>
        <div style={{ marginTop: 10, padding: '8px 10px', background: 'var(--bg-elev)', borderRadius: 6, fontSize: 10.5, color: 'var(--text-2)' }}>
          Alternate: <span className="mono">Wave {e.alternate && e.alternate.activeWave || '—'}</span> · {e.alternate && e.alternate.confidence || 0}% · secondary count
        </div>
        <div style={{ marginTop: 10, fontSize: 10.5, color: 'var(--text-4)', lineHeight: 1.45 }}>{e.disclaimer || 'Scenario count only; not financial advice.'}</div>
      </div>
    </div>
  );
}
// === Seasonality grid =====================================================
function W_Seasonality() {
  const season = window.MOCK.SEASONALITY || { years: [], rows: [] };
  const years  = season.years;
  const months = ['J','F','M','A','M','J','J','A','S','O','N','D'];
  const data   = season.rows;
  const max = 50;
  function cellColor(v) {
    if (v == null) return 'var(--bg-elev)';
    const t = Math.max(-1, Math.min(1, v / max));
    if (t > 0) return `oklch(${0.4 + t * 0.3} ${0.08 + t * 0.08} 148 / ${0.35 + t * 0.55})`;
    return `oklch(${0.4 + Math.abs(t) * 0.25} ${0.08 + Math.abs(t) * 0.10} 25 / ${0.35 + Math.abs(t) * 0.55})`;
  }
  const monthAvg = months.map((_, mi) => {
    const vs = data.map(row => row[mi]).filter(v => v != null);
    return vs.length ? vs.reduce((a, b) => a + b, 0) / vs.length : 0;
  });

  return (
    <div className="card" style={{ gridColumn: 'span 8' }}>
      <div className="card-h">
        <CardTitle topic="seasonality">Monthly seasonality · BTC % return</CardTitle>
        <window.DataTag src="live" />
      </div>
      <div className="card-b">
        <div style={{ display: 'grid', gridTemplateColumns: '32px repeat(12, 1fr)', gap: 3, fontFamily: 'var(--font-mono)' }}>
          <div></div>
          {months.map((m, i) => <div key={i} style={{ fontSize: 10, color: 'var(--text-4)', textAlign: 'center' }}>{m}</div>)}
          {data.map((row, ri) => (
            <React.Fragment key={ri}>
              <div style={{ fontSize: 10, color: 'var(--text-4)', display: 'flex', alignItems: 'center' }}>{years[ri]}</div>
              {row.map((v, ci) => (
                <div key={ci} style={{
                  height: 28, borderRadius: 3, background: cellColor(v),
                  display: 'grid', placeItems: 'center', fontSize: 10,
                  color: v == null ? 'var(--text-4)' : 'var(--text)',
                  border: '1px solid ' + (v == null ? 'var(--border-soft)' : 'transparent')
                }}>
                  {v == null ? '·' : (v > 0 ? '+' : '') + v}
                </div>
              ))}
            </React.Fragment>
          ))}
          <div style={{ fontSize: 10, color: 'var(--gold)', display: 'flex', alignItems: 'center' }}>AVG</div>
          {monthAvg.map((v, i) => (
            <div key={i} style={{
              height: 28, borderRadius: 3, background: cellColor(v),
              display: 'grid', placeItems: 'center',
              fontSize: 10, fontWeight: 600, color: 'var(--text)',
              outline: '1px solid var(--gold-dim)'
            }}>{(v > 0 ? '+' : '') + v.toFixed(0)}</div>
          ))}
        </div>
        <div style={{ display: 'flex', gap: 14, marginTop: 12, fontSize: 11.5, color: 'var(--text-2)' }}>
          <span>Sept avg <span className="down num">{monthAvg[8].toFixed(1)}%</span> — historically the worst month</span>
          <span style={{ marginLeft: 'auto' }}>Nov avg <span className="up num">+{monthAvg[10].toFixed(1)}%</span> — Q4 rally bias intact</span>
        </div>
      </div>
    </div>
  );
}

function Stat({ label, value, note, color }) {
  return (
    <div style={{ background: 'var(--bg-elev)', padding: '8px 10px', borderRadius: 6 }}>
      <div style={{ fontSize: 10, color: 'var(--text-4)', letterSpacing: '0.06em', textTransform: 'uppercase' }}>{label}</div>
      <div className="mono" style={{ fontSize: 16, color: color || 'var(--text)', marginTop: 2 }}>{value}</div>
      {note && <div style={{ fontSize: 10, color: 'var(--text-4)' }}>{note}</div>}
    </div>
  );
}

Object.assign(window, { ConfidenceChip, W_Calendar, W_DXY, W_Inflation, W_FOMC, W_FearGreed, W_Halving, W_Wyckoff, W_Elliott, W_Seasonality });