﻿const { useEffect, useMemo, useRef, useState } = React;

function createCourseSections(courseData) {
  return [
    { id: 'overview', label: 'Ficha técnica', type: 'static' },
    ...courseData.lessons.map((lesson, lessonIndex) => ({
      id: `lesson-${lessonIndex + 1}`,
      label: lesson.label,
      type: 'lesson',
      lessonIndex,
    })),
    { id: 'final-evaluation', label: 'Evaluación Final', type: 'final' },
    { id: 'downloadable-material', label: 'Material descargable', type: 'download' },
  ];
}

function createDefaultCourseProgress() {
  let studentName = 'Usuario';

  if (typeof window !== 'undefined') {
    try {
      const currentUser = JSON.parse(window.localStorage.getItem('eg_user') || 'null');
      studentName = currentUser?.name || studentName;
    } catch (error) {
      studentName = 'Usuario';
    }
  }

  return {
    lessonsCompleted: {},
    lessonVideosCompleted: {},
    lessonQuizzesCompleted: {},
    lessonQuizAnswers: {},
    finalExamAnswers: {},
    finalExamQuestionOrder: [],
    finalExamOptionOrder: {},
    finalExamAttempts: 0,
    finalExamAttemptStartedAt: null,
    finalExamPassed: false,
    finalExamScore: null,
    certificateUnlocked: false,
    currentSectionId: 'overview',
    studentName,
  };
}

const COURSE_RUNTIME_CONFIGS = {
  EG101: {
    storageKey: 'eg101_course_progress_v4',
    dataKey: 'EG101_COURSE_DATA',
    isPublished: true,
  },
  EG102: {
    storageKey: 'eg102_course_progress_v1',
    dataKey: 'EG102_COURSE_DATA',
    isPublished: true,
  },
  EG103: {
    storageKey: 'eg103_course_progress_v2',
    dataKey: 'EG103_COURSE_DATA',
    isPublished: true,
  },
};

const GENERIC_PUBLISHED_COURSES = new Set([]);

function getStructuredCourseConfig(courseCode) {
  if (!courseCode || !COURSE_RUNTIME_CONFIGS[courseCode]) return null;
  const runtimeConfig = COURSE_RUNTIME_CONFIGS[courseCode];
  const courseData = typeof window !== 'undefined' ? window[runtimeConfig.dataKey] : null;
  if (!courseData) return null;

  return {
    ...runtimeConfig,
    courseData,
    sections: createCourseSections(courseData),
  };
}

function readCourseProgress(storageKey) {
  const fallback = createDefaultCourseProgress();

  if (typeof window === 'undefined') return fallback;

  try {
    const saved = JSON.parse(window.localStorage.getItem(storageKey) || 'null');
    return saved ? { ...fallback, ...saved } : fallback;
  } catch (error) {
    return fallback;
  }
}

async function fetchCourseRecordByCode(courseCode) {
  const courses = await apiCall('/api/courses');
  if (!Array.isArray(courses)) return null;
  return courses.find((course) => course.code === courseCode) || null;
}

function useCourseSidebarBelow(breakpoint = 1180) {
  const [sidebarBelow, setSidebarBelow] = useState(() =>
    typeof window !== 'undefined' ? window.innerWidth < breakpoint : false,
  );

  useEffect(() => {
    if (typeof window === 'undefined') return undefined;

    const syncLayout = () => {
      setSidebarBelow(window.innerWidth < breakpoint);
    };

    syncLayout();
    window.addEventListener('resize', syncLayout);
    return () => window.removeEventListener('resize', syncLayout);
  }, [breakpoint]);

  return sidebarBelow;
}

function shuffleArray(items) {
  const nextItems = [...items];

  for (let index = nextItems.length - 1; index > 0; index -= 1) {
    const swapIndex = Math.floor(Math.random() * (index + 1));
    [nextItems[index], nextItems[swapIndex]] = [nextItems[swapIndex], nextItems[index]];
  }

  return nextItems;
}

const FINAL_EXAM_PICK_COUNT = 10;

function createFinalExamOrder(questions) {
  const sampledQuestions =
    questions.length > FINAL_EXAM_PICK_COUNT ? shuffleArray(questions).slice(0, FINAL_EXAM_PICK_COUNT) : [...questions];
  const shuffledQuestions = shuffleArray(sampledQuestions.map((question) => question.id));
  const optionOrder = sampledQuestions.reduce((accumulator, question) => {
    accumulator[question.id] = shuffleArray(question.options.map((option) => option.id));
    return accumulator;
  }, {});

  return {
    questionOrder: shuffledQuestions,
    optionOrder,
  };
}

const COURSE_PDF_SUPPORT_CONTENT = {
  EG101: {
  subtitle: 'Material de apoyo para operadores de televigilancia',
  title: 'EG101 — Introducción al Servicio eGuardian',
  closing: {
    title: 'Cierre del curso',
    paragraphs: [
      'EG101 entrega una base común para entender cómo opera eGuardian y qué se espera de un operador en formación.',
      'El cierre del curso busca reforzar una idea central: observar bien, entender el contexto y actuar a tiempo marca la diferencia en la prevención.',
      'Este material puede utilizarse como apoyo de lectura, repaso posterior al video y referencia inicial antes de avanzar al siguiente módulo.',
    ],
  },
  lessons: {
    'eg101-lesson-1': {
      videoTitle: 'Video 1 — ¿Qué es eGuardian?',
      videoSummary: 'Presenta el servicio eGuardian como un modelo de televigilancia activa, explica su enfoque preventivo y diferencia su operación del CCTV tradicional.',
      keyIdeas: [
        'eGuardian monitorea en tiempo real para prevenir incidentes.',
        'El servicio combina tecnología, protocolos y criterio humano.',
        'El objetivo no es solo registrar, sino anticiparse y actuar oportunamente.',
      ],
      ideaHighlight: 'eGuardian no espera a que el incidente ocurra: observa, analiza y responde a tiempo para prevenirlo.',
    },
    'eg101-lesson-2': {
      videoTitle: 'Video 2 — Rol del Operador eGuardian',
      videoSummary: 'Explica por qué el operador cumple un rol activo dentro del servicio, cómo analiza el contexto y por qué su criterio es clave para prevenir eventos.',
      keyIdeas: [
        'El operador observa, entiende y actúa.',
        'La atención, el análisis y el tiempo de reacción sostienen una operación efectiva.',
        'Un operador distraído o pasivo debilita la prevención.',
      ],
      ideaHighlight: 'La tecnología apoya la operación, pero el criterio del operador sigue siendo el factor que convierte la observación en prevención.',
    },
    'eg101-lesson-3': {
      videoTitle: 'Video 3 — Clasificación de eventos',
      videoSummary: 'Muestra cómo clasificar eventos críticos, relevantes e informativos para priorizar correctamente la respuesta y evitar errores operativos.',
      keyIdeas: [
        'No todos los eventos requieren la misma respuesta.',
        'Un evento crítico exige acción inmediata.',
        'Una clasificación correcta ayuda a enfocar la atención en lo importante.',
      ],
      ideaHighlight: 'Clasificar bien un evento permite actuar con oportunidad, sin subestimar riesgos ni distraerse con situaciones normales.',
    },
    'eg101-lesson-4': {
      videoTitle: 'Video 4 — Cultura eGuardian',
      videoSummary: 'Refuerza los principios de trabajo esperados en la operación diaria: atención constante, criterio profesional, responsabilidad y respuesta oportuna.',
      keyIdeas: [
        'La cultura eGuardian se refleja en cada decisión del operador.',
        'No se deben normalizar riesgos ni ignorar señales.',
        'La calidad del servicio depende del foco y criterio operativo.',
      ],
      ideaHighlight: 'La cultura eGuardian se sostiene en la práctica diaria: foco, responsabilidad y decisiones oportunas en cada turno.',
    },
  },
  },
  EG102: {
    subtitle: 'Material de apoyo para operadores de televigilancia',
    title: 'EG102 — Flujo de Monitoreo en Tiempo Real',
    closing: {
      title: 'Cierre del curso',
      paragraphs: [
        'EG102 entrega una base práctica para entender cómo funciona el monitoreo en tiempo real dentro de eGuardian.',
        'El aprendizaje central del curso es seguir un flujo simple y ordenado: observar, entender, decidir y actuar.',
        'Este material puede utilizarse como guía de apoyo, repaso posterior al video y referencia antes de avanzar al siguiente módulo.',
      ],
    },
    lessons: {
      'eg102-lesson-1': {
        videoTitle: 'Video 1 — Monitoreo en tiempo real',
        videoSummary: 'Explica qué significa monitorear en el momento, por qué no todo lo observado es un problema y cómo el operador interpreta cada situación.',
        keyIdeas: [
          'El monitoreo ocurre mientras la situación está sucediendo.',
          'No todo lo que se observa representa un riesgo.',
          'El operador interpreta el contexto antes de decidir si actuar.',
        ],
        ideaHighlight: 'Monitorear en tiempo real no es solo mirar pantallas: es entender lo que pasa y actuar a tiempo.',
      },
      'eg102-lesson-2': {
        videoTitle: 'Video 2 — Flujo de monitoreo',
        videoSummary: 'Presenta la secuencia de trabajo del monitoreo eGuardian y muestra por qué seguir el orden correcto ayuda a tomar mejores decisiones.',
        keyIdeas: [
          'El monitoreo sigue una secuencia simple y clara.',
          'Saltarse pasos puede generar errores.',
          'El orden ayuda a decidir mejor frente a cada situación.',
        ],
        ideaHighlight: 'Observar, entender, decidir y actuar es la base del flujo operativo eGuardian.',
      },
      'eg102-lesson-3': {
        videoTitle: 'Video 3 — Errores en monitoreo',
        videoSummary: 'Muestra errores frecuentes durante el monitoreo y refuerza la importancia de verificar antes de asumir o actuar.',
        keyIdeas: [
          'La distracción puede hacer perder señales importantes.',
          'No se debe asumir que algo es normal sin revisarlo.',
          'Primero se entiende la situación y luego se actúa.',
        ],
        ideaHighlight: 'Un buen operador no solo responde bien: también evita errores que pueden abrir espacio a un incidente.',
      },
      'eg102-lesson-4': {
        videoTitle: 'Video 4 — Buenas prácticas',
        videoSummary: 'Refuerza hábitos de trabajo que ayudan a mantener un monitoreo ordenado, claro y efectivo durante toda la operación.',
        keyIdeas: [
          'Mantener foco constante mejora la detección.',
          'Verificar antes de actuar evita decisiones incorrectas.',
          'Comunicar con claridad ayuda a que la respuesta sea oportuna.',
        ],
        ideaHighlight: 'La calidad del monitoreo depende de cómo trabaja el operador en cada momento del turno.',
      },
      'eg102-lesson-5': {
        videoTitle: 'Video 5 — Aplicación del flujo',
        videoSummary: 'Muestra cómo aplicar el flujo completo cuando aparecen varias señales al mismo tiempo y es necesario priorizar correctamente.',
        keyIdeas: [
          'Las situaciones reales pueden ser dinámicas y cambiantes.',
          'El operador debe priorizar sin perder el orden del análisis.',
          'El criterio mejora con práctica y aplicación constante.',
        ],
        ideaHighlight: 'El flujo de monitoreo se vuelve útil de verdad cuando se aplica en situaciones reales y cambiantes.',
      },
    },
  },
  EG103: {
    subtitle: 'Material de apoyo para operadores de televigilancia',
    title: 'EG103 — Sistemas CCTV',
    closing: {
      title: 'Cierre del curso',
      paragraphs: [
        'EG103 entrega una base t\u00e9cnica inicial para comprender c\u00f3mo funciona un sistema CCTV y qu\u00e9 se puede esperar de \u00e9l en una operaci\u00f3n real.',
        'El aprendizaje central del curso es que la tecnolog\u00eda de videovigilancia aporta valor cuando se entiende bien su estructura, su capacidad y sus limitaciones.',
        'Este material puede utilizarse como referencia t\u00e9cnica inicial antes de avanzar a cursos m\u00e1s aplicados dentro de la academia.',
      ],
    },
    lessons: {
      'eg103-lesson-1': {
        videoTitle: 'Video 1 — Qué es un sistema CCTV',
        videoSummary: 'Presenta el concepto de sistema CCTV, su objetivo en seguridad y por qué debe entenderse como una red organizada de equipos y no como una cámara aislada.',
        keyIdeas: [
          'Un sistema CCTV permite observar y registrar imágenes de un entorno.',
          'Su utilidad depende de varios equipos trabajando en conjunto.',
          'La comprensión del sistema es clave para operarlo correctamente.',
        ],
        ideaHighlight: 'Un sistema CCTV no es solo una cámara: es una estructura completa que apoya la vigilancia y la interpretación de eventos.',
      },
      'eg103-lesson-2': {
        videoTitle: 'Video 2 — Componentes de un sistema CCTV',
        videoSummary: 'Explica los componentes principales del sistema CCTV y cómo cada uno aporta a la captura, visualización y almacenamiento de información.',
        keyIdeas: [
          'Las cámaras, grabadores, monitores, red y energía forman parte del sistema.',
          'Cada componente cumple una función específica dentro de la operación.',
          'Si un componente falla, el rendimiento general puede verse afectado.',
        ],
        ideaHighlight: 'Conocer los componentes del sistema ayuda a detectar fallas y entender mejor el funcionamiento global del CCTV.',
      },
      'eg103-lesson-3': {
        videoTitle: 'Video 3 — Tipos de cámaras',
        videoSummary: 'Muestra los principales tipos de cámaras CCTV y explica cómo su diseño influye en la cobertura, el alcance y la lectura de las escenas.',
        keyIdeas: [
          'No todas las cámaras cumplen la misma función.',
          'El tipo de cámara influye en cobertura, ángulo y detalle.',
          'Conocer estas diferencias mejora la interpretación operativa.',
        ],
        ideaHighlight: 'Comprender qué tipo de cámara se está usando ayuda a interpretar mejor lo que realmente puede observarse en pantalla.',
      },
      'eg103-lesson-4': {
        videoTitle: 'Video 4 — Calidad de imagen y visualización',
        videoSummary: 'Refuerza cómo la resolución, la iluminación, el enfoque y la forma de visualización afectan la capacidad de interpretar una imagen CCTV.',
        keyIdeas: [
          'La calidad de imagen afecta directamente la interpretación.',
          'Una imagen poco clara puede limitar la confirmación de detalles.',
          'El análisis debe considerar las capacidades reales de visualización.',
        ],
        ideaHighlight: 'La calidad de imagen no es un detalle secundario: condiciona lo que el operador puede confirmar y decidir.',
      },
      'eg103-lesson-5': {
        videoTitle: 'Video 5 — Limitaciones de los sistemas CCTV',
        videoSummary: 'Explica las principales limitaciones técnicas y operativas del CCTV para usar el sistema con expectativas realistas y mejor criterio profesional.',
        keyIdeas: [
          'El CCTV es útil, pero no resuelve todo por sí solo.',
          'Existen límites de cobertura, imagen y contexto.',
          'El criterio del operador también consiste en reconocer esos límites.',
        ],
        ideaHighlight: 'Entender las limitaciones del sistema mejora la calidad del análisis y evita conclusiones incorrectas sobre lo que una imagen puede mostrar.',
      },
    },
  },
  EG105: {
    subtitle: 'Material de apoyo para operadores de televigilancia',
    title: 'EG105 — Ley 21.659',
    closing: {
      title: 'Cierre del curso',
      paragraphs: [
        'EG105 ya cuenta con la misma base estructural de los cursos anteriores para soportar lecciones, evaluación, certificado y material PDF.',
        'El siguiente paso será completar progresivamente el contenido formativo definitivo del curso, manteniendo la experiencia interna ya resuelta.',
        'Esta base permite seguir creciendo sin rehacer navegación, progreso ni componentes principales.',
      ],
    },
    lessons: {
      'eg105-lesson-1': {
        videoTitle: 'Video 1 — Introducción a la Ley 21.659',
        videoSummary: 'Presenta la base inicial del curso y deja lista la estructura para cargar contenido específico del módulo legal.',
        keyIdeas: [
          'La estructura del curso ya está habilitada.',
          'El contenido definitivo se puede incorporar progresivamente.',
          'Se mantiene el mismo estándar visual y funcional de la academia.',
        ],
        ideaHighlight: 'Primero se asegura una estructura reusable y consistente; luego se completa el contenido específico del curso.',
      },
      'eg105-lesson-2': {
        videoTitle: 'Video 2 — Contenido en preparación',
        videoSummary: 'Mantiene activa la segunda lección dentro del flujo estructurado del curso.',
        keyIdeas: [
          'La arquitectura del curso ya reconoce esta lección.',
          'Videos, lectura, ejemplos y quiz pueden cargarse después.',
          'La experiencia del alumno se mantiene consistente.',
        ],
        ideaHighlight: 'La estructura reusable permite avanzar por etapas sin perder coherencia entre cursos.',
      },
      'eg105-lesson-3': {
        videoTitle: 'Video 3 — Contenido en preparación',
        videoSummary: 'Reserva el espacio de la tercera lección dentro del curso publicado.',
        keyIdeas: [
          'El curso ya soporta progreso y navegación por sección.',
          'Las lecciones pueden refinarse sin rehacer la base.',
          'La consistencia entre cursos mejora la escalabilidad.',
        ],
        ideaHighlight: 'Escalar la academia depende tanto de la estructura como del contenido.',
      },
      'eg105-lesson-4': {
        videoTitle: 'Video 4 — Contenido en preparación',
        videoSummary: 'Mantiene la cuarta lección disponible dentro de la misma lógica interna de EG101 a EG103.',
        keyIdeas: [
          'La plataforma ya ofrece una experiencia uniforme.',
          'El alumno encuentra el mismo patrón de aprendizaje.',
          'La carga de contenido posterior es más simple.',
        ],
        ideaHighlight: 'La consistencia de uso también es parte de la calidad de la academia.',
      },
      'eg105-lesson-5': {
        videoTitle: 'Video 5 — Cierre y continuidad del curso',
        videoSummary: 'Cierra la base estructural de EG105 y deja preparada la evaluación final del módulo.',
        keyIdeas: [
          'EG105 ya cuenta con recorrido interno completo.',
          'La base se integró al catálogo, al certificado y al PDF.',
          'El siguiente paso es completar el contenido real del curso.',
        ],
        ideaHighlight: 'Con la estructura habilitada, el desarrollo del curso puede avanzar de forma ordenada y sostenible.',
      },
    },
  },
};

function getCourseLessonReadingContent(courseCode, lesson) {
  if (courseCode !== 'EG101') {
    return lesson.content || [];
  }

  if (lesson.id === 'eg101-lesson-1') {
    return [
      'eGuardian es un servicio de televigilancia activa diseñado para prevenir incidentes de seguridad mediante el monitoreo en tiempo real.',
      'A diferencia de los sistemas tradicionales de CCTV, eGuardian trabaja con un enfoque proactivo: observa, analiza y gestiona cada evento mientras ocurre.',
      'El servicio combina tecnología, protocolos y criterio humano para detectar riesgos, evaluar el contexto y responder a tiempo.',
      'Su propósito no es solo registrar lo que sucede, sino anticiparse a los incidentes y ayudar a evitarlos.',
    ];
  }

  if (lesson.id === 'eg101-lesson-2') {
    return [
      'El operador es una parte clave del sistema eGuardian.',
      'La tecnología permite ver lo que pasa. Pero es el operador quien entiende la situación y decide qué hacer.',
      'Su trabajo no es solo mirar cámaras. También debe observar, entender lo que está viendo y actuar a tiempo.',
      'Cada decisión parte de una secuencia simple: observar, entender y actuar.',
      'Una operación efectiva depende de tres factores clave:',
      '• Atención constante',
      '• Capacidad de análisis',
      '• Tiempo de reacción',
      'En este rol, distraerse, demorarse o no actuar puede permitir que un incidente ocurra.',
      'El operador no solo observa. El operador evita que ocurran los incidentes.',
    ];
  }

  if (lesson.id === 'eg101-lesson-3') {
    return [
      'Un evento es cualquier situación que aparece en las cámaras y que requiere atención del operador.',
      'No todo lo que se ve es igual de importante.',
      'Por eso, los eventos se clasifican según su nivel de riesgo:',
      'Evento crítico',
      'Requiere acción inmediata.',
      'Son situaciones donde existe un riesgo real o inminente.',
      'Ejemplos:',
      '• Intento de ingreso no autorizado',
      '• Manipulación de accesos',
      '• Persona dentro de un área restringida',
      'Evento relevante',
      'Requiere observación y seguimiento.',
      'No es una amenaza inmediata, pero puede evolucionar.',
      'Ejemplos:',
      '• Persona merodeando',
      '• Vehículo detenido por tiempo prolongado',
      '• Comportamiento inusual',
      'Evento informativo',
      'No requiere acción.',
      'Son situaciones normales dentro de la operación.',
      'Ejemplos:',
      '• Tránsito habitual de personas',
      '• Actividades esperadas en horarios normales',
      'Rol del operador',
      'El operador debe observar, analizar y clasificar cada evento correctamente.',
      'Una buena clasificación permite:',
      '• actuar a tiempo en situaciones críticas',
      '• no perder de vista eventos que pueden escalar',
      '• evitar distracciones con eventos normales',
      'No todos los eventos requieren acción.',
      'Pero todos deben ser entendidos correctamente.',
    ];
  }

  if (lesson.id === 'eg101-lesson-4') {
    return [
      'La cultura eGuardian define cómo trabaja un operador en su día a día.',
      'No se trata solo de seguir protocolos, sino de mantener una forma de actuar basada en atención, criterio y responsabilidad.',
      'Cada operador representa el estándar del servicio.',
      'Por eso, se espera que actúe con foco constante, evalúe correctamente lo que observa y responda de forma oportuna.',
      'Principios clave',
      'En eGuardian, todos los operadores deben:',
      '• Mantener atención constante durante el monitoreo',
      '• Tomar en serio cada evento, sin asumir que “no pasará nada”',
      '• Actuar a tiempo, según el nivel de riesgo',
      '• Seguir protocolos, sin dejar de aplicar criterio',
      'Conductas que no forman parte de la cultura eGuardian',
      'Para asegurar un servicio profesional, no se permiten:',
      '• Distracción durante el monitoreo',
      '• Ignorar o no revisar eventos',
      '• Asumir que algo es normal sin verificar',
      '• Retrasar acciones cuando se requiere intervención',
      'En eGuardian, la calidad del servicio depende del operador.',
      'Cada decisión cuenta.',
    ];
  }

  return lesson.content || [];
}

function getCoursePdfContent(courseData) {
  const lessonHeadingByCourse = {
    EG101: {
      'eg101-lesson-1': 'LECCIÓN 1 — ¿Qué es eGuardian?',
      'eg101-lesson-2': 'LECCIÓN 2 — Rol del Operador',
      'eg101-lesson-3': 'LECCIÓN 3 — Clasificación de Eventos',
      'eg101-lesson-4': 'LECCIÓN 4 — Cultura eGuardian',
    },
    EG102: {
      'eg102-lesson-1': 'LECCIÓN 1 — ¿Qué es el monitoreo en tiempo real?',
      'eg102-lesson-2': 'LECCIÓN 2 — Flujo operativo del monitoreo',
      'eg102-lesson-3': 'LECCIÓN 3 — Errores comunes en el monitoreo',
      'eg102-lesson-4': 'LECCIÓN 4 — Buenas prácticas de monitoreo',
      'eg102-lesson-5': 'LECCIÓN 5 — Aplicación del flujo en situaciones reales',
    },
    EG103: {
      'eg103-lesson-1': 'LECCIÓN 1 — Qué es un sistema CCTV',
      'eg103-lesson-2': 'LECCIÓN 2 — Componentes de un sistema CCTV',
      'eg103-lesson-3': 'LECCIÓN 3 — Tipos de cámaras',
      'eg103-lesson-4': 'LECCIÓN 4 — Calidad de imagen y visualización',
      'eg103-lesson-5': 'LECCIÓN 5 — Limitaciones de los sistemas CCTV',
    },
    EG105: {
      'eg105-lesson-1': 'LECCIÓN 1 — Introducción a la Ley 21.659',
      'eg105-lesson-2': 'LECCIÓN 2 — Contenido en preparación',
      'eg105-lesson-3': 'LECCIÓN 3 — Contenido en preparación',
      'eg105-lesson-4': 'LECCIÓN 4 — Contenido en preparación',
      'eg105-lesson-5': 'LECCIÓN 5 — Cierre y continuidad del curso',
    },
  };
  const pdfSupportContent = COURSE_PDF_SUPPORT_CONTENT[courseData.code];
  const lessonHeadingById = lessonHeadingByCourse[courseData.code] || {};

  return {
    title: pdfSupportContent?.title || `${courseData.code} — ${courseData.title}`,
    subtitle: pdfSupportContent?.subtitle || 'Material de apoyo para operadores de televigilancia',
    description: courseData.longDescription,
    lessons: courseData.lessons.map((lesson) => ({
      id: lesson.id,
      heading: lessonHeadingById[lesson.id] || `${lesson.label.toUpperCase()} — ${lesson.title}`,
      reading: getCourseLessonReadingContent(courseData.code, lesson),
      videoTitle: pdfSupportContent?.lessons?.[lesson.id]?.videoTitle || `Video — ${lesson.title}`,
      videoSummary: pdfSupportContent?.lessons?.[lesson.id]?.videoSummary || lesson.objective,
      keyIdeas: pdfSupportContent?.lessons?.[lesson.id]?.keyIdeas || [lesson.objective],
      ideaHighlight: pdfSupportContent?.lessons?.[lesson.id]?.ideaHighlight || lesson.objective,
      examples: lesson.examples || [],
      quiz: lesson.quiz || [],
    })),
    closing: pdfSupportContent?.closing || {
      title: 'Cierre del curso',
      paragraphs: [`${courseData.title} entrega una base inicial para avanzar con mayor seguridad al siguiente módulo.`],
    },
  };
}

function computeCourseProgress(courseData, progress) {
  const totalLessons = courseData.lessons.length;
  const completedLessons = courseData.lessons.filter((lesson) => progress.lessonsCompleted[lesson.id]).length;
  const completedLessonQuizzes = courseData.lessons.filter((lesson) => progress.lessonQuizzesCompleted[lesson.id]).length;
  const finalEvaluationCompleted = progress.finalExamPassed ? 1 : 0;
  const completedUnits = completedLessons + completedLessonQuizzes + finalEvaluationCompleted;
  const totalUnits = totalLessons * 2 + 1;
  const percentage = Math.round((completedUnits / totalUnits) * 100);
  const status = progress.finalExamPassed && completedLessons === totalLessons
    ? 'Completado'
    : completedUnits > 0
      ? 'En progreso'
      : 'No iniciado';

  return {
    completedLessons,
    completedLessonQuizzes,
    totalLessons,
    totalUnits,
    completedUnits,
    percentage,
    status,
    canUnlockCertificate: completedLessons === totalLessons && progress.finalExamPassed,
  };
}

function getAllowedCourseSectionId(targetSectionId, courseData, progressState, sections, allowBypass = false) {
  if (allowBypass) return targetSectionId;
  if (targetSectionId === 'overview') return 'overview';

  const targetSection = sections.find((section) => section.id === targetSectionId);
  if (!targetSection) return 'overview';

  const targetLessonIndex =
    targetSection.type === 'lesson'
      ? targetSection.lessonIndex
      : courseData.lessons.length;

  for (let index = 0; index < targetLessonIndex; index += 1) {
    const lesson = courseData.lessons[index];
    if (!progressState.lessonVideosCompleted[lesson.id]) {
      return `lesson-${index + 1}`;
    }
  }

  return targetSectionId;
}

function isLessonCompletedByRules(courseData, progressState, lessonId, quizAnswersOverride) {
  const lesson = courseData.lessons.find((item) => item.id === lessonId);
  if (!lesson) return false;

  const answers = quizAnswersOverride || progressState.lessonQuizAnswers[lessonId] || {};
  const score = lesson.quiz.reduce(
    (total, question) => total + (answers[question.id] === question.correctOptionId ? 1 : 0),
    0,
  );

  return !!progressState.lessonVideosCompleted[lessonId] && score === lesson.quiz.length;
}

function getCourseWithRuntimeProgress(course) {
  const structuredConfig = getStructuredCourseConfig(course.code);
  if (!structuredConfig) {
    return {
      ...course,
      isReady: GENERIC_PUBLISHED_COURSES.has(course.code),
    };
  }
  const runtime = readCourseProgress(structuredConfig.storageKey);
  const summary = computeCourseProgress(structuredConfig.courseData, runtime);
  return {
    ...course,
    title: structuredConfig.courseData.shortTitle || structuredConfig.courseData.title.replace(/^[A-Z]{2}\d+\s+[—-]\s+/, ''),
    dur: structuredConfig.courseData.duration,
    mods: structuredConfig.courseData.lessons.length,
    progress: summary.percentage,
    grade: runtime.finalExamScore,
    desc: structuredConfig.courseData.description,
    isReady: !!structuredConfig.isPublished,
  };
}

function CourseCatalog({ role, onSelectCourse }) {
  const [filterLevel, setFilterLevel] = useState(0);
  const [search, setSearch] = useState('');
  const [catalogCourses, setCatalogCourses] = useState(() => COURSES.map(getCourseWithRuntimeProgress));

  useEffect(() => {
    setCatalogCourses(COURSES.map(getCourseWithRuntimeProgress));
  }, []);

  const filtered = catalogCourses.filter((course) => {
    const matchLevel = filterLevel === 0 || course.level === filterLevel;
    const matchSearch =
      !search ||
      course.title.toLowerCase().includes(search.toLowerCase()) ||
      course.code.toLowerCase().includes(search.toLowerCase());
    return matchLevel && matchSearch;
  });

  const levelCounts = [0, 1, 2, 3, 4].map((level) =>
    level === 0 ? catalogCourses.length : catalogCourses.filter((course) => course.level === level).length,
  );



  return (
    <div>
      <div style={{ marginBottom: 24 }}>
        <h2
          style={{
            fontFamily: 'Space Grotesk,sans-serif',
            fontWeight: 800,
            fontSize: 24,
            color: BRAND.text.primary,
            margin: '0 0 4px',
          }}
        >
          Catálogo de Cursos
        </h2>
        <p style={{ fontSize: 14, color: BRAND.text.muted, margin: 0 }}>
          22 cursos en 4 niveles de certificacion · 75 horas de contenido
        </p>
        <div style={{ marginTop: 12, display: 'inline-flex', alignItems: 'center', gap: 8, padding: '10px 12px', borderRadius: 10, background: '#F8FAF9', border: `1px solid ${BRAND.border}`, fontSize: 12, color: BRAND.text.muted }}>
          <span style={{ width: 8, height: 8, borderRadius: '50%', background: BRAND.green.main }} />
          `EG101`, `EG102`, `EG103` y `EG105` están habilitados por ahora. Los demás cursos quedarán disponibles cuando estén completos al 100%.
        </div>
      </div>

      <div
        style={{
          display: 'flex',
          gap: 12,
          marginBottom: 24,
          flexWrap: 'wrap',
          alignItems: 'center',
        }}
      >
        <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
          {[
            { lv: 0, label: 'Todos', color: BRAND.text.secondary },
            { lv: 1, label: 'NV1 · Inicial', color: LEVEL[1].color },
            { lv: 2, label: 'NV2 · Certificado', color: BRAND.amber.main },
            { lv: 3, label: 'NV3 · Analista', color: BRAND.blue.main },
            { lv: 4, label: 'NV4 · Supervisor', color: BRAND.red.main },
          ].map((filter) => {
            const active = filterLevel === filter.lv;
            const level = filter.lv > 0 ? LEVEL[filter.lv] : null;

            return (
              <button
                key={filter.lv}
                onClick={() => setFilterLevel(filter.lv)}
                style={{
                  padding: '6px 14px',
                  borderRadius: 20,
                  border: '1.5px solid',
                  borderColor: active ? filter.color : BRAND.border,
                  background: active ? (level ? level.light : `${BRAND.text.secondary}15`) : 'white',
                  color: active ? filter.color : BRAND.text.muted,
                  fontSize: 12,
                  fontWeight: active ? 600 : 400,
                  cursor: 'pointer',
                  transition: 'all 0.15s',
                  fontFamily: 'Outfit,sans-serif',
                }}
                type="button"
              >
                {filter.label} <span style={{ opacity: 0.6 }}>({levelCounts[filter.lv]})</span>
              </button>
            );
          })}
        </div>

        <div
          style={{
            marginLeft: 'auto',
            display: 'flex',
            alignItems: 'center',
            gap: 8,
            background: 'white',
            border: `1.5px solid ${BRAND.border}`,
            borderRadius: 8,
            padding: '7px 12px',
            width: 220,
            maxWidth: '100%',
          }}
        >
          <svg width="13" height="13" viewBox="0 0 14 14" fill="none">
            <circle cx="6" cy="6" r="4.5" stroke={BRAND.text.light} strokeWidth="1.5" />
            <line x1="9.5" y1="9.5" x2="13" y2="13" stroke={BRAND.text.light} strokeWidth="1.5" strokeLinecap="round" />
          </svg>
          <input
            value={search}
            onChange={(event) => setSearch(event.target.value)}
            placeholder="Buscar curso..."
            style={{
              border: 'none',
              background: 'transparent',
              outline: 'none',
              fontSize: 13,
              fontFamily: 'Outfit,sans-serif',
              color: BRAND.text.primary,
              width: '100%',
            }}
          />
        </div>
      </div>

      {filterLevel === 0 ? (
        [1, 2, 3, 4].map((levelNumber) => {
          const levelData = LEVEL[levelNumber];
          const levelCourses = filtered.filter((course) => course.level === levelNumber);

          if (!levelCourses.length) return null;

          return (
            <div key={levelNumber} style={{ marginBottom: 36 }}>
              <div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 16 }}>
                <div style={{ width: 4, height: 20, borderRadius: 2, background: levelData.color }} />
                <h3
                  style={{
                    fontFamily: 'Space Grotesk,sans-serif',
                    fontWeight: 700,
                    fontSize: 15,
                    color: BRAND.text.primary,
                    margin: 0,
                  }}
                >
                  Nivel {levelNumber} - {levelData.name}
                </h3>
                <span
                  style={{
                    fontSize: 12,
                    padding: '2px 10px',
                    borderRadius: 10,
                    background: levelData.light,
                    color: levelData.color,
                    fontWeight: 500,
                  }}
                >
                  {levelCourses.length} cursos
                </span>
              </div>

              <div
                style={{
                  display: 'grid',
                  gridTemplateColumns: 'repeat(auto-fill, minmax(280px, 1fr))',
                  gap: 16,
                }}
              >
                {levelCourses.map((course) => (
                  <CourseCard key={course.code} course={course} onClick={() => onSelectCourse(course)} />
                ))}
              </div>
            </div>
          );
        })
      ) : (
        <div
          style={{
            display: 'grid',
            gridTemplateColumns: 'repeat(auto-fill, minmax(280px, 1fr))',
            gap: 16,
          }}
        >
          {filtered.map((course) => (
            <CourseCard key={course.code} course={course} onClick={() => onSelectCourse(course)} />
          ))}
        </div>
      )}
    </div>
  );
}

function CourseCard({ course, onClick }) {
  const [hovered, setHovered] = useState(false);
  const level = LEVEL[course.level];
  const isLocked = !course.isReady;

  return (
    <div
      onClick={() => {
        if (isLocked) return;
        onClick();
      }}
      onMouseEnter={() => {
        if (isLocked) return;
        setHovered(true);
      }}
      onMouseLeave={() => setHovered(false)}
      style={{
        background: isLocked ? '#F8FAF9' : 'white',
        border: `1.5px solid ${isLocked ? BRAND.border : hovered ? level.color : BRAND.border}`,
        borderRadius: 12,
        overflow: 'hidden',
        cursor: isLocked ? 'not-allowed' : 'pointer',
        boxShadow: isLocked ? '0 1px 4px rgba(14,31,24,0.04)' : hovered ? `0 8px 24px ${level.color}20` : '0 2px 8px rgba(14,31,24,0.05)',
        transform: isLocked ? 'none' : hovered ? 'translateY(-2px)' : 'none',
        transition: 'all 0.2s',
        opacity: isLocked ? 0.82 : 1,
      }}
    >
      <CourseThumbnail course={course} />
      <div style={{ padding: '14px 16px 16px' }}>
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: 6 }}>
          <span style={{ fontFamily: 'DM Mono,monospace', fontSize: 10, color: BRAND.text.light }}>{course.code}</span>
          <div style={{ display: 'flex', gap: 6, alignItems: 'center', flexWrap: 'wrap', justifyContent: 'flex-end' }}>
            <span style={{ fontSize: 10, fontWeight: 600, padding: '2px 8px', borderRadius: 6, background: level.light, color: level.color }}>
              NV{course.level}
            </span>
            {isLocked ? (
              <span style={{ fontSize: 10, fontWeight: 700, padding: '2px 8px', borderRadius: 999, background: '#EEF2F1', color: BRAND.text.muted }}>
                Próximamente
              </span>
            ) : null}
          </div>
        </div>
        <h4
          style={{
            fontFamily: 'Space Grotesk,sans-serif',
            fontWeight: 700,
            fontSize: 14,
            color: isLocked ? BRAND.text.secondary : BRAND.text.primary,
            margin: '0 0 6px',
            lineHeight: 1.3,
          }}
        >
          {course.title}
        </h4>
        <p
          style={{
            fontSize: 12,
            color: isLocked ? BRAND.text.light : BRAND.text.muted,
            margin: '0 0 12px',
            lineHeight: 1.5,
            display: '-webkit-box',
            WebkitLineClamp: 2,
            WebkitBoxOrient: 'vertical',
            overflow: 'hidden',
          }}
        >
          {course.desc}
        </p>
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: 12 }}>
          <div style={{ display: 'flex', gap: 12, flexWrap: 'wrap' }}>
            <span style={{ fontSize: 11, color: BRAND.text.light }}>{course.dur}</span>
            <span style={{ fontSize: 11, color: BRAND.text.light }}>{course.mods} módulos</span>
          </div>
          {isLocked ? (
            <span style={{ fontSize: 11, fontWeight: 600, color: BRAND.text.light }}>
              Bloqueado
            </span>
          ) : course.progress > 0 ? (
            <span style={{ fontSize: 11, fontWeight: 600, color: course.progress === 100 ? BRAND.green.main : level.color }}>
              {course.progress === 100 ? 'Completado' : `${course.progress}%`}
            </span>
          ) : null}
        </div>
        {!isLocked && course.progress > 0 && course.progress < 100 ? (
          <div style={{ marginTop: 8 }}>
            <ProgressBar value={course.progress} color={level.color} height={4} />
          </div>
        ) : null}
      </div>
    </div>
  );
}

function CourseDetail({ course, onBack, role, currentUser }) {
  if (!course) {
    return (
      <Card style={{ padding: 32, textAlign: 'center' }}>
        <div style={{ fontSize: 16, fontWeight: 600, color: BRAND.text.primary, marginBottom: 8 }}>
          No hay un curso seleccionado
        </div>
        <p style={{ fontSize: 13, color: BRAND.text.muted, marginBottom: 16 }}>
          Vuelve al catálogo y selecciona un curso para ver su detalle.
        </p>
        <button
          onClick={onBack}
          style={{
            padding: '10px 18px',
            borderRadius: 8,
            border: 'none',
            background: BRAND.green.main,
            color: 'white',
            fontSize: 13,
            fontWeight: 600,
            cursor: 'pointer',
            fontFamily: 'Outfit,sans-serif',
          }}
          type="button"
        >
          Ir al catálogo
        </button>
      </Card>
    );
  }

  if (getStructuredCourseConfig(course.code)) {
    return <CourseDetailPage course={course} onBack={onBack} role={role} currentUser={currentUser} />;
  }

  return <GenericCourseDetail course={course} onBack={onBack} />;
}

function CourseDetailPage({ course, onBack, role, currentUser }) {
  const structuredConfig = getStructuredCourseConfig(course.code);
  if (!structuredConfig) {
    return <GenericCourseDetail course={course} onBack={onBack} />;
  }

  const { courseData, sections, storageKey } = structuredConfig;
  const isAdminReview = role === 'admin';
  const level = LEVEL[course.level];
  const [progressState, setProgressState] = useState(() => readCourseProgress(storageKey));
  const [activeSectionId, setActiveSectionId] = useState(() => readCourseProgress(storageKey).currentSectionId || 'overview');
  const [showCertificate, setShowCertificate] = useState(false);
  const [courseRecordId, setCourseRecordId] = useState(null);
  const sidebarBelow = useCourseSidebarBelow();
  const sectionContentRef = useRef(null);

  useEffect(() => {
    const nextState = { ...progressState, currentSectionId: activeSectionId };
    window.localStorage.setItem(storageKey, JSON.stringify(nextState));
  }, [progressState, activeSectionId, storageKey]);

  useEffect(() => {
    if (!currentUser?.name) return;

    setProgressState((current) => {
      if (current.studentName === currentUser.name) return current;
      return {
        ...current,
        studentName: currentUser.name,
      };
    });
  }, [currentUser]);

  useEffect(() => {
    if (!currentUser?.id || isAdminReview) return;

    let cancelled = false;

    const syncRuntimeProgress = async () => {
      try {
        const courseRecord = await fetchCourseRecordByCode(courseData.code);
        if (!courseRecord || cancelled) return;

        setCourseRecordId(courseRecord.id);

        const attemptsData = await apiCall(`/api/courses/${courseRecord.id}/assessment-attempts?userId=${currentUser.id}`);
        if (cancelled) return;

        const attempts = Array.isArray(attemptsData?.attempts) ? attemptsData.attempts : [];
        const latestAttempt = attempts.length > 0 ? attempts[attempts.length - 1] : null;

        setProgressState((current) => ({
          ...current,
          studentName: currentUser.name,
          finalExamAttempts: Math.max(current.finalExamAttempts, attempts.length),
          finalExamPassed: latestAttempt?.isPassed || current.finalExamPassed,
          finalExamScore: latestAttempt?.score ?? current.finalExamScore,
        }));
      } catch (error) {
        console.error('No se pudo sincronizar el progreso real del curso:', error);
      }
    };

    syncRuntimeProgress();

    return () => {
      cancelled = true;
    };
  }, [courseData.code, currentUser, isAdminReview]);

  useEffect(() => {
    if (!sectionContentRef.current) return;

    window.requestAnimationFrame(() => {
      sectionContentRef.current.scrollIntoView({ behavior: 'smooth', block: 'start' });
    });
  }, [activeSectionId, showCertificate]);

  const summary = useMemo(() => computeCourseProgress(courseData, progressState), [courseData, progressState]);
  const activeSection = sections.find((section) => section.id === activeSectionId) || sections[0];

  const updateProgress = (updater) => {
    setProgressState((current) => {
      const updated = typeof updater === 'function' ? updater(current) : updater;
      return {
        ...updated,
        certificateUnlocked: computeCourseProgress(courseData, updated).canUnlockCertificate,
      };
    });
  };

  const syncLessonCompletion = (nextState, lessonId, quizAnswersOverride) => {
    const completed = isLessonCompletedByRules(courseData, nextState, lessonId, quizAnswersOverride);
    return {
      ...nextState,
      lessonsCompleted: {
        ...nextState.lessonsCompleted,
        [lessonId]: completed,
      },
    };
  };

  const navigateToSection = (sectionId) => {
    const allowedSectionId = getAllowedCourseSectionId(sectionId, courseData, progressState, sections, isAdminReview);
    setActiveSectionId(allowedSectionId);
    if (allowedSectionId !== 'downloadable-material') setShowCertificate(false);
  };

  const goToNextSection = () => {
    const currentIndex = sections.findIndex((section) => section.id === activeSectionId);
    const nextSection = sections[Math.min(currentIndex + 1, sections.length - 1)];
    navigateToSection(nextSection.id);
  };

  const startCourse = () => {
    navigateToSection('overview');
    setShowCertificate(false);
  };

  const continueCourse = () => {
    if (isAdminReview) {
      if (activeSectionId === 'overview') {
        navigateToSection('lesson-1');
        return;
      }

      goToNextSection();
      return;
    }

    const pendingLesson = courseData.lessons.find((lesson, index) => {
      const sectionId = `lesson-${index + 1}`;
      return !progressState.lessonsCompleted[lesson.id] || activeSectionId === sectionId;
    });

    if (pendingLesson) {
      const lessonIndex = courseData.lessons.findIndex((lesson) => lesson.id === pendingLesson.id);
      navigateToSection(`lesson-${lessonIndex + 1}`);
      return;
    }

    if (!progressState.finalExamPassed) {
      navigateToSection('final-evaluation');
      return;
    }

    navigateToSection('downloadable-material');
  };

  return (
    <div>
      <button
        onClick={onBack}
        style={{
          display: 'flex',
          alignItems: 'center',
          gap: 6,
          background: 'none',
          border: 'none',
          cursor: 'pointer',
          color: BRAND.text.muted,
          fontSize: 13,
          fontWeight: 500,
          marginBottom: 20,
          fontFamily: 'Outfit,sans-serif',
          padding: 0,
        }}
        type="button"
      >
        <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
          <path d="M10 3L5 8L10 13" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" />
        </svg>
        Volver al catálogo
      </button>

      <div
        style={{
          display: 'grid',
          gridTemplateColumns: sidebarBelow ? 'minmax(0, 1fr)' : 'minmax(0, 1fr) 300px',
          gap: 24,
          alignItems: 'start',
        }}
      >
        <div style={{ minWidth: 0 }}>
          <Card style={{ overflow: 'hidden', marginBottom: 20 }}>
            <div
              style={{
                background: `linear-gradient(135deg, ${level.dark}, ${level.color})`,
                padding: '28px 28px 24px',
                color: 'white',
                position: 'relative',
                overflow: 'hidden',
              }}
            >
              <div style={{ position: 'absolute', inset: 0, opacity: 0.08, backgroundImage: 'radial-gradient(circle, white 1px, transparent 1px)', backgroundSize: '18px 18px' }} />
              <div style={{ position: 'relative' }}>
                <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap', marginBottom: 14 }}>
                  <span style={{ fontSize: 11, fontWeight: 700, padding: '4px 10px', borderRadius: 999, background: 'rgba(255,255,255,0.14)' }}>
                    {courseData.code}
                  </span>
                  <span style={{ fontSize: 11, fontWeight: 700, padding: '4px 10px', borderRadius: 999, background: 'rgba(255,255,255,0.14)' }}>
                    {courseData.levelLabel || LEVEL[courseData.level]?.name || `Nivel ${courseData.level}`}
                  </span>
                </div>
                <h1 style={{ fontFamily: 'Space Grotesk,sans-serif', fontWeight: 800, fontSize: 28, lineHeight: 1.08, margin: '0 0 8px' }}>
                  {courseData.title}
                </h1>
                <p style={{ fontSize: 14, color: 'rgba(255,255,255,0.76)', margin: '0 0 18px' }}>
                  {(courseData.levelLabel || LEVEL[courseData.level]?.name || `Nivel ${courseData.level}`) + ' · ' + courseData.duration + ' · ' + courseData.lessons.length + ' lecciones'}
                </p>
                <p style={{ fontSize: 14, lineHeight: 1.65, maxWidth: 720, color: 'rgba(255,255,255,0.86)', margin: 0 }}>
                  {courseData.description}
                </p>
              </div>
            </div>

            <div style={{ padding: 24 }}>
              <div
                style={{
                  display: 'grid',
                  gridTemplateColumns: 'repeat(auto-fit, minmax(190px, 1fr))',
                  gap: 14,
                  marginBottom: 18,
                }}
              >
                <SummaryChip label="Estado del curso" value={summary.status} tone={level.color} />
                <SummaryChip label="Lecciones completadas" value={`${summary.completedLessons}/${summary.totalLessons}`} tone={level.color} />
                <SummaryChip label="Quiz completados" value={`${summary.completedLessonQuizzes}/${summary.totalLessons}`} tone={level.color} />
                <SummaryChip label="Evaluación final" value={progressState.finalExamPassed ? 'Aprobada' : 'Pendiente'} tone={level.color} />
              </div>

              <div style={{ marginBottom: 18 }}>
                <div style={{ display: 'flex', justifyContent: 'space-between', gap: 12, marginBottom: 8 }}>
                  <span style={{ fontSize: 13, fontWeight: 600, color: BRAND.text.primary }}>Progreso general del curso</span>
                  <span style={{ fontSize: 13, fontWeight: 700, color: level.color }}>{summary.percentage}%</span>
                </div>
                <ProgressBar value={summary.percentage} color={level.color} height={10} />
              </div>

              <div style={{ display: 'flex', gap: 10, flexWrap: 'wrap' }}>
                <button
                  onClick={summary.status === 'No iniciado' ? startCourse : continueCourse}
                  style={{
                    border: 'none',
                    borderRadius: 10,
                    padding: '12px 18px',
                    background: `linear-gradient(135deg, ${level.color}, ${level.dark})`,
                    color: 'white',
                    fontSize: 14,
                    fontWeight: 700,
                    cursor: 'pointer',
                    boxShadow: `0 8px 24px ${level.color}28`,
                  }}
                  type="button"
                >
                  Continuar curso
                </button>

                {summary.canUnlockCertificate ? (
                  <button
                    onClick={() => {
                      setShowCertificate(true);
                      navigateToSection('downloadable-material');
                    }}
                    style={{
                      border: `1.5px solid ${level.color}`,
                      borderRadius: 10,
                      padding: '12px 18px',
                      background: 'white',
                      color: level.color,
                      fontSize: 14,
                      fontWeight: 700,
                      cursor: 'pointer',
                    }}
                    type="button"
                  >
                    Descargar certificado
                  </button>
                ) : null}
              </div>
            </div>
          </Card>

          <div ref={sectionContentRef} style={{ display: 'grid', gap: 20 }}>
            <CourseSectionNav
              activeSectionId={activeSectionId}
              onSelectSection={navigateToSection}
              progressState={progressState}
              sections={sections}
              courseData={courseData}
            />

            {showCertificate ? (
              <CertificateView
                courseData={courseData}
                progressState={progressState}
                summary={summary}
              />
            ) : (
              <SectionContent
                activeSection={activeSection}
                courseData={courseData}
                goToNextSection={goToNextSection}
                isAdminReview={isAdminReview}
                onCompleteLessonVideo={(lessonId) =>
                  updateProgress((current) =>
                    syncLessonCompletion(
                      {
                        ...current,
                        lessonVideosCompleted: { ...current.lessonVideosCompleted, [lessonId]: true },
                      },
                      lessonId,
                    ),
                  )
                }
                onCompleteLessonQuiz={(lessonId, answers) =>
                  updateProgress((current) =>
                    syncLessonCompletion(
                      {
                        ...current,
                        lessonQuizzesCompleted: { ...current.lessonQuizzesCompleted, [lessonId]: true },
                        lessonQuizAnswers: { ...current.lessonQuizAnswers, [lessonId]: answers },
                      },
                      lessonId,
                      answers,
                    ),
                  )
                }
                onResetLessonQuiz={(lessonId) =>
                  updateProgress((current) => {
                    const nextCompleted = { ...current.lessonQuizzesCompleted };
                    const nextAnswers = { ...current.lessonQuizAnswers };
                    delete nextCompleted[lessonId];
                    delete nextAnswers[lessonId];
                    return syncLessonCompletion(
                      {
                        ...current,
                        lessonQuizzesCompleted: nextCompleted,
                        lessonQuizAnswers: nextAnswers,
                      },
                      lessonId,
                      {},
                    );
                  })
                }
                onStartFinalExamAttempt={(attemptOrder) =>
                  updateProgress((current) => ({
                    ...current,
                    finalExamQuestionOrder: attemptOrder.questionOrder,
                    finalExamOptionOrder: attemptOrder.optionOrder,
                    finalExamAttemptStartedAt: current.finalExamAttemptStartedAt ?? Date.now(),
                  }))
                }
                onFinalExamSubmit={async (result) => {
                  let persistedResult = null;

                  if (!isAdminReview && currentUser?.id && courseRecordId) {
                    try {
                      persistedResult = await apiCall(`/api/courses/${courseRecordId}/assessment-attempts`, {
                        method: 'POST',
                        body: JSON.stringify({
                          userId: currentUser.id,
                          score: result.score,
                        }),
                      });
                    } catch (error) {
                      console.error('No se pudo guardar la evaluación final en la base:', error);
                    }
                  }

                  updateProgress((current) => ({
                    ...current,
                    studentName: currentUser?.name || current.studentName,
                    finalExamAnswers: result.answers,
                    finalExamAttempts: persistedResult?.attempt?.attemptNumber ?? (current.finalExamAttempts + 1),
                    finalExamAttemptStartedAt: null,
                    finalExamPassed: persistedResult?.passed ?? result.passed,
                    finalExamScore: persistedResult?.attempt?.score ?? result.score,
                  }));

                  if ((persistedResult?.passed ?? result.passed)) {
                    setShowCertificate(false);
                    navigateToSection('downloadable-material');
                  }
                }}
                onRetryFinalExam={() =>
                  updateProgress((current) => ({
                    ...current,
                    finalExamAnswers: {},
                    finalExamQuestionOrder: [],
                    finalExamOptionOrder: {},
                    finalExamAttemptStartedAt: null,
                    finalExamPassed: false,
                    finalExamScore: current.finalExamPassed ? current.finalExamScore : null,
                  }))
                }
                progressState={progressState}
                summary={summary}
              />
            )}
          </div>
        </div>

        <div
          style={{
            display: 'flex',
            flexDirection: 'column',
            gap: 16,
            position: sidebarBelow ? 'static' : 'sticky',
            top: sidebarBelow ? 'auto' : 0,
            order: sidebarBelow ? 2 : 0,
          }}
        >
          <Card style={{ padding: 20 }}>
            <div style={{ fontFamily: 'Space Grotesk,sans-serif', fontWeight: 700, fontSize: 15, color: BRAND.text.primary, marginBottom: 14 }}>
              {`Ruta ${courseData.code}`}
            </div>
            <div style={{ display: 'grid', gap: 10 }}>
              {sections.map((section, index) => {
                const done =
                  section.type === 'lesson'
                    ? progressState.lessonsCompleted[courseData.lessons[section.lessonIndex].id]
                    : section.id === 'final-evaluation'
                      ? progressState.finalExamPassed
                      : section.id === 'downloadable-material'
                        ? summary.canUnlockCertificate
                        : true;

                return (
                  <button
                    key={section.id}
                    onClick={() => {
                      setActiveSectionId(section.id);
                      if (section.id !== 'downloadable-material') setShowCertificate(false);
                    }}
                    style={{
                      display: 'flex',
                      alignItems: 'center',
                      gap: 10,
                      borderRadius: 10,
                      border: `1px solid ${activeSectionId === section.id ? `${level.color}55` : BRAND.border}`,
                      background: activeSectionId === section.id ? level.light : 'white',
                      padding: '10px 12px',
                      cursor: 'pointer',
                      textAlign: 'left',
                    }}
                    type="button"
                  >
                    <div
                      style={{
                        width: 26,
                        height: 26,
                        borderRadius: '50%',
                        display: 'flex',
                        alignItems: 'center',
                        justifyContent: 'center',
                        border: `1.5px solid ${done ? level.color : BRAND.border}`,
                        background: done ? level.color : BRAND.bg,
                        color: done ? 'white' : BRAND.text.light,
                        flexShrink: 0,
                      }}
                    >
                      {done ? <Icon name="check" color="currentColor" size={13} /> : <span style={{ fontSize: 11, fontWeight: 700 }}>{index + 1}</span>}
                    </div>
                    <div style={{ minWidth: 0 }}>
                      <div style={{ fontSize: 13, fontWeight: 600, color: BRAND.text.primary }}>{section.label}</div>
                      <div style={{ fontSize: 11, color: BRAND.text.light }}>
                        {section.type === 'lesson'
                          ? progressState.lessonQuizzesCompleted[courseData.lessons[section.lessonIndex].id]
                            ? 'Lección y quiz completados'
                            : 'Contenido en curso'
                          : section.id === 'final-evaluation'
                            ? progressState.finalExamPassed
                              ? `Aprobada con ${progressState.finalExamScore}%`
                              : '10 preguntas · minimo 80%'
                            : section.id === 'downloadable-material'
                              ? 'Resumen PDF y certificado'
                              : 'Vision general del curso'}
                      </div>
                    </div>
                  </button>
                );
              })}
            </div>
          </Card>

          <Card style={{ padding: 20 }}>
            <div style={{ fontFamily: 'Space Grotesk,sans-serif', fontWeight: 700, fontSize: 15, color: BRAND.text.primary, marginBottom: 14 }}>
              Información del curso
            </div>
            {[
              ['Código', courseData.code],
              ['Título', courseData.title],
              ['Nivel', `Nivel ${courseData.level}`],
              ['Duración', courseData.duration],
              ['Lecciones', `${courseData.lessons.length} lecciones`],
              ['Estado', summary.status],
            ].map(([label, value]) => (
              <div
                key={label}
                style={{
                  display: 'flex',
                  justifyContent: 'space-between',
                  gap: 12,
                  padding: '7px 0',
                  borderBottom: `1px solid ${BRAND.border}`,
                  fontSize: 12,
                }}
              >
                <span style={{ color: BRAND.text.light }}>{label}</span>
                <span style={{ color: BRAND.text.secondary, fontWeight: 600, textAlign: 'right' }}>{value}</span>
              </div>
            ))}
          </Card>
        </div>
      </div>
    </div>
  );
}

function SummaryChip({ label, value, tone }) {
  return (
    <div style={{ background: '#FAFCFA', border: `1px solid ${BRAND.border}`, borderRadius: 12, padding: '14px 16px' }}>
      <div style={{ fontSize: 10, color: BRAND.text.light, textTransform: 'uppercase', letterSpacing: '0.06em', marginBottom: 6 }}>
        {label}
      </div>
      <div style={{ fontSize: 14, fontWeight: 700, color: tone }}>{value}</div>
    </div>
  );
}

function CourseSectionNav({ activeSectionId, onSelectSection, progressState, sections, courseData }) {
  return (
    <Card style={{ padding: 10 }}>
      <div style={{ display: 'flex', gap: 8, overflowX: 'auto', paddingBottom: 2 }}>
        {sections.map((section) => (
          <button
            key={section.id}
            onClick={() => onSelectSection(section.id)}
            style={{
              whiteSpace: 'nowrap',
              borderRadius: 999,
              border: `1px solid ${activeSectionId === section.id ? BRAND.green.main : BRAND.border}`,
              background: activeSectionId === section.id ? BRAND.green.light : 'white',
              color: activeSectionId === section.id ? BRAND.green.dark : BRAND.text.muted,
              padding: '9px 14px',
              fontSize: 12,
              fontWeight: 600,
              cursor: 'pointer',
            }}
            type="button"
          >
            {section.label}
            {section.type === 'lesson' && progressState.lessonsCompleted[courseData.lessons[section.lessonIndex].id] ? ' · OK' : ''}
          </button>
        ))}
      </div>
    </Card>
  );
}

function SectionContent({
  activeSection,
  courseData,
  progressState,
  summary,
  isAdminReview,
  onCompleteLessonVideo,
  onCompleteLessonQuiz,
  onResetLessonQuiz,
  onStartFinalExamAttempt,
  onFinalExamSubmit,
  onRetryFinalExam,
  goToNextSection,
}) {
  if (activeSection.type === 'lesson') {
    const lesson = courseData.lessons[activeSection.lessonIndex];
    return (
      <LessonContent
        lesson={lesson}
        isAdminReview={isAdminReview}
        onCompleteLessonVideo={onCompleteLessonVideo}
        onCompleteLessonQuiz={onCompleteLessonQuiz}
        onResetLessonQuiz={onResetLessonQuiz}
        progressState={progressState}
        goToNextSection={goToNextSection}
      />
    );
  }

  if (activeSection.type === 'final') {
    return (
      <FinalEvaluation
        evaluation={courseData.finalEvaluation}
        isAdminReview={isAdminReview}
        onRetry={onRetryFinalExam}
        onStartAttempt={onStartFinalExamAttempt}
        onSubmit={onFinalExamSubmit}
        progressState={progressState}
        summary={summary}
      />
    );
  }

  if (activeSection.type === 'download') {
    return (
      <DownloadableMaterial
        courseData={courseData}
        progressState={progressState}
        summary={summary}
      />
    );
  }

  return <CourseOverview courseData={courseData} summary={summary} sections={createCourseSections(courseData)} />;
}

function CourseOverview({ courseData, summary, sections }) {
  const levelName = courseData.levelLabel || LEVEL[courseData.level]?.name || `Nivel ${courseData.level}`;
  const totalLessons = courseData.totalLessons || courseData.lessons.length;

  return (
    <Card style={{ padding: 24 }}>
      <div style={{ display: 'grid', gap: 18 }}>
        <div>
          <div style={{ fontSize: 11, fontWeight: 700, color: BRAND.green.main, textTransform: 'uppercase', letterSpacing: '0.08em', marginBottom: 8 }}>
            Ficha técnica del curso
          </div>
          <h2 style={{ fontFamily: 'Space Grotesk,sans-serif', fontWeight: 700, fontSize: 22, color: BRAND.text.primary, margin: '0 0 10px' }}>
            {courseData.title}
          </h2>
          <p style={{ fontSize: 14, lineHeight: 1.7, color: BRAND.text.muted, margin: 0 }}>
            {courseData.longDescription}
          </p>
        </div>

        <div
          style={{
            display: 'grid',
            gridTemplateColumns: 'repeat(auto-fit, minmax(220px, 1fr))',
            gap: 14,
          }}
        >
          <InfoBlock label="Código" value={courseData.code} />
          <InfoBlock label="Nivel" value={levelName} />
          <InfoBlock label="Duración total" value={courseData.duration} />
          <InfoBlock label="Cantidad de lecciones" value={String(totalLessons)} />
          <InfoBlock label="Estado actual" value={summary.status} />
        </div>

        <div style={{ display: 'grid', gap: 14 }}>
          <PanelTitle>Objetivo general</PanelTitle>
          <p style={{ fontSize: 14, lineHeight: 1.7, color: BRAND.text.secondary, margin: 0 }}>
            {courseData.objective}
          </p>
        </div>

        {courseData.competencies?.length ? (
          <div style={{ display: 'grid', gap: 14 }}>
            <PanelTitle>Competencias a desarrollar</PanelTitle>
            <div style={{ display: 'grid', gap: 10 }}>
              {courseData.competencies.map((competency, index) => (
                <div
                  key={index}
                  style={{
                    display: 'flex',
                    gap: 10,
                    alignItems: 'flex-start',
                    padding: '12px 14px',
                    borderRadius: 10,
                    border: `1px solid ${BRAND.border}`,
                    background: '#FAFCFA',
                  }}
                >
                  <div style={{ color: BRAND.green.main, fontWeight: 700, lineHeight: 1.4 }}>•</div>
                  <div style={{ fontSize: 14, lineHeight: 1.65, color: BRAND.text.secondary }}>{competency}</div>
                </div>
              ))}
            </div>
          </div>
        ) : null}

        <div style={{ display: 'grid', gap: 14 }}>
          <PanelTitle>Ruta del curso</PanelTitle>
          <div style={{ display: 'grid', gap: 10 }}>
            {sections.map((section, index) => (
              <div
                key={section.id}
                style={{
                  display: 'flex',
                  alignItems: 'center',
                  gap: 12,
                  padding: '12px 14px',
                  borderRadius: 10,
                  border: `1px solid ${BRAND.border}`,
                  background: index === 0 ? BRAND.green.light : 'white',
                }}
              >
                <div style={{ width: 28, height: 28, borderRadius: '50%', background: BRAND.bg, color: BRAND.green.main, display: 'flex', alignItems: 'center', justifyContent: 'center', fontWeight: 700, fontSize: 12 }}>
                  {index + 1}
                </div>
                <div style={{ fontSize: 13, fontWeight: 600, color: BRAND.text.primary }}>{section.label}</div>
              </div>
            ))}
          </div>
        </div>

        {courseData.evaluationFocus?.length ? (
          <div style={{ display: 'grid', gap: 14 }}>
            <PanelTitle>Evaluación</PanelTitle>
            <div style={{ display: 'grid', gap: 10 }}>
              {courseData.evaluationFocus.map((item, index) => (
                <div
                  key={index}
                  style={{
                    display: 'flex',
                    gap: 10,
                    alignItems: 'flex-start',
                    padding: '12px 14px',
                    borderRadius: 10,
                    border: `1px solid ${BRAND.border}`,
                    background: '#FAFCFA',
                  }}
                >
                  <div style={{ color: BRAND.green.main, fontWeight: 700, lineHeight: 1.4 }}>•</div>
                  <div style={{ fontSize: 14, lineHeight: 1.65, color: BRAND.text.secondary }}>{item}</div>
                </div>
              ))}
            </div>
          </div>
        ) : null}

        {courseData.complementaryMaterial?.length ? (
          <div style={{ display: 'grid', gap: 14 }}>
            <PanelTitle>Material complementario</PanelTitle>
            <div style={{ display: 'grid', gap: 10 }}>
              {courseData.complementaryMaterial.map((item, index) => (
                <div
                  key={index}
                  style={{
                    display: 'flex',
                    gap: 10,
                    alignItems: 'flex-start',
                    padding: '12px 14px',
                    borderRadius: 10,
                    border: `1px solid ${BRAND.border}`,
                    background: '#FAFCFA',
                  }}
                >
                  <div style={{ color: BRAND.green.main, fontWeight: 700, lineHeight: 1.4 }}>•</div>
                  <div style={{ fontSize: 14, lineHeight: 1.65, color: BRAND.text.secondary }}>{item}</div>
                </div>
              ))}
            </div>
          </div>
        ) : null}
      </div>
    </Card>
  );
}

function InfoBlock({ label, value }) {
  return (
    <div style={{ background: '#FAFCFA', border: `1px solid ${BRAND.border}`, borderRadius: 12, padding: '14px 16px' }}>
      <div style={{ fontSize: 10, color: BRAND.text.light, textTransform: 'uppercase', letterSpacing: '0.06em', marginBottom: 6 }}>{label}</div>
      <div style={{ fontSize: 14, fontWeight: 700, color: BRAND.text.primary }}>{value}</div>
    </div>
  );
}

function PanelTitle({ children }) {
  return (
    <div style={{ fontFamily: 'Space Grotesk,sans-serif', fontWeight: 700, fontSize: 16, color: BRAND.text.primary }}>
      {children}
    </div>
  );
}

function LessonContent({ lesson, progressState, isAdminReview, onCompleteLessonVideo, onCompleteLessonQuiz, onResetLessonQuiz, goToNextSection }) {
  const completed = !!progressState.lessonsCompleted[lesson.id];
  const quizCompleted = !!progressState.lessonQuizzesCompleted[lesson.id];
  const videoCompleted = !!progressState.lessonVideosCompleted[lesson.id];
  let contentParagraphs = lesson.id === 'eg101-lesson-2'
    ? [
        '📘 Rol del Operador eGuardian',
        'El operador es una parte clave del sistema eGuardian.',
        'La tecnología permite ver lo que pasa. Pero es el operador quien entiende la situación y decide qué hacer.',
        'Su trabajo no es solo mirar cámaras. También debe observar, entender lo que está viendo y actuar a tiempo.',
        'Cada decisión parte de una secuencia simple: observar, entender y actuar.',
        'Una operación efectiva depende de tres factores clave:',
        '• Atención constante',
        '• Capacidad de análisis',
        '• Tiempo de reacción',
        'En este rol, distraerse, demorarse o no actuar puede permitir que un incidente ocurra.',
        'El operador no solo observa. El operador evita que ocurran los incidentes.',
      ]
    : lesson.content;

  if (lesson.id === 'eg101-lesson-3') {
    contentParagraphs = [
      '📘 LECCIÓN 3 — Clasificación de eventos',
      '🧠 Contenido',
      'Un evento es cualquier situación que aparece en las cámaras y que requiere atención del operador.',
      'No todo lo que se ve es igual de importante.',
      'Por eso, los eventos se clasifican según su nivel de riesgo:',
      '🔴 Evento crítico',
      'Requiere acción inmediata.',
      'Son situaciones donde existe un riesgo real o inminente.',
      'Ejemplos:',
      '• Intento de ingreso no autorizado',
      '• Manipulación de accesos',
      '• Persona dentro de un área restringida',
      '🟡 Evento relevante',
      'Requiere observación y seguimiento.',
      'No es una amenaza inmediata, pero puede evolucionar.',
      'Ejemplos:',
      '• Persona merodeando',
      '• Vehículo detenido por tiempo prolongado',
      '• Comportamiento inusual',
      '⚪ Evento informativo',
      'No requiere acción.',
      'Son situaciones normales dentro de la operación.',
      'Ejemplos:',
      '• Tránsito habitual de personas',
      '• Actividades esperadas en horarios normales',
      '🎯 Rol del operador',
      'El operador debe observar, analizar y clasificar cada evento correctamente.',
      'Una buena clasificación permite:',
      '• actuar a tiempo en situaciones críticas',
      '• no perder de vista eventos que pueden escalar',
      '• evitar distracciones con eventos normales',
      '🧠 Idea clave',
      'No todos los eventos requieren acción.',
      'Pero todos deben ser entendidos correctamente.',
    ];
  }

  if (lesson.id === 'eg101-lesson-4') {
    contentParagraphs = [
      '📘 LECCIÓN 4 — Cultura eGuardian',
      '🧠 Contenido',
      'La cultura eGuardian define cómo trabaja un operador en su día a día.',
      'No se trata solo de seguir protocolos, sino de mantener una forma de actuar basada en atención, criterio y responsabilidad.',
      'Cada operador representa el estándar del servicio.',
      'Por eso, se espera que actúe con foco constante, evalúe correctamente lo que observa y responda de forma oportuna.',
      '🎯 Principios clave',
      'En eGuardian, todos los operadores deben:',
      '• Mantener atención constante durante el monitoreo',
      '• Tomar en serio cada evento, sin asumir que “no pasará nada”',
      '• Actuar a tiempo, según el nivel de riesgo',
      '• Seguir protocolos, sin dejar de aplicar criterio',
      '⚠️ Conductas que no forman parte de la cultura eGuardian',
      'Para asegurar un servicio profesional, no se permiten:',
      '• Distracción durante el monitoreo',
      '• Ignorar o no revisar eventos',
      '• Asumir que algo es normal sin verificar',
      '• Retrasar acciones cuando se requiere intervención',
      '🧠 Idea clave',
      'En eGuardian, la calidad del servicio depende del operador.',
      'Cada decisión cuenta.',
    ];
  }

  return (
    <div style={{ display: 'grid', gap: 20 }}>
      <Card style={{ padding: 24 }}>
        <div style={{ display: 'flex', justifyContent: 'space-between', gap: 12, flexWrap: 'wrap', marginBottom: 14 }}>
          <div>
            <div style={{ fontSize: 11, fontWeight: 700, color: BRAND.green.main, textTransform: 'uppercase', letterSpacing: '0.08em', marginBottom: 8 }}>
              {lesson.label}
            </div>
            <h2 style={{ fontFamily: 'Space Grotesk,sans-serif', fontWeight: 700, fontSize: 22, color: BRAND.text.primary, margin: 0 }}>
              {lesson.title}
            </h2>
          </div>
          <span style={{ alignSelf: 'flex-start', borderRadius: 999, background: completed ? BRAND.green.light : '#FAFCFA', border: `1px solid ${completed ? `${BRAND.green.main}33` : BRAND.border}`, padding: '8px 12px', fontSize: 12, fontWeight: 700, color: completed ? BRAND.green.dark : BRAND.text.muted }}>
            {completed ? 'Lección completada' : lesson.duration}
          </span>
        </div>

        <div style={{ display: 'grid', gap: 18 }}>
          <section>
            <PanelTitle>Objetivo de aprendizaje</PanelTitle>
            <p style={{ margin: '10px 0 0', fontSize: 14, lineHeight: 1.7, color: BRAND.text.secondary }}>{lesson.objective}</p>
          </section>

          <VideoPlayer
            lesson={lesson}
            videoCompleted={videoCompleted}
            onCompleteLessonVideo={onCompleteLessonVideo}
          />

          <section>
            <PanelTitle>Contenido escrito</PanelTitle>
            <div
              style={{
                marginTop: 12,
                marginBottom: 14,
                borderRadius: 12,
                border: `1px solid ${BRAND.green.main}22`,
                background: BRAND.green.light,
                padding: '14px 16px',
              }}
            >
              <div style={{ fontSize: 12, fontWeight: 700, color: BRAND.green.dark, marginBottom: 6, textTransform: 'uppercase', letterSpacing: '0.04em' }}>
                Lectura obligatoria
              </div>
              <div style={{ fontSize: 13, lineHeight: 1.7, color: BRAND.text.secondary }}>
                Lee este contenido con atención antes de continuar. Aquí están las definiciones y conceptos clave que luego se aplican en los ejemplos, el quiz y la evaluación final.
              </div>
            </div>
            <div style={{ display: 'grid', gap: 10 }}>
              {contentParagraphs.map((paragraph, index) => (
                <p
                  key={index}
                  style={{
                    margin: 0,
                    fontSize: index === 0 ? 18 : 14,
                    lineHeight: index === 0 ? 1.4 : 1.72,
                    color: index === 0 ? BRAND.text.primary : BRAND.text.muted,
                    fontWeight: index === 0 ? 700 : 400,
                  }}
                >
                  {paragraph}
                </p>
              ))}
            </div>
          </section>

          <section>
            <PanelTitle>Ejemplos practicos</PanelTitle>
            <div style={{ display: 'grid', gap: 14, marginTop: 12 }}>
              {lesson.examples.map((example, index) => (
                <PracticalCaseCard
                  key={index}
                  example={example}
                  index={index}
                />
              ))}
            </div>
          </section>
        </div>
      </Card>

      <QuizComponent
        lesson={lesson}
        onCompleteLessonQuiz={onCompleteLessonQuiz}
        onResetLessonQuiz={onResetLessonQuiz}
        progressState={progressState}
      />

      <Card style={{ padding: 20 }}>
        <div style={{ display: 'flex', gap: 10, flexWrap: 'wrap' }}>
          <button
            onClick={goToNextSection}
            style={{
              border: `1.5px solid ${completed || isAdminReview ? BRAND.green.main : BRAND.border}`,
              borderRadius: 10,
              padding: '12px 18px',
              background: completed || isAdminReview ? 'white' : '#F5F7F6',
              color: completed || isAdminReview ? BRAND.green.main : BRAND.text.light,
              fontSize: 14,
              fontWeight: 700,
              cursor: completed || isAdminReview ? 'pointer' : 'not-allowed',
              opacity: completed || isAdminReview ? 1 : 0.9,
            }}
            disabled={!completed && !isAdminReview}
            type="button"
          >
            Continuar
          </button>

          <div style={{ alignSelf: 'center', fontSize: 12, color: BRAND.text.light }}>
            {isAdminReview
              ? 'Modo administrador: puedes revisar libremente esta lección y avanzar sin depender del video o del quiz.'
              : !videoCompleted
              ? 'Debes ver el video completo para habilitar el avance.'
              : !completed
                ? 'Debes responder correctamente las 2 preguntas del quiz para completar la lección.'
                : quizCompleted
                  ? 'Lección completada automáticamente.'
                  : 'Completa el quiz para registrar el avance formativo.'}
          </div>
        </div>
      </Card>
    </div>
  );
}

function PracticalCaseCard({ example, index }) {
  const casePresets = {
    person: {
      context: '23:47 hrs - Acceso principal sin flujo habitual',
      operatorDecision: 'Se identifica comportamiento anómalo por repetición y horario, lo que activa protocolo preventivo.',
      status: 'observacion',
    },
    vehicle: {
      context: '01:12 hrs - Frente a acceso crítico con permanencia extendida',
      operatorDecision: 'La permanencia prolongada frente a un punto sensible exige elevar observación y evaluar posible escalamiento.',
      status: 'escalado',
    },
    shield: {
      context: '02:18 hrs - Límite perimetral con baja visibilidad',
      operatorDecision: 'La manipulación temprana del cierre se interpreta como señal de ingreso en preparación y exige decisión inmediata.',
      status: 'escalado',
    },
    alert: {
      context: '04:05 hrs - Punto sensible con actividad repetitiva no validada',
      operatorDecision: 'El criterio del operador exige verificar antes de normalizar una conducta fuera de patrón.',
      status: 'observacion',
    },
    monitor: {
      context: '10:15 hrs - Área técnica con mantenimiento programado',
      operatorDecision: 'El contexto debe confirmarse antes de descartar riesgo; el criterio está en validar la normalidad operativa.',
      status: 'observacion',
    },
    report: {
      context: '21:32 hrs - Secuencia sospechosa en zona de monitoreo activo',
      operatorDecision: 'La calidad del análisis también se expresa en la claridad del registro y la trazabilidad comunicada.',
      status: 'observacion',
    },
  };
  const titlePresets = {
    'IntrusiÃ³n parcial en cierre perimetral': {
      title: 'Persona manipulando una reja perimetral',
      context: '⏱️ De noche, sin tránsito de personas',
      situation: 'Se observa a una persona manipulando una reja perimetral en un sector exterior.',
      risk: 'Existe riesgo de que la persona intente ingresar al lugar.',
      response: 'El operador revisa otras cámaras cercanas, confirma la situación y avisa según protocolo.',
      operatorDecision: 'Como la persona está manipulando la reja fuera de horario, se considera una posible intrusión y se decide actuar de inmediato.',
    },
    'NormalizaciÃ³n de conducta anÃ³mala': {
      title: 'Cuando algo extraño empieza a parecer normal',
      context: '⏱️ Durante el turno, en un punto sensible del recinto',
      situation: 'Se observa una actividad repetitiva en una zona sensible y, por costumbre, podría parecer normal.',
      risk: 'Si no se revisa a tiempo, una situación real puede pasar desapercibida.',
      response: 'El operador vuelve a revisar el contexto, compara cámaras cercanas y confirma si corresponde actuar.',
      operatorDecision: 'Como la conducta se repite en un lugar sensible, no debe asumirse como normal sin verificar primero.',
    },
  };
  const simpleTitlePresets = {
    'Persona merodeando fuera de horario': {
      context: 'De noche, sin tránsito de personas',
      situation: 'Se ve a una persona pasar varias veces por el mismo acceso fuera de horario.',
      risk: 'Existe riesgo de que esté observando el lugar o buscando una forma de entrar.',
      response: 'El operador revisa cámaras cercanas, confirma el recorrido y avisa según protocolo.',
      operatorDecision: 'Como la persona repite el recorrido fuera de horario, se considera una conducta extraña y se decide actuar.',
    },
    'VehÃ­culo detenido frente a acceso crÃ­tico': {
      context: 'De noche, frente a un acceso sensible',
      situation: 'Se observa un vehículo detenido por varios minutos frente a un acceso importante.',
      risk: 'Existe riesgo de vigilancia previa, bloqueo del acceso o preparación de un incidente.',
      response: 'El operador revisa otras cámaras, confirma la permanencia del vehículo y avisa si corresponde.',
      operatorDecision: 'Como el vehículo permanece demasiado tiempo en un punto sensible, se decide revisar la situación y mantener atención.',
    },
    'IntrusiÃƒÂ³n parcial en cierre perimetral': {
      title: 'Persona manipulando una reja perimetral',
      context: 'De noche, sin tránsito de personas',
      situation: 'Se observa a una persona manipulando una reja perimetral.',
      risk: 'Existe riesgo de que la persona intente ingresar al lugar.',
      response: 'El operador revisa otras cámaras cercanas, confirma la situación y avisa según protocolo.',
      operatorDecision: 'Como la persona está manipulando la reja fuera de horario, se considera una posible intrusión y se decide actuar de inmediato.',
    },
    'NormalizaciÃƒÂ³n de conducta anÃƒÂ³mala': {
      title: 'Cuando algo extraño empieza a parecer normal',
      context: 'Durante el turno, en un punto sensible',
      situation: 'Se observa algo extraño que se repite en una zona importante del recinto.',
      risk: 'Si no se revisa a tiempo, un problema real puede pasar desapercibido.',
      response: 'El operador vuelve a revisar el lugar, mira cámaras cercanas y confirma si debe avisar.',
      operatorDecision: 'Como la conducta se repite en un lugar sensible, no debe asumirse como normal sin revisar primero.',
    },
    'Merodeo nocturno en acceso sensible': {
      context: 'De noche, en un acceso secundario',
      situation: 'Se observa a una persona rondando varias veces un acceso durante la madrugada.',
      risk: 'Existe riesgo de que intente ingresar o de que esté observando el lugar antes de actuar.',
      response: 'El operador sigue el movimiento por cámaras cercanas y avisa según lo que confirma.',
      operatorDecision: 'Como la persona aparece varias veces en el mismo punto y fuera de horario, se trata como una situación de riesgo.',
    },
    'Ingreso autorizado en Ã¡rea tÃ©cnica': {
      context: 'Durante el turno, en un área técnica',
      situation: 'Se observa el ingreso de personas a una zona técnica en horario de trabajo.',
      risk: 'Si no se revisa el contexto, una actividad normal puede confundirse con un riesgo.',
      response: 'El operador revisa el horario, la zona y el movimiento para confirmar si el ingreso corresponde.',
      operatorDecision: 'Como hay movimiento en una zona sensible, primero se confirma si está autorizado antes de descartarlo.',
    },
    'Registro claro y comunicaciÃ³n oportuna': {
      context: 'Durante el turno, ante una situación sospechosa',
      situation: 'Se observa una secuencia que puede transformarse en incidente si no se informa bien.',
      risk: 'Si la información sale confusa o incompleta, la respuesta puede demorarse.',
      response: 'El operador registra lo que ve con claridad y avisa de forma simple y directa.',
      operatorDecision: 'Como otros dependen de esa información para actuar, se decide comunicar de forma clara y completa.',
    },
    'SuposiciÃ³n sin verificaciÃ³n': {
      context: 'Durante el turno, en una situación poco clara',
      situation: 'Se observa algo extraño y existe el riesgo de asumir que no tiene importancia.',
      risk: 'Si no se revisa, un incidente real puede pasar desapercibido.',
      response: 'El operador vuelve a revisar la situación, confirma lo que está pasando y luego decide cómo actuar.',
      operatorDecision: 'Como no hay suficiente claridad, no se debe asumir nada sin revisar primero.',
    },
  };

  const normalizedExample = typeof example === 'string'
    ? {
        title: `Caso práctico ${index + 1}`,
        icon: 'shield',
        situation: example,
        risk: 'Analizar el contexto para identificar si existe riesgo operativo.',
        response: 'Aplicar criterio operativo y responder según protocolo eGuardian.',
      }
    : example;

  const titleText = String(normalizedExample.title || '').toLowerCase();
  const matchedSimplePreset =
    titleText.includes('merodeando fuera de horario') ? {
      context: 'De noche, sin tránsito de personas',
      situation: 'Se ve a una persona pasar varias veces por el mismo acceso fuera de horario.',
      risk: 'Existe riesgo de que esté observando el lugar o buscando una forma de entrar.',
      response: 'El operador revisa cámaras cercanas, confirma el recorrido y avisa según protocolo.',
      operatorDecision: 'Como la persona repite el recorrido fuera de horario, se considera una conducta extraña y se decide actuar.',
    } :
    (titleText.includes('veh') && titleText.includes('acceso')) ? {
      context: 'De noche, frente a un acceso sensible',
      situation: 'Se observa un vehículo detenido por varios minutos frente a un acceso importante.',
      risk: 'Existe riesgo de vigilancia previa, bloqueo del acceso o preparación de un incidente.',
      response: 'El operador revisa otras cámaras, confirma la permanencia del vehículo y avisa si corresponde.',
      operatorDecision: 'Como el vehículo permanece demasiado tiempo en un punto sensible, se decide revisar la situación y mantener atención.',
    } :
    (titleText.includes('reja') || titleText.includes('perimetral')) ? {
      title: 'Persona manipulando una reja perimetral',
      context: 'De noche, sin tránsito de personas',
      situation: 'Se observa a una persona manipulando una reja perimetral.',
      risk: 'Existe riesgo de que la persona intente ingresar al lugar.',
      response: 'El operador revisa otras cámaras cercanas, confirma la situación y avisa según protocolo.',
      operatorDecision: 'Como la persona está manipulando la reja fuera de horario, se considera una posible intrusión y se decide actuar de inmediato.',
    } :
    (titleText.includes('normal') || titleText.includes('extra')) ? {
      title: 'Cuando algo extraño empieza a parecer normal',
      context: 'Durante el turno, en un punto sensible',
      situation: 'Se observa algo extraño que se repite en una zona importante del recinto.',
      risk: 'Si no se revisa a tiempo, un problema real puede pasar desapercibido.',
      response: 'El operador vuelve a revisar el lugar, mira cámaras cercanas y confirma si debe avisar.',
      operatorDecision: 'Como la conducta se repite en un lugar sensible, no debe asumirse como normal sin revisar primero.',
    } :
    titleText.includes('merodeo nocturno') ? {
      context: 'De noche, en un acceso secundario',
      situation: 'Se observa a una persona rondando varias veces un acceso durante la madrugada.',
      risk: 'Existe riesgo de que intente ingresar o de que esté observando el lugar antes de actuar.',
      response: 'El operador sigue el movimiento por cámaras cercanas y avisa según lo que confirma.',
      operatorDecision: 'Como la persona aparece varias veces en el mismo punto y fuera de horario, se trata como una situación de riesgo.',
    } :
    (titleText.includes('area') || titleText.includes('área')) ? {
      context: 'Durante el turno, en un área técnica',
      situation: 'Se observa el ingreso de personas a una zona técnica en horario de trabajo.',
      risk: 'Si no se revisa el contexto, una actividad normal puede confundirse con un riesgo.',
      response: 'El operador revisa el horario, la zona y el movimiento para confirmar si el ingreso corresponde.',
      operatorDecision: 'Como hay movimiento en una zona sensible, primero se confirma si está autorizado antes de descartarlo.',
    } :
    (titleText.includes('registro') || titleText.includes('comunic')) ? {
      context: 'Durante el turno, ante una situación sospechosa',
      situation: 'Se observa una secuencia que puede transformarse en incidente si no se informa bien.',
      risk: 'Si la información sale confusa o incompleta, la respuesta puede demorarse.',
      response: 'El operador registra lo que ve con claridad y avisa de forma simple y directa.',
      operatorDecision: 'Como otros dependen de esa información para actuar, se decide comunicar de forma clara y completa.',
    } :
    (titleText.includes('supos') || titleText.includes('verific')) ? {
      context: 'Durante el turno, en una situación poco clara',
      situation: 'Se observa algo extraño y existe el riesgo de asumir que no tiene importancia.',
      risk: 'Si no se revisa, un incidente real puede pasar desapercibido.',
      response: 'El operador vuelve a revisar la situación, confirma lo que está pasando y luego decide cómo actuar.',
      operatorDecision: 'Como no hay suficiente claridad, no se debe asumir nada sin revisar primero.',
    } :
    null;

  const enrichedExample = {
    ...(casePresets[normalizedExample.icon] || {}),
    operatorDecision: 'Aplicar juicio profesional a partir del contexto observado antes de escalar o descartar.',
    context: 'Caso de entrenamiento - Contexto operativo por validar',
    status: 'observacion',
    ...normalizedExample,
    ...(titlePresets[normalizedExample.title] || {}),
    ...(simpleTitlePresets[normalizedExample.title] || {}),
  };

  const statusMeta = enrichedExample.status === 'escalado'
    ? { icon: '🔴', label: 'Escalado', bg: BRAND.red.light, color: BRAND.red.dark, border: `${BRAND.red.main}22` }
    : { icon: '🟡', label: 'En observación', bg: BRAND.amber.light, color: BRAND.amber.dark, border: `${BRAND.amber.main}22` };

  return (
    <div
      style={{
        borderRadius: 20,
        border: `1px solid ${BRAND.border}`,
        background: 'white',
        boxShadow: '0 12px 28px rgba(12, 36, 28, 0.07)',
        padding: 18,
      }}
    >
      <div style={{ display: 'flex', gap: 18, flexWrap: 'wrap', alignItems: 'stretch' }}>
        <div
          style={{
            width: 144,
            minHeight: 184,
            borderRadius: 18,
            background: `linear-gradient(160deg, ${BRAND.green.light}, #F7FBF9)`,
            border: `1px solid ${BRAND.green.main}22`,
            display: 'flex',
            flexDirection: 'column',
            alignItems: 'center',
            justifyContent: 'space-between',
            padding: 16,
            flexShrink: 0,
          }}
        >
          <div
            style={{
              alignSelf: 'flex-start',
              borderRadius: 999,
              background: 'rgba(255,255,255,0.72)',
              border: `1px solid ${BRAND.green.main}22`,
              color: BRAND.green.dark,
              padding: '4px 9px',
              fontSize: 10,
              fontWeight: 700,
              textTransform: 'uppercase',
              letterSpacing: '0.06em',
            }}
          >
            Caso
          </div>
          <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 10 }}>
            <ExampleVisual icon={enrichedExample.icon} />
          </div>
          <div
            style={{
              marginTop: 10,
              fontSize: 10,
              fontWeight: 700,
              color: BRAND.green.dark,
              letterSpacing: '0.08em',
              textTransform: 'uppercase',
              textAlign: 'center',
            }}
          >
            Caso práctico
          </div>
        </div>

        <div style={{ flex: '1 1 320px', minWidth: 0, display: 'grid', gap: 12 }}>
          <div>
            <div style={{ fontSize: 12, fontWeight: 600, color: BRAND.text.light, marginBottom: 8 }}>
              ⏱️ {enrichedExample.context}
            </div>
            <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap', marginBottom: 8 }}>
              <span
                style={{
                  borderRadius: 999,
                  background: BRAND.green.light,
                  color: BRAND.green.dark,
                  border: `1px solid ${BRAND.green.main}22`,
                  padding: '5px 10px',
                  fontSize: 11,
                  fontWeight: 700,
                }}
              >
                Ejemplo {index + 1}
              </span>
            </div>
            <div style={{ fontFamily: 'Space Grotesk,sans-serif', fontSize: 21, fontWeight: 700, color: BRAND.text.primary, lineHeight: 1.2 }}>
              {enrichedExample.title}
            </div>
          </div>

          <ExampleInfoBlock
            label="Situación observada"
            tone="neutral"
            text={enrichedExample.situation}
          />
          <ExampleInfoBlock
            label="Riesgo detectado"
            tone="warning"
            text={enrichedExample.risk}
          />
          <ExampleInfoBlock
            label="Respuesta eGuardian"
            tone="success"
            text={enrichedExample.response}
          />
          <ExampleInfoBlock
            label="Decisión del operador"
            tone="analysis"
            text={enrichedExample.operatorDecision}
          />
        </div>
      </div>
    </div>
  );
}

function ExampleInfoBlock({ label, text, tone }) {
  const palette = {
    neutral: { chipBg: '#F5F7F6', chipColor: BRAND.text.secondary, bg: '#FAFCFA', border: BRAND.border },
    warning: { chipBg: BRAND.amber.light, chipColor: BRAND.amber.dark, bg: '#FFF9EE', border: `${BRAND.amber.main}22` },
    success: { chipBg: BRAND.green.light, chipColor: BRAND.green.dark, bg: '#F5FBF8', border: `${BRAND.green.main}2A` },
    analysis: { chipBg: '#EAF0F8', chipColor: '#3E5D7A', bg: '#F5F8FC', border: '#D8E3F0' },
  }[tone];
  const iconPrefix = {
    neutral: '👁️ ',
    warning: '⚠️ ',
    success: '🛡️ ',
    analysis: '🧠 ',
  }[tone] || '';

  return (
    <div
      style={{
        borderRadius: 14,
        border: `1px solid ${palette.border}`,
        background: palette.bg,
        padding: '12px 14px',
      }}
    >
      <div
        style={{
          display: 'inline-flex',
          alignItems: 'center',
          borderRadius: 999,
          background: palette.chipBg,
          color: palette.chipColor,
          padding: '4px 9px',
          fontSize: 11,
          fontWeight: 700,
          marginBottom: 8,
        }}
      >
        {iconPrefix}{label}
      </div>
      <div style={{ fontSize: 13, lineHeight: 1.7, color: BRAND.text.secondary }}>
        {text}
      </div>
    </div>
  );
}

function ExampleVisual({ icon }) {
  const commonProps = {
    width: 42,
    height: 42,
    viewBox: '0 0 48 48',
    fill: 'none',
  };

  if (icon === 'person') {
    return (
      <svg {...commonProps}>
        <circle cx="24" cy="14" r="6.5" stroke={BRAND.green.main} strokeWidth="2.5" />
        <path d="M11 37C11 30.5 16.4 26 24 26C31.6 26 37 30.5 37 37" stroke={BRAND.green.main} strokeWidth="2.5" strokeLinecap="round" />
        <path d="M34 15H41" stroke={BRAND.amber.main} strokeWidth="2.5" strokeLinecap="round" />
        <path d="M37.5 11.5V18.5" stroke={BRAND.amber.main} strokeWidth="2.5" strokeLinecap="round" />
      </svg>
    );
  }

  if (icon === 'vehicle') {
    return (
      <svg {...commonProps}>
        <rect x="8" y="17" width="32" height="13" rx="4" stroke={BRAND.green.main} strokeWidth="2.5" />
        <path d="M14 17L19 11H29L34 17" stroke={BRAND.green.main} strokeWidth="2.5" strokeLinejoin="round" />
        <circle cx="16" cy="33" r="3.5" fill={BRAND.amber.light} stroke={BRAND.amber.main} strokeWidth="2" />
        <circle cx="32" cy="33" r="3.5" fill={BRAND.amber.light} stroke={BRAND.amber.main} strokeWidth="2" />
      </svg>
    );
  }

  if (icon === 'monitor') {
    return (
      <svg {...commonProps}>
        <rect x="8" y="10" width="32" height="21" rx="4" stroke={BRAND.green.main} strokeWidth="2.5" />
        <path d="M19 38H29" stroke={BRAND.green.main} strokeWidth="2.5" strokeLinecap="round" />
        <path d="M24 31V38" stroke={BRAND.green.main} strokeWidth="2.5" strokeLinecap="round" />
        <path d="M16 23L21 18L25 22L32 15" stroke={BRAND.amber.main} strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" />
      </svg>
    );
  }

  if (icon === 'report') {
    return (
      <svg {...commonProps}>
        <path d="M14 8H29L35 14V38H14V8Z" stroke={BRAND.green.main} strokeWidth="2.5" strokeLinejoin="round" />
        <path d="M29 8V14H35" stroke={BRAND.green.main} strokeWidth="2.5" strokeLinejoin="round" />
        <path d="M19 21H30" stroke={BRAND.amber.main} strokeWidth="2.5" strokeLinecap="round" />
        <path d="M19 27H30" stroke={BRAND.green.main} strokeWidth="2.5" strokeLinecap="round" />
        <path d="M19 33H26" stroke={BRAND.green.main} strokeWidth="2.5" strokeLinecap="round" />
      </svg>
    );
  }

  return (
    <svg {...commonProps}>
      <path d="M24 6L37 11V22C37 29.5 31.5 36 24 39C16.5 36 11 29.5 11 22V11Z" fill={BRAND.green.light} stroke={BRAND.green.main} strokeWidth="2.5" />
      <polyline points="18,23 22,27 30,18" fill="none" stroke={BRAND.amber.main} strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" />
    </svg>
  );
}

function VideoPlayer({ lesson, videoCompleted, onCompleteLessonVideo }) {
  const [showFallback, setShowFallback] = useState(false);
  const isPresentation = !!lesson.presentationUrl;

  useEffect(() => {
    setShowFallback(false);
  }, [lesson.videoUrl, lesson.presentationUrl]);

  useEffect(() => {
    if (!isPresentation) return undefined;

    const handleMessage = (event) => {
      if (event.data?.type !== 'eguardian-presentation-complete') return;
      if (event.data?.lessonId !== lesson.id) return;
      onCompleteLessonVideo(lesson.id);
    };

    window.addEventListener('message', handleMessage);
    return () => window.removeEventListener('message', handleMessage);
  }, [isPresentation, lesson.id, onCompleteLessonVideo]);

  return (
    <section>
      <div style={{ display: 'flex', justifyContent: 'space-between', gap: 12, alignItems: 'center', flexWrap: 'wrap' }}>
        <PanelTitle>{isPresentation ? 'Presentación de la lección' : 'Video de la lección'}</PanelTitle>
        <span style={{ borderRadius: 999, padding: '8px 12px', background: videoCompleted ? BRAND.green.light : '#FAFCFA', border: `1px solid ${videoCompleted ? `${BRAND.green.main}33` : BRAND.border}`, fontSize: 12, fontWeight: 700, color: videoCompleted ? BRAND.green.dark : BRAND.text.muted }}>
          {videoCompleted ? (isPresentation ? 'Presentación completada' : 'Video completado') : (isPresentation ? 'Pendiente de revisión' : 'Pendiente de visualización')}
        </span>
      </div>
      <div
        style={{
          marginTop: 12,
          borderRadius: 16,
          overflow: 'hidden',
          border: `1px solid ${BRAND.border}`,
          background: '#0E1F18',
        }}
      >
        {isPresentation ? (
          <div style={{ background: '#F7FAF8' }}>
            <iframe
              key={lesson.presentationUrl}
              src={lesson.presentationUrl}
              title={lesson.title}
              style={{ width: '100%', height: 640, border: 'none', display: 'block', background: '#0E1F18' }}
              allow="autoplay"
            />
          </div>
        ) : !showFallback ? (
          <video
            key={lesson.videoUrl}
            controls
            onError={() => setShowFallback(true)}
            onEnded={() => onCompleteLessonVideo(lesson.id)}
            poster=""
            style={{ width: '100%', display: 'block', maxHeight: 420, background: 'linear-gradient(135deg, #0C4A36, #136B4D)' }}
          >
            <source src={lesson.videoUrl} type="video/mp4" />
          </video>
        ) : null}

        {showFallback ? (
          <div style={{ padding: '28px 24px', background: 'linear-gradient(135deg, #0C4A36, #136B4D)' }}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 12 }}>
              <div style={{ width: 42, height: 42, borderRadius: '50%', background: 'rgba(255,255,255,0.14)', display: 'flex', alignItems: 'center', justifyContent: 'center', color: 'white' }}>
                <Icon name="play" color="currentColor" size={18} />
              </div>
              <div>
                <div style={{ fontSize: 15, fontWeight: 700, color: 'white' }}>{lesson.videoUrl}</div>
                <div style={{ fontSize: 12, color: 'rgba(255,255,255,0.68)' }}>Placeholder listo para reemplazar por el video final.</div>
              </div>
            </div>
            <div style={{ fontSize: 13, lineHeight: 1.7, color: 'rgba(255,255,255,0.84)' }}>
              El reproductor ya esta preparado para videos reales. Cuando exista el archivo MP4 definitivo, se reproducira aqui sin cambiar la estructura del componente.
            </div>
          </div>
        ) : null}
      </div>
      {isPresentation ? (
        <div style={{ marginTop: 10, display: 'flex', justifyContent: 'space-between', gap: 12, flexWrap: 'wrap', alignItems: 'center' }}>
          <div style={{ fontSize: 12, color: BRAND.text.light }}>
            El avance se registrará automáticamente cuando la presentación llegue al cierre final.
          </div>
          <a
            href={lesson.presentationUrl}
            target="_blank"
            rel="noreferrer"
            style={{
              display: 'inline-flex',
              alignItems: 'center',
              gap: 8,
              textDecoration: 'none',
              borderRadius: 10,
              border: `1px solid ${BRAND.border}`,
              background: 'white',
              color: BRAND.text.secondary,
              fontSize: 12,
              fontWeight: 700,
              padding: '10px 14px',
            }}
          >
            <Icon name="play" color="currentColor" size={14} />
            Abrir presentación completa
          </a>
        </div>
      ) : null}
      {!videoCompleted ? (
        <div style={{ marginTop: 10, fontSize: 12, color: BRAND.text.light }}>
          {isPresentation
            ? 'El botón Continuar se habilitará cuando la presentación termine por completo.'
            : 'El botón Continuar se habilitará cuando este video termine por completo.'}
        </div>
      ) : null}
    </section>
  );
}

function QuizComponent({ lesson, progressState, onCompleteLessonQuiz, onResetLessonQuiz }) {
  const existingAnswers = progressState.lessonQuizAnswers[lesson.id] || {};
  const [selectedAnswers, setSelectedAnswers] = useState(existingAnswers);
  const [checkedAnswers, setCheckedAnswers] = useState(progressState.lessonQuizzesCompleted[lesson.id]);

  useEffect(() => {
    setSelectedAnswers(progressState.lessonQuizAnswers[lesson.id] || {});
    setCheckedAnswers(!!progressState.lessonQuizzesCompleted[lesson.id]);
  }, [lesson.id, progressState.lessonQuizAnswers, progressState.lessonQuizzesCompleted]);

  const score = lesson.quiz.reduce((total, question) => total + (selectedAnswers[question.id] === question.correctOptionId ? 1 : 0), 0);
  const isCompleted = !!progressState.lessonQuizzesCompleted[lesson.id];

  return (
    <Card style={{ padding: 24 }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', gap: 12, flexWrap: 'wrap', marginBottom: 16 }}>
        <div>
          <PanelTitle>Quiz de la lección</PanelTitle>
          <div style={{ marginTop: 6, fontSize: 13, color: BRAND.text.muted }}>
            Evaluación formativa. No bloquea el avance, pero registra aprendizaje y feedback inmediato.
          </div>
        </div>
        <span style={{ borderRadius: 999, padding: '8px 12px', background: isCompleted ? BRAND.green.light : '#FAFCFA', border: `1px solid ${isCompleted ? `${BRAND.green.main}33` : BRAND.border}`, fontSize: 12, fontWeight: 700, color: isCompleted ? BRAND.green.dark : BRAND.text.muted }}>
          {isCompleted ? `Completado · ${score}/${lesson.quiz.length}` : 'Pendiente'}
        </span>
      </div>

      <div style={{ display: 'grid', gap: 14 }}>
        {lesson.quiz.map((question, index) => {
          const selectedOption = selectedAnswers[question.id];
          const showFeedback = checkedAnswers && !!selectedOption;

          return (
            <div key={question.id} style={{ borderRadius: 12, border: `1px solid ${BRAND.border}`, padding: '16px 16px 14px', background: 'white' }}>
              <div style={{ fontSize: 13, fontWeight: 700, color: BRAND.text.primary, marginBottom: 10 }}>
                {index + 1}. {question.question}
              </div>

              <div style={{ display: 'grid', gap: 8 }}>
                {question.options.map((option) => {
                  const selected = selectedOption === option.id;
                  const correct = option.id === question.correctOptionId;

                  return (
                    <button
                      key={option.id}
                      onClick={() => {
                        if (checkedAnswers) return;
                        setSelectedAnswers((current) => ({ ...current, [question.id]: option.id }));
                      }}
                      style={{
                        textAlign: 'left',
                        borderRadius: 10,
                        border: `1px solid ${
                          checkedAnswers
                            ? correct
                              ? `${BRAND.green.main}55`
                              : selected
                                ? `${BRAND.red.main}55`
                                : BRAND.border
                            : selected
                              ? `${BRAND.green.main}55`
                              : BRAND.border
                        }`,
                        background: checkedAnswers
                          ? correct
                            ? BRAND.green.light
                            : selected
                              ? BRAND.red.light
                              : 'white'
                          : selected
                            ? BRAND.green.light
                            : 'white',
                        padding: '11px 12px',
                        cursor: checkedAnswers ? 'default' : 'pointer',
                        fontSize: 13,
                        color: BRAND.text.secondary,
                      }}
                      type="button"
                    >
                      {option.text}
                    </button>
                  );
                })}
              </div>

              {showFeedback ? (
                <div style={{ marginTop: 10, fontSize: 12, color: selectedOption === question.correctOptionId ? BRAND.green.dark : BRAND.red.dark }}>
                  {selectedOption === question.correctOptionId ? question.correctFeedback : question.incorrectFeedback}
                </div>
              ) : null}
            </div>
          );
        })}
      </div>

      <div style={{ display: 'flex', gap: 10, flexWrap: 'wrap', alignItems: 'center', marginTop: 18 }}>
        <button
          onClick={() => {
            setCheckedAnswers(true);
            onCompleteLessonQuiz(lesson.id, selectedAnswers);
          }}
          disabled={lesson.quiz.some((question) => !selectedAnswers[question.id])}
          style={{
            border: 'none',
            borderRadius: 10,
            padding: '12px 18px',
            background: lesson.quiz.some((question) => !selectedAnswers[question.id]) ? '#C9D8D1' : BRAND.green.main,
            color: 'white',
            fontSize: 14,
            fontWeight: 700,
            cursor: lesson.quiz.some((question) => !selectedAnswers[question.id]) ? 'not-allowed' : 'pointer',
          }}
          type="button"
        >
          Validar respuestas
        </button>

        <button
          onClick={() => {
            setSelectedAnswers({});
            setCheckedAnswers(false);
            onResetLessonQuiz(lesson.id);
          }}
          style={{
            border: `1.5px solid ${BRAND.border}`,
            borderRadius: 10,
            padding: '12px 18px',
            background: 'white',
            color: BRAND.text.secondary,
            fontSize: 14,
            fontWeight: 700,
            cursor: 'pointer',
          }}
          type="button"
        >
          Resetear quiz
        </button>

        {checkedAnswers ? (
          <div style={{ fontSize: 12, color: BRAND.text.muted }}>
            Resultado formativo: {score}/{lesson.quiz.length} respuestas correctas.
          </div>
        ) : null}
      </div>
    </Card>
  );
}

function FinalEvaluation({ evaluation, progressState, isAdminReview, onSubmit, onRetry, onStartAttempt }) {
  const [answers, setAnswers] = useState(progressState.finalExamAnswers || {});
  const [submitted, setSubmitted] = useState(progressState.finalExamScore !== null);
  const [now, setNow] = useState(Date.now());
  const maxAttempts = 2;
  const attemptDurationMs = 45 * 60 * 1000;
  const attemptsRemaining = isAdminReview ? maxAttempts : Math.max(0, maxAttempts - progressState.finalExamAttempts);
  const attemptStartedAt = progressState.finalExamAttemptStartedAt;
  const remainingMs = attemptStartedAt ? Math.max(0, attemptDurationMs - (now - attemptStartedAt)) : attemptDurationMs;
  const minutesLeft = String(Math.floor(remainingMs / 60000)).padStart(2, '0');
  const secondsLeft = String(Math.floor((remainingMs % 60000) / 1000)).padStart(2, '0');
  const canStartOrContinue = isAdminReview ? true : progressState.finalExamPassed || progressState.finalExamAttempts < maxAttempts;
  const hasActiveAttempt = isAdminReview ? true : !!progressState.finalExamAttemptStartedAt;
  const shouldShowIntro = !isAdminReview && !submitted && !progressState.finalExamPassed && canStartOrContinue && !hasActiveAttempt;
  const orderedQuestions = useMemo(() => {
    const questionMap = new Map(evaluation.questions.map((question) => [question.id, question]));
    const savedQuestionOrder =
      Array.isArray(progressState.finalExamQuestionOrder) &&
      progressState.finalExamQuestionOrder.length > 0 &&
      progressState.finalExamQuestionOrder.every((questionId) => questionMap.has(questionId))
        ? progressState.finalExamQuestionOrder
        : evaluation.questions.map((question) => question.id);

    return savedQuestionOrder
      .map((questionId) => questionMap.get(questionId))
      .filter(Boolean)
      .map((question) => {
        const savedOptionOrder = progressState.finalExamOptionOrder?.[question.id];
        const optionMap = new Map(question.options.map((option) => [option.id, option]));
        const optionOrder =
          Array.isArray(savedOptionOrder) && savedOptionOrder.length === question.options.length
            ? savedOptionOrder
            : question.options.map((option) => option.id);

        return {
          ...question,
          options: optionOrder.map((optionId) => optionMap.get(optionId)).filter(Boolean),
        };
      });
  }, [evaluation.questions, progressState.finalExamOptionOrder, progressState.finalExamQuestionOrder]);
  const questionCount = orderedQuestions.length || Math.min(evaluation.questions.length, FINAL_EXAM_PICK_COUNT);
  const score = orderedQuestions.reduce((total, question) => total + (answers[question.id] === question.correctOptionId ? 1 : 0), 0);
  const percentage = questionCount > 0 ? Math.round((score / questionCount) * 100) : 0;
  const passed = percentage >= evaluation.passingScore;

  useEffect(() => {
    if (isAdminReview || submitted || progressState.finalExamPassed || !progressState.finalExamAttemptStartedAt) return undefined;

    const intervalId = window.setInterval(() => {
      setNow(Date.now());
    }, 1000);

    return () => window.clearInterval(intervalId);
  }, [isAdminReview, submitted, progressState.finalExamPassed, progressState.finalExamAttemptStartedAt]);

  useEffect(() => {
    if (
      isAdminReview ||
      submitted ||
      progressState.finalExamPassed ||
      !progressState.finalExamAttemptStartedAt ||
      progressState.finalExamAttempts >= maxAttempts ||
      remainingMs > 0
    ) {
      return;
    }

    setSubmitted(true);
    onSubmit({ answers, score: percentage, passed: false });
  }, [
    isAdminReview,
    submitted,
    progressState.finalExamPassed,
    progressState.finalExamAttemptStartedAt,
    progressState.finalExamAttempts,
    remainingMs,
    answers,
    percentage,
    onSubmit,
  ]);

  return (
    <Card style={{ padding: 24 }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', gap: 12, flexWrap: 'wrap', marginBottom: 18 }}>
        <div>
          <div style={{ fontSize: 11, fontWeight: 700, color: BRAND.green.main, textTransform: 'uppercase', letterSpacing: '0.08em', marginBottom: 8 }}>
            Evaluación final
          </div>
          <h2 style={{ fontFamily: 'Space Grotesk,sans-serif', fontWeight: 700, fontSize: 22, color: BRAND.text.primary, margin: 0 }}>
            {evaluation.title}
          </h2>
          <p style={{ margin: '8px 0 0', fontSize: 14, lineHeight: 1.7, color: BRAND.text.muted }}>
            {isAdminReview
              ? `${evaluation.questions.length} preguntas disponibles en modo revisión para administrador.`
              : `${questionCount} preguntas de alternativas. Se aprueba con 80% o más, tienes un máximo de 2 intentos y 45 minutos por intento.`}
          </p>
        </div>
        <div style={{ borderRadius: 12, border: `1px solid ${BRAND.border}`, background: '#FAFCFA', padding: '12px 14px', minWidth: 180 }}>
          <div style={{ fontSize: 10, color: BRAND.text.light, textTransform: 'uppercase', letterSpacing: '0.06em', marginBottom: 6 }}>
            {isAdminReview ? 'Modo actual' : 'Intentos registrados'}
          </div>
          <div style={{ fontSize: 18, fontWeight: 700, color: BRAND.text.primary }}>
            {isAdminReview ? 'Administrador' : progressState.finalExamAttempts}
          </div>
          <div style={{ fontSize: 11, color: BRAND.text.muted, marginTop: 4 }}>
            {isAdminReview ? 'Vista libre de preguntas y contenido evaluativo' : `${attemptsRemaining} intento(s) restante(s)`}
          </div>
        </div>
      </div>

      {!isAdminReview ? (
      <div style={{ display: 'flex', gap: 12, flexWrap: 'wrap', marginBottom: 18 }}>
        <div style={{ borderRadius: 12, border: `1px solid ${BRAND.border}`, background: '#FAFCFA', padding: '12px 14px', minWidth: 180 }}>
          <div style={{ fontSize: 10, color: BRAND.text.light, textTransform: 'uppercase', letterSpacing: '0.06em', marginBottom: 6 }}>Tiempo por intento</div>
          <div style={{ fontSize: 18, fontWeight: 700, color: remainingMs <= 300000 ? BRAND.red.main : BRAND.text.primary }}>
            {minutesLeft}:{secondsLeft}
          </div>
        </div>
      </div>
      ) : (
        <div style={{ marginBottom: 18, padding: '14px 16px', borderRadius: 14, border: `1px solid ${BRAND.blue.main}22`, background: BRAND.blue.light, color: BRAND.blue.dark, fontSize: 13, lineHeight: 1.65 }}>
          Modo administrador activo. Aquí puedes revisar todas las preguntas del banco sin temporizador, sin intentos y sin necesidad de rendir la evaluación.
        </div>
      )}

      {!canStartOrContinue ? (
        <div style={{ marginBottom: 18, padding: '16px 18px', borderRadius: 14, border: `1px solid ${BRAND.red.main}33`, background: BRAND.red.light, color: BRAND.red.dark, fontSize: 13, lineHeight: 1.65 }}>
          Alcanzaste el máximo de 2 intentos para esta evaluación final. El certificado permanece bloqueado mientras no exista una aprobación.
        </div>
      ) : null}

      {shouldShowIntro ? (
        <div style={{ borderRadius: 16, border: `1px solid ${BRAND.border}`, background: '#FAFCFA', padding: '22px 22px 20px' }}>
          <div style={{ fontFamily: 'Space Grotesk,sans-serif', fontWeight: 700, fontSize: 18, color: BRAND.text.primary, marginBottom: 10 }}>
            Antes de comenzar la evaluación
          </div>
          <div style={{ display: 'grid', gap: 10, marginBottom: 18 }}>
            <div style={{ fontSize: 14, lineHeight: 1.7, color: BRAND.text.muted }}>
              Dispondrás de 45 minutos para responder las {Math.min(evaluation.questions.length, FINAL_EXAM_PICK_COUNT)} preguntas de esta evaluación final.
            </div>
            <div style={{ fontSize: 14, lineHeight: 1.7, color: BRAND.text.muted }}>
              Solo cuentas con 2 intentos en total y necesitas obtener 80% o más para aprobar.
            </div>
            <div style={{ fontSize: 14, lineHeight: 1.7, color: BRAND.text.muted }}>
              Una vez que inicies, las preguntas se mostrarán de inmediato y el temporizador comenzará a correr.
            </div>
          </div>
          <div style={{ display: 'flex', gap: 10, flexWrap: 'wrap', alignItems: 'center' }}>
            <button
              onClick={() => {
                const attemptOrder = createFinalExamOrder(evaluation.questions);
                setAnswers({});
                setSubmitted(false);
                setNow(Date.now());
                onStartAttempt(attemptOrder);
              }}
              style={{
                border: 'none',
                borderRadius: 10,
                padding: '12px 18px',
                background: BRAND.green.main,
                color: 'white',
                fontSize: 14,
                fontWeight: 700,
                cursor: 'pointer',
              }}
              type="button"
            >
              Comenzar evaluación
            </button>
            <div style={{ fontSize: 12, color: BRAND.text.light }}>
              Intentos disponibles: {attemptsRemaining}
            </div>
          </div>
        </div>
      ) : (
        <>
          <div style={{ display: 'grid', gap: 14 }}>
            {orderedQuestions.map((question, index) => (
              <div key={question.id} style={{ borderRadius: 12, border: `1px solid ${BRAND.border}`, padding: '16px 16px 14px', background: 'white' }}>
                <div style={{ fontSize: 13, fontWeight: 700, color: BRAND.text.primary, marginBottom: 10 }}>
                  {index + 1}. {question.question}
                </div>
                <div style={{ display: 'grid', gap: 8 }}>
                  {question.options.map((option) => {
                    const selected = answers[question.id] === option.id;

                    return (
                      <button
                        key={option.id}
                        onClick={() => {
                          if (submitted || !canStartOrContinue || isAdminReview) return;
                          setAnswers((current) => ({ ...current, [question.id]: option.id }));
                        }}
                        style={{
                          textAlign: 'left',
                          borderRadius: 10,
                          border: `1px solid ${
                            submitted
                              ? selected
                                ? `${BRAND.green.main}55`
                                : BRAND.border
                              : selected
                                ? `${BRAND.green.main}55`
                                : BRAND.border
                          }`,
                          background: submitted
                            ? selected
                              ? BRAND.green.light
                              : 'white'
                            : selected
                              ? BRAND.green.light
                              : 'white',
                          padding: '11px 12px',
                          cursor: submitted || !canStartOrContinue ? 'default' : 'pointer',
                          fontSize: 13,
                          color: BRAND.text.secondary,
                        }}
                        type="button"
                      >
                        {option.text}
                      </button>
                    );
                  })}
                </div>
              </div>
            ))}
          </div>

          {!isAdminReview ? (
          <div style={{ marginTop: 18, display: 'flex', gap: 10, flexWrap: 'wrap', alignItems: 'center' }}>
            <button
              onClick={() => {
                setSubmitted(true);
                onSubmit({ answers, score: percentage, passed });
              }}
              disabled={orderedQuestions.some((question) => !answers[question.id]) || !canStartOrContinue || remainingMs <= 0}
              style={{
                border: 'none',
                borderRadius: 10,
                padding: '12px 18px',
                background: orderedQuestions.some((question) => !answers[question.id]) || !canStartOrContinue || remainingMs <= 0 ? '#C9D8D1' : BRAND.green.main,
                color: 'white',
                fontSize: 14,
                fontWeight: 700,
                cursor: orderedQuestions.some((question) => !answers[question.id]) || !canStartOrContinue || remainingMs <= 0 ? 'not-allowed' : 'pointer',
              }}
              type="button"
            >
              Enviar evaluación
            </button>

            {submitted && !progressState.finalExamPassed && progressState.finalExamAttempts < maxAttempts ? (
              <button
                onClick={() => {
                  setAnswers({});
                  setSubmitted(false);
                  setNow(Date.now());
                  onRetry();
                }}
                style={{
                  border: `1.5px solid ${BRAND.green.main}`,
                  borderRadius: 10,
                  padding: '12px 18px',
                  background: 'white',
                  color: BRAND.green.main,
                  fontSize: 14,
                  fontWeight: 700,
                  cursor: 'pointer',
                }}
                type="button"
              >
                Reintentar
              </button>
            ) : null}
          </div>
          ) : null}
        </>
      )}

      {submitted && !isAdminReview ? (
          <div
            style={{
              marginTop: 18,
              padding: '16px 18px',
              borderRadius: 14,
            border: `1px solid ${passed ? `${BRAND.green.main}33` : `${BRAND.red.main}33`}`,
            background: passed ? BRAND.green.light : BRAND.red.light,
          }}
        >
          <div style={{ fontSize: 18, fontWeight: 800, color: passed ? BRAND.green.dark : BRAND.red.dark, marginBottom: 6 }}>
            {passed ? 'Aprobado' : 'No aprobado'}
          </div>
          <div style={{ fontSize: 13, lineHeight: 1.65, color: BRAND.text.secondary }}>
            Puntaje obtenido: {percentage}%. {passed ? 'La evaluación final fue aprobada y el certificado queda desbloqueado al completar las 4 lecciones.' : progressState.finalExamAttempts >= maxAttempts ? 'Necesitabas 80% o más para aprobar y ya usaste tus 2 intentos disponibles.' : 'Necesitas 80% o más para aprobar. Aún puedes reintentar si te quedan intentos.'}
          </div>
        </div>
      ) : null}
    </Card>
  );
}

function DownloadableMaterial({ courseData, progressState, summary }) {
  const summaryPoints = courseData.code === 'EG101'
    ? [
        'Definición de eGuardian y su enfoque preventivo.',
        'Rol operativo del alumno dentro del ecosistema de monitoreo.',
        'Clasificación de eventos y respuesta según criticidad.',
        'Principios de la cultura eGuardian y estándares esperados.',
      ]
    : courseData.downloadableMaterial.summaryPoints;

  return (
    <div style={{ display: 'grid', gap: 20 }}>
      <Card style={{ padding: 24 }}>
        <div style={{ display: 'flex', justifyContent: 'space-between', gap: 12, flexWrap: 'wrap', marginBottom: 16 }}>
          <div>
            <div style={{ fontSize: 11, fontWeight: 700, color: BRAND.green.main, textTransform: 'uppercase', letterSpacing: '0.08em', marginBottom: 8 }}>
              Material descargable
            </div>
            <h2 style={{ fontFamily: 'Space Grotesk,sans-serif', fontWeight: 700, fontSize: 22, color: BRAND.text.primary, margin: 0 }}>
              Resumen del curso
            </h2>
          </div>
          <button
            onClick={() => generateCourseSupportPDF({ courseData })}
            style={{
              border: 'none',
              borderRadius: 10,
              background: BRAND.green.dark,
              color: 'white',
              fontSize: 14,
              fontWeight: 700,
              padding: '12px 18px',
              display: 'inline-flex',
              alignItems: 'center',
              gap: 8,
              cursor: 'pointer',
            }}
            type="button"
          >
            <Icon name="download" color="currentColor" size={16} />
            Descargar PDF del curso
          </button>
        </div>

        <div style={{ borderRadius: 14, border: `1px solid ${BRAND.border}`, background: '#FAFCFA', padding: '18px 18px 16px' }}>
          <div style={{ fontSize: 13, fontWeight: 700, color: BRAND.text.primary, marginBottom: 12 }}>
            Contenido del manual
          </div>
          <div style={{ display: 'grid', gap: 10 }}>
            {courseData.lessons.map((lesson, index) => (
              <div key={index} style={{ display: 'flex', gap: 10 }}>
                <div style={{ color: BRAND.green.main, fontWeight: 700 }}>•</div>
                <div style={{ fontSize: 13, lineHeight: 1.6, color: BRAND.text.secondary }}>
                  <strong>{lesson.label}:</strong> {lesson.title}
                </div>
              </div>
            ))}
          </div>
        </div>
      </Card>

      {summary.canUnlockCertificate ? (
        <>
          <Card
            style={{
              padding: 24,
              background: `linear-gradient(135deg, ${LEVEL[courseData.level].light}, white)`,
              border: `1px solid ${LEVEL[courseData.level].color}22`,
            }}
          >
            <div style={{ display: 'grid', gap: 10 }}>
              <div style={{ fontSize: 11, fontWeight: 700, color: LEVEL[courseData.level].color, textTransform: 'uppercase', letterSpacing: '0.08em' }}>
                Curso completado
              </div>
              <div style={{ fontFamily: 'Space Grotesk,sans-serif', fontWeight: 700, fontSize: 24, color: BRAND.text.primary }}>
                Felicitaciones, aprobaste {courseData.code}
              </div>
              <div style={{ fontSize: 14, lineHeight: 1.75, color: BRAND.text.muted, maxWidth: 760 }}>
                Completaste satisfactoriamente <strong>{courseData.title}</strong> con una calificación final de <strong>{progressState.finalExamScore || 0}%</strong>. Ya puedes descargar tu certificado y, cuando estés listo, continuar con el siguiente módulo del nivel 1 para seguir avanzando en tu ruta formativa.
              </div>
            </div>
          </Card>

          <CertificateView courseData={courseData} progressState={progressState} summary={summary} />
        </>
      ) : (
        <Card style={{ padding: 24 }}>
          <div style={{ fontFamily: 'Space Grotesk,sans-serif', fontWeight: 700, fontSize: 18, color: BRAND.text.primary, marginBottom: 8 }}>
            Certificado bloqueado
          </div>
          <div style={{ fontSize: 14, lineHeight: 1.7, color: BRAND.text.muted }}>
            Completa las {courseData.lessons.length} lecciones y aprueba la evaluación final con 80% o más para habilitar el botón "Descargar certificado".
          </div>
        </Card>
      )}
    </div>
  );
}

function generateEg101CertificatePDF({ courseData, progressState }) {
  const level = LEVEL[courseData.level];
  const issueDate = new Date().toLocaleDateString('es-CL', { day: '2-digit', month: 'long', year: 'numeric' });
  const certId = `CERT-${courseData.code}-${new Date().getFullYear()}`;
  const grade = progressState.finalExamScore ? `${progressState.finalExamScore}` : 'Sin registro';
  const win = window.open('', '_blank', 'width=1200,height=850');
  if (!win) return;

  win.document.write(`<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Certificado ${certId}</title>
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@500;600;700&family=Outfit:wght@300;400;500;600&family=DM+Mono:wght@400;500&display=swap" rel="stylesheet">
<style>
  *{box-sizing:border-box;margin:0;padding:0}
  @page{size:A4 landscape;margin:0}
  html,body{width:297mm;height:210mm;overflow:hidden;font-family:'Outfit',sans-serif}
  body{display:flex;print-color-adjust:exact;-webkit-print-color-adjust:exact}
  .left{width:25%;background:linear-gradient(160deg,${level.dark} 0%,${level.color} 100%);display:flex;flex-direction:column;align-items:center;justify-content:space-between;padding:34px 20px;position:relative;overflow:hidden}
  .dots{position:absolute;inset:0;width:100%;height:100%;opacity:.08}
  .logo-text{font-family:'Space Grotesk',sans-serif;font-weight:700;font-size:22px;color:white;line-height:1;letter-spacing:-0.01em}
  .logo-sub{font-size:10px;color:rgba(255,255,255,.5);letter-spacing:.12em;text-transform:uppercase;margin-top:4px}
  .shield{width:64px;height:64px;border-radius:50%;border:2.5px solid rgba(255,255,255,.8);background:rgba(255,255,255,.1);display:flex;align-items:center;justify-content:center;margin:18px auto 0}
  .level-pill{background:${level.color};border:2px solid rgba(255,255,255,.7);border-radius:6px;padding:8px 18px;text-align:center;margin-bottom:10px}
  .level-pill .nl{font-size:8px;color:rgba(255,255,255,.65);letter-spacing:.1em;text-transform:uppercase;font-family:'DM Mono',monospace}
  .level-pill .lv{font-family:'Space Grotesk',sans-serif;font-weight:700;font-size:13px;color:white}
  .cert-id{font-family:'DM Mono',monospace;font-size:8px;color:rgba(255,255,255,.45)}
  .right{flex:1;padding:26px 34px;background:white;position:relative}
  .right-inner{width:100%;max-width:760px;height:100%;margin:0 auto;display:flex;flex-direction:column;justify-content:space-between;position:relative;z-index:1}
  .main-content{flex:1;display:flex;flex-direction:column;justify-content:center;padding:6px 0 0}
  .gold-strip{position:absolute;top:0;right:0;width:62%;height:5px;background:linear-gradient(90deg,transparent,${BRAND.amber.main})}
  .watermark{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;pointer-events:none;opacity:.06}
  .eyebrow{font-size:10px;font-weight:500;letter-spacing:.15em;text-transform:uppercase;color:${BRAND.text.light};font-family:'DM Mono',monospace;margin-bottom:6px}
  .cert-type{font-family:'Space Grotesk',sans-serif;font-weight:500;font-size:12px;color:${BRAND.text.muted};letter-spacing:.06em;text-transform:uppercase;margin-bottom:4px}
  .title{font-family:'Space Grotesk',sans-serif;font-weight:700;font-size:30px;color:${BRAND.text.primary};line-height:1.04;margin-bottom:22px;max-width:700px}
  .to-label{font-size:12px;color:${BRAND.text.muted};margin-bottom:6px}
  .recipient{font-family:'Space Grotesk',sans-serif;font-weight:700;font-size:30px;color:${level.color};margin-bottom:18px}
  .body-text{font-size:13.5px;color:${BRAND.text.muted};line-height:1.9;max-width:700px;margin-bottom:28px}
  .meta{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:26px;max-width:700px}
  .meta-item .mk{font-size:9px;color:${BRAND.text.light};text-transform:uppercase;letter-spacing:.08em}
  .meta-item .mv{font-size:13px;font-weight:600;color:${BRAND.text.primary}}
  .sigs{display:flex;gap:72px;border-top:1px solid ${BRAND.border};padding-top:20px;justify-content:center}
  .sig-line{height:30px;margin-bottom:7px;border-bottom:1.5px solid ${BRAND.text.primary};width:190px}
  .sig-block{text-align:center;width:190px}
  .sig-name{font-size:12px;font-weight:600;color:${BRAND.text.primary}}
  .sig-role{font-size:10px;color:${BRAND.text.light};line-height:1.4}
  @media print{body{width:297mm;height:210mm}}
</style>
</head>
<body>
<div class="left">
  <svg class="dots" viewBox="0 0 200 600"><pattern id="d" width="24" height="24" patternUnits="userSpaceOnUse"><circle cx="12" cy="12" r="1.3" fill="white"/></pattern><rect width="200" height="600" fill="url(#d)"/></svg>
  <div style="position:relative;text-align:center">
    <div class="logo-text">eGuardian</div>
    <div class="logo-sub">Academia</div>
    <div class="shield">
      <svg width="32" height="36" viewBox="0 0 34 38" fill="none">
        <path d="M17 2L31 7.5V19C31 27 24.5 33.5 17 36C9.5 33.5 3 27 3 19V7.5Z" fill="rgba(255,255,255,.15)" stroke="white" stroke-width="1.5"/>
        <polyline points="10,19 14.5,24 24,13" fill="none" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
      </svg>
    </div>
  </div>
  <div style="position:relative;text-align:center">
    <div class="level-pill"><div class="nl">Nivel</div><div class="lv">${level.name}</div></div>
    <div class="cert-id">${certId}</div>
  </div>
</div>
<div class="right">
  <div class="gold-strip"></div>
  <div class="watermark">
    <svg width="380" height="380" viewBox="0 0 340 340" fill="none">
      <circle cx="170" cy="170" r="118" stroke="${level.color}" stroke-width="10"/>
      <circle cx="170" cy="170" r="92" stroke="${level.color}" stroke-width="4"/>
      <path d="M170 82L236 108V160C236 216 205 255 170 268C135 255 104 216 104 160V108Z" fill="${level.color}" fill-opacity="0.08" stroke="${level.color}" stroke-width="4"/>
      <polyline points="140,172 162,194 206,146" fill="none" stroke="${level.color}" stroke-width="12" stroke-linecap="round" stroke-linejoin="round"/>
      <text x="170" y="292" text-anchor="middle" font-family="DM Mono, monospace" font-size="18" fill="${level.color}">eGuardian Academy</text>
    </svg>
  </div>
  <div class="right-inner">
  <div class="main-content">
    <div class="eyebrow">SoiTel · Academia eGuardian</div>
    <div class="cert-type">Certificado de Aprobación</div>
    <h1 class="title">${courseData.title}</h1>
    <div class="to-label">Otorgado a</div>
    <div class="recipient">${progressState.studentName}</div>
    <p class="body-text">SoiTel Soluciones TIC SpA certifica que <strong>${progressState.studentName}</strong> ha aprobado satisfactoriamente el curso <strong>${courseData.title}</strong>, demostrando comprensión del servicio eGuardian, del rol operativo en televigilancia activa, de la identificación de eventos y de los estándares profesionales asociados a la operación preventiva.</p>
    <div class="meta">
      <div class="meta-item"><div class="mk">Fecha de emisión</div><div class="mv">${issueDate}</div></div>
      <div class="meta-item"><div class="mk">Calificación</div><div class="mv" style="color:${level.color}">${grade} / 100</div></div>
      <div class="meta-item"><div class="mk">Código del curso</div><div class="mv" style="font-family:'DM Mono',monospace;font-size:12px">${courseData.code}</div></div>
    </div>
  </div>
  <div class="sigs">
    <div class="sig-block"><div class="sig-line"></div><div class="sig-name">Miguel Salgado</div><div class="sig-role">Director Académico</div></div>
    <div class="sig-block"><div class="sig-line"></div><div class="sig-name">Hugo Villa</div><div class="sig-role">Gerente General SoiTel</div></div>
  </div>
  </div>
</div>
<script>window.onload=()=>{setTimeout(()=>window.print(),400)}</script>
</body></html>`);
  win.document.close();
}

function generateCourseSupportPDF({ courseData }) {
  const jsPDF = window.jspdf?.jsPDF;
  if (!jsPDF) {
    window.alert('No fue posible generar el PDF en este momento.');
    return;
  }

  const pdfContent = getCoursePdfContent(courseData);
  const courseDisplayTitle = courseData.shortTitle || courseData.title.replace(/^[A-Z]{2}\d+\s+[—-]\s+/, '');
  const doc = new jsPDF({ unit: 'pt', format: 'a4' });
  const pageWidth = doc.internal.pageSize.getWidth();
  const pageHeight = doc.internal.pageSize.getHeight();
  const marginX = 42;
  const innerWidth = pageWidth - marginX * 2;
  const headerTop = 34;
  const footerY = pageHeight - 28;
  const contentTop = 86;
  const contentBottom = pageHeight - 46;
  const pageFill = [247, 249, 251];
  const lineSoft = [220, 229, 236];
  const blueDark = [28, 54, 73];
  const blueGrey = [89, 112, 128];
  const greenDark = [12, 74, 54];
  const greenSoft = [232, 245, 240];
  const riskSoft = [255, 248, 230];
  const responseSoft = [236, 246, 241];
  const decisionSoft = [241, 246, 250];
  let cursorY = contentTop;
  let pageNumber = 1;
  let currentLessonTitle = '';

  const rgb = (color) => {
    if (Array.isArray(color)) return color;
    if (typeof color === 'string' && color.startsWith('#')) {
      const hex = color.replace('#', '');
      const normalized = hex.length === 3 ? hex.split('').map((value) => value + value).join('') : hex;
      return [
        parseInt(normalized.slice(0, 2), 16),
        parseInt(normalized.slice(2, 4), 16),
        parseInt(normalized.slice(4, 6), 16),
      ];
    }
    return [50, 69, 81];
  };

  const setFill = (color) => {
    const [r, g, b] = rgb(color);
    doc.setFillColor(r, g, b);
  };

  const setStroke = (color) => {
    const [r, g, b] = rgb(color);
    doc.setDrawColor(r, g, b);
  };

  const setText = (color) => {
    const [r, g, b] = rgb(color);
    doc.setTextColor(r, g, b);
  };

  const drawPageChrome = ({ title = '', subtitle = '', showLessonTag = false } = {}) => {
    setFill(pageFill);
    doc.rect(0, 0, pageWidth, pageHeight, 'F');
    setFill(greenDark);
    doc.rect(0, 0, pageWidth, 12, 'F');
    setStroke(lineSoft);
    doc.setLineWidth(1);
    doc.line(marginX, 78, pageWidth - marginX, 78);

    doc.setFont('courier', 'normal');
    doc.setFontSize(10);
    setText(greenDark);
    doc.text('EGUARDIAN ACADEMY', marginX, headerTop);

    if (showLessonTag && title) {
      setFill(greenSoft);
      doc.roundedRect(marginX, 42, 98, 24, 12, 12, 'F');
      doc.setFont('helvetica', 'bold');
      doc.setFontSize(10);
      setText(greenDark);
      doc.text(`MANUAL ${courseData.code}`, marginX + 13, 57);
    }

    if (title) {
      doc.setFont('helvetica', 'bold');
      doc.setFontSize(showLessonTag ? 22 : 20);
      setText(blueDark);
      doc.text(title, marginX, showLessonTag ? 118 : 58);
    }

    if (subtitle) {
      doc.setFont('helvetica', 'normal');
      doc.setFontSize(11);
      setText(blueGrey);
      const lines = doc.splitTextToSize(subtitle, innerWidth - 10);
      doc.text(lines, marginX, showLessonTag ? 138 : 74);
    }

    doc.setFont('courier', 'normal');
    doc.setFontSize(9);
    setText(BRAND.text.light);
    doc.text(`Página ${pageNumber}`, pageWidth - marginX, footerY, { align: 'right' });
    if (currentLessonTitle) {
      doc.text(currentLessonTitle, marginX, footerY);
    } else {
      doc.text(`${courseData.code} · Material de apoyo`, marginX, footerY);
    }
  };

  const newPage = (options = {}) => {
    if (pageNumber > 1) doc.addPage();
    drawPageChrome(options);
    cursorY = options.showLessonTag ? 160 : contentTop;
    pageNumber += 1;
  };

  const ensureSpace = (requiredHeight = 24, onBreak) => {
    if (cursorY + requiredHeight <= contentBottom) return;
    if (typeof onBreak === 'function') {
      doc.addPage();
      pageNumber += 1;
      onBreak();
      return;
    }
    doc.addPage();
    pageNumber += 1;
    drawPageChrome({ title: currentLessonTitle, subtitle: 'Continuación', showLessonTag: !!currentLessonTitle });
    cursorY = currentLessonTitle ? 160 : contentTop;
  };

  const drawSmallEyebrow = (text, tone = greenDark) => {
    ensureSpace(20);
    doc.setFont('courier', 'normal');
    doc.setFontSize(9);
    setText(tone);
    doc.text(String(text).toUpperCase(), marginX, cursorY);
    cursorY += 10;
  };

  const drawParagraph = (text, options = {}) => {
    const {
      fontSize = 11,
      color = BRAND.text.secondary,
      lineHeight = 17,
      indent = 0,
      weight = 'normal',
      topGap = 0,
      maxWidth = innerWidth - indent,
    } = options;

    cursorY += topGap;
    doc.setFont('helvetica', weight);
    doc.setFontSize(fontSize);
    setText(color);
    const lines = doc.splitTextToSize(String(text), maxWidth);
    ensureSpace(lines.length * lineHeight + 4);
    doc.text(lines, marginX + indent, cursorY);
    cursorY += lines.length * lineHeight;
  };

  const drawDivider = (gapTop = 12, gapBottom = 12) => {
    cursorY += gapTop;
    ensureSpace(10);
    setStroke(lineSoft);
    doc.setLineWidth(1);
    doc.line(marginX, cursorY, pageWidth - marginX, cursorY);
    cursorY += gapBottom;
  };

  const drawSectionHeader = (title, subtitle) => {
    ensureSpace(subtitle ? 42 : 28);
    doc.setFont('helvetica', 'bold');
    doc.setFontSize(15);
    setText(blueDark);
    doc.text(title, marginX, cursorY);
    cursorY += 15;
    if (subtitle) {
      drawParagraph(subtitle, { fontSize: 10, color: blueGrey, lineHeight: 14 });
      cursorY += 2;
    }
  };

  const drawReadingBlock = (text, isHeading = false) => {
    const bg = isHeading ? [238, 243, 247] : [255, 255, 255];
    const border = isHeading ? [215, 224, 232] : lineSoft;
    const lines = doc.splitTextToSize(text, innerWidth - 34);
    const boxHeight = Math.max(34, 18 + lines.length * (isHeading ? 16 : 14));
    ensureSpace(boxHeight + 8, () => {
      drawPageChrome({ title: currentLessonTitle, showLessonTag: true });
      cursorY = 160;
    });
    setFill(bg);
    setStroke(border);
    doc.roundedRect(marginX, cursorY, innerWidth, boxHeight, 10, 10, 'FD');
    doc.setFont('helvetica', isHeading ? 'bold' : 'normal');
    doc.setFontSize(isHeading ? 11.5 : 10.3);
    setText(isHeading ? blueDark : BRAND.text.secondary);
    doc.text(lines, marginX + 16, cursorY + 20);
    cursorY += boxHeight + 6;
  };

  const drawVideoSummaryPanel = (lesson) => {
    const conceptLines = lesson.keyIdeas.flatMap((item) => doc.splitTextToSize(`• ${item}`, innerWidth - 44));
    const summaryLines = doc.splitTextToSize(lesson.videoSummary, innerWidth - 32);
    const boxHeight = 66 + summaryLines.length * 15 + conceptLines.length * 13;
    ensureSpace(boxHeight + 10, () => {
      drawPageChrome({ title: currentLessonTitle, showLessonTag: true });
      cursorY = 160;
    });
    setFill([255, 255, 255]);
    setStroke(lineSoft);
    doc.roundedRect(marginX, cursorY, innerWidth, boxHeight, 12, 12, 'FD');
    doc.setFont('helvetica', 'bold');
    doc.setFontSize(12);
    setText(blueDark);
    doc.text(lesson.videoTitle, marginX + 16, cursorY + 24);
    doc.setFont('helvetica', 'normal');
    doc.setFontSize(10.5);
    setText(BRAND.text.secondary);
    doc.text(summaryLines, marginX + 16, cursorY + 44);
    let innerY = cursorY + 42 + summaryLines.length * 15 + 10;
    doc.setFont('helvetica', 'bold');
    doc.setFontSize(10.2);
    setText(greenDark);
    doc.text('Conceptos principales', marginX + 16, innerY);
    innerY += 12;
    doc.setFont('helvetica', 'normal');
    doc.setFontSize(10);
    setText(BRAND.text.secondary);
    doc.text(conceptLines, marginX + 16, innerY);
    cursorY += boxHeight + 8;
  };

  const drawIdeaHighlight = (text) => {
    const lines = doc.splitTextToSize(text, innerWidth - 36);
    const boxHeight = 40 + lines.length * 16;
    ensureSpace(boxHeight + 10, () => {
      drawPageChrome({ title: currentLessonTitle, showLessonTag: true });
      cursorY = 160;
    });
    setFill([233, 240, 247]);
    doc.roundedRect(marginX, cursorY, innerWidth, boxHeight, 14, 14, 'F');
    setStroke([188, 205, 220]);
    doc.setLineWidth(4);
    doc.line(marginX + 14, cursorY + 14, marginX + 14, cursorY + boxHeight - 14);
    doc.setFont('courier', 'normal');
    doc.setFontSize(9);
    setText(blueGrey);
    doc.text('IDEA CLAVE', marginX + 28, cursorY + 20);
    doc.setFont('helvetica', 'bold');
    doc.setFontSize(12);
    setText(blueDark);
    doc.text(lines, marginX + 28, cursorY + 38);
    cursorY += boxHeight + 10;
  };

  const drawBadgeTag = (x, y, fillColor, textColor, label) => {
    doc.setFont('helvetica', 'bold');
    doc.setFontSize(9.5);
    const width = Math.max(74, doc.getTextWidth(label) + 22);
    setFill(fillColor);
    doc.roundedRect(x, y - 10, width, 20, 10, 10, 'F');
    setText(textColor);
    doc.text(label, x + 10, y + 4);
    return width;
  };

  const drawCaseField = (title, text, config, startY, availableWidth) => {
    const titleWidth = drawBadgeTag(marginX + 22, startY, config.fillColor, config.textColor, title) + 18;
    doc.setFont('helvetica', 'normal');
    doc.setFontSize(10.2);
    setText(BRAND.text.secondary);
    const lines = doc.splitTextToSize(text, availableWidth - titleWidth);
    doc.text(lines, marginX + 28 + titleWidth, startY + 3);
    return Math.max(18, lines.length * 13);
  };

  const drawExampleCard = (example) => {
    const blocks = [
      { title: 'Contexto', text: example.context || 'Caso práctico del curso', config: { fillColor: [232, 245, 240], textColor: greenDark } },
      { title: 'Situación observada', text: example.situation, config: { fillColor: [238, 243, 247], textColor: blueDark } },
      { title: 'Riesgo detectado', text: example.risk, config: { fillColor: riskSoft, textColor: [160, 106, 7] } },
      { title: 'Respuesta eGuardian', text: example.response, config: { fillColor: responseSoft, textColor: greenDark } },
      { title: 'Decisión del operador', text: example.operatorDecision || 'Aplicar criterio operativo según el contexto observado.', config: { fillColor: decisionSoft, textColor: blueDark } },
    ];

    let estimatedHeight = 52;
    blocks.forEach((block) => {
      const lines = doc.splitTextToSize(block.text, innerWidth - 190);
      estimatedHeight += Math.max(20, lines.length * 13) + 8;
    });

    ensureSpace(estimatedHeight + 12, () => {
      drawPageChrome({ title: currentLessonTitle, showLessonTag: true });
      cursorY = 160;
    });

    setFill([255, 255, 255]);
    setStroke(lineSoft);
    doc.roundedRect(marginX, cursorY, innerWidth, estimatedHeight, 16, 16, 'FD');
    setFill([241, 247, 250]);
    doc.roundedRect(marginX, cursorY, innerWidth, 44, 16, 16, 'F');
    doc.rect(marginX, cursorY + 30, innerWidth, 14, 'F');

    doc.setFont('helvetica', 'bold');
    doc.setFontSize(13);
    setText(blueDark);
    doc.text(example.title, marginX + 18, cursorY + 27);

    let blockY = cursorY + 58;
    blocks.forEach((block, blockIndex) => {
      const usedHeight = drawCaseField(block.title, block.text, block.config, blockY, innerWidth - 30);
      blockY += Math.max(20, usedHeight) + 8;
      if (blockIndex < blocks.length - 1) {
        setStroke([232, 237, 242]);
        doc.setLineWidth(1);
        doc.line(marginX + 18, blockY - 4, pageWidth - marginX - 18, blockY - 4);
      }
    });

    cursorY += estimatedHeight + 10;
  };

  const drawQuizPanel = (lesson) => {
    if (!lesson.quiz?.length) return;

    lesson.quiz.forEach((question, index) => {
      const optionLines = question.options.flatMap((option) =>
        doc.splitTextToSize(`${option.id.toUpperCase()}. ${option.text}`, innerWidth - 54),
      );
      const answerText =
        question.options.find((option) => option.id === question.correctOptionId)?.text ||
        question.correctOptionId;
      const feedbackText = question.correctFeedback || question.incorrectFeedback || '';
      const answerLines = doc.splitTextToSize(`Respuesta esperada: ${answerText}`, innerWidth - 54);
      const feedbackLines = feedbackText ? doc.splitTextToSize(feedbackText, innerWidth - 54) : [];
      const boxHeight =
        50 +
        optionLines.length * 13 +
        answerLines.length * 13 +
        (feedbackLines.length ? feedbackLines.length * 13 + 16 : 0);

      ensureSpace(boxHeight + 10, () => {
        drawPageChrome({ title: currentLessonTitle, showLessonTag: true });
        cursorY = 160;
      });

      setFill([255, 255, 255]);
      setStroke(lineSoft);
      doc.roundedRect(marginX, cursorY, innerWidth, boxHeight, 14, 14, 'FD');

      doc.setFont('helvetica', 'bold');
      doc.setFontSize(11.5);
      setText(blueDark);
      const questionLines = doc.splitTextToSize(`${index + 1}. ${question.question}`, innerWidth - 32);
      doc.text(questionLines, marginX + 16, cursorY + 22);

      let innerY = cursorY + 24 + questionLines.length * 13;
      doc.setFont('helvetica', 'normal');
      doc.setFontSize(10.2);
      setText(BRAND.text.secondary);
      doc.text(optionLines, marginX + 16, innerY);
      innerY += optionLines.length * 13 + 8;

      setFill(greenSoft);
      doc.roundedRect(marginX + 16, innerY - 10, innerWidth - 32, answerLines.length * 13 + 18, 10, 10, 'F');
      doc.setFont('helvetica', 'bold');
      doc.setFontSize(10);
      setText(greenDark);
      doc.text(answerLines, marginX + 28, innerY + 2);
      innerY += answerLines.length * 13 + 16;

      if (feedbackLines.length) {
        doc.setFont('helvetica', 'normal');
        doc.setFontSize(9.8);
        setText(blueGrey);
        doc.text(feedbackLines, marginX + 16, innerY + 2);
      }

      cursorY += boxHeight + 10;
    });
  };

  const drawCoverPage = () => {
    setFill([242, 246, 248]);
    doc.rect(0, 0, pageWidth, pageHeight, 'F');
    setFill(greenDark);
    doc.rect(0, 0, pageWidth, 110, 'F');
    setFill([24, 88, 68]);
    doc.roundedRect(pageWidth - 210, 36, 150, 150, 28, 28, 'F');
    setFill([232, 245, 240]);
    doc.circle(pageWidth - 130, 108, 38, 'F');
    setStroke([255, 255, 255]);
    doc.setLineWidth(2.2);
    doc.circle(pageWidth - 130, 108, 26, 'S');
    doc.line(pageWidth - 142, 108, pageWidth - 132, 118);
    doc.line(pageWidth - 132, 118, pageWidth - 112, 95);

    doc.setFont('courier', 'normal');
    doc.setFontSize(11);
    setText([230, 243, 236]);
    doc.text('EGUARDIAN ACADEMY', marginX, 48);
    doc.setFont('helvetica', 'bold');
    doc.setFontSize(36);
    setText([255, 255, 255]);
    doc.text(courseData.code, marginX, 86);

    doc.setFont('helvetica', 'bold');
    doc.setFontSize(28);
    setText(blueDark);
    doc.text(courseDisplayTitle, marginX, 168);

    doc.setFont('helvetica', 'normal');
    doc.setFontSize(14);
    setText(blueGrey);
    const subtitleLines = doc.splitTextToSize(pdfContent.subtitle, innerWidth - 40);
    doc.text(subtitleLines, marginX, 196);

    setFill([255, 255, 255]);
    setStroke(lineSoft);
    doc.roundedRect(marginX, 226, innerWidth, 104, 18, 18, 'FD');
    doc.setFont('helvetica', 'normal');
    doc.setFontSize(12);
    setText(BRAND.text.secondary);
    const coverDescription = doc.splitTextToSize(courseData.description, innerWidth - 34);
    doc.text(coverDescription, marginX + 18, 252);

    const statWidth = (innerWidth - 20) / 3;
    [
      { label: 'Duración', value: courseData.duration },
      { label: 'Lecciones', value: `${pdfContent.lessons.length}` },
      { label: 'Formato', value: 'Manual PDF' },
    ].forEach((item, index) => {
      const x = marginX + index * (statWidth + 10);
      setFill([255, 255, 255]);
      doc.roundedRect(x, 356, statWidth, 68, 14, 14, 'F');
      doc.setFont('courier', 'normal');
      doc.setFontSize(9);
      setText(blueGrey);
      doc.text(item.label.toUpperCase(), x + 16, 382);
      doc.setFont('helvetica', 'bold');
      doc.setFontSize(16);
      setText(blueDark);
      doc.text(item.value, x + 16, 408);
    });

    setFill(greenSoft);
    doc.roundedRect(marginX, 456, innerWidth, 120, 18, 18, 'F');
    doc.setFont('helvetica', 'bold');
    doc.setFontSize(13);
    setText(greenDark);
    doc.text('Este material incluye', marginX + 18, 482);
    doc.setFont('helvetica', 'normal');
    doc.setFontSize(11);
    setText(BRAND.text.secondary);
    const bullets = [
      'Lecturas obligatorias por lección',
      'Resumen del contenido de cada video',
      'Casos prácticos con criterio operativo',
      'Quiz formativo por cada lección',
      'Ideas clave y cierre del curso',
    ];
    bullets.forEach((item, index) => {
      doc.text(`• ${item}`, marginX + 18, 510 + index * 20);
    });

    doc.setFont('courier', 'normal');
    doc.setFontSize(9);
    setText(BRAND.text.light);
    doc.text('eGuardian Academy · Material de apoyo para operadores de televigilancia', marginX, footerY);
    doc.text('Página 1', pageWidth - marginX, footerY, { align: 'right' });
  };

  const drawIndexPage = () => {
    drawPageChrome({ title: 'Índice', subtitle: 'Estructura del material de apoyo', showLessonTag: false });
    cursorY = 116;
    const items = [
      { title: 'Portada', subtitle: 'Presentación general del curso', section: '01' },
      { title: 'Índice', subtitle: 'Navegación del documento', section: '02' },
      ...pdfContent.lessons.map((lesson, index) => ({
        title: lesson.heading,
        subtitle: 'Lectura obligatoria, resumen del video, idea clave, casos prácticos y quiz formativo',
        section: `0${index + 3}`,
      })),
      { title: pdfContent.closing.title, subtitle: 'Síntesis del aprendizaje, mensaje final y próximo paso', section: `0${pdfContent.lessons.length + 3}` },
    ];

    items.forEach((item, index) => {
      const boxHeight = 52;
      ensureSpace(boxHeight + 8);
      setFill(index % 2 === 0 ? [255, 255, 255] : [250, 252, 253]);
      setStroke(lineSoft);
      doc.roundedRect(marginX, cursorY, innerWidth, boxHeight, 12, 12, 'FD');
      doc.setFont('helvetica', 'bold');
      doc.setFontSize(12);
      setText(blueDark);
      doc.text(item.title, marginX + 18, cursorY + 22);
      doc.setFont('helvetica', 'normal');
      doc.setFontSize(10.5);
      setText(blueGrey);
      doc.text(doc.splitTextToSize(item.subtitle, innerWidth - 96), marginX + 18, cursorY + 38);
      doc.setFont('courier', 'bold');
      doc.setFontSize(10);
      setText(greenDark);
      doc.text(item.section, pageWidth - marginX - 20, cursorY + 30, { align: 'right' });
      cursorY += boxHeight + 10;
    });
  };

  const drawLessonPage = (lesson) => {
    currentLessonTitle = lesson.heading;
    drawPageChrome({ title: lesson.heading, showLessonTag: true });
    cursorY = 150;

    drawSectionHeader('Lectura obligatoria');
    lesson.reading.forEach((paragraph, index) => {
      const isHeading = !paragraph.startsWith('•') && paragraph.length <= 42 && !paragraph.endsWith('.');
      drawReadingBlock(paragraph, isHeading);
    });

    drawDivider();
    drawSectionHeader('Resumen del video');
    drawVideoSummaryPanel(lesson);

    drawDivider();
    drawSectionHeader('Idea clave');
    drawIdeaHighlight(lesson.ideaHighlight);

    drawDivider();
    drawSectionHeader(lesson.id === 'eg101-lesson-3' ? 'Ejemplos de eventos' : 'Casos prácticos');
    lesson.examples.forEach((example) => drawExampleCard(example));

    if (lesson.quiz?.length) {
      drawDivider();
      drawSectionHeader('Quiz formativo', 'Preguntas de repaso para reforzar los conceptos principales de la lección.');
      drawQuizPanel(lesson);
    }
  };

  const drawClosingPage = () => {
    currentLessonTitle = pdfContent.closing.title;
    drawPageChrome({ title: pdfContent.closing.title, showLessonTag: false });
    cursorY = 128;
    drawSectionHeader('Síntesis del aprendizaje');
    pdfContent.closing.paragraphs.forEach((paragraph) => {
      drawParagraph(paragraph, { fontSize: 11.5, color: BRAND.text.secondary, lineHeight: 19, topGap: 6 });
      cursorY += 8;
    });
    drawDivider(18, 18);
    drawSectionHeader('Mensaje final');
    drawIdeaHighlight('Observar con atención, entender el contexto y actuar a tiempo es la base del servicio eGuardian y del trabajo profesional del operador.');
    drawDivider(18, 18);
    drawSectionHeader('Próximo paso');
    drawParagraph('Continúa con el siguiente módulo del nivel 1 para seguir fortaleciendo tu criterio operativo y consolidar lo aprendido en este curso base.', {
      fontSize: 11.5,
      color: BRAND.text.secondary,
      lineHeight: 19,
    });
  };

  drawCoverPage();
  pageNumber = 2;
  currentLessonTitle = '';
  doc.addPage();
  drawIndexPage();
  pageNumber = 3;
  pdfContent.lessons.forEach((lesson) => {
    doc.addPage();
    drawLessonPage(lesson);
    pageNumber += 1;
  });
  doc.addPage();
  drawClosingPage();
  const safeFileName = `${courseData.code}_${courseDisplayTitle}`
    .normalize('NFD')
    .replace(/[\u0300-\u036f]/g, '')
    .replace(/[^A-Za-z0-9]+/g, '_')
    .replace(/^_+|_+$/g, '');
  doc.save(`${safeFileName}.pdf`);
}

function CertificateView({ courseData, progressState, summary }) {
  const today = new Date().toLocaleDateString('es-CL', { day: '2-digit', month: 'long', year: 'numeric' });
  const level = LEVEL[courseData.level];
  const certId = `CERT-${courseData.code}-${new Date().getFullYear()}`;

  return (
    <div style={{ maxWidth: 840, margin: '0 auto' }}>
      <Card style={{ padding: 0, overflow: 'hidden', aspectRatio: '1.414/1', display: 'flex' }}>
        <div
          style={{
            width: '25%',
            background: `linear-gradient(160deg, ${level.dark} 0%, ${level.color} 100%)`,
            display: 'flex',
            flexDirection: 'column',
            alignItems: 'center',
            justifyContent: 'space-between',
            padding: '34px 20px',
            position: 'relative',
            overflow: 'hidden',
          }}
        >
          <svg style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', opacity: 0.08 }}>
            {[...Array(10)].map((_, row) => [...Array(6)].map((__, col) => (
              <circle key={`${row}-${col}`} cx={16 + col * 28} cy={16 + row * 28} r="1.5" fill="white" />
            )))}
          </svg>

          <div style={{ position: 'relative', textAlign: 'center' }}>
            <Logo dark={true} />
            <div
              style={{
                width: 64,
                height: 64,
                borderRadius: '50%',
                border: '3px solid rgba(255,255,255,0.8)',
                background: 'rgba(255,255,255,0.1)',
                display: 'flex',
                alignItems: 'center',
                justifyContent: 'center',
                margin: '20px auto 0',
              }}
            >
              <svg width="32" height="36" viewBox="0 0 34 38" fill="none">
                <path
                  d="M17 2L31 7.5V19C31 27 24.5 33.5 17 36C9.5 33.5 3 27 3 19V7.5Z"
                  fill="rgba(255,255,255,0.15)"
                  stroke="white"
                  strokeWidth="1.5"
                />
                <polyline
                  points="10,19 14.5,24 24,13"
                  fill="none"
                  stroke="white"
                  strokeWidth="2.5"
                  strokeLinecap="round"
                  strokeLinejoin="round"
                />
              </svg>
            </div>
          </div>

          <div style={{ position: 'relative', textAlign: 'center' }}>
            <div
              style={{
                background: level.color,
                border: '2px solid rgba(255,255,255,0.6)',
                borderRadius: 6,
                padding: '8px 16px',
                marginBottom: 12,
              }}
            >
              <div
                style={{
                  fontSize: 8,
                  color: 'rgba(255,255,255,0.7)',
                  letterSpacing: '0.1em',
                  textTransform: 'uppercase',
                  fontFamily: 'DM Mono,monospace',
                }}
              >
                Nivel
              </div>
              <div style={{ fontFamily: 'Space Grotesk,sans-serif', fontWeight: 800, fontSize: 14, color: 'white' }}>
                {level.name}
              </div>
            </div>
            <div style={{ fontSize: 8, color: 'rgba(255,255,255,0.5)', fontFamily: 'DM Mono,monospace' }}>
              {certId}
            </div>
          </div>
        </div>

        <div
          style={{
            flex: 1,
            padding: '26px 34px',
            position: 'relative',
            background: 'white',
          }}
          >
            <div
              style={{
                position: 'absolute',
                top: 0,
              right: 0,
              width: '60%',
              height: 6,
                background: `linear-gradient(90deg, transparent, ${BRAND.amber.main})`,
              }}
            />
            <div
              style={{
                position: 'absolute',
                inset: 0,
                display: 'flex',
                alignItems: 'center',
                justifyContent: 'center',
                opacity: 0.06,
                pointerEvents: 'none',
              }}
            >
              <svg width="340" height="340" viewBox="0 0 340 340" fill="none">
                <circle cx="170" cy="170" r="118" stroke={level.color} strokeWidth="10" />
                <circle cx="170" cy="170" r="92" stroke={level.color} strokeWidth="4" />
                <path d="M170 82L236 108V160C236 216 205 255 170 268C135 255 104 216 104 160V108Z" fill={level.color} fillOpacity="0.08" stroke={level.color} strokeWidth="4" />
                <polyline points="140,172 162,194 206,146" fill="none" stroke={level.color} strokeWidth="12" strokeLinecap="round" strokeLinejoin="round" />
                <text x="170" y="292" textAnchor="middle" fontFamily="DM Mono, monospace" fontSize="18" fill={level.color}>
                  eGuardian Academy
                </text>
              </svg>
            </div>

          <div
            style={{
              width: '100%',
              maxWidth: 760,
              height: '100%',
              margin: '0 auto',
              display: 'flex',
              flexDirection: 'column',
              justifyContent: 'space-between',
              position: 'relative',
              zIndex: 1,
            }}
          >
          <div
            style={{
              flex: 1,
              display: 'flex',
              flexDirection: 'column',
              justifyContent: 'center',
              paddingTop: 6,
            }}
          >
            <div
              style={{
                fontSize: 11,
                fontWeight: 500,
                letterSpacing: '0.15em',
                textTransform: 'uppercase',
                color: BRAND.text.light,
                marginBottom: 8,
                fontFamily: 'DM Mono,monospace',
              }}
            >
              SoiTel · Academia eGuardian
            </div>
            <div
              style={{
                fontFamily: 'Space Grotesk,sans-serif',
                fontWeight: 300,
                fontSize: 13,
                color: BRAND.text.muted,
                letterSpacing: '0.06em',
                textTransform: 'uppercase',
                marginBottom: 4,
              }}
            >
              Certificado de Aprobación
            </div>
            <h1
              style={{
                fontFamily: 'Space Grotesk,sans-serif',
                fontWeight: 800,
                fontSize: 32,
                color: BRAND.text.primary,
                margin: '0 0 22px',
                lineHeight: 1.05,
                maxWidth: 700,
              }}
            >
              {courseData.title}
            </h1>
            <div style={{ fontSize: 12, color: BRAND.text.muted, marginBottom: 6 }}>Otorgado a</div>
            <div
              style={{
                fontFamily: 'Space Grotesk,sans-serif',
                fontWeight: 700,
                fontSize: 30,
                color: level.color,
                marginBottom: 18,
              }}
            >
              {progressState.studentName}
            </div>
            <p style={{ fontSize: 13.5, color: BRAND.text.muted, lineHeight: 1.9, maxWidth: 700, margin: '0 0 28px' }}>
              SoiTel Soluciones TIC SpA certifica que <strong>{progressState.studentName}</strong> ha aprobado satisfactoriamente el curso <strong>{courseData.title}</strong>, demostrando comprensión del servicio eGuardian, del rol operativo en televigilancia activa, de la identificación de eventos y de los estándares profesionales asociados a la operación preventiva.
            </p>
            <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, minmax(0, 1fr))', gap: 26, maxWidth: 700 }}>
              <div>
                <div style={{ fontSize: 10, color: BRAND.text.light, textTransform: 'uppercase', letterSpacing: '0.08em' }}>Fecha de emisión</div>
                <div style={{ fontSize: 13, fontWeight: 600, color: BRAND.text.primary }}>{today}</div>
              </div>
              <div>
                <div style={{ fontSize: 10, color: BRAND.text.light, textTransform: 'uppercase', letterSpacing: '0.08em' }}>Calificación</div>
                <div style={{ fontSize: 13, fontWeight: 700, color: level.color }}>
                  {progressState.finalExamScore ? `${progressState.finalExamScore} / 100` : 'Sin registro'}
                </div>
              </div>
              <div>
                <div style={{ fontSize: 10, color: BRAND.text.light, textTransform: 'uppercase', letterSpacing: '0.08em' }}>Estado</div>
                <div style={{ fontSize: 13, fontWeight: 700, color: summary.canUnlockCertificate ? level.color : BRAND.text.muted }}>
                  {summary.canUnlockCertificate ? 'Aprobado' : 'Pendiente'}
                </div>
              </div>
            </div>
          </div>

          <div style={{ display: 'flex', gap: 40, borderTop: `1px solid ${BRAND.border}`, paddingTop: 20, alignItems: 'flex-end', justifyContent: 'space-between' }}>
            <div style={{ display: 'flex', gap: 72, justifyContent: 'center', flex: 1 }}>
              {[['Miguel Salgado', 'Director Académico'], ['Hugo Villa', 'Gerente General SoiTel']].map(([name, role]) => (
                <div key={name} style={{ width: 190, textAlign: 'center' }}>
                  <div style={{ width: 190, borderTop: `1px solid ${BRAND.text.light}`, marginBottom: 7 }} />
                  <div style={{ fontSize: 12, fontWeight: 600, color: BRAND.text.primary }}>{name}</div>
                  <div style={{ fontSize: 11, color: BRAND.text.light }}>{role}</div>
                </div>
              ))}
            </div>

            <button
              onClick={() => generateEg101CertificatePDF({ courseData, progressState })}
              style={{
                border: `1.5px solid ${level.color}`,
                borderRadius: 8,
                padding: '10px 14px',
                background: 'white',
                color: level.color,
                fontSize: 12,
                fontWeight: 700,
                cursor: 'pointer',
                whiteSpace: 'nowrap',
              }}
              type="button"
            >
              Descargar certificado
            </button>
          </div>
          </div>
        </div>
      </Card>
    </div>
  );
}

function GenericCourseDetail({ course, onBack }) {
  const [activeTab, setActiveTab] = useState('overview');
  const [moduleExpanded, setModuleExpanded] = useState(null);
  const sidebarBelow = useCourseSidebarBelow();
  const level = LEVEL[course.level];
  const modules = Array.from({ length: course.mods }, (_, index) => ({
    num: index + 1,
    title: getModuleTitle(course.icon, index),
    duration: `${12 + (index % 5) * 4} min`,
    done: index < Math.floor((course.mods * course.progress) / 100),
  }));

  return (
    <div>
      <button
        onClick={onBack}
        style={{
          display: 'flex',
          alignItems: 'center',
          gap: 6,
          background: 'none',
          border: 'none',
          cursor: 'pointer',
          color: BRAND.text.muted,
          fontSize: 13,
          fontWeight: 500,
          marginBottom: 20,
          fontFamily: 'Outfit,sans-serif',
          padding: 0,
        }}
        type="button"
      >
        <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
          <path d="M10 3L5 8L10 13" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" />
        </svg>
        Volver al catálogo
      </button>

      <div
        style={{
          display: 'grid',
          gridTemplateColumns: sidebarBelow ? 'minmax(0, 1fr)' : 'minmax(0, 1fr) 320px',
          gap: 24,
          alignItems: 'start',
        }}
      >
        <div style={{ minWidth: 0 }}>
          <Card style={{ overflow: 'hidden', marginBottom: 20 }}>
            <CourseThumbnail course={{ ...course, progress: 0 }} style={{ width: '100%' }} />
            <div style={{ padding: 24 }}>
              <div style={{ display: 'flex', gap: 8, marginBottom: 12, flexWrap: 'wrap' }}>
                <span style={{ fontSize: 11, fontWeight: 600, padding: '3px 10px', borderRadius: 6, background: level.light, color: level.color }}>
                  Nivel {course.level} - {level.name}
                </span>
                <span style={{ fontSize: 11, padding: '3px 10px', borderRadius: 6, background: BRAND.bg, color: BRAND.text.muted, border: `1px solid ${BRAND.border}` }}>
                  {course.code}
                </span>
              </div>
              <h1 style={{ fontFamily: 'Space Grotesk,sans-serif', fontWeight: 800, fontSize: 22, color: BRAND.text.primary, margin: '0 0 10px' }}>
                {course.title}
              </h1>
              <p style={{ fontSize: 14, color: BRAND.text.muted, margin: 0, lineHeight: 1.7 }}>{course.desc}</p>
            </div>
          </Card>

          <div style={{ display: 'flex', gap: 0, borderBottom: `1.5px solid ${BRAND.border}`, marginBottom: 20 }}>
            {[
              ['overview', 'Descripcion'],
              ['modules', 'Modulos'],
              ['resources', 'Recursos'],
            ].map(([tabId, label]) => (
              <button
                key={tabId}
                onClick={() => setActiveTab(tabId)}
                style={{
                  padding: '10px 20px',
                  border: 'none',
                  background: 'none',
                  cursor: 'pointer',
                  fontSize: 13,
                  fontWeight: activeTab === tabId ? 600 : 400,
                  color: activeTab === tabId ? level.color : BRAND.text.muted,
                  borderBottom: activeTab === tabId ? `2px solid ${level.color}` : '2px solid transparent',
                  marginBottom: -1.5,
                  fontFamily: 'Outfit,sans-serif',
                }}
                type="button"
              >
                {label}
              </button>
            ))}
          </div>

          {activeTab === 'overview' ? (
            <Card style={{ padding: 24 }}>
              <PanelTitle>Que aprenderas</PanelTitle>
              <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(240px, 1fr))', gap: 12, marginTop: 14 }}>
                {getObjectives(course.icon).map((objective, index) => (
                  <div key={index} style={{ display: 'flex', gap: 8, alignItems: 'flex-start' }}>
                    <span style={{ color: level.color }}>•</span>
                    <span style={{ fontSize: 13, lineHeight: 1.6, color: BRAND.text.secondary }}>{objective}</span>
                  </div>
                ))}
              </div>
            </Card>
          ) : null}

          {activeTab === 'modules' ? (
            <Card style={{ padding: 24 }}>
              <PanelTitle>Programa del curso</PanelTitle>
              <div style={{ display: 'grid', gap: 8, marginTop: 16 }}>
                {modules.map((module) => (
                  <div key={module.num}>
                    <button
                      onClick={() => setModuleExpanded(moduleExpanded === module.num ? null : module.num)}
                      style={{
                        width: '100%',
                        display: 'flex',
                        alignItems: 'center',
                        gap: 12,
                        padding: '12px 14px',
                        borderRadius: 10,
                        border: `1px solid ${BRAND.border}`,
                        background: module.done ? level.light : 'white',
                        textAlign: 'left',
                        cursor: 'pointer',
                      }}
                      type="button"
                    >
                      <div style={{ width: 28, height: 28, borderRadius: '50%', background: module.done ? level.color : BRAND.bg, color: module.done ? 'white' : BRAND.text.light, display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
                        {module.done ? <Icon name="check" color="currentColor" size={13} /> : module.num}
                      </div>
                      <div style={{ flex: 1 }}>
                        <div style={{ fontSize: 13, fontWeight: 600, color: BRAND.text.primary }}>{module.title}</div>
                        <div style={{ fontSize: 11, color: BRAND.text.light }}>{module.duration}</div>
                      </div>
                    </button>
                    {moduleExpanded === module.num ? (
                      <div style={{ padding: '14px 16px', background: '#FAFCFA', borderRadius: '0 0 10px 10px', border: `1px solid ${BRAND.border}`, borderTop: 'none' }}>
                        <div style={{ fontSize: 13, lineHeight: 1.65, color: BRAND.text.muted }}>
                          Este módulo cubre los conceptos fundamentales de {module.title.toLowerCase()}, con actividades prácticas y repaso final.
                        </div>
                      </div>
                    ) : null}
                  </div>
                ))}
              </div>
            </Card>
          ) : null}

          {activeTab === 'resources' ? (
            <Card style={{ padding: 24 }}>
              <PanelTitle>Material de apoyo</PanelTitle>
              <div style={{ display: 'grid', gap: 10, marginTop: 16 }}>
                {[
                  { type: 'PDF', name: `Manual del operador - ${course.title}`, size: '2.4 MB' },
                  { type: 'PDF', name: 'Guia de ejercicios practicos', size: '1.1 MB' },
                  { type: 'XLS', name: 'Plantilla de evaluación', size: '340 KB' },
                ].map((resource) => (
                  <div key={resource.name} style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '12px 0', borderBottom: `1px solid ${BRAND.border}` }}>
                    <div style={{ width: 36, height: 36, borderRadius: 8, background: resource.type === 'PDF' ? BRAND.red.light : BRAND.blue.light, color: resource.type === 'PDF' ? BRAND.red.main : BRAND.blue.main, display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 10, fontWeight: 700 }}>
                      {resource.type}
                    </div>
                    <div style={{ flex: 1 }}>
                      <div style={{ fontSize: 13, fontWeight: 600, color: BRAND.text.primary }}>{resource.name}</div>
                      <div style={{ fontSize: 11, color: BRAND.text.light }}>{resource.size}</div>
                    </div>
                  </div>
                ))}
              </div>
            </Card>
          ) : null}
        </div>

        <div
          style={{
            display: 'flex',
            flexDirection: 'column',
            gap: 16,
            position: sidebarBelow ? 'static' : 'sticky',
            top: sidebarBelow ? 'auto' : 0,
            order: sidebarBelow ? 2 : 0,
          }}
        >
          <Card style={{ padding: 20 }}>
            <div style={{ marginBottom: 12 }}>
              <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 6 }}>
                <span style={{ fontSize: 13, fontWeight: 600, color: BRAND.text.primary }}>Progreso</span>
                <span style={{ fontSize: 13, fontWeight: 700, color: level.color }}>{course.progress}%</span>
              </div>
              <ProgressBar value={course.progress} color={level.color} height={8} />
            </div>
            <button
              style={{
                width: '100%',
                padding: '12px 0',
                borderRadius: 10,
                border: 'none',
                background: `linear-gradient(135deg, ${level.color}, ${level.dark})`,
                color: 'white',
                fontSize: 14,
                fontWeight: 700,
                cursor: 'pointer',
              }}
              type="button"
            >
              {course.progress > 0 ? 'Continuar' : 'Iniciar ahora'}
            </button>
          </Card>

          <Card style={{ padding: 20 }}>
            <div style={{ fontFamily: 'Space Grotesk,sans-serif', fontWeight: 700, fontSize: 15, color: BRAND.text.primary, marginBottom: 14 }}>
              Informacion del curso
            </div>
            {[
              ['Codigo', course.code],
              ['Nivel', `Nivel ${course.level} - ${level.name}`],
              ['Duracion', course.dur],
              ['Modulos', `${course.mods} unidades`],
              ['Certificacion', 'Soitel · Academia eGuardian'],
            ].map(([label, value]) => (
              <div key={label} style={{ display: 'flex', justifyContent: 'space-between', gap: 12, padding: '7px 0', borderBottom: `1px solid ${BRAND.border}`, fontSize: 12 }}>
                <span style={{ color: BRAND.text.light }}>{label}</span>
                <span style={{ color: BRAND.text.secondary, fontWeight: 600, textAlign: 'right' }}>{value}</span>
              </div>
            ))}
          </Card>
        </div>
      </div>
    </div>
  );
}

function getModuleTitle(icon, index) {
  const titles = {
    shield: ['Introducción a eGuardian', 'Interfaz y navegación', 'Roles y permisos', 'Configuración inicial', 'Protocolos básicos', 'Evaluación final'],
    camera: ['Fundamentos de CCTV', 'Tipos de camaras', 'Angulos y cobertura', 'Resolucion y calidad', 'Almacenamiento de video', 'Mantenimiento preventivo', 'Configuracion IP', 'Evaluacion practica', 'Troubleshooting', 'Evaluacion final'],
    monitor: ['Instalacion iVMS-4200', 'Interfaz de usuario', 'Adicion de dispositivos', 'Visualizacion en vivo', 'Reproduccion y busqueda', 'Configuracion de alarmas', 'Exportacion de video', 'Usuarios y permisos', 'Reportes', 'Configuracion avanzada', 'Integraciones', 'Evaluacion final'],
    checklist: ['Marco operacional', 'Protocolo de inicio de turno', 'Gestión de incidentes', 'Escalamiento', 'Comunicaciones', 'Cierre de turno', 'Documentación', 'Evaluación final'],
    scales: ['Ley 21.659 - Antecedentes', 'Ámbito de aplicación', 'Obligaciones del operador', 'Registro y habilitación', 'Sanciones', 'Evaluación final'],
    default: Array.from({ length: 12 }, (_, idx) => `Modulo ${idx + 1} - Unidad de aprendizaje`),
  };
  const collection = titles[icon] || titles.default;
  return collection[index] || `Modulo ${index + 1}`;
}

function getObjectives(icon) {
  const objectives = {
    camera: ['Identificar tipos de camaras y sus aplicaciones', 'Configurar angulos y zonas de cobertura', 'Interpretar imagenes en tiempo real', 'Detectar fallas tecnicas comunes', 'Aplicar protocolos de mantenimiento', 'Gestionar grabaciones y almacenamiento'],
    monitor: ['Instalar y configurar iVMS-4200', 'Anadir y gestionar dispositivos de video', 'Monitorear camaras en tiempo real', 'Revisar grabaciones y exportar evidencia', 'Gestionar usuarios y permisos', 'Configurar alertas y notificaciones'],
    shield: ['Conocer la estructura de la plataforma', 'Navegar el catálogo de cursos', 'Entender el sistema de niveles', 'Configurar perfil personal', 'Interpretar el dashboard de progreso', 'Usar herramientas de estudio'],
    default: ['Dominar los conceptos clave del módulo', 'Aplicar protocolos en situaciones reales', 'Documentar correctamente los procedimientos', 'Identificar riesgos y tomar decisiones', 'Trabajar en equipo de forma efectiva', 'Cumplir estándares de calidad SoiTel'],
  };
  return objectives[icon] || objectives.default;
}

Object.assign(window, {
  CourseCatalog,
  CourseDetail,
  CourseDetailPage,
  CourseSectionNav,
  LessonContent,
  VideoPlayer,
  QuizComponent,
  FinalEvaluation,
  DownloadableMaterial,
  CertificateView,
});
