// src/features/instructor/ui/instructor-schedule-list.jsx
//
// List view of the instructor's bookings, organized by student.
// One of the two "view" modes mounted by InstructorPortal.
//
// Public: window.InstructorScheduleList.
//
// Depends on (via window.*): AddLessonModal, AvailabilityModal,
// LessonDetailPopup, TeacherStudentRow (all in
// instructor-dashboard-modals.jsx).

// Mon-Sun week bucket: 'this-week' | 'next-week' | 'later'
const instrWeekBucket = (iso, todayMidnight) => {
  const mon = new Date(todayMidnight); mon.setHours(0,0,0,0);
  const dow = mon.getDay(); mon.setDate(mon.getDate() - (dow === 0 ? 6 : dow - 1));
  const d = new Date(iso); d.setHours(0,0,0,0);
  if (d < new Date(+mon + 7*86400000))  return 'this-week';
  if (d < new Date(+mon + 14*86400000)) return 'next-week';
  return 'later';
};

const INSTR_BUCKET_DEFS = [
  { key:'this-week', label:'This week' },
  { key:'next-week', label:'Next week' },
  { key:'later',     label:'Later'     },
];

// Small floating panel anchored to the Settings trigger in the top nav.
// Holds the items Joe wanted off the main bar: Connect Google Calendar
// card, Add lesson, Availability, Block time, and Sign out. The Add /
// Availability / Block triggers fire the same modal setters the inline
// buttons did before — only the click target moves. Closes on outside
// click and Escape.
const InstructorSettingsMenu = ({
  canAddLesson, onAddLesson, onAvailability, onBlockTime, onSignOut, instructorId,
}) => {
  const [open, setOpen] = React.useState(false);
  const wrapRef = React.useRef(null);
  const firstItemRef = React.useRef(null);

  React.useEffect(() => {
    if (!open) return undefined;
    // Move focus to the first menuitem so keyboard users can navigate
    // immediately rather than tabbing through the GcalCard.
    if (firstItemRef.current) firstItemRef.current.focus();
    const onPointer = (e) => {
      if (wrapRef.current && !wrapRef.current.contains(e.target)) setOpen(false);
    };
    // Escape closes; Arrow keys + Home/End cycle focus through menuitems per
    // the WAI-ARIA menu button pattern.
    const onKey = (e) => {
      if (e.key === 'Escape') { setOpen(false); return; }
      if (!['ArrowDown', 'ArrowUp', 'Home', 'End'].includes(e.key)) return;
      // Don't hijack arrow/Home/End while the user is typing in or navigating
      // a form control inside the menu (e.g. the Phone number country picker
      // and input). Only the menuitem buttons participate in roving focus.
      const t = e.target;
      if (t && /^(INPUT|SELECT|TEXTAREA)$/.test(t.tagName)) return;
      const menu = wrapRef.current && wrapRef.current.querySelector('[role="menu"]');
      if (!menu) return;
      const items = Array.from(menu.querySelectorAll('[role="menuitem"]'));
      if (!items.length) return;
      e.preventDefault();
      const idx = items.indexOf(document.activeElement);
      if (e.key === 'ArrowDown')      items[(idx + 1) % items.length].focus();
      else if (e.key === 'ArrowUp')   items[(idx - 1 + items.length) % items.length].focus();
      else if (e.key === 'Home')      items[0].focus();
      else if (e.key === 'End')       items[items.length - 1].focus();
    };
    document.addEventListener('mousedown', onPointer);
    document.addEventListener('touchstart', onPointer);
    document.addEventListener('keydown', onKey);
    return () => {
      document.removeEventListener('mousedown', onPointer);
      document.removeEventListener('touchstart', onPointer);
      document.removeEventListener('keydown', onKey);
    };
  }, [open]);

  const run = (fn) => () => { setOpen(false); fn && fn(); };

  const itemStyle = {
    display:'block', width:'100%', textAlign:'left',
    background:'none', border:'none', cursor:'pointer',
    padding:'10px 12px', borderRadius:8, fontSize:14, fontWeight:600,
    color:'oklch(22% 0.06 265)', fontFamily:"'Plus Jakarta Sans', sans-serif",
  };
  const hoverIn  = e => { e.currentTarget.style.background = 'oklch(96% 0.01 265)'; };
  const hoverOut = e => { e.currentTarget.style.background = 'none'; };

  const GcalCard = window.InstructorCalendarSync && window.InstructorCalendarSync.Card;

  return (
    <div ref={wrapRef} style={{ position:'relative' }}>
      <button
        type="button"
        onClick={() => setOpen(o => !o)}
        aria-haspopup="menu"
        aria-expanded={open}
        title="Settings"
        style={{
          background:'oklch(96.5% 0.007 265)', color:'oklch(38% 0.04 265)',
          border:'1px solid oklch(90% 0.01 265)', borderRadius:8,
          padding:'8px 14px', fontSize:13, fontWeight:500, cursor:'pointer',
          fontFamily:"'Plus Jakarta Sans', sans-serif", whiteSpace:'nowrap',
          display:'inline-flex', alignItems:'center', gap:6,
        }}
      >
        Settings
        <span aria-hidden="true" style={{ fontSize:9, lineHeight:1, transform: open ? 'rotate(180deg)' : 'rotate(0deg)', transition:'transform 0.15s' }}>▼</span>
      </button>
      {open && (
        <div
          role="menu"
          aria-label="Settings"
          style={{
            position:'absolute', top:'calc(100% + 6px)', right:0,
            minWidth:260, maxWidth:'calc(100vw - 24px)',
            maxHeight:'calc(100vh - 120px)', overflowY:'auto',
            background:'#fff', border:'1px solid oklch(90% 0.01 265)',
            borderRadius:12, boxShadow:'0 12px 32px oklch(18% 0.06 265 / 0.14)',
            padding:10, zIndex:1000,
          }}
        >
          {GcalCard && (
            <div role="presentation" style={{ marginBottom:6 }}>
              <GcalCard />
            </div>
          )}
          {window.MaskedCalls?.ui?.InstructorDestinationSetting && instructorId && (
            <div role="presentation" style={{ marginBottom:6 }}>
              <window.MaskedCalls.ui.InstructorDestinationSetting instructorId={instructorId} />
            </div>
          )}
          {canAddLesson && (
            <button ref={firstItemRef} role="menuitem" type="button" onClick={run(onAddLesson)} onMouseEnter={hoverIn} onMouseLeave={hoverOut} style={itemStyle}>
              + Add lesson
            </button>
          )}
          <button ref={canAddLesson ? null : firstItemRef} role="menuitem" type="button" onClick={run(onAvailability)} onMouseEnter={hoverIn} onMouseLeave={hoverOut} style={itemStyle}>
            Availability
          </button>
          <button role="menuitem" type="button" onClick={run(onBlockTime)} onMouseEnter={hoverIn} onMouseLeave={hoverOut} style={itemStyle}>
            Block time
          </button>
          <div role="separator" style={{ height:1, background:'oklch(92% 0.01 265)', margin:'8px 4px' }} />
          <button role="menuitem" type="button" onClick={run(onSignOut)} onMouseEnter={hoverIn} onMouseLeave={hoverOut} style={{ ...itemStyle, color:'oklch(40% 0.1 25)' }}>
            Sign out
          </button>
        </div>
      )}
    </div>
  );
};

const InstructorScheduleList = ({ onBack, setPage }) => {
  const data = window.useDashboardData();
  const auth = window.useAuth();
  const [showAdd,        setShowAdd]        = React.useState(false);
  const [showAvail,      setShowAvail]      = React.useState(false);
  const [showBlock,      setShowBlock]      = React.useState(false);
  const [activeBooking,  setActiveBooking]  = React.useState(null);
  const [showPast,       setShowPast]       = React.useState(false);
  const [showLater,      setShowLater]      = React.useState(false);
  const [showStudents,   setShowStudents]   = React.useState(false);
  const [toast,          setToast]          = React.useState('');
  const [verification,   setVerification]   = React.useState(null);
  const [bannerDismissed, setBannerDismissed] = React.useState(() => {
    try { return sessionStorage.getItem('mastery_verify_banner_dismissed') === '1'; } catch(e) { return false; }
  });
  // T&C banner state — admin-published terms versions the teacher hasn't
  // agreed to yet (v62 schema). Modal opens via state below so dismissing
  // it without clicking "I agree" leaves the banner up.
  const termsBanner = window.useTeacherTermsBanner ? window.useTeacherTermsBanner() : { pending: null, agree: async () => {} };
  const [showTermsModal, setShowTermsModal] = React.useState(false);

  const instructor = data.teacherProfile;
  const earnings   = data.earnings() || { earnedThisMonth: 0, earnedThisMonthUnsettled: 0, owedToPlatform: 0, pendingPayout: 0 };
  const availMap   = React.useMemo(() => availabilityToMockMap(data.teacherAvailability), [data.teacherAvailability]);

  // Live-refresh strategy: poll teacherBookings + availability every 8s
  // while the dashboard is mounted. We tried Realtime postgres_changes
  // events first (still wired in logic.js as a best-effort), but the
  // events are RLS-aware and don't reliably fire for every authed session
  // — leading to "I booked a lesson, why isn't it on the dashboard?" bugs.
  // Polling is ugly but bulletproof: 8s is the latency-floor Joe asked
  // for ("show immediately, not wait for me to reload"), and the
  // tables are tiny so the cost is negligible.
  //
  // Test bot caught this — scenarios 04 + 05 fail any time the live
  // refresh doesn't actually deliver new rows within ~15s.
  React.useEffect(() => {
    if (!instructor?.id) return;
    const tick = () => { if (data?.refresh) data.refresh(); };
    // First catch-up runs immediately so a write that landed during page
    // load (eg. realtime handshake window) doesn't have to wait 8s.
    tick();
    const id = setInterval(tick, 8000);
    return () => clearInterval(id);
  }, [instructor?.id]);

  // v32 — signup-progress (v2 funnel review state). Same subscribe pattern.
  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, rejection_reason, submitted_at, reviewed_at')
      .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-inst-${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]);

  // ─ Verification status. Loads once + subscribes to realtime so the
  // banner / card flip immediately when the admin requests or approves. ─
  const loadVerification = React.useCallback(async () => {
    if (!instructor?.id) return;
    const { data: row } = await window.supa
      .from('instructor_verifications')
      .select('status, requested_fields, admin_message, identity_completed_at, gov_id_completed_at, address_completed_at, bank_completed_at, rejection_reason')
      .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-inst-${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]);

  const goVerify = () => {
    if (setPage) setPage('verify');
    else window.location.assign('?page=verify');
  };
  const dismissBanner = () => {
    setBannerDismissed(true);
    try { sessionStorage.setItem('mastery_verify_banner_dismissed', '1'); } catch(e) {}
  };

  const showToast = msg => { setToast(msg); setTimeout(() => setToast(''), 2500); };

  const addLesson = async (form) => {
    await data.createBookingAsTeacher(form);
  };
  const addSeries = async (form) => {
    await window.supa.rpc('instructor_create_series', {
      p_student_id: form.studentId, p_first_at: form.firstAt,
      p_duration: form.durationMinutes, p_weeks: form.weeks, p_message: form.message,
      p_infinite: !!form.infinite,
    });
    await data.refresh();
  };

  const updateBooking = async (id, patch) => {
    const { data: row, error } = await window.supa.from('bookings').update(patch).eq('id', id)
      .select('*, student:profiles(*)').single();
    if (error) { alert(error.message); return; }
    await data.refresh();
    setActiveBooking(null);
  };

  // Split upcoming vs past, sort upcoming asc / past desc.
  const today = new Date(); today.setHours(0,0,0,0);
  const tomorrow = new Date(today); tomorrow.setDate(today.getDate() + 1);
  const allBookings = (data.teacherBookings || []).filter(b => b.status !== 'cancelled');
  const upcoming = allBookings
    .filter(b => new Date(b.scheduledAt) >= today)
    .sort((a, b) => new Date(a.scheduledAt) - new Date(b.scheduledAt));
  const past = allBookings
    .filter(b => new Date(b.scheduledAt) < today)
    .sort((a, b) => new Date(b.scheduledAt) - new Date(a.scheduledAt));

  // Today's lessons surface separately at the top — Joe's request.
  const todaysLessons = upcoming.filter(b => {
    const d = new Date(b.scheduledAt);
    return d >= today && d < tomorrow;
  });
  const laterUpcoming = upcoming.filter(b => new Date(b.scheduledAt) >= tomorrow);

  const upcomingBuckets = INSTR_BUCKET_DEFS.reduce((acc, { key, label }) => {
    const items = laterUpcoming.filter(b => instrWeekBucket(b.scheduledAt, today) === key);
    if (items.length) acc.push({ key, label, items });
    return acc;
  }, []);

  // Joe's request: the dashboard shouldn't be a wall of every future lesson.
  // Show Today + This week + Next week inline; fold everything beyond next
  // week (the 'later' bucket) into a closed-by-default disclosure.
  const visibleBuckets = upcomingBuckets.filter(b => b.key !== 'later');
  const laterBucket    = upcomingBuckets.find(b => b.key === 'later') || null;
  const visibleCount   = visibleBuckets.reduce((n, b) => n + b.items.length, 0);

  const fmtDayLabel = (iso) => {
    const d = new Date(iso);
    const dStart = new Date(d); dStart.setHours(0,0,0,0);
    const days = Math.round((dStart - today) / 86400000);
    if (days === 0) return 'Today';
    if (days === 1) return 'Tomorrow';
    if (days === -1) return 'Yesterday';
    return `${INST_DAYS[(d.getDay()+6)%7]} · ${INST_MONTHS[d.getMonth()]} ${d.getDate()}`;
  };

  const btnStyle = (primary) => ({
    background: primary ? 'oklch(22% 0.06 265)' : 'oklch(96.5% 0.007 265)',
    color:      primary ? '#fff'                 : 'oklch(38% 0.04 265)',
    border:     primary ? 'none'                 : '1px solid oklch(90% 0.01 265)',
    borderRadius:8, padding:'8px 16px', fontSize:13, fontWeight: primary ? 600 : 500,
    cursor:'pointer', fontFamily:"'Plus Jakarta Sans', sans-serif", whiteSpace:'nowrap',
  });

  // Consistent visual treatment across Today / Upcoming / Students sections:
  // a coloured-dot eyebrow + section heading + count chip. Tightens vertical
  // rhythm vs. the old per-section duplicated headers.
  const SectionBlock = ({ label, count, accent, children }) => (
    <section style={{ marginTop:28 }}>
      <div style={{ display:'flex', alignItems:'baseline', gap:10, marginBottom:14, flexWrap:'wrap' }}>
        <div style={{ display:'flex', alignItems:'center', gap:8 }}>
          <span style={{ width:6, height:6, borderRadius:'50%', background: accent || 'oklch(72% 0.07 265)', display:'inline-block' }} />
          <h2 style={{ fontFamily:"'Cormorant Garamond', serif", fontSize:'clamp(20px, 4.5vw, 22px)', fontWeight:600, color:'oklch(18% 0.03 265)', margin:0, lineHeight:1.15 }}>{label}</h2>
        </div>
        {typeof count === 'number' && (
          <span style={{ fontSize:11, fontWeight:700, padding:'2px 10px', borderRadius:20, background:'oklch(96% 0.01 265)', color:'oklch(48% 0.04 265)' }}>{count}</span>
        )}
      </div>
      {children}
    </section>
  );

  const LessonRow = ({ b, dim, hideDate }) => {
    const isTr = /trial/i.test(b.message || '');
    const d = new Date(b.scheduledAt);
    const joinUrl = (() => {
      try {
        return window.Meetings?.db?.resolveForBooking?.({ studentId: b.studentId, instructorId: b.instructorId, meetingLink: b.meetingLink }, data.teacherAssignments) || null;
      } catch (e) { return null; }
    })();
    return (
      <div onClick={() => setActiveBooking(b)} style={{
        display:'flex', alignItems:'center', gap:14, padding:'14px 16px',
        borderRadius:12, border:'1px solid oklch(92% 0.01 265)',
        background:'#fff', cursor:'pointer', transition:'border-color 0.12s, box-shadow 0.12s',
        opacity: dim ? 0.7 : 1,
        minHeight:64,
      }}
        onMouseEnter={e => { e.currentTarget.style.borderColor='oklch(78% 0.04 265)'; e.currentTarget.style.boxShadow='0 4px 14px oklch(18% 0.06 265 / 0.08)'; }}
        onMouseLeave={e => { e.currentTarget.style.borderColor='oklch(92% 0.01 265)'; e.currentTarget.style.boxShadow='none'; }}
      >
        {!hideDate && (
          <>
            <div style={{ width:44, textAlign:'center', flexShrink:0 }}>
              <div style={{ fontSize:10, fontWeight:700, color:'oklch(58% 0.03 265)', textTransform:'uppercase', letterSpacing:'0.07em' }}>{INST_MONTHS[d.getMonth()]}</div>
              <div style={{ fontFamily:"'Cormorant Garamond', serif", fontSize:24, fontWeight:700, color:'oklch(22% 0.06 265)', lineHeight:1, marginTop:2 }}>{d.getDate()}</div>
            </div>
            <div style={{ width:1, alignSelf:'stretch', background:'oklch(94% 0.005 265)', flexShrink:0 }} />
          </>
        )}
        {hideDate && (
          <div style={{ width:44, height:44, borderRadius:'50%', background:'oklch(95% 0.04 265)', color:'oklch(28% 0.1 265)', display:'flex', alignItems:'center', justifyContent:'center', flexShrink:0 }}>
            <div style={{ fontFamily:"'Cormorant Garamond', serif", fontSize:18, fontWeight:700, lineHeight:1 }}>{instH(d.getHours()).split(' ')[0]}</div>
          </div>
        )}
        <div style={{ flex:1, minWidth:0 }}>
          <div style={{ fontSize:14.5, fontWeight:700, color:'oklch(20% 0.04 265)', overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap' }}>
            {b.student?.name || '—'}
            {isTr && <span style={{ marginLeft:8, fontSize:10, fontWeight:700, padding:'2px 8px', borderRadius:20, background:'oklch(95% 0.13 80)', color:'oklch(36% 0.14 75)', verticalAlign:'middle' }}>TRIAL</span>}
          </div>
          <div style={{ fontSize:12.5, color:'oklch(54% 0.03 265)', marginTop:4, overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap' }}>
            {hideDate ? `${instH(d.getHours())} · ${b.durationMinutes} min` : `${fmtDayLabel(b.scheduledAt)} · ${instH(d.getHours())} · ${b.durationMinutes} min`}
          </div>
        </div>
        <div style={{ display:'flex', alignItems:'center', gap:6, flexShrink:1, flexWrap:'wrap', justifyContent:'flex-end', minWidth:0 }} onClick={e => e.stopPropagation()}>
          {joinUrl && <window.Meetings.ui.JoinButton booking={{ id: b.id, scheduled_at: b.scheduledAt }} meetingUrl={joinUrl} compact />}
          {b.rescheduleRequestedAt && (
            <span title="Reschedule pending" style={{ fontSize:11, fontWeight:700, padding:'4px 10px', borderRadius:14, background:'oklch(95% 0.13 80)', color:'oklch(36% 0.14 75)', whiteSpace:'nowrap' }}>↻ pending</span>
          )}
          {b.status === 'completed' && (
            b.noShow
              ? <span style={{ fontSize:11, fontWeight:700, padding:'4px 10px', borderRadius:14, background:'oklch(93% 0.015 265)', color:'oklch(46% 0.03 265)', whiteSpace:'nowrap' }}>Absent</span>
              : <span style={{ fontSize:11, fontWeight:700, padding:'4px 10px', borderRadius:14, background:'oklch(94% 0.07 150)', color:'oklch(30% 0.13 150)', whiteSpace:'nowrap' }}>completed</span>
          )}
        </div>
      </div>
    );
  };

  return (
    <div style={{ display:'flex', flexDirection:'column', minHeight:'100vh', fontFamily:"'Plus Jakarta Sans', sans-serif", background:'oklch(98.5% 0.007 60)' }}>

      {/* Header — two rows on mobile, single row on desktop.
          flexWrap allows graceful collapse without scroll. */}
      <div style={{ display:'flex', alignItems:'center', gap:10, padding:'10px 16px', minHeight:60, borderBottom:'1px solid oklch(92% 0.01 265)', background:'#fff', flexShrink:0, flexWrap:'wrap', rowGap:8 }}>
        <div style={{ display:'flex', alignItems:'center', gap:10, flex:'1 1 auto', minWidth:0 }}>
          <div style={{ fontFamily:"'Cormorant Garamond', serif", fontSize:18, fontWeight:700, letterSpacing:'0.12em', color:'oklch(22% 0.06 265)', flexShrink:0 }}>COACHING</div>
          <div style={{ width:1, height:20, background:'oklch(91% 0.01 265)', flexShrink:0 }} />
          <div style={{ fontSize:13, fontWeight:600, color:'oklch(28% 0.05 265)', minWidth:0, overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap' }}>{instructor?.full_name || '—'}</div>
        </div>
        <div style={{ display:'flex', alignItems:'center', gap:10, flex:'1 1 auto', flexWrap:'wrap', justifyContent:'flex-end', minWidth:0 }}>
          <window.MessagesNavButton />
          <InstructorSettingsMenu
            canAddLesson={(data.teacherAssignments || []).length > 0}
            onAddLesson={() => setShowAdd(true)}
            onAvailability={() => setShowAvail(true)}
            onBlockTime={() => setShowBlock(true)}
            onSignOut={onBack}
            instructorId={instructor?.id}
          />
        </div>
      </div>

      {/* Verification / signup-progress / availability banners — extracted
          to instructor-schedule-banners.jsx so this file stays under the
          400 LOC budget. */}
      <window.InstructorScheduleBanners
        verification={verification}
        bannerDismissed={bannerDismissed}
        onDismissBanner={dismissBanner}
        onGoVerify={goVerify}
        signupProgress={signupProgress}
        setPage={setPage}
        availMap={availMap}
        onAvailClick={() => setShowAvail(true)}
        termsPending={termsBanner.pending}
        onOpenTerms={() => setShowTermsModal(true)}
      />
      {showTermsModal && termsBanner.pending && window.TeacherTermsModal && (
        <window.TeacherTermsModal
          pending={termsBanner.pending}
          onAgree={termsBanner.agree}
          onClose={() => setShowTermsModal(false)}
        />
      )}

      {/* Main content. Padding scales with viewport: 16px sides on mobile,
          24px on tablet+. maxWidth:760 keeps the column readable on desktop. */}
      <div style={{ flex:1, padding:'24px clamp(12px, 4vw, 24px) 100px', maxWidth:760, width:'100%', margin:'0 auto', boxSizing:'border-box' }}>

        {/* Page eyebrow + title */}
        <div style={{ marginBottom:6, fontSize:11, fontWeight:700, letterSpacing:'0.1em', color:'oklch(72% 0.17 80)', textTransform:'uppercase' }}>Your schedule</div>
        <div style={{ fontFamily:"'Cormorant Garamond', serif", fontSize:'clamp(26px, 6vw, 32px)', fontWeight:600, color:'oklch(18% 0.03 265)', marginBottom:24, lineHeight:1.15 }}>Next classes</div>

        {/* Today's lessons */}
        {todaysLessons.length > 0 && (
          <SectionBlock label="Today" count={todaysLessons.length} accent="oklch(72% 0.17 80)">
            <div style={{ display:'flex', flexDirection:'column', gap:10 }}>
              {todaysLessons.map(b => <LessonRow key={b.id} b={b} hideDate />)}
            </div>
          </SectionBlock>
        )}

        {/* Upcoming lessons. Today + This week + Next week show inline;
            everything beyond next week folds into a closed-by-default
            "Later" disclosure (mirrors the Past-lessons one below) so the
            page doesn't become a wall of every future booking. */}
        {upcoming.length === 0 ? (
          <div style={{ background:'#fff', borderRadius:14, border:'1px solid oklch(92% 0.01 265)', padding:'40px 24px', textAlign:'center', color:'oklch(58% 0.03 265)' }}>
            {(data.teacherAssignments || []).length > 0 ? (
              <>
                <div style={{ fontSize:14, marginBottom:14 }}>No upcoming lessons.</div>
                <button onClick={() => setShowAdd(true)} style={{ ...btnStyle(true), padding:'10px 22px' }}>+ Add a lesson</button>
              </>
            ) : (
              <div style={{ fontSize:14, lineHeight:1.6 }}>
                You don't have any assigned students yet.<br />
                Your admin will assign students to you — we'll email you when they do.
              </div>
            )}
          </div>
        ) : (
          <>
            {visibleBuckets.length > 0 && (
              <SectionBlock label="Upcoming" count={visibleCount}>
                <div style={{ display:'flex', flexDirection:'column', gap:18 }}>
                  {visibleBuckets.map(bucket => (
                    <div key={bucket.key}>
                      <div style={{
                        fontSize:11, fontWeight:700, textTransform:'uppercase',
                        letterSpacing:'0.09em', color:'oklch(62% 0.03 265)',
                        marginBottom:8,
                      }}>{bucket.label}</div>
                      <div style={{ display:'flex', flexDirection:'column', gap:10 }}>
                        {bucket.items.map(b => <LessonRow key={b.id} b={b} />)}
                      </div>
                    </div>
                  ))}
                </div>
              </SectionBlock>
            )}

            {/* Later — everything past next week, collapsed by default. */}
            {laterBucket && (
              <div style={{ marginTop: visibleBuckets.length > 0 ? 18 : 28 }}>
                <button onClick={() => setShowLater(p => !p)} style={{
                  background:'none', border:'none', cursor:'pointer', padding:0,
                  display:'flex', alignItems:'center', gap:8,
                  fontSize:13, fontWeight:600, color:'oklch(48% 0.04 265)',
                  fontFamily:"'Plus Jakarta Sans', sans-serif", marginBottom: showLater ? 14 : 0,
                }}>
                  <span style={{ fontSize:11, transform: showLater ? 'rotate(90deg)' : 'rotate(0deg)', transition:'transform 0.15s', display:'inline-block' }}>▶</span>
                  Later ({laterBucket.items.length})
                </button>
                {showLater && (
                  <div style={{ display:'flex', flexDirection:'column', gap:10 }}>
                    {laterBucket.items.map(b => <LessonRow key={b.id} b={b} />)}
                  </div>
                )}
              </div>
            )}
          </>
        )}

        {/* Students — collapsed into a minimal disclosure (closed by default),
            same treatment as Later / Past, so the page stays focused on what's
            next instead of listing every assigned student. */}
        {(data.teacherAssignments || []).length > 0 && (
          <div style={{ marginTop:32 }}>
            <button onClick={() => setShowStudents(p => !p)} style={{
              background:'none', border:'none', cursor:'pointer', padding:0,
              display:'flex', alignItems:'center', gap:8,
              fontSize:13, fontWeight:600, color:'oklch(48% 0.04 265)',
              fontFamily:"'Plus Jakarta Sans', sans-serif", marginBottom: showStudents ? 14 : 0,
            }}>
              <span style={{ fontSize:11, transform: showStudents ? 'rotate(90deg)' : 'rotate(0deg)', transition:'transform 0.15s', display:'inline-block' }}>▶</span>
              Students ({(data.teacherAssignments || []).length})
            </button>
            {showStudents && (
              <div style={{ display:'flex', flexDirection:'column', gap:8 }}>
                {(data.teacherAssignments || []).map(a => {
                  const sBookings = (data.teacherBookings || []).filter(b => b.studentId === a.studentId);
                  const sUpcoming = sBookings
                    .filter(b => b.status !== 'cancelled' && new Date(b.scheduledAt) >= new Date(new Date().setHours(0,0,0,0)))
                    .sort((x,y) => new Date(x.scheduledAt) - new Date(y.scheduledAt));
                  const next = sUpcoming[0];
                  const noteRows = sBookings.filter(b => b.teacherNotes && b.teacherNotes.trim().length > 0)
                    .sort((x,y) => new Date(y.scheduledAt) - new Date(x.scheduledAt));
                  return (
                    <window.TeacherStudentRow key={a.studentId}
                      name={a.student?.name || '—'}
                      nextLesson={next}
                      notes={noteRows}
                      adminNote={a.notes || ''}
                    />
                  );
                })}
              </div>
            )}
          </div>
        )}

        {/* Past — collapsible so the page stays focused on what's next */}
        {past.length > 0 && (
          <div style={{ marginTop:32 }}>
            <button onClick={() => setShowPast(p => !p)} style={{
              background:'none', border:'none', cursor:'pointer', padding:0,
              display:'flex', alignItems:'center', gap:8,
              fontSize:13, fontWeight:600, color:'oklch(48% 0.04 265)',
              fontFamily:"'Plus Jakarta Sans', sans-serif", marginBottom: showPast ? 14 : 0,
            }}>
              <span style={{ fontSize:11, transform: showPast ? 'rotate(90deg)' : 'rotate(0deg)', transition:'transform 0.15s', display:'inline-block' }}>▶</span>
              Past lessons ({past.length})
            </button>
            {showPast && (
              <div style={{ display:'flex', flexDirection:'column', gap:10 }}>
                {past.slice(0, 30).map(b => <LessonRow key={b.id} b={b} dim />)}
                {past.length > 30 && <div style={{ fontSize:12, color:'oklch(60% 0.03 265)', textAlign:'center', padding:8 }}>… {past.length - 30} more</div>}
              </div>
            )}
          </div>
        )}
      </div>

      {/* Earnings strip (sticky bottom) — teacher only sees this month */}
      <div style={{ position:'sticky', bottom:0, borderTop:'1px solid oklch(92% 0.01 265)', padding:'12px clamp(16px, 4vw, 26px)', display:'flex', gap:24, alignItems:'center', background:'#fff', flexShrink:0, boxShadow:'0 -8px 20px rgba(0,0,0,0.04)' }}>
        <div>
          <div style={{ fontSize:10, fontWeight:700, textTransform:'uppercase', letterSpacing:'0.08em', color:'oklch(62% 0.03 265)', marginBottom:2 }}>Earned this month</div>
          <div style={{ fontFamily:"'Cormorant Garamond', serif", fontSize:'clamp(20px, 5vw, 22px)', fontWeight:700, color:'oklch(22% 0.06 265)', lineHeight:1.1 }}>{instFmt$(earnings.earnedThisMonthUnsettled)}</div>
        </div>
      </div>

      {toast && (
        <div style={{ position:'fixed', bottom:90, left:'50%', transform:'translateX(-50%)', background:'oklch(18% 0.06 265)', color:'#fff', padding:'10px 22px', borderRadius:10, fontSize:13, fontWeight:600, zIndex:9999, boxShadow:'0 8px 24px rgba(0,0,0,0.2)', maxWidth:'calc(100vw - 32px)', textAlign:'center' }}>
          {toast}
        </div>
      )}

      {showAdd && (
        <window.AddLessonModal
          assignments={data.teacherAssignments || []}
          defaultDate={localDateStr(new Date())}
          defaultHour={10}
          onAdd={addLesson}
          onAddSeries={addSeries}
          onClose={() => setShowAdd(false)}
        />
      )}
      {showAvail && (
        <window.AvailabilityModal
          initialAvail={availMap}
          onSave={(rows) => data.saveAvailability(rows)}
          onClose={() => setShowAvail(false)}
        />
      )}
      {showBlock && instructor && (
        <window.BlockTimeModal
          instructorId={instructor.id}
          ownerName={instructor.full_name}
          ownerTimezone={auth.profile?.timezone || 'America/New_York'}
          onClose={() => setShowBlock(false)}
        />
      )}
      {activeBooking && (
        <window.LessonDetailPopup
          booking={activeBooking}
          onMarkComplete={() => updateBooking(activeBooking.id, { status: 'completed' })}
          onCancel={() => updateBooking(activeBooking.id, { status: 'cancelled' })}
          onMarkPaid={() => updateBooking(activeBooking.id, { paid: true })}
          onApproveReschedule={async () => {
            try { await data.approveReschedule(activeBooking.id); showToast('Reschedule approved'); setActiveBooking(null); }
            catch (e) { alert(e.message); }
          }}
          onDeclineReschedule={async () => {
            try { await data.declineReschedule(activeBooking.id); showToast('Reschedule declined'); setActiveBooking(null); }
            catch (e) { alert(e.message); }
          }}
          onSaveNotes={async (id, notes, complete, noShow) => {
            await data.saveLessonNotes(id, notes, complete, noShow);
            if (noShow)        { showToast('Marked absent');         setActiveBooking(null); }
            else if (complete) { showToast('Lesson marked complete'); setActiveBooking(null); }
          }}
          onClose={() => setActiveBooking(null)}
        />
      )}
    </div>
  );
};


window.InstructorScheduleList = InstructorScheduleList;
