/* global React, Icon, UI, LessonHighData */
// ============================================================
// I9 · 강사 운영 — F7 (정산 · KPI · 스케줄 스왑 · 휴가)
//   강사 카드 그리드 + 드로어 상세 + 정산서 모달 + 휴가/스왑 모달
// ============================================================
(function () {
const { useState, useMemo } = React;
const D = LessonHighData;

const PAY_MODELS = {
  session:     { label: '수업당',         hint: '회당 정액' },
  share:       { label: '매출 셰어 %',    hint: '담당 회원 결제의 일정 비율' },
  hourly:      { label: '시간당',         hint: '시급 × 누적 시간' },
  salary_perf: { label: '기본급 + 성과',  hint: '기본급 + 갱신/신규 성과급' },
};

const PAY_STATUS = {
  pending:  { label: '정산 대기', tone: 'warning' },
  paid:     { label: '지급 완료', tone: 'success' },
  disputed: { label: '이의 제기', tone: 'danger' },
};

// ────────────────────────────────────────────────────────────
// Main page
// ────────────────────────────────────────────────────────────
function InstructorsPage({ onOpenMember, initialTeacherId }) {
  const [openTeacher,   setOpenTeacher]   = useState(initialTeacherId || null);
  const [payoutFor,     setPayoutFor]     = useState(null);
  const [timeOffOpen,   setTimeOffOpen]   = useState(false);
  const [swapsOpen,     setSwapsOpen]     = useState(false);
  const [inviteOpen,    setInviteOpen]    = useState(false);
  const [payRuleFor,    setPayRuleFor]    = useState(null);
  const [timeOff,       setTimeOff]       = useState(() => [...D.timeOff]);
  const [swaps,         setSwaps]         = useState(() => [...D.schedSwaps]);
  const [viewMode,      setViewMode]      = useState(() => {
    try { return localStorage.getItem('lh-teachers-view') || (D.teachers.length >= 6 ? 'list' : 'cards'); }
    catch { return D.teachers.length >= 6 ? 'list' : 'cards'; }
  });
  const switchView = (m) => { setViewMode(m); try { localStorage.setItem('lh-teachers-view', m); } catch {} };

  // 집계 KPI
  const totalNetPay = D.teachers.reduce((s, t) => s + D.payouts[t.id].net, 0);
  const avgRenewal  = Math.round(D.teachers.reduce((s, t) => s + D.teacherStats[t.id].renewalTrend.at(-1), 0) / D.teachers.length);
  const pendingSwaps = swaps.filter(s => s.status === 'pending').length;
  const pendingTO   = timeOff.filter(t => t.status === 'pending').length;

  return (
    <React.Fragment>
      <header className="page-header">
        <div>
          <h1 className="page-title">강사</h1>
          <div className="page-sub">
            {D.teachers.length}명 · 이번 달 정산 합계 {D.formatMoney(totalNetPay)} · 평균 재등록률 {avgRenewal}%
            {pendingSwaps > 0 && <span style={{ color: 'var(--status-warning)', marginLeft: 8 }}>· 스왑 요청 {pendingSwaps}건</span>}
            {pendingTO > 0    && <span style={{ color: 'var(--status-warning)', marginLeft: 8 }}>· 휴가 승인 {pendingTO}건</span>}
          </div>
        </div>
        <div className="page-actions">
          <UI.Button variant="ghost" icon={Icon.Calendar} onClick={() => setSwapsOpen(true)}>스왑 요청 {pendingSwaps > 0 && <span className="num-pill">{pendingSwaps}</span>}</UI.Button>
          <UI.Button variant="ghost" icon={Icon.Coffee} onClick={() => setTimeOffOpen(true)}>휴가 / 결강</UI.Button>
          <UI.Button variant="primary" icon={Icon.Plus} onClick={() => setInviteOpen(true)}>강사 초대</UI.Button>
        </div>
      </header>

      {/* KPI */}
      <div className="grid-4" style={{ marginBottom: 16 }}>
        <InsKpi label="총 강사" value={`${D.teachers.length}명`} sub={`이번 달 신규 +0 · 정원 없음`}/>
        <InsKpi label="이번 달 정산 합계" value={D.formatMoney(totalNetPay)} sub="12/05 지급 예정" tone="accent"/>
        <InsKpi label="평균 재등록률" value={`${avgRenewal}%`} sub={`6개월 평균 +${avgRenewal - 84}%p`} tone="success"/>
        <InsKpi label="결강 / 휴가" value={`${timeOff.length}건`} sub={`승인 ${timeOff.filter(t => t.status === 'approved').length} · 대기 ${pendingTO}`} tone={pendingTO > 0 ? 'warning' : 'neutral'}/>
      </div>

      {/* 강사 그리드 / 목록 */}
      <div className="instructors-toolbar">
        <span className="t-caption">강사 {D.teachers.length}명</span>
        <div style={{ flex: 1 }}/>
        <div className="seg-toggle" role="tablist" aria-label="강사 보기 방식">
          <button type="button" role="tab" aria-selected={viewMode === 'list'}  className={`seg-toggle__btn ${viewMode === 'list'  ? 'is-active' : ''}`} onClick={() => switchView('list')}>목록</button>
          <button type="button" role="tab" aria-selected={viewMode === 'cards'} className={`seg-toggle__btn ${viewMode === 'cards' ? 'is-active' : ''}`} onClick={() => switchView('cards')}>카드</button>
        </div>
      </div>

      {viewMode === 'cards' ? (
        <div className="grid-3" style={{ alignItems: 'stretch' }}>
          {D.teachers.map(t => <TeacherCard key={t.id} teacher={t} onOpen={() => setOpenTeacher(t.id)} onOpenPayout={() => setPayoutFor(t.id)} onOpenPayRule={() => setPayRuleFor(t.id)}/>)}
        </div>
      ) : (
        <UI.Card>
          <UI.CardBody flush>
            <div className="teacher-rows">
              {D.teachers.map(t => <TeacherRow key={t.id} teacher={t} onOpen={() => setOpenTeacher(t.id)} onOpenPayout={() => setPayoutFor(t.id)} onOpenPayRule={() => setPayRuleFor(t.id)}/>)}
            </div>
          </UI.CardBody>
        </UI.Card>
      )}

      {/* 6개월 매출 비교 차트 */}
      <UI.Card style={{ marginTop: 24 }}>
        <UI.CardHeader title="강사별 매출 기여 — 6개월" subtitle="담당 회원의 결제 합계 · 정산과는 별개"/>
        <UI.CardBody>
          <TeacherTrendChart/>
        </UI.CardBody>
      </UI.Card>

      {/* 강사 상세 드로어 */}
      <UI.Drawer
        open={!!openTeacher}
        onClose={() => setOpenTeacher(null)}
        title={<span className="t-micro">강사 상세</span>}
        width={620}
        footer={
          <React.Fragment>
            <UI.Button variant="ghost" icon={Icon.Mail} onClick={() => window.toast && window.toast(`${D.getTeacher(openTeacher).name}님께 정산서 미리보기 이메일 발송`, { tone: 'success' })}>정산서 보내기</UI.Button>
            <div style={{ flex: 1 }}/>
            <UI.Button icon={Icon.Edit} onClick={() => { setPayRuleFor(openTeacher); }}>정산 룰 편집</UI.Button>
            <UI.Button variant="primary" icon={Icon.Receipt} onClick={() => { setPayoutFor(openTeacher); }}>정산서 보기</UI.Button>
          </React.Fragment>
        }
      >
        {openTeacher && <TeacherDetail teacherId={openTeacher} onOpenMember={onOpenMember} onOpenPayout={() => setPayoutFor(openTeacher)}/>}
      </UI.Drawer>

      {/* 모달들 */}
      <PayoutModal open={!!payoutFor} onClose={() => setPayoutFor(null)} teacherId={payoutFor}/>
      <PayRuleModal open={!!payRuleFor} onClose={() => setPayRuleFor(null)} teacherId={payRuleFor}/>
      <SwapsModal open={swapsOpen} onClose={() => setSwapsOpen(false)} swaps={swaps} setSwaps={setSwaps} onOpenMember={onOpenMember}/>
      <TimeOffModal open={timeOffOpen} onClose={() => setTimeOffOpen(false)} items={timeOff} setItems={setTimeOff}/>
      <InviteModal open={inviteOpen} onClose={() => setInviteOpen(false)}/>
    </React.Fragment>
  );
}

// ────────────────────────────────────────────────────────────
// Teacher card
// ────────────────────────────────────────────────────────────
function TeacherCard({ teacher, onOpen, onOpenPayout, onOpenPayRule }) {
  const p = D.getProfile(teacher.id);
  const s = D.getStats(teacher.id);
  const pay = D.getPayout(teacher.id);
  const members = D.getTeacherMembers(teacher.id);
  const completion = Math.round(s.thisMonth.completed / s.thisMonth.scheduled * 100);
  const renewal = s.renewalTrend.at(-1);

  return (
    <UI.Card className="teacher-card">
      <UI.CardBody>
        <div className="t-card-head">
          <div className="t-card-avatar" style={{ background: teacher.color }}>{teacher.initials}</div>
          <div style={{ flex: 1, minWidth: 0 }}>
            <strong style={{ fontSize: 16, letterSpacing: '-0.01em' }}>{teacher.name}</strong>
            <div style={{ fontSize: 11, color: 'var(--text-tertiary)', marginTop: 2 }}>{p.tier} · {p.bio}</div>
          </div>
          <button className="icon-btn" onClick={onOpen} title="상세"><Icon.ChevRight size={16}/></button>
        </div>

        <div className="t-card-stats">
          <div><div className="t-micro">담당 회원</div><strong className="t-mono">{members.length}<span style={{ fontSize: 11, color: 'var(--text-tertiary)' }}>/{p.capacityPerWeek}</span></strong></div>
          <div><div className="t-micro">출석 완료율</div><strong className="t-mono">{completion}%</strong></div>
          <div><div className="t-micro">재등록률</div><strong className="t-mono">{renewal}%</strong></div>
        </div>

        <div className="t-card-payout">
          <div>
            <div className="t-micro" style={{ marginBottom: 2 }}>이번 달 정산</div>
            <strong className="t-mono" style={{ fontSize: 18 }}>{D.formatMoney(pay.net)}</strong>
            <div style={{ fontSize: 11, color: 'var(--text-tertiary)' }}>{PAY_MODELS[p.payModel].label} · {pay.summary}</div>
          </div>
          <UI.Badge tone={PAY_STATUS[pay.status].tone}>{PAY_STATUS[pay.status].label}</UI.Badge>
        </div>

        <div className="t-card-spark">
          <UI.Sparkline values={s.trend6m.map(v => v / 1000000)} stroke={teacher.color} width={260} height={32}/>
          <span style={{ fontSize: 10, color: 'var(--text-tertiary)' }}>6개월 매출 기여 추이</span>
        </div>

        <div style={{ display: 'flex', gap: 6, marginTop: 14 }}>
          <UI.Button size="sm" variant="ghost" onClick={onOpenPayRule}>정산 룰</UI.Button>
          <div style={{ flex: 1 }}/>
          <UI.Button size="sm" variant="ghost" onClick={onOpen}>상세</UI.Button>
          <UI.Button size="sm" variant="primary" onClick={onOpenPayout}>정산서</UI.Button>
        </div>
      </UI.CardBody>
    </UI.Card>
  );
}

// ────────────────────────────────────────────────────────────
// Teacher row (compact list view — scales to 10+ teachers)
// ────────────────────────────────────────────────────────────
function TeacherRow({ teacher, onOpen, onOpenPayout, onOpenPayRule }) {
  const p = D.getProfile(teacher.id);
  const s = D.getStats(teacher.id);
  const pay = D.getPayout(teacher.id);
  const members = D.getTeacherMembers(teacher.id);
  const completion = Math.round(s.thisMonth.completed / s.thisMonth.scheduled * 100);
  const renewal = s.renewalTrend.at(-1);

  return (
    <div className="teacher-row" role="button" tabIndex={0} onClick={onOpen} onKeyDown={(e) => { if (e.key === 'Enter') onOpen(); }}>
      <div className="teacher-row__id">
        <div className="t-card-avatar" style={{ background: teacher.color, width: 36, height: 36, fontSize: 15, borderRadius: 9 }}>{teacher.initials}</div>
        <div className="col" style={{ gap: 1, minWidth: 0 }}>
          <strong className="t-body-strong" style={{ letterSpacing: '-0.01em' }}>{teacher.name}</strong>
          <span className="t-caption" style={{ fontSize: 11.5 }}>{p.tier} · {PAY_MODELS[p.payModel].label}</span>
        </div>
      </div>

      <div className="teacher-row__metric">
        <div className="t-micro">담당</div>
        <strong className="t-mono num">{members.length}<span style={{ fontSize: 11, color: 'var(--text-tertiary)', fontWeight: 500 }}>/{p.capacityPerWeek}</span></strong>
      </div>
      <div className="teacher-row__metric">
        <div className="t-micro">완료율</div>
        <strong className="t-mono num">{completion}%</strong>
      </div>
      <div className="teacher-row__metric">
        <div className="t-micro">재등록</div>
        <strong className="t-mono num">{renewal}%</strong>
      </div>
      <div className="teacher-row__spark">
        <UI.Sparkline values={s.trend6m.map(v => v / 1000000)} stroke={teacher.color} width={120} height={28}/>
      </div>
      <div className="teacher-row__pay">
        <div className="t-micro">이번 달 정산</div>
        <div className="row" style={{ gap: 6, alignItems: 'baseline' }}>
          <strong className="t-mono num" style={{ fontSize: 15 }}>{D.formatMoney(pay.net)}</strong>
          <UI.Badge tone={PAY_STATUS[pay.status].tone}>{PAY_STATUS[pay.status].label}</UI.Badge>
        </div>
      </div>
      <div className="teacher-row__actions" onClick={(e) => e.stopPropagation()}>
        <UI.Button size="sm" variant="ghost" onClick={onOpenPayRule}>정산 룰</UI.Button>
        <UI.Button size="sm" variant="primary" onClick={onOpenPayout}>정산서</UI.Button>
      </div>
    </div>
  );
}

function InsKpi({ label, value, sub, tone = 'neutral' }) {
  const c = tone === 'success' ? 'var(--status-success)' : tone === 'warning' ? 'var(--status-warning)' : tone === 'accent' ? 'var(--accent-default)' : 'var(--text-primary)';
  return (
    <UI.Card>
      <UI.CardBody>
        <div className="t-micro" style={{ color: 'var(--text-tertiary)', marginBottom: 6 }}>{label}</div>
        <div style={{ fontSize: 22, fontWeight: 700, letterSpacing: '-0.018em', color: c }}>{value}</div>
        {sub && <div style={{ fontSize: 11, color: 'var(--text-tertiary)', marginTop: 4 }}>{sub}</div>}
      </UI.CardBody>
    </UI.Card>
  );
}

// ────────────────────────────────────────────────────────────
// Teacher detail drawer body
// ────────────────────────────────────────────────────────────
function TeacherDetail({ teacherId, onOpenMember, onOpenPayout }) {
  const [tab, setTab] = useState('overview');
  const t = D.getTeacher(teacherId);
  const p = D.getProfile(teacherId);
  const s = D.getStats(teacherId);
  const pay = D.getPayout(teacherId);
  const members = D.getTeacherMembers(teacherId);

  const tabs = [
    { id: 'overview', label: '개요' },
    { id: 'members',  label: `담당 회원 ${members.length}` },
    { id: 'kpi',      label: 'KPI' },
    { id: 'schedule', label: '일정' },
    { id: 'payout',   label: '정산 이력' },
  ];

  return (
    <div>
      {/* hero */}
      <div className="t-detail-hero">
        <div className="t-detail-avatar" style={{ background: t.color }}>{t.initials}</div>
        <div>
          <div style={{ fontSize: 18, fontWeight: 700 }}>{t.name}</div>
          <div style={{ fontSize: 12, color: 'var(--text-tertiary)' }}>{p.tier} · {p.email}</div>
          <div style={{ fontSize: 12, color: 'var(--text-secondary)', marginTop: 4 }}>{p.bio}</div>
          <div style={{ display: 'flex', gap: 6, marginTop: 8 }}>
            <UI.Badge tone="accent">{PAY_MODELS[p.payModel].label}</UI.Badge>
            <UI.Badge tone="neutral">합류 {p.joined}</UI.Badge>
          </div>
        </div>
      </div>

      <UI.Tabs items={tabs} active={tab} onChange={setTab}/>

      <div style={{ marginTop: 14 }}>
        {tab === 'overview' && <TeacherOverview teacherId={teacherId}/>}
        {tab === 'members'  && <TeacherMembers teacherId={teacherId} onOpenMember={onOpenMember}/>}
        {tab === 'kpi'      && <TeacherKpi teacherId={teacherId}/>}
        {tab === 'schedule' && <TeacherSchedule teacherId={teacherId}/>}
        {tab === 'payout'   && <TeacherPayoutHistory teacherId={teacherId} onOpen={onOpenPayout}/>}
      </div>
    </div>
  );
}

function TeacherOverview({ teacherId }) {
  const s = D.getStats(teacherId);
  const pay = D.getPayout(teacherId);
  const items = [
    { label: '이번 달 매출 기여', value: D.formatMoney(s.thisMonth.revenueAttributed), delta: `${pct(s.thisMonth.revenueAttributed, s.lastMonth.revenueAttributed)}` },
    { label: '담당 회원',         value: `${s.thisMonth.members}명`, sub: `신규 +${s.thisMonth.newMembers} · 유지 ${s.thisMonth.retainedMembers}` },
    { label: '출석률',           value: `${s.thisMonth.avgAttendanceRate}%`, sub: `완료 ${s.thisMonth.completed} / 예정 ${s.thisMonth.scheduled}` },
    { label: '재등록률',         value: `${s.renewalTrend.at(-1)}%`, sub: `직전 ${s.renewalTrend.at(-2)}%` },
    { label: '평균 일지 길이',   value: `${s.thisMonth.avgJournalChars}자`, sub: '학원 평균 117자' },
    { label: '이번 달 정산',     value: D.formatMoney(pay.net), sub: `${PAY_STATUS[pay.status].label}` },
  ];
  return (
    <div className="t-detail-grid">
      {items.map(it => (
        <div key={it.label} className="t-detail-cell">
          <div className="t-micro">{it.label}</div>
          <div style={{ fontSize: 16, fontWeight: 700, color: 'var(--text-primary)', marginTop: 4 }}>{it.value}</div>
          {it.delta && <div style={{ fontSize: 11, color: 'var(--status-success)', fontWeight: 600 }}>{it.delta}</div>}
          {it.sub && <div style={{ fontSize: 11, color: 'var(--text-tertiary)' }}>{it.sub}</div>}
        </div>
      ))}
    </div>
  );
}

function TeacherMembers({ teacherId, onOpenMember }) {
  const members = D.getTeacherMembers(teacherId);
  return (
    <div>
      {members.map(m => (
        <div key={m.id} className="t-detail-member" onClick={() => onOpenMember && onOpenMember(m.id)}>
          <UI.Avatar name={m.name} tone={m.tone}/>
          <div style={{ flex: 1 }}>
            <strong>{m.name}</strong>
            <div style={{ fontSize: 11, color: 'var(--text-tertiary)' }}>{m.passType === 'SESSION' ? `잔여 ${m.remaining}/${m.total}회` : '월정액'} · 최근 {D.formatRelative ? D.formatRelative(m.lastVisit) : m.lastVisit}</div>
          </div>
          {m.risk && <UI.Badge tone={m.risk === 'overdue' ? 'danger' : m.risk === 'low-pass' ? 'warning' : 'neutral'}>{riskLabel(m.risk)}</UI.Badge>}
        </div>
      ))}
      {members.length === 0 && <UI.Empty title="담당 회원이 없습니다"/>}
    </div>
  );
}

function TeacherKpi({ teacherId }) {
  const s = D.getStats(teacherId);
  return (
    <div>
      <div className="t-kpi-block">
        <div className="t-micro" style={{ marginBottom: 6 }}>매출 기여 (6개월, M원)</div>
        <MiniBars values={s.trend6m.map(v => v / 1000000)} color="var(--accent-default)"/>
      </div>
      <div className="t-kpi-block">
        <div className="t-micro" style={{ marginBottom: 6 }}>재등록률 (6개월)</div>
        <MiniLine values={s.renewalTrend} color="#B8761F" suffix="%" baseLine={80}/>
      </div>
      <div className="t-kpi-block">
        <div className="t-micro" style={{ marginBottom: 6 }}>출석률 (6개월)</div>
        <MiniLine values={s.attendanceTrend} color="#6B5B95" suffix="%" baseLine={85}/>
      </div>
      <div style={{ padding: 12, background: 'var(--bg-canvas)', borderRadius: 8, fontSize: 12, color: 'var(--text-secondary)', lineHeight: 1.6 }}>
        <strong style={{ color: 'var(--text-primary)' }}>강사 본인에게만 노출.</strong> 학원장 뷰에서는 집계만, 강사 ↔ 강사 비교는 정치 우려로 강사 모바일에 노출하지 않습니다.
      </div>
    </div>
  );
}

function TeacherSchedule({ teacherId }) {
  const dows = D.dayNames;
  const myRes = D.reservations.filter(r => r.teacherId === teacherId);
  const byDay = {};
  myRes.forEach(r => { (byDay[r.dow] = byDay[r.dow] || []).push(r); });
  return (
    <div>
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(7, 1fr)', gap: 8, marginBottom: 14 }}>
        {[1,2,3,4,5,6,7].map(d => (
          <div key={d} className="t-sch-day">
            <div className="t-micro">{dows[d - 1]}</div>
            <div style={{ fontSize: 18, fontWeight: 700, marginTop: 2 }}>{(byDay[d] || []).length}</div>
            <div style={{ fontSize: 10, color: 'var(--text-tertiary)' }}>{(byDay[d] || []).length === 0 ? '—' : '수업'}</div>
          </div>
        ))}
      </div>
      <div className="t-micro" style={{ marginBottom: 8 }}>이번 주 (Mon-Sat)</div>
      {[1,2,3,4,5,6].map(d => {
        const list = (byDay[d] || []).sort((a,b) => a.start - b.start);
        if (list.length === 0) return null;
        return (
          <div key={d} style={{ marginBottom: 10 }}>
            <div style={{ fontSize: 12, fontWeight: 600, marginBottom: 4 }}>{dows[d - 1]}요일</div>
            {list.map(r => {
              const m = r.memberId ? D.getMember(r.memberId) : null;
              return (
                <div key={r.id} className="t-sch-line">
                  <span className="t-mono" style={{ fontSize: 12, color: 'var(--text-tertiary)', minWidth: 48 }}>
                    {String(Math.floor(r.start/60)).padStart(2,'0')}:{String(r.start%60).padStart(2,'0')}
                  </span>
                  <span style={{ flex: 1 }}>
                    {r.type === 'lesson' ? <span>{m?.name || '회원'}</span>
                      : r.type === 'consultation' ? <span><span className="kw-chip">상담</span> {r.prospect}</span>
                      : <span><span className="kw-chip">대여</span></span>}
                  </span>
                  <span style={{ fontSize: 10, color: 'var(--text-tertiary)' }}>{D.getRoom(r.roomId)?.name}</span>
                </div>
              );
            })}
          </div>
        );
      })}
    </div>
  );
}

function TeacherPayoutHistory({ teacherId, onOpen }) {
  const pay = D.getPayout(teacherId);
  return (
    <div>
      <div className="t-payout-current" onClick={onOpen}>
        <div>
          <div className="t-micro">이번 달 (2025-11) · {PAY_STATUS[pay.status].label}</div>
          <strong style={{ fontSize: 20, marginTop: 2, display: 'block' }}>{D.formatMoney(pay.net)}</strong>
          <div style={{ fontSize: 11, color: 'var(--text-tertiary)' }}>{pay.summary}</div>
        </div>
        <UI.Button size="sm">정산서 보기 →</UI.Button>
      </div>
      <div className="t-micro" style={{ margin: '12px 0 6px' }}>이전 정산</div>
      {pay.history.map(h => (
        <div key={h.period} className="t-payout-row">
          <span style={{ flex: 1 }}>{h.period}</span>
          <span className="t-mono">{D.formatMoney(h.net)}</span>
          <span style={{ fontSize: 11, color: 'var(--text-tertiary)' }}>지급 {h.paidAt}</span>
        </div>
      ))}
    </div>
  );
}

// ────────────────────────────────────────────────────────────
// Payout modal — 월별 정산서 상세
// ────────────────────────────────────────────────────────────
function PayoutModal({ open, onClose, teacherId }) {
  if (!teacherId) return null;
  const t = D.getTeacher(teacherId);
  const p = D.getProfile(teacherId);
  const pay = D.getPayout(teacherId);
  const [period, setPeriod] = useState(pay.period);

  return (
    <UI.Modal open={open} onClose={onClose} title={`${t?.name} · 월별 정산서`} width={720}
      footer={
        <React.Fragment>
          <UI.Button variant="ghost" icon={Icon.Send} onClick={() => window.toast && window.toast(`${t.name}님께 정산서 알림톡 발송`, { tone: 'success' })}>강사에게 발송</UI.Button>
          <UI.Button variant="ghost" icon={Icon.Receipt} onClick={() => window.toast && window.toast('PDF 다운로드 완료', { tone: 'success' })}>PDF</UI.Button>
          <div style={{ flex: 1 }}/>
          <UI.Button variant="ghost" onClick={onClose}>닫기</UI.Button>
          {pay.status === 'pending' && <UI.Button variant="primary" icon={Icon.Check} onClick={() => { window.toast && window.toast(`${t.name}님 ${pay.period} 정산 지급 완료 처리됨`, { tone: 'success' }); onClose(); }}>지급 완료</UI.Button>}
          {pay.status === 'disputed' && <UI.Button variant="primary" icon={Icon.Check} onClick={() => { window.toast && window.toast('이의 사항 확인 후 재산정', { tone: 'success' }); onClose(); }}>재산정</UI.Button>}
        </React.Fragment>
      }>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: 14, gap: 16 }}>
        <div>
          <div className="t-micro">정산 기간</div>
          <strong style={{ fontSize: 16 }}>{period}</strong>
        </div>
        <div style={{ textAlign: 'right' }}>
          <div className="t-micro">지급 예정일</div>
          <strong style={{ fontSize: 16 }}>{pay.expectedPayout}</strong>
        </div>
      </div>

      {pay.disputeNote && (
        <div style={{ background: 'var(--status-danger-bg)', border: '1px solid var(--status-danger)', borderRadius: 8, padding: 12, marginBottom: 14, fontSize: 13 }}>
          <strong style={{ color: 'var(--status-danger)' }}>⚠ 이의 제기.</strong> <span>{pay.disputeNote}</span>
        </div>
      )}

      <div className="grid-3" style={{ marginBottom: 14, gap: 8 }}>
        <div className="t-detail-cell">
          <div className="t-micro">정산 룰</div>
          <strong>{PAY_MODELS[p.payModel].label}</strong>
          <div style={{ fontSize: 11, color: 'var(--text-tertiary)' }}>
            {p.payModel === 'session' ? `회당 ${D.formatMoney(p.payRate)}` :
             p.payModel === 'share' ? `매출 ${p.paySharePct}% 셰어` :
             p.payModel === 'hourly' ? `시간당 ${D.formatMoney(p.hourlyRate)}` : '기본급 + 성과'}
          </div>
        </div>
        <div className="t-detail-cell">
          <div className="t-micro">기준 산정</div>
          <strong>{pay.summary}</strong>
          <div style={{ fontSize: 11, color: 'var(--text-tertiary)' }}>출석 완료 기준</div>
        </div>
        <div className="t-detail-cell">
          <div className="t-micro">최종 지급</div>
          <strong style={{ fontSize: 18 }}>{D.formatMoney(pay.net)}</strong>
          <div style={{ fontSize: 11, color: 'var(--text-tertiary)' }}>총액 {D.formatMoney(pay.gross)} − 차감 {D.formatMoney(pay.deductions)}</div>
        </div>
      </div>

      <div className="t-micro" style={{ marginBottom: 6 }}>수업 라인 · {pay.lines.length}건</div>
      <div style={{ border: '1px solid var(--border-default)', borderRadius: 8, overflow: 'hidden' }}>
        <table className="tbl tbl--zebra" style={{ margin: 0 }}>
          <thead>
            <tr><th>날짜</th><th>회원</th><th>상태</th><th>분</th><th>금액</th><th></th></tr>
          </thead>
          <tbody>
            {pay.lines.map((l, i) => {
              const m = D.getMember(l.memberId);
              return (
                <tr key={i}>
                  <td className="t-mono">{l.date.slice(5)}</td>
                  <td><strong>{m?.name}</strong></td>
                  <td>
                    <UI.Badge tone={l.status === 'completed' ? 'success' : l.status === 'absent' ? 'warning' : 'danger'}>
                      {l.status === 'completed' ? '출석' : l.status === 'absent' ? '결석' : '노쇼'}
                    </UI.Badge>
                  </td>
                  <td className="t-mono">{l.minutes || '—'}</td>
                  <td className="t-mono">{l.amount ? D.formatMoney(l.amount) : (l.note || '—')}</td>
                  <td style={{ textAlign: 'right' }}>
                    {l.note && <span style={{ fontSize: 10, color: 'var(--text-tertiary)' }}>{l.note}</span>}
                  </td>
                </tr>
              );
            })}
          </tbody>
        </table>
      </div>
    </UI.Modal>
  );
}

// ────────────────────────────────────────────────────────────
// Pay rule modal — 정산 룰 편집
// ────────────────────────────────────────────────────────────
function PayRuleModal({ open, onClose, teacherId }) {
  if (!teacherId) return null;
  const t = D.getTeacher(teacherId);
  const p = D.getProfile(teacherId);
  const [model, setModel] = useState(p.payModel);
  const [rate,  setRate]  = useState(p.payRate || p.hourlyRate || 0);
  const [share, setShare] = useState(p.paySharePct || 45);
  const [base,  setBase]  = useState(p.baseSalary || 1500000);
  const [bonus, setBonus] = useState(p.perfBonusPct || 5);

  return (
    <UI.Modal open={open} onClose={onClose} title={`${t.name} · 정산 룰`} width={560}
      footer={
        <React.Fragment>
          <UI.Button variant="ghost" onClick={onClose}>취소</UI.Button>
          <UI.Button variant="primary" icon={Icon.Check} onClick={() => { onClose(); window.toast && window.toast(`${t.name} 정산 룰을 ${PAY_MODELS[model].label}로 저장 — 다음 달 정산부터 적용`, { tone: 'success' }); }}>저장</UI.Button>
        </React.Fragment>
      }>
      <div className="t-micro" style={{ marginBottom: 8 }}>정산 모델</div>
      <div className="pay-grid">
        {Object.entries(PAY_MODELS).map(([k, v]) => (
          <button key={k} className={`pay-card ${model === k ? 'is-active' : ''}`} onClick={() => setModel(k)}>
            <strong>{v.label}</strong>
            <span style={{ fontSize: 11, color: 'var(--text-tertiary)' }}>{v.hint}</span>
          </button>
        ))}
      </div>

      <div style={{ marginTop: 16, padding: 16, background: 'var(--bg-canvas)', borderRadius: 8 }}>
        {model === 'session' && (
          <div>
            <div className="t-micro">수업 1회당 금액</div>
            <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginTop: 6 }}>
              <input type="number" value={rate} onChange={e => setRate(+e.target.value)} className="inp t-mono" style={{ width: 160 }}/>
              <span style={{ color: 'var(--text-tertiary)' }}>원</span>
            </div>
            <div style={{ fontSize: 11, color: 'var(--text-tertiary)', marginTop: 6 }}>이번 달 예상 — {D.formatMoney(rate * 25)} (25회 기준)</div>
          </div>
        )}
        {model === 'share' && (
          <div>
            <div className="t-micro">담당 회원 매출 셰어</div>
            <div style={{ display: 'flex', alignItems: 'center', gap: 12, marginTop: 6 }}>
              <input type="range" min="20" max="80" value={share} onChange={e => setShare(+e.target.value)} style={{ flex: 1 }}/>
              <span className="t-mono" style={{ fontSize: 20, fontWeight: 700, minWidth: 60 }}>{share}%</span>
            </div>
            <div style={{ fontSize: 11, color: 'var(--text-tertiary)', marginTop: 6 }}>이번 달 예상 — {D.formatMoney(2240000 * share / 100)} (담당 매출 224만 기준)</div>
          </div>
        )}
        {model === 'hourly' && (
          <div>
            <div className="t-micro">시간당 금액</div>
            <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginTop: 6 }}>
              <input type="number" value={rate} onChange={e => setRate(+e.target.value)} className="inp t-mono" style={{ width: 160 }}/>
              <span style={{ color: 'var(--text-tertiary)' }}>원 / 시간</span>
            </div>
            <div style={{ fontSize: 11, color: 'var(--text-tertiary)', marginTop: 6 }}>50분 수업 × 회당 {D.formatMoney(rate * 50 / 60)}</div>
          </div>
        )}
        {model === 'salary_perf' && (
          <div>
            <div className="t-micro">기본급</div>
            <div style={{ display: 'flex', alignItems: 'center', gap: 8, margin: '6px 0 14px' }}>
              <input type="number" value={base} onChange={e => setBase(+e.target.value)} className="inp t-mono" style={{ width: 160 }}/>
              <span style={{ color: 'var(--text-tertiary)' }}>원 / 월</span>
            </div>
            <div className="t-micro">성과급 — 갱신 1건당</div>
            <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
              <input type="range" min="0" max="15" value={bonus} onChange={e => setBonus(+e.target.value)} style={{ flex: 1 }}/>
              <span className="t-mono" style={{ fontSize: 16, fontWeight: 700, minWidth: 60 }}>{bonus}%</span>
            </div>
          </div>
        )}
      </div>

      <div style={{ background: 'var(--bg-canvas)', padding: 12, borderRadius: 8, fontSize: 11.5, color: 'var(--text-tertiary)', marginTop: 12, lineHeight: 1.6 }}>
        <strong style={{ color: 'var(--text-secondary)' }}>주의.</strong> 룰 변경은 다음 달 정산부터 적용됩니다. 현재 진행 중인 11월 정산에는 영향 없음.
        강사 본인에게는 알림톡으로 변경 사실이 자동 통보됩니다.
      </div>
    </UI.Modal>
  );
}

// ────────────────────────────────────────────────────────────
// Swaps modal
// ────────────────────────────────────────────────────────────
function SwapsModal({ open, onClose, swaps, setSwaps, onOpenMember }) {
  return (
    <UI.Modal open={open} onClose={onClose} title="스케줄 스왑 요청" width={620}>
      {swaps.length === 0 && <UI.Empty title="대기 중인 요청 없음"/>}
      {swaps.map(s => {
        const from = D.getTeacher(s.fromTeacher);
        const to   = D.getTeacher(s.toTeacher);
        const m    = D.getMember(s.memberId);
        return (
          <div key={s.id} className="swap-card">
            <div className="swap-card__head">
              <div className="swap-card__teachers">
                <span className="swap-card__t" style={{ background: from.color }}>{from.initials}</span>
                <Icon.ChevRight size={14}/>
                <span className="swap-card__t" style={{ background: to.color }}>{to.initials}</span>
                <strong style={{ marginLeft: 8 }}>{from.name} → {to.name}</strong>
              </div>
              <UI.Badge tone={s.status === 'pending' ? 'warning' : s.status === 'approved' ? 'success' : 'neutral'}>
                {s.status === 'pending' ? '대기' : s.status === 'approved' ? '승인' : '거절'}
              </UI.Badge>
            </div>
            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8, marginTop: 8 }}>
              <div className="t-detail-cell">
                <div className="t-micro">대상 수업</div>
                <strong>{s.date} {s.time}</strong>
                <button className="t-link" onClick={() => onOpenMember && onOpenMember(s.memberId)}>{m?.name} · 회원 카드 →</button>
              </div>
              <div className="t-detail-cell">
                <div className="t-micro">사유</div>
                <strong style={{ fontSize: 13 }}>{s.reason}</strong>
                <div style={{ fontSize: 11, color: 'var(--text-tertiary)' }}>요청 {D.fmt.relative(s.requestedAt)}</div>
              </div>
            </div>
            {s.status === 'pending' && (
              <div style={{ display: 'flex', gap: 6, marginTop: 12 }}>
                <UI.Button size="sm" variant="ghost" onClick={() => { setSwaps(prev => prev.map(x => x.id === s.id ? { ...x, status: 'rejected', decidedAt: '지금' } : x)); window.toast && window.toast('스왑 요청 거절', { tone: 'warning' }); }}>거절</UI.Button>
                <div style={{ flex: 1 }}/>
                <UI.Button size="sm" variant="ghost" onClick={() => window.toast && window.toast(`${m.name}님께 상황 설명 알림톡 발송`, { tone: 'success' })}>회원에게 안내</UI.Button>
                <UI.Button size="sm" variant="primary" icon={Icon.Check} onClick={() => { setSwaps(prev => prev.map(x => x.id === s.id ? { ...x, status: 'approved', decidedAt: '지금' } : x)); window.toast && window.toast(`스왑 승인 — ${m.name}님께 강사 변경 알림톡 발송`, { tone: 'success' }); }}>승인 + 회원 알림</UI.Button>
              </div>
            )}
          </div>
        );
      })}
    </UI.Modal>
  );
}

// ────────────────────────────────────────────────────────────
// Time-off / 결강 modal
// ────────────────────────────────────────────────────────────
function TimeOffModal({ open, onClose, items, setItems }) {
  const [composing, setComposing] = useState(false);
  const [draft, setDraft] = useState({ teacherId: 't1', kind: 'vacation', from: '', to: '', reason: '' });

  const save = () => {
    if (!draft.from || !draft.reason) { window.toast && window.toast('기간과 사유를 입력해 주세요', { tone: 'warning' }); return; }
    const newItem = { id: 'to' + Date.now(), ...draft, status: 'pending', affected: 0 };
    setItems(prev => [newItem, ...prev]);
    setComposing(false);
    setDraft({ teacherId: 't1', kind: 'vacation', from: '', to: '', reason: '' });
    window.toast && window.toast('휴가/결강 등록 — 영향받는 수업이 학원장 검토 대기열에 추가됨', { tone: 'success' });
  };

  return (
    <UI.Modal open={open} onClose={onClose} title="휴가 · 결강 관리" width={680}
      footer={composing
        ? <React.Fragment>
            <UI.Button variant="ghost" onClick={() => setComposing(false)}>취소</UI.Button>
            <UI.Button variant="primary" icon={Icon.Check} onClick={save}>등록</UI.Button>
          </React.Fragment>
        : <UI.Button variant="primary" icon={Icon.Plus} onClick={() => setComposing(true)}>새 등록</UI.Button>
      }>
      {composing && (
        <div style={{ background: 'var(--bg-canvas)', padding: 14, borderRadius: 8, marginBottom: 14 }}>
          <div className="t-micro" style={{ marginBottom: 8 }}>강사 / 유형</div>
          <div style={{ display: 'flex', gap: 8, marginBottom: 12 }}>
            <select className="inp" value={draft.teacherId} onChange={e => setDraft({ ...draft, teacherId: e.target.value })}>
              {D.teachers.map(t => <option key={t.id} value={t.id}>{t.name}</option>)}
            </select>
            <select className="inp" value={draft.kind} onChange={e => setDraft({ ...draft, kind: e.target.value })}>
              <option value="vacation">휴가</option>
              <option value="sick">병가</option>
              <option value="training">교육 / 워크샵</option>
              <option value="personal">개인 사정</option>
            </select>
          </div>
          <div className="t-micro" style={{ marginBottom: 8 }}>기간</div>
          <div style={{ display: 'flex', gap: 8, marginBottom: 12 }}>
            <input type="date" className="inp" value={draft.from} onChange={e => setDraft({ ...draft, from: e.target.value, to: draft.to || e.target.value })}/>
            <span style={{ alignSelf: 'center', color: 'var(--text-tertiary)' }}>—</span>
            <input type="date" className="inp" value={draft.to} onChange={e => setDraft({ ...draft, to: e.target.value })}/>
          </div>
          <div className="t-micro" style={{ marginBottom: 8 }}>사유</div>
          <textarea className="inp" style={{ width: '100%', minHeight: 60 }} placeholder="간단한 사유" value={draft.reason} onChange={e => setDraft({ ...draft, reason: e.target.value })}/>
        </div>
      )}

      {items.length === 0 && !composing && <UI.Empty title="등록된 휴가가 없습니다"/>}

      {items.map(it => {
        const t = D.getTeacher(it.teacherId);
        return (
          <div key={it.id} className="to-card">
            <div className="to-card__head">
              <UI.Avatar name={t.name}/>
              <div style={{ flex: 1 }}>
                <strong>{t.name}</strong>
                <div style={{ fontSize: 11, color: 'var(--text-tertiary)' }}>
                  {it.kind === 'vacation' ? '휴가' : it.kind === 'sick' ? '병가' : it.kind === 'training' ? '교육' : '개인'} · {it.from} {it.to !== it.from ? `→ ${it.to}` : ''}
                </div>
              </div>
              <UI.Badge tone={it.status === 'approved' ? 'success' : it.status === 'pending' ? 'warning' : 'neutral'}>
                {it.status === 'approved' ? '승인' : it.status === 'pending' ? '대기' : '거절'}
              </UI.Badge>
            </div>
            <div style={{ fontSize: 12, color: 'var(--text-secondary)', margin: '6px 0' }}>{it.reason}</div>
            <div style={{ display: 'flex', alignItems: 'center', gap: 6, fontSize: 11, color: 'var(--text-tertiary)' }}>
              <Icon.Warn size={12}/> 영향 받는 수업 {it.affected || 0}건 — 강사 폰에서 회원 알림 자동 발송
              <div style={{ flex: 1 }}/>
              {it.status === 'pending' && (
                <React.Fragment>
                  <UI.Button size="sm" variant="ghost" onClick={() => { setItems(prev => prev.map(x => x.id === it.id ? { ...x, status: 'rejected' } : x)); window.toast && window.toast('휴가 거절', { tone: 'warning' }); }}>거절</UI.Button>
                  <UI.Button size="sm" variant="primary" onClick={() => { setItems(prev => prev.map(x => x.id === it.id ? { ...x, status: 'approved' } : x)); window.toast && window.toast(`승인 — 회원 ${it.affected || 0}명에게 변경 알림톡 발송 예약`, { tone: 'success' }); }}>승인 + 회원 알림</UI.Button>
                </React.Fragment>
              )}
            </div>
          </div>
        );
      })}
    </UI.Modal>
  );
}

// ────────────────────────────────────────────────────────────
// Invite teacher modal
// ────────────────────────────────────────────────────────────
function InviteModal({ open, onClose }) {
  const [name, setName]   = useState('');
  const [email, setEmail] = useState('');
  const [phone, setPhone] = useState('');
  const [model, setModel] = useState('session');

  return (
    <UI.Modal open={open} onClose={onClose} title="강사 초대" width={500}
      footer={
        <React.Fragment>
          <UI.Button variant="ghost" onClick={onClose}>취소</UI.Button>
          <UI.Button variant="primary" icon={Icon.Send} onClick={() => {
            if (!name || !email) { window.toast && window.toast('이름과 이메일을 입력해 주세요', { tone: 'warning' }); return; }
            onClose();
            window.toast && window.toast(`${name}님께 강사 초대 메일 발송 — 가입 후 정산 룰 자동 적용`, { tone: 'success' });
            setName(''); setEmail(''); setPhone('');
          }}>초대 메일 발송</UI.Button>
        </React.Fragment>
      }>
      <div className="t-micro" style={{ marginBottom: 6 }}>이름</div>
      <input className="inp" style={{ width: '100%', marginBottom: 12 }} value={name} onChange={e => setName(e.target.value)} placeholder="예: 김보컬"/>
      <div className="t-micro" style={{ marginBottom: 6 }}>이메일</div>
      <input className="inp" style={{ width: '100%', marginBottom: 12 }} value={email} onChange={e => setEmail(e.target.value)} placeholder="teacher@example.com"/>
      <div className="t-micro" style={{ marginBottom: 6 }}>전화번호</div>
      <input className="inp" style={{ width: '100%', marginBottom: 12 }} value={phone} onChange={e => setPhone(e.target.value)} placeholder="010-0000-0000"/>
      <div className="t-micro" style={{ marginBottom: 6 }}>기본 정산 모델</div>
      <select className="inp" style={{ width: '100%' }} value={model} onChange={e => setModel(e.target.value)}>
        {Object.entries(PAY_MODELS).map(([k, v]) => <option key={k} value={k}>{v.label} — {v.hint}</option>)}
      </select>
      <div style={{ background: 'var(--bg-canvas)', padding: 12, borderRadius: 8, fontSize: 12, color: 'var(--text-tertiary)', marginTop: 12, lineHeight: 1.6 }}>
        강사는 가입 즉시 자기 모바일에서 일정·정산서·일지를 열 수 있습니다. 회원 데이터는 학원장이 담당 배정 시점에 단계적으로 노출됩니다.
      </div>
    </UI.Modal>
  );
}

// ────────────────────────────────────────────────────────────
// Charts
// ────────────────────────────────────────────────────────────
function TeacherTrendChart() {
  const W = 720, H = 240, Pad = 36;
  const teachers = D.teachers;
  const months = 6;
  const all = teachers.flatMap(t => D.teacherStats[t.id].trend6m);
  const yMax = Math.max(...all) * 1.1;
  const step = (W - Pad * 2) / (months - 1);

  return (
    <div style={{ overflowX: 'auto' }}>
      <svg width={W} height={H + 24} style={{ display: 'block' }}>
        {/* grid */}
        {[0, 0.25, 0.5, 0.75, 1].map((g, i) => {
          const y = Pad + (H - Pad * 2) * (1 - g);
          return (
            <g key={i}>
              <line x1={Pad} x2={W - 8} y1={y} y2={y} stroke="var(--border-default)" strokeDasharray="2 4"/>
              <text x={4} y={y + 4} fontSize="9" fill="var(--text-tertiary)" fontFamily="JetBrains Mono">{Math.round(yMax / 1000000 * g * 10) / 10}M</text>
            </g>
          );
        })}
        {/* lines */}
        {teachers.map(t => {
          const trend = D.teacherStats[t.id].trend6m;
          const points = trend.map((v, i) => {
            const x = Pad + i * step;
            const y = Pad + (H - Pad * 2) * (1 - v / yMax);
            return [x, y];
          });
          const path = points.map((p, i) => (i === 0 ? `M ${p[0]} ${p[1]}` : `L ${p[0]} ${p[1]}`)).join(' ');
          return (
            <g key={t.id}>
              <path d={path} stroke={t.color} fill="none" strokeWidth="2"/>
              {points.map((p, i) => (
                <circle key={i} cx={p[0]} cy={p[1]} r="3" fill={t.color}/>
              ))}
              <text x={points.at(-1)[0] + 6} y={points.at(-1)[1] + 4} fontSize="11" fontWeight="600" fill={t.color}>{t.name}</text>
            </g>
          );
        })}
        {/* x labels */}
        {Array.from({ length: months }, (_, i) => {
          const x = Pad + i * step;
          const monthLabel = D.monthlyRevenue.slice(-months)[i]?.month.slice(5);
          return <text key={i} x={x} y={H + 4} fontSize="10" fill="var(--text-tertiary)" textAnchor="middle" fontFamily="JetBrains Mono">{monthLabel}</text>;
        })}
      </svg>
    </div>
  );
}

function MiniBars({ values, color }) {
  const max = Math.max(...values) * 1.15;
  const W = 480, H = 60;
  const step = W / values.length;
  const barW = step * 0.65;
  return (
    <svg viewBox={`0 0 ${W} ${H}`} style={{ width: '100%', height: 60 }}>
      {values.map((v, i) => {
        const x = i * step + (step - barW) / 2;
        const h = (v / max) * H;
        return (
          <g key={i}>
            <rect x={x} y={H - h} width={barW} height={h} fill={color} opacity={i === values.length - 1 ? 1 : 0.5} rx={2}/>
            <text x={x + barW / 2} y={H - h - 2} fontSize="9" textAnchor="middle" fontFamily="JetBrains Mono" fill={i === values.length - 1 ? color : 'var(--text-tertiary)'}>{v.toFixed(1)}</text>
          </g>
        );
      })}
    </svg>
  );
}

function MiniLine({ values, color, suffix = '', baseLine }) {
  const max = Math.max(...values, baseLine || 0) * 1.1;
  const min = Math.min(...values, baseLine || 100) * 0.85;
  const span = max - min;
  const W = 480, H = 60, Pad = 12;
  const step = (W - Pad * 2) / (values.length - 1);
  const pts = values.map((v, i) => {
    const x = Pad + i * step;
    const y = Pad + (H - Pad * 2) * (1 - (v - min) / span);
    return [x, y];
  });
  return (
    <svg viewBox={`0 0 ${W} ${H}`} style={{ width: '100%', height: 60 }}>
      {baseLine != null && (
        (() => {
          const y = Pad + (H - Pad * 2) * (1 - (baseLine - min) / span);
          return <line x1={Pad} x2={W - Pad} y1={y} y2={y} stroke="var(--border-strong)" strokeDasharray="3 3"/>;
        })()
      )}
      <path d={pts.map((p, i) => (i === 0 ? `M ${p[0]} ${p[1]}` : `L ${p[0]} ${p[1]}`)).join(' ')} stroke={color} fill="none" strokeWidth="2"/>
      {pts.map((p, i) => <circle key={i} cx={p[0]} cy={p[1]} r="3" fill={color}/>)}
      {pts.map((p, i) => <text key={i} x={p[0]} y={p[1] - 8} fontSize="9" textAnchor="middle" fill="var(--text-tertiary)" fontFamily="JetBrains Mono">{values[i]}{suffix}</text>)}
    </svg>
  );
}

// helpers
function pct(cur, prev) {
  const d = ((cur - prev) / prev * 100).toFixed(1);
  return (d >= 0 ? '▲ +' : '▼ ') + d + '%';
}
function riskLabel(r) {
  return r === 'overdue' ? '미납' : r === 'due-soon' ? '결제 임박' : r === 'low-pass' ? '잔여 부족' : r;
}

window.InstructorsPage = { InstructorsPage };
})();
