// app.jsx — Authenticated app shell + main pages
// Pages: dashboard, detail/:id, search, portfolio, compare, loan, profile, notifications

function AppShell({ route, children }) {
  const { user, logout } = useAuth();
  const wl = useWatchlist();

  useEffect(() => {
    if (!user) navigate('/login');
  }, [user]);

  if (!user) return null;

  const nav = [
    { id: 'dashboard', label: '대시보드', icon: '◉', path: '/app/dashboard' },
    { id: 'search', label: '검색·지도', icon: '⌕', path: '/app/search' },
    { id: 'portfolio', label: '관심 단지', icon: '★', path: '/app/portfolio', badge: wl.ids.length || null },
    { id: 'compare', label: '지역 비교', icon: '⇄', path: '/app/compare' },
    { id: 'loan', label: '대출·세금', icon: '₩', path: '/app/loan' },
    { id: 'notifications', label: '알림', icon: '◷', path: '/app/notifications', badge: 3 },
  ];

  return (
    <div style={{ minHeight: '100vh', background: T.bg, color: T.fg, fontFamily: T.font, display: 'flex', flexDirection: 'column' }}>
      <AppHeader user={user} onLogout={logout} />
      <div style={{ display: 'flex', flex: 1, minHeight: 0 }}>
        <aside style={{
          width: 220, flexShrink: 0, borderRight: '1px solid ' + T.line,
          background: T.bgAlt, padding: '20px 12px', display: 'flex', flexDirection: 'column',
        }}>
          <div style={{ fontSize: 10, color: T.fgMuted, fontFamily: T.mono, letterSpacing: 1.2, padding: '0 8px 8px' }}>주요 메뉴</div>
          {nav.map((n) => {
            const active = route.startsWith(n.path);
            return (
              <a key={n.id} href={'#' + n.path}
                data-track="nav_sidebar" data-track-target={n.id}
                style={{
                  display: 'flex', alignItems: 'center', gap: 10,
                  padding: '10px 12px', borderRadius: 4, marginBottom: 2,
                  background: active ? T.cardHi : 'transparent',
                  color: active ? T.fg : T.fgDim, textDecoration: 'none',
                  fontSize: 13, fontWeight: active ? 600 : 500,
                  borderLeft: active ? '2px solid ' + T.hot : '2px solid transparent',
                }}>
                <span style={{ width: 18, color: active ? T.hot : T.fgMuted, fontFamily: T.mono, fontSize: 14 }}>{n.icon}</span>
                <span style={{ flex: 1 }}>{n.label}</span>
                {n.badge && <span style={{
                  background: T.hot, color: T.bg, fontSize: 10, fontFamily: T.mono,
                  padding: '1px 6px', borderRadius: 8, fontWeight: 700,
                }}>{n.badge}</span>}
              </a>
            );
          })}

          <div style={{ marginTop: 'auto', padding: 12, background: T.card, border: '1px solid ' + T.line, borderRadius: 4 }}>
            <div style={{ fontSize: 11, color: T.ai, fontFamily: T.mono, letterSpacing: 1, marginBottom: 6 }}>AI 챗봇</div>
            <div style={{ fontSize: 12, color: T.fgDim, lineHeight: 1.5 }}>매수·이자·세금·대출 한도까지 즉시 답변</div>
            <a href="#/app/chat" data-track="open_chatbot" style={{
              display: 'block', marginTop: 10, padding: '8px 12px',
              background: T.ai, color: T.fg, textAlign: 'center', borderRadius: 3,
              fontSize: 12, fontWeight: 600, textDecoration: 'none',
            }}>들어보기</a>
          </div>
        </aside>

        <main style={{ flex: 1, minWidth: 0, overflow: 'auto' }}>
          {children}
        </main>
      </div>
    </div>
  );
}

function AppHeader({ user, onLogout }) {
  const [searchOpen, setSearchOpen] = useState(false);
  const [q, setQ] = useState('');
  const [showMenu, setShowMenu] = useState(false);
  return (
    <header style={{
      height: 56, borderBottom: '1px solid ' + T.line, background: T.bgAlt,
      display: 'flex', alignItems: 'center', padding: '0 16px', gap: 16, flexShrink: 0,
    }}>
      <a href="#/app/dashboard" data-track="header_logo" style={{ display: 'flex', alignItems: 'center', gap: 10, textDecoration: 'none' }}>
        <Logo size={28} />
        <span style={{ fontSize: 15, fontWeight: 700, color: T.fg }}>우리집사기</span>
        <span style={{ fontSize: 10, color: T.fgFaint, fontFamily: T.mono, marginTop: 2 }}>TERMINAL</span>
      </a>

      <nav style={{ display: 'flex', alignItems: 'center', gap: 4, marginLeft: 12 }}>
        {[['대시보드', '/app/dashboard'], ['실거주', '/app/search?mode=resident'], ['수도권', '/app/search?mode=metro']].map(([l, p]) => (
          <a key={l} href={'#' + p} data-track="header_tab" data-track-label={l}
            style={{ fontSize: 12, color: T.fgDim, textDecoration: 'none', padding: '6px 10px' }}>{l}</a>
        ))}
      </nav>

      <div style={{ flex: 1, maxWidth: 480, position: 'relative' }}>
        <form onSubmit={(e) => {
          e.preventDefault();
          if (q.trim()) {
            window.UJTrack.track('search', { query: q, source: 'header' });
            navigate('/app/search?q=' + encodeURIComponent(q));
          }
        }}>
          <input
            placeholder="🔍  지역·단지·동 검색"
            value={q}
            onChange={(e) => setQ(e.target.value)}
            onFocus={() => { setSearchOpen(true); window.UJTrack.track('search_focus', {}); }}
            onBlur={() => setTimeout(() => setSearchOpen(false), 150)}
            style={{
              width: '100%', padding: '8px 14px', borderRadius: 4,
              background: T.card, color: T.fg, border: '1px solid ' + T.line,
              fontSize: 13, fontFamily: T.font, outline: 'none',
            }}
          />
        </form>
        {searchOpen && q.length > 0 && (
          <div style={{
            position: 'absolute', top: '100%', left: 0, right: 0, marginTop: 4,
            background: T.card, border: '1px solid ' + T.line, borderRadius: 4,
            maxHeight: 320, overflow: 'auto', zIndex: 10,
            boxShadow: '0 12px 32px rgba(0,0,0,0.5)',
          }}>
            {APTS.filter(a => (a.name + a.region + a.dong).toLowerCase().includes(q.toLowerCase())).slice(0, 6).map((a) => (
              <a key={a.id} href={'#/app/detail/' + a.id}
                data-track="search_suggest_click" data-track-id={a.id} data-track-q={q}
                style={{
                  display: 'flex', alignItems: 'center', gap: 12, padding: '10px 14px',
                  textDecoration: 'none', borderBottom: '1px solid ' + T.line,
                }}>
                <GradePill grade={a.grade} size="sm" />
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ fontSize: 13, color: T.fg }}>{a.name}</div>
                  <div style={{ fontSize: 11, color: T.fgMuted }}>{a.region}</div>
                </div>
                <SignalBadge kind={a.signal} size="sm" />
              </a>
            ))}
          </div>
        )}
      </div>

      <LiveDot label="LIVE · RTMS" />

      <button onClick={() => setShowMenu(v => !v)} data-track="header_user_menu" style={{
        display: 'flex', alignItems: 'center', gap: 6, padding: '6px 10px',
        background: T.card, border: '1px solid ' + T.line, borderRadius: 4,
        color: T.fg, cursor: 'pointer', fontFamily: T.font, fontSize: 12, position: 'relative',
      }}>
        <Avatar name={user.name} size={22} />
        <span>{user.name}</span>
        {showMenu && (
          <div style={{
            position: 'absolute', top: '100%', right: 0, marginTop: 4,
            background: T.card, border: '1px solid ' + T.line, borderRadius: 4,
            minWidth: 180, padding: 6, zIndex: 20, textAlign: 'left',
            boxShadow: '0 12px 32px rgba(0,0,0,0.5)',
          }}>
            <MenuItem href="#/app/profile" label="프로필" />
            <MenuItem href="#/app/settings" label="설정" />
            <MenuItem href="#/admin" label="로그 분석 (관리자)" />
            <div style={{ height: 1, background: T.line, margin: '4px 0' }} />
            <MenuItem onClick={onLogout} label="로그아웃" danger />
          </div>
        )}
      </button>
    </header>
  );
}

function MenuItem({ href, onClick, label, danger }) {
  const props = href ? { href, as: 'a' } : { onClick };
  const Tag = href ? 'a' : 'div';
  return (
    <Tag {...props} data-track="menu_item" data-track-label={label}
      style={{
        display: 'block', padding: '8px 12px', borderRadius: 3,
        fontSize: 12, color: danger ? T.down : T.fgDim, textDecoration: 'none', cursor: 'pointer',
      }}
      onMouseEnter={(e) => e.currentTarget.style.background = T.cardHi}
      onMouseLeave={(e) => e.currentTarget.style.background = 'transparent'}
    >{label}</Tag>
  );
}

function Avatar({ name, size = 24 }) {
  const initial = (name || '?').charAt(0).toUpperCase();
  return (
    <div style={{
      width: size, height: size, borderRadius: size / 2,
      background: T.hot, color: T.bg,
      display: 'flex', alignItems: 'center', justifyContent: 'center',
      fontSize: size * 0.45, fontWeight: 700, fontFamily: T.mono,
      flexShrink: 0,
    }}>{initial}</div>
  );
}

// ─── DASHBOARD ────────────────────────────────────────
function DashboardPage() {
  const wl = useWatchlist();
  useEffect(() => { document.title = '대시보드 — 우리집사기'; }, []);

  const topApt = APTS[4]; // 대치 은마
  return (
    <div style={{ padding: 20 }}>
      <PageHead title="대시보드" sub="2026.05.27 · RTMS 실시간 동기화" />

      {/* KPI row */}
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(5, 1fr)', gap: 12, marginBottom: 16 }}>
        {[
          { label: 'AI 추천 단지', value: '42', sub: '7건 vs 어제', color: T.up },
          { label: '강력매수 신호', value: '14', sub: '3등급 6 · 4등급 8', color: T.hot },
          { label: '내 관심 단지', value: wl.ids.length || 8, sub: '6건 알림 대기', color: T.info },
          { label: '평균 상승률', value: '+0.42%', sub: 'WoW · 매주', color: T.up },
          { label: '기준금리', value: '2.75%', sub: '6월 동결 (78%)', color: T.fgDim },
        ].map((k) => (
          <Panel key={k.label} pad={14}>
            <KPI label={k.label} value={k.value} deltaValue={null} sub={k.sub} big />
          </Panel>
        ))}
      </div>

      <div style={{ display: 'grid', gridTemplateColumns: '1fr 380px', gap: 16 }}>
        <div>
          {/* AI Top Pick */}
          <Panel title="AI TOP PICK" subtitle="2026.05.27 · 모델 v3.2" action={
            <div style={{ display: 'flex', gap: 4 }}>
              {['1Y', '3Y', '5Y', '10Y'].map((p, i) => (
                <button key={p} data-track="dashboard_period" data-track-period={p} style={{
                  padding: '4px 10px', borderRadius: 3,
                  background: i === 0 ? T.hot : T.card, color: i === 0 ? T.bg : T.fgDim,
                  border: '1px solid ' + (i === 0 ? T.hot : T.line),
                  fontFamily: T.mono, fontSize: 11, cursor: 'pointer',
                }}>{p}</button>
              ))}
            </div>
          }>
            <div style={{ display: 'flex', gap: 16, marginBottom: 16 }}>
              <ScoreRing score={topApt.score} size={88} stroke={6} />
              <div style={{ flex: 1, minWidth: 0 }}>
                <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 4 }}>
                  <span style={{ fontSize: 11, color: T.fgMuted, fontFamily: T.mono }}>{topApt.region} · {topApt.dong}</span>
                  <SignalBadge kind={topApt.signal} size="sm" />
                </div>
                <div style={{ fontSize: 22, fontWeight: 700, marginBottom: 4 }}>
                  <a href={'#/app/detail/' + topApt.id} data-track="top_pick_click" data-track-id={topApt.id}
                    style={{ color: T.fg, textDecoration: 'none' }}>{topApt.name}</a>
                </div>
                <div style={{ fontSize: 11, color: T.fgMuted, fontFamily: T.mono }}>
                  {topApt.area} · {topApt.built}년식 · {topApt.units.toLocaleString()}세대 · {topApt.tags.join(' · ')}
                </div>
              </div>
              <div style={{ textAlign: 'right' }}>
                <div className="uj-num" style={{ fontSize: 26, fontWeight: 700 }}>{won(topApt.currentPrice)}</div>
                <Delta value={topApt.delta1y} size={14} />
              </div>
            </div>

            <div style={{ display: 'grid', gridTemplateColumns: '1fr 220px', gap: 16 }}>
              <div style={{ background: T.cardLo, borderRadius: 4, padding: 12 }}>
                <LineChart
                  history={topApt.history}
                  forecast={forecast(topApt.currentPrice, 12, 0.005, 0.025).forecast}
                  band={forecast(topApt.currentPrice, 12, 0.005, 0.025).band}
                  width={600} height={220}
                />
              </div>
              <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
                <div style={{ padding: 12, background: T.hotBg, border: '1px solid ' + T.hot + '40', borderRadius: 4 }}>
                  <div style={{ fontSize: 10, fontFamily: T.mono, color: T.hot, letterSpacing: 1 }}>AI BUY SIGNAL</div>
                  <div style={{ marginTop: 8, fontSize: 11, color: T.fgMuted, fontFamily: T.mono }}>매수 적기</div>
                  <div className="uj-num" style={{ fontSize: 22, fontWeight: 700, color: T.hot }}>{topApt.bestBuyMonth}</div>
                  <div style={{ marginTop: 8, fontSize: 11, color: T.fgMuted, fontFamily: T.mono }}>적정 매수가</div>
                  <div className="uj-num" style={{ fontSize: 16, fontWeight: 600 }}>{won(topApt.targetPrice)}</div>
                  <div style={{ marginTop: 6, fontSize: 10, color: T.fgMuted }}>현재가 대비 -1.6%</div>
                  <div style={{ marginTop: 10, fontSize: 10, color: T.fgMuted, fontFamily: T.mono }}>모델 확신도</div>
                  <div style={{ marginTop: 4 }}><ScoreBar score={topApt.confidence} height={4} showTicks={false} /></div>
                </div>
              </div>
            </div>
          </Panel>

          {/* Top recommendations table */}
          <Panel title="AI 매수 추천 리스트" subtitle="실거주 모드 · 가중치 학습됨" style={{ marginTop: 16 }} action={
            <div style={{ display: 'flex', gap: 4 }}>
              {['실거주', '투자', '혼합'].map((m, i) => (
                <button key={m} data-track="dashboard_mode" data-track-mode={m} style={{
                  padding: '4px 10px', borderRadius: 3,
                  background: i === 0 ? T.hot : T.card, color: i === 0 ? T.bg : T.fgDim,
                  border: '1px solid ' + (i === 0 ? T.hot : T.line), fontSize: 11, cursor: 'pointer',
                }}>{m}</button>
              ))}
            </div>
          }>
            <table style={{ width: '100%', borderCollapse: 'collapse', fontSize: 12 }}>
              <thead>
                <tr style={{ color: T.fgMuted, fontFamily: T.mono }}>
                  {['#', '단지명', '지역', '등급', '현재가', '1M / 1Y', '매수 적기', '적정가', '신호'].map((h) => (
                    <th key={h} style={{ textAlign: 'left', padding: '8px 6px', fontWeight: 500, borderBottom: '1px solid ' + T.line }}>{h}</th>
                  ))}
                </tr>
              </thead>
              <tbody>
                {APTS.slice().sort((a, b) => b.score - a.score).slice(0, 12).map((a, i) => (
                  <tr key={a.id} style={{ borderBottom: '1px solid ' + T.line }}
                    onClick={() => { window.UJTrack.track('table_row_click', { apt_id: a.id }); navigate('/app/detail/' + a.id); }}
                    onMouseEnter={(e) => e.currentTarget.style.background = T.cardHi}
                    onMouseLeave={(e) => e.currentTarget.style.background = 'transparent'}
                    style={{ cursor: 'pointer' }}
                  >
                    <td style={{ padding: '10px 6px', fontFamily: T.mono, color: T.fgMuted }}>{String(i + 1).padStart(2, '0')}</td>
                    <td style={{ padding: '10px 6px', fontWeight: 600 }}>{a.name}</td>
                    <td style={{ padding: '10px 6px', color: T.fgDim }}>{a.region}</td>
                    <td style={{ padding: '10px 6px' }}><GradePill grade={a.grade} size="sm" /></td>
                    <td style={{ padding: '10px 6px', fontFamily: T.mono }}>{won(a.currentPrice)}</td>
                    <td style={{ padding: '10px 6px' }}><Delta value={a.delta1m} size={11} /> &nbsp;<Delta value={a.delta1y} size={11} /></td>
                    <td style={{ padding: '10px 6px', fontFamily: T.mono, color: T.hot }}>{a.bestBuyMonth}</td>
                    <td style={{ padding: '10px 6px', fontFamily: T.mono }}>{won(a.targetPrice)}</td>
                    <td style={{ padding: '10px 6px' }}><SignalBadge kind={a.signal} size="sm" /></td>
                  </tr>
                ))}
              </tbody>
            </table>
          </Panel>
        </div>

        {/* Right column */}
        <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
          <Panel title="실시간 호재·악재" subtitle="액세스 1초 전…" action={<LiveDot color={T.up} label="" />}>
            <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
              {NEWS.map((n, i) => (
                <a key={i} href="#/app/news"
                  data-track="news_click" data-track-news={n.title}
                  style={{ display: 'block', textDecoration: 'none', color: T.fg }}>
                  <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: 4 }}>
                    <div style={{ display: 'flex', gap: 6 }}>
                      <span style={{
                        padding: '1px 6px', borderRadius: 2, fontSize: 9, fontFamily: T.mono,
                        background: n.impact === 'positive' ? T.upBg : T.downBg,
                        color: n.impact === 'positive' ? T.up : T.down,
                        border: '1px solid ' + (n.impact === 'positive' ? T.up : T.down) + '40',
                      }}>{n.tag}</span>
                      <span style={{ fontSize: 10, color: T.fgMuted }}>{n.region}</span>
                    </div>
                    <span style={{ fontSize: 9, color: T.fgFaint, fontFamily: T.mono }}>{n.time}</span>
                  </div>
                  <div style={{ fontSize: 13, lineHeight: 1.4 }}>{n.title}</div>
                  <div style={{ fontSize: 10, color: T.fgFaint, fontFamily: T.mono, marginTop: 2 }}>{n.source}</div>
                </a>
              ))}
            </div>
          </Panel>

          <Panel title="모델 요인 가중치" subtitle="v3.2 · SHAP">
            <div style={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
              {FACTORS.slice(0, 8).map((f) => (
                <FactorBar key={f.id} label={f.label} value={f.value} sub={f.sub} impact={f.impact} />
              ))}
            </div>
            <a href="#/app/model" data-track="view_all_factors" style={{ display: 'block', marginTop: 8, fontSize: 11, color: T.hot, textDecoration: 'none', textAlign: 'right' }}>
              전체 12개 요인 보기 →
            </a>
          </Panel>
        </div>
      </div>
    </div>
  );
}

// ─── DETAIL ───────────────────────────────────────────
function DetailPage({ id }) {
  const apt = APTS.find(a => a.id === id) || APTS[0];
  const wl = useWatchlist();
  const isFav = wl.has(apt.id);
  useEffect(() => {
    document.title = apt.name + ' — 우리집사기';
    window.UJTrack.track('view_apt_detail', { apt_id: apt.id, apt_name: apt.name });
  }, [apt.id]);

  const fc = forecast(apt.currentPrice, 12, 0.005, 0.025);

  return (
    <div style={{ padding: 20 }}>
      <div style={{ marginBottom: 16, display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 16 }}>
          <a href="#/app/dashboard" data-track="detail_back" style={{ color: T.fgDim, textDecoration: 'none', fontSize: 13 }}>← 대시보드</a>
          <ScoreRing score={apt.score} size={64} stroke={5} />
          <div>
            <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
              <h1 style={{ fontSize: 24, fontWeight: 700, margin: 0 }}>{apt.name}</h1>
              <SignalBadge kind={apt.signal} />
            </div>
            <div style={{ fontSize: 12, color: T.fgMuted, marginTop: 4, fontFamily: T.mono }}>
              {apt.region} · {apt.dong} · {apt.area} · {apt.built}년식 · {apt.units.toLocaleString()}세대
            </div>
          </div>
        </div>
        <div style={{ display: 'flex', gap: 8 }}>
          <button onClick={() => wl.toggle(apt.id)} data-track="toggle_watchlist" data-track-id={apt.id}
            style={{
              padding: '10px 16px', borderRadius: 4,
              background: isFav ? T.hotBg : T.card,
              color: isFav ? T.hot : T.fg,
              border: '1px solid ' + (isFav ? T.hot : T.line),
              fontSize: 13, fontWeight: 600, cursor: 'pointer', fontFamily: T.font,
            }}>
            {isFav ? '★ 관심 등록됨' : '☆ 관심 추가'}
          </button>
          <button data-track="share_apt" data-track-id={apt.id} style={{ ...ghostBtn, padding: '10px 16px' }}>공유</button>
        </div>
      </div>

      <div style={{ display: 'grid', gridTemplateColumns: '1fr 360px', gap: 16 }}>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
          <Panel title="가격 추이 · 12개월 예측">
            <LineChart history={apt.history} forecast={fc.forecast} band={fc.band} width={780} height={280} />
          </Panel>

          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 12 }}>
            <Panel pad={14}><KPI label="현재가" value={won(apt.currentPrice)} big /></Panel>
            <Panel pad={14}><KPI label="1년 변동" value={pct(apt.delta1y)} big /></Panel>
            <Panel pad={14}><KPI label="PIR" value={apt.pir.toFixed(1)} big sub={apt.pir > 25 ? '고평가' : '적정'} /></Panel>
            <Panel pad={14}><KPI label="전세가율" value={apt.jeonseRatio.toFixed(1) + '%'} big /></Panel>
          </div>

          <Panel title="기여 요인 (SHAP)">
            <div style={{ display: 'flex', flexDirection: 'column', gap: 0 }}>
              {FACTORS.slice(0, 6).map((f) => (
                <FactorBar key={f.id} label={f.label} value={f.value} sub={f.sub} impact={f.impact} />
              ))}
            </div>
          </Panel>
        </div>

        <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
          <Panel title="AI BUY SIGNAL">
            <div style={{ padding: 16, background: T.hotBg, border: '1px solid ' + T.hot + '40', borderRadius: 4, textAlign: 'center' }}>
              <div style={{ fontSize: 10, fontFamily: T.mono, color: T.hot, letterSpacing: 1.5 }}>최적 매수 시점</div>
              <div className="uj-num" style={{ fontSize: 36, fontWeight: 700, color: T.hot, margin: '8px 0' }}>{apt.bestBuyMonth}</div>
              <div style={{ fontSize: 12, color: T.fgDim }}>{Math.round((new Date(2026, parseInt(apt.bestBuyMonth.split('.')[1]) - 1) - new Date()) / 86400000)}일 후</div>
              <div style={{ marginTop: 16, paddingTop: 16, borderTop: '1px solid ' + T.line + '60' }}>
                <div style={{ fontSize: 10, color: T.fgMuted, fontFamily: T.mono }}>적정 매수가</div>
                <div className="uj-num" style={{ fontSize: 22, fontWeight: 700 }}>{won(apt.targetPrice)}</div>
                <div style={{ fontSize: 11, color: T.fgMuted, marginTop: 4 }}>현재가 대비 {pct((apt.targetPrice - apt.currentPrice) / apt.currentPrice * 100)}</div>
              </div>
              <div style={{ marginTop: 12 }}>
                <div style={{ fontSize: 10, color: T.fgMuted, fontFamily: T.mono, marginBottom: 4 }}>신뢰도 {apt.confidence}%</div>
                <ScoreBar score={apt.confidence} height={4} showTicks={false} />
              </div>
            </div>
            <a href={'#/app/loan?apt=' + apt.id} data-track="detail_to_loan"
              style={{ display: 'block', marginTop: 12, padding: '10px', textAlign: 'center', background: T.card, border: '1px solid ' + T.lineHi, borderRadius: 4, color: T.fg, textDecoration: 'none', fontSize: 12, fontWeight: 600 }}>
              대출 한도로 매수 가능 시뮬레이션 →
            </a>
          </Panel>

          <Panel title="호재">
            <ul style={{ margin: 0, padding: 0, listStyle: 'none', display: 'flex', flexDirection: 'column', gap: 8 }}>
              {apt.boons.map((b, i) => (
                <li key={i} style={{ fontSize: 12, color: T.fgDim, display: 'flex', gap: 8 }}>
                  <span style={{ color: T.up }}>+</span> {b}
                </li>
              ))}
            </ul>
          </Panel>

          <Panel title="리스크">
            <ul style={{ margin: 0, padding: 0, listStyle: 'none', display: 'flex', flexDirection: 'column', gap: 8 }}>
              {apt.risks.map((r, i) => (
                <li key={i} style={{ fontSize: 12, color: T.fgDim, display: 'flex', gap: 8 }}>
                  <span style={{ color: T.down }}>−</span> {r}
                </li>
              ))}
            </ul>
          </Panel>
        </div>
      </div>
    </div>
  );
}

// ─── PORTFOLIO ────────────────────────────────────────
function PortfolioPage() {
  const wl = useWatchlist();
  useEffect(() => { document.title = '관심 단지 — 우리집사기'; }, []);
  const items = APTS.filter(a => wl.ids.includes(a.id));
  return (
    <div style={{ padding: 20 }}>
      <PageHead title="관심 단지" sub={items.length + '개 단지 추적 중'} />
      {items.length === 0 ? (
        <EmptyState
          title="아직 관심 단지가 없어요"
          desc="검색에서 ☆ 버튼을 눌러 단지를 추가하면, 시그널 변화·뉴스를 자동으로 추적해드립니다."
          cta="단지 검색하러 가기"
          ctaHref="#/app/search"
        />
      ) : (
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: 16 }}>
          {items.map((a) => (
            <a key={a.id} href={'#/app/detail/' + a.id}
              data-track="portfolio_card_click" data-track-id={a.id}
              style={{ textDecoration: 'none' }}>
              <Panel pad={16}>
                <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: 12 }}>
                  <div style={{ display: 'flex', gap: 10 }}>
                    <ScoreRing score={a.score} size={48} stroke={4} />
                    <div>
                      <div style={{ fontSize: 14, fontWeight: 600, color: T.fg }}>{a.name}</div>
                      <div style={{ fontSize: 11, color: T.fgMuted }}>{a.region}</div>
                    </div>
                  </div>
                  <SignalBadge kind={a.signal} size="sm" />
                </div>
                <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: 8 }}>
                  <span className="uj-num" style={{ fontSize: 20, fontWeight: 700, color: T.fg }}>{won(a.currentPrice)}</span>
                  <Delta value={a.delta1y} size={12} />
                </div>
                <Spark data={a.history.map(h => h.v)} w={300} h={36} />
                <div style={{ marginTop: 8, fontSize: 11, color: T.fgMuted, fontFamily: T.mono }}>
                  매수 적기 <span style={{ color: T.hot }}>{a.bestBuyMonth}</span> · 적정가 {won(a.targetPrice)}
                </div>
              </Panel>
            </a>
          ))}
        </div>
      )}
    </div>
  );
}

// ─── COMPARE ──────────────────────────────────────────
function ComparePage() {
  useEffect(() => { document.title = '지역 비교 — 우리집사기'; }, []);
  const [selected, setSelected] = useState(['강남구', '분당구', '수원영통']);
  return (
    <div style={{ padding: 20 }}>
      <PageHead title="지역 비교" sub="최대 4개 지역 동시 분석" />
      <Panel>
        <div style={{ display: 'flex', gap: 8, marginBottom: 16, flexWrap: 'wrap' }}>
          {REGIONS.map((r) => (
            <button key={r.name} onClick={() => {
              if (selected.includes(r.name)) setSelected(selected.filter(x => x !== r.name));
              else if (selected.length < 4) setSelected([...selected, r.name]);
              window.UJTrack.track('compare_toggle', { region: r.name });
            }}
              data-track="compare_chip" data-track-region={r.name}
              style={{
                padding: '8px 14px', borderRadius: 999,
                background: selected.includes(r.name) ? T.hot : T.card,
                color: selected.includes(r.name) ? T.bg : T.fg,
                border: '1px solid ' + (selected.includes(r.name) ? T.hot : T.line),
                fontSize: 12, fontWeight: 600, cursor: 'pointer',
              }}>{r.name}</button>
          ))}
        </div>
        <table style={{ width: '100%', borderCollapse: 'collapse', fontSize: 12 }}>
          <thead>
            <tr style={{ color: T.fgMuted, fontFamily: T.mono }}>
              {['지역', '점수', '1Y 변동', 'PIR', '전세가율', '신호', '추세'].map(h => (
                <th key={h} style={{ textAlign: 'left', padding: '10px 8px', borderBottom: '1px solid ' + T.line }}>{h}</th>
              ))}
            </tr>
          </thead>
          <tbody>
            {REGIONS.filter(r => selected.includes(r.name)).map((r) => (
              <tr key={r.name} style={{ borderBottom: '1px solid ' + T.line }}>
                <td style={{ padding: '12px 8px', fontWeight: 600 }}>{r.name}</td>
                <td style={{ padding: '12px 8px' }}>
                  <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                    <span className="uj-num" style={{ color: r.score >= 80 ? T.hot : r.score >= 65 ? T.up : T.info, fontWeight: 700 }}>{r.score}</span>
                    <div style={{ width: 60 }}><ScoreBar score={r.score} height={4} showTicks={false} /></div>
                  </div>
                </td>
                <td style={{ padding: '12px 8px' }}><Delta value={r.delta1y} /></td>
                <td style={{ padding: '12px 8px', fontFamily: T.mono }}>{r.pir.toFixed(1)}</td>
                <td style={{ padding: '12px 8px', fontFamily: T.mono }}>{r.jeonse.toFixed(1)}%</td>
                <td style={{ padding: '12px 8px' }}><SignalBadge kind={r.signal} size="sm" /></td>
                <td style={{ padding: '12px 8px' }}><Spark data={r.trend} w={120} h={28} /></td>
              </tr>
            ))}
          </tbody>
        </table>
      </Panel>
    </div>
  );
}

// ─── LOAN ─────────────────────────────────────────────
function LoanPage() {
  useEffect(() => { document.title = '대출·세금 시뮬레이터 — 우리집사기'; }, []);
  const [price, setPrice] = useState(120000); // 12억
  const [income, setIncome] = useState(8000); // 연 8000만
  const [own, setOwn] = useState(30); // 자기자본 %
  const [rate, setRate] = useState(4.2);
  const [years, setYears] = useState(30);

  const loanAmount = price * (1 - own / 100);
  const monthRate = rate / 100 / 12;
  const months = years * 12;
  const monthly = loanAmount * monthRate * Math.pow(1 + monthRate, months) / (Math.pow(1 + monthRate, months) - 1);
  const dsr = (monthly * 12) / (income * 10) * 100; // %
  const dsrOk = dsr <= 40;

  // 취득세 (1.1%~3.3% 가정)
  const acqTax = Math.round(price * 0.011);
  // 양도세 (20% 가정)
  const capGainTax = Math.round(price * 0.02);
  // 재산세 연간
  const propTax = Math.round(price * 0.0015);

  useEffect(() => {
    const t = setTimeout(() => window.UJTrack.track('loan_calc', { price, income, own, rate, years, dsr: dsr.toFixed(1) }), 500);
    return () => clearTimeout(t);
  }, [price, income, own, rate, years]);

  return (
    <div style={{ padding: 20 }}>
      <PageHead title="대출 · 세금 시뮬레이터" sub="DSR 한도 + 취득세 + 재산세 + 양도세" />
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16 }}>
        <Panel title="입력">
          <div style={{ display: 'flex', flexDirection: 'column', gap: 18 }}>
            <RangeField label="매수가" unit={won(price)} value={price} onChange={setPrice} min={20000} max={500000} step={5000} />
            <RangeField label="연소득" unit={(income / 10).toFixed(1) + '억'} value={income} onChange={setIncome} min={2000} max={50000} step={500} />
            <RangeField label="자기자본 비율" unit={own + '%'} value={own} onChange={setOwn} min={10} max={100} step={5} />
            <RangeField label="대출 금리" unit={rate.toFixed(2) + '%'} value={rate} onChange={(v) => setRate(parseFloat(v))} min={2.5} max={7} step={0.05} />
            <RangeField label="상환 기간" unit={years + '년'} value={years} onChange={setYears} min={10} max={40} step={5} />
          </div>
        </Panel>

        <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
          <Panel title="결과">
            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 14 }}>
              <KPI label="대출 원금" value={won(Math.round(loanAmount))} big />
              <KPI label="월 상환액" value={Math.round(monthly).toLocaleString() + ' 만원'} big />
              <div>
                <div style={{ fontSize: 10, color: T.fgMuted, fontFamily: T.mono, letterSpacing: 1, textTransform: 'uppercase' }}>DSR</div>
                <div style={{ display: 'flex', alignItems: 'baseline', gap: 6 }}>
                  <span className="uj-num" style={{ fontSize: 28, fontWeight: 700, color: dsrOk ? T.up : T.down }}>{dsr.toFixed(1)}%</span>
                </div>
                <div style={{ fontSize: 11, color: dsrOk ? T.up : T.down, fontFamily: T.mono, marginTop: 4 }}>
                  {dsrOk ? '✓ DSR 40% 이내 — 대출 가능' : '⚠ DSR 한도 초과'}
                </div>
              </div>
              <KPI label="총 이자" value={Math.round((monthly * months - loanAmount) / 10000).toLocaleString() + ' 억'} big />
            </div>
          </Panel>
          <Panel title="세금 시뮬레이션">
            <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
              <TaxRow label="취득세 (1.1%)" value={won(acqTax)} />
              <TaxRow label="재산세 (연)" value={won(propTax)} />
              <TaxRow label="양도세 (예상)" value={won(capGainTax)} sub="5년 후 +20% 가정 시" />
              <div style={{ height: 1, background: T.line }} />
              <TaxRow label="첫 해 총 부담" value={won(acqTax + propTax)} bold />
            </div>
          </Panel>
        </div>
      </div>
    </div>
  );
}

function RangeField({ label, unit, value, onChange, min, max, step }) {
  return (
    <div>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: 6 }}>
        <span style={{ fontSize: 11, color: T.fgDim, fontFamily: T.mono, letterSpacing: 1, textTransform: 'uppercase' }}>{label}</span>
        <span className="uj-num" style={{ fontSize: 16, fontWeight: 700, color: T.hot }}>{unit}</span>
      </div>
      <input type="range" min={min} max={max} step={step} value={value}
        onChange={(e) => onChange(parseFloat(e.target.value))}
        style={{ width: '100%', accentColor: T.hot }} />
    </div>
  );
}

function TaxRow({ label, value, sub, bold }) {
  return (
    <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline' }}>
      <div>
        <div style={{ fontSize: 13, color: bold ? T.fg : T.fgDim, fontWeight: bold ? 600 : 400 }}>{label}</div>
        {sub && <div style={{ fontSize: 10, color: T.fgFaint, fontFamily: T.mono }}>{sub}</div>}
      </div>
      <span className="uj-num" style={{ fontSize: bold ? 18 : 14, fontWeight: bold ? 700 : 500, color: bold ? T.hot : T.fg }}>{value}</span>
    </div>
  );
}

// ─── SEARCH ───────────────────────────────────────────
function SearchPage() {
  useEffect(() => { document.title = '검색 — 우리집사기'; }, []);
  const params = new URLSearchParams(location.hash.split('?')[1] || '');
  const initialQ = params.get('q') || '';
  const [q, setQ] = useState(initialQ);
  const [signal, setSignal] = useState('all');
  const [grade, setGrade] = useState('all');
  const [limit, setLimit] = useState(20);
  const [sort, setSort] = useState('score');

  const filtered = APTS.filter((a) => {
    if (q && !(a.name + a.region + a.dong + a.tags.join('')).toLowerCase().includes(q.toLowerCase())) return false;
    if (signal !== 'all' && a.signal !== signal) return false;
    if (grade !== 'all' && a.grade !== grade) return false;
    return true;
  }).sort((a, b) => {
    if (sort === 'score') return b.score - a.score;
    if (sort === 'price_asc') return a.currentPrice - b.currentPrice;
    if (sort === 'price_desc') return b.currentPrice - a.currentPrice;
    if (sort === 'delta') return b.delta1y - a.delta1y;
    return 0;
  });

  useEffect(() => {
    setLimit(20);
    if (q) window.UJTrack.track('search_filter', { q, signal, grade, sort, results: filtered.length });
  }, [q, signal, grade, sort]);

  const visible = filtered.slice(0, limit);

  return (
    <div style={{ padding: 20 }}>
      <PageHead title="단지 검색" sub={filtered.length.toLocaleString() + '개 단지 (전체 ' + APTS.length.toLocaleString() + '개)'} />
      <Panel style={{ marginBottom: 16 }}>
        <div style={{ display: 'flex', gap: 12, alignItems: 'center', flexWrap: 'wrap' }}>
          <input
            value={q}
            onChange={(e) => setQ(e.target.value)}
            placeholder="단지명·지역·동·태그… (예: 강남, 정자, 자이, GTX)"
            style={{
              flex: 1, minWidth: 240, padding: '10px 14px', borderRadius: 4,
              background: T.cardLo, color: T.fg, border: '1px solid ' + T.line,
              fontSize: 13, fontFamily: T.font, outline: 'none',
            }}
          />
          <Filter label="신호" value={signal} onChange={setSignal}
            options={[['all', '전체'], ['BUY', '강력매수'], ['ACC', '매수'], ['WATCH', '관망'], ['HOLD', '보유'], ['AVOID', '회피']]} />
          <Filter label="등급" value={grade} onChange={setGrade}
            options={[['all', '전체'], ['S', 'S'], ['A', 'A'], ['B', 'B'], ['C', 'C']]} />
          <Filter label="정렬" value={sort} onChange={setSort}
            options={[['score', '점수순'], ['price_asc', '가격 낮은순'], ['price_desc', '가격 높은순'], ['delta', '상승률']]} />
        </div>
        {!q && (
          <div style={{ marginTop: 12, display: 'flex', gap: 6, flexWrap: 'wrap', fontSize: 11, alignItems: 'center' }}>
            <span style={{ color: T.fgMuted, fontFamily: T.mono, letterSpacing: 1 }}>빠른 검색:</span>
            {['강남', '서초', '송파', '마포', '용산', '분당', '판교', '광교', '송도', '해운대', '동탄', '광명'].map((kw) => (
              <button key={kw} onClick={() => setQ(kw)} data-track="quick_search" data-track-q={kw}
                style={{ padding: '4px 10px', borderRadius: 999, background: T.card, color: T.fgDim, border: '1px solid ' + T.line, fontSize: 11, cursor: 'pointer' }}>{kw}</button>
            ))}
          </div>
        )}
      </Panel>

      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: 16 }}>
        {visible.map((a) => (
          <a key={a.id} href={'#/app/detail/' + a.id}
            data-track="search_result_click" data-track-id={a.id} data-track-q={q}
            style={{ textDecoration: 'none' }}>
            <Panel pad={16}>
              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', gap: 12 }}>
                <div style={{ display: 'flex', gap: 12, minWidth: 0 }}>
                  <ScoreRing score={a.score} size={56} stroke={4} />
                  <div style={{ minWidth: 0 }}>
                    <div style={{ fontSize: 15, fontWeight: 600, color: T.fg }}>{a.name}</div>
                    <div style={{ fontSize: 11, color: T.fgMuted }}>{a.region} · {a.dong}</div>
                    <div style={{ display: 'flex', gap: 4, marginTop: 6, flexWrap: 'wrap' }}>
                      {a.tags.slice(0, 3).map((t) => (
                        <span key={t} style={{ fontSize: 10, color: T.fgDim, padding: '1px 6px', border: '1px solid ' + T.line, borderRadius: 2, fontFamily: T.mono }}>{t}</span>
                      ))}
                    </div>
                  </div>
                </div>
                <div style={{ textAlign: 'right' }}>
                  <SignalBadge kind={a.signal} size="sm" />
                  <div className="uj-num" style={{ fontSize: 17, fontWeight: 700, color: T.fg, marginTop: 8 }}>{won(a.currentPrice)}</div>
                  <Delta value={a.delta1y} size={11} />
                </div>
              </div>
            </Panel>
          </a>
        ))}
        {filtered.length === 0 && <EmptyState title="검색 결과가 없습니다" desc="필터를 풀거나 다른 키워드로 검색해보세요." />}
      </div>
      {limit < filtered.length && (
        <button onClick={() => { setLimit(limit + 20); window.UJTrack.track('search_load_more', { new_limit: limit + 20 }); }}
          data-track="search_load_more_btn"
          style={{
            display: 'block', margin: '24px auto', padding: '12px 32px',
            background: T.card, color: T.fg, border: '1px solid ' + T.lineHi, borderRadius: 4,
            fontSize: 13, fontWeight: 600, cursor: 'pointer', fontFamily: T.font,
          }}>
          더 보기 ({(filtered.length - limit).toLocaleString()}개 남음)
        </button>
      )}
    </div>
  );
}

function Filter({ label, value, onChange, options }) {
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
      <span style={{ fontSize: 11, color: T.fgMuted, fontFamily: T.mono, textTransform: 'uppercase', letterSpacing: 1 }}>{label}</span>
      <select value={value} onChange={(e) => { onChange(e.target.value); window.UJTrack.track('filter_change', { filter: label, value: e.target.value }); }}
        style={{
          padding: '6px 10px', borderRadius: 3,
          background: T.cardLo, color: T.fg, border: '1px solid ' + T.line,
          fontSize: 12, fontFamily: T.font,
        }}>
        {options.map(([v, l]) => <option key={v} value={v}>{l}</option>)}
      </select>
    </div>
  );
}

// ─── PROFILE ──────────────────────────────────────────
function ProfilePage() {
  const { user, logout } = useAuth();
  useEffect(() => { document.title = '프로필 — 우리집사기'; }, []);
  return (
    <div style={{ padding: 20 }}>
      <PageHead title="프로필" sub={user.email} />
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16 }}>
        <Panel title="계정">
          <div style={{ display: 'flex', alignItems: 'center', gap: 16, marginBottom: 20 }}>
            <Avatar name={user.name} size={56} />
            <div>
              <div style={{ fontSize: 18, fontWeight: 700 }}>{user.name}</div>
              <div style={{ fontSize: 12, color: T.fgMuted }}>{user.email}</div>
              <div style={{ fontSize: 11, color: T.fgFaint, fontFamily: T.mono, marginTop: 4 }}>
                {user.provider.toUpperCase()} · 가입일 {new Date(user.joinedAt).toLocaleDateString('ko-KR')}
              </div>
            </div>
          </div>
          <button onClick={() => { logout(); navigate('/'); }} data-track="profile_logout"
            style={{ ...ghostBtn, color: T.down, borderColor: T.down + '60', padding: '10px 16px' }}>로그아웃</button>
        </Panel>
        <Panel title="활동 통계">
          <ProfileStats />
        </Panel>
      </div>
    </div>
  );
}

function ProfileStats() {
  const [stats, setStats] = useState(() => computeMyStats());
  useEffect(() => {
    const h = () => setStats(computeMyStats());
    window.addEventListener('urijib:event', h);
    return () => window.removeEventListener('urijib:event', h);
  }, []);
  return (
    <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 14 }}>
      <KPI label="조회한 단지" value={stats.detailViews} big />
      <KPI label="검색 횟수" value={stats.searches} big />
      <KPI label="로그인 일수" value={stats.activeDays} big />
      <KPI label="알림 활성" value={stats.alerts} big sub="자동 추적 중" />
    </div>
  );
}

function computeMyStats() {
  const uid = window.UJTrack.getUid();
  const all = window.UJTrack.readAll();
  const mine = all.filter(e => e.uid === uid);
  return {
    detailViews: mine.filter(e => e.event === 'view_apt_detail').length,
    searches: mine.filter(e => e.event === 'search').length,
    activeDays: new Set(mine.map(e => new Date(e.ts).toDateString())).size,
    alerts: 3,
  };
}

// ─── NOTIFICATIONS ────────────────────────────────────
function NotificationsPage() {
  useEffect(() => { document.title = '알림 — 우리집사기'; }, []);
  const items = [
    { ts: '12분 전', tag: 'BUY 시그널', title: '대치 은마, BUY → STRONG BUY 변경', body: '재건축 사업시행계획 가결로 시그널 강화', kind: 'signal' },
    { ts: '1시간 전', tag: '뉴스', title: 'GTX-A 성남역 6월 부분 개통 확정', body: '관심 단지 3개 영향 — 분당 시세 +2.4% 전망', kind: 'news' },
    { ts: '오늘 09:00', tag: '주간 요약', title: '이번 주 매수 적기 단지 5건', body: '관심 지역 강남·분당·수원에서 BUY 시그널 5건', kind: 'digest' },
  ];
  return (
    <div style={{ padding: 20 }}>
      <PageHead title="알림" sub="실시간 + 주간 다이제스트" />
      <Panel>
        <div style={{ display: 'flex', flexDirection: 'column' }}>
          {items.map((n, i) => (
            <div key={i} style={{
              padding: '16px 0', borderBottom: i < items.length - 1 ? '1px solid ' + T.line : 'none',
              display: 'flex', gap: 12,
            }}>
              <div style={{ width: 8, height: 8, borderRadius: 4, marginTop: 7, flexShrink: 0,
                background: n.kind === 'signal' ? T.hot : n.kind === 'news' ? T.info : T.up }} />
              <div style={{ flex: 1 }}>
                <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: 4 }}>
                  <span style={{ fontSize: 10, fontFamily: T.mono, color: T.fgMuted, letterSpacing: 1 }}>{n.tag}</span>
                  <span style={{ fontSize: 10, color: T.fgFaint, fontFamily: T.mono }}>{n.ts}</span>
                </div>
                <div style={{ fontSize: 14, fontWeight: 600 }}>{n.title}</div>
                <div style={{ fontSize: 12, color: T.fgDim, marginTop: 4 }}>{n.body}</div>
              </div>
            </div>
          ))}
        </div>
      </Panel>
    </div>
  );
}

// ─── Atoms ────────────────────────────────────────────
function PageHead({ title, sub }) {
  return (
    <div style={{ marginBottom: 16 }}>
      <h1 style={{ fontSize: 22, fontWeight: 700, margin: 0 }}>{title}</h1>
      {sub && <div style={{ fontSize: 12, color: T.fgMuted, fontFamily: T.mono, marginTop: 4 }}>{sub}</div>}
    </div>
  );
}

function EmptyState({ title, desc, cta, ctaHref }) {
  return (
    <div style={{
      padding: 64, textAlign: 'center',
      border: '1px dashed ' + T.line, borderRadius: 4, background: T.bgAlt,
    }}>
      <div style={{ fontSize: 40, opacity: 0.4 }}>○</div>
      <div style={{ fontSize: 16, fontWeight: 600, marginTop: 12 }}>{title}</div>
      <div style={{ fontSize: 13, color: T.fgDim, marginTop: 6 }}>{desc}</div>
      {cta && <a href={ctaHref} data-track="empty_cta" style={{ ...primaryBtn, marginTop: 20, display: 'inline-flex' }}>{cta} →</a>}
    </div>
  );
}

Object.assign(window, {
  AppShell, DashboardPage, DetailPage, SearchPage,
  PortfolioPage, ComparePage, LoanPage, ProfilePage, NotificationsPage,
  EmptyState, PageHead,
});
