// src/features/admin/ui/admin-instructors-card.jsx
//
// The per-instructor card + the two side panels (earnings history,
// unsettled bookings) rendered by AdminInstructors. Extracted from
// admin-instructors.jsx for size.
//
// Depends on window for all the modals (AIAvailModal, AIEditInstructorModal,
// etc.) — those live in admin-instructors-modals.jsx and load first.

const {
  AIAssignStudentModal, AIAvailModal, AIBookLessonModal, AIConfirmDelete,
  AIEditInstructorModal, AIEditStudioModal,
} = window;

const AIAssignedStudentRow = ({ assignment }) => {
  const data = window.useAdminData();
  const [busy, setBusy] = React.useState(false);
  const [noteOpen, setNoteOpen] = React.useState(false);
  const [noteDraft, setNoteDraft] = React.useState(assignment.notes || '');
  const [noteBusy, setNoteBusy] = React.useState(false);
  const [noteMsg, setNoteMsg] = React.useState('');
  const [copied, setCopied] = React.useState(false);

  const copyMeetingUrl = () => {
    const url = assignment.meetingUrl;
    if (!url) return;
    try {
      if (navigator.clipboard && navigator.clipboard.writeText) {
        navigator.clipboard.writeText(url);
      } else {
        const ta = document.createElement('textarea');
        ta.value = url; ta.style.position = 'fixed'; ta.style.opacity = '0';
        document.body.appendChild(ta); ta.select(); document.execCommand('copy'); document.body.removeChild(ta);
      }
      setCopied(true);
      setTimeout(() => setCopied(false), 1400);
    } catch (_) { /* clipboard blocked — ignore */ }
  };
  // Keep draft in sync if the underlying assignment.notes refreshes while
  // the editor is closed (another admin saved a change in parallel, or
  // initial cache fill landed after first render).
  React.useEffect(() => {
    if (!noteOpen) setNoteDraft(assignment.notes || '');
  }, [assignment.notes, noteOpen]);

  const isHidden = !!assignment.teacherHiddenAt;
  const remove = async () => {
    const who = assignment.student?.name || 'this student';
    if (!confirm(
      `Remove ${who}'s upcoming lessons from the teacher's calendar?\n\n` +
      `Their future lessons disappear from the teacher's dashboard and stop sending reminders. ` +
      `Past lessons stay, the student stays in the roster, and nothing changes on the admin side. ` +
      `You can undo this anytime.`
    )) return;
    setBusy(true);
    try { await data.setStudentHiddenFromTeacher(assignment.id, true); }
    catch (e) { alert(e.message || 'Could not remove.'); }
    finally { setBusy(false); }
  };
  const restore = async () => {
    setBusy(true);
    try { await data.setStudentHiddenFromTeacher(assignment.id, false); }
    catch (e) { alert(e.message || 'Could not undo.'); }
    finally { setBusy(false); }
  };

  const dirty = noteDraft !== (assignment.notes || '');

  const saveNote = async () => {
    if (!dirty) { setNoteMsg(''); return; }
    setNoteBusy(true); setNoteMsg('');
    try {
      await data.updateAssignment(assignment.id, { notes: noteDraft });
      setNoteMsg('Saved.');
      setTimeout(() => setNoteMsg(''), 1800);
    } catch (e) {
      setNoteMsg('Error: ' + (e.message || 'could not save'));
    } finally { setNoteBusy(false); }
  };

  const sendNote = async () => {
    const trimmed = (noteDraft || '').trim();
    if (!trimmed) { setNoteMsg('Type a note before sending.'); return; }
    if (noteBusy) return;
    setNoteBusy(true); setNoteMsg('');
    try {
      // Persist any pending edits first so the email matches what's on screen.
      if (dirty) {
        try { await data.updateAssignment(assignment.id, { notes: noteDraft }); }
        catch (e) { setNoteMsg('Error saving: ' + (e.message || 'unknown')); return; }
      }
      await data.sendAssignmentNote(assignment.id);
      setNoteMsg('Sent.');
      setTimeout(() => setNoteMsg(''), 2200);
    } catch (e) {
      setNoteMsg('Error: ' + (e.message || 'could not send'));
    } finally { setNoteBusy(false); }
  };

  const hasNote = (assignment.notes || '').trim().length > 0;

  return (
    <div style={{ padding:'7px 0', borderBottom:'1px solid oklch(95% 0.005 60)' }}>
      <div style={{ display:'flex', justifyContent:'space-between', alignItems:'center', gap:8 }}>
        <span style={{ display:'flex', alignItems:'center', gap:7, minWidth:0 }}>
          <span style={{ fontSize:13, fontWeight:600, color: isHidden ? 'oklch(55% 0.03 265)' : 'oklch(26% 0.05 265)' }}>{assignment.student?.name || '—'}</span>
          {isHidden && (
            <span title={`Upcoming lessons hidden from the teacher's calendar since ${new Date(assignment.teacherHiddenAt).toLocaleDateString()}. The admin view is unaffected.`}
              style={{ fontSize:9.5, fontWeight:700, textTransform:'uppercase', letterSpacing:'0.04em', color:'oklch(48% 0.04 265)', background:'oklch(95% 0.01 265)', border:'1px solid oklch(89% 0.01 265)', borderRadius:5, padding:'2px 6px', whiteSpace:'nowrap' }}>
              Removed
            </span>
          )}
        </span>
        <div style={{ display:'flex', gap:6, alignItems:'center' }}>
          <button
            type="button" onClick={() => setNoteOpen(v => !v)}
            title={hasNote ? 'Edit note for teacher' : 'Add a note for teacher'}
            style={{ background: hasNote ? 'oklch(96% 0.06 80)' : 'oklch(96% 0.01 265)', color: hasNote ? 'oklch(36% 0.14 75)' : 'oklch(40% 0.04 265)', border:'1px solid ' + (hasNote ? 'oklch(86% 0.1 80)' : 'oklch(90% 0.01 265)'), borderRadius:6, padding:'4px 10px', fontSize:11, fontWeight:600, cursor:'pointer', fontFamily:"'Plus Jakarta Sans', sans-serif" }}
          >
            {hasNote ? (noteOpen ? 'Hide note' : 'Note') : (noteOpen ? 'Hide' : '+ Note')}
          </button>
          {isHidden ? (
            <button
              type="button" onClick={restore} disabled={busy}
              title="Restore this student's upcoming lessons to the teacher's calendar"
              style={{ background:'oklch(97% 0.03 150)', color:'oklch(38% 0.14 150)', border:'1px solid oklch(85% 0.08 150)', borderRadius:6, padding:'4px 10px', fontSize:11, fontWeight:600, cursor:busy?'wait':'pointer', fontFamily:"'Plus Jakarta Sans', sans-serif", opacity:busy?0.6:1 }}
            >
              {busy ? '…' : 'Restore'}
            </button>
          ) : (
            <button
              type="button" onClick={remove} disabled={busy}
              title="Remove this student's upcoming lessons from the teacher's calendar"
              style={{ background:'oklch(98% 0.04 25)', color:'oklch(40% 0.18 25)', border:'1px solid oklch(88% 0.05 25)', borderRadius:6, padding:'4px 10px', fontSize:11, fontWeight:600, cursor:busy?'wait':'pointer', fontFamily:"'Plus Jakarta Sans', sans-serif", opacity:busy?0.6:1 }}
            >
              {busy ? '…' : 'Remove'}
            </button>
          )}
        </div>
      </div>
      {/* Per-student meeting room link. Auto-set to a Google Meet room when the
          teacher is Google-connected (v126); otherwise whatever URL is on the
          assignment. Read-only here — edit it from the student's card. */}
      {assignment.meetingUrl ? (
        <div style={{ display:'flex', alignItems:'center', gap:6, marginTop:5, fontSize:11 }}>
          <span style={{ color:'oklch(58% 0.03 265)', fontWeight:600, whiteSpace:'nowrap' }}>
            {/meet\.google\.com/i.test(assignment.meetingUrl) ? 'Google Meet:' : 'Meeting link:'}
          </span>
          <a
            href={assignment.meetingUrl} target="_blank" rel="noopener noreferrer"
            title={assignment.meetingUrl}
            style={{ color:'oklch(45% 0.16 265)', fontWeight:600, textDecoration:'none', overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap', maxWidth:200 }}
          >
            {assignment.meetingUrl.replace(/^https?:\/\//, '')}
          </a>
          <button
            type="button" onClick={copyMeetingUrl}
            title="Copy link"
            style={{ background:'oklch(96% 0.01 265)', color: copied ? 'oklch(34% 0.13 150)' : 'oklch(42% 0.04 265)', border:'1px solid ' + (copied ? 'oklch(80% 0.12 150)' : 'oklch(90% 0.01 265)'), borderRadius:6, padding:'2px 8px', fontSize:10, fontWeight:700, cursor:'pointer', fontFamily:"'Plus Jakarta Sans', sans-serif", whiteSpace:'nowrap', flexShrink:0 }}
          >
            {copied ? 'Copied' : 'Copy'}
          </button>
        </div>
      ) : (
        <div style={{ marginTop:5, fontSize:10.5, color:'oklch(66% 0.02 265)', fontStyle:'italic' }}>
          No meeting room yet
        </div>
      )}
      {noteOpen && (
        <div style={{ marginTop:8, padding:'10px 12px', background:'oklch(98.5% 0.005 60)', borderRadius:8, border:'1px solid oklch(93% 0.01 265)' }}>
          <div style={{ fontSize:10, fontWeight:700, textTransform:'uppercase', letterSpacing:'0.06em', color:'oklch(52% 0.04 265)', marginBottom:6 }}>
            Note for teacher about this student
          </div>
          <textarea
            value={noteDraft}
            onChange={e => setNoteDraft(e.target.value)}
            placeholder="Level, goals, scheduling quirks, anything the teacher should know."
            rows={3}
            disabled={noteBusy}
            style={{ width:'100%', minHeight:70, padding:'8px 10px', borderRadius:6, border:'1px solid oklch(88% 0.02 265)', fontSize:12, fontFamily:"'Plus Jakarta Sans', sans-serif", color:'oklch(22% 0.06 265)', background:'#fff', outline:'none', boxSizing:'border-box', resize:'vertical' }}
          />
          <div style={{ display:'flex', gap:8, marginTop:8, alignItems:'center', flexWrap:'wrap' }}>
            <button
              type="button" onClick={saveNote}
              disabled={noteBusy || !dirty}
              style={{ background:'oklch(22% 0.06 265)', color:'#fff', border:'none', borderRadius:6, padding:'6px 12px', fontSize:11, fontWeight:600, cursor: (noteBusy || !dirty) ? 'not-allowed' : 'pointer', fontFamily:"'Plus Jakarta Sans', sans-serif", opacity: (noteBusy || !dirty) ? 0.5 : 1 }}
            >
              Save
            </button>
            <button
              type="button" onClick={sendNote}
              disabled={noteBusy || !(noteDraft || '').trim()}
              title="Email this note to the teacher now"
              style={{ background:'oklch(96% 0.06 80)', color:'oklch(36% 0.14 75)', border:'1px solid oklch(86% 0.1 80)', borderRadius:6, padding:'6px 12px', fontSize:11, fontWeight:600, cursor: (noteBusy || !(noteDraft || '').trim()) ? 'not-allowed' : 'pointer', fontFamily:"'Plus Jakarta Sans', sans-serif", opacity: (noteBusy || !(noteDraft || '').trim()) ? 0.5 : 1 }}
            >
              Send note to teacher
            </button>
            {noteMsg && (
              <span style={{ fontSize:11, fontWeight:600, color: noteMsg.startsWith('Error') ? 'oklch(40% 0.15 25)' : 'oklch(30% 0.13 150)' }}>{noteMsg}</span>
            )}
          </div>
          <div style={{ fontSize:10, color:'oklch(60% 0.03 265)', marginTop:6, fontStyle:'italic' }}>
            The first assignment email already includes the note. Use "Send note" to re-send after edits.
          </div>
        </div>
      )}
    </div>
  );
};

// (Reminder rules used to live here per-instructor; v36-schema globalised
// them into `global_reminder_rules` and the editor moved to the admin's
// Reminders tab — see admin-reminders.jsx.)

// ── Inline "see + change login email" field ───────────────────────────────────
// The login email lives in auth.users; editing it routes through
// data.updateUserEmail → /api/admin-update-email (service-role GoTrue admin
// call). The user id never changes, so bookings/assignments/etc. are
// untouched — only the login address moves. Kept local to each card file
// (the students card has a twin) so each .jsx stays standalone for the
// claude.ai paste workflow.
const AIEmailEditorRow = ({ userId, email, onSaved }) => {
  const data = window.useAdminData();
  const [editing, setEditing] = React.useState(false);
  const [draft, setDraft]     = React.useState(email || '');
  const [busy, setBusy]       = React.useState(false);
  const [msg, setMsg]         = React.useState('');
  const [isErr, setIsErr]     = React.useState(false);
  React.useEffect(() => { if (!editing) setDraft(email || ''); }, [email, editing]);

  const begin  = () => { setDraft(email || ''); setMsg(''); setIsErr(false); setEditing(true); };
  const cancel = () => { setEditing(false); setMsg(''); setIsErr(false); setDraft(email || ''); };
  const save = async () => {
    const next = (draft || '').trim().toLowerCase();
    if (next === (email || '').trim().toLowerCase()) { setEditing(false); return; }
    if (!next.includes('@') || /\s/.test(next)) { setIsErr(true); setMsg('Enter a valid email address.'); return; }
    if (!window.confirm(`Change the login email to "${next}"?\n\nThey'll use this address to log in (magic link) from now on. Lessons, payments and all other data are unaffected.`)) return;
    setBusy(true); setMsg(''); setIsErr(false);
    try {
      const saved = await data.updateUserEmail(userId, next);
      onSaved && onSaved(saved);
      setEditing(false); setIsErr(false); setMsg('Email updated');
      setTimeout(() => setMsg(''), 2400);
    } catch (e) {
      setIsErr(true); setMsg(e.message || 'Could not update email.');
    } finally { setBusy(false); }
  };

  const lbl = { fontSize:10, fontWeight:700, textTransform:'uppercase', letterSpacing:'0.06em', color:'oklch(52% 0.04 265)', marginBottom:5 };
  return (
    <div style={{ padding:'2px 0 10px' }}>
      <div style={lbl}>Login email</div>
      {!editing ? (
        <div style={{ display:'flex', alignItems:'center', gap:10, flexWrap:'wrap' }}>
          <span style={{ fontSize:13.5, fontWeight:600, color: email ? 'oklch(24% 0.05 265)' : 'oklch(64% 0.03 265)', wordBreak:'break-all' }}>
            {email || 'none on file'}
          </span>
          {userId ? (
            <button onClick={begin} style={{ background:'oklch(96% 0.04 265)', color:'oklch(28% 0.13 265)', border:'1px solid oklch(86% 0.08 265)', borderRadius:7, padding:'4px 11px', fontSize:11, fontWeight:700, cursor:'pointer', fontFamily:"'Plus Jakarta Sans', sans-serif" }}>Edit</button>
          ) : (
            <span style={{ fontSize:11, color:'oklch(64% 0.03 265)', fontStyle:'italic' }}>no account — can't edit</span>
          )}
          {msg && <span style={{ fontSize:12, fontWeight:600, color: isErr ? 'oklch(45% 0.16 25)' : 'oklch(34% 0.13 150)' }}>{msg}</span>}
        </div>
      ) : (
        <div style={{ display:'flex', alignItems:'center', gap:8, flexWrap:'wrap' }}>
          <input
            type="email" value={draft} autoFocus disabled={busy}
            onChange={e => setDraft(e.target.value)}
            onKeyDown={e => { if (e.key === 'Enter') { e.preventDefault(); save(); } if (e.key === 'Escape') cancel(); }}
            placeholder="name@example.com"
            style={{ flex:'1 1 240px', minWidth:200, padding:'7px 10px', borderRadius:7, border:'1px solid oklch(80% 0.04 265)', fontSize:13, fontFamily:"'Plus Jakarta Sans', sans-serif", color:'oklch(22% 0.05 265)', background:'#fff', outline:'none' }}
          />
          <button onClick={save} disabled={busy} style={{ background:'oklch(22% 0.06 265)', color:'#fff', border:'none', borderRadius:7, padding:'7px 14px', fontSize:12, fontWeight:700, cursor:busy?'wait':'pointer', fontFamily:"'Plus Jakarta Sans', sans-serif", opacity:busy?0.6:1 }}>{busy ? 'Saving…' : 'Save'}</button>
          <button onClick={cancel} disabled={busy} style={{ background:'oklch(96% 0.01 265)', color:'oklch(34% 0.05 265)', border:'1px solid oklch(88% 0.02 265)', borderRadius:7, padding:'7px 12px', fontSize:12, fontWeight:600, cursor:'pointer', fontFamily:"'Plus Jakarta Sans', sans-serif" }}>Cancel</button>
          {msg && isErr && <span style={{ fontSize:12, fontWeight:600, color:'oklch(45% 0.16 25)' }}>{msg}</span>}
        </div>
      )}
    </div>
  );
};

// ── Right-side drawer + collapsible "folder" ──────────────────────────────────
// Mirrors the admin Schedule detail drawer (admin-schedule-detail.jsx) — the
// "very clean" slide-in pattern Joe asked for: dim backdrop, panel slides in
// from the right, click-outside / × / Esc to close. Defined locally and
// AI-prefixed (the .jsx files share one global scope, so the students card
// carries an AS-prefixed twin) — keeps each card standalone for the claude.ai
// paste workflow.
const AIDrawer = ({ eyebrow, title, subtitle, statusChip, accentHue = 258, onClose, children }) => {
  const [shown, setShown] = React.useState(false);
  const handleClose = React.useCallback(() => { setShown(false); setTimeout(onClose, 200); }, [onClose]);
  React.useEffect(() => {
    const t = setTimeout(() => setShown(true), 10);
    const onKey = (e) => { if (e.key === 'Escape') handleClose(); };
    document.addEventListener('keydown', onKey);
    const prevOverflow = document.body.style.overflow;
    document.body.style.overflow = 'hidden';
    return () => { clearTimeout(t); document.removeEventListener('keydown', onKey); document.body.style.overflow = prevOverflow; };
  }, [handleClose]);
  return (
    <div onClick={handleClose}
      style={{ position:'fixed', inset:0, background:'rgba(15,18,40,0.35)', zIndex:1000, display:'flex', justifyContent:'flex-end' }}>
      <div onClick={e => e.stopPropagation()}
        style={{ width:'min(460px, 96vw)', height:'100%', background:'oklch(99% 0.004 60)',
          boxShadow:'-8px 0 40px rgba(0,0,0,0.18)', transform: shown ? 'translateX(0)' : 'translateX(100%)',
          transition:'transform 0.24s cubic-bezier(0.4,0,0.2,1)', overflowY:'auto',
          fontFamily:"'Plus Jakarta Sans', sans-serif", display:'flex', flexDirection:'column' }}>
        {/* Sticky header — identity only; the actions live below */}
        <div style={{ padding:'18px 22px 16px', borderBottom:'1px solid oklch(92% 0.01 265)', position:'sticky', top:0, background:'oklch(99% 0.004 60)', zIndex:2 }}>
          <div style={{ display:'flex', justifyContent:'space-between', alignItems:'flex-start', gap:12 }}>
            <div style={{ minWidth:0 }}>
              {eyebrow && <div style={{ fontSize:11, fontWeight:700, letterSpacing:'0.08em', textTransform:'uppercase', color:`oklch(58% 0.05 ${accentHue})`, marginBottom:6 }}>{eyebrow}</div>}
              <div style={{ fontFamily:"'Cormorant Garamond', serif", fontSize:24, fontWeight:700, color:'oklch(18% 0.05 265)', lineHeight:1.12, wordBreak:'break-word' }}>{title}</div>
              {subtitle && <div style={{ marginTop:6, fontSize:13, color:'oklch(46% 0.04 265)', fontWeight:600 }}>{subtitle}</div>}
              {statusChip && <div style={{ marginTop:10 }}>{statusChip}</div>}
            </div>
            <button onClick={handleClose} aria-label="Close" title="Close (Esc)"
              style={{ flexShrink:0, width:34, height:34, borderRadius:'50%', background:'oklch(95% 0.005 60)', border:'1px solid oklch(90% 0.01 265)', cursor:'pointer', fontSize:18, lineHeight:1, color:'oklch(40% 0.04 265)', display:'flex', alignItems:'center', justifyContent:'center' }}>×</button>
          </div>
        </div>
        {/* Body */}
        <div style={{ display:'flex', flexDirection:'column' }}>{children}</div>
      </div>
    </div>
  );
};

// Collapsible folder inside the drawer. Closed by default — Joe wants the
// secondary stuff "very hidden" behind dropdowns. `hint` previews a line of
// content while collapsed so a folder isn't a total black box.
const AISection = ({ title, hint, count, accent, defaultOpen = false, children }) => {
  const [open, setOpen] = React.useState(defaultOpen);
  return (
    <div style={{ borderBottom:'1px solid oklch(94% 0.006 265)' }}>
      <button type="button" onClick={() => setOpen(o => !o)}
        style={{ width:'100%', display:'flex', alignItems:'center', gap:10, padding:'14px 22px', background: open ? 'oklch(98% 0.005 265)' : '#fff', border:'none', cursor:'pointer', textAlign:'left', fontFamily:"'Plus Jakarta Sans', sans-serif" }}>
        <span style={{ fontSize:13.5, fontWeight:700, color:'oklch(26% 0.05 265)', flexShrink:0 }}>
          {title}
          {count != null && <span style={{ marginLeft:8, fontSize:12, fontWeight:700, color: accent || 'oklch(55% 0.04 265)' }}>{count}</span>}
        </span>
        {hint && !open && <span style={{ flex:1, fontSize:11.5, color:'oklch(60% 0.03 265)', fontWeight:500, overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap', textAlign:'right' }}>{hint}</span>}
        <span style={{ marginLeft:'auto', fontSize:12, color:'oklch(54% 0.04 265)', transform: open ? 'rotate(180deg)' : 'none', transition:'transform 0.15s', flexShrink:0 }}>▾</span>
      </button>
      {open && <div style={{ padding:'2px 22px 18px' }}>{children}</div>}
    </div>
  );
};

// ── Instructor Card ───────────────────────────────────────────────────────────
const AICard = ({ instructor }) => {
  const data = window.useAdminData();
  const isMobile = window.useIsMobile();
  const [rowHover, setRowHover] = React.useState(false);
  const [instructorEmail, setInstructorEmail] = React.useState('');
  const [editAvail, setEditAvail] = React.useState(false);
  const [editStudio, setEditStudio] = React.useState(false);
  const [editAll,    setEditAll]   = React.useState(false);
  const [assign,     setAssign]    = React.useState(false);
  const [book,       setBook]      = React.useState(false);
  const [del,        setDel]       = React.useState(false);
  const [expanded,  setExpanded]   = React.useState(false);
  const [verifyModal, setVerifyModal] = React.useState(false);
  const [reviewDrawer, setReviewDrawer] = React.useState(false);
  const [signupDrawer, setSignupDrawer] = React.useState(false);
  const [showBlock,    setShowBlock]   = React.useState(false);
  const [loginLinkBusy, setLoginLinkBusy] = React.useState(false);
  // Result of the most recent getImpersonateLink call. When non-null we
  // render an overlay panel with the URL + copy controls. Inline panel
  // instead of window.alert/prompt because the admin needs to SEE the
  // URL before pasting (verify the email is correct) and the modal
  // alert blocks document_idle in headless drivers, making A-to-Z
  // tests impossible.
  const [loginLinkResult, setLoginLinkResult] = React.useState(null);
  const [loginLinkError, setLoginLinkError]   = React.useState('');
  const [loginLinkCopied, setLoginLinkCopied] = React.useState(false);
  // Shown inside the URL panel when clipboard write fails — we keep the
  // URL visible so the admin can select + copy it by hand, rather than
  // discarding it by switching to the error view.
  const [loginLinkCopyHint, setLoginLinkCopyHint] = React.useState('');
  const closeLoginLinkPanel = () => {
    setLoginLinkResult(null); setLoginLinkError(''); setLoginLinkCopied(false); setLoginLinkCopyHint('');
  };

  // Mint a one-time magic-link URL for this teacher's account, show it
  // in the inline panel, and auto-copy to clipboard. Admin opens an
  // incognito window and pastes — lands logged in as the teacher for
  // real, so Messages / realtime / Join-Meeting behave authentically.
  // See api/admin-send-magic-link.js (returnLink branch) for guards.
  const handleLoginLink = async () => {
    if (!instructor.userId || loginLinkBusy) return;
    setLoginLinkBusy(true);
    setLoginLinkError('');
    setLoginLinkCopied(false);
    setLoginLinkCopyHint('');
    try {
      const { url, email } = await data.getImpersonateLink(instructor.userId);
      setLoginLinkResult({ url, email });
      // Optimistic auto-copy. Failure here is non-fatal — the panel shows
      // the URL with a manual Copy button for the rare clipboard-blocked
      // case (cross-origin iframe, denied permission).
      try {
        if (navigator.clipboard && navigator.clipboard.writeText) {
          await navigator.clipboard.writeText(url);
          setLoginLinkCopied(true);
        } else {
          setLoginLinkCopyHint('Select the URL above and copy it (Cmd+C).');
        }
      } catch (_) {
        setLoginLinkCopyHint('Select the URL above and copy it (Cmd+C).');
      }
    } catch (e) {
      setLoginLinkError(e?.message || String(e));
    } finally {
      setLoginLinkBusy(false);
    }
  };
  const copyLoginLink = async () => {
    if (!loginLinkResult) return;
    try {
      await navigator.clipboard.writeText(loginLinkResult.url);
      setLoginLinkCopied(true);
      setLoginLinkCopyHint('');
      setTimeout(() => setLoginLinkCopied(false), 1800);
    } catch (e) {
      // Clipboard blocked — do NOT switch to the error panel (that hides
      // the URL the admin can still copy by hand). Keep the URL visible
      // and prompt a manual copy.
      setLoginLinkCopyHint('Clipboard blocked — select the URL above and copy it manually (Cmd+C).');
    }
  };

  // Verification status (own row in instructor_verifications). Loaded on
  // mount + after modal actions + on realtime postgres_changes.
  const [verification, setVerification] = React.useState(null);
  const loadVerification = React.useCallback(async () => {
    if (!instructor?.id) return;
    const { data: row } = await window.supa
      .from('instructor_verifications')
      .select('status, requested_fields, requested_at, submitted_at, reviewed_at, identity_completed_at, gov_id_completed_at, address_completed_at, bank_completed_at')
      .eq('instructor_id', instructor.id).maybeSingle();
    setVerification(row || null);
  }, [instructor?.id]);
  React.useEffect(() => { loadVerification(); }, [loadVerification]);
  React.useEffect(() => {
    if (!instructor?.id) return;
    const ch = window.supa.channel(`iv-${instructor.id}`)
      .on('postgres_changes', { event:'*', schema:'public', table:'instructor_verifications', filter:`instructor_id=eq.${instructor.id}` },
          () => loadVerification())
      .subscribe();
    return () => { try { window.supa.removeChannel(ch); } catch(e) {} };
  }, [instructor?.id, loadVerification]);

  // v32: signup-progress (own row in instructor_signup_progress). Same shape
  // as the verification subscription, separate concern (v2 funnel state).
  const [signupProgress, setSignupProgress] = React.useState(null);
  const loadSignupProgress = React.useCallback(async () => {
    if (!instructor?.id) return;
    const { data: row } = await window.supa
      .from('instructor_signup_progress')
      .select('review_status, submitted_at, reviewed_at, rejection_reason')
      .eq('instructor_id', instructor.id).maybeSingle();
    setSignupProgress(row || null);
  }, [instructor?.id]);
  React.useEffect(() => { loadSignupProgress(); }, [loadSignupProgress]);
  React.useEffect(() => {
    if (!instructor?.id) return;
    const ch = window.supa.channel(`isp-${instructor.id}`)
      .on('postgres_changes', { event:'*', schema:'public', table:'instructor_signup_progress', filter:`instructor_id=eq.${instructor.id}` },
          () => loadSignupProgress())
      .subscribe();
    return () => { try { window.supa.removeChannel(ch); } catch(e) {} };
  }, [instructor?.id, loadSignupProgress]);

  // Look up the instructor's login email (auth.users) so the row can show it
  // and the expanded panel can edit it. Lazy fetch on mount — fast admin RPC.
  React.useEffect(() => {
    if (!instructor?.userId) { setInstructorEmail(''); return; }
    let cancelled = false;
    (async () => {
      try {
        const email = await data.getUserEmail(instructor.userId);
        if (!cancelled && email) setInstructorEmail(email);
      } catch (e) { /* leave blank — Edit still lets the admin set one */ }
    })();
    return () => { cancelled = true; };
  }, [instructor?.userId]);

  // Computed metrics from live admin data.
  // Bug fix 2026-05-21: previously bucketed by UTC date (slice(0,10) of
  // the raw ISO), which rolled late-evening lessons in negative-UTC zones
  // into the wrong day/month. Project to admin TZ before deriving keys.
  const adminTz = window.adminSchTz;
  const ymdInAdminTz = (iso) => {
    if (!iso) return '';
    try { return window.Calendar.time.dateKey(new Date(iso), adminTz); }
    catch (e) { return (iso || '').slice(0, 10); }
  };
  const todayStr   = ymdInAdminTz(new Date().toISOString());
  const weekEnd    = new Date(); weekEnd.setDate(weekEnd.getDate()+6);
  const weekEndStr = ymdInAdminTz(weekEnd.toISOString());
  const monthKey   = todayStr.slice(0, 7);

  const students = (data.assignments || []).filter(a => a.instructorId === instructor.id);
  const myBookings = (data.bookings || []).filter(b => b.instructorId === instructor.id);
  // Per-instructor "this month" rollup, computed the SAME way the teacher's own
  // dashboard computes "Earned this month" (shared window.computeInstructorEarnings).
  //   roll.classesThisMonth         = lessons GIVEN this month (settled or not).
  //   roll.earnedThisMonthUnsettled = payout OWED this month — drops to $0 as the
  //                                   admin settles each lesson ("Payout sent").
  const roll = window.computeInstructorEarnings(myBookings);
  const payoutThisMonth  = roll.earnedThisMonthUnsettled;
  const classesThisMonth = roll.classesThisMonth;
  const thisWeek = myBookings.filter(b => {
    const d = ymdInAdminTz(b.scheduledAt);
    return d >= todayStr && d <= weekEndStr && b.status !== 'cancelled';
  });
  // Cancelled lessons keep their `paid` flag and `teacher_rate` (we don't
  // want to silently drop money state on cancel), but they must NOT count
  // toward Earned/Cash/Payout — see BACKLOG #1.
  const earnedRaw = myBookings
    .filter(b => b.paid && b.status !== 'cancelled' && ymdInAdminTz(b.scheduledAt).slice(0,7) === monthKey)
    .reduce((s,b) => s + (Number(b.teacherRate) || 0), 0);
  const cashRaw = myBookings
    .filter(b => b.paid && b.status !== 'cancelled' && b.paymentMethod === 'cash' && !b.settled)
    .reduce((s,b) => s + (Number(b.teacherRate) || 0), 0);
  const payoutRaw = myBookings
    .filter(b => b.paid && b.status !== 'cancelled' && b.paymentMethod !== 'cash' && !b.settled)
    .reduce((s,b) => s + (Number(b.teacherRate) || 0), 0);
  // Apply admin manual adjustments on top of booking-derived totals.
  const earnedThisMonth = earnedRaw + (Number(instructor.earnedAdjustment) || 0);
  const cashCollected   = cashRaw   + (Number(instructor.cashAdjustment)   || 0);
  const pendingPayout   = payoutRaw + (Number(instructor.payoutAdjustment) || 0);

  const studio = instructor.studio || null;

  // Shared button style helpers for the action bar
  const btnPrimary  = (bg, fg, border) => ({ height:32, background:bg, color:fg, border: border || 'none', borderRadius:8, padding:'0 14px', fontSize:12, fontWeight:700, cursor:'pointer', fontFamily:"'Plus Jakarta Sans', sans-serif", whiteSpace:'nowrap', display:'inline-flex', alignItems:'center' });
  const btnSecondary = () => ({ height:32, background:'oklch(97% 0.005 265)', color:'oklch(30% 0.05 265)', border:'1px solid oklch(88% 0.02 265)', borderRadius:8, padding:'0 12px', fontSize:12, fontWeight:600, cursor:'pointer', fontFamily:"'Plus Jakarta Sans', sans-serif", whiteSpace:'nowrap', display:'inline-flex', alignItems:'center' });
  const btnDanger   = () => ({ height:32, background:'oklch(98.5% 0.03 25)', color:'oklch(42% 0.16 25)', border:'1px solid oklch(88% 0.05 25)', borderRadius:8, padding:'0 12px', fontSize:12, fontWeight:600, cursor:'pointer', fontFamily:"'Plus Jakarta Sans', sans-serif", whiteSpace:'nowrap', display:'inline-flex', alignItems:'center' });

  // Status pill for verification / signup review (contextual — only one shows at a time)
  const statusPill = (() => {
    const sp = signupProgress?.review_status;
    if (sp === 'pending_review') return (
      <button onClick={()=>setSignupDrawer(true)} title="Review submitted profile"
        style={{ ...btnPrimary('oklch(96% 0.07 80)', 'oklch(34% 0.14 80)', '1px solid oklch(86% 0.1 80)') }}>
        Review profile
      </button>
    );
    if (sp === 'rejected') return (
      <button onClick={()=>setSignupDrawer(true)} title="View rejection"
        style={{ ...btnPrimary('oklch(96% 0.05 25)', 'oklch(40% 0.15 25)', '1px solid oklch(88% 0.06 25)') }}>
        Profile rejected
      </button>
    );
    const s = verification?.status;
    const requested = verification?.requested_fields || [];
    const completed = ['identity','gov_id','address','bank'].filter(k => verification?.[`${k}_completed_at`] && requested.includes(k));
    if (!s || s === 'not_requested') return (
      <button onClick={()=>setVerifyModal(true)} title="Send a verification request to this instructor"
        style={{ ...btnSecondary() }}>
        Request verification
      </button>
    );
    if (s === 'requested' || s === 'in_progress') return (
      <button onClick={()=>setVerifyModal(true)} title="Update verification request"
        style={{ ...btnSecondary(), color:'oklch(28% 0.1 265)' }}>
        {s === 'requested' ? 'Verification requested' : `Verif. ${completed.length}/${requested.length}`}
      </button>
    );
    if (s === 'submitted') return (
      <button onClick={()=>setReviewDrawer(true)} title="Review submission"
        style={{ ...btnPrimary('oklch(96% 0.07 80)', 'oklch(34% 0.14 80)', '1px solid oklch(86% 0.1 80)') }}>
        Review verification
      </button>
    );
    if (s === 'approved') return (
      <button onClick={()=>setReviewDrawer(true)} title="View verification details"
        style={{ ...btnPrimary('oklch(94% 0.07 150)', 'oklch(30% 0.13 150)', '1px solid oklch(82% 0.1 150)') }}>
        Verified
      </button>
    );
    if (s === 'rejected') return (
      <button onClick={()=>setReviewDrawer(true)} title="View rejection details"
        style={{ ...btnPrimary('oklch(96% 0.05 25)', 'oklch(40% 0.15 25)', '1px solid oklch(88% 0.06 25)') }}>
        Verification rejected
      </button>
    );
    return null;
  })();

  const needsAttention = signupProgress?.review_status === 'pending_review' || verification?.status === 'submitted';

  return (
    <div style={{ borderBottom:'1px solid oklch(95% 0.006 265)', background:'#fff' }}>
      {/* Collapsed row — minimal Google-Sheets style: name · teaches · #students.
          No email, no rate (Joe: keep the outside row bare). Click → drawer. */}
      <div
        onClick={() => setExpanded(true)}
        onMouseEnter={() => setRowHover(true)}
        onMouseLeave={() => setRowHover(false)}
        style={{
          display:'grid',
          gridTemplateColumns: isMobile ? 'minmax(0,1fr) 76px 22px' : 'minmax(0,2.1fr) minmax(0,1.25fr) 52px 56px 80px 22px',
          alignItems:'center', gap:12, padding:'9px 16px', cursor:'pointer',
          background: rowHover ? 'oklch(98% 0.006 265)' : '#fff', transition:'background 0.12s',
        }}
      >
        {/* Name (+ sample / needs-review dot) */}
        <div style={{ display:'flex', alignItems:'center', gap:10, minWidth:0 }}>
          <div style={{ width:30, height:30, borderRadius:7, background:`oklch(72% 0.12 ${instructor.hue||258})`, display:'flex', alignItems:'center', justifyContent:'center', fontFamily:"'Plus Jakarta Sans', sans-serif", fontSize:12, fontWeight:700, color:'#fff', flexShrink:0 }}>
            {instructor.initials}
          </div>
          <span style={{ fontWeight:700, fontSize:14, color:'oklch(16% 0.04 265)', overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap' }}>{instructor.name}</span>
          {instructor.isSample && <span style={{ fontSize:9, fontWeight:700, color:'oklch(44% 0.04 265)', background:'oklch(94% 0.01 265)', padding:'1px 6px', borderRadius:4, textTransform:'uppercase', letterSpacing:'0.05em', flexShrink:0 }}>sample</span>}
          {needsAttention && <span title="Needs review" style={{ width:7, height:7, borderRadius:'50%', background:'oklch(72% 0.17 70)', flexShrink:0 }} />}
        </div>
        {/* Teaches (hidden on mobile) */}
        {!isMobile && (
          <div style={{ fontSize:12.5, color:'oklch(46% 0.04 265)', overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap' }}>
            {(instructor.topics && instructor.topics.length > 0 ? instructor.topics : [instructor.subject].filter(Boolean)).join(', ') || '—'}
          </div>
        )}
        {/* Student count (hidden on mobile) */}
        {!isMobile && (
          <div style={{ fontSize:13.5, fontWeight:700, color:'oklch(28% 0.06 265)', textAlign:'right' }}>
            {students.length}<span style={{ fontSize:11, fontWeight:500, color:'oklch(62% 0.03 265)' }}> stu</span>
          </div>
        )}
        {/* Classes given this month (hidden on mobile) */}
        {!isMobile && (
          <div title="Lessons given this month" style={{ fontSize:13.5, fontWeight:700, color: classesThisMonth > 0 ? 'oklch(28% 0.06 265)' : 'oklch(72% 0.02 265)', textAlign:'right' }}>
            {classesThisMonth}
          </div>
        )}
        {/* Payout owed this month — shown on mobile too (the number Joe scans for).
            Resets to $0 as each lesson is settled. */}
        <div title="Payout owed for this month — resets to $0 when you settle" style={{ fontSize:14, fontWeight:700, color: payoutThisMonth > 0 ? 'oklch(34% 0.13 150)' : 'oklch(72% 0.02 265)', textAlign:'right', overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap' }}>
          ${(payoutThisMonth || 0).toLocaleString()}
        </div>
        {/* Open-drawer chevron */}
        <div style={{ textAlign:'right', color:'oklch(60% 0.04 265)', fontSize:16, userSelect:'none', lineHeight:1 }}>›</div>
      </div>

      {/* Detail drawer — slides in from the right (like the Schedule drawer).
          Two primary actions up top; everything else folded into sections. */}
      {expanded && (
        <AIDrawer
          eyebrow="Instructor"
          title={instructor.name}
          subtitle={`${(instructor.topics?.length ? instructor.topics : [instructor.subject].filter(Boolean)).join(', ') || 'No topics'} · ${students.length} student${students.length === 1 ? '' : 's'} · $${instructor.rate}/hr`}
          statusChip={statusPill}
          onClose={() => setExpanded(false)}
        >
          {/* Primary actions — the two things Joe reaches for most */}
          <div style={{ padding:'16px 22px', display:'grid', gridTemplateColumns:'1fr 1fr', gap:10, borderBottom:'1px solid oklch(94% 0.006 265)' }}>
            <button onClick={()=>setAssign(true)}
              style={{ height:42, width:'100%', background:'oklch(22% 0.06 265)', color:'#fff', border:'none', borderRadius:10, fontSize:13.5, fontWeight:700, cursor:'pointer', fontFamily:"'Plus Jakarta Sans', sans-serif", display:'inline-flex', alignItems:'center', justifyContent:'center' }}>
              + Assign Student
            </button>
            <button onClick={()=>setBook(true)}
              style={{ height:42, width:'100%', background:'oklch(34% 0.13 150)', color:'#fff', border:'none', borderRadius:10, fontSize:13.5, fontWeight:700, cursor:'pointer', fontFamily:"'Plus Jakarta Sans', sans-serif", display:'inline-flex', alignItems:'center', justifyContent:'center' }}>
              + Book Lesson
            </button>
          </div>

          {/* Folder: Students + their meeting links */}
          <AISection title="Students" count={students.length}
            hint={students.length ? students.map(a=>a.student?.name?.split(' ')[0]).filter(Boolean).slice(0,3).join(', ') : 'none yet'}>
            {students.length === 0
              ? <div style={{ fontSize:13, color:'oklch(65% 0.03 265)', fontStyle:'italic', padding:'4px 0' }}>No students assigned yet. Use “+ Assign Student”.</div>
              : students.map(a => <AIAssignedStudentRow key={a.id} assignment={a} />)}
          </AISection>

          {/* Folder: Schedule & earnings */}
          <AISection title="Schedule & earnings"
            hint={`${thisWeek.length} this wk · $${earnedThisMonth.toLocaleString()}`}>
            <div style={{ display:'flex', gap:22, flexWrap:'wrap', marginBottom:16 }}>
              {[
                { label:'Students',     value: students.length },
                { label:'This week',    value: thisWeek.length },
                { label:'Earned (mo)',  value: '$'+earnedThisMonth.toLocaleString() },
                { label:'Cash to send', value: '$'+cashCollected.toLocaleString(),  alert: cashCollected > 0 },
                { label:'Payout due',   value: '$'+pendingPayout.toLocaleString() },
              ].map(m => (
                <div key={m.label}>
                  <div style={{ fontSize:10, fontWeight:700, textTransform:'uppercase', letterSpacing:'0.06em', color:'oklch(58% 0.03 265)', marginBottom:3 }}>{m.label}</div>
                  <div style={{ fontSize:15, fontWeight:700, color: m.alert ? 'oklch(38% 0.12 75)' : 'oklch(22% 0.06 265)' }}>{m.value}</div>
                </div>
              ))}
            </div>
            <div style={{ fontSize:11, fontWeight:700, textTransform:'uppercase', letterSpacing:'0.07em', color:'oklch(55% 0.03 265)', marginBottom:10 }}>This week's lessons</div>
            {thisWeek.length === 0
              ? <div style={{ fontSize:13, color:'oklch(65% 0.03 265)', fontStyle:'italic' }}>No lessons scheduled</div>
              : thisWeek.sort((a,b) => a.scheduledAt.localeCompare(b.scheduledAt)).map(l => {
                  const d = new Date(l.scheduledAt);
                  return (
                    <div key={l.id} style={{ display:'flex', justifyContent:'space-between', alignItems:'center', padding:'7px 0', borderBottom:'1px solid oklch(95% 0.005 60)', fontSize:13 }}>
                      <div>
                        <span style={{ fontWeight:600, color:'oklch(26% 0.05 265)' }}>{l.student?.name?.split(' ')[0] || '—'}</span>
                        {l.paymentMethod === 'cash' && <span style={{ marginLeft:4, fontSize:11, color:'oklch(48% 0.04 265)' }}>cash</span>}
                      </div>
                      <span style={{ color:'oklch(55% 0.03 265)' }}>{d.toLocaleDateString(undefined, { month:'short', day:'numeric' })} · {aiFmtH(d.getHours())} {window.adminSchTzAbbrev?.(d) || ''}</span>
                    </div>
                  );
                })}
            <div style={{ marginTop:18 }}>
              <window.AIEarningsHistory instructor={instructor} bookings={myBookings} />
              <window.AIUnsettledBookings instructor={instructor} bookings={myBookings} />
            </div>
          </AISection>

          {/* Folder: Settings — login email (see + edit), profile, availability, etc. */}
          <AISection title="Settings" hint={instructorEmail || 'email · availability · studio…'}>
            <AIEmailEditorRow userId={instructor.userId} email={instructorEmail} onSaved={setInstructorEmail} />
            <div style={{ padding:'4px 0 2px', fontSize:12, color: studio?.address ? 'oklch(56% 0.03 265)' : 'oklch(72% 0.02 265)', fontStyle: studio?.address ? 'normal' : 'italic' }}>
              {studio?.address ? <>Studio: {studio.address}{studio.city ? `, ${studio.city}` : ''}{studio.state ? `, ${studio.state}` : ''}</> : 'No studio address set'}
            </div>
            <div style={{ display:'flex', gap:8, flexWrap:'wrap', marginTop:12 }}>
              <button onClick={()=>setEditAll(true)} style={btnSecondary()}>Edit profile</button>
              <button onClick={()=>setEditAvail(true)} style={btnSecondary()}>Availability</button>
              <button onClick={()=>setEditStudio(true)} style={btnSecondary()}>Studio</button>
              <button onClick={()=>setShowBlock(true)} title="Block off a specific date/time exception for this teacher" style={btnSecondary()}>Block time</button>
              {instructor.userId && (
                <button onClick={() => window.open('?admin_view_instructor=' + instructor.userId, '_blank', 'noopener')} title="Open this teacher's dashboard in a new tab (read-only)" style={btnSecondary()}>View as</button>
              )}
              {instructor.userId && (
                <button onClick={handleLoginLink} disabled={loginLinkBusy}
                  title="Mint a one-time magic-link URL and copy to clipboard. Paste in an incognito window to log in as this teacher for real."
                  style={{ ...btnSecondary(), opacity: loginLinkBusy ? 0.6 : 1, cursor: loginLinkBusy ? 'wait' : 'pointer' }}>
                  {loginLinkBusy ? '…' : 'Login link'}
                </button>
              )}
            </div>
            <div style={{ marginTop:14, paddingTop:14, borderTop:'1px solid oklch(95% 0.005 60)' }}>
              <button onClick={()=>setDel(true)} style={btnDanger()}>Delete instructor</button>
            </div>
          </AISection>
        </AIDrawer>
      )}

      {editAvail  && <window.AIAvailModal           instructor={instructor} onClose={()=>setEditAvail(false)} />}
      {editStudio && <window.AIEditStudioModal      instructor={instructor} onClose={()=>setEditStudio(false)} />}
      {editAll    && <window.AIEditInstructorModal  instructor={instructor} onClose={()=>setEditAll(false)} />}
      {showBlock  && <window.BlockTimeModal  instructorId={instructor.id} ownerName={instructor.name} ownerTimezone={instructor.timezone || 'America/New_York'} onClose={()=>setShowBlock(false)} />}
      {assign     && <window.AIAssignStudentModal   instructor={instructor} onClose={()=>setAssign(false)} />}
      {book       && <window.AIBookLessonModal      instructor={instructor} onClose={()=>setBook(false)} />}
      {del        && <window.AIConfirmDelete        instructor={instructor} onClose={()=>setDel(false)} onConfirm={(typedName)=>data.deleteInstructor(instructor.id, typedName)} />}
      {verifyModal  && window.AdminVerificationModal  && <window.AdminVerificationModal  instructor={instructor} onClose={()=>setVerifyModal(false)}  onSent={loadVerification} />}
      {reviewDrawer && window.AdminVerificationReview && <window.AdminVerificationReview instructor={instructor} onClose={()=>{setReviewDrawer(false); loadVerification();}} />}
      {signupDrawer && window.AdminSignupReview && <window.AdminSignupReview instructor={instructor} onClose={()=>{setSignupDrawer(false); loadSignupProgress();}} />}

      {(loginLinkResult || loginLinkError) && (
        <div data-testid="login-link-panel" style={{ position:'fixed', inset:0, background:'rgba(15,23,42,0.45)', zIndex:1100, display:'flex', alignItems:'center', justifyContent:'center', padding:20 }}
             onClick={(e)=>{ if (e.target === e.currentTarget) closeLoginLinkPanel(); }}>
          <div style={{ background:'#fff', borderRadius:14, padding:24, maxWidth:600, width:'100%', boxShadow:'0 20px 60px rgba(15,23,42,0.35)', fontFamily:"'Plus Jakarta Sans', sans-serif" }}>
            {loginLinkError ? (
              <>
                <div style={{ fontSize:16, fontWeight:700, color:'oklch(40% 0.18 25)', marginBottom:8 }}>Could not mint login link</div>
                <div style={{ fontSize:13, color:'oklch(35% 0.04 265)', marginBottom:18, lineHeight:1.5 }}>{loginLinkError}</div>
                <div style={{ display:'flex', justifyContent:'flex-end' }}>
                  <button onClick={closeLoginLinkPanel} style={{ background:'oklch(96% 0.01 265)', border:'1px solid oklch(88% 0.02 265)', borderRadius:8, padding:'9px 16px', fontSize:13, fontWeight:600, color:'oklch(28% 0.05 265)', cursor:'pointer' }}>Close</button>
                </div>
              </>
            ) : (
              <>
                <div style={{ fontSize:16, fontWeight:700, color:'oklch(18% 0.03 265)', marginBottom:6 }}>Login link for {loginLinkResult.email}</div>
                <div style={{ fontSize:13, color:'oklch(45% 0.04 265)', marginBottom:14, lineHeight:1.5 }}>
                  Open an <b>incognito</b> Chrome window (Cmd+Shift+N) and paste this URL — you'll be signed in as this teacher for real. Same RLS, same realtime, same view. Single-use, expires in 1 hour.
                </div>
                <textarea data-testid="login-link-url" readOnly value={loginLinkResult.url}
                  onFocus={(e)=>e.target.select()}
                  style={{ width:'100%', height:90, padding:10, fontFamily:'ui-monospace, SFMono-Regular, monospace', fontSize:11, border:'1px solid oklch(88% 0.02 265)', borderRadius:8, resize:'none', background:'oklch(97% 0.005 265)', color:'oklch(28% 0.05 265)', boxSizing:'border-box' }} />
                {loginLinkCopyHint && <div style={{ fontSize:12, color:'oklch(45% 0.16 60)', fontWeight:600, marginTop:8 }}>{loginLinkCopyHint}</div>}
                <div style={{ display:'flex', gap:8, marginTop:14, justifyContent:'flex-end', alignItems:'center' }}>
                  {loginLinkCopied && <span style={{ fontSize:12, color:'oklch(45% 0.13 150)', fontWeight:600, marginRight:8 }}>Copied to clipboard</span>}
                  <button onClick={closeLoginLinkPanel} style={{ background:'oklch(96% 0.01 265)', border:'1px solid oklch(88% 0.02 265)', borderRadius:8, padding:'9px 16px', fontSize:13, fontWeight:600, color:'oklch(28% 0.05 265)', cursor:'pointer' }}>Close</button>
                  <button onClick={copyLoginLink} style={{ background:'oklch(22% 0.06 265)', color:'#fff', border:'none', borderRadius:8, padding:'9px 18px', fontSize:13, fontWeight:700, cursor:'pointer' }}>{loginLinkCopied ? 'Copied ✓' : 'Copy URL'}</button>
                </div>
              </>
            )}
          </div>
        </div>
      )}
    </div>
  );
};


// ── Page ──────────────────────────────────────────────────────────────────────


Object.assign(window, { AICard, AIAssignedStudentRow });
// AIEarningsHistory + AIUnsettledBookings now live in
// admin-instructors-card-payments.jsx.
