// admin-pages2.jsx — additional 8 admin pages
const C2 = window.ADMIN_COLORS;
const { Card, StatCard, Pill, Btn, Input, Select, Tabs } = window.AdminShell;

// ===== shared table =====
function Table2({ columns, rows, empty = '데이터 없음' }) {
  return (
    <div style={{ overflowX: 'auto', border: `1px solid ${C2.border}`, borderRadius: 10, background: C2.panel }}>
      <table style={{ width: '100%', borderCollapse: 'collapse', fontSize: 13 }}>
        <thead>
          <tr style={{ background: C2.primarySofter }}>
            {columns.map((col, i) => (
              <th key={i} style={{
                textAlign: col.align || 'left',
                padding: '10px 14px',
                fontSize: 11, fontWeight: 700, letterSpacing: '0.04em',
                color: C2.muted, textTransform: 'uppercase',
                borderBottom: `1px solid ${C2.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: C2.faint, fontSize: 13 }}>{empty}</td></tr>
          ) : rows.map((row, i) => (
            <tr key={i} style={{ borderBottom: i === rows.length - 1 ? 'none' : `1px solid ${C2.border}` }}
              onMouseEnter={e => e.currentTarget.style.background = C2.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 ? C2.muted : C2.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. AI COST
// ============================================================
// [β-Phase C-3-3] hardcoded → props 인터페이스 변환.
// JSX/시각 디테일 0 변경 — 게이지/Tabs/StatCard/Table2 디테일 100% 보존.
// hardcoded fallback default 유지 — mockup standalone 미리보기 시각 호환.
function AICost({
  range: rangeProp,
  onRangeChange,
  todayUsed: todayUsedProp,
  todayCap:  todayCapProp,
  stats:     statsProp,
  models:    modelsProp,
  expensive: expensiveProp,
}) {
  const [innerRange, setInnerRange] = React.useState('30d');
  const range    = rangeProp !== undefined ? rangeProp : innerRange;
  const setRange = onRangeChange || setInnerRange;
  const todayCap  = todayCapProp  !== undefined ? todayCapProp  : 50;
  const todayUsed = todayUsedProp !== undefined ? todayUsedProp : 12.34;
  const pct = todayCap > 0 ? (todayUsed / todayCap) * 100 : 0;

  const stats = statsProp || [
    { label: '총 비용', value: '$184.22', sub: '30일 누적' },
    { label: '총 호출', value: '5,841', sub: '평균 195/일' },
    { label: '입력 토큰', value: '4.21M', sub: 'p90 입력 8.4k' },
    { label: '출력 토큰', value: '1.86M', sub: 'p90 출력 3.1k' },
  ];

  const models = modelsProp || [
    { model: 'claude-sonnet-4', calls: 3214, inTok: '2.84M', outTok: '1.21M', cost: '$108.42', avg: '$0.0337' },
    { model: 'claude-haiku-4-5', calls: 2104, inTok: '1.12M', outTok: '0.51M', cost: '$48.91', avg: '$0.0232' },
    { model: 'gpt-5-mini', calls: 412, inTok: '0.18M', outTok: '0.09M', cost: '$18.04', avg: '$0.0438' },
    { model: 'gemini-2.5-pro', calls: 111, inTok: '0.07M', outTok: '0.05M', cost: '$8.85', avg: '$0.0797' },
  ];
  const expensive = expensiveProp !== undefined ? expensiveProp : [
    { time: '2026.5.4. 11:03', user: 'memnyan35@gmail.com', model: 'claude-sonnet-4', tokens: '14,210', cost: '$0.281', reason: 'Long context · AAPL deep' },
    { time: '2026.5.4. 09:47', user: 'mybj0610@gmail.com', model: 'claude-sonnet-4', tokens: '11,802', cost: '$0.234', reason: 'NVDA · multi-source' },
    { time: '2026.5.3. 22:18', user: 'memnyan35@gmail.com', model: 'gpt-5-mini', tokens: '8,401', cost: '$0.198', reason: 'Retry × 3' },
    { time: '2026.5.3. 14:05', user: '2261071@pcu.ac.kr', model: 'claude-sonnet-4', tokens: '7,998', cost: '$0.158', reason: 'TSLA · vibe_check' },
  ];

  return (
    <>
      {/* 오늘 비용 + 게이지 */}
      <Card padding={0} style={{ marginBottom: 12 }}>
        <div style={{ padding: '16px 20px', borderBottom: `1px solid ${C2.border}`, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
          <div>
            <div style={{ fontSize: 11, fontWeight: 700, color: C2.muted, letterSpacing: '0.04em', textTransform: 'uppercase' }}>오늘 비용 (KST)</div>
            <div className="mono" style={{ fontSize: 32, fontWeight: 700, color: C2.text, marginTop: 6, letterSpacing: '-0.02em' }}>${todayUsed.toFixed(4)}</div>
          </div>
          <div style={{ textAlign: 'right' }}>
            <div className="mono" style={{ fontSize: 11, color: C2.faint }}>일한도 ${todayCap.toFixed(2)}</div>
            <div className="mono" style={{ fontSize: 13, color: pct > 80 ? C2.bad : C2.muted, marginTop: 4, fontWeight: 600 }}>{pct.toFixed(1)}% 사용</div>
          </div>
        </div>
        <div style={{ padding: '14px 20px' }}>
          <div style={{ height: 10, background: C2.primarySofter, borderRadius: 5, overflow: 'hidden' }}>
            <div style={{ width: `${pct}%`, height: '100%', background: pct > 80 ? C2.bad : C2.primary, borderRadius: 5 }}/>
          </div>
        </div>
      </Card>

      {/* 기간 토글 */}
      <div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 12 }}>
        <span style={{ fontSize: 11, color: C2.muted, fontWeight: 700, letterSpacing: '0.04em', textTransform: 'uppercase' }}>기간</span>
        <Tabs value={range} onChange={setRange} tabs={[
          { value: '7d', label: '7일' }, { value: '30d', label: '30일' }, { value: '90d', label: '90일' },
        ]}/>
      </div>

      {/* 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={{ fontSize: 11, fontWeight: 700, color: C2.muted, letterSpacing: '0.04em', textTransform: 'uppercase', margin: '20px 0 10px' }}>모델별 사용</div>
      <Table2
        columns={[
          { header: '모델', mono: true, render: r => <span style={{ color: C2.primary, fontWeight: 600 }}>{r.model}</span> },
          { header: '호출 수', mono: true, align: 'right' },
          { header: '입력 토큰', mono: true, align: 'right', render: r => r.inTok },
          { header: '출력 토큰', mono: true, align: 'right', render: r => r.outTok },
          { header: '비용 (USD)', mono: true, align: 'right', render: r => <span style={{ fontWeight: 600 }}>{r.cost}</span> },
          { header: '건당 평균', mono: true, align: 'right', render: r => <span style={{ color: C2.muted }}>{r.avg}</span> },
        ]}
        rows={models.map(m => ({ ...m, '호출 수': m.calls.toLocaleString() }))}
      />

      {/* 고비용 호출 */}
      <div style={{ fontSize: 11, fontWeight: 700, color: C2.muted, letterSpacing: '0.04em', textTransform: 'uppercase', margin: '20px 0 10px' }}>최근 고비용 호출</div>
      <Table2
        columns={[
          { header: '시각', mono: true, render: r => <span style={{ fontSize: 11.5, color: C2.muted }}>{r.time}</span> },
          { header: '유저', mono: true, render: r => <span style={{ color: C2.primary, fontWeight: 600 }}>{r.user}</span> },
          { header: '모델', render: r => <Pill tone="mono" size="xs">{r.model}</Pill> },
          { header: '토큰', mono: true, align: 'right' },
          { header: '비용', mono: true, align: 'right', render: r => <span style={{ fontWeight: 600 }}>{r.cost}</span> },
          { header: '사유', render: r => <span style={{ color: C2.muted, fontSize: 12 }}>{r.reason}</span> },
        ]}
        rows={expensive}
      />
    </>
  );
}

// ============================================================
// 2. REVIEW QUEUE
// ============================================================
// [β-Phase C-3-5] hardcoded → props 인터페이스 변환.
// 비주얼 JSX 0 변경 — Tabs/Table2/Pill/처리 버튼 100% 보존.
// hardcoded fallback default 유지 — mockup standalone 미리보기 시각 호환.
// filter는 mockup 자체 state (UI 책임). counts는 queue에서 자체 계산 (그대로).
// 처리 버튼 (무시/조치)에 onClick 추가 (시각 변경 X).
function ReviewQueue({ items, onAct, onDismiss }) {
  const [filter, setFilter] = React.useState('all');
  const queue = items || [
    { sev: 'critical', type: 'abuse_burst', user: 'unknown@temp-mail.org', detected: '2026.5.4. 13:42', data: '5분간 회원가입 12건 (IP 동일)', status: 'pending' },
    { sev: 'high', type: 'cost_spike', user: 'memnyan35@gmail.com', detected: '2026.5.4. 11:03', data: '단일 세션 $0.84 (평균 12배)', status: 'pending' },
    { sev: 'high', type: 'judge_error', user: 'system', detected: '2026.5.4. 09:21', data: 'claude-sonnet-4 timeout × 8 (5분)', status: 'pending' },
    { sev: 'medium', type: 'feedback_low', user: 'rina@studio.io', detected: '2026.5.3. 22:11', data: 'Judge 응답에 1점 (3회 연속)', status: 'pending' },
    { sev: 'low', type: 'whitelist_unused', user: 'system', detected: '2026.5.3. 00:00', data: '14일째 미사용 항목 3건', status: 'pending' },
  ];
  const filtered = filter === 'all' ? queue : queue.filter(q => q.sev === filter);
  const sevPill = (s) => {
    const map = { critical: { tone: 'bad', label: 'Critical' }, high: { tone: 'warn', label: 'High' }, medium: { tone: 'primary', label: 'Medium' }, low: { tone: 'mono', label: 'Low' } };
    const m = map[s] || map.low;
    return <Pill tone={m.tone} size="xs">● {m.label}</Pill>;
  };

  return (
    <>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 12, flexWrap: 'wrap', gap: 10 }}>
        <Tabs value={filter} onChange={setFilter} tabs={[
          { value: 'all', label: '전체', count: queue.length },
          { value: 'critical', label: 'Critical', count: queue.filter(q => q.sev === 'critical').length },
          { value: 'high', label: 'High', count: queue.filter(q => q.sev === 'high').length },
          { value: 'medium', label: 'Medium', count: queue.filter(q => q.sev === 'medium').length },
          { value: 'low', label: 'Low', count: queue.filter(q => q.sev === 'low').length },
        ]}/>
        <div style={{ fontSize: 11, color: C2.faint }} className="mono">실시간 polling · 30s</div>
      </div>

      <Table2
        columns={[
          { header: '심각도', render: r => sevPill(r.sev) },
          { header: '유형', mono: true, render: r => <span style={{ color: C2.primary, fontWeight: 600 }}>{r.type}</span> },
          { header: '유저', mono: true, render: r => <span style={{ fontSize: 12, color: r.user === 'system' ? C2.faint : C2.text }}>{r.user}</span> },
          { header: '탐지 시각', mono: true, render: r => <span style={{ fontSize: 11.5, color: C2.muted }}>{r.detected}</span> },
          { header: '탐지 데이터', render: r => <span style={{ color: C2.muted, fontSize: 12.5 }}>{r.data}</span> },
          { header: '처리', align: 'right', render: r => (
            <div style={{ display: 'flex', gap: 6, justifyContent: 'flex-end' }}>
              <Btn kind="ghost" size="sm" onClick={() => onDismiss && onDismiss(r)}>무시</Btn>
              <Btn kind="primary" size="sm" onClick={() => onAct && onAct(r)}>조치</Btn>
            </div>
          )},
        ]}
        rows={filtered}
        empty="검토 대기 없음 — 어뷰징 자동 감지가 아직 발동되지 않았거나, 큐가 비어있습니다."
      />
    </>
  );
}

// ============================================================
// 3. ACTION LOGS
// ============================================================
// [β-Phase C-2-3] hardcoded rows + 자체 state → props 인터페이스 변환.
// 비주얼 JSX 0 변경 — Select/Input/Btn/Table2/Pill/페이지네이션 디테일 100% 보존.
// rows는 Wrapper에서 매핑 (server rows → mockup display rows).
function ActionLogs({
  rows = [],
  action = 'all', target = 'all', search = '',
  page = 1, totalPages = 1, total = 0,
  onChangeAction, onChangeTarget, onChangeSearch,
  onChangePage,
  onCsvDownload,
}) {
  return (
    <>
      <div style={{ display: 'flex', gap: 10, alignItems: 'center', marginBottom: 12, flexWrap: 'wrap' }}>
        <Select value={action} onChange={e => onChangeAction && onChangeAction(e.target.value)} options={[
          { value: 'all', label: '액션: 전체' },
          { value: 'beta', label: 'beta_*' },
          { value: 'whitelist', label: 'whitelist_*' },
          { value: 'toggle', label: 'toggle_*' },
          { value: 'message', label: 'message_*' },
        ]}/>
        <Select value={target} onChange={e => onChangeTarget && onChangeTarget(e.target.value)} options={[
          { value: 'all', label: '대상: 전체' },
          { value: 'user', label: 'user' },
          { value: 'beta_request', label: 'beta_request' },
          { value: 'whitelist', label: 'whitelist' },
        ]}/>
        <Input icon="🔍" placeholder="관리자 ID / IP 검색" value={search} onChange={e => onChangeSearch && onChangeSearch(e.target.value)} style={{ flex: 1, minWidth: 180 }}/>
        <Btn kind="default" size="sm" onClick={onCsvDownload}>CSV 내려받기</Btn>
      </div>

      <Table2
        columns={[
          { header: '시각', mono: true, render: r => <span style={{ fontSize: 11.5, color: C2.muted }}>{r.time}</span> },
          { header: '관리자', mono: true, render: r => <span className="mono" style={{ fontSize: 11.5, color: C2.text }}>{r.admin}</span> },
          { header: '액션', render: r => <Pill tone="primary" size="xs">{r.action}</Pill> },
          { header: '대상', mono: true, render: r => <span style={{ fontSize: 11.5, color: C2.muted }}>{r.target}</span> },
          { header: 'IP', mono: true, render: r => <span style={{ fontSize: 11.5, color: C2.faint }}>{r.ip}</span> },
          { header: '상세', render: r => <span style={{ color: C2.muted, fontSize: 12 }}>{r.detail}</span> },
        ]}
        rows={rows}
      />

      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginTop: 14, fontSize: 12, color: C2.muted }}>
        <span className="mono">전체 {total}건 · 7일 보존</span>
        <div style={{ display: 'flex', gap: 4 }}>
          <button onClick={() => onChangePage && onChangePage(page - 1)} disabled={page <= 1} style={{ width: 28, height: 28, border: `1px solid ${C2.border}`, background: C2.panel, borderRadius: 6, color: C2.faint, cursor: page <= 1 ? 'not-allowed' : 'pointer' }}>‹</button>
          <button style={{ width: 28, height: 28, border: `1px solid ${C2.primary}`, background: C2.primary, borderRadius: 6, color: 'white', fontWeight: 700 }}>{page}</button>
          <button onClick={() => onChangePage && onChangePage(page + 1)} disabled={page >= totalPages} style={{ width: 28, height: 28, border: `1px solid ${C2.border}`, background: C2.panel, borderRadius: 6, color: C2.faint, cursor: page >= totalPages ? 'not-allowed' : 'pointer' }}>›</button>
        </div>
      </div>
    </>
  );
}

// ============================================================
// 4. TOGGLES
// ============================================================
function Toggle({ on, onChange }) {
  return (
    <button onClick={() => onChange(!on)} style={{
      width: 38, height: 22, borderRadius: 11,
      background: on ? C2.primary : C2.borderStrong,
      border: 0, cursor: 'pointer', padding: 2,
      transition: 'background 0.18s',
      position: 'relative',
    }}>
      <div style={{
        width: 18, height: 18, borderRadius: '50%', background: 'white',
        boxShadow: '0 1px 3px rgba(0,0,0,0.2)',
        transform: on ? 'translateX(16px)' : 'translateX(0)',
        transition: 'transform 0.18s',
      }}/>
    </button>
  );
}

// [β-Phase C-1] 자체 state/items hardcoded → props 인터페이스 변환 (d3-C-toggles 패턴).
// 비주얼 JSX 0 변경 — Card / 메시지 박스 / Pill / Toggle / 메타 100% 보존.
// editable=true 시 메시지 박스 자리에 input + 저장 Btn.
function Toggles({ items, onToggle, onMessageChange, editable = false }) {
  // editable 모드 buffer (서버 reload 전까지 input 값 보존). 정적 모드에선 미사용.
  const [editingMsg, setEditingMsg] = React.useState({});

  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
      {items.map((it) => {
        const buffer = editingMsg[it.key];
        const inputValue = buffer !== undefined ? buffer : (it.userMsg || '');
        const dirty = buffer !== undefined && buffer !== (it.userMsg || '');
        return (
          <Card key={it.key} padding={0}>
            <div style={{ padding: '16px 20px', borderBottom: it.enabled ? `1px solid ${C2.border}` : 'none' }}>
              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', gap: 16 }}>
                <div style={{ flex: 1 }}>
                  <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 6 }}>
                    <span style={{ fontSize: 16 }}>{it.emoji}</span>
                    <span style={{ fontSize: 14.5, fontWeight: 700, color: C2.text }}>{it.title}</span>
                    {it.enabled && <Pill tone="bad" size="xs">● ACTIVE</Pill>}
                  </div>
                  <div style={{ fontSize: 12.5, color: C2.muted, lineHeight: 1.55 }}>{it.desc}</div>
                </div>
                <Toggle on={it.enabled} onChange={(v) => onToggle && onToggle(it.key, v)}/>
              </div>
              <div style={{ marginTop: 12, fontSize: 10.5, color: C2.muted, fontWeight: 700, letterSpacing: '0.04em', textTransform: 'uppercase', marginBottom: 4 }}>사용자 표시 메시지</div>
              {editable ? (
                <div style={{ display: 'flex', gap: 8 }}>
                  <input
                    type="text"
                    value={inputValue}
                    onChange={(e) => setEditingMsg((prev) => ({ ...prev, [it.key]: e.target.value }))}
                    placeholder="(미입력 시 default 메시지)"
                    maxLength={500}
                    style={{
                      flex: 1, padding: '10px 12px', borderRadius: 7,
                      background: C2.primarySofter, border: `1px solid ${C2.border}`,
                      fontSize: 12.5, color: C2.textSoft,
                      fontFamily: "'JetBrains Mono', ui-monospace, monospace",
                      outline: 'none',
                    }}
                  />
                  {dirty && (
                    <Btn kind="primary" size="sm" onClick={() => {
                      if (onMessageChange) onMessageChange(it.key, buffer);
                      setEditingMsg((prev) => { const next = { ...prev }; delete next[it.key]; return next; });
                    }}>저장</Btn>
                  )}
                </div>
              ) : (
                <div style={{
                  padding: '10px 12px', background: C2.primarySofter, borderRadius: 7,
                  fontSize: 12.5, color: C2.textSoft, fontFamily: "'JetBrains Mono', ui-monospace, monospace",
                }}>{it.userMsg}</div>
              )}
              <div className="mono" style={{ fontSize: 10.5, color: C2.faint, marginTop: 8 }}>{it.metaText}</div>
            </div>
          </Card>
        );
      })}
    </div>
  );
}

// ============================================================
// 5. ALERTS
// ============================================================
function Alerts({ items = [], onChannelToggle, onToggle }) {

  return (
    <>
      <Card padding={16} style={{ marginBottom: 14 }}>
        <div style={{ fontSize: 13, color: C2.muted, lineHeight: 1.6 }}>
          이벤트별로 어떤 채널 (Discord / Email / 둘 다) 로 알림 받을지 설정.
          <br/>구독 안 된 이벤트는 default 동작 — 모든 어드민에게 디스코드 발송 가능.
        </div>
        <div style={{ marginTop: 12, display: 'flex', gap: 16, fontSize: 11, color: C2.faint }}>
          <span>환경변수</span>
          <span className="mono" style={{ background: C2.primarySofter, padding: '2px 6px', borderRadius: 4, color: C2.primary }}>DISCORD_WEBHOOK_URL</span>
          <span className="mono" style={{ background: C2.primarySofter, padding: '2px 6px', borderRadius: 4, color: C2.primary }}>RESEND_API_KEY</span>
          <span>설정 필요 (미설정 시 silent skip).</span>
        </div>
      </Card>

      <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
        {items.map((it, i) => (
          <Card key={it.key} padding={14}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 16 }}>
              <div style={{ flex: 1, display: 'flex', alignItems: 'center', gap: 10 }}>
                <span style={{ fontSize: 16 }}>{it.emoji}</span>
                <div>
                  <div style={{ fontSize: 13.5, fontWeight: 600, color: C2.text }}>{it.title}</div>
                  <div className="mono" style={{ fontSize: 10.5, color: C2.faint, marginTop: 3, background: C2.rowSofter, display: 'inline-block', padding: '1px 6px', borderRadius: 3 }}>{it.key}</div>
                </div>
              </div>
              <div style={{ display: 'flex', gap: 4 }}>
                {[
                  { k: 'discord', label: 'Discord' },
                  { k: 'email', label: 'Email' },
                  { k: 'both', label: '둘 다' },
                ].map(ch => (
                  <button key={ch.k} onClick={() => onChannelToggle && onChannelToggle(i, ch.k)} style={{
                    padding: '5px 10px', borderRadius: 6,
                    background: it[ch.k] ? C2.primary : C2.panel,
                    color: it[ch.k] ? 'white' : C2.muted,
                    border: `1px solid ${it[ch.k] ? C2.primary : C2.border}`,
                    fontSize: 11, fontWeight: 600, cursor: 'pointer',
                  }}>{ch.label}</button>
                ))}
              </div>
              <Toggle on={it.on} onChange={v => onToggle && onToggle(i, v)}/>
            </div>
          </Card>
        ))}
      </div>
    </>
  );
}

// ============================================================
// 6. FEEDBACK
// ============================================================
// [β-Phase C-3-6] hardcoded → props 인터페이스 변환.
// 비주얼 JSX 0 변경 — Tabs/Card/빈 상태 디테일 100% 보존.
// list / 카드 / 상세 / 회신 UI는 mockup 시각 부재 → 미통합 (docs/admin-pending.md 추적).
// props: tab/onTabChange/emptyTitle/emptySub (텍스트만 props화).
// hardcoded fallback default 유지 — mockup standalone 미리보기 시각 호환.
function Feedback({ tab: tabProp, onTabChange, emptyTitle, emptySub }) {
  const [innerTab, setInnerTab] = React.useState('new');
  const tab    = tabProp !== undefined ? tabProp : innerTab;
  const setTab = onTabChange || setInnerTab;
  const tabs = [
    { value: 'new', label: '🆕 신규' },
    { value: 'progress', label: '🚧 진행' },
    { value: 'closed', label: '✓ 처리' },
    { value: 'reject', label: '❌ 거절' },
    { value: 'unfinished', label: '◐ 미수정' },
    { value: 'reply', label: '💬 응답' },
  ];
  const title = emptyTitle !== undefined ? emptyTitle : '#눈누가 없습니다.';
  const sub   = emptySub   !== undefined ? emptySub   : "feedback 테이블에 status='new' 인 항목이 없습니다.";
  return (
    <>
      <Tabs value={tab} onChange={setTab} tabs={tabs}/>
      <div style={{ marginTop: 14 }}>
        <Card padding={0}>
          <div style={{ padding: '60px 20px', textAlign: 'center', color: C2.faint, fontSize: 13.5 }}>
            {title}
            <div style={{ fontSize: 11.5, color: C2.faint, marginTop: 8 }} className="mono">{sub}</div>
          </div>
        </Card>
      </div>
    </>
  );
}

// ============================================================
// 7. MESSAGES
// ============================================================
// [β-Phase C-3-7] hardcoded → props 인터페이스 변환.
// 비주얼 JSX 0 변경 — Card/Input/Tabs/textarea/Btn 100% 보존.
// form 자체 state 유지 (UI 책임 — Whitelist 패턴).
// 발송 버튼에 onClick 추가 (시각 변경 X). onSend 성공 시 title/body 비움.
function Messages({ onSend }) {
  const [recipient, setRecipient] = React.useState('');
  const [type, setType] = React.useState('admin_reply');
  const [title, setTitle] = React.useState('');
  const [body, setBody] = React.useState('');

  const handleSend = async () => {
    if (!onSend) return;
    const ok = await onSend({ recipient, type, title, body });
    if (ok) {
      setTitle(''); setBody('');
    }
  };

  return (
    <Card padding={24}>
      <div style={{ fontSize: 13, color: C2.muted, lineHeight: 1.6, marginBottom: 20, padding: '12px 14px', background: C2.primarySofter, borderRadius: 8 }}>
        개별 사용자에게 1:1 인앱 메시지를 보냅니다. 사용자는 메인 앱 헤더의 종 배지로 받아봅니다.<br/>
        피드백 회신은 피드백 인박스 → "회신" 버튼으로 자동 처리됩니다 (이 화면은 직접 발송용).
      </div>

      <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
        <div>
          <div style={{ fontSize: 11, color: C2.muted, fontWeight: 700, letterSpacing: '0.04em', textTransform: 'uppercase', marginBottom: 6 }}>recipient_id (users.id UUID)</div>
          <Input value={recipient} onChange={e => setRecipient(e.target.value)} placeholder="00000000-0000-0000-0000-000000000000"/>
        </div>

        <div>
          <div style={{ fontSize: 11, color: C2.muted, fontWeight: 700, letterSpacing: '0.04em', textTransform: 'uppercase', marginBottom: 6 }}>type</div>
          <Tabs value={type} onChange={setType} tabs={[
            { value: 'admin_reply', label: '어드민 회신' },
            { value: 'system', label: '시스템' },
            { value: 'support', label: '지원' },
            { value: 'reward', label: '보상' },
          ]}/>
        </div>

        <div>
          <div style={{ fontSize: 11, color: C2.muted, fontWeight: 700, letterSpacing: '0.04em', textTransform: 'uppercase', marginBottom: 6 }}>제목</div>
          <Input value={title} onChange={e => setTitle(e.target.value)} placeholder=""/>
        </div>

        <div>
          <div style={{ fontSize: 11, color: C2.muted, fontWeight: 700, letterSpacing: '0.04em', textTransform: 'uppercase', marginBottom: 6 }}>본문</div>
          <textarea value={body} onChange={e => setBody(e.target.value)} rows={6} style={{
            width: '100%', padding: '10px 12px',
            background: C2.panel, border: `1px solid ${C2.border}`, borderRadius: 7,
            fontSize: 13, color: C2.text, fontFamily: 'inherit',
            resize: 'vertical', outline: 'none',
          }}
            onFocus={e => { e.currentTarget.style.borderColor = C2.primary; e.currentTarget.style.boxShadow = `0 0 0 3px ${C2.primarySoft}`; }}
            onBlur={e => { e.currentTarget.style.borderColor = C2.border; e.currentTarget.style.boxShadow = 'none'; }}
          />
        </div>

        <div style={{ display: 'flex', justifyContent: 'center', marginTop: 8 }}>
          <Btn kind="primary" onClick={handleSend}>메시지 발송</Btn>
        </div>
      </div>
    </Card>
  );
}

// ============================================================
// 8. ANNOUNCEMENTS
// ============================================================
// [β-Phase C-3-8] hardcoded → props 인터페이스 변환.
// 비주얼 JSX 0 변경 — Card/Input/Tabs/checkbox/datetime-local/Btn/list 자리 100% 보존.
// form 자체 state 유지 (UI 책임). 시작/종료 datetime-local 자체 state 추가 (현재는 hardcoded value="").
// 발송 버튼에 onClick 추가 (시각 변경 X). 성공 시 title/body 자체 비움.
// list / 편집 / 발행 / 삭제 UI는 mockup 시각 부재 → 미통합 (docs/admin-pending.md 추적).
// count / emptyText만 props로 (Feedback 패턴).
function Announcements({ onSubmit, count, emptyText }) {
  const [title, setTitle] = React.useState('');
  const [body, setBody] = React.useState('');
  const [sev, setSev] = React.useState('info');
  const [target, setTarget] = React.useState('all');
  const [showBanner, setShowBanner] = React.useState(true);
  const [silentEmail, setSilentEmail] = React.useState(false);
  const [startsAt, setStartsAt] = React.useState('');
  const [endsAt,   setEndsAt]   = React.useState('');

  const handleSubmit = async () => {
    if (!onSubmit) return;
    const ok = await onSubmit({ title, body, sev, target, showBanner, silentEmail, startsAt, endsAt });
    if (ok) {
      setTitle(''); setBody(''); setStartsAt(''); setEndsAt('');
    }
  };

  const cnt = count !== undefined ? count : 0;
  const empty = emptyText !== undefined ? emptyText : '등록된 공지가 없습니다.';

  return (
    <>
      <Card padding={24} style={{ marginBottom: 16 }}>
        <div style={{ fontSize: 14, fontWeight: 700, color: C2.text, marginBottom: 14, display: 'flex', alignItems: 'center', gap: 8 }}>
          ✨ 새 공지 작성
        </div>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
          <div>
            <div style={{ fontSize: 11, color: C2.muted, fontWeight: 700, letterSpacing: '0.04em', textTransform: 'uppercase', marginBottom: 6 }}>제목</div>
            <Input value={title} onChange={e => setTitle(e.target.value)}/>
          </div>
          <div>
            <div style={{ fontSize: 11, color: C2.muted, fontWeight: 700, letterSpacing: '0.04em', textTransform: 'uppercase', marginBottom: 6 }}>내용</div>
            <textarea value={body} onChange={e => setBody(e.target.value)} rows={5} placeholder="유니코드 그대로 표시됩니다." style={{
              width: '100%', padding: '10px 12px',
              background: C2.panel, border: `1px solid ${C2.border}`, borderRadius: 7,
              fontSize: 13, color: C2.text, fontFamily: 'inherit', resize: 'vertical', outline: 'none',
            }}/>
          </div>

          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16 }}>
            <div>
              <div style={{ fontSize: 11, color: C2.muted, fontWeight: 700, letterSpacing: '0.04em', textTransform: 'uppercase', marginBottom: 6 }}>심각도</div>
              <Tabs value={sev} onChange={setSev} tabs={[
                { value: 'info', label: '🔵 Info' },
                { value: 'warning', label: '⚠ Warning' },
                { value: 'critical', label: '🔴 Critical' },
              ]}/>
            </div>
            <div>
              <div style={{ fontSize: 11, color: C2.muted, fontWeight: 700, letterSpacing: '0.04em', textTransform: 'uppercase', marginBottom: 6 }}>대상</div>
              <Tabs value={target} onChange={setTarget} tabs={[
                { value: 'all', label: '전체' },
                { value: 'beta', label: '베타 사용자' },
                { value: 'paid', label: '유료 구독자' },
                { value: 'admin', label: '어드민' },
              ]}/>
            </div>
          </div>

          <div style={{ display: 'flex', gap: 14, fontSize: 13, color: C2.muted }}>
            <label style={{ display: 'flex', alignItems: 'center', gap: 6, cursor: 'pointer' }}>
              <input type="checkbox" checked={showBanner} onChange={e => setShowBanner(e.target.checked)} style={{ accentColor: C2.primary }}/>
              메인 앱 배너 노출
            </label>
            <label style={{ display: 'flex', alignItems: 'center', gap: 6, cursor: 'pointer' }}>
              <input type="checkbox" checked={silentEmail} onChange={e => setSilentEmail(e.target.checked)} style={{ accentColor: C2.primary }}/>
              발송 시 대상에게 이메일 별송 (1회 한정)
            </label>
          </div>

          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16 }}>
            <div>
              <div style={{ fontSize: 11, color: C2.muted, fontWeight: 700, letterSpacing: '0.04em', textTransform: 'uppercase', marginBottom: 6 }}>시작 (선택, 기본: 즉시)</div>
              <Input type="datetime-local" value={startsAt} onChange={e => setStartsAt(e.target.value)}/>
            </div>
            <div>
              <div style={{ fontSize: 11, color: C2.muted, fontWeight: 700, letterSpacing: '0.04em', textTransform: 'uppercase', marginBottom: 6 }}>종료 (선택, 빈 값 = 무기한)</div>
              <Input type="datetime-local" value={endsAt} onChange={e => setEndsAt(e.target.value)}/>
            </div>
          </div>

          <div>
            <Btn kind="primary" onClick={handleSubmit}>📢 공지 생성 (초안)</Btn>
          </div>
        </div>
      </Card>

      <div style={{ fontSize: 11, fontWeight: 700, color: C2.muted, letterSpacing: '0.04em', textTransform: 'uppercase', margin: '24px 0 10px' }}>📋 기존 공지 ({cnt}건)</div>
      <Card padding={0}>
        <div style={{ padding: '50px 20px', textAlign: 'center', color: C2.faint, fontSize: 13 }}>{empty}</div>
      </Card>
    </>
  );
}

window.AdminPages2 = { AICost, ReviewQueue, ActionLogs, Toggles, Alerts, Feedback, Messages, Announcements };
