/* ============================================================
   Panel de producción — diseño 100% del ZIP, datos de Firebase
   ============================================================ */

const LOGO = '/assets/guitarfabrik-logo.png';

// ── helpers ──────────────────────────────────────────────────
function VideoThumb({ size = 54 }) {
  return (
    <div style={{ width: '100%', height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', background: 'var(--card)' }}>
      <Icon name="video" size={size * 0.4} color="var(--muted)" />
    </div>
  );
}

// Detecta ventana estrecha (móvil) para alternar entre layout de
// escritorio (2 columnas) y de móvil (1 columna, navegación por pantallas).
function useIsNarrow(breakpoint = 760) {
  const [narrow, setNarrow] = React.useState(window.innerWidth < breakpoint);
  React.useEffect(() => {
    const mq = window.matchMedia(`(max-width: ${breakpoint}px)`);
    const onChange = (e) => setNarrow(e.matches);
    mq.addEventListener('change', onChange);
    return () => mq.removeEventListener('change', onChange);
  }, [breakpoint]);
  return narrow;
}

// El input de archivo nativo no se puede maquillar (el botón "Seleccionar
// archivo" lo pinta el navegador/SO) y desentona con el diseño oscuro.
// Se superpone invisible sobre una caja con nuestro propio estilo.
function VideoFileField({ onChange, hasFile }) {
  return (
    <div className="field" style={{ position: 'relative', cursor: 'pointer', minHeight: 50, padding: '0 14px' }}>
      <Icon name="video" size={18} />
      <span style={{ color: hasFile ? 'var(--text)' : 'var(--muted)', fontSize: 14.5 }}>
        {hasFile ? 'Cambiar archivo de vídeo…' : 'Elegir archivo de vídeo…'}
      </span>
      <input type="file" accept="video/*" onChange={onChange}
        style={{ position: 'absolute', inset: 0, opacity: 0, cursor: 'pointer' }} />
    </div>
  );
}

// ── shared sub-components (same as ZIP) ──────────────────────
function Avatar({ letter, size = 48 }) {
  return (
    <div style={{
      width: size, height: size, borderRadius: '50%', flexShrink: 0,
      background: 'radial-gradient(circle at 35% 30%, #e7cf86, #c9a84c 60%, #9c7e34)',
      display: 'flex', alignItems: 'center', justifyContent: 'center',
      fontWeight: 800, color: '#1c1a16', fontSize: size * 0.4,
    }}>{letter}</div>
  );
}

function StatusBadge({ done }) {
  return (
    <span style={{
      display: 'inline-flex', alignItems: 'center', gap: 5,
      fontSize: 11, fontWeight: 700, padding: '4px 10px', borderRadius: 999,
      background: done ? 'rgba(92,184,95,.13)' : 'var(--gold-soft)',
      border: `1px solid ${done ? 'rgba(92,184,95,.4)' : 'var(--gold-line)'}`,
      color: done ? 'var(--success)' : 'var(--gold)', whiteSpace: 'nowrap',
    }}>
      {done ? <><Icon name="check" size={12} color="var(--success)" /> Cuestionario</> : <><Icon name="clock" size={12} /> Pendiente</>}
    </span>
  );
}

// F21 — los invitados (sesión anónima) también tienen documento en users pero sin email: no son alumnos.
const isRegistered = (u) => !!u.email;

function subscriptionOf(u) {
  const sub = u.subscription;
  const exp = sub?.expiresAt?.toDate ? sub.expiresAt.toDate() : (sub?.expiresAt ? new Date(sub.expiresAt) : null);
  return { active: !!sub?.active && !!exp && exp > new Date(), expiresAt: exp, everSubscribed: !!sub };
}

// F21 — seguimiento semanal: MISMA regla que functions/followUp.js (el panel no puede importarlo, no hay
// bundler; si cambia la regla, cambiar los dos sitios). Un vídeo cuenta para una clase si se envió en los
// 7 días que terminan en el día de esa clase, ambos incluidos, en hora de Madrid. Con la antigua regla de
// "semana de lunes a domingo", el vídeo del domingo no contaba para la clase del lunes.
const DAY_MS = 86400000;

// Día civil (días desde 1970-01-01) de un instante, visto en Madrid. Se usa para la fecha de envío.
function madridDay(date) {
  const d = date instanceof Date ? date : new Date(date);
  if (Number.isNaN(d.getTime())) return null;
  const parts = new Intl.DateTimeFormat('en-US', { timeZone: 'Europe/Madrid', year: 'numeric', month: 'numeric', day: 'numeric' }).formatToParts(d);
  const get = (type) => Number(parts.find((p) => p.type === type).value);
  return Math.round(Date.UTC(get('year'), get('month') - 1, get('day')) / DAY_MS);
}

// Día civil de una fecha elegida en el calendario del navegador (la que el profesor ve y pulsa), sin pasar
// por zonas horarias: así "el día 5" es el 5 aunque el navegador no esté en Madrid.
function civilDayOf(localDate) {
  return Math.round(Date.UTC(localDate.getFullYear(), localDate.getMonth(), localDate.getDate()) / DAY_MS);
}

function sentForLesson(sentAt, lessonDay) {
  if (sentAt == null) return false;
  const sentDay = madridDay(sentAt);
  return sentDay !== null && sentDay >= lessonDay - 6 && sentDay <= lessonDay;
}

// Estado del seguimiento de un alumno para la clase de `lessonDate` (Date local):
// 'sent' (ya lo envió), 'nosub' (no puede enviarlo: sin suscripción vigente o cuenta desactivada) o 'pending'.
function followUpStatus(st, lessonDate) {
  if (sentForLesson(st.followUpVideoUpdatedAt, civilDayOf(lessonDate))) return 'sent';
  if (st.active === false || !subscriptionOf(st).active) return 'nosub';
  return 'pending';
}

const FOLLOWUP_STATUS = {
  sent:    { label: 'Vídeo enviado',    color: 'var(--success)', bg: 'rgba(92,184,95,.13)',  border: 'rgba(92,184,95,.4)',  title: 'Vídeo de seguimiento enviado (últimos 7 días)' },
  pending: { label: 'Falta el vídeo',   color: '#f44336',        bg: 'rgba(244,67,54,.1)',   border: 'rgba(244,67,54,.4)',  title: 'Sin vídeo de seguimiento para esta clase' },
  nosub:   { label: 'Sin suscripción',  color: 'var(--muted)',   bg: 'transparent',          border: 'var(--border)',       title: 'Sin suscripción vigente: no puede enviar vídeo' },
};

function FollowUpChip({ status }) {
  const s = FOLLOWUP_STATUS[status];
  return (
    <span title={s.title} style={{
      display: 'inline-flex', alignItems: 'center', fontSize: 11, fontWeight: 700, padding: '4px 10px',
      borderRadius: 999, whiteSpace: 'nowrap', background: s.bg, border: `1px solid ${s.border}`, color: s.color,
    }}>{s.label}</span>
  );
}

// Alumnos con clase hoy y el estado de su vídeo. La consulta solo trae a quienes tienen clase hoy (y no a todos
// los alumnos, ni a los invitados, que no tienen lessonWeekday). Se recarga cada vez que cambia `refreshKey`.
function useTodayClasses(refreshKey) {
  const [state, setState] = React.useState({ rows: null, error: false });
  React.useEffect(() => {
    let cancelled = false;
    const { collection, getDocs, query, where } = window._fb;
    const today = new Date();
    getDocs(query(collection(window._db, 'users'), where('role', '==', 'student'), where('lessonWeekday', '==', today.getDay())))
      .then((snap) => {
        if (cancelled) return;
        const rows = snap.docs.map((d) => ({ id: d.id, ...d.data() }))
          .filter(isRegistered)
          .map((st) => ({ ...st, followUp: followUpStatus(st, today) }))
          .sort((a, b) => (a.name || a.email || '').localeCompare(b.name || b.email || ''));
        setState({ rows, error: false });
      })
      .catch(() => { if (!cancelled) setState({ rows: [], error: true }); });
    return () => { cancelled = true; };
  }, [refreshKey]);
  return state;
}

function SubBadge({ user }) {
  const { active, everSubscribed } = subscriptionOf(user);
  const label = active ? 'Suscrito' : everSubscribed ? 'Caducada' : 'Sin suscripción';
  const color = active ? 'var(--success)' : 'var(--muted)';
  return (
    <span style={{
      display: 'inline-flex', alignItems: 'center', gap: 5, fontSize: 11, fontWeight: 700,
      padding: '4px 10px', borderRadius: 999, whiteSpace: 'nowrap',
      background: active ? 'rgba(92,184,95,.13)' : 'transparent',
      border: `1px solid ${active ? 'rgba(92,184,95,.4)' : 'var(--border)'}`, color,
    }}>{label}</span>
  );
}

function AccessBadge({ access }) {
  const free = access === 'free';
  return (
    <span style={{
      fontSize: 11, fontWeight: 700, padding: '3px 9px', borderRadius: 999, whiteSpace: 'nowrap',
      background: free ? 'rgba(92,184,95,.13)' : 'var(--gold-soft)',
      border: `1px solid ${free ? 'rgba(92,184,95,.4)' : 'var(--gold-line)'}`,
      color: free ? 'var(--success)' : 'var(--gold)',
    }}>{free ? 'Gratuita · intro' : 'Premium'}</span>
  );
}

function QAns({ label, value }) {
  return (
    <div style={{ background: 'var(--surface)', border: '1px solid var(--border)', borderRadius: 11, padding: '10px 13px' }}>
      <div style={{ fontSize: 10.5, color: 'var(--muted)', fontWeight: 700, letterSpacing: .3, marginBottom: 3 }}>{label.toUpperCase()}</div>
      <div style={{ fontSize: 13.5, fontWeight: 600, color: 'var(--text)' }}>{value}</div>
    </div>
  );
}

// ── Login ────────────────────────────────────────────────────
function WebLogin({ onLogin, deniedMsg, onForgot }) {
  const [email, setEmail] = React.useState('');
  const [pass, setPass] = React.useState('');
  const [err, setErr] = React.useState('');
  const [loading, setLoading] = React.useState(false);

  const handleLogin = async (e) => {
    e.preventDefault();
    setErr(''); setLoading(true);
    try {
      await window._fb.signInWithEmailAndPassword(window._auth, email, pass);
      onLogin();
    } catch {
      setErr('Email o contraseña incorrectos');
    } finally {
      setLoading(false);
    }
  };

  const shownError = err || deniedMsg;

  return (
    <div style={{ width: '100%', minHeight: '100vh', background: 'radial-gradient(700px 400px at 50% 0%, #2c2925 0%, transparent 60%), var(--bg)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
      <div className="card" style={{ width: 380, maxWidth: 'calc(100vw - 32px)', padding: 36 }}>
        <div style={{ display: 'flex', justifyContent: 'center', marginBottom: 8 }}>
          <img src={LOGO} alt="GuitarFabrik" style={{ height: 34 }} />
        </div>
        <div className="brand-academy" style={{ textAlign: 'center', marginBottom: 6 }}>ACADEMY</div>
        <div style={{ textAlign: 'center', fontSize: 13.5, fontStyle: 'italic', color: 'var(--text-2)', marginBottom: 26 }}>Panel del profesor</div>
        <form className="col" style={{ gap: 13 }} onSubmit={handleLogin}>
          <div>
            <label className="web-label">Email</label>
            <input className="web-input" type="email" placeholder="profesor@guitarfabrik.com" value={email} onChange={e => setEmail(e.target.value)} required />
          </div>
          <div>
            <label className="web-label">Contraseña</label>
            <input className="web-input" type="password" placeholder="••••••••" value={pass} onChange={e => setPass(e.target.value)} required />
          </div>
          {shownError && <div style={{ fontSize: 13, color: '#f44336', background: 'rgba(244,67,54,.1)', borderRadius: 8, padding: '8px 12px' }}>⚠ {shownError}</div>}
          <button className="btn-action" type="submit" disabled={loading} style={{ marginTop: 8 }}>
            {loading ? 'Entrando…' : 'Entrar'}
          </button>
          <button type="button" onClick={onForgot} style={{ textAlign: 'center', marginTop: 2, fontSize: 13.5, color: 'var(--text-2)' }}>
            ¿Olvidaste tu <span style={{ color: 'var(--gold)', fontWeight: 600 }}>contraseña?</span>
          </button>
        </form>
        <div style={{ display: 'flex', justifyContent: 'center', gap: 14, marginTop: 26, paddingTop: 18, borderTop: '1px solid var(--border)' }}>
          <a href="/privacy.html" style={{ fontSize: 12, color: 'var(--muted)' }}>Política de privacidad</a>
          <a href="/terms.html" style={{ fontSize: 12, color: 'var(--muted)' }}>Términos de uso</a>
        </div>
      </div>
    </div>
  );
}

// ── Dashboard ────────────────────────────────────────────────
function WebStat({ icon, label, value }) {
  return (
    <div className="gf-stat" style={{ background: 'var(--card)', border: '1px solid var(--gold-line)', borderRadius: 16, padding: 24 }}>
      <div style={{ width: 46, height: 46, borderRadius: 13, background: 'var(--gold-soft)', border: '1px solid var(--gold-line)', display: 'flex', alignItems: 'center', justifyContent: 'center', marginBottom: 16 }}>
        <Icon name={icon} size={23} color="var(--gold)" />
      </div>
      <div style={{ fontSize: 38, fontWeight: 800, color: 'var(--gold)', lineHeight: 1, marginBottom: 6 }}>{value}</div>
      <div style={{ fontSize: 14, color: 'var(--muted)' }}>{label}</div>
    </div>
  );
}

// Tarjeta del Dashboard: quién tiene clase hoy y si ya ha enviado su vídeo de seguimiento.
function TodayClassesCard({ today, onOpenCalendar }) {
  const { rows, error } = today;
  const rawDate = new Date().toLocaleDateString('es-ES', { weekday: 'long', day: 'numeric', month: 'long' });
  const dateLabel = rawDate.charAt(0).toUpperCase() + rawDate.slice(1);
  return (
    <div className="card" style={{ padding: '18px 20px', marginBottom: 36 }}>
      <div className="row" style={{ alignItems: 'baseline', gap: 12, flexWrap: 'wrap', marginBottom: 14 }}>
        <h2 style={{ fontSize: 19, fontWeight: 700, margin: 0 }}>Clases de hoy</h2>
        <span style={{ fontSize: 13, color: 'var(--muted)' }}>{dateLabel}</span>
        <div style={{ flex: 1 }} />
        <button onClick={onOpenCalendar} style={{ fontSize: 13, fontWeight: 600, color: 'var(--gold)', background: 'none', border: 'none', cursor: 'pointer', fontFamily: 'inherit' }}>
          Ver calendario →
        </button>
      </div>
      {rows === null ? (
        <div style={{ color: 'var(--muted)', fontSize: 14, padding: '6px 0' }}>Cargando…</div>
      ) : error ? (
        <div style={{ color: '#f44336', fontSize: 14, padding: '6px 0' }}>No se pudieron cargar las clases de hoy. Recarga la página.</div>
      ) : rows.length === 0 ? (
        <div style={{ color: 'var(--muted)', fontSize: 14, padding: '6px 0' }}>
          Hoy no tienes clase con ningún alumno. El día de clase de cada alumno se fija en Alumnos.
        </div>
      ) : (
        <div className="col" style={{ gap: 10 }}>
          {rows.map(st => (
            <div key={st.id} style={{ display: 'flex', alignItems: 'center', gap: 12, flexWrap: 'wrap' }}>
              <Avatar letter={(st.name?.[0] || st.email?.[0] || '?').toUpperCase()} size={36} />
              <div style={{ flex: 1, minWidth: 120 }}>
                <div style={{ fontSize: 14, fontWeight: 700 }}>{st.name || 'Sin nombre'}</div>
                <div style={{ fontSize: 12.5, color: 'var(--muted)' }}>{st.email}</div>
              </div>
              {st.followUp === 'sent' && st.followUpVideoURL && (
                <a href={st.followUpVideoURL} target="_blank" rel="noreferrer"
                  style={{ display: 'flex', alignItems: 'center', gap: 6, fontSize: 12.5, fontWeight: 600, color: 'var(--text-2)', padding: '5px 11px', borderRadius: 8, border: '1px solid var(--border)', textDecoration: 'none' }}>
                  <Icon name="play" size={12} color="var(--text-2)" /> Ver vídeo
                </a>
              )}
              <FollowUpChip status={st.followUp} />
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

function WebDashboard({ today, onOpenCalendar }) {
  const [stats, setStats] = React.useState({ students: '…', videos: '…', pending: '…' });
  const [videos, setVideos] = React.useState([]);

  React.useEffect(() => {
    const { collection, getDocs, query, where } = window._fb;
    const db = window._db;
    Promise.all([
      getDocs(query(collection(db,'users'), where('role','==','student'))),
      getDocs(collection(db,'videos')),
      getDocs(query(collection(db,'users'), where('role','==','student'), where('questionnaireCompleted','==',false))),
    ]).then(([s,v,p]) => {
      const vids = v.docs.map(d=>({id:d.id,...d.data()}))
        .sort((a,b)=>(b.createdAt||'').localeCompare(a.createdAt||''));
      setStats({ students: s.docs.filter(d => isRegistered(d.data())).length, videos: v.size, pending: p.docs.filter(d => isRegistered(d.data())).length });
      setVideos(vids.slice(0,5));
    });
  }, []);

  return (
    <div>
      <h1 style={{ fontSize: 28, fontWeight: 800, marginBottom: 4 }}>Dashboard</h1>
      <p style={{ color: 'var(--muted)', fontSize: 14.5, marginBottom: 28 }}>Resumen de tu academia.</p>
      <div className="gf-stats" style={{ display: 'grid', gridTemplateColumns: 'repeat(3,1fr)', gap: 18, marginBottom: 36 }}>
        <WebStat icon="people" label="Alumnos" value={stats.students} />
        <WebStat icon="video" label="Vídeos subidos" value={stats.videos} />
        <WebStat icon="clipboard" label="Sin cuestionario" value={stats.pending} />
      </div>
      <TodayClassesCard today={today} onOpenCalendar={onOpenCalendar} />
      <h2 style={{ fontSize: 19, fontWeight: 700, marginBottom: 16 }}>Últimas clases subidas</h2>
      <div className="col" style={{ gap: 11 }}>
        {videos.length === 0
          ? <div style={{ color: 'var(--muted)', padding: '40px 0', textAlign: 'center' }}>Aún no has subido ningún vídeo</div>
          : videos.map(v => {
            return (
              <div key={v.id} className="gf-lrow" style={{ background: 'var(--card)', border: '1px solid var(--border)', borderRadius: 12, padding: '12px 16px', display: 'flex', alignItems: 'center', gap: 16 }}>
                <div style={{ width: 96, height: 54, borderRadius: 8, overflow: 'hidden', flexShrink: 0 }}>
                  <VideoThumb />
                </div>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ fontWeight: 700, fontSize: 15 }}>{v.title}</div>
                  <div style={{ color: 'var(--muted)', fontSize: 13, marginTop: 3 }}>
                    {v.week ? `Semana ${v.week} · ` : ''}{v.description?.slice(0,70)}{v.description?.length > 70 ? '…' : ''}
                  </div>
                </div>
                <div className="gf-ldate" style={{ color: 'var(--muted)', fontSize: 13, whiteSpace: 'nowrap' }}>
                  {v.createdAt ? new Date(v.createdAt).toLocaleDateString('es-ES',{day:'numeric',month:'short',year:'numeric'}) : ''}
                </div>
              </div>
            );
          })
        }
      </div>
    </div>
  );
}

// ── Videos ───────────────────────────────────────────────────
const MAX_VIDEO_BYTES = 100 * 1024 * 1024;

function WebVideos() {
  const [videos, setVideos] = React.useState([]);
  const [students, setStudents] = React.useState([]);
  const [form, setForm] = React.useState({ title: '', desc: '', notes: '', access: 'premium' });
  const [videoFile, setVideoFile] = React.useState(null);
  const [selected, setSelected] = React.useState(new Set());
  const [studentSearch, setStudentSearch] = React.useState('');
  const [saving, setSaving] = React.useState(false);
  const [progress, setProgress] = React.useState(0);
  const [err, setErr] = React.useState('');
  const set = (k, v) => setForm(f => ({ ...f, [k]: v }));

  const handleFile = (e) => {
    setErr('');
    const file = e.target.files[0] || null;
    if (file && file.size >= MAX_VIDEO_BYTES) {
      setErr('El vídeo supera el límite de 100MB');
      setVideoFile(null);
      e.target.value = '';
      return;
    }
    setVideoFile(file);
  };

  const load = () => {
    const { collection, getDocs, query, where } = window._fb;
    getDocs(collection(window._db,'videos')).then(snap => {
      setVideos(snap.docs.map(d=>({id:d.id,...d.data()}))
        .sort((a,b)=>(b.createdAt||'').localeCompare(a.createdAt||'')));
    });
    getDocs(query(collection(window._db,'users'), where('role','==','student'))).then(snap => {
      setStudents(snap.docs.map(d=>({id:d.id,...d.data()}))
        .filter(st => isRegistered(st) && st.active !== false)
        .sort((a,b)=>(a.name||a.email||'').localeCompare(b.name||b.email||'')));
    });
  };
  React.useEffect(load, []);

  const toggleStudent = (id) => setSelected(prev => {
    const next = new Set(prev);
    next.has(id) ? next.delete(id) : next.add(id);
    return next;
  });

  const filteredStudents = students.filter(st => {
    const q = studentSearch.trim().toLowerCase();
    if (!q) return true;
    return (st.name||'').toLowerCase().includes(q) || (st.email||'').toLowerCase().includes(q);
  });

  const handleAdd = async (e) => {
    e.preventDefault();
    setErr('');
    if (!form.title.trim() || !videoFile) { setErr('Título y archivo de vídeo son obligatorios'); return; }
    setSaving(true);
    setProgress(0);
    try {
      const { collection, doc, setDoc, updateDoc, arrayUnion, ref, uploadBytesResumable, getDownloadURL } = window._fb;
      const videoRef = doc(collection(window._db, 'videos'));
      const storagePath = `videos-clases/${videoRef.id}.mp4`;
      const storageRef = ref(window._storage, storagePath);
      const task = uploadBytesResumable(storageRef, videoFile, { contentType: videoFile.type || 'video/mp4' });
      const url = await new Promise((resolve, reject) => {
        task.on('state_changed',
          (snap) => setProgress(Math.round((snap.bytesTransferred / snap.totalBytes) * 100)),
          reject,
          async () => resolve(await getDownloadURL(task.snapshot.ref)),
        );
      });
      await setDoc(videoRef, {
        title: form.title.trim(), url, storagePath,
        description: form.desc.trim(), notes: form.notes.trim(),
        access: form.access,
        createdAt: new Date().toISOString(),
      });
      await Promise.all([...selected].map(uid =>
        updateDoc(doc(window._db,'users',uid), { assignedVideos: arrayUnion(videoRef.id) })
      ));
      setForm({ title:'', desc:'', notes:'', access: form.access });
      setVideoFile(null);
      setSelected(new Set());
      setStudentSearch('');
      load();
    } catch(e) { setErr('Error: ' + e.message); }
    finally { setSaving(false); setProgress(0); }
  };

  const handleToggleAccess = async (v) => {
    const next = v.access === 'free' ? 'premium' : 'free';
    const { doc, updateDoc } = window._fb;
    await updateDoc(doc(window._db, 'videos', v.id), { access: next });
    setVideos(vs => vs.map(x => x.id === v.id ? { ...x, access: next } : x));
  };

  const handleDelete = async (v) => {
    if (!confirm(`¿Eliminar "${v.title}"?`)) return;
    const { doc, deleteDoc, ref, deleteObject } = window._fb;
    await deleteDoc(doc(window._db,'videos',v.id));
    if (v.storagePath) {
      try { await deleteObject(ref(window._storage, v.storagePath)); } catch {}
    }
    setVideos(vs => vs.filter(x => x.id !== v.id));
  };

  return (
    <div>
      <h1 style={{ fontSize: 28, fontWeight: 800, marginBottom: 4 }}>Vídeos / Clases</h1>
      <p style={{ color: 'var(--muted)', fontSize: 14.5, marginBottom: 28 }}>
        Sube el archivo de vídeo directamente (máx. 100MB).
      </p>
      <div className="gf-videos-grid" style={{ display: 'grid', gridTemplateColumns: '380px 1fr', gap: 28, alignItems: 'start' }}>
        {/* Form */}
        <div className="card" style={{ padding: 24 }}>
          <div style={{ fontSize: 16, fontWeight: 700, marginBottom: 18 }}>Nueva clase</div>
          <form className="col" style={{ gap: 15 }} onSubmit={handleAdd}>
            <div>
              <label className="web-label">Título</label>
              <input className="web-input" placeholder="Ej: Técnica de picado — Farruca" value={form.title} onChange={e=>set('title',e.target.value)} />
            </div>
            <div>
              <label className="web-label">Acceso</label>
              <select className="web-input" value={form.access} onChange={e=>set('access',e.target.value)}>
                <option value="premium">Premium · solo suscriptores</option>
                <option value="free">Gratuita · curso de introducción</option>
              </select>
              <div style={{ fontSize:12, color:'var(--muted)', marginTop:6 }}>
                {form.access === 'free' ? 'Cualquiera que descargue la app la verá, sin cuenta ni pago.' : 'Solo la verán los usuarios con suscripción activa.'}
              </div>
            </div>
            <div>
              <label className="web-label">Archivo de vídeo</label>
              <VideoFileField onChange={handleFile} hasFile={!!videoFile} />
              {videoFile && (
                <div className="row" style={{ alignItems:'center', gap:6, marginTop:8, fontSize:12.5, color: 'var(--success)' }}>
                  <Icon name="checkCircle" size={15} color="var(--success)" />
                  {videoFile.name} · {(videoFile.size / (1024*1024)).toFixed(1)}MB
                </div>
              )}
              {saving && (
                <div style={{ marginTop:10, background:'var(--border)', borderRadius:999, overflow:'hidden', height:8 }}>
                  <div style={{ width:`${progress}%`, height:'100%', background:'var(--gold)', transition:'width .2s' }} />
                </div>
              )}
            </div>
            <div>
              <label className="web-label">Descripción</label>
              <textarea className="web-input" rows="3" placeholder="Qué aprende el alumno" value={form.desc} onChange={e=>set('desc',e.target.value)} style={{ resize:'none' }} />
            </div>
            <div>
              <label className="web-label">Notas para el alumno</label>
              <textarea className="web-input" rows="3" placeholder="Ejercicios para practicar…" value={form.notes} onChange={e=>set('notes',e.target.value)} style={{ resize:'none' }} />
            </div>
            <div>
              <label className="web-label">Asignar a · {selected.size} alumno{selected.size!==1?'s':''}</label>
              <div className="field" style={{ marginBottom: 8 }}>
                <Icon name="search" size={16} />
                <input placeholder="Buscar alumno…" value={studentSearch} onChange={e=>setStudentSearch(e.target.value)} />
              </div>
              <div style={{ maxHeight: 180, overflowY: 'auto', border: '1px solid var(--border)', borderRadius: 10, padding: 6 }}>
                {filteredStudents.length === 0
                  ? <div style={{ color: 'var(--muted)', fontSize: 12.5, padding: '10px 6px' }}>Sin alumnos activos que coincidan.</div>
                  : filteredStudents.map(st => (
                    <label key={st.id} style={{ display:'flex', alignItems:'center', gap:10, padding:'7px 8px', borderRadius:8, cursor:'pointer' }}>
                      <input type="checkbox" checked={selected.has(st.id)} onChange={()=>toggleStudent(st.id)} style={{ width:16, height:16, accentColor:'var(--gold)' }} />
                      <span style={{ fontSize:13.5 }}>{st.name || st.email}</span>
                    </label>
                  ))
                }
              </div>
            </div>
            {err && <div style={{ fontSize:13, color:'#f44336', background:'rgba(244,67,54,.1)', borderRadius:8, padding:'8px 12px' }}>⚠ {err}</div>}
            <button className="btn-gold" type="submit" disabled={saving || !videoFile || !form.title.trim()}>
              <Icon name="checkCircle" size={18} color="#1c1a16" /> {saving ? `Subiendo… ${progress}%` : 'Publicar clase'}
            </button>
          </form>
        </div>

        {/* List */}
        <div>
          <div style={{ fontSize:15, fontWeight:700, marginBottom:14, color:'var(--text-2)' }}>Clases publicadas · {videos.length}</div>
          <div className="col" style={{ gap:14 }}>
            {videos.length === 0
              ? <div style={{ color:'var(--muted)', textAlign:'center', padding:'60px 0' }}>Aún no has publicado ninguna clase</div>
              : videos.map(v => {
                return (
                  <div key={v.id} className="card gf-vcard" style={{ padding:14, display:'flex', gap:16 }}>
                    <div className="gf-vthumb" style={{ width:150, height:84, borderRadius:10, overflow:'hidden', flexShrink:0 }}>
                      <VideoThumb />
                    </div>
                    <div style={{ flex:1, minWidth:0, display:'flex', flexDirection:'column' }}>
                      <div className="row" style={{ alignItems:'center', gap:10, flexWrap:'wrap' }}>
                        <div style={{ fontWeight:700, fontSize:15.5 }}>{v.title}</div>
                        <AccessBadge access={v.access} />
                      </div>
                      {v.description && <div style={{ color:'var(--muted)', fontSize:13, margin:'5px 0', lineHeight:1.45 }}>{v.description.slice(0,90)}{v.description.length>90?'…':''}</div>}
                      <div className="row gf-vactions" style={{ alignItems:'center', gap:12, marginTop:'auto', flexWrap:'wrap' }}>
                        <span style={{ fontSize:12.5, color:'var(--muted)' }}>
                          {v.createdAt ? new Date(v.createdAt).toLocaleDateString('es-ES',{day:'numeric',month:'short',year:'numeric'}) : ''}
                        </span>
                        <div style={{ flex:1 }} />
                        <a href={v.url} target="_blank" rel="noreferrer" style={{ display:'flex', alignItems:'center', gap:6, fontSize:13, fontWeight:600, color:'var(--gold)', padding:'6px 12px', borderRadius:8, border:'1px solid var(--gold-line)', textDecoration:'none' }}>
                          <Icon name="play" size={13} color="var(--gold)" /> Ver
                        </a>
                        <button onClick={()=>handleToggleAccess(v)} style={{ display:'flex', alignItems:'center', gap:6, fontSize:13, fontWeight:600, color:'var(--text-2)', padding:'6px 12px', borderRadius:8, border:'1px solid var(--border)' }}>
                          {v.access === 'free' ? 'Pasar a premium' : 'Hacer gratuita'}
                        </button>
                        <button onClick={()=>handleDelete(v)} style={{ display:'flex', alignItems:'center', gap:6, fontSize:13, fontWeight:600, color:'var(--muted)', padding:'6px 12px', borderRadius:8, border:'1px solid var(--border)' }}>
                          <Icon name="trash" size={13} color="var(--muted)" /> Eliminar
                        </button>
                      </div>
                    </div>
                  </div>
                );
              })
            }
          </div>
        </div>
      </div>
    </div>
  );
}

// ── Chats ────────────────────────────────────────────────────
function ChatThread({ student, onBack, narrow }) {
  const [messages, setMessages] = React.useState([]);
  const [text, setText] = React.useState('');
  const listRef = React.useRef(null);
  const chatId = `student_${student.id}`;

  React.useEffect(() => {
    const { collection, query, orderBy, onSnapshot } = window._fb;
    const ref = collection(window._db, 'chats', chatId, 'messages');
    let q;
    try { q = query(ref, orderBy('createdAt', 'asc')); } catch { q = ref; }
    return onSnapshot(q, snap => {
      setMessages(snap.docs.map(d => ({ id: d.id, ...d.data() })));
      setTimeout(() => { if (listRef.current) listRef.current.scrollTop = listRef.current.scrollHeight; }, 50);
    });
  }, [chatId]);

  const send = async (e) => {
    e.preventDefault();
    const t = text.trim();
    if (!t) return;
    setText('');
    const { collection, addDoc } = window._fb;
    await addDoc(collection(window._db, 'chats', chatId, 'messages'), {
      text: t, senderId: window._auth.currentUser.uid, role: 'teacher',
      createdAt: new Date().toISOString(),
    });
  };

  return (
    <div className="col" style={{ flex: 1, minWidth: 0, height: '100%' }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '16px 20px', borderBottom: '1px solid var(--border)' }}>
        {narrow && (
          <button onClick={onBack} style={{ padding: 4 }}>
            <Icon name="back" size={20} color="var(--text)" />
          </button>
        )}
        <Avatar letter={(student.name?.[0] || '?').toUpperCase()} size={38} />
        <div style={{ fontSize: 15, fontWeight: 700 }}>{student.name || 'Alumno'}</div>
      </div>
      <div ref={listRef} style={{ flex: 1, overflowY: 'auto', padding: 20, display: 'flex', flexDirection: 'column', gap: 10 }}>
        {messages.map(m => {
          const mine = m.role === 'teacher';
          return (
            <div key={m.id} style={{ alignSelf: mine ? 'flex-end' : 'flex-start', maxWidth: '70%' }}>
              <div style={{
                background: mine ? 'var(--action)' : 'var(--surface)',
                border: `1px solid ${mine ? 'var(--gold-line)' : 'var(--border)'}`,
                borderRadius: 16, padding: '10px 14px', fontSize: 14.5, lineHeight: 1.4,
              }}>
                {m.videoURL ? <a href={m.videoURL} target="_blank" rel="noreferrer" style={{ color: 'var(--gold)' }}>Ver vídeo</a> : m.text}
              </div>
              <div style={{ fontSize: 11, color: 'var(--muted)', marginTop: 3 }}>
                {m.createdAt ? new Date(m.createdAt).toLocaleTimeString('es-ES', { hour: '2-digit', minute: '2-digit' }) : ''}
              </div>
            </div>
          );
        })}
      </div>
      <form onSubmit={send} style={{ display: 'flex', gap: 10, padding: 16, borderTop: '1px solid var(--border)' }}>
        <input className="web-input" style={{ flex: 1 }} placeholder="Escribe un mensaje…" value={text} onChange={e => setText(e.target.value)} />
        <button className="btn-gold" type="submit" style={{ width: 'auto', flex: '0 0 auto', paddingLeft: 18, paddingRight: 18 }}>
          <Icon name="send" size={16} color="#1c1a16" />
        </button>
      </form>
    </div>
  );
}

function WebChats() {
  const [students, setStudents] = React.useState([]);
  const [selected, setSelected] = React.useState(null);
  const [loading, setLoading] = React.useState(true);
  const narrow = useIsNarrow();

  React.useEffect(() => {
    const { collection, getDocs, query, where, orderBy, limit } = window._fb;
    const load = async () => {
      const snap = await getDocs(query(collection(window._db, 'users'), where('role', '==', 'student')));
      const list = snap.docs.map(d => ({ id: d.id, ...d.data() })).filter(isRegistered);
      const withLastMsg = await Promise.all(list.map(async (st) => {
        try {
          const msgSnap = await getDocs(query(
            collection(window._db, 'chats', `student_${st.id}`, 'messages'),
            orderBy('createdAt', 'desc'), limit(1)
          ));
          if (!msgSnap.empty) {
            const last = msgSnap.docs[0].data();
            const date = last.createdAt ? new Date(last.createdAt) : null;
            return { ...st, lastMsg: last.text || (last.videoURL ? 'Vídeo' : ''), lastTime: date ? date.toLocaleTimeString('es-ES', { hour: '2-digit', minute: '2-digit' }) : '' };
          }
        } catch {}
        return st;
      }));
      setStudents(withLastMsg);
      setLoading(false);
    };
    load();
  }, []);

  const list = (
    <div className="col" style={{ gap: 10, width: narrow ? '100%' : 340, flexShrink: 0, borderRight: narrow ? 'none' : '1px solid var(--border)', padding: narrow ? 0 : '0 16px 0 0', overflowY: 'auto' }}>
      <div style={{ padding: narrow ? '0 0 10px' : '0 0 10px' }}>
        <h1 style={{ fontSize: 22, fontWeight: 800, marginBottom: 2 }}>Chats</h1>
        <p style={{ color: 'var(--muted)', fontSize: 13.5 }}>Mensajes de tus alumnos.</p>
      </div>
      {loading ? (
        <div style={{ color: 'var(--muted)', padding: '20px 0' }}>Cargando…</div>
      ) : students.length === 0 ? (
        <div style={{ color: 'var(--muted)', padding: '20px 0' }}>Aún no hay alumnos registrados.</div>
      ) : students.map(st => (
        <button key={st.id} onClick={() => setSelected(st)}
          className="card"
          style={{
            display: 'flex', alignItems: 'center', gap: 12, padding: 12, textAlign: 'left', width: '100%',
            borderColor: selected?.id === st.id ? 'var(--gold-line)' : 'var(--border)',
          }}>
          <Avatar letter={(st.name?.[0] || '?').toUpperCase()} size={40} />
          <div style={{ flex: 1, minWidth: 0 }}>
            <div style={{ fontSize: 14, fontWeight: 700 }}>{st.name || 'Sin nombre'}</div>
            <div style={{ fontSize: 12.5, color: 'var(--muted)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{st.lastMsg || 'Sin mensajes'}</div>
          </div>
          <div style={{ fontSize: 11, color: 'var(--muted)' }}>{st.lastTime || ''}</div>
        </button>
      ))}
    </div>
  );

  if (narrow) {
    return (
      <div style={{ height: 'calc(100dvh - 160px - env(safe-area-inset-bottom))' }}>
        {selected ? <ChatThread student={selected} onBack={() => setSelected(null)} narrow /> : list}
      </div>
    );
  }

  return (
    <div style={{ display: 'flex', height: 'calc(100vh - 57px - 68px)' }}>
      {list}
      {selected
        ? <ChatThread student={selected} onBack={() => setSelected(null)} narrow={false} />
        : <div style={{ flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center', color: 'var(--muted)' }}>Elige un alumno para ver la conversación.</div>
      }
    </div>
  );
}

// ── Calendario ───────────────────────────────────────────────
const WEEKDAY_LETTERS = ['D', 'L', 'M', 'X', 'J', 'V', 'S'];
const MONTH_NAMES = [
  'Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio',
  'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre',
];

function calStartOfDay(d) {
  const x = new Date(d);
  x.setHours(0, 0, 0, 0);
  return x;
}

function buildMonthGrid(year, month) {
  const first = new Date(year, month, 1);
  const startOffset = first.getDay();
  const gridStart = new Date(year, month, 1 - startOffset);
  return Array.from({ length: 42 }, (_, i) => {
    const d = new Date(gridStart);
    d.setDate(gridStart.getDate() + i);
    return d;
  });
}

// Subida rápida de una clase para un único alumno, con asignación inmediata
// — versión reducida del formulario de WebVideos, pensada para el flujo
// "estoy mirando el calendario, subo la clase de hoy sin cambiar de página".
function QuickUploadModal({ student, onClose, onUploaded }) {
  const [title, setTitle] = React.useState('');
  const [videoFile, setVideoFile] = React.useState(null);
  const [saving, setSaving] = React.useState(false);
  const [progress, setProgress] = React.useState(0);
  const [err, setErr] = React.useState('');

  const handleFile = (e) => {
    setErr('');
    const file = e.target.files[0] || null;
    if (file && file.size >= MAX_VIDEO_BYTES) {
      setErr('El vídeo supera el límite de 100MB');
      setVideoFile(null);
      e.target.value = '';
      return;
    }
    setVideoFile(file);
  };

  const handleUpload = async (e) => {
    e.preventDefault();
    if (!title.trim() || !videoFile) { setErr('Título y archivo de vídeo son obligatorios'); return; }
    setErr('');
    setSaving(true);
    setProgress(0);
    try {
      const { collection, doc, setDoc, updateDoc, arrayUnion, ref, uploadBytesResumable, getDownloadURL } = window._fb;
      const videoRef = doc(collection(window._db, 'videos'));
      const storagePath = `videos-clases/${videoRef.id}.mp4`;
      const storageRef = ref(window._storage, storagePath);
      const task = uploadBytesResumable(storageRef, videoFile, { contentType: videoFile.type || 'video/mp4' });
      const url = await new Promise((resolve, reject) => {
        task.on('state_changed',
          (snap) => setProgress(Math.round((snap.bytesTransferred / snap.totalBytes) * 100)),
          reject,
          async () => resolve(await getDownloadURL(task.snapshot.ref)),
        );
      });
      await setDoc(videoRef, { title: title.trim(), url, storagePath, description: '', notes: '', access: 'premium', createdAt: new Date().toISOString() });
      await updateDoc(doc(window._db, 'users', student.id), { assignedVideos: arrayUnion(videoRef.id) });
      onUploaded(videoRef.id);
      onClose();
    } catch (e2) { setErr('Error: ' + e2.message); }
    finally { setSaving(false); setProgress(0); }
  };

  return (
    <div style={{ position:'fixed', inset:0, background:'rgba(0,0,0,.6)', display:'flex', alignItems:'center', justifyContent:'center', zIndex:100 }} onClick={onClose}>
      <div className="card" style={{ width:420, maxWidth:'calc(100vw - 32px)', padding:24 }} onClick={e=>e.stopPropagation()}>
        <div style={{ display:'flex', justifyContent:'space-between', alignItems:'center', marginBottom:6 }}>
          <div style={{ fontSize:17, fontWeight:800 }}>Subir clase</div>
          <button onClick={onClose} style={{ padding:4 }}><Icon name="close" size={18} color="var(--muted)" /></button>
        </div>
        <div style={{ fontSize:13, color:'var(--muted)', marginBottom:16 }}>Se asignará directamente a {student.name || student.email}</div>
        <form className="col" style={{ gap:15 }} onSubmit={handleUpload}>
          <div>
            <label className="web-label">Título</label>
            <input className="web-input" placeholder="Ej: Técnica de picado — Farruca" value={title} onChange={e=>setTitle(e.target.value)} />
          </div>
          <div>
            <label className="web-label">Archivo de vídeo</label>
            <VideoFileField onChange={handleFile} hasFile={!!videoFile} />
            {videoFile && (
              <div className="row" style={{ alignItems:'center', gap:6, marginTop:8, fontSize:12.5, color:'var(--success)' }}>
                <Icon name="checkCircle" size={15} color="var(--success)" />
                {videoFile.name} · {(videoFile.size/(1024*1024)).toFixed(1)}MB
              </div>
            )}
            {saving && (
              <div style={{ marginTop:10, background:'var(--border)', borderRadius:999, overflow:'hidden', height:8 }}>
                <div style={{ width:`${progress}%`, height:'100%', background:'var(--gold)', transition:'width .2s' }} />
              </div>
            )}
          </div>
          {err && <div style={{ fontSize:13, color:'#f44336', background:'rgba(244,67,54,.1)', borderRadius:8, padding:'8px 12px' }}>⚠ {err}</div>}
          <button className="btn-gold" type="submit" disabled={saving || !videoFile || !title.trim()}>
            <Icon name="checkCircle" size={18} color="#1c1a16" /> {saving ? `Subiendo… ${progress}%` : 'Subir y asignar'}
          </button>
        </form>
      </div>
    </div>
  );
}

function WebCalendar() {
  const today = React.useMemo(() => calStartOfDay(new Date()), []);
  const [monthCursor, setMonthCursor] = React.useState(new Date(today.getFullYear(), today.getMonth(), 1));
  const [selectedDate, setSelectedDate] = React.useState(today);
  const [students, setStudents] = React.useState([]);
  const [videos, setVideos] = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const [assigningStudent, setAssigningStudent] = React.useState(null);
  const [uploadingFor, setUploadingFor] = React.useState(null);
  const narrow = useIsNarrow();

  const loadStudents = () => {
    const { collection, getDocs, query, where } = window._fb;
    getDocs(query(collection(window._db, 'users'), where('role', '==', 'student'))).then(snap => {
      setStudents(snap.docs.map(d => ({ id: d.id, ...d.data() })).filter(isRegistered));
      setLoading(false);
    });
  };

  React.useEffect(() => {
    loadStudents();
    const { collection, getDocs } = window._fb;
    getDocs(collection(window._db, 'videos')).then(snap => {
      setVideos(snap.docs.map(d => ({ id: d.id, ...d.data() })));
    });
  }, []);

  const grid = React.useMemo(() => buildMonthGrid(monthCursor.getFullYear(), monthCursor.getMonth()), [monthCursor]);

  const weekdaysWithClass = React.useMemo(() => {
    const set = new Set();
    students.forEach(st => { if (st.lessonWeekday !== undefined && st.lessonWeekday !== null) set.add(st.lessonWeekday); });
    return set;
  }, [students]);

  // El vídeo de seguimiento cuenta para la clase si se envió en los 7 días hasta ese día (ver followUpStatus).
  // Ojo: solo se guarda el ÚLTIMO envío de cada alumno, así que para días pasados el estado no es fiable.
  const studentsForSelectedDay = React.useMemo(() => {
    return students
      .filter(st => st.lessonWeekday === selectedDate.getDay())
      .map(st => ({ ...st, followUp: followUpStatus(st, selectedDate) }));
  }, [students, selectedDate]);

  const changeMonth = (delta) => setMonthCursor(m => new Date(m.getFullYear(), m.getMonth() + delta, 1));

  return (
    <div style={{ display: 'flex', gap: 32, flexDirection: narrow ? 'column' : 'row', alignItems: 'flex-start' }}>
      <div style={{ width: narrow ? '100%' : 380, flexShrink: 0 }}>
        <h1 style={{ fontSize: 28, fontWeight: 800, marginBottom: 20 }}>Calendario</h1>

        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 14 }}>
          <button onClick={() => changeMonth(-1)} className="card" style={{ width: 36, height: 36, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
            <Icon name="back" size={18} color="var(--text)" />
          </button>
          <div style={{ fontSize: 16, fontWeight: 700, textTransform: 'capitalize' }}>{MONTH_NAMES[monthCursor.getMonth()]} {monthCursor.getFullYear()}</div>
          <button onClick={() => changeMonth(1)} className="card" style={{ width: 36, height: 36, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
            <Icon name="arrowRight" size={18} color="var(--text)" />
          </button>
        </div>

        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(7, 1fr)', marginBottom: 4 }}>
          {WEEKDAY_LETTERS.map((l, i) => (
            <div key={i} style={{ textAlign: 'center', fontSize: 12, fontWeight: 700, color: 'var(--muted)' }}>{l}</div>
          ))}
        </div>

        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(7, 1fr)' }}>
          {grid.map((d, i) => {
            const inMonth = d.getMonth() === monthCursor.getMonth();
            const isSelected = calStartOfDay(d).getTime() === calStartOfDay(selectedDate).getTime();
            const isToday = calStartOfDay(d).getTime() === today.getTime();
            const hasClass = weekdaysWithClass.has(d.getDay());
            return (
              <button
                key={i}
                onClick={() => setSelectedDate(calStartOfDay(d))}
                style={{
                  aspectRatio: '1', display: 'flex', alignItems: 'center', justifyContent: 'center',
                  position: 'relative', background: 'none', border: 'none', cursor: 'pointer', padding: 4,
                }}
              >
                <span style={{
                  width: 32, height: 32, borderRadius: '50%', display: 'flex', alignItems: 'center', justifyContent: 'center',
                  fontSize: 14, fontWeight: isSelected || isToday ? 800 : 400,
                  color: isSelected ? '#1c1a16' : !inMonth ? 'var(--muted)' : isToday ? 'var(--gold)' : 'var(--text)',
                  opacity: inMonth ? 1 : 0.4,
                  background: isSelected ? 'var(--gold)' : 'transparent',
                }}>
                  {d.getDate()}
                </span>
                {hasClass && inMonth && !isSelected && (
                  <span style={{ position: 'absolute', bottom: 2, width: 4, height: 4, borderRadius: 2, background: 'var(--gold)' }} />
                )}
              </button>
            );
          })}
        </div>
      </div>

      <div style={{ flex: 1, minWidth: 0, width: '100%' }}>
        <div style={{ fontSize: 16, fontWeight: 700, marginBottom: 14, textTransform: 'capitalize' }}>
          {selectedDate.toLocaleDateString('es-ES', { weekday: 'long', day: 'numeric', month: 'long' })}
        </div>
        {loading ? (
          <div style={{ color: 'var(--muted)' }}>Cargando…</div>
        ) : studentsForSelectedDay.length === 0 ? (
          <div style={{ color: 'var(--muted)' }}>Ningún alumno tiene clase este día.</div>
        ) : (
          <div className="col" style={{ gap: 10 }}>
            {studentsForSelectedDay.map(st => (
              <div key={st.id} className="card" style={{ display: 'flex', alignItems: 'center', gap: 12, padding: 12, flexWrap: 'wrap' }}>
                <Avatar letter={(st.name?.[0] || st.email?.[0] || '?').toUpperCase()} size={40} />
                <div style={{ flex: 1, minWidth: 120 }}>
                  <div style={{ fontSize: 14, fontWeight: 700 }}>{st.name || 'Sin nombre'}</div>
                  <div style={{ fontSize: 12.5, color: 'var(--muted)' }}>{st.email}</div>
                </div>
                <button onClick={() => setAssigningStudent(st)}
                  style={{ display: 'flex', alignItems: 'center', gap: 6, fontSize: 12.5, fontWeight: 600, color: 'var(--gold)', padding: '6px 11px', borderRadius: 8, border: '1px solid var(--gold-line)' }}>
                  <Icon name="video" size={13} color="var(--gold)" /> Gestionar clases · {(st.assignedVideos || []).length}
                </button>
                <button onClick={() => setUploadingFor(st)}
                  style={{ display: 'flex', alignItems: 'center', gap: 6, fontSize: 12.5, fontWeight: 600, color: 'var(--text)', padding: '6px 11px', borderRadius: 8, border: '1px solid var(--border)' }}>
                  <Icon name="checkCircle" size={13} color="var(--text)" /> Subir clase
                </button>
                <FollowUpChip status={st.followUp} />
              </div>
            ))}
          </div>
        )}
      </div>

      {assigningStudent && (
        <WebAssignModal
          student={assigningStudent}
          videos={videos}
          onClose={() => setAssigningStudent(null)}
          onSaved={(ids) => setStudents(prev => prev.map(s => s.id === assigningStudent.id ? { ...s, assignedVideos: ids } : s))}
        />
      )}
      {uploadingFor && (
        <QuickUploadModal
          student={uploadingFor}
          onClose={() => setUploadingFor(null)}
          onUploaded={(videoId) => {
            setVideos(prev => [{ id: videoId }, ...prev]);
            setStudents(prev => prev.map(s => s.id === uploadingFor.id ? { ...s, assignedVideos: [...(s.assignedVideos || []), videoId] } : s));
          }}
        />
      )}
    </div>
  );
}

// ── Alumnos ──────────────────────────────────────────────────
function WebAssignModal({ student, videos, onClose, onSaved }) {
  const [selected, setSelected] = React.useState(new Set(student.assignedVideos || []));
  const [search, setSearch] = React.useState('');
  const [saving, setSaving] = React.useState(false);

  const toggle = (id) => setSelected(prev => {
    const next = new Set(prev);
    next.has(id) ? next.delete(id) : next.add(id);
    return next;
  });

  const filtered = videos.filter(v => v.title.toLowerCase().includes(search.trim().toLowerCase()));

  const save = async () => {
    setSaving(true);
    try {
      const { doc, updateDoc } = window._fb;
      await updateDoc(doc(window._db, 'users', student.id), { assignedVideos: [...selected] });
      onSaved([...selected]);
      onClose();
    } catch (e) {
      alert('Error: ' + e.message);
    } finally {
      setSaving(false);
    }
  };

  return (
    <div style={{ position:'fixed', inset:0, background:'rgba(0,0,0,.6)', display:'flex', alignItems:'center', justifyContent:'center', zIndex:100 }} onClick={onClose}>
      <div className="card" style={{ width:460, maxWidth:'calc(100vw - 32px)', maxHeight:'80vh', padding:24, display:'flex', flexDirection:'column' }} onClick={e=>e.stopPropagation()}>
        <div style={{ display:'flex', justifyContent:'space-between', alignItems:'center', marginBottom:6 }}>
          <div style={{ fontSize:17, fontWeight:800 }}>Gestionar clases</div>
          <button onClick={onClose} style={{ padding:4 }}><Icon name="close" size={18} color="var(--muted)" /></button>
        </div>
        <div style={{ fontSize:13, color:'var(--muted)', marginBottom:16 }}>{student.name || student.email} · {selected.size} clase{selected.size!==1?'s':''} asignada{selected.size!==1?'s':''}</div>
        <div className="field" style={{ marginBottom:10 }}>
          <Icon name="search" size={16} />
          <input placeholder="Buscar clase…" value={search} onChange={e=>setSearch(e.target.value)} />
        </div>
        <div style={{ overflowY:'auto', flex:1, border:'1px solid var(--border)', borderRadius:10, padding:6 }}>
          {filtered.length === 0
            ? <div style={{ color:'var(--muted)', fontSize:12.5, padding:'10px 6px' }}>Sin clases que coincidan.</div>
            : filtered.map(v => (
              <label key={v.id} style={{ display:'flex', alignItems:'center', gap:10, padding:'8px', borderRadius:8, cursor:'pointer' }}>
                <input type="checkbox" checked={selected.has(v.id)} onChange={()=>toggle(v.id)} style={{ width:16, height:16, accentColor:'var(--gold)', flexShrink:0 }} />
                <span style={{ fontSize:13.5 }}>{v.title}{v.week ? ` · Semana ${v.week}` : ''}</span>
              </label>
            ))
          }
        </div>
        <button className="btn-gold" onClick={save} disabled={saving} style={{ marginTop:16 }}>
          {saving ? 'Guardando…' : `Guardar — ${selected.size} clase${selected.size!==1?'s':''}`}
        </button>
      </div>
    </div>
  );
}

const WEEKDAYS = ['Domingo', 'Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado'];

function WebAlumnos() {
  const [students, setStudents] = React.useState([]);
  const [videos, setVideos] = React.useState([]);
  const [exp, setExp] = React.useState(null);
  const [search, setSearch] = React.useState('');
  const [assigning, setAssigning] = React.useState(null);

  React.useEffect(() => {
    const { collection, getDocs, query, where } = window._fb;
    getDocs(query(collection(window._db,'users'), where('role','==','student'))).then(snap => {
      const list = snap.docs.map(d=>({id:d.id,...d.data()})).filter(isRegistered)
        .sort((a,b)=>(b.createdAt||'').localeCompare(a.createdAt||''));
      setStudents(list);
    });
    getDocs(collection(window._db, 'videos')).then(snap => {
      setVideos(snap.docs.map(d=>({id:d.id,...d.data()}))
        .sort((a,b)=>(b.createdAt||'').localeCompare(a.createdAt||'')));
    });
  }, []);

  const handleToggleActive = async (s) => {
    const nextActive = s.active === false;
    if (!nextActive && !confirm(`¿Desactivar el acceso de ${s.name || s.email}? Se le cerrará la sesión y no podrá volver a entrar hasta que lo reactives.`)) return;
    const { doc, updateDoc } = window._fb;
    await updateDoc(doc(window._db, 'users', s.id), { active: nextActive });
    setStudents(list => list.map(x => x.id === s.id ? { ...x, active: nextActive } : x));
  };

  const handleWeekdayChange = async (s, value) => {
    const { doc, updateDoc } = window._fb;
    const lessonWeekday = value === '' ? null : Number(value);
    await updateDoc(doc(window._db, 'users', s.id), { lessonWeekday });
    setStudents(list => list.map(x => x.id === s.id ? { ...x, lessonWeekday } : x));
  };

  const handleAssignSaved = (studentId, ids) => {
    setStudents(list => list.map(x => x.id === studentId ? { ...x, assignedVideos: ids } : x));
  };

  const filteredStudents = students.filter(s => {
    const q = search.trim().toLowerCase();
    if (!q) return true;
    return (s.name||'').toLowerCase().includes(q) || (s.email||'').toLowerCase().includes(q);
  });

  return (
    <div>
      <h1 style={{ fontSize:28, fontWeight:800, marginBottom:4 }}>Alumnos</h1>
      <p style={{ color:'var(--muted)', fontSize:14.5, marginBottom:16 }}>
        {students.length} alumnos inscritos · pulsa para ver el cuestionario.
      </p>
      <div className="field" style={{ maxWidth:760, marginBottom:20 }}>
        <Icon name="search" size={16} />
        <input placeholder="Buscar por nombre o email…" value={search} onChange={e=>setSearch(e.target.value)} />
      </div>
      <div className="col" style={{ gap:14, maxWidth:760 }}>
        {filteredStudents.length === 0
          ? <div style={{ color:'var(--muted)', textAlign:'center', padding:'60px 0' }}>{students.length === 0 ? 'Aún no hay alumnos registrados' : 'Sin alumnos que coincidan con la búsqueda'}</div>
          : filteredStudents.map(s => {
            const open = exp === s.id;
            const letter = s.name?.[0]?.toUpperCase() || s.email?.[0]?.toUpperCase() || '?';
            const q = s.questionnaire || {};
            return (
              <div key={s.id} className="card" style={{ overflow:'hidden' }}>
                <button onClick={()=>setExp(open?null:s.id)} className="gf-arow"
                  style={{ display:'flex', alignItems:'center', gap:16, padding:'16px 20px', width:'100%', textAlign:'left', background:'none', border:'none', cursor:'pointer', color:'var(--text)', fontFamily:'inherit' }}>
                  <Avatar letter={letter} size={48} />
                  <div className="col" style={{ flex:1, minWidth:0 }}>
                    <span style={{ fontSize:16, fontWeight:700 }}>{s.name||'Sin nombre'}</span>
                    <span style={{ fontSize:13, color:'var(--muted)', marginTop:2 }}>{s.email}</span>
                  </div>
                  <div className="gf-abadges" style={{ display:'flex', alignItems:'center', gap:16 }}>
                  {s.active === false && (
                    <span style={{ fontSize:11, fontWeight:700, padding:'4px 10px', borderRadius:999, background:'rgba(244,67,54,.12)', border:'1px solid rgba(244,67,54,.4)', color:'#f44336', whiteSpace:'nowrap' }}>
                      Desactivado
                    </span>
                  )}
                  <SubBadge user={s} />
                  <StatusBadge done={s.questionnaireCompleted} />
                  </div>
                  <Icon name="chevDown" size={20} color="var(--muted)"
                    style={{ transform: open?'rotate(180deg)':'none', transition:'.2s' }} />
                </button>
                {open && (
                  <div style={{ padding:'4px 20px 22px', borderTop:'1px solid var(--border)' }}>
                    {s.questionnaireCompleted && Object.keys(q).length > 0 ? (
                      <>
                        <div className="gf-qa-grid" style={{ display:'grid', gridTemplateColumns:'1fr 1fr', gap:12, marginTop:16 }}>
                          {q.experience && <QAns label="Experiencia" value={q.experience} />}
                          {q.goal && <QAns label="Objetivo" value={q.goal} />}
                          {q.availableTime && <QAns label="Tiempo disponible" value={q.availableTime} />}
                          {q.guitar && <QAns label="Guitarra" value={q.guitar} />}
                        </div>
                        {q.style && (
                          <>
                            <div style={{ fontSize:12, color:'var(--gold)', fontWeight:700, margin:'18px 0 9px', letterSpacing:.3 }}>ESTILOS DE INTERÉS</div>
                            <div className="row" style={{ flexWrap:'wrap', gap:8 }}>
                              {(Array.isArray(q.style)?q.style:[q.style]).map(e=>(
                                <span key={e} style={{ fontSize:13, fontWeight:600, padding:'7px 13px', borderRadius:999, background:'var(--gold-soft)', border:'1px solid var(--gold-line)', color:'var(--gold)' }}>{e}</span>
                              ))}
                            </div>
                          </>
                        )}
                        {q.extra && (
                          <div style={{ marginTop:18, background:'var(--surface)', borderLeft:'3px solid var(--gold)', borderRadius:'0 10px 10px 0', padding:'13px 16px' }}>
                            <div style={{ fontSize:11.5, color:'var(--gold)', fontWeight:700, marginBottom:5 }}>NOTAS</div>
                            <div style={{ fontSize:14, color:'var(--text-2)', lineHeight:1.55 }}>{q.extra}</div>
                          </div>
                        )}
                        <div style={{ fontSize:12, color:'var(--muted)', marginTop:14 }}>
                          Registrado el {s.createdAt ? new Date(s.createdAt).toLocaleDateString('es-ES',{day:'numeric',month:'long',year:'numeric'}) : '—'}
                        </div>
                      </>
                    ) : (
                      <div style={{ fontSize:14, color:'var(--muted)', padding:'16px 0 2px' }}>
                        Este alumno todavía no ha completado el cuestionario inicial.
                      </div>
                    )}
                    <div style={{ display:'flex', alignItems:'center', gap:12, marginTop:16, paddingTop:14, borderTop:'1px solid var(--border)', flexWrap:'wrap' }}>
                      {(() => { const { active, expiresAt } = subscriptionOf(s); return (
                        <span style={{ fontSize:12.5, color:'var(--muted)' }}>
                          {active
                            ? <>Suscripción activa hasta <b style={{ color:'var(--text-2)' }}>{expiresAt.toLocaleDateString('es-ES',{day:'numeric',month:'short',year:'numeric'})}</b></>
                            : s.subscription ? 'Suscripción caducada o cancelada' : 'Sin suscripción · solo ve la introducción'}
                        </span>
                      ); })()}
                      <div style={{ display:'flex', alignItems:'center', gap:6 }}>
                        <Icon name="calendar" size={13} color="var(--muted)" />
                        <select
                          value={s.lessonWeekday ?? ''}
                          onChange={e => handleWeekdayChange(s, e.target.value)}
                          style={{ background:'var(--input)', color:'var(--text)', border:'1px solid var(--border)', borderRadius:8, padding:'6px 8px', fontSize:13 }}
                        >
                          <option value="">Día de clase: sin fijar</option>
                          {WEEKDAYS.map((label, i) => <option key={i} value={i}>{label}</option>)}
                        </select>
                      </div>
                      <div style={{ flex:1 }} />
                      {s.levelVideoURL && (
                        <a href={s.levelVideoURL} target="_blank" rel="noreferrer"
                          style={{ display:'flex', alignItems:'center', gap:6, fontSize:13, fontWeight:600,
                            color:'var(--text-2)', padding:'7px 13px', borderRadius:8, border:'1px solid var(--border)', textDecoration:'none' }}>
                          <Icon name="play" size={13} color="var(--text-2)" /> Vídeo de nivel
                        </a>
                      )}
                      {s.followUpVideoURL && (
                        <a href={s.followUpVideoURL} target="_blank" rel="noreferrer"
                          style={{ display:'flex', alignItems:'center', gap:6, fontSize:13, fontWeight:600,
                            color:'var(--text-2)', padding:'7px 13px', borderRadius:8, border:'1px solid var(--border)', textDecoration:'none' }}>
                          <Icon name="play" size={13} color="var(--text-2)" /> Vídeo de seguimiento
                        </a>
                      )}
                      <button onClick={()=>setAssigning(s)}
                        style={{ display:'flex', alignItems:'center', gap:6, fontSize:13, fontWeight:600,
                          color:'var(--gold)', padding:'7px 13px', borderRadius:8, border:'1px solid var(--gold-line)' }}>
                        <Icon name="video" size={13} color="var(--gold)" />
                        Gestionar clases · {(s.assignedVideos||[]).length}
                      </button>
                      <button onClick={()=>handleToggleActive(s)}
                        style={{ display:'flex', alignItems:'center', gap:6, fontSize:13, fontWeight:600,
                          color: s.active===false ? 'var(--success)' : '#f44336', padding:'7px 13px', borderRadius:8,
                          border:`1px solid ${s.active===false ? 'var(--gold-line)' : 'rgba(244,67,54,.4)'}` }}>
                        <Icon name="lock" size={13} color={s.active===false ? 'var(--success)' : '#f44336'} />
                        {s.active===false ? 'Reactivar acceso' : 'Desactivar acceso'}
                      </button>
                    </div>
                  </div>
                )}
              </div>
            );
          })
        }
      </div>
      {assigning && (
        <WebAssignModal
          student={assigning}
          videos={videos}
          onClose={()=>setAssigning(null)}
          onSaved={(ids)=>handleAssignSaved(assigning.id, ids)}
        />
      )}
    </div>
  );
}

// ── Perfil ───────────────────────────────────────────────────
function WebProfile() {
  const [name, setName] = React.useState('');
  const [photo, setPhoto] = React.useState(null);
  const [newPass, setNewPass] = React.useState('');
  const [confirmPass, setConfirmPass] = React.useState('');
  const [loading, setLoading] = React.useState(true);
  const [saving, setSaving] = React.useState(false);
  const [msg, setMsg] = React.useState('');
  const [err, setErr] = React.useState('');

  React.useEffect(() => {
    const { doc, getDoc } = window._fb;
    getDoc(doc(window._db, 'users', window._auth.currentUser.uid)).then(snap => {
      if (snap.exists()) {
        setName(snap.data().name || '');
        setPhoto(snap.data().photoURL || null);
      }
      setLoading(false);
    });
  }, []);

  const handlePhoto = (e) => {
    const file = e.target.files[0];
    if (!file) return;
    const reader = new FileReader();
    reader.onload = () => setPhoto(reader.result);
    reader.readAsDataURL(file);
  };

  const save = async (e) => {
    e.preventDefault();
    setErr(''); setMsg('');
    if (!name.trim()) { setErr('El nombre no puede estar vacío.'); return; }
    if (newPass && newPass.length < 6) { setErr('La contraseña debe tener al menos 6 caracteres.'); return; }
    if (newPass && newPass !== confirmPass) { setErr('Las contraseñas no coinciden.'); return; }
    setSaving(true);
    try {
      const { doc, updateDoc, updatePassword } = window._fb;
      await updateDoc(doc(window._db, 'users', window._auth.currentUser.uid), { name: name.trim(), photoURL: photo });
      if (newPass) await updatePassword(window._auth.currentUser, newPass);
      setNewPass(''); setConfirmPass('');
      setMsg('¡Perfil actualizado!');
    } catch (e2) {
      setErr(e2.code === 'auth/requires-recent-login'
        ? 'Para cambiar la contraseña, cierra sesión y vuelve a entrar primero.'
        : 'Error: ' + e2.message);
    } finally { setSaving(false); }
  };

  if (loading) return <div style={{ color:'var(--muted)' }}>Cargando…</div>;

  return (
    <div style={{ maxWidth: 440 }}>
      <h1 style={{ fontSize: 28, fontWeight: 800, marginBottom: 20 }}>Mi perfil</h1>
      <form className="col" style={{ gap: 15 }} onSubmit={save}>
        <div style={{ position: 'relative', width: 84, height: 84, marginBottom: 8 }}>
          {photo ? (
            <img src={photo} style={{ width: 84, height: 84, borderRadius: 42, objectFit: 'cover', border: '3px solid var(--gold-line)' }} />
          ) : (
            <Avatar letter={(name?.[0] || '?').toUpperCase()} size={84} />
          )}
          <label style={{
            position: 'absolute', bottom: 0, right: 0, width: 28, height: 28, borderRadius: 14,
            background: 'var(--gold)', display: 'flex', alignItems: 'center', justifyContent: 'center',
            border: '2px solid var(--bg)', cursor: 'pointer',
          }}>
            <Icon name="video" size={13} color="#1c1a16" />
            <input type="file" accept="image/*" onChange={handlePhoto} style={{ display: 'none' }} />
          </label>
        </div>
        <div>
          <label className="web-label">Nombre</label>
          <input className="web-input" value={name} onChange={e => setName(e.target.value)} />
        </div>
        <div>
          <label className="web-label">Nueva contraseña</label>
          <input className="web-input" type="password" placeholder="Dejar vacío para no cambiar" value={newPass} onChange={e => setNewPass(e.target.value)} autoComplete="new-password" />
        </div>
        {newPass.length > 0 && (
          <div>
            <label className="web-label">Confirmar contraseña</label>
            <input className="web-input" type="password" value={confirmPass} onChange={e => setConfirmPass(e.target.value)} autoComplete="new-password" />
          </div>
        )}
        {err && <div style={{ fontSize:13, color:'#f44336', background:'rgba(244,67,54,.1)', borderRadius:8, padding:'8px 12px' }}>⚠ {err}</div>}
        {msg && <div style={{ fontSize:13, color:'var(--success)', background:'rgba(92,184,95,.1)', borderRadius:8, padding:'8px 12px' }}>{msg}</div>}
        <button className="btn-gold" type="submit" disabled={saving}>
          {saving ? 'Guardando…' : 'Guardar cambios'}
        </button>
      </form>
    </div>
  );
}

// ── Shell ────────────────────────────────────────────────────
const NAV = [
  { key:'dashboard',  icon:'grid',      label:'Dashboard'  },
  { key:'alumnos',    icon:'people',    label:'Alumnos'    },
  { key:'calendario', icon:'calendar',  label:'Calendario' },
  { key:'videos',     icon:'video',     label:'Vídeos'     },
  { key:'chats',      icon:'chat',      label:'Chats'      },
  { key:'perfil',     icon:'person',    label:'Mi perfil'  },
];

function WebApp() {
  const [page, setPage] = React.useState('dashboard');
  const narrow = useIsNarrow();
  // Escritorio: barra lateral que se oculta con la hamburguesa (se recuerda la
  // preferencia). Móvil: barra de pestañas inferior.
  const [open, setOpen] = React.useState(() => {
    try { return localStorage.getItem('gf-menu-open') !== '0'; } catch (e) { return true; }
  });
  // Se recarga al cambiar de página: son pocos documentos (solo los alumnos con clase hoy).
  const today = useTodayClasses(page);
  const pendingToday = (today.rows || []).filter(r => r.followUp === 'pending').length;

  const handleLogout = async () => {
    await window._fb.signOut(window._auth);
  };
  const toggleMenu = () => {
    setOpen(o => {
      try { localStorage.setItem('gf-menu-open', o ? '0' : '1'); } catch (e) {}
      return !o;
    });
  };
  const go = (key) => { setPage(key); window.scrollTo(0, 0); };
  const mainBg = 'radial-gradient(1200px 600px at 70% 0%, #27241f 0%, transparent 55%), var(--bg)';

  const sideContent = (
    <>
      <div style={{ marginBottom:26 }}>
        <img src={LOGO} alt="GuitarFabrik" style={{ height:26, marginBottom:8 }} />
        <div className="brand-academy" style={{ fontSize:10, letterSpacing:5 }}>ACADEMY</div>
        <div style={{ fontSize:12, color:'var(--muted)', marginTop:6 }}>Panel del profesor</div>
      </div>
      <div className="col" style={{ gap:5 }}>
        {NAV.map(n => (
          <button key={n.key} className={`web-nav${page===n.key?' on':''}`} onClick={()=>go(n.key)}>
            <Icon name={n.icon} size={19} /> {n.label}
            {n.key === 'calendario' && pendingToday > 0 && (
              <span title={`${pendingToday} alumno${pendingToday !== 1 ? 's' : ''} con clase hoy sin vídeo de seguimiento`}
                style={{ marginLeft: 'auto', minWidth: 20, height: 20, padding: '0 6px', borderRadius: 10, background: '#f44336', color: '#fff', fontSize: 11.5, fontWeight: 700, display: 'inline-flex', alignItems: 'center', justifyContent: 'center' }}>
                {pendingToday}
              </span>
            )}
          </button>
        ))}
      </div>
      <div style={{ flex:1, minHeight:20 }} />
      <button onClick={handleLogout} style={{ display:'flex', alignItems:'center', gap:10, border:'1px solid var(--border)', color:'var(--muted)', padding:'11px 14px', borderRadius:10, fontSize:14 }}>
        <Icon name="logout" size={18} color="var(--muted)" /> Cerrar sesión
      </button>
    </>
  );

  const topBar = narrow ? (
    <header style={{ position:'sticky', top:0, zIndex:40, display:'flex', alignItems:'center', justifyContent:'space-between', height:56, padding:'0 16px', background:'var(--surface)', borderBottom:'1px solid var(--border)' }}>
      <div>
        <img src={LOGO} alt="GuitarFabrik" style={{ height:20, display:'block' }} />
        <div className="brand-academy" style={{ fontSize:8, letterSpacing:4, marginTop:3 }}>ACADEMY</div>
      </div>
      <button onClick={handleLogout} aria-label="Cerrar sesión"
        style={{ display:'flex', alignItems:'center', justifyContent:'center', width:40, height:40, border:'1px solid var(--border)', borderRadius:10 }}>
        <Icon name="logout" size={18} color="var(--muted)" />
      </button>
    </header>
  ) : (
    <header style={{ position:'sticky', top:0, zIndex:40, display:'flex', alignItems:'center', gap:12, height:56, padding:'0 24px', background:'rgba(38,38,38,.92)', backdropFilter:'blur(8px)', borderBottom:'1px solid var(--border)' }}>
      <button onClick={toggleMenu} aria-label="Menú" aria-expanded={open}
        style={{ display:'flex', alignItems:'center', justifyContent:'center', width:40, height:40, borderRadius:10, border:'1px solid var(--border)', color:'var(--text)' }}>
        <Icon name="menu" size={20} />
      </button>
      <div style={{ fontSize:15, fontWeight:700 }}>{(NAV.find(n => n.key === page) || {}).label}</div>
      {pendingToday > 0 && page !== 'calendario' && (
        <button onClick={()=>go('calendario')} aria-label="Seguimientos pendientes"
          style={{ marginLeft:'auto', minWidth:22, height:22, padding:'0 7px', borderRadius:11, background:'#f44336', color:'#fff', fontSize:12, fontWeight:700 }}>
          {pendingToday}
        </button>
      )}
    </header>
  );

  const main = (
    <main style={{ flex:1, minWidth:0, minHeight:'calc(100dvh - 57px)', padding: narrow ? '20px 16px calc(84px + env(safe-area-inset-bottom))' : '34px 38px', background: mainBg }}>
      {page==='dashboard' && <WebDashboard today={today} onOpenCalendar={() => setPage('calendario')} />}
      {page==='alumnos'   && <WebAlumnos />}
      {page==='calendario' && <WebCalendar />}
      {page==='videos'    && <WebVideos />}
      {page==='chats'     && <WebChats />}
      {page==='perfil'    && <WebProfile />}
    </main>
  );

  if (narrow) {
    return (
      <div style={{ minHeight:'100dvh', background:'var(--bg)' }}>
        {topBar}
        {main}
        <nav style={{ position:'fixed', bottom:0, left:0, right:0, zIndex:50, display:'flex', background:'var(--surface)', borderTop:'1px solid var(--border)', paddingBottom:'env(safe-area-inset-bottom)' }}>
          {NAV.map(n => {
            const on = page === n.key;
            return (
              <button key={n.key} onClick={()=>go(n.key)} aria-label={n.label}
                style={{ flex:1, minWidth:0, height:64, position:'relative', display:'flex', flexDirection:'column', alignItems:'center', justifyContent:'center', gap:4, color: on ? 'var(--gold)' : 'var(--muted)', fontSize:10, fontWeight: on ? 700 : 500 }}>
                <Icon name={n.icon} size={21} color={on ? 'var(--gold)' : 'var(--muted)'} />
                <span style={{ maxWidth:'100%', overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap' }}>{n.label}</span>
                {n.key === 'calendario' && pendingToday > 0 && (
                  <span style={{ position:'absolute', top:6, left:'calc(50% + 6px)', minWidth:16, height:16, padding:'0 4px', borderRadius:8, background:'#f44336', color:'#fff', fontSize:10, fontWeight:700, display:'inline-flex', alignItems:'center', justifyContent:'center' }}>
                    {pendingToday}
                  </span>
                )}
              </button>
            );
          })}
        </nav>
      </div>
    );
  }

  return (
    <div style={{ display:'flex', minHeight:'100vh' }}>
      {open && (
        <aside className="web-side" style={{ position:'sticky', top:0, height:'100vh', overflowY:'auto' }}>
          {sideContent}
        </aside>
      )}
      <div style={{ flex:1, minWidth:0, display:'flex', flexDirection:'column' }}>
        {topBar}
        {main}
      </div>
    </div>
  );
}

// ── Forgot password ──────────────────────────────────────────
function ForgotPasswordView({ onBack }) {
  const [email, setEmail] = React.useState('');
  const [err, setErr] = React.useState('');
  const [loading, setLoading] = React.useState(false);
  const [sent, setSent] = React.useState(false);

  const handleSend = async (e) => {
    e.preventDefault();
    setErr('');
    if (!email.trim()) { setErr('Introduce tu email.'); return; }
    setLoading(true);
    try {
      await window._fb.sendPasswordResetEmail(window._auth, email.trim());
      setSent(true);
    } catch (e2) {
      const msgs = {
        'auth/invalid-email': 'Ese email no tiene un formato válido.',
        'auth/missing-email': 'Introduce tu email.',
        'auth/too-many-requests': 'Demasiados intentos. Espera unos minutos.',
        'auth/network-request-failed': 'Sin conexión. Comprueba tu red e inténtalo de nuevo.',
      };
      setErr(msgs[e2.code] || 'No se pudo enviar el enlace. Inténtalo de nuevo.');
    } finally {
      setLoading(false);
    }
  };

  return (
    <div style={{ width: '100%', minHeight: '100vh', background: 'radial-gradient(700px 400px at 50% 0%, #2c2925 0%, transparent 60%), var(--bg)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
      <div className="card" style={{ width: 380, maxWidth: 'calc(100vw - 32px)', padding: 36 }}>
        <div style={{ display: 'flex', justifyContent: 'center', marginBottom: 8 }}>
          <img src={LOGO} alt="GuitarFabrik" style={{ height: 34 }} />
        </div>
        <div className="brand-academy" style={{ textAlign: 'center', marginBottom: 6 }}>ACADEMY</div>
        <div style={{ textAlign: 'center', fontSize: 13.5, fontStyle: 'italic', color: 'var(--text-2)', marginBottom: 26 }}>Recuperar contraseña</div>

        {sent ? (
          <div className="col" style={{ gap: 16, alignItems: 'center' }}>
            <div style={{ fontSize: 13.5, color: 'var(--text-2)', textAlign: 'center', lineHeight: 1.5 }}>
              Si existe una cuenta con ese email, te hemos enviado un enlace para restablecer tu contraseña.
              <br /><br />
              Si no lo ves en unos minutos, revisa también tu carpeta de spam.
            </div>
            <button className="btn-action" style={{ width: '100%' }} onClick={onBack}>Volver a Entrar</button>
          </div>
        ) : (
          <form className="col" style={{ gap: 13 }} onSubmit={handleSend}>
            <div>
              <label className="web-label">Email</label>
              <input className="web-input" type="email" placeholder="profesor@guitarfabrik.com" value={email} onChange={e => setEmail(e.target.value)} required />
            </div>
            {err && <div style={{ fontSize: 13, color: '#f44336', background: 'rgba(244,67,54,.1)', borderRadius: 8, padding: '8px 12px' }}>⚠ {err}</div>}
            <button className="btn-action" type="submit" disabled={loading} style={{ marginTop: 8 }}>
              {loading ? 'Enviando…' : 'Enviar enlace'}
            </button>
            <button type="button" onClick={onBack} style={{ textAlign: 'center', marginTop: 4, fontSize: 14, color: 'var(--text-2)' }}>
              Volver a <span style={{ color: 'var(--gold)', fontWeight: 600 }}>Entrar</span>
            </button>
          </form>
        )}
      </div>
    </div>
  );
}

// ── Root with auth guard ─────────────────────────────────────
function PanelRoot() {
  const [authState, setAuthState] = React.useState('loading'); // loading|out|in
  const [deniedMsg, setDeniedMsg] = React.useState('');
  const [showForgot, setShowForgot] = React.useState(false);

  React.useEffect(() => {
    return window._fb.onAuthStateChanged(window._auth, async (u) => {
      if (!u) { setAuthState('out'); return; }
      try {
        const snap = await window._fb.getDoc(window._fb.doc(window._db, 'users', u.uid));
        if (snap.exists() && snap.data().role === 'teacher') {
          setAuthState('in');
        } else {
          await window._fb.signOut(window._auth);
          setDeniedMsg('Esta cuenta no tiene acceso al panel.');
          setAuthState('out');
        }
      } catch {
        await window._fb.signOut(window._auth);
        setDeniedMsg('No se pudo verificar el acceso. Intenta de nuevo.');
        setAuthState('out');
      }
    });
  }, []);

  if (authState === 'loading') {
    return (
      <div style={{ minHeight:'100vh', display:'flex', alignItems:'center', justifyContent:'center', background:'var(--bg)' }}>
        <div style={{ width:40, height:40, borderRadius:'50%', border:'3px solid rgba(201,168,76,.18)', borderTopColor:'var(--gold)', animation:'spin .9s linear infinite' }} />
        <style>{`@keyframes spin{to{transform:rotate(360deg)}}`}</style>
      </div>
    );
  }

  if (authState === 'out') {
    if (showForgot) return <ForgotPasswordView onBack={() => setShowForgot(false)} />;
    return <WebLogin onLogin={() => {}} deniedMsg={deniedMsg} onForgot={() => setShowForgot(true)} />;
  }
  return <WebApp />;
}

// Wait for Firebase to be ready before rendering
const mount = () => ReactDOM.createRoot(document.getElementById('root')).render(<PanelRoot />);
if (window._fbReady) { mount(); }
else { document.addEventListener('fbReady', mount, { once: true }); }
