/* global React, Icon, UI, LessonHighData */
// ============================================================
// I3 · 출결 — Attendance table (today's lessons) + Kiosk mode
// Attaches to schedule page below the calendar OR as a separate
// view. Provides quick check-in/late/absent actions and a
// fullscreen tablet kiosk for entrance check-in.
// ============================================================
(function () {
const { useState, useEffect, useMemo } = React;
const D = LessonHighData;

const STATUS_META = {
  scheduled: { label: '예정',     tone: 'neutral'  },
  present:   { label: '출석',     tone: 'success'  },
  late:      { label: '지각',     tone: 'warning'  },
  absent:    { label: '사전결석', tone: 'neutral'  },
  'no-show': { label: '노쇼',     tone: 'danger'   },
  cancelled: { label: '취소',     tone: 'neutral'  },
};

// ────────────────────────────────────────────────────────────
// AttendancePage — dedicated route (page-header + KPIs + table)
// ────────────────────────────────────────────────────────────
function AttendancePage({ onOpenMember }) {
  const [kioskOpen, setKioskOpen] = useState(false);
  return (
    <React.Fragment>
      <header className="page-header">
        <div>
          <h1 className="page-title">출결</h1>
          <div className="page-sub">{D.formatDate(D.TODAY)} · 오늘의 등하원과 패스 차감을 한 화면에서</div>
        </div>
        <div className="page-actions">
          <UI.Button icon={Icon.Settings} variant="ghost" onClick={() => window.toast && window.toast('설정 → 출결 정책으로 이동')}>출결 정책</UI.Button>
          <UI.Button icon={Icon.Receipt} variant="ghost" onClick={() => window.toast && window.toast('11월 월간 출석부 Excel 다운로드 — 회원·강사·반별 필터 적용', { tone: 'success' })}>월간 출석부</UI.Button>
          <UI.Button icon={Icon.Mobile} variant="primary" onClick={() => setKioskOpen(true)}>키오스크 모드</UI.Button>
        </div>
      </header>

      {/* 출결 KPIs */}
      <div className="grid-4" style={{ marginBottom: 16 }}>
        <AttKpi label="오늘 출석" value={D.attendanceToday.present} sub={`패스 ${D.attendanceToday.passesDeducted}건 차감`} tone="success"/>
        <AttKpi label="지각"     value={D.attendanceToday.late}    sub={`grace ${D.attendancePolicy.lateGraceMin}분`} tone="warning"/>
        <AttKpi label="노쇼"     value={D.attendanceToday['no-show'] || D.attendanceToday.noShow} sub="정책상 패스 1회 차감" tone="danger"/>
        <AttKpi label="예정"     value={D.attendanceToday.scheduled} sub="오늘 남은 수업" tone="neutral"/>
      </div>

      <AttendanceToday onOpenMember={onOpenMember} onOpenKiosk={() => setKioskOpen(true)} hideHeader={false}/>

      <Kiosk open={kioskOpen} onClose={() => setKioskOpen(false)}/>
    </React.Fragment>
  );
}

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

// ────────────────────────────────────────────────────────────
// PassCell — "어떤 패스에서 얼마나 차감됐고, 얼마나 남았는지"
//   상태별:
//     예정       → 차감 전 (X/Y)
//     출석/지각   → 차감 후 (X/Y) + "−1" 칩 + 차감 전 표시
//     사전결석    → 차감 없음 (X/Y) + "차감 없음" 칩
//     노쇼       → 차감 후 (X/Y) + "노쇼 차감" 칩 (정책)
// ────────────────────────────────────────────────────────────
function PassCell({ member: m, att }) {
  if (!m) return <span className="t-caption" style={{ fontSize: 11 }}>—</span>;
  const isPeriod = m.passType === 'PERIOD';
  const before = m.remaining;
  const delta = att.passDelta || 0;
  const after = Math.max(0, before + delta);
  const willDeduct = delta < 0;
  const isLow = after <= 3 && !isPeriod;
  const unit = isPeriod ? '일' : '회';

  return (
    <span className="pass-cell">
      <span className={`pass-cell__num t-mono num ${willDeduct ? 'is-changed' : isLow ? 'is-low' : ''}`}>
        {after}
      </span>
      <span className="pass-cell__sep t-mono">/ {m.total}{unit}</span>
      {willDeduct && <span className="pass-cell__delta t-mono">−1</span>}
    </span>
  );
}

// ────────────────────────────────────────────────────────────
// AttendanceToday — table for today's lessons w/ inline actions
// ────────────────────────────────────────────────────────────
function AttendanceToday({ onOpenMember, onOpenKiosk }) {
  // local override (mutable demo)
  const [state, setState] = useState(() => ({ ...D.attendance }));
  const [revertTarget, setRevertTarget] = useState(null);   // { id, name, status }
  const [timeTarget, setTimeTarget]     = useState(null);   // { id, name, mode: 'set'|'edit', currentTime?, currentStatus? }
  const today = D.reservations
    .filter(r => r.dow === 3)
    .sort((a, b) => a.start - b.start);

  const counts = useMemo(() => {
    const c = { present: 0, late: 0, absent: 0, 'no-show': 0, scheduled: 0 };
    today.forEach(r => {
      const s = (state[r.id] || { status: 'scheduled' }).status;
      if (c[s] != null) c[s]++;
    });
    return c;
  }, [state, today]);

  function setStatus(id, status, method = 'teacher') {
    const time = currentTime();
    setState(s => ({
      ...s,
      [id]: {
        ...(s[id] || {}),
        status,
        checkedInAt: ['present','late'].includes(status) ? time : null,
        method,
        passDelta: (status === 'present' || status === 'late' || status === 'no-show') ? -1 : 0,
      },
    }));
  }

  function applyTime(id, status, time) {
    setState(s => ({
      ...s,
      [id]: {
        ...(s[id] || {}),
        status,
        checkedInAt: time,
        method: 'teacher',
        passDelta: -1,
      },
    }));
  }

  function openRevert(r, att) {
    const m = r.memberId ? D.getMember(r.memberId) : null;
    setRevertTarget({ id: r.id, name: m?.name || r.prospect || '회원', status: att.status, checkedInAt: att.checkedInAt });
  }
  function openTimeEdit(r, att, mode) {
    const m = r.memberId ? D.getMember(r.memberId) : null;
    setTimeTarget({
      id: r.id,
      name: m?.name || r.prospect || '회원',
      scheduledAt: D.formatTime(r.start),
      mode,
      currentTime: att.checkedInAt || currentTime(),
      currentStatus: ['present','late'].includes(att.status) ? att.status : 'present',
    });
  }

  return (
    <UI.Card>
      <UI.CardHeader
        title="오늘의 출결"
        subtitle={`${D.formatDate(D.TODAY, { short: true })} · 출석 ${counts.present} · 지각 ${counts.late} · 결석 ${counts.absent} · 노쇼 ${counts['no-show']} · 예정 ${counts.scheduled}`}
        actions={
          <React.Fragment>
            <UI.Button size="sm" variant="ghost" icon={Icon.Receipt} onClick={() => window.toast && window.toast('이번 주 출석부 Excel 다운로드 완료', { tone: 'success' })}>출석부 Excel</UI.Button>
            <UI.Button size="sm" variant="primary" icon={Icon.Mobile} onClick={onOpenKiosk}>키오스크 모드</UI.Button>
          </React.Fragment>
        }
      />
      <UI.CardBody flush>
        <table className="tbl att-tbl">
          <thead><tr>
            <th style={{ width: 64 }}>시간</th>
            <th>회원</th>
            <th>강사</th>
            <th>{D.getRoom('r1') ? '연습실' : '공간'}</th>
            <th>상태</th>
            <th>패스</th>
            <th style={{ width: 220 }}></th>
          </tr></thead>
          <tbody>
            {today.map(r => {
              const att = state[r.id] || { status: 'scheduled' };
              const m   = r.memberId ? D.getMember(r.memberId) : null;
              const t   = r.teacherId ? D.getTeacher(r.teacherId) : null;
              const room = r.roomId ? D.getRoom(r.roomId) : null;
              const isLesson = r.type === 'lesson';
              const meta = STATUS_META[att.status] || STATUS_META.scheduled;
              const rowClass = `att-row att-row--${att.status}`;
              return (
                <tr key={r.id} className={rowClass}>
                  <td data-label="시간" className="t-mono num" style={{ fontWeight: 600 }}>{D.formatTime(r.start)}</td>
                  <td className="cell-identity">
                    {m ? (
                      <div className="row" style={{ gap: 10 }}>
                        <UI.Avatar name={m.name} tone={m.tone} size="md"/>
                        <button onClick={() => onOpenMember && onOpenMember(m.id)} className="t-body-strong" style={{ background: 'none', border: 'none', cursor: 'pointer', textAlign: 'left' }}>{m.name}</button>
                      </div>
                    ) : (
                      <span className="t-caption" style={{ fontSize: 13 }}>{r.prospect || '대여'}</span>
                    )}
                  </td>
                  <td data-label="강사" className="t-caption">{t ? `${t.name} 선생님` : '—'}</td>
                  <td data-label="공간" className="t-caption">{room?.name || '—'}</td>
                  <td data-label="상태">
                    <div className="col" style={{ gap: 2 }}>
                      <UI.Badge tone={meta.tone} dot>{meta.label}</UI.Badge>
                      {att.checkedInAt && (
                        <span className="t-mono num" style={{ fontSize: 11, color: 'var(--text-tertiary)' }}>{att.checkedInAt} · {methodLabel(att.method)}</span>
                      )}
                      {att.note && (
                        <span className="t-caption" style={{ fontSize: 11 }}>{att.note}</span>
                      )}
                    </div>
                  </td>
                  <td data-label="패스" className="att-pass">
                    {!isLesson ? (
                      <span className="t-caption" style={{ fontSize: 11 }}>—</span>
                    ) : (
                      <PassCell member={m} att={att}/>
                    )}
                  </td>
                  <td className="cell-actions" style={{ textAlign: 'right' }}>
                    {isLesson && att.status === 'scheduled' && (
                      <div className="row" style={{ justifyContent: 'flex-end', gap: 4 }}>
                        <UI.Button size="sm" variant="primary" onClick={() => setStatus(r.id, 'present')}>체크인</UI.Button>
                        <UI.Button size="sm" variant="ghost" onClick={() => setStatus(r.id, 'late')}>지각</UI.Button>
                        <UI.Button size="sm" variant="ghost" onClick={() => setStatus(r.id, 'absent')}>결석</UI.Button>
                      </div>
                    )}
                    {isLesson && att.status === 'scheduled' && (
                      <div className="row" style={{ justifyContent: 'flex-end', marginTop: 4 }}>
                        <button className="att-link" onClick={() => openTimeEdit(r, att, 'set')}>
                          <Icon.Clock size={12}/> 시간 지정 체크인
                        </button>
                      </div>
                    )}
                    {isLesson && att.status !== 'scheduled' && (
                      <div className="row" style={{ justifyContent: 'flex-end', gap: 4 }}>
                        {['present','late'].includes(att.status) && (
                          <UI.Button size="sm" variant="ghost" icon={Icon.Clock} onClick={() => openTimeEdit(r, att, 'edit')}>시간 수정</UI.Button>
                        )}
                        <UI.Button size="sm" variant="ghost" onClick={() => openRevert(r, att)}>되돌리기</UI.Button>
                      </div>
                    )}
                    {!isLesson && (
                      <span className="t-caption" style={{ fontSize: 11 }}>—</span>
                    )}
                  </td>
                </tr>
              );
            })}
          </tbody>
        </table>
      </UI.CardBody>
      <UI.CardFooter>
        <span>패스 자동 차감 <strong className="t-mono num" style={{ color: 'var(--text-primary)' }}>{D.attendanceToday.passesDeducted}건</strong></span>
        <span className="tertiary">·</span>
        <span>등하원 알림톡 발송됨 <strong className="t-mono num" style={{ color: 'var(--text-primary)' }}>{counts.present + counts.late}건</strong></span>
        <span style={{ marginLeft: 'auto' }} className="t-caption">노쇼는 정책상 패스 1회 차감 — <a style={{ color: 'var(--accent-default)' }}>출결 정책</a></span>
      </UI.CardFooter>

      <RevertConfirmModal
        target={revertTarget}
        onClose={() => setRevertTarget(null)}
        onConfirm={() => { if (revertTarget) setStatus(revertTarget.id, 'scheduled'); setRevertTarget(null); }}
      />
      <TimeEditModal
        target={timeTarget}
        onClose={() => setTimeTarget(null)}
        onSave={(time, status) => { if (timeTarget) applyTime(timeTarget.id, status, time); setTimeTarget(null); }}
      />
    </UI.Card>
  );
}

// ────────────────────────────────────────────────────────────
// RevertConfirmModal — confirm before undoing a check-in
// ────────────────────────────────────────────────────────────
function RevertConfirmModal({ target, onClose, onConfirm }) {
  const open = !!target;
  const statusLabel = target ? (STATUS_META[target.status]?.label || target.status) : '';
  const restoresPass = target && ['present','late','no-show'].includes(target.status);
  return (
    <UI.Modal
      open={open}
      onClose={onClose}
      title="체크인 되돌리기"
      width={440}
      footer={
        <React.Fragment>
          <UI.Button variant="ghost" onClick={onClose}>취소</UI.Button>
          <UI.Button variant="primary" onClick={onConfirm}>되돌리기</UI.Button>
        </React.Fragment>
      }
    >
      {target && (
        <div className="col" style={{ gap: 14 }}>
          <div className="t-body" style={{ lineHeight: 1.55 }}>
            <strong>{target.name}</strong> 님의 상태를 <strong>{statusLabel}</strong>에서 <strong>예정</strong>으로 되돌릴까요?
          </div>
          <div style={{
            background: 'var(--bg-subtle)', border: '1px solid var(--border-default)',
            borderRadius: 'var(--radius-md)', padding: '10px 12px',
            display: 'flex', flexDirection: 'column', gap: 6,
          }}>
            {target.checkedInAt && (
              <div className="row" style={{ gap: 8, fontSize: 12, color: 'var(--text-secondary)' }}>
                <Icon.Clock size={12}/>
                <span>체크인 시간 <span className="t-mono num">{target.checkedInAt}</span> 기록이 지워집니다</span>
              </div>
            )}
            {restoresPass && (
              <div className="row" style={{ gap: 8, fontSize: 12, color: 'var(--text-secondary)' }}>
                <Icon.Refresh size={12}/>
                <span>차감된 패스 <span className="t-mono num">1회</span>가 복원됩니다</span>
              </div>
            )}
            <div className="row" style={{ gap: 8, fontSize: 12, color: 'var(--text-secondary)' }}>
              <Icon.Send size={12}/>
              <span>이미 발송된 알림톡은 회수되지 않습니다</span>
            </div>
          </div>
          <div className="t-caption" style={{ fontSize: 12 }}>
            자주 일어나는 작업이 아니라서 한 번 더 여쭤봤어요.
          </div>
        </div>
      )}
    </UI.Modal>
  );
}

// ────────────────────────────────────────────────────────────
// TimeEditModal — set/edit check-in time manually
// ────────────────────────────────────────────────────────────
function TimeEditModal({ target, onClose, onSave }) {
  const open = !!target;
  const [time, setLocalTime]     = useState('');
  const [status, setLocalStatus] = useState('present');
  const [reason, setReason]      = useState('');

  useEffect(() => {
    if (target) {
      setLocalTime(target.currentTime || currentTime());
      setLocalStatus(target.currentStatus || 'present');
      setReason('');
    }
  }, [target]);

  if (!target) {
    return <UI.Modal open={false} onClose={onClose} title="" width={480}>{null}</UI.Modal>;
  }

  const isEdit = target.mode === 'edit';
  const valid = /^\d{2}:\d{2}$/.test(time);

  // Quick presets relative to scheduled time
  const presets = ['-10', '-5', '정시', '+5', '+10'];
  function applyPreset(p) {
    if (p === '정시') { setLocalTime(target.scheduledAt); return; }
    const delta = parseInt(p, 10);
    const [hh, mm] = target.scheduledAt.split(':').map(n => parseInt(n, 10));
    const total = hh * 60 + mm + delta;
    const h = String(Math.floor((total + 1440) % 1440 / 60)).padStart(2, '0');
    const m = String(((total % 60) + 60) % 60).padStart(2, '0');
    setLocalTime(`${h}:${m}`);
  }

  return (
    <UI.Modal
      open={open}
      onClose={onClose}
      title={isEdit ? '체크인 시간 수정' : '시간 지정 체크인'}
      width={480}
      footer={
        <React.Fragment>
          <UI.Button variant="ghost" onClick={onClose}>취소</UI.Button>
          <UI.Button variant="primary" icon={Icon.Check} onClick={() => valid && onSave(time, status)}>저장</UI.Button>
        </React.Fragment>
      }
    >
      <div className="col" style={{ gap: 18 }}>
        <div className="t-caption" style={{ fontSize: 13, lineHeight: 1.55 }}>
          <strong style={{ color: 'var(--text-primary)' }}>{target.name}</strong> 님 · 수업 시간 <span className="t-mono num">{target.scheduledAt}</span>
        </div>

        <div className="col" style={{ gap: 6 }}>
          <label className="t-micro" style={{ fontSize: 11 }}>체크인 시간 (HH:MM)</label>
          <div className="row" style={{ gap: 8, alignItems: 'center' }}>
            <input
              className="input t-mono num"
              value={time}
              onChange={(e) => setLocalTime(e.target.value)}
              placeholder="15:00"
              maxLength={5}
              style={{ width: 120, fontSize: 18, textAlign: 'center', letterSpacing: '0.04em' }}
            />
            <span className="t-caption" style={{ fontSize: 12 }}>
              {valid ? '24시간제로 입력해주세요' : <span style={{ color: 'var(--status-danger)' }}>HH:MM 형식으로 입력</span>}
            </span>
          </div>
          <div className="row" style={{ gap: 4, flexWrap: 'wrap', marginTop: 4 }}>
            {presets.map(p => (
              <button key={p} className="att-chip" onClick={() => applyPreset(p)}>
                {p === '정시' ? '정시' : `${p}분`}
              </button>
            ))}
          </div>
        </div>

        <div className="col" style={{ gap: 6 }}>
          <label className="t-micro" style={{ fontSize: 11 }}>상태</label>
          <div className="row" style={{ gap: 6 }}>
            {[
              { v: 'present', label: '출석' },
              { v: 'late',    label: '지각' },
            ].map(o => (
              <button
                key={o.v}
                className={`att-seg ${status === o.v ? 'is-on' : ''}`}
                onClick={() => setLocalStatus(o.v)}
              >{o.label}</button>
            ))}
          </div>
        </div>

        <div className="col" style={{ gap: 6 }}>
          <label className="t-micro" style={{ fontSize: 11 }}>메모 (선택)</label>
          <input
            className="input"
            value={reason}
            onChange={(e) => setReason(e.target.value)}
            placeholder="예: 키오스크 미사용 · 강사 수기 입력"
          />
        </div>

        {!isEdit && (
          <div className="t-caption" style={{
            fontSize: 12, background: 'var(--bg-subtle)',
            border: '1px solid var(--border-default)', borderRadius: 'var(--radius-md)',
            padding: '8px 10px',
          }}>
            패스 1회가 차감되며 등하원 알림톡이 발송됩니다.
          </div>
        )}
      </div>
    </UI.Modal>
  );
}

function methodLabel(m) {
  if (m === 'kiosk-pin') return '키오스크';
  if (m === 'kiosk-qr')  return 'QR';
  if (m === 'teacher')   return '강사 체크';
  if (m === 'auto')      return '자동';
  return '';
}

function currentTime() {
  const d = new Date();
  return `${String(d.getHours()).padStart(2,'0')}:${String(d.getMinutes()).padStart(2,'0')}`;
}

// ────────────────────────────────────────────────────────────
// Kiosk — fullscreen overlay; tap last-4 of phone → confirm member
// ────────────────────────────────────────────────────────────
function Kiosk({ open, onClose }) {
  const [pin, setPin] = useState('');
  const [step, setStep] = useState('keypad'); // 'keypad' | 'disambig' | 'confirmed' | 'notfound'
  const [candidates, setCandidates] = useState([]);
  const [confirmed, setConfirmed] = useState(null);

  useEffect(() => {
    if (!open) { setPin(''); setStep('keypad'); setCandidates([]); setConfirmed(null); }
  }, [open]);

  useEffect(() => {
    if (pin.length === 4) {
      const matches = D.kioskMatches.filter(km => km.last4 === pin);
      if (matches.length === 0)      setStep('notfound');
      else if (matches.length === 1) { setConfirmed(matches[0]); setStep('confirmed'); }
      else                           { setCandidates(matches); setStep('disambig'); }
    } else if (step !== 'keypad') {
      setStep('keypad'); setCandidates([]); setConfirmed(null);
    }
  }, [pin]);

  useEffect(() => {
    if (!open) return;
    const onKey = (e) => { if (e.key === 'Escape') onClose && onClose(); };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [open, onClose]);

  function press(d) { if (pin.length < 4) setPin(p => p + d); }
  function back()   { setPin(p => p.slice(0, -1)); }
  function reset()  { setPin(''); }
  function pick(m)  { setConfirmed(m); setStep('confirmed'); }

  // Build a curated demo set covering each branch
  const demoNumbers = useMemo(() => {
    const byLast4 = {};
    D.kioskMatches.forEach(km => { (byLast4[km.last4] = byLast4[km.last4] || []).push(km); });
    const dupKey = Object.keys(byLast4).find(k => byLast4[k].length > 1);
    const dupSet = dupKey ? byLast4[dupKey] : [];
    const singles = D.kioskMatches.filter(km => byLast4[km.last4].length === 1);
    const out = [];
    if (singles[0]) out.push({ last4: singles[0].last4, label: singles[0].name, hint: '일치 1명' });
    if (dupKey)     out.push({ last4: dupKey,           label: `동명이인 ${dupSet.length}명`, hint: '분기 화면' });
    if (singles[1]) out.push({ last4: singles[1].last4, label: singles[1].name, hint: '일치 1명' });
    out.push({ last4: '0000', label: '일치 없음', hint: '안내 화면' });
    return out;
  }, []);

  if (!open) return null;
  return (
    <div className="kiosk-overlay" onClick={onClose}>
      <div className="kiosk" onClick={(e) => e.stopPropagation()}>
        <header className="kiosk__head">
          <div className="row" style={{ gap: 10 }}>
            <span className="kiosk__brand-mark">L</span>
            <div className="col" style={{ gap: 0 }}>
              <span style={{ fontWeight: 700, color: 'var(--text-on-accent)' }}>송유이보컬발성클래스</span>
              <span style={{ fontSize: 12, color: 'rgba(245,243,238,0.6)' }}>키오스크 모드 · 수요일 {currentTime()}</span>
            </div>
          </div>
          <button onClick={onClose} className="kiosk__close" title="키오스크 종료 (학원장 PIN 필요)">
            <Icon.X size={18}/>
            <span>종료</span>
          </button>
        </header>

        <main className="kiosk__main">
          <div className="kiosk__center">
            {step === 'keypad'    && <KioskKeypad pin={pin} press={press} back={back}/>}
            {step === 'disambig'  && <KioskDisambig pin={pin} candidates={candidates} onPick={pick} onRetry={reset}/>}
            {step === 'confirmed' && confirmed && <KioskConfirmed match={confirmed} onDone={reset}/>}
            {step === 'notfound'  && <KioskNotFound pin={pin} onRetry={reset}/>}
          </div>

          {/* Demo number panel — visible on keypad step only */}
          {step === 'keypad' && (
            <aside className="kiosk-demo">
              <div className="kiosk-demo__label">
                <span className="kiosk-demo__tag">DEMO</span>
                <span>테스트할 수 있는 번호</span>
              </div>
              <div className="kiosk-demo__list">
                {demoNumbers.map(d => (
                  <button key={d.last4 + d.label} className="kiosk-demo__row" onClick={() => setPin(d.last4)}>
                    <span className="t-mono kiosk-demo__pin">{d.last4}</span>
                    <span className="kiosk-demo__meta">
                      <span className="kiosk-demo__name">{d.label}</span>
                      <span className="kiosk-demo__hint">{d.hint}</span>
                    </span>
                    <span className="kiosk-demo__chev">→</span>
                  </button>
                ))}
              </div>
              <div className="kiosk-demo__foot">
                실제 운영에서는 이 패널이 숨겨집니다.
              </div>
            </aside>
          )}
        </main>

        <footer className="kiosk__foot">
          <span>오늘 출석 <strong className="t-mono num" style={{ color: 'var(--text-on-accent)' }}>{D.attendanceToday.present}</strong>명</span>
          <span style={{ color: 'rgba(245,243,238,0.4)' }}>·</span>
          <span>지각 <strong className="t-mono num" style={{ color: 'var(--text-on-accent)' }}>{D.attendanceToday.late}</strong></span>
          <span style={{ color: 'rgba(245,243,238,0.4)' }}>·</span>
          <span>노쇼 <strong className="t-mono num" style={{ color: 'var(--text-on-accent)' }}>{D.attendanceToday.noShow}</strong></span>
          <span style={{ marginLeft: 'auto', color: 'rgba(245,243,238,0.55)' }}>전화번호 뒷 4자리 또는 강사 QR로 체크인</span>
        </footer>
      </div>
    </div>
  );
}

// Disambiguation — when ≥2 members share the last-4
function KioskDisambig({ pin, candidates, onPick, onRetry }) {
  return (
    <div className="kiosk-disambig">
      <div className="kiosk-disambig__h">
        뒷자리 <span className="t-mono num kiosk-disambig__pin">{pin}</span>로 등록된 회원이 {candidates.length}명 있습니다
      </div>
      <div className="kiosk-disambig__sub">본인을 선택해주세요</div>
      <div className="kiosk-disambig__grid">
        {candidates.map(c => {
          const t = D.getTeacher(c.teacherId);
          return (
            <button key={c.id} className="kiosk-disambig__card" onClick={() => onPick(c)}>
              <span className={`avatar is-lg ${c.tone}`}>{c.name.slice(0, 1)}</span>
              <div className="kiosk-disambig__name">{c.name}</div>
              <div className="kiosk-disambig__meta">{t?.name} 선생님</div>
              <div className="kiosk-disambig__pass t-mono num">수업권 {c.remaining}회</div>
            </button>
          );
        })}
      </div>
      <button className="kiosk-disambig__retry" onClick={onRetry}>← 번호 다시 입력</button>
    </div>
  );
}

function KioskKeypad({ pin, press, back }) {
  return (
    <div className="kiosk-keypad">
      <div className="kiosk-keypad__h">전화번호 뒷자리 4글자</div>
      <div className="kiosk-pin">
        {[0,1,2,3].map(i => (
          <span key={i} className={`kiosk-pin__cell ${pin.length > i ? 'is-on' : ''}`}>
            {pin[i] ? '●' : ''}
          </span>
        ))}
      </div>
      <div className="kiosk-pad">
        {[1,2,3,4,5,6,7,8,9].map(d => (
          <button key={d} className="kiosk-pad__k" onClick={() => press(String(d))}>{d}</button>
        ))}
        <button className="kiosk-pad__k is-ghost" disabled></button>
        <button className="kiosk-pad__k" onClick={() => press('0')}>0</button>
        <button className="kiosk-pad__k is-accent" onClick={back}>←</button>
      </div>
    </div>
  );
}

function KioskConfirmed({ match, onDone }) {
  const teacher = D.getTeacher(match.teacherId);
  return (
    <div className="kiosk-confirm">
      <div className="kiosk-confirm__icon"><Icon.Check size={40}/></div>
      <div className="kiosk-confirm__name">{match.name} 님, 환영합니다</div>
      <div className="kiosk-confirm__sub">{teacher?.name} 선생님 · 수업권 {match.remaining}회 남음</div>
      <div className="kiosk-confirm__time t-mono num">{currentTime()} 체크인</div>
      <div className="kiosk-confirm__hint">학부모님께 등원 알림톡이 발송되었습니다.</div>
      <button className="kiosk-confirm__cta" onClick={onDone}>확인</button>
    </div>
  );
}

function KioskNotFound({ pin, onRetry }) {
  return (
    <div className="kiosk-confirm" style={{ borderColor: 'var(--status-warning)' }}>
      <div className="kiosk-confirm__icon" style={{ background: 'var(--status-warning)', color: 'white' }}>
        <Icon.Warn size={36}/>
      </div>
      <div className="kiosk-confirm__name">일치하는 회원이 없습니다</div>
      <div className="kiosk-confirm__sub">뒷자리 {pin} · 데스크에 문의해주세요</div>
      <button className="kiosk-confirm__cta" onClick={onRetry}>다시 입력</button>
    </div>
  );
}

window.Attendance = { AttendancePage, AttendanceToday, Kiosk };
})();
