/* global React, Icon, UI, LessonHighData */
(function () {
const { useState, useMemo } = React;
const D = LessonHighData;

// ============================================================
// Page header
// ============================================================
function PageHeader({ title, sub, actions }) {
  return (
    <header className="page-header">
      <div>
        <h1 className="page-title">{title}</h1>
        {sub && <div className="page-sub">{sub}</div>}
      </div>
      {actions && <div className="page-actions">{actions}</div>}
    </header>
  );
}

// ============================================================
// 1. Dashboard — "오늘 한눈에"
// ============================================================
function Dashboard({ onNav, spaceLabel, onOpenNote }) {
  const today = D.reservations.filter(r => r.dow === 3).sort((a,b) => a.start - b.start);
  const overdue = D.payments.filter(p => p.status === 'OVERDUE');
  const dueSoon = D.payments.filter(p => p.status === 'DUE_SOON');
  const lowPass = D.members.filter(m => m.risk === 'low-pass');
  const dormantRisk = [
    { name: '윤서아', reason: '6주째 예약 없음 · 휴원 처리됨' },
    { name: '한지호', reason: '최근 2주 결석 2회' },
  ];
  const PAGE = 5;
  const [scheduleAll, setScheduleAll] = useState(false);
  const [paymentsAll, setPaymentsAll] = useState(false);
  const [consultsAll, setConsultsAll] = useState(false);

  const allPayments = [...overdue, ...dueSoon];
  const visiblePayments = paymentsAll ? allPayments : allPayments.slice(0, PAGE);
  const visibleToday = scheduleAll ? today : today.slice(0, PAGE);
  const allConsults = D.consultations;
  const visibleConsults = consultsAll ? allConsults : allConsults.slice(0, PAGE);

  return (
    <React.Fragment>
      <PageHeader
        title={<span>안녕하세요, <span style={{ color: 'var(--accent-default)' }}>송유이</span>님</span>}
        sub={`${D.formatDate(D.TODAY)} · 오늘 수업 ${today.filter(r=>r.type==='lesson').length}건, 상담 ${today.filter(r=>r.type==='consultation').length}건이 예정되어 있습니다.`}
        actions={
          <React.Fragment>
            <UI.Button icon={Icon.Mail} variant="ghost" onClick={() => window.toast && window.toast('알림톡 로그 — 알림함을 여세요', { tone: 'success' })}>알림톡 로그</UI.Button>
            <UI.Button icon={Icon.Plus} variant="primary" onClick={() => onNav('members')}>회원 등록</UI.Button>
          </React.Fragment>
        }
      />

      {/* KPI row */}
      <div className="grid-4" style={{ marginBottom: 24 }}>
        <KpiCard label="이번 달 매출" value="8,420,000원" delta="+12.4%" deltaTone="success" sparkline={[3,4,3,5,4,6,5,7,8,7,9,8.4]} />
        <KpiCard label="활동 회원" value="11명" delta="+2" deltaTone="success" sub="이번 달 신규 가입" indicator={<KpiAvatars tones={['lesson','consultation','rental','lesson','consultation']}/>} />
        <KpiCard label="결제 임박 / 누락" value={<span><span>{dueSoon.length}</span><span style={{ color: 'var(--text-tertiary)', fontWeight: 500 }}> / </span><span style={{ color: 'var(--status-danger)' }}>{overdue.length}</span></span>} sub="이번 주 안에 알림 발송" indicator={<KpiDots filled={overdue.length} total={7} tone="var(--status-danger)"/>} />
        <KpiCard label="휴면 위험" value={`${dormantRisk.length}명`} delta="개별 연락 권장" deltaTone="warning" indicator={<KpiDots filled={dormantRisk.length} total={6} tone="var(--status-warning)"/>} />
      </div>

      {/* 시간 변경 요청 — 슬림 배너 (대기 시에만) */}
      {D.changeRequests.length > 0 && (
        <div className="db-change-banner" style={{ marginBottom: 16 }}>
          {D.changeRequests.map(cr => {
            const m = D.getMember(cr.memberId);
            return (
              <div key={cr.id} className="db-change-banner__row">
                <span className="db-change-banner__icon"><Icon.Refresh size={14}/></span>
                <UI.Avatar name={m.name} tone={m.tone} size="sm"/>
                <span className="t-body-strong" style={{ whiteSpace: 'nowrap' }}>{m.name}</span>
                <span className="t-caption" style={{ whiteSpace: 'nowrap' }}>시간 변경 요청</span>
                <span className="t-mono num db-change-banner__time">수요일 19:00</span>
                <Icon.ChevRight size={14}/>
                <span className="t-mono num db-change-banner__time" style={{ color: 'var(--accent-default)' }}>금요일 20:00</span>
                <span className="t-caption" style={{ marginLeft: 'auto', whiteSpace: 'nowrap' }}>{D.fmt.relative(cr.requestedAt)} · {cr.reason}</span>
                <UI.Button size="sm" variant="ghost" onClick={() => window.toast && window.toast(`${m.name}님 시간 변경 요청 거절 — 사유 메시지 발송`, { tone: 'warning' })}>거절</UI.Button>
                <UI.Button size="sm" variant="primary" icon={Icon.Check} onClick={() => window.toast && window.toast(`${m.name}님 시간 변경 승인 — 알림톡 발송 완료`, { tone: 'success' })}>승인</UI.Button>
              </div>
            );
          })}
        </div>
      )}

      <div className="dashboard-bento">
        <div className="dashboard-bento__col">
          {/* 오늘의 일정 */}
          <UI.Card className="db-cell db-cell--schedule">
            <UI.CardHeader
              title="오늘의 일정"
              actions={
                <React.Fragment>
                  <span className="t-caption">{D.formatDate(D.TODAY, { short: true })}</span>
                  <UI.Button size="sm" variant="ghost" icon={Icon.Calendar} onClick={() => onNav('schedule')}>전체 일정</UI.Button>
                </React.Fragment>
              }
            />
            <UI.CardBody flush>
              <ul className="today-list" style={{ listStyle: 'none', margin: 0, padding: '4px 0' }}>
                {visibleToday.map((r) => (
                  <TodayRow key={r.id} r={r} />
                ))}
              </ul>
              {today.length > PAGE && (
                <button className="db-more" onClick={() => setScheduleAll(v => !v)}>
                  {scheduleAll ? '접기' : `+${today.length - PAGE}개 더 보기`}
                </button>
              )}
            </UI.CardBody>
            <UI.CardFooter>
              <Icon.Clock size={14}/>
              <span>마지막 수업 종료 21:00 · 마감 직전 알림은 사용 안 함</span>
            </UI.CardFooter>
          </UI.Card>

          {/* F5 · 이번 주 챙겨야 할 회원 */}
          <div className="db-cell db-cell--risk">
            {window.RetentionUI && <window.RetentionUI.RiskWidget onOpenMember={(id) => { onNav && onNav({ view: 'members', memberId: id }); }}/>}
          </div>
        </div>

        <div className="dashboard-bento__col">
          {/* 결제 임박/누락 */}
          <UI.Card className="db-cell db-cell--payments">
            <UI.CardHeader title="결제 임박 / 누락" actions={<UI.Button size="sm" variant="ghost" onClick={() => onNav('payments')}>모두 보기</UI.Button>} />
            <UI.CardBody flush>
              <ul style={{ listStyle: 'none', margin: 0, padding: 0 }}>
                {visiblePayments.map(p => {
                  const m = D.getMember(p.memberId);
                  return (
                    <li key={p.id} style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '12px 20px', borderBottom: '1px solid var(--border-default)' }}>
                      <UI.Avatar name={m.name} tone={m.tone} size="md"/>
                      <div className="col" style={{ gap: 0, flex: 1 }}>
                        <div className="row" style={{ gap: 8 }}>
                          <span className="t-body-strong">{m.name}</span>
                          {p.status === 'OVERDUE'
                            ? <UI.Badge tone="danger" dot>누락 D+{p.daysOverdue}</UI.Badge>
                            : <UI.Badge tone="warning" dot>D-{p.daysUntil}</UI.Badge>}
                        </div>
                        <div className="t-caption">{D.formatDate(p.dueDate, { short: true })} · {p.method}</div>
                      </div>
                      <div className="num t-mono" style={{ fontSize: 13, fontWeight: 600 }}>{D.formatMoney(p.amount)}</div>
                      <UI.Button size="sm" onClick={(e) => { e.stopPropagation(); window.toast && window.toast(`${m.name}님 ${D.formatMoney(p.amount)} 결제 완료 처리 — 영수증 알림톡 발송`, { tone: 'success' }); }}>결제 완료</UI.Button>
                    </li>
                  );
                })}
              </ul>
              {allPayments.length > PAGE && (
                <button className="db-more" onClick={() => setPaymentsAll(v => !v)}>
                  {paymentsAll ? '접기' : `+${allPayments.length - PAGE}개 더 보기`}
                </button>
              )}
            </UI.CardBody>
          </UI.Card>

          {/* 최근 상담 */}
          <UI.Card className="db-cell db-cell--consults">
            <UI.CardHeader title="최근 상담" actions={<UI.Button size="sm" variant="ghost" onClick={() => onNav('consults')}>전체</UI.Button>} />
            <UI.CardBody flush>
              <ul style={{ listStyle: 'none', margin: 0, padding: 0 }}>
                {visibleConsults.map(c => (
                  <li key={c.id} className="row" style={{ padding: '12px 20px', borderBottom: '1px solid var(--border-default)' }}>
                    <UI.Avatar name={c.prospect} tone={c.joined ? 'tone-a' : 'tone-b'} size="md"/>
                    <div className="col" style={{ gap: 0, flex: 1 }}>
                      <div className="row" style={{ gap: 8 }}>
                        <span className="t-body-strong">{c.prospect}</span>
                        {c.joined ? <UI.Badge tone="success" dot>가입 완료</UI.Badge> : <UI.Badge tone="accent">대기 중</UI.Badge>}
                      </div>
                      <span className="t-caption">{c.goal}</span>
                      <span className="t-caption">{D.fmt.shortDate(c.conductedAt)} · {D.getTeacher(c.teacherId).name} 선생님 · {c.source}</span>
                    </div>
                    {!c.joined && <UI.Button size="sm" onClick={() => onNav('consults')}>회원 등록</UI.Button>}
                  </li>
                ))}
              </ul>
              {allConsults.length > PAGE && (
                <button className="db-more" onClick={() => setConsultsAll(v => !v)}>
                  {consultsAll ? '접기' : `+${allConsults.length - PAGE}개 더 보기`}
                </button>
              )}
            </UI.CardBody>
          </UI.Card>
        </div>
      </div>
    </React.Fragment>
  );
}

function KpiCard({ label, value, sub, delta, deltaTone, sparkline, indicator }) {
  const tone = deltaTone === 'success' ? 'var(--status-success)' : deltaTone === 'warning' ? 'var(--status-warning)' : 'var(--status-danger)';
  return (
    <UI.Card>
      <UI.CardBody>
        <div className="row" style={{ justifyContent: 'space-between', alignItems: 'flex-start', minHeight: 16 }}>
          <div className="t-micro">{label}</div>
          {indicator}
        </div>
        <div className="row" style={{ justifyContent: 'space-between', alignItems: 'flex-end', marginTop: 6, gap: 12 }}>
          <div style={{ fontSize: 26, lineHeight: 1.15, fontWeight: 700, letterSpacing: '-0.022em', whiteSpace: 'nowrap' }} className="num">{value}</div>
          {sparkline && (
            <div style={{ color: 'var(--accent-default)', flexShrink: 0, display: 'flex', alignItems: 'flex-end' }}>
              <UI.Sparkline values={sparkline} width={72} height={28} stroke="currentColor" />
            </div>
          )}
        </div>
        <div className="row" style={{ marginTop: 4, gap: 6 }}>
          {delta && <span style={{ color: tone, fontSize: 12, fontWeight: 600, whiteSpace: 'nowrap' }}>{delta}</span>}
          {sub && <span className="t-caption" style={{ fontSize: 12, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{sub}</span>}
        </div>
      </UI.CardBody>
    </UI.Card>
  );
}

// Small visual indicators for KPI cards — keep the row visually unified
function KpiDots({ filled, total, tone = 'var(--accent-default)' }) {
  return (
    <div style={{ display: 'flex', gap: 3, alignItems: 'center' }}>
      {Array.from({ length: total }).map((_, i) => (
        <span key={i} style={{
          width: 6, height: 6, borderRadius: 99,
          background: i < filled ? tone : 'var(--bg-inset)',
        }}/>
      ))}
    </div>
  );
}
function KpiAvatars({ tones }) {
  return (
    <div style={{ display: 'flex' }}>
      {tones.slice(0, 5).map((t, i) => (
        <div key={i} style={{
          width: 18, height: 18, borderRadius: 99,
          background: `var(--type-${t}-bg, var(--bg-inset))`,
          marginLeft: i === 0 ? 0 : -6,
          border: '2px solid var(--bg-surface)',
        }}/>
      ))}
    </div>
  );
}

// Compact KPI used at top of the Members page — no chart, just label + value + sub
function MembersKpi({ label, value, sub, tone }) {
  const valColor = tone === 'danger' ? 'var(--status-danger)' : tone === 'accent' ? 'var(--accent-default)' : 'var(--text-primary)';
  return (
    <div style={{
      background: 'var(--bg-surface)',
      border: '1px solid var(--border-default)',
      borderRadius: 'var(--radius-lg)',
      padding: '12px 16px',
      display: 'flex', flexDirection: 'column', gap: 2,
    }}>
      <div className="t-micro" style={{ whiteSpace: 'nowrap' }}>{label}</div>
      <div className="num" style={{ fontSize: 20, fontWeight: 700, letterSpacing: '-0.02em', color: valColor, whiteSpace: 'nowrap' }}>{value}</div>
      {sub && <div className="t-caption" style={{ fontSize: 12, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{sub}</div>}
    </div>
  );
}

function TodayRow({ r }) {
  const isLesson = r.type === 'lesson';
  const isConsult = r.type === 'consultation';
  const m = isLesson ? D.getMember(r.memberId) : null;
  const t = r.teacherId ? D.getTeacher(r.teacherId) : null;
  const room = D.getRoom(r.roomId);
  const isPast = r.end <= (15 * 60); // before 15:00 = "지난 수업"
  const isNow  = r.start <= (15 * 60 + 30) && r.end > (15 * 60 + 30);

  return (
    <li className="today-row" style={{
      opacity: isPast ? 0.5 : 1,
      background: isNow ? 'var(--accent-subtle-bg)' : 'transparent',
    }}>
      <div className="col t-mono num" style={{ gap: 0, width: 60, color: 'var(--text-primary)', flexShrink: 0 }}>
        <span style={{ fontWeight: 600 }}>{D.formatTime(r.start)}</span>
        <span style={{ fontSize: 11, color: 'var(--text-tertiary)' }}>{D.formatTime(r.end)}</span>
      </div>

      <div className={`today-rail today-rail--${r.type} ${isNow ? 'is-now' : ''} ${isPast ? 'is-past' : ''}`}>
        <span className="today-rail__dot"/>
      </div>

      <div className="col" style={{ gap: 0, flex: 1, minWidth: 0 }}>
        <div className="row" style={{ gap: 8 }}>
          <span className="t-body-strong">
            {isLesson ? m.name : isConsult ? `${r.prospect} (신규 상담)` : '공간 대여'}
          </span>
          {isNow && <UI.Badge tone="accent" dot>지금</UI.Badge>}
          {isLesson && m.risk === 'overdue' && <UI.Badge tone="danger" dot>결제 누락</UI.Badge>}
          {isLesson && m.risk === 'low-pass' && <UI.Badge tone="warning">수업권 {m.remaining}회 남음</UI.Badge>}
        </div>
        <div className="t-caption">{room?.name}{t ? ` · ${t.name} 선생님` : ''}</div>
      </div>

      <UI.Button size="sm" variant="ghost" isIcon icon={Icon.More} title="더 보기"/>
    </li>
  );
}

// ============================================================
// 2. Members list
// ============================================================

// 휴원 기간 계산·분류 (단기/중기/장기)
function classifyHold(m) {
  if (!m.holdStart || !m.expectedReturn) return { days: null, type: 'unknown', label: '기간 미정' };
  const start = new Date(m.holdStart);
  const end = new Date(m.expectedReturn);
  const days = Math.max(1, Math.round((end - start) / 86400000));
  if (days <= 30) return { days, type: 'short', label: '단기 휴원', tone: 'success', range: '~30일' };
  if (days <= 90) return { days, type: 'mid',   label: '중기 휴원', tone: 'warning', range: '1~3개월' };
  return                { days, type: 'long',  label: '장기 휴원', tone: 'danger',  range: '3개월+' };
}

// 남은 일수 (클래이언트 시점에서)
function daysFromToday(dateStr) {
  if (!dateStr) return null;
  const d = new Date(dateStr);
  return Math.round((d - D.TODAY) / 86400000);
}

function Members({ onOpenMember, initialMember, onOpenRegister }) {
  const [segment, setSegment] = useState('active'); // 'active' | 'new' | 'hold' | 'out'
  const [riskFilter, setRiskFilter] = useState('all'); // 'all' | 'due' | 'low'
  const [q, setQ] = useState('');
  const [page, setPage] = useState(1);
  const PAGE_SIZE = 12;
  React.useEffect(() => { setPage(1); }, [segment, riskFilter, q]);

  const counts = {
    active: D.members.filter(m => m.status === 'ACTIVE').length,
    new:    D.members.filter(m => m.isNew).length,
    hold:   D.members.filter(m => m.status === 'ON_HOLD').length,
    out:    D.members.filter(m => m.status === 'WITHDRAWN').length,
  };

  const segments = [
    { id: 'active', label: '활동 회원',     sub: '현재 수업에 참여',  count: counts.active, icon: Icon.Users,   tone: 'success' },
    { id: 'new',    label: '신규',         sub: '최근 30일 가입',     count: counts.new,    icon: Icon.Sparkle, tone: 'accent',  hint: '온보딩 관리' },
    { id: 'hold',   label: '휴원',         sub: '일시 연기 수강생',  count: counts.hold,   icon: Icon.Refresh, tone: 'warning', hint: '단/중/장기 구분' },
    { id: 'out',    label: '퇴원',         sub: '재등록 유도 대상', count: counts.out,    icon: Icon.X,       tone: 'neutral', hint: '재등록 제안' },
  ];

  // 활동 회원 세그먼트 안의 위험 도 필터
  const riskFilters = [
    { id: 'all', label: '전체',         count: counts.active },
    { id: 'due', label: '결제 임박·누락', count: D.members.filter(m => m.status === 'ACTIVE' && (m.risk === 'due-soon' || m.risk === 'overdue')).length },
    { id: 'low', label: '수업권 부족',  count: D.members.filter(m => m.status === 'ACTIVE' && m.risk === 'low-pass').length },
  ];

  // 세그먼트별 기본 로우
  let baseRows;
  if (segment === 'active') {
    baseRows = D.members.filter(m => {
      if (m.status !== 'ACTIVE') return false;
      if (riskFilter === 'due' && !(m.risk === 'due-soon' || m.risk === 'overdue')) return false;
      if (riskFilter === 'low' && m.risk !== 'low-pass') return false;
      return true;
    });
  } else if (segment === 'new') {
    baseRows = D.members.filter(m => m.isNew);
  } else if (segment === 'hold') {
    baseRows = D.members.filter(m => m.status === 'ON_HOLD');
  } else {
    baseRows = D.members.filter(m => m.status === 'WITHDRAWN');
  }

  const rows = q ? baseRows.filter(m => m.name.includes(q)) : baseRows;

  return (
    <React.Fragment>
      <PageHeader
        title="회원"
        sub={`단일 지점 · 활동 중 ${D.members.filter(m => m.status === 'ACTIVE').length}명 · 휴원 ${D.members.filter(m => m.status === 'ON_HOLD').length}명 · 퇴원 ${D.members.filter(m => m.status === 'WITHDRAWN').length}명`}
        actions={
          <React.Fragment>
            <UI.Button icon={Icon.Mail} onClick={() => window.toast && window.toast(`활동 회원 ${counts.active}명에게 일괄 알림톡 발송 예약 — 정숙시간 회피`, { tone: 'success' })}>알림톡 일괄 발송</UI.Button>
            <UI.Button icon={Icon.Plus} variant="primary" onClick={onOpenRegister}>회원 등록</UI.Button>
          </React.Fragment>
        }
      />

      {/* Members KPI row — 운영 지표 */}
      <div className="grid-4" style={{ marginBottom: 16, gap: 12 }}>
        <MembersKpi label="이번 달 예상 결제" value={`${D.formatMoney(D.members.reduce((s, m) => s + (m.status === 'ACTIVE' ? (m.amount || 0) : 0), 0))}`} sub={`활동 회원 ${counts.active}명 기준`} />
        <MembersKpi label="평균 잔여 수업권" value={(() => {
          const acts = D.members.filter(m => m.status === 'ACTIVE' && m.passType === 'SESSION');
          const avg = acts.length ? Math.round(acts.reduce((s, m) => s + (m.remaining || 0), 0) / acts.length) : 0;
          return `${avg}회`;
        })()} sub="회당 평균 — 결제 시점 파악용" />
        <MembersKpi label="미수금 합계" value={`${D.formatMoney(D.payments.filter(p => p.status === 'OVERDUE').reduce((s, p) => s + p.amount, 0))}`} sub={`${D.payments.filter(p => p.status === 'OVERDUE').length}건 누락 중`} tone="danger" />
        <MembersKpi label="이번 주 신규 상담" value={`${D.consultations.filter(c => !c.joined).length}건`} sub="가입 전환 대기" tone="accent" />
      </div>

      {/* 1층: 코호트 세그먼트 — 가볍게 세그먼티드 컨트롤 형태로 */}
      <div className="cohort-tabs">
        {segments.map(s => {
          const active = segment === s.id;
          return (
            <button
              key={s.id}
              className={`cohort-tab ${active ? 'is-active' : ''}`}
              onClick={() => { setSegment(s.id); setRiskFilter('all'); }}
            >
              <span className={`cohort-tab__icon cohort-tab__icon--${s.tone}`}>
                <s.icon size={14}/>
              </span>
              <span className="cohort-tab__label">{s.label}</span>
              <span className="cohort-tab__count t-mono num">{s.count}</span>
            </button>
          );
        })}
      </div>
      <div className="t-caption" style={{ marginBottom: 16, marginTop: -4 }}>
        {segment === 'active' && '현재 수업에 참여 중인 회원 — 결제·수업권 위험도로 추가 필터링'}
        {segment === 'new'    && '최근 30일 가입 회원 — 온보딩과 첫 수업 일지 작성 추적'}
        {segment === 'hold'   && '일시 수업을 연기한 회원 — 휴원 기간별로 자동 분류'}
        {segment === 'out'    && '퇴원 이력 — 재등록 타이밍별 권유 액션'}
      </div>

      {/* 2층: 활동 회원 안에서만 위험도 필터 + 검색 */}
      <div className="filter-row row" style={{ marginBottom: 16, gap: 4, flexWrap: 'wrap' }}>
        {segment === 'active' ? (
          <React.Fragment>
            <span className="t-caption" style={{ paddingRight: 4 }}>위험도</span>
            {riskFilters.map(f => (
              <button key={f.id}
                className={`btn is-sm ${riskFilter === f.id ? 'is-secondary' : 'is-ghost'}`}
                onClick={() => setRiskFilter(f.id)}
              >
                {f.label} <span style={{ color: 'var(--text-tertiary)', marginLeft: 4 }}>{f.count}</span>
              </button>
            ))}
          </React.Fragment>
        ) : (
          <span className="t-caption" style={{ paddingRight: 4 }}>
            {segment === 'new'  && `최근 30일 안에 가입한 회원 · 온보딩이 필요합니다`}
            {segment === 'hold' && `일시 수업을 연기한 회원 · 기간별로 자동 분류됩니다`}
            {segment === 'out'  && `퇴원 이력 · 재등록 타이밍별 권유 액션`}
          </span>
        )}
        <div style={{ flex: 1 }}></div>
        <div style={{ position: 'relative' }}>
          <Icon.Search size={14} style={{ position: 'absolute', left: 10, top: '50%', transform: 'translateY(-50%)', color: 'var(--text-tertiary)' }} />
          <input className="input" style={{ width: 240, paddingLeft: 32 }} placeholder="이름 검색" value={q} onChange={(e) => setQ(e.target.value)} />
        </div>
      </div>

      {segment === 'new'  && <CohortNew  rows={rows} onOpenMember={onOpenMember} />}
      {segment === 'hold' && <CohortHold rows={rows} onOpenMember={onOpenMember} />}
      {segment === 'out'  && <CohortOut  rows={rows} onOpenMember={onOpenMember} onOpenRegister={onOpenRegister} />}

      {segment === 'active' && (
      <UI.Card>
        <UI.CardBody flush>
          <table className="tbl">
            <thead>
              <tr>
                <th style={{ width: '28%' }}>회원</th>
                <th>담당 선생님</th>
                <th>수업권</th>
                <th>다음 결제</th>
                <th>마지막 출석</th>
                <th>상태</th>
              </tr>
            </thead>
            <tbody>
              {rows.slice((page - 1) * PAGE_SIZE, page * PAGE_SIZE).map(m => {
                const t = D.getTeacher(m.teacherId);
                const sel = initialMember === m.id;
                return (
                  <tr key={m.id} className="row-clickable" onClick={() => onOpenMember(m.id)}>
                    <td className="cell-identity">
                      <div className="row" style={{ gap: 10 }}>
                        <UI.Avatar name={m.name} tone={m.tone} size="md"/>
                        <div className="col" style={{ gap: 0 }}>
                          <span className="t-body-strong">{m.name}</span>
                          <span className="t-caption t-mono num" style={{ fontSize: 12 }}>{m.phone}</span>
                        </div>
                      </div>
                    </td>
                    <td data-label="담당">
                      <div className="row" style={{ gap: 6 }}>
                        <UI.Avatar name={t.name} tone={t.tone} size="sm" />
                        <span>{t.name}</span>
                      </div>
                    </td>
                    <td data-label="수업권">
                      {m.passType === 'SESSION' ? (
                        <div className="col" style={{ gap: 2 }}>
                          <span className="t-body-strong t-mono num">{m.remaining} <span style={{ color: 'var(--text-tertiary)', fontWeight: 500 }}>/ {m.total}회</span></span>
                          <PassBar value={m.remaining / m.total} risk={m.risk === 'low-pass'} />
                        </div>
                      ) : <span className="t-caption">기간권</span>}
                    </td>
                    <td data-label="다음 결제">
                      {m.nextDue ? (
                        <div className="col" style={{ gap: 0 }}>
                          <span className="t-mono num">{D.formatDate(m.nextDue, { short: true })}</span>
                          <span className="t-caption" style={{ fontSize: 12 }}>{D.formatMoney(m.amount)}</span>
                        </div>
                      ) : <span className="tertiary">—</span>}
                    </td>
                    <td data-label="마지막 출석" className="t-mono num">{D.formatRelative(m.lastVisit)}</td>
                    <td data-label="상태">
                      {m.status === 'ACTIVE' && m.risk === 'overdue' && <UI.Badge tone="danger" dot>결제 누락</UI.Badge>}
                      {m.status === 'ACTIVE' && m.risk === 'due-soon' && <UI.Badge tone="warning" dot>결제 임박</UI.Badge>}
                      {m.status === 'ACTIVE' && m.risk === 'low-pass' && <UI.Badge tone="warning">수업권 부족</UI.Badge>}
                      {m.status === 'ACTIVE' && !m.risk && <UI.Badge tone="success" dot>활동 중</UI.Badge>}
                      {m.status === 'ON_HOLD' && <UI.Badge tone="neutral">휴원</UI.Badge>}
                    </td>
                  </tr>
                );
              })}
            </tbody>
          </table>
        </UI.CardBody>
        <UI.CardFooter>
          {(() => {
            const total = rows.length;
            const pageCount = Math.max(1, Math.ceil(total / PAGE_SIZE));
            const safePage = Math.min(page, pageCount);
            const start = total === 0 ? 0 : (safePage - 1) * PAGE_SIZE + 1;
            const end = Math.min(safePage * PAGE_SIZE, total);
            return (
              <React.Fragment>
                <span className="t-caption">
                  {total === 0 ? '0명' : <React.Fragment><strong className="t-mono num" style={{ color: 'var(--text-primary)' }}>{start}–{end}</strong> / 총 {total}명</React.Fragment>}
                </span>
                {pageCount > 1 && (
                  <div className="pager" role="navigation" aria-label="페이지">
                    <UI.Button size="sm" variant="ghost" icon={Icon.ChevLeft} onClick={() => setPage(p => Math.max(1, p - 1))} disabled={safePage <= 1}>이전</UI.Button>
                    <div className="pager__nums">
                      {(() => {
                        const nums = [];
                        const windowSize = 5;
                        let lo = Math.max(1, safePage - Math.floor(windowSize / 2));
                        let hi = Math.min(pageCount, lo + windowSize - 1);
                        lo = Math.max(1, hi - windowSize + 1);
                        if (lo > 1) nums.push(<button key="first" className="pager__num" onClick={() => setPage(1)}>1</button>);
                        if (lo > 2) nums.push(<span key="dot1" className="pager__dots">…</span>);
                        for (let i = lo; i <= hi; i++) nums.push(
                          <button key={i} className={`pager__num ${i === safePage ? 'is-active' : ''}`} onClick={() => setPage(i)} aria-current={i === safePage ? 'page' : undefined}>{i}</button>
                        );
                        if (hi < pageCount - 1) nums.push(<span key="dot2" className="pager__dots">…</span>);
                        if (hi < pageCount) nums.push(<button key="last" className="pager__num" onClick={() => setPage(pageCount)}>{pageCount}</button>);
                        return nums;
                      })()}
                    </div>
                    <UI.Button size="sm" variant="ghost" iconRight={Icon.ChevRight} onClick={() => setPage(p => Math.min(pageCount, p + 1))} disabled={safePage >= pageCount}>다음</UI.Button>
                  </div>
                )}
                <span style={{ marginLeft: 'auto' }} className="t-caption">월 결제 합계 <strong className="t-mono num" style={{ color: 'var(--text-primary)' }}>{D.formatMoney(rows.reduce((s, m) => s + (m.amount || 0), 0))}</strong></span>
              </React.Fragment>
            );
          })()}
        </UI.CardFooter>
      </UI.Card>
      )}
    </React.Fragment>
  );
}

function PassBar({ value, risk }) {
  const pct = Math.max(0, Math.min(1, value));
  return (
    <div style={{ width: 96, height: 4, background: 'var(--bg-inset)', borderRadius: 99, overflow: 'hidden' }}>
      <div style={{ width: `${pct * 100}%`, height: '100%', background: risk ? 'var(--status-warning)' : 'var(--accent-default)' }}></div>
    </div>
  );
}

// ============================================================
// Cohort views — 세그먼트별 맞춤 테이블
// ============================================================

// 신규 회원 — 최근 30일 가입
function CohortNew({ rows, onOpenMember }) {
  const sorted = [...rows].sort((a, b) => b.joined.localeCompare(a.joined));
  const totalMRR = sorted.reduce((s, m) => s + (m.amount || 0), 0);

  return (
    <React.Fragment>
      <div className="grid-3" style={{ marginBottom: 16, gap: 12 }}>
        <CohortSummary label="신규 가입" value={`${sorted.length}명`} sub="최근 30일" tone="accent"/>
        <CohortSummary label="예상 월 매출 증가" value={`+${D.formatMoney(totalMRR)}`} sub="이번 달 결제 기준" tone="success"/>
        <CohortSummary label="온보딩 필요" value={`${sorted.filter(m => !D.lessonNotes.some(n => n.memberId === m.id)).length}명`} sub="첫 수업 다음 일지 미작성" tone="warning"/>
      </div>

      <UI.Card>
        <UI.CardHeader
          title="신규 회원 명단"
          subtitle="가입일 기준 웰 세이·첫 수업 온보딩 상태"
          actions={<UI.Button size="sm" icon={Icon.Mail} onClick={() => window.toast && window.toast('신규 회원 환영 알림톡 일괄 발송 — D+0 / D+7 / D+30 자동 큐 등록', { tone: 'success' })}>환영 알림톡 일괄 발송</UI.Button>}
        />
        <UI.CardBody flush>
          <table className="tbl">
            <thead>
              <tr>
                <th style={{ width: '24%' }}>회원</th>
                <th>가입일</th>
                <th>담당 선생님</th>
                <th>수업권</th>
                <th>첫 수업</th>
                <th>온보딩</th>
              </tr>
            </thead>
            <tbody>
              {sorted.map(m => {
                const t = D.getTeacher(m.teacherId);
                const daysSinceJoin = Math.abs(daysFromToday(m.joined));
                const hasNote = D.lessonNotes.some(n => n.memberId === m.id);
                const hasReservation = D.reservations.some(r => r.memberId === m.id);
                return (
                  <tr key={m.id} className="row-clickable" onClick={() => onOpenMember(m.id)}>
                    <td className="cell-identity">
                      <div className="row" style={{ gap: 10 }}>
                        <UI.Avatar name={m.name} tone={m.tone} size="md"/>
                        <div className="col" style={{ gap: 0 }}>
                          <span className="t-body-strong">{m.name} <UI.Badge tone="accent" dot>NEW</UI.Badge></span>
                          <span className="t-caption t-mono num" style={{ fontSize: 12 }}>{m.phone || '— 폰 없음'}</span>
                        </div>
                      </div>
                    </td>
                    <td data-label="가입일">
                      <div className="col" style={{ gap: 0 }}>
                        <span className="t-mono num">{D.formatDate(m.joined, { short: true })}</span>
                        <span className="t-caption" style={{ fontSize: 12 }}>{daysSinceJoin}일 전</span>
                      </div>
                    </td>
                    <td data-label="담당">
                      <div className="row" style={{ gap: 6 }}>
                        <UI.Avatar name={t.name} tone={t.tone} size="sm"/>
                        <span>{t.name}</span>
                      </div>
                    </td>
                    <td data-label="수업권" className="t-mono num">{m.passType === 'SESSION' ? `${m.remaining}/${m.total}회` : '기간권'}</td>
                    <td data-label="첫 수업">
                      {hasReservation
                        ? <UI.Badge tone="success" dot>예약됨</UI.Badge>
                        : <UI.Badge tone="warning">미예약</UI.Badge>}
                    </td>
                    <td data-label="온보딩">
                      {hasNote
                        ? <UI.Badge tone="success" dot>첫 일지 작성됨</UI.Badge>
                        : <UI.Badge tone="neutral">대기</UI.Badge>}
                    </td>
                  </tr>
                );
              })}
              {sorted.length === 0 && (
                <tr><td colSpan="6"><UI.Empty title="최근 30일 신규 가입이 없습니다" hint="상담 이휴·마케팅 강화 검토"/></td></tr>
              )}
            </tbody>
          </table>
        </UI.CardBody>
      </UI.Card>
    </React.Fragment>
  );
}

// 휴원 — 단기/중기/장기
function CohortHold({ rows, onOpenMember }) {
  const enriched = rows.map(m => ({ m, hold: classifyHold(m) }));
  const buckets = {
    short: enriched.filter(e => e.hold.type === 'short'),
    mid:   enriched.filter(e => e.hold.type === 'mid'),
    long:  enriched.filter(e => e.hold.type === 'long'),
  };

  return (
    <React.Fragment>
      <div className="grid-3" style={{ marginBottom: 16, gap: 12 }}>
        <CohortSummary label="단기 휴원" value={`${buckets.short.length}명`} sub="~30일 · 복귀 일정 안정" tone="success"/>
        <CohortSummary label="중기 휴원" value={`${buckets.mid.length}명`} sub="1~3개월 · 복귀일 D-7 알림 예정" tone="warning"/>
        <CohortSummary label="장기 휴원" value={`${buckets.long.length}명`} sub="3개월+ · 퇴원 전환 가능성 계속 관찰" tone="danger"/>
      </div>

      {[
        { key: 'short', title: '단기 휴원', subtitle: '대면 ≤ 30일 — 일시적 연기, 자동 복귀 워크플로우', items: buckets.short },
        { key: 'mid',   title: '중기 휴원', subtitle: '1~3개월 — 연수·수술 등', items: buckets.mid },
        { key: 'long',  title: '장기 휴원', subtitle: '3개월 이상 — 군입대·장기 이탈 등, 주기적 케어 필요', items: buckets.long },
      ].filter(g => g.items.length > 0).map(group => (
        <UI.Card key={group.key} style={{ marginBottom: 16 }}>
          <UI.CardHeader title={group.title} subtitle={group.subtitle} actions={<UI.Badge tone={group.key === 'short' ? 'success' : group.key === 'mid' ? 'warning' : 'danger'}>{group.items.length}명</UI.Badge>}/>
          <UI.CardBody flush>
            <table className="tbl">
              <thead>
                <tr>
                  <th style={{ width: '22%' }}>회원</th>
                  <th>휴원 사유</th>
                  <th>시작</th>
                  <th>복귀 예정</th>
                  <th>기간</th>
                  <th>담당</th>
                  <th style={{ width: 140 }}></th>
                </tr>
              </thead>
              <tbody>
                {group.items.map(({ m, hold }) => {
                  const t = D.getTeacher(m.teacherId);
                  const dToReturn = daysFromToday(m.expectedReturn);
                  const returnLabel = dToReturn == null
                    ? '미정'
                    : dToReturn > 0 ? `D-${dToReturn}` : dToReturn === 0 ? '오늘' : `${-dToReturn}일 경과 · 미복귀`;
                  const overdue = dToReturn != null && dToReturn < 0;
                  return (
                    <tr key={m.id} className="row-clickable" onClick={() => onOpenMember(m.id)}>
                      <td className="cell-identity">
                        <div className="row" style={{ gap: 10 }}>
                          <UI.Avatar name={m.name} tone={m.tone} size="md"/>
                          <div className="col" style={{ gap: 0 }}>
                            <span className="t-body-strong">{m.name}</span>
                            <span className="t-caption t-mono num" style={{ fontSize: 12 }}>{m.phone || '—'}</span>
                          </div>
                        </div>
                      </td>
                      <td data-label="사유">{m.holdReason || <span className="tertiary">—</span>}</td>
                      <td data-label="시작" className="t-mono num">{D.formatDate(m.holdStart, { short: true })}</td>
                      <td data-label="복귀">
                        <div className="col" style={{ gap: 0 }}>
                          <span className="t-mono num">{m.expectedReturn ? D.formatDate(m.expectedReturn, { short: true }) : '미정'}</span>
                          {dToReturn != null && (
                            <UI.Badge tone={overdue ? 'danger' : dToReturn <= 7 ? 'warning' : 'neutral'} dot={overdue || dToReturn <= 7}>{returnLabel}</UI.Badge>
                          )}
                        </div>
                      </td>
                      <td data-label="기간" className="t-mono num">{hold.days}일</td>
                      <td data-label="담당">
                        <div className="row" style={{ gap: 6 }}>
                          <UI.Avatar name={t.name} tone={t.tone} size="sm"/>
                          <span>{t.name}</span>
                        </div>
                      </td>
                      <td className="cell-actions" style={{ textAlign: 'right' }}>
                        <div className="row" style={{ justifyContent: 'flex-end', gap: 4 }}>
                          <UI.Button size="sm" variant="ghost" icon={Icon.Phone} onClick={(e) => { e.stopPropagation(); window.toast && window.toast(`${m.name}님께 안부 알림톡 발송`, { tone: 'success' }); }}>안부</UI.Button>
                          <UI.Button size="sm" variant="primary" icon={Icon.Check} onClick={(e) => { e.stopPropagation(); window.toast && window.toast(`${m.name}님 복귀 처리 — 수강권/일정 복구`, { tone: 'success' }); }}>복귀 처리</UI.Button>
                        </div>
                      </td>
                    </tr>
                  );
                })}
              </tbody>
            </table>
          </UI.CardBody>
        </UI.Card>
      ))}

      {enriched.length === 0 && (
        <UI.Card><UI.CardBody><UI.Empty title="휴원 회원이 없습니다"/></UI.CardBody></UI.Card>
      )}
    </React.Fragment>
  );
}

// 퇴원 — 재등록 유도
function CohortOut({ rows, onOpenMember, onOpenRegister }) {
  const sorted = [...rows].sort((a, b) => (b.withdrawnAt || '').localeCompare(a.withdrawnAt || ''));
  const within90 = sorted.filter(m => {
    if (!m.withdrawnAt) return false;
    const days = Math.abs(daysFromToday(m.withdrawnAt));
    return days <= 90;
  });

  return (
    <React.Fragment>
      <div className="grid-3" style={{ marginBottom: 16, gap: 12 }}>
        <CohortSummary label="총 퇴원 회원" value={`${sorted.length}명·누적`} sub="데이터는 보존 · 알림 정지" tone="neutral"/>
        <CohortSummary label="최근 90일 퇴원" value={`${within90.length}명`} sub="재등록 제안 권장 구간" tone="warning"/>
        <CohortSummary label="재등록 전환 (YTD)" value={`0명`} sub="재등록 메시지 완성 필요" tone="accent"/>
      </div>

      <UI.Card>
        <UI.CardHeader
          title="퇴원 회원 명단"
          subtitle="재등록 시 기존 상담·일지 이력 자동 복구"
          actions={<UI.Button size="sm" icon={Icon.Mail} onClick={() => window.toast && window.toast('퇴원 회원 대상 재등록 안내 일괄 발송', { tone: 'success' })}>재등록 알림톡 일괄 발송</UI.Button>}
        />
        <UI.CardBody flush>
          <table className="tbl">
            <thead>
              <tr>
                <th style={{ width: '22%' }}>회원</th>
                <th>퇴원 일자</th>
                <th>사유</th>
                <th>마지막 출석</th>
                <th>재가입 시점</th>
                <th>담당</th>
                <th style={{ width: 140 }}></th>
              </tr>
            </thead>
            <tbody>
              {sorted.map(m => {
                const t = D.getTeacher(m.teacherId);
                const daysSince = m.withdrawnAt ? Math.abs(daysFromToday(m.withdrawnAt)) : null;
                const reEntryStage = daysSince == null ? null
                  : daysSince <= 30 ? { label: '송별 메시지', tone: 'success' }
                  : daysSince <= 90 ? { label: '재등록 권유', tone: 'warning' }
                  :                   { label: '장기 관찰', tone: 'neutral' };
                return (
                  <tr key={m.id} className="row-clickable" onClick={() => onOpenMember(m.id)}>
                    <td className="cell-identity">
                      <div className="row" style={{ gap: 10 }}>
                        <UI.Avatar name={m.name} tone={m.tone} size="md"/>
                        <div className="col" style={{ gap: 0 }}>
                          <span className="t-body-strong" style={{ opacity: 0.85 }}>{m.name}</span>
                          <span className="t-caption t-mono num" style={{ fontSize: 12 }}>{m.phone || '—'}</span>
                        </div>
                      </div>
                    </td>
                    <td data-label="퇴원일">
                      <div className="col" style={{ gap: 0 }}>
                        <span className="t-mono num">{m.withdrawnAt ? D.formatDate(m.withdrawnAt, { short: true }) : '—'}</span>
                        {daysSince != null && <span className="t-caption" style={{ fontSize: 12 }}>{daysSince}일 경과</span>}
                      </div>
                    </td>
                    <td data-label="사유">{m.withdrawnReason || <span className="tertiary">—</span>}</td>
                    <td data-label="마지막 출석" className="t-mono num">{D.formatDate(m.lastVisit, { short: true })}</td>
                    <td data-label="재가입 시점">
                      {reEntryStage && <UI.Badge tone={reEntryStage.tone}>{reEntryStage.label}</UI.Badge>}
                    </td>
                    <td data-label="담당">
                      <div className="row" style={{ gap: 6, opacity: 0.7 }}>
                        <UI.Avatar name={t.name} tone={t.tone} size="sm"/>
                        <span>{t.name}</span>
                      </div>
                    </td>
                    <td className="cell-actions" style={{ textAlign: 'right' }}>
                      <div className="row" style={{ justifyContent: 'flex-end', gap: 4 }}>
                        <UI.Button size="sm" variant="ghost" icon={Icon.Mail} onClick={(e) => { e.stopPropagation(); window.toast && window.toast(`${m.name}님께 안부 메시지 발송`, { tone: 'success' }); }}>메시지</UI.Button>
                        <UI.Button size="sm" variant="primary" icon={Icon.Refresh} onClick={(e) => { e.stopPropagation(); onOpenRegister && onOpenRegister(); }}>재등록</UI.Button>
                      </div>
                    </td>
                  </tr>
                );
              })}
              {sorted.length === 0 && (
                <tr><td colSpan="7"><UI.Empty title="퇴원 이력이 없습니다"/></td></tr>
              )}
            </tbody>
          </table>
        </UI.CardBody>
      </UI.Card>
    </React.Fragment>
  );
}

function CohortSummary({ label, value, sub, tone }) {
  const c = tone === 'success' ? 'var(--status-success)' :
            tone === 'warning' ? 'var(--status-warning)' :
            tone === 'danger'  ? 'var(--status-danger)'  :
            tone === 'accent'  ? 'var(--accent-default)' :
                                 'var(--text-primary)';
  return (
    <div style={{ background: 'var(--bg-surface)', borderRadius: 'var(--radius-lg)', border: '1px solid var(--border-default)', padding: 16, position: 'relative', overflow: 'hidden' }}>
      <div style={{ position: 'absolute', top: 0, left: 0, width: 4, height: '100%', background: c }}/>
      <div className="t-micro" style={{ marginLeft: 8 }}>{label}</div>
      <div className="num t-mono" style={{ fontSize: 22, fontWeight: 700, marginLeft: 8, marginTop: 4, color: c, letterSpacing: '-0.02em' }}>{value}</div>
      <div className="t-caption" style={{ marginLeft: 8, fontSize: 12 }}>{sub}</div>
    </div>
  );
}

// ============================================================
// 3. Member Detail (drawer content)
// ============================================================
function MemberDetail({ memberId, onClose, onSendLink, onOpenJournal }) {
  const [tab, setTab] = useState('overview');
  const m = D.getMember(memberId);
  const t = D.getTeacher(m.teacherId);
  const myPayments = D.payments.filter(p => p.memberId === memberId);
  const myReservations = D.reservations.filter(r => r.memberId === memberId);
  const myNote = D.lessonNotes.find(n => n.memberId === memberId);
  const ac = m.additionalContact;

  return (
    <React.Fragment>
      {/* Header summary */}
      <div className="row" style={{ gap: 12, marginBottom: 16 }}>
        <UI.Avatar name={m.name} tone={m.tone} size="lg"/>
        <div className="col" style={{ gap: 2, flex: 1 }}>
          <div className="row" style={{ gap: 8 }}>
            <h2 className="t-h2" style={{ margin: 0 }}>{m.name}</h2>
            {m.status === 'ACTIVE' && <UI.Badge tone="success" dot>활동 중</UI.Badge>}
            {m.status === 'ON_HOLD' && <UI.Badge tone="neutral">휴원</UI.Badge>}
          </div>
          <div className="t-caption">{t.name} 선생님 담당 · 가입 {D.formatDate(m.joined, { short: true })}</div>
        </div>
        <UI.Button icon={Icon.Phone} size="sm" onClick={() => window.toast && window.toast(`${m.name}님 ${m.phone || '전화 미등록'} 발신`, m.phone ? { tone: 'success' } : { tone: 'warning' })}>전화</UI.Button>
        <UI.Button icon={Icon.Mail} size="sm" onClick={onSendLink}>알림톡</UI.Button>
      </div>

      <div className="grid-3" style={{ marginBottom: 16, gap: 8 }}>
        <Stat label="수업권 잔여" value={`${m.remaining}회`} sub={`/ ${m.total}회`} tone={m.risk === 'low-pass' ? 'warning' : null} />
        <Stat label="다음 결제일" value={m.nextDue ? D.formatRelative(m.nextDue) : '—'} sub={m.nextDue ? D.formatDate(m.nextDue, { short: true }) : ''} tone={m.risk === 'overdue' ? 'danger' : null} />
        <Stat label="이번 달 결제" value={D.formatMoney(m.amount)} sub={m.passType === 'SESSION' ? '회차권' : '기간권'} />
      </div>

      <UI.Tabs
        items={[
          { id: 'overview', label: '기본 정보' },
          { id: 'consult',  label: '상담 이력' },
          { id: 'pass',     label: '수업권·결제' },
          { id: 'sched',    label: '예약·출석' },
          { id: 'notes',    label: '수업일지' },
          { id: 'access',   label: '접근 링크' },
        ]}
        active={tab}
        onChange={setTab}
      />

      {tab === 'overview' && (
        <div className="col" style={{ gap: 16 }}>
          {/* F5 이탈 위험 점수 카드 — 위험 점수가 있는 회원만 */}
          {window.RetentionUI && D.getRisk && D.getRisk(m.id) && <window.RetentionUI.RiskCard memberId={m.id}/>}

          <UI.Card>
            <UI.CardBody>
              <div className="grid-2" style={{ gap: 12 }}>
                <Field label="이름" value={m.name} />
                <Field label="회원 본인 전화번호" value={m.phone || '— 등록 없음'} mono={!!m.phone}/>
                <Field label="담당 선생님" value={`${t.name} 선생님`} />
                <Field label="상태" value={m.status === 'ACTIVE' ? '활동 중' : m.status === 'ON_HOLD' ? '휴원' : '퇴원'} />
                <Field label="가입일" value={D.formatDate(m.joined)} />
                <Field label="음역대" value={m.range || '—'} mono />
              </div>
              {m.goal && (
                <div style={{ marginTop: 12, padding: 12, background: 'var(--bg-canvas)', borderRadius: 'var(--radius-md)', border: '1px dashed var(--border-default)' }}>
                  <div className="t-micro" style={{ marginBottom: 4 }}>레슨 목표</div>
                  <div>{m.goal}</div>
                </div>
              )}
            </UI.CardBody>
          </UI.Card>

          {/* 도메인 프로필 — 학원이 설정에서 직접 정의하는 정적 속성 */}
          {(m.voiceTone || (m.prefGenres && m.prefGenres.length) || m.favSingers) && (
            <UI.Card>
              <UI.CardHeader
                title="음악 프로필"
                subtitle="수업마다 바뀌지 않는 안정적 속성 · 설정에서 학원이 정의"
                actions={<UI.Button size="sm" variant="ghost" icon={Icon.Edit} onClick={() => window.toast && window.toast('회원 편집 모달에서 수정')}>편집</UI.Button>}
              />
              <UI.CardBody>
                <div className="grid-2" style={{ gap: 12 }}>
                  <Field label="음색 / 톤 특성" value={m.voiceTone || '—'} />
                  <Field label="선호 가수" value={m.favSingers || '—'} />
                </div>
                {m.prefGenres && m.prefGenres.length > 0 && (
                  <div style={{ marginTop: 14 }}>
                    <div className="t-micro" style={{ marginBottom: 6 }}>선호 장르</div>
                    <div className="row" style={{ gap: 6, flexWrap: 'wrap' }}>
                      {m.prefGenres.map(g => <UI.Badge key={g} tone="neutral">{g}</UI.Badge>)}
                    </div>
                  </div>
                )}
                <div className="t-caption" style={{ fontSize: 11, marginTop: 12, lineHeight: 1.5 }}>
                  이 필드는 보컬 학원 카테고리의 기본값입니다. 미술 / 댄스 / 어학 등 다른 카테고리는 다른 필드 세트를 가집니다.
                </div>
              </UI.CardBody>
            </UI.Card>
          )}

          {ac && (
            <UI.Card>
              <UI.CardHeader title="추가 알림 수신자" subtitle="시스템 사용자 아님 · 알림 수신 연락처"/>
              <UI.CardBody>
                <div className="grid-3" style={{ gap: 12 }}>
                  <Field label="이름" value={ac.name}/>
                  <Field label="전화번호" value={ac.phone} mono/>
                  <Field label="관계" value={ac.relation}/>
                </div>
              </UI.CardBody>
            </UI.Card>
          )}

          <UI.Card>
            <UI.CardHeader title="이번 주 일정" />
            <UI.CardBody flush>
              {myReservations.length === 0 ? (
                <UI.Empty title="예정된 수업이 없습니다" hint="새 정기 예약을 만들어 보세요" />
              ) : (
                <ul style={{ listStyle: 'none', margin: 0, padding: 0 }}>
                  {myReservations.map(r => (
                    <li key={r.id} className="row" style={{ padding: '10px 20px', gap: 12, borderBottom: '1px solid var(--border-default)' }}>
                      <span className={`dot-tone-${r.type}`}></span>
                      <span className="t-mono num" style={{ width: 110 }}>{D.dayNames[r.dow]}요일 {D.formatTime(r.start)}</span>
                      <span className="t-caption">{D.getRoom(r.roomId)?.name}</span>
                    </li>
                  ))}
                </ul>
              )}
            </UI.CardBody>
          </UI.Card>
        </div>
      )}

      {tab === 'access' && (
        <window.Modals.TokenManager memberId={memberId} onSend={onSendLink}/>
      )}

      {tab === 'pass' && (
        <UI.Card>
          <UI.CardHeader title="결제 이력" actions={<UI.Button size="sm" icon={Icon.Plus} onClick={() => window.toast && window.toast(`${m.name}님 수업권 추가 — 결제 페이지에서 발급`, { tone: 'success' })}>수업권 추가</UI.Button>} />
          <UI.CardBody flush>
            <table className="tbl">
              <thead><tr><th>날짜</th><th>금액</th><th>방법</th><th>상태</th></tr></thead>
              <tbody>
                {myPayments.length === 0 ? (
                  <tr><td colSpan="4"><UI.Empty title="결제 이력이 없습니다" hint="첫 수업권을 등록하면 여기에 표시됩니다"/></td></tr>
                ) : myPayments.map(p => (
                  <tr key={p.id}>
                    <td data-label="날짜" className="t-mono num">{D.formatDate(p.paidAt || p.dueDate, { short: true })}</td>
                    <td data-label="금액" className="t-mono num">{D.formatMoney(p.amount)}</td>
                    <td data-label="방법">{p.method}</td>
                    <td data-label="상태">
                      {p.status === 'PAID'     && <UI.Badge tone="success" dot>완료</UI.Badge>}
                      {p.status === 'DUE_SOON' && <UI.Badge tone="warning" dot>D-{p.daysUntil}</UI.Badge>}
                      {p.status === 'OVERDUE'  && <UI.Badge tone="danger" dot>D+{p.daysOverdue} 누락</UI.Badge>}
                    </td>
                  </tr>
                ))}
              </tbody>
            </table>
          </UI.CardBody>
        </UI.Card>
      )}

      {tab === 'notes' && (
        <div className="col" style={{ gap: 12 }}>
          <div className="row" style={{ padding: '12px 14px', background: 'var(--accent-subtle-bg)', borderRadius: 'var(--radius-md)', border: '1px solid var(--accent-default)', gap: 10 }}>
            <Icon.Note size={16} style={{ color: 'var(--accent-default)' }}/>
            <div className="col" style={{ gap: 0, flex: 1 }}>
              <span className="t-body-strong" style={{ fontSize: 13 }}>일지는 전용 작성 페이지에서</span>
              <span className="t-caption" style={{ fontSize: 12 }}>패드/태블릿에 열어두고 수업 중 계속 작성하도록 별도 화면으로 분리했습니다.</span>
            </div>
            <UI.Button size="sm" variant="primary" icon={Icon.Edit} onClick={() => onOpenJournal && onOpenJournal(memberId)}>일지 작성 열기</UI.Button>
          </div>
          {myNote ? (
            <UI.Card>
              <UI.CardHeader
                title={`최근 일지 · ${D.formatDate(myNote.date, { short: true })}`}
                subtitle="읽기 전용 미리보기"
                actions={<UI.Button size="sm" variant="ghost" icon={Icon.ChevRight} onClick={() => onOpenJournal && onOpenJournal(memberId)}>전체 일지 타임라인</UI.Button>}
              />
              <UI.CardBody>
                <div className="col" style={{ gap: 12 }}>
                  {Object.entries(myNote.fields).map(([k, v]) => (
                    <Field key={k} label={k} value={v} />
                  ))}
                  <div className="divider"></div>
                  {myNote.internalNote && (
                    <Field label="강사 내부 메모 (강사·원장만)" value={myNote.internalNote}/>
                  )}
                  {myNote.memberMessage && (
                    <Field label="회원에게 한마디 (회원 공개)" value={myNote.memberMessage}/>
                  )}
                </div>
              </UI.CardBody>
            </UI.Card>
          ) : (
            <UI.Empty title="수업일지가 없습니다" hint="첫 수업 후 선생님이 작성합니다" action={<UI.Button icon={Icon.Plus} variant="primary" onClick={() => onOpenJournal && onOpenJournal(memberId)}>일지 작성 페이지 열기</UI.Button>}/>
          )}
        </div>
      )}

      {tab === 'consult' && (
        <UI.Card>
          <UI.CardBody>
            {D.consultations.filter(c => c.memberId === memberId).length > 0 ? (
              D.consultations.filter(c => c.memberId === memberId).map(c => (
                <div key={c.id} className="col" style={{ gap: 8 }}>
                  <div className="row" style={{ gap: 8 }}>
                    <span className="t-body-strong">{D.fmt.shortDate(c.conductedAt)}</span>
                    <span className="t-caption">{D.getTeacher(c.teacherId).name} 선생님 진행</span>
                  </div>
                  <Field label="목표" value={c.goal}/>
                  <Field label="음역대" value={c.range} mono/>
                  <Field label="유입 채널" value={c.source}/>
                </div>
              ))
            ) : <UI.Empty title="상담 이력이 없습니다"/>}
          </UI.CardBody>
        </UI.Card>
      )}

      {tab === 'sched' && (
        <UI.Card>
          <UI.CardHeader title="이번 달 출석률" actions={<UI.Badge tone="success" dot>92%</UI.Badge>} />
          <UI.CardBody>
            <div style={{ display: 'grid', gridTemplateColumns: 'repeat(7, 1fr)', gap: 6 }}>
              {Array.from({ length: 28 }).map((_, i) => {
                const v = (i * 7 + 3) % 11;
                const state = v < 6 ? 'present' : v < 8 ? 'none' : v < 10 ? 'absent' : 'present';
                const bg = state === 'present' ? 'var(--accent-subtle-bg)' : state === 'absent' ? 'var(--status-danger-bg)' : 'var(--bg-subtle)';
                const fg = state === 'present' ? 'var(--accent-default)' : state === 'absent' ? 'var(--status-danger)' : 'var(--text-tertiary)';
                return <div key={i} style={{ height: 28, borderRadius: 4, background: bg, color: fg, fontSize: 11, display: 'grid', placeItems: 'center', fontWeight: 600 }} className="num t-mono">{i + 1}</div>;
              })}
            </div>
            <div className="row" style={{ gap: 16, marginTop: 12, color: 'var(--text-tertiary)', fontSize: 12 }}>
              <span><span style={{ display: 'inline-block', width: 10, height: 10, background: 'var(--accent-subtle-bg)', borderRadius: 2, marginRight: 6, verticalAlign: 'middle' }}/>출석</span>
              <span><span style={{ display: 'inline-block', width: 10, height: 10, background: 'var(--status-danger-bg)', borderRadius: 2, marginRight: 6, verticalAlign: 'middle' }}/>결석</span>
              <span><span style={{ display: 'inline-block', width: 10, height: 10, background: 'var(--bg-subtle)', borderRadius: 2, marginRight: 6, verticalAlign: 'middle' }}/>예약 없음</span>
            </div>
          </UI.CardBody>
        </UI.Card>
      )}
    </React.Fragment>
  );
}

function Stat({ label, value, sub, tone }) {
  const color = tone === 'warning' ? 'var(--status-warning)' : tone === 'danger' ? 'var(--status-danger)' : 'var(--text-primary)';
  return (
    <div style={{ padding: 12, background: 'var(--bg-canvas)', borderRadius: 'var(--radius-md)', border: '1px solid var(--border-default)' }}>
      <div className="t-micro">{label}</div>
      <div className="row" style={{ alignItems: 'baseline', gap: 6, marginTop: 4 }}>
        <span style={{ fontSize: 20, fontWeight: 700, color }} className="t-mono num">{value}</span>
        {sub && <span className="t-caption" style={{ fontSize: 12 }}>{sub}</span>}
      </div>
    </div>
  );
}

function Field({ label, value, mono }) {
  return (
    <div className="col" style={{ gap: 2 }}>
      <div className="t-micro">{label}</div>
      <div className={mono ? 't-mono num' : ''} style={{ fontSize: 14 }}>{value}</div>
    </div>
  );
}

window.AdminPages1 = { Dashboard, Members, MemberDetail };
})();
