// admin-pages.jsx — 5 pages: Dashboard, Users, Stats, BetaRequests, Whitelist
const C = window.ADMIN_COLORS;
const { Card, StatCard, Pill, Btn, Input, Select, Tabs } = window.AdminShell;

// ===== Mock data =====
const NOW = new Date('2026-05-04T14:19:38');
const fmt = (d) => d.toLocaleDateString('ko-KR', { year: 'numeric', month: 'numeric', day: 'numeric' });
const fmtTime = (d) => d.toLocaleString('ko-KR', { dateStyle: 'medium', timeStyle: 'short' });

// ===== 공통: 데이터 테이블 =====
function Table({ columns, rows, empty = '데이터 없음' }) {
  return (
    <div style={{ overflowX: 'auto', border: `1px solid ${C.border}`, borderRadius: 10, background: C.panel }}>
      <table style={{ width: '100%', borderCollapse: 'collapse', fontSize: 13 }}>
        <thead>
          <tr style={{ background: C.primarySofter }}>
            {columns.map((col, i) => (
              <th key={i} style={{
                textAlign: col.align || 'left',
                padding: '10px 14px',
                fontSize: 11, fontWeight: 700, letterSpacing: '0.04em',
                color: C.muted, textTransform: 'uppercase',
                borderBottom: `1px solid ${C.border}`,
                whiteSpace: 'nowrap',
                width: col.width,
              }}>{col.header}</th>
            ))}
          </tr>
        </thead>
        <tbody>
          {rows.length === 0 ? (
            <tr>
              <td colSpan={columns.length} style={{ padding: '60px 20px', textAlign: 'center', color: C.faint, fontSize: 13 }}>
                {empty}
              </td>
            </tr>
          ) : rows.map((row, i) => (
            <tr key={i} style={{
              borderBottom: i === rows.length - 1 ? 'none' : `1px solid ${C.border}`,
              transition: 'background 0.1s',
            }}
              onMouseEnter={e => e.currentTarget.style.background = C.primarySofter}
              onMouseLeave={e => e.currentTarget.style.background = ''}
            >
              {columns.map((col, j) => (
                <td key={j} style={{
                  padding: '12px 14px',
                  textAlign: col.align || 'left',
                  color: col.muted ? C.muted : C.text,
                  fontFamily: col.mono ? "'JetBrains Mono', ui-monospace, monospace" : undefined,
                  fontSize: col.mono ? 12.5 : 13,
                  verticalAlign: 'middle',
                }}>
                  {col.render ? col.render(row, i) : row[col.key]}
                </td>
              ))}
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  );
}

// ============================================================
// 1. DASHBOARD
// ============================================================
function Dashboard() {
  const stats = [
    { label: 'MAU (30일)', value: '2', sub: 'distinct user_id (judgments)', hint: '지난주 대비', trend: 0 },
    { label: '전체 가입자', value: '4', sub: 'deleted_at IS NULL', trend: 33 },
    { label: '누적 Judge', value: '58', sub: 'judgments 테이블', trend: 12 },
    { label: '활성 구독', value: '0', sub: 'users.subscription_tier ≠ free' },
  ];
  const revenue = [
    { label: '메이슬', value: '0%', sub: '전월 대비 증감', tone: 'neutral' },
    { label: 'MRR', value: '₩0', sub: '월 반복 매출', tone: 'neutral' },
    { label: '전환율', value: '0.0%', sub: 'free → paid', tone: 'neutral' },
  ];
  const recent = [
    { event: 'judge.create', user: 'memnyan35@gmail.com', meta: 'AAPL · 매수', time: '14:11' },
    { event: 'user.signup', user: '2261071@pcu.ac.kr', meta: 'free', time: '13:42' },
    { event: 'judge.create', user: 'memnyan35@gmail.com', meta: 'TSLA · 매도', time: '11:08' },
    { event: 'whitelist.add', user: 'admin', meta: '2261071@pcu.ac.kr', time: '10:33' },
    { event: 'judge.create', user: 'mybj0610@gmail.com', meta: 'NVDA · 매수', time: '09:21' },
    { event: 'user.signup', user: 'byeongjun0021@naver.com', meta: 'free', time: '어제 22:14' },
  ];

  return (
    <>
      {/* KPI 카드 4개 */}
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(200px, 1fr))', gap: 12, marginBottom: 12 }}>
        {stats.map((s, i) => <StatCard key={i} {...s}/>)}
      </div>

      {/* 매출 + 분포 */}
      <div style={{ display: 'grid', gridTemplateColumns: '2fr 1fr', gap: 12, marginBottom: 12 }}>
        <Card padding={0}>
          <div style={{ padding: '14px 18px', borderBottom: `1px solid ${C.border}`, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
            <div style={{ fontSize: 12, fontWeight: 700, color: C.muted, letterSpacing: '0.04em', textTransform: 'uppercase' }}>매출</div>
            <Pill tone="mono" size="xs">전월 대비</Pill>
          </div>
          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)' }}>
            {revenue.map((r, i) => (
              <div key={i} style={{
                padding: '20px 22px',
                borderRight: i < 2 ? `1px solid ${C.border}` : 'none',
              }}>
                <div style={{ fontSize: 11, color: C.muted, marginBottom: 8 }}>{r.label}</div>
                <div className="mono" style={{ fontSize: 26, fontWeight: 700, color: C.text, letterSpacing: '-0.02em' }}>{r.value}</div>
                <div style={{ fontSize: 11, color: C.faint, marginTop: 6 }}>{r.sub}</div>
              </div>
            ))}
          </div>
        </Card>

        <Card padding={0}>
          <div style={{ padding: '14px 18px', borderBottom: `1px solid ${C.border}` }}>
            <div style={{ fontSize: 12, fontWeight: 700, color: C.muted, letterSpacing: '0.04em', textTransform: 'uppercase' }}>유저 분포</div>
          </div>
          <div style={{ padding: '18px 22px' }}>
            {[
              { label: 'Free', val: 4, pct: 100, tone: C.muted },
              { label: 'Pro', val: 0, pct: 0, tone: C.primary },
              { label: '기관', val: 0, pct: 0, tone: C.primaryDeep },
            ].map((r, i) => (
              <div key={i} style={{ marginBottom: i === 2 ? 0 : 14 }}>
                <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 12, marginBottom: 5 }}>
                  <span style={{ color: C.muted }}>{r.label}</span>
                  <span className="mono" style={{ color: C.text, fontWeight: 600 }}>{r.val}</span>
                </div>
                <div style={{ height: 6, background: C.rowSoft, borderRadius: 3, overflow: 'hidden' }}>
                  <div style={{ width: `${r.pct}%`, height: '100%', background: r.tone, borderRadius: 3 }}/>
                </div>
              </div>
            ))}
          </div>
        </Card>
      </div>

      {/* 최근 이벤트 */}
      <Card padding={0}>
        <div style={{ padding: '14px 18px', borderBottom: `1px solid ${C.border}`, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
          <div style={{ fontSize: 12, fontWeight: 700, color: C.muted, letterSpacing: '0.04em', textTransform: 'uppercase' }}>최근 이벤트</div>
          <div className="mono" style={{ fontSize: 11, color: C.faint }}>실시간 · audit_log</div>
        </div>
        <div>
          {recent.map((r, i) => (
            <div key={i} style={{
              display: 'grid', gridTemplateColumns: '160px 1fr 1fr 70px',
              gap: 16, padding: '11px 18px',
              borderTop: i === 0 ? 'none' : `1px solid ${C.border}`,
              fontSize: 12.5, alignItems: 'center',
            }}>
              <div className="mono" style={{ color: C.primary, fontWeight: 600 }}>{r.event}</div>
              <div style={{ color: C.text, fontFamily: "'JetBrains Mono', ui-monospace, monospace", fontSize: 12 }}>{r.user}</div>
              <div style={{ color: C.muted }}>{r.meta}</div>
              <div className="mono" style={{ color: C.faint, textAlign: 'right', fontSize: 11.5 }}>{r.time}</div>
            </div>
          ))}
        </div>
      </Card>

      <div className="mono" style={{ fontSize: 10.5, color: C.faint, marginTop: 16, textAlign: 'right' }}>
        업데이트: {fmtTime(NOW)}
      </div>
    </>
  );
}

// ============================================================
// 2. USERS
// ============================================================
function Users() {
  const [search, setSearch] = React.useState('');
  const [tier, setTier] = React.useState('all');
  const [sort, setSort] = React.useState('newest');

  const allUsers = [
    { email: '2261071@pcu.ac.kr', role: 'user', mbti: '—', tier: 'free', credits: 0, joined: '2026.5.4.', status: '활성' },
    { email: 'byeongjun0021@naver.com', role: 'user', mbti: '—', tier: 'free', credits: 0, joined: '2026.5.4.', status: '활성' },
    { email: 'mybj0610@gmail.com', role: 'user', mbti: '—', tier: 'free', credits: 0, joined: '2026.5.2.', status: '활성' },
    { email: 'memnyan35@gmail.com', role: 'admin', mbti: 'vibe_trader', tier: 'free', credits: 22, joined: '2026.5.2.', status: '활성' },
  ];
  const filtered = allUsers
    .filter(u => !search || u.email.toLowerCase().includes(search.toLowerCase()))
    .filter(u => tier === 'all' || u.tier === tier);

  return (
    <>
      {/* 필터 바 */}
      <Card padding={16} style={{ marginBottom: 12 }}>
        <div style={{ display: 'grid', gridTemplateColumns: '1fr auto auto auto', gap: 10, alignItems: 'center' }}>
          <Input icon="🔍" value={search} onChange={e => setSearch(e.target.value)} placeholder="이메일 검색"/>
          <Select value={sort} onChange={e => setSort(e.target.value)} options={[
            { value: 'newest', label: '가입일 ↓ 내림차순' },
            { value: 'oldest', label: '가입일 ↑ 오름차순' },
            { value: 'credits', label: '크레딧 순' },
          ]}/>
          <Select value="all" onChange={() => {}} options={[
            { value: 'all', label: '상태: 전체' },
            { value: 'active', label: '활성' },
            { value: 'suspended', label: '정지' },
          ]}/>
          <Btn kind="default">CSV 내보내기</Btn>
        </div>
        <div style={{ marginTop: 12, display: 'flex', alignItems: 'center', gap: 12 }}>
          <span style={{ fontSize: 11, color: C.muted, fontWeight: 700, letterSpacing: '0.04em', textTransform: 'uppercase' }}>등급</span>
          <Tabs value={tier} onChange={setTier} tabs={[
            { value: 'all', label: '전체', count: 4 },
            { value: 'free', label: 'Free', count: 4 },
            { value: 'pro', label: 'Pro', count: 0 },
            { value: 'org', label: '기관', count: 0 },
          ]}/>
        </div>
      </Card>

      <Table
        columns={[
          { header: '', width: 32, render: () => <input type="checkbox" style={{ accentColor: C.primary }}/> },
          { header: '이메일', key: 'email', mono: true, render: r => <span style={{ color: C.primary, fontWeight: 600 }}>{r.email}</span> },
          { header: 'Role', render: r => <Pill tone={r.role === 'admin' ? 'primary' : 'mono'} size="xs">{r.role}</Pill> },
          { header: 'MBTI', render: r => <span className="mono" style={{ fontSize: 11.5, color: r.mbti === '—' ? C.faint : C.text }}>{r.mbti}</span> },
          { header: '구독', render: r => <Pill tone="mono" size="xs">{r.tier}</Pill> },
          { header: '크레딧', mono: true, align: 'right', render: r => <span style={{ color: r.credits > 0 ? C.text : C.faint }}>{r.credits}</span> },
          { header: '가입일', mono: true, render: r => <span style={{ color: C.muted, fontSize: 12 }}>{r.joined}</span> },
          { header: '상태', render: r => <Pill tone="good" size="xs">● {r.status}</Pill> },
          { header: '', width: 60, align: 'right', render: () => <button style={{ background: 'transparent', border: 0, color: C.faint, cursor: 'pointer', fontSize: 16 }}>⋯</button> },
        ]}
        rows={filtered}
        empty="검색 결과 없음"
      />

      {/* 페이지네이션 */}
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginTop: 16, fontSize: 12, color: C.muted }}>
        <span>{filtered.length}명 / 총 {allUsers.length}명</span>
        <div style={{ display: 'flex', gap: 4 }}>
          <button style={{ width: 28, height: 28, border: `1px solid ${C.border}`, background: C.panel, borderRadius: 6, color: C.faint, cursor: 'not-allowed' }}>‹</button>
          <button style={{ width: 28, height: 28, border: `1px solid ${C.primary}`, background: C.primary, borderRadius: 6, color: 'white', fontWeight: 700 }}>1</button>
          <button style={{ width: 28, height: 28, border: `1px solid ${C.border}`, background: C.panel, borderRadius: 6, color: C.faint, cursor: 'not-allowed' }}>›</button>
        </div>
      </div>
    </>
  );
}

// ============================================================
// 3. STATS
// ============================================================
function MiniLineChart({ data, color, height = 120 }) {
  const W = 800, H = height;
  const max = Math.max(...data, 1);
  const pts = data.map((v, i) => [i * (W / (data.length - 1)), H - (v / max) * (H - 20) - 10]);
  const path = pts.map((p, i) => (i === 0 ? `M ${p[0]} ${p[1]}` : `L ${p[0]} ${p[1]}`)).join(' ');
  const area = `M 0 ${H} L ${pts.map(p => `${p[0]} ${p[1]}`).join(' L ')} L ${W} ${H} Z`;
  return (
    <svg viewBox={`0 0 ${W} ${H}`} style={{ width: '100%', height: H, display: 'block' }}>
      <defs>
        <linearGradient id={`grad-${color.replace('#','')}`} x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%" stopColor={color} stopOpacity="0.18"/>
          <stop offset="100%" stopColor={color} stopOpacity="0"/>
        </linearGradient>
      </defs>
      {/* 그리드 */}
      {[0.25, 0.5, 0.75].map((t, i) => (
        <line key={i} x1={0} y1={H * t} x2={W} y2={H * t} stroke="var(--c-row-soft)" strokeWidth="1"/>
      ))}
      <path d={area} fill={`url(#grad-${color.replace('#','')})`}/>
      <path d={path} stroke={color} strokeWidth="2" fill="none" strokeLinecap="round" strokeLinejoin="round"/>
      {pts.filter((_, i) => i === pts.length - 1).map((p, i) => (
        <circle key={i} cx={p[0]} cy={p[1]} r="4" fill={color}/>
      ))}
    </svg>
  );
}

function ChartCard({ title, value, change, data, color, xLabels }) {
  return (
    <Card padding={0}>
      <div style={{ padding: '14px 18px', borderBottom: `1px solid ${C.border}`, display: 'flex', justifyContent: 'space-between', alignItems: 'baseline' }}>
        <div>
          <div style={{ fontSize: 11, fontWeight: 700, color: C.muted, letterSpacing: '0.04em', textTransform: 'uppercase' }}>{title}</div>
          <div style={{ display: 'flex', gap: 10, alignItems: 'baseline', marginTop: 6 }}>
            <span className="mono" style={{ fontSize: 22, fontWeight: 700, color: C.text, letterSpacing: '-0.02em' }}>{value}</span>
            {change !== undefined && (
              <span style={{ fontSize: 12, fontWeight: 700, color: change >= 0 ? C.good : C.bad }}>
                {change >= 0 ? '▲' : '▼'} {Math.abs(change)}%
              </span>
            )}
          </div>
        </div>
        <div style={{ display: 'flex', gap: 6, fontSize: 11, color: C.faint }} className="mono">
          <span>min {Math.min(...data)}</span>
          <span>·</span>
          <span>max {Math.max(...data)}</span>
        </div>
      </div>
      <div style={{ padding: '12px 18px 14px' }}>
        <MiniLineChart data={data} color={color}/>
        <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 10, color: C.faint, marginTop: 4 }} className="mono">
          {xLabels.map((l, i) => <span key={i}>{l}</span>)}
        </div>
      </div>
    </Card>
  );
}

function Stats() {
  const [range, setRange] = React.useState('30d');
  const signups = [3, 2.8, 2.6, 2.5, 2.4, 2.3, 2.2, 2.2, 2.2, 2.2, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1, 2.1];
  const judges = [2, 1, 1.5, 0.5, 1, 2, 3, 4, 1, 0.5, 0.5, 1.5, 2, 1, 0.5, 1, 2, 3, 2.5, 2, 1.5, 1, 1.5, 2, 2.5, 3, 3.5, 3, 4.5, 4.5];
  const xLabels30 = ['2026-04-04', '2026-04-11', '2026-04-18', '2026-04-25', '2026-05-04'];

  return (
    <>
      <div style={{ display: 'flex', gap: 10, alignItems: 'center', marginBottom: 12 }}>
        <Tabs value={range} onChange={setRange} tabs={[
          { value: '1d', label: '일별' },
          { value: '30d', label: '30일' },
          { value: '90d', label: '90일' },
          { value: '1y', label: '1년' },
        ]}/>
        <span style={{ flex: 1 }}/>
        <Btn kind="default" size="sm">CSV 내려받기</Btn>
      </div>

      <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
        <ChartCard title="신규 가입자" value={62} change={8} data={signups} color={C.primary} xLabels={xLabels30}/>
        <ChartCard title="Judge 호출" value={58} change={24} data={judges} color={C.primaryDeep} xLabels={xLabels30}/>
      </div>

      {/* 보조 통계 */}
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(180px, 1fr))', gap: 12, marginTop: 12 }}>
        {[
          { label: '평균 세션', value: '4분 22초', hint: 'judgments timestamp 추정' },
          { label: '재방문율', value: '36%', hint: '7일 기준' },
          { label: 'D7 리텐션', value: '14%', hint: 'cohort 분석' },
          { label: 'API 응답', value: '184ms', hint: 'p50' },
        ].map((s, i) => (
          <Card key={i} padding={16}>
            <div style={{ fontSize: 11, color: C.muted, fontWeight: 700, letterSpacing: '0.04em', textTransform: 'uppercase', marginBottom: 6 }}>{s.label}</div>
            <div className="mono" style={{ fontSize: 20, fontWeight: 700, color: C.text }}>{s.value}</div>
            <div style={{ fontSize: 10.5, color: C.faint, marginTop: 4 }} className="mono">{s.hint}</div>
          </Card>
        ))}
      </div>

      <div style={{ marginTop: 20, padding: '14px 18px', background: C.primarySofter, borderRadius: 10, fontSize: 12, color: C.textSoft, lineHeight: 1.6 }}>
        구독 시계열은 별도 RPC 메뷰로 변경 — 추후 통합 예정.
      </div>
    </>
  );
}

// ============================================================
// 4. BETA REQUESTS
// ============================================================
function BetaRequests() {
  const [tab, setTab] = React.useState('pending');

  const all = [
    { email: 'taejin.choi@kakao.com', name: '최태진', reason: '주식 시작한 지 3개월. 충동 매수가 너무 잦음.', date: '2026.5.4. 11:42', status: 'pending' },
    { email: 'jhseo@daum.net', name: '서지훈', reason: '공모주 청약 흐름 점검용', date: '2026.5.4. 09:15', status: 'pending' },
    { email: 'rina@studio.io', name: '리나', reason: '미국주식 위주, 단타 줄이고 싶어요', date: '2026.5.3. 22:08', status: 'pending' },
    { email: 'ksk1234@naver.com', name: '김상규', reason: '이미 사용 중인 친구 추천', date: '2026.5.3. 18:31', status: 'approved' },
    { email: 'wonbin.lee@hanmail.net', name: '이원빈', reason: '—', date: '2026.5.3. 14:02', status: 'rejected' },
  ];
  const counts = {
    pending: all.filter(r => r.status === 'pending').length,
    approved: all.filter(r => r.status === 'approved').length,
    rejected: all.filter(r => r.status === 'rejected').length,
    all: all.length,
  };
  const filtered = tab === 'all' ? all : all.filter(r => r.status === tab);

  const statusPill = (s) => {
    if (s === 'pending') return <Pill tone="warn" size="xs">● 대기</Pill>;
    if (s === 'approved') return <Pill tone="good" size="xs">● 승인</Pill>;
    return <Pill tone="bad" size="xs">● 거부</Pill>;
  };

  return (
    <>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 12, flexWrap: 'wrap', gap: 10 }}>
        <Tabs value={tab} onChange={setTab} tabs={[
          { value: 'pending', label: '대기', count: counts.pending },
          { value: 'approved', label: '승인', count: counts.approved },
          { value: 'rejected', label: '거부', count: counts.rejected },
          { value: 'all', label: '전체', count: counts.all },
        ]}/>
        <div style={{ display: 'flex', gap: 8 }}>
          <Btn kind="default" size="sm">선택 거부</Btn>
          <Btn kind="primary" size="sm">선택 승인</Btn>
        </div>
      </div>

      <Table
        columns={[
          { header: '', width: 32, render: () => <input type="checkbox" style={{ accentColor: C.primary }}/> },
          { header: '이메일', mono: true, render: r => <span style={{ color: C.primary, fontWeight: 600 }}>{r.email}</span> },
          { header: '이름', render: r => <span style={{ fontWeight: 500 }}>{r.name}</span> },
          { header: '신청 이유', render: r => <span style={{ color: C.muted, fontSize: 12.5 }}>{r.reason}</span> },
          { header: '신청일', mono: true, render: r => <span style={{ color: C.muted, fontSize: 11.5 }}>{r.date}</span> },
          { header: '상태', render: r => statusPill(r.status) },
          { header: '액션', align: 'right', render: r => r.status === 'pending' ? (
            <div style={{ display: 'flex', gap: 6, justifyContent: 'flex-end' }}>
              <Btn kind="danger" size="sm">거부</Btn>
              <Btn kind="primary" size="sm">승인</Btn>
            </div>
          ) : <span style={{ fontSize: 11, color: C.faint }} className="mono">처리됨</span> },
        ]}
        rows={filtered}
        empty={tab === 'pending' ? '대기 중인 신청자가 없습니다' : '신청자 없음'}
      />
    </>
  );
}

// ============================================================
// 5. WHITELIST
// ============================================================
function Whitelist() {
  const [email, setEmail] = React.useState('');
  const [domain, setDomain] = React.useState('');
  const [validity, setValidity] = React.useState('forever');
  const [memo, setMemo] = React.useState('');

  const rows = [
    { email: '2261071@pcu.ac.kr', domain: '—', source: 'manual', addedAt: '2026.5.4.', expires: '무기한', memo: '베타 신청 승인 (id=2caaed57-2179-4ad9-bcae-6e0d1f02a542)' },
    { email: 'memnyan35@gmail.com', domain: '—', source: 'manual', addedAt: '2026.5.4.', expires: '무기한', memo: 'admin (Brian)' },
    { email: 'byeongjun0021@naver.com', domain: '—', source: 'csv', addedAt: '2026.5.4.', expires: '2026.6.4.', memo: '대학 동아리 베타 1차' },
    { email: '—', domain: 'pcu.ac.kr', source: 'manual', addedAt: '2026.5.3.', expires: '무기한', memo: '도메인 통째 — 학생 대상' },
    { email: 'mybj0610@gmail.com', domain: '—', source: 'manual', addedAt: '2026.5.2.', expires: '2026.8.1.', memo: '내부 QA' },
  ];

  return (
    <>
      <div style={{ display: 'flex', justifyContent: 'flex-end', marginBottom: 12 }}>
        <Btn kind="default" size="sm">📄 CSV 일괄 업로드</Btn>
      </div>

      {/* 추가 폼 */}
      <Card padding={18} style={{ marginBottom: 16 }}>
        <div style={{ fontSize: 11, color: C.muted, fontWeight: 700, letterSpacing: '0.04em', textTransform: 'uppercase', marginBottom: 12 }}>새 항목 추가</div>
        <div style={{ display: 'grid', gridTemplateColumns: '1.4fr 1.2fr 1fr 1.6fr auto', gap: 10, alignItems: 'center' }}>
          <Input value={email} onChange={e => setEmail(e.target.value)} placeholder="email (단일 이메일)"/>
          <Input value={domain} onChange={e => setDomain(e.target.value)} placeholder="domain (예: keitta.com)"/>
          <Select value={validity} onChange={e => setValidity(e.target.value)} options={[
            { value: 'forever', label: '연장 ⓜ 무기한' },
            { value: '30d', label: '30일' },
            { value: '90d', label: '90일' },
            { value: '1y', label: '1년' },
          ]}/>
          <Input value={memo} onChange={e => setMemo(e.target.value)} placeholder="메모 (선택)"/>
          <Btn kind="primary">+ 추가</Btn>
        </div>
        <div style={{ fontSize: 11, color: C.faint, marginTop: 10 }}>
          email 또는 domain 중 하나만 입력. domain을 등록하면 해당 도메인 모든 이메일이 자동 통과합니다.
        </div>
      </Card>

      <Table
        columns={[
          { header: 'Email', mono: true, render: r => r.email === '—' ? <span style={{ color: C.faint }}>—</span> : <span style={{ color: C.primary, fontWeight: 600 }}>{r.email}</span> },
          { header: 'Domain', mono: true, render: r => r.domain === '—' ? <span style={{ color: C.faint }}>—</span> : <span className="mono" style={{ color: C.text, fontWeight: 600 }}>{r.domain}</span> },
          { header: '출처', render: r => <Pill tone={r.source === 'csv' ? 'primary' : 'mono'} size="xs">{r.source}</Pill> },
          { header: '추가일', mono: true, render: r => <span style={{ color: C.muted, fontSize: 11.5 }}>{r.addedAt}</span> },
          { header: '만료일', mono: true, render: r => <span style={{ color: r.expires === '무기한' ? C.faint : C.text, fontSize: 11.5 }}>{r.expires}</span> },
          { header: '메모', render: r => <span style={{ color: C.muted, fontSize: 12.5 }}>{r.memo}</span> },
          { header: '', width: 60, align: 'right', render: () => <Btn kind="ghost" size="sm">삭제</Btn> },
        ]}
        rows={rows}
      />

      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginTop: 16, fontSize: 12, color: C.muted }}>
        <span className="mono">전체 {rows.length}건 · 활성 {rows.filter(r => r.expires === '무기한' || r.expires > '2026.5.4.').length}건</span>
        <span style={{ fontSize: 11, color: C.faint }} className="mono">테이블: whitelist · 인덱스 (email, domain)</span>
      </div>
    </>
  );
}

window.AdminPages = { Dashboard, Users, Stats, BetaRequests, Whitelist };
