// ICONTEC compliance — Competencias master + Sesiones (capture flow) + employee TrainingProfile
const { useState: _ic_useState, useMemo: _ic_useMemo } = React;

// ─── 12 Competencias maestras Atica ──────────────────

const COMPETENCIAS_MAESTRAS = [
  {
    id: 'CMP-01', name: 'Manejo seguro de maquinaria de clasificación',
    cat: 'sst', criticality: 'alta', recur: 'Anual',
    description: 'Operación segura de trituradoras industriales: identificación de peligros mecánicos y térmicos, procedimiento LOTO, EPP térmico, parada de emergencia.',
    rolesCount: 8,
    coursesIds: ['c-01'],
    norma: 'Resolución 0312 de 2019 · SG-SST',
    evaluacionRequerida: true,
    evidenciasMinimas: ['lista_asistencia_firmada', 'acta_instructor', 'foto_aula', 'evaluacion_calificada', 'material_entregado'],
  },
  {
    id: 'CMP-02', name: 'Buenas Prácticas de Manufactura · BPM',
    cat: 'normativo', criticality: 'alta', recur: 'Anual',
    description: 'Higiene personal, manipulación, control de plagas y trazabilidad. Curso INVIMA obligatorio para personal con contacto con producto.',
    rolesCount: 7,
    coursesIds: ['c-02'],
    norma: 'Resolución 2674 de 2013 · INVIMA',
    evaluacionRequerida: true,
    evidenciasMinimas: ['lista_asistencia_firmada', 'evaluacion_calificada', 'material_entregado', 'reporte_plataforma'],
  },
  {
    id: 'CMP-03', name: 'Manipulación higiénica de alimentos',
    cat: 'normativo', criticality: 'alta', recur: 'Anual',
    description: 'Microbiología básica, ETAs, higiene locativa y personal. Certificación municipal con vigencia anual.',
    rolesCount: 6,
    coursesIds: ['c-07'],
    norma: 'Resolución 2674 · Sec. Salud Valle del Cauca',
    evaluacionRequerida: true,
    evidenciasMinimas: ['lista_asistencia_firmada', 'evaluacion_calificada', 'material_entregado', 'certificado_externo'],
  },
  {
    id: 'CMP-04', name: 'Trabajo seguro en alturas',
    cat: 'sst', criticality: 'alta', recur: 'Trianual',
    description: 'Trabajo > 1.5m. Equipos anticaídas, sistemas de acceso, normativa Resolución 1409 de 2012.',
    rolesCount: 1,
    coursesIds: ['c-08'],
    norma: 'Resolución 1409 de 2012 · CCS',
    evaluacionRequerida: true,
    evidenciasMinimas: ['lista_asistencia_firmada', 'acta_instructor', 'foto_aula', 'evaluacion_calificada', 'certificado_externo'],
  },
  {
    id: 'CMP-05', name: 'Primeros auxilios y brigada de emergencias',
    cat: 'sst', criticality: 'media', recur: 'Anual',
    description: 'Primeros auxilios, control de incendios incipientes, evacuación y rescate. Brigadistas voluntarios.',
    rolesCount: 1,
    coursesIds: ['c-03'],
    norma: 'Decreto 1072 · Brigada SG-SST',
    evaluacionRequerida: true,
    evidenciasMinimas: ['lista_asistencia_firmada', 'acta_instructor', 'foto_aula', 'evaluacion_calificada', 'material_entregado'],
  },
  {
    id: 'CMP-06', name: 'Reglamento Interno de Trabajo · Reforma Laboral 2025',
    cat: 'normativo', criticality: 'alta', recur: 'Por cambio',
    description: 'Conducta, jornada, sanciones, conducto regular. Actualizado conforme a la Reforma Laboral 2025.',
    rolesCount: 9,
    coursesIds: ['c-05'],
    norma: 'CST · Reforma Laboral Ley 2466 de 2025',
    evaluacionRequerida: false,
    evidenciasMinimas: ['lista_asistencia_firmada', 'acta_instructor', 'material_entregado'],
  },
  {
    id: 'CMP-07', name: 'Clasificación avanzada de residuos',
    cat: 'tecnico', criticality: 'media', recur: 'Una vez',
    description: 'Química del material entrante, curva del proceso, control de olores, análisis de laboratorio protocolar ISO 14001.',
    rolesCount: 2,
    coursesIds: ['c-04'],
    norma: 'Estándar ambiental Gestor ambiental',
    evaluacionRequerida: true,
    evidenciasMinimas: ['lista_asistencia_firmada', 'certificado_externo', 'material_entregado'],
  },
  {
    id: 'CMP-08', name: 'Liderazgo de equipos operativos',
    cat: 'liderazgo', criticality: 'media', recur: 'Una vez',
    description: 'Liderazgo situacional, manejo de conflictos, retroalimentación, coaching.',
    rolesCount: 3,
    coursesIds: ['c-06'],
    norma: 'Diplomado EAFIT',
    evaluacionRequerida: true,
    evidenciasMinimas: ['lista_asistencia_firmada', 'certificado_externo', 'reporte_plataforma'],
  },
  {
    id: 'CMP-09', name: 'Inducción Atica',
    cat: 'onboarding', criticality: 'alta', recur: 'Una vez · ingreso',
    description: 'Recorrido por la cultura, valores, procesos críticos y stakeholders de Atica.',
    rolesCount: 9,
    coursesIds: ['c-09'],
    norma: 'Política interna · matriz onboarding',
    evaluacionRequerida: false,
    evidenciasMinimas: ['lista_asistencia_firmada', 'acta_instructor', 'material_entregado'],
  },
  {
    id: 'CMP-10', name: 'Protección de datos personales · habeas data',
    cat: 'normativo', criticality: 'media', recur: 'Anual',
    description: 'Tratamiento de datos personales conforme Ley 1581 de 2012. Aplicable a roles que manejan información de clientes y empleados.',
    rolesCount: 4,
    coursesIds: ['c-10'],
    norma: 'Ley 1581 de 2012',
    evaluacionRequerida: true,
    evidenciasMinimas: ['lista_asistencia_firmada', 'evaluacion_calificada', 'material_entregado', 'reporte_plataforma'],
  },
  {
    id: 'CMP-11', name: 'SG-SST · Sistema de Gestión SST',
    cat: 'sst', criticality: 'alta', recur: 'Anual',
    description: 'Marco general del sistema de gestión SST según Decreto 1072 y Resolución 0312. Para todos los colaboradores.',
    rolesCount: 9,
    coursesIds: ['c-11'],
    norma: 'Decreto 1072 · Resolución 0312',
    evaluacionRequerida: true,
    evidenciasMinimas: ['lista_asistencia_firmada', 'acta_instructor', 'evaluacion_calificada', 'material_entregado'],
  },
  {
    id: 'CMP-12', name: 'Manejo defensivo y seguridad vial',
    cat: 'sst', criticality: 'alta', recur: 'Anual',
    description: 'Conducción defensiva, fatiga, riesgos viales. Plan estratégico de seguridad vial Resolución 1565.',
    rolesCount: 1,
    coursesIds: ['c-12'],
    norma: 'Resolución 1565 de 2014 · PESV',
    evaluacionRequerida: true,
    evidenciasMinimas: ['lista_asistencia_firmada', 'acta_instructor', 'evaluacion_calificada', 'material_entregado'],
  },
];

const CMP_CAT = {
  sst:        { label: 'SST',         tone: 'rose',  icon: 'IconShield' },
  normativo:  { label: 'Normativo',   tone: 'navy',  icon: 'IconBook' },
  tecnico:    { label: 'Técnico',     tone: 'blue',  icon: 'IconZap' },
  liderazgo:  { label: 'Liderazgo',   tone: 'amber', icon: 'IconStar' },
  onboarding: { label: 'Onboarding',  tone: 'green', icon: 'IconRocket' },
};

const CRITICALITY = {
  alta:  { label: 'Alta',  pill: 'rose',  dot: 'var(--rose-600)' },
  media: { label: 'Media', pill: 'amber-soft', dot: '#854D0E' },
  baja:  { label: 'Baja',  pill: 'navy',  dot: 'var(--text-muted)' },
};

// Catálogo de tipos de evidencia exigibles por una competencia.
// Lo consumen SessionCapture (Fase B) y Modo Auditoría (Fase D).
const EVIDENCE_TYPES = {
  lista_asistencia_firmada: { label: 'Lista de asistencia firmada', icon: 'IconCheck',    obligatoria: true  },
  acta_instructor:          { label: 'Acta del instructor',          icon: 'IconBook',     obligatoria: true  },
  foto_aula:                { label: 'Foto del aula / sesión',       icon: 'IconExternal', obligatoria: false },
  evaluacion_calificada:    { label: 'Evaluación calificada',        icon: 'IconStar',     obligatoria: true  },
  material_entregado:       { label: 'Material entregado',           icon: 'IconBook',     obligatoria: false },
  certificado_externo:      { label: 'Certificado emitido por proveedor', icon: 'IconShield', obligatoria: true },
  reporte_plataforma:       { label: 'Reporte de plataforma e-learning',  icon: 'IconExternal', obligatoria: true },
};

// ─── Sesiones programadas + 1 capturable de demo ──────────

const SESSIONS_SCHED = [
  {
    id: 'SES-2026-0518', courseId: 'c-01', courseName: 'Manejo seguro de maquinaria de clasificación',
    competenciaId: 'CMP-01', cat: 'sst', criticality: 'alta',
    date: '14/05/2026', time: '08:00 - 16:00', site: 'Cali · Aula 2',
    instructor: 'Carlos Fernando Vélez', instructorOrg: 'ARL Sura',
    contentVersion: 'v3.2', minScore: 80,
    state: 'programada',
    enrolled: [
      { empId: 'E0247', initials: 'AM', name: 'Andrés Felipe Mejía',  role: 'Operario de Clasificación' },
      { empId: 'E0205', initials: 'CV', name: 'Carlos Andrés Vélez',  role: 'Operario de Clasificación' },
      { empId: 'E0156', initials: 'PV', name: 'Paula Valencia Ríos',  role: 'Téc. Mantenimiento' },
      { empId: 'E0067', initials: 'LM', name: 'Luis Miguel Marín',    role: 'Líder de Aprovechamiento' },
    ],
  },
  {
    id: 'SES-2026-0519', courseId: 'c-02', courseName: 'Buenas Prácticas de Manufactura · BPM',
    competenciaId: 'CMP-02', cat: 'normativo', criticality: 'alta',
    date: '15/05/2026', time: 'E-learning · 24h',  site: 'Plataforma interna',
    instructor: 'Sistema autoinstructivo', instructorOrg: 'INVIMA contenido',
    contentVersion: 'v1.4', minScore: 75,
    state: 'programada',
    enrolled: [
      { empId: 'E0246', initials: 'LG', name: 'Laura Gómez Restrepo', role: 'Operaria de Planta' },
      { empId: 'E0245', initials: 'JR', name: 'Juan David Ruiz',     role: 'Aux. Calidad' },
      { empId: 'E0198', initials: 'MF', name: 'María Fernanda López', role: 'Op. Aprovechamiento' },
    ],
  },
  {
    id: 'SES-2026-0520', courseId: 'c-08', courseName: 'Trabajo seguro en alturas',
    competenciaId: 'CMP-04', cat: 'sst', criticality: 'alta',
    date: '04/06/2026', time: '08:00 - 17:00 (5 días)', site: 'Cali · Centro CCS',
    instructor: 'Equipo CCS', instructorOrg: 'Consejo Colombiano de Seguridad',
    contentVersion: 'v2.0', minScore: 85,
    state: 'programada',
    enrolled: [
      { empId: 'E0156', initials: 'PV', name: 'Paula Valencia Ríos', role: 'Téc. Mantenimiento' },
    ],
  },
  // Una sesión histórica completamente capturada — para demo del expediente
  {
    id: 'SES-2026-0412', courseId: 'c-01', courseName: 'Manejo seguro de maquinaria de clasificación',
    competenciaId: 'CMP-01', cat: 'sst', criticality: 'alta',
    date: '12/04/2026', time: '08:00 - 16:00', site: 'Cali · Aula 2',
    instructor: 'Carlos Fernando Vélez', instructorOrg: 'ARL Sura',
    contentVersion: 'v3.2', minScore: 80,
    state: 'cerrada',
    closedAt: '12/04/2026 16:42',
    closedBy: 'Carolina Restrepo · Jefe de RRHH',
    sessionHash: 'a4f2c1e89b7d3f5e26c8d4b1a9f3e7c5d8b2a4f6e9c1d3b5a7f8e0c2d4b6a9f1',
    enrolled: 18, passed: 17,
    evidencesCount: 4,
  },
];

const STATE_SES = {
  programada: { label: 'Programada',  pill: 'amber-soft', dot: '#854D0E' },
  curso:      { label: 'En captura',  pill: 'navy',       dot: 'var(--navy-800)' },
  cerrada:    { label: 'Cerrada',     pill: 'green',      dot: 'var(--green-600)' },
  anulada:    { label: 'Anulada',     pill: 'rose',       dot: 'var(--rose-600)' },
};

// ─── Plantilla de evaluación (banco de preguntas para c-01) ──

const EVAL_TEMPLATE_C01 = [
  {
    id: 'q1', text: '¿Cuál es el primer paso del procedimiento LOTO antes de intervenir una trituradora?',
    options: ['Avisar al supervisor', 'Bloquear la fuente de energía y colocar etiqueta', 'Cerrar la válvula de gas', 'Apagar el extractor'],
    correct: 1,
  },
  {
    id: 'q2', text: 'Temperatura mínima a la que un EPP térmico clase 2 protege:',
    options: ['100°C', '200°C', '350°C', '500°C'],
    correct: 2,
  },
  {
    id: 'q3', text: 'Si detectas humo en la zona de clasificación durante el ciclo, la primera acción es:',
    options: ['Activar parada de emergencia', 'Llamar a bomberos', 'Salir del área inmediatamente', 'Verificar la temperatura del lote'],
    correct: 0,
  },
  {
    id: 'q4', text: '¿Cada cuánto debe revisarse el sistema de venteo de la trituradora?',
    options: ['Cada turno', 'Semanal', 'Quincenal', 'Mensual'],
    correct: 0,
  },
  {
    id: 'q5', text: 'Verdadero o falso: el operario puede retirar la guarda mecánica si el lote está atascado.',
    options: ['Verdadero · si avisa al supervisor', 'Verdadero · siempre', 'Falso · jamás bajo ninguna circunstancia', 'Falso · solo bajo bloqueo LOTO previo'],
    correct: 3,
  },
];

// ─── Bitácora · 30 entradas demo ──────────────────────────

const AUDIT_LOG = [
  { id: 'AL-2391', when: '05/05/2026 09:42', who: 'Carolina Restrepo', ip: '190.85.247.32', action: 'sesión programada', entity: 'SES-2026-0518', detail: 'Manejo seguro de maquinaria · 14/05', tone: 'navy' },
  { id: 'AL-2390', when: '05/05/2026 09:38', who: 'Carolina Restrepo', ip: '190.85.247.32', action: 'asignación generada', entity: '4 colaboradores', detail: 'Inscripción automática por matriz cargo', tone: 'navy' },
  { id: 'AL-2387', when: '04/05/2026 16:12', who: 'Patricia Gómez',   ip: '190.85.247.18', action: 'evidencia cargada', entity: 'SES-2026-0412', detail: 'lista-asistencia-firmada.pdf · 380 KB', tone: 'green' },
  { id: 'AL-2384', when: '03/05/2026 14:30', who: 'Carlos F. Vélez',  ip: '181.49.122.4',  action: 'curso publicado',   entity: 'c-01 → v3.2',  detail: 'Cambio: actualizada sección LOTO', tone: 'amber' },
  { id: 'AL-2383', when: '03/05/2026 14:28', who: 'Carlos F. Vélez',  ip: '181.49.122.4',  action: 'curso editado',     entity: 'c-01 v3.1 → v3.2', detail: 'Pendiente publicación', tone: 'navy' },
  { id: 'AL-2380', when: '12/04/2026 16:42', who: 'Carolina Restrepo', ip: '190.85.247.32', action: 'sesión CERRADA',    entity: 'SES-2026-0412', detail: '17 aprobados · 1 no aprobado · hash a4f2c1…', tone: 'green' },
  { id: 'AL-2379', when: '12/04/2026 16:40', who: 'Carolina Restrepo', ip: '190.85.247.32', action: 'certificados emitidos', entity: 'SES-2026-0412', detail: '17 certificados · vigencia 365 días', tone: 'green' },
  { id: 'AL-2378', when: '12/04/2026 16:35', who: 'Carlos F. Vélez',  ip: '181.49.122.4',  action: 'evaluación aplicada', entity: 'SES-2026-0412', detail: '5 preguntas · 18 respondieron', tone: 'navy' },
  { id: 'AL-2377', when: '12/04/2026 16:20', who: 'Carlos F. Vélez',  ip: '181.49.122.4',  action: 'evidencia cargada', entity: 'SES-2026-0412', detail: 'foto-aula-clasificacion.jpg · 1.2 MB', tone: 'green' },
  { id: 'AL-2376', when: '12/04/2026 16:18', who: 'Carlos F. Vélez',  ip: '181.49.122.4',  action: 'evidencia cargada', entity: 'SES-2026-0412', detail: 'acta-instructor-firmada.pdf · 240 KB', tone: 'green' },
  { id: 'AL-2375', when: '12/04/2026 09:15', who: 'Carlos Vélez (E0205)', ip: '181.49.55.21', action: 'firma electrónica · asistencia', entity: 'SES-2026-0412', detail: 'OTP verificado vía SMS · +57 ··· 4321', tone: 'green' },
  { id: 'AL-2374', when: '12/04/2026 09:14', who: 'Andrés Mejía (E0247)', ip: '181.49.55.18', action: 'firma electrónica · asistencia', entity: 'SES-2026-0412', detail: 'OTP verificado vía email', tone: 'green' },
  { id: 'AL-2373', when: '12/04/2026 09:12', who: 'Carlos F. Vélez',  ip: '181.49.122.4',  action: 'sesión iniciada',   entity: 'SES-2026-0412', detail: 'Captura abierta · 18 inscritos', tone: 'navy' },
  { id: 'AL-2371', when: '08/04/2026 11:00', who: 'Carolina Restrepo', ip: '190.85.247.32', action: 'matriz actualizada', entity: 'cargo: Operario de Clasificación', detail: 'Agregada CMP-11 SG-SST', tone: 'amber' },
  { id: 'AL-2368', when: '02/04/2026 09:30', who: 'Sistema',          ip: 'sistema',       action: 'reasignación automática', entity: '12 colaboradores', detail: 'Vencimiento BPM · nueva sesión generada', tone: 'navy' },
  { id: 'AL-2360', when: '15/03/2026 14:22', who: 'Carolina Restrepo', ip: '190.85.247.32', action: 'sesión anulada',   entity: 'SES-2026-0314', detail: 'Anulada por instructor ausente · razón documentada', tone: 'rose' },
];

// ─── Per-employee training data — used by employee-detail ─

const COMPETENCIAS_BY_EMP = {
  // Carlos Andrés Vélez — Operario de Clasificación
  'E0205': {
    required: ['CMP-01', 'CMP-02', 'CMP-03', 'CMP-06', 'CMP-09', 'CMP-11'],
    completed: [
      { cmpId: 'CMP-01', courseId: 'c-01', sessionId: 'SES-2026-0412', date: '12/04/2026', score: 92, certId: 'CERT-2026-c-01-04821', vigencia: '12/04/2027', signedAttendance: true, evidences: 4, version: 'v3.2' },
      { cmpId: 'CMP-09', courseId: 'c-09', sessionId: 'SES-2024-0314', date: '14/03/2024', score: 95, certId: 'CERT-2024-c-09-01102', vigencia: 'sin vencimiento', signedAttendance: true, evidences: 3, version: 'v2.0' },
      { cmpId: 'CMP-06', courseId: 'c-05', sessionId: 'SES-2026-0210', date: '10/02/2026', score: 88, certId: 'CERT-2026-c-05-03415', vigencia: '10/02/2027', signedAttendance: true, evidences: 2, version: 'v1.4' },
      { cmpId: 'CMP-11', courseId: 'c-11', sessionId: 'SES-2025-0820', date: '20/08/2025', score: 84, certId: 'CERT-2025-c-11-09921', vigencia: '20/08/2026', signedAttendance: true, evidences: 3, version: 'v2.1' },
    ],
    inProgress: [
      { cmpId: 'CMP-02', courseId: 'c-02', sessionId: 'SES-2026-0501', date: '01/05/2026', progress: 65 },
    ],
    expired: [
      { cmpId: 'CMP-03', courseId: 'c-07', sessionId: 'SES-2024-0510', date: '10/05/2024', score: 82, daysExpired: 4, status: 'reasignada', newSessionId: 'SES-2026-0530' },
    ],
  },
};

// ─── Helpers ──────────────────────────────────────────────

const getCmp = (id) => COMPETENCIAS_MAESTRAS.find(c => c.id === id);

// ═══════════════════════════════════════════════════════════
// Tab: Competencias master
// ═══════════════════════════════════════════════════════════

const CompetenciasTab = () => {
  // State local mutable hidratado desde window.COMPETENCIAS_MAESTRAS
  const [comps, setComps] = _ic_useState(() => [...(window.COMPETENCIAS_MAESTRAS || COMPETENCIAS_MAESTRAS)]);
  const [bancoPreguntas, setBancoPreguntas] = _ic_useState(() => [...(window.BANCO_PREGUNTAS || [])]);
  const [filterCat, setFilterCat]   = _ic_useState('todas');
  const [filterCrit, setFilterCrit] = _ic_useState('todas');
  const [editing, setEditing]   = _ic_useState(null); // 'nueva' | competencia | null

  // Sincronizar window con cambios
  React.useEffect(() => { window.COMPETENCIAS_MAESTRAS = comps; }, [comps]);
  React.useEffect(() => { window.BANCO_PREGUNTAS = bancoPreguntas; }, [bancoPreguntas]);

  const list = comps.filter(c =>
    (filterCat === 'todas'  || c.cat === filterCat) &&
    (filterCrit === 'todas' || c.criticality === filterCrit)
  );
  const altas = comps.filter(c => c.criticality === 'alta').length;

  const saveCompetencia = (data) => {
    if (data.id && comps.some(c => c.id === data.id)) {
      setComps(comps.map(c => c.id === data.id ? { ...c, ...data } : c));
    } else {
      const nextNum = String(comps.length + 1).padStart(2, '0');
      const id = data.id || `CMP-${nextNum}`;
      setComps([...comps, { ...data, id, rolesCount: 0, coursesIds: [] }]);
    }
    setEditing(null);
  };

  const deleteCompetencia = (id) => {
    if (!confirm(`¿Eliminar la competencia ${id}? Esta acción afecta cualquier cargo o curso vinculado.`)) return;
    setComps(comps.filter(c => c.id !== id));
    setBancoPreguntas(bancoPreguntas.filter(p => p.competenciaId !== id));
  };

  const savePregunta = (data) => {
    if (data.id && bancoPreguntas.some(p => p.id === data.id)) {
      setBancoPreguntas(bancoPreguntas.map(p => p.id === data.id ? { ...p, ...data } : p));
    } else {
      const id = data.id || `BP-${String(bancoPreguntas.length + 200).padStart(3, '0')}`;
      setBancoPreguntas([...bancoPreguntas, { ...data, id }]);
    }
  };

  const deletePregunta = (id) => setBancoPreguntas(bancoPreguntas.filter(p => p.id !== id));

  const preguntasPorComp = (cmpId) => bancoPreguntas.filter(p => p.competenciaId === cmpId);

  return (
    <>
      <div className="ic-cmp-banner">
        <div className="ic-cmp-banner-icon"><IconShield size={18} /></div>
        <div style={{ flex: 1 }}>
          <div className="ic-cmp-banner-title">Catálogo maestro · base ICONTEC</div>
          <div className="ic-cmp-banner-sub">
            Las competencias son el contrato auditable entre cargo y formación. Cada cargo exige <b>n competencias</b>; cada competencia se cubre con <b>uno o más cursos</b> con versionado de contenido. <b>El banco de preguntas de la evaluación se asocia a la competencia</b>.
          </div>
        </div>
        <div className="ic-cmp-banner-stat">
          <div className="ic-cmp-banner-num">{comps.length}</div>
          <div className="muted" style={{ fontSize: 11 }}>competencias · {altas} críticas</div>
        </div>
      </div>

      <div className="sst-quickfilter">
        <button className={`sst-chip ${filterCat === 'todas' ? 'active' : ''}`} onClick={() => setFilterCat('todas')}>Todas las áreas</button>
        {Object.entries(CMP_CAT).map(([k, v]) => (
          <button key={k} className={`sst-chip ${filterCat === k ? 'active' : ''}`} onClick={() => setFilterCat(k)}>{v.label}</button>
        ))}
        <div style={{ flex: 1 }} />
        <select value={filterCrit} onChange={e => setFilterCrit(e.target.value)}>
          <option value="todas">Cualquier criticidad</option>
          <option value="alta">Solo críticas</option>
          <option value="media">Media</option>
          <option value="baja">Baja</option>
        </select>
        <button className="btn primary" onClick={() => setEditing('nueva')}><IconPlus size={14} /> Nueva competencia</button>
      </div>

      <div className="ic-cmp-grid">
        {list.map(c => {
          const cat = CMP_CAT[c.cat];
          const crit = CRITICALITY[c.criticality];
          const Ico = window[cat.icon] || IconBook;
          const numPreguntas = preguntasPorComp(c.id).length;
          return (
            <div key={c.id} className={`ic-cmp-card crit-${c.criticality}`}>
              <div className="ic-cmp-head">
                <span className={`tr-cat-badge ${cat.tone}`}><Ico size={11} /> {cat.label}</span>
                <span className={`pill ${crit.pill}`}>
                  <span className="status-dot" style={{ background: crit.dot }} /> Crit. {crit.label}
                </span>
              </div>
              <div className="ic-cmp-id mono">{c.id}</div>
              <div className="ic-cmp-name">{c.name}</div>
              <div className="ic-cmp-desc">{c.description}</div>
              <div className="ic-cmp-meta">
                <div><div className="muted-xs">Recurrencia</div><div className="ic-cmp-meta-val">{c.recur}</div></div>
                <div><div className="muted-xs">Cursos que la cubren</div><div className="ic-cmp-meta-val mono">{(c.coursesIds || []).length}</div></div>
                <div><div className="muted-xs">Preguntas en banco</div><div className="ic-cmp-meta-val mono" style={{ color: numPreguntas === 0 ? 'var(--rose-600)' : 'var(--green-600)' }}>{numPreguntas}</div></div>
              </div>
              <div className="ic-cmp-norma">
                <IconShield size={10} /> {c.norma}
              </div>
              <div style={{ display: 'flex', gap: 6 }}>
                <button className="btn" style={{ flex: 1, padding: '6px 10px', fontSize: 12 }} onClick={() => deleteCompetencia(c.id)}>
                  <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><path d="M3 6h18M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2m3 0v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6" /></svg>
                  Eliminar
                </button>
                <button className="btn primary" style={{ flex: 1, padding: '6px 10px', fontSize: 12 }} onClick={() => setEditing(c)}>
                  <IconExternal size={13} /> Editar
                </button>
              </div>
            </div>
          );
        })}
      </div>

      {editing && (
        <CompetenciaEditorModal
          competencia={editing === 'nueva' ? null : editing}
          preguntas={editing && editing !== 'nueva' ? preguntasPorComp(editing.id) : []}
          onClose={() => setEditing(null)}
          onSave={saveCompetencia}
          onSavePregunta={savePregunta}
          onDeletePregunta={deletePregunta}
        />
      )}
    </>
  );
};

// ═══════════════════════════════════════════════════════════
// Generador IA mock · produce preguntas a partir de la competencia
// ═══════════════════════════════════════════════════════════

// Templates de preguntas según categoría · simulan generación contextual
const _IA_TEMPLATES = {
  sst: [
    (cmp) => ({ enunciado: `¿Cuál es el primer paso del procedimiento de seguridad en ${cmp.name.toLowerCase()}?`,
      opciones: ['Avisar al supervisor', 'Bloquear la fuente de energía y colocar etiqueta', 'Cerrar todas las válvulas', 'Apagar el sistema completo'],
      respuestaCorrecta: 1, dificultad: 'media', etiquetas: ['LOTO', 'seguridad'] }),
    (cmp) => ({ enunciado: `Según ${cmp.norma}, ¿qué EPP es obligatorio para esta actividad?`,
      opciones: ['Solo guantes', 'EPP completo según matriz IPVR', 'Casco y botas únicamente', 'Depende del operario'],
      respuestaCorrecta: 1, dificultad: 'facil', etiquetas: ['EPP'] }),
    (cmp) => ({ enunciado: `Ante un incidente durante la operación, la primera acción es:`,
      opciones: ['Activar parada de emergencia', 'Continuar y reportar al final', 'Llamar a recursos humanos', 'Documentar después del turno'],
      respuestaCorrecta: 0, dificultad: 'dificil', etiquetas: ['emergencia'] }),
    (cmp) => ({ enunciado: `¿Con qué frecuencia debe revisarse el sistema según el plan de mantenimiento?`,
      opciones: ['Cada turno', 'Semanal', 'Mensual', 'Anual'],
      respuestaCorrecta: 0, dificultad: 'media', etiquetas: ['mantenimiento'] }),
    (cmp) => ({ enunciado: `Verdadero o falso: El operario puede modificar los procedimientos de seguridad si considera que es más eficiente.`,
      opciones: ['Verdadero', 'Falso'],
      respuestaCorrecta: 1, dificultad: 'facil', etiquetas: ['cumplimiento'], tipo: 'verdadero-falso' }),
  ],
  normativo: [
    (cmp) => ({ enunciado: `¿Qué resolución regula esta competencia?`,
      opciones: [cmp.norma.split('·')[0]?.trim() || 'Norma A', 'Resolución 1409 de 2012', 'Decreto 1072 de 2015', 'Ley 100 de 1993'],
      respuestaCorrecta: 0, dificultad: 'facil', etiquetas: ['normativa'] }),
    (cmp) => ({ enunciado: `¿Quién audita el cumplimiento de esta normativa?`,
      opciones: ['MinTrabajo', 'INVIMA', 'Secretaría de Salud', 'Depende de la norma específica'],
      respuestaCorrecta: 3, dificultad: 'media', etiquetas: ['auditoría'] }),
    (cmp) => ({ enunciado: `¿Cada cuánto se debe refrescar este conocimiento?`,
      opciones: ['Una vez al ingreso', cmp.recur || 'Anual', 'Cada 3 años', 'Solo cuando cambia la norma'],
      respuestaCorrecta: 1, dificultad: 'facil', etiquetas: ['recurrencia'] }),
    (cmp) => ({ enunciado: `Verdadero o falso: el cumplimiento de esta norma es opcional para empresas pequeñas.`,
      opciones: ['Verdadero', 'Falso'],
      respuestaCorrecta: 1, dificultad: 'facil', etiquetas: ['cumplimiento'], tipo: 'verdadero-falso' }),
    (cmp) => ({ enunciado: `Frente a una observación de auditoría sobre esta competencia, la acción correcta es:`,
      opciones: ['Esperar al cierre', 'Levantar plan de acción inmediato', 'Justificar por costos', 'Solicitar prórroga indefinida'],
      respuestaCorrecta: 1, dificultad: 'dificil', etiquetas: ['auditoría'] }),
  ],
  tecnico: [
    (cmp) => ({ enunciado: `¿Cuál es el indicador clave para medir el desempeño en ${cmp.name.toLowerCase()}?`,
      opciones: ['Velocidad de proceso', 'Calidad del resultado final', 'Cumplimiento de protocolo', 'Todas las anteriores'],
      respuestaCorrecta: 3, dificultad: 'media', etiquetas: ['KPI'] }),
    (cmp) => ({ enunciado: `Una desviación crítica en el proceso debe escalarse a:`,
      opciones: ['Operario del siguiente turno', 'Líder de área inmediatamente', 'Reportar en informe semanal', 'No se requiere escalar'],
      respuestaCorrecta: 1, dificultad: 'media', etiquetas: ['escalamiento'] }),
    (cmp) => ({ enunciado: `Verdadero o falso: la documentación del proceso es opcional si el operario tiene experiencia.`,
      opciones: ['Verdadero', 'Falso'],
      respuestaCorrecta: 1, dificultad: 'facil', etiquetas: ['trazabilidad'], tipo: 'verdadero-falso' }),
  ],
  liderazgo: [
    (cmp) => ({ enunciado: `Frente a un conflicto entre dos colaboradores, la primera acción del líder es:`,
      opciones: ['Decidir por su cuenta', 'Escuchar a ambas partes por separado', 'Notificar a RRHH', 'Ignorar hasta que se resuelva solo'],
      respuestaCorrecta: 1, dificultad: 'media', etiquetas: ['conflictos'] }),
    (cmp) => ({ enunciado: `¿Cuál es el estilo de liderazgo más adecuado para un equipo nuevo?`,
      opciones: ['Directivo', 'Coaching', 'Delegativo', 'Depende del nivel de cada persona'],
      respuestaCorrecta: 3, dificultad: 'dificil', etiquetas: ['liderazgo-situacional'] }),
    (cmp) => ({ enunciado: `Una retroalimentación efectiva debe ser:`,
      opciones: ['Específica y oportuna', 'Pública para mayor impacto', 'Solo positiva', 'Únicamente al cierre del año'],
      respuestaCorrecta: 0, dificultad: 'facil', etiquetas: ['feedback'] }),
  ],
  onboarding: [
    (cmp) => ({ enunciado: `¿Cuáles son los valores corporativos de la organización?`,
      opciones: ['Solo crecimiento económico', 'Calidad, seguridad, integridad y compromiso', 'Eficiencia ante todo', 'Solo cumplir con la ley'],
      respuestaCorrecta: 1, dificultad: 'facil', etiquetas: ['cultura'] }),
    (cmp) => ({ enunciado: `El conducto regular para reportar una inquietud es:`,
      opciones: ['Directamente al CEO', 'Líder directo → RRHH → Comité de Convivencia', 'Redes sociales', 'Esperar a que pase'],
      respuestaCorrecta: 1, dificultad: 'facil', etiquetas: ['conducto-regular'] }),
    (cmp) => ({ enunciado: `Verdadero o falso: la firma del reglamento interno es opcional.`,
      opciones: ['Verdadero', 'Falso'],
      respuestaCorrecta: 1, dificultad: 'facil', etiquetas: ['reglamento'], tipo: 'verdadero-falso' }),
  ],
};

// Genera N preguntas a partir de la competencia. Mock asíncrono (1.5s) para
// simular llamada a un LLM. En operaciones esto invocaría OpenAI / Claude / Gemini.
const generarPreguntasIA = (cmp, n = 5) => {
  return new Promise((resolve) => {
    setTimeout(() => {
      const templates = _IA_TEMPLATES[cmp.cat] || _IA_TEMPLATES.sst;
      const seleccionadas = templates.slice(0, n);
      const preguntas = seleccionadas.map((tpl, i) => {
        const base = tpl(cmp);
        return {
          id: `BP-IA-${Date.now()}-${i}`,
          competenciaId: cmp.id,
          tipo: base.tipo || 'multiple',
          enunciado: base.enunciado,
          opciones: base.opciones,
          respuestaCorrecta: base.respuestaCorrecta,
          dificultad: base.dificultad,
          etiquetas: base.etiquetas,
          _generadaPorIA: true,
        };
      });
      resolve(preguntas);
    }, 1500);
  });
};

// ═══════════════════════════════════════════════════════════
// CompetenciaEditorModal · CRUD competencia + banco de preguntas
// ═══════════════════════════════════════════════════════════

const EVIDENCE_OPTIONS = [
  { id: 'lista_asistencia_firmada', label: 'Lista de asistencia firmada' },
  { id: 'acta_instructor',          label: 'Acta del instructor' },
  { id: 'foto_aula',                label: 'Foto del aula / sesión' },
  { id: 'evaluacion_calificada',    label: 'Evaluación calificada' },
  { id: 'material_entregado',       label: 'Material entregado' },
  { id: 'certificado_externo',      label: 'Certificado emitido por proveedor' },
  { id: 'reporte_plataforma',       label: 'Reporte plataforma e-learning' },
];

const CompetenciaEditorModal = ({ competencia, preguntas, onClose, onSave, onSavePregunta, onDeletePregunta }) => {
  const isNew = !competencia;
  const [tab, setTab] = _ic_useState('datos');
  const [form, setForm] = _ic_useState(() => competencia ? { ...competencia } : {
    id: '',
    name: '',
    cat: 'sst',
    criticality: 'media',
    recur: 'Anual',
    description: '',
    rolesCount: 0,
    coursesIds: [],
    norma: '',
    evaluacionRequerida: true,
    evidenciasMinimas: ['lista_asistencia_firmada', 'acta_instructor'],
  });
  const [editingPregunta, setEditingPregunta] = _ic_useState(null); // null | 'nueva' | pregunta | 'ia'

  React.useEffect(() => {
    document.body.style.overflow = 'hidden';
    const onEsc = (e) => { if (e.key === 'Escape' && !editingPregunta) onClose(); };
    window.addEventListener('keydown', onEsc);
    return () => { document.body.style.overflow = ''; window.removeEventListener('keydown', onEsc); };
  }, [editingPregunta]);

  const upd = (k, v) => setForm({ ...form, [k]: v });
  const toggleEvidencia = (id) => {
    const has = form.evidenciasMinimas.includes(id);
    upd('evidenciasMinimas', has ? form.evidenciasMinimas.filter(x => x !== id) : [...form.evidenciasMinimas, id]);
  };

  const canSave = !!(form.name && form.cat);

  const tabs = [
    { k: 'datos',     l: 'Datos básicos',           ico: 'IconBook' },
    { k: 'preguntas', l: `Banco de preguntas · ${preguntas.length}`, ico: 'IconStar' },
    // Tab "Evidencias requeridas" oculto temporalmente · se reactivará en una iteración futura
  ];

  return (
    <div className="modal-overlay" onMouseDown={e => { if (e.target === e.currentTarget) onClose(); }}>
      <div className="modal" style={{ maxWidth: 880, height: 'calc(100vh - 64px)' }}>
        <div className="modal-head">
          <div>
            <div className="muted mono" style={{ fontSize: 11 }}>{isNew ? 'Nueva competencia' : form.id}</div>
            <div className="modal-title">{isNew ? 'Crear competencia maestra' : `Editar · ${competencia.name}`}</div>
            <div className="modal-sub">Las competencias son la base auditable. El banco de preguntas asociado se usa al cierre de cada sesión para evaluar al colaborador.</div>
          </div>
          <button className="icon-btn" onClick={onClose}><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><path d="M18 6 6 18M6 6l12 12" /></svg></button>
        </div>

        <div className="doc-tabs" style={{ margin: '0 24px', marginBottom: 0, borderBottom: '1px solid var(--border)' }}>
          {tabs.map(t => {
            const Ico = window[t.ico] || IconBook;
            return (
              <button key={t.k} className={`doc-tab ${tab === t.k ? 'active' : ''}`} onClick={() => setTab(t.k)}>
                <Ico size={13} /> {t.l}
              </button>
            );
          })}
        </div>

        <div className="modal-body" style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>

          {/* Tab Datos */}
          {tab === 'datos' && (
            <div className="ic-cap-section">
              <div className="ic-cap-setup-grid">
                <div className="ic-cap-field" style={{ gridColumn: '1 / -1' }}>
                  <div className="muted-xs">Nombre de la competencia *</div>
                  <input className="ic-cap-version" type="text" value={form.name} onChange={e => upd('name', e.target.value)} placeholder="Manejo seguro de maquinaria de clasificación" />
                </div>
                <div className="ic-cap-field">
                  <div className="muted-xs">Categoría *</div>
                  <select className="ic-cap-version" value={form.cat} onChange={e => upd('cat', e.target.value)}>
                    {Object.entries(CMP_CAT).map(([k, v]) => <option key={k} value={k}>{v.label}</option>)}
                  </select>
                </div>
                <div className="ic-cap-field">
                  <div className="muted-xs">Criticidad *</div>
                  <select className="ic-cap-version" value={form.criticality} onChange={e => upd('criticality', e.target.value)}>
                    <option value="alta">Alta</option>
                    <option value="media">Media</option>
                    <option value="baja">Baja</option>
                  </select>
                </div>
                <div className="ic-cap-field">
                  <div className="muted-xs">Recurrencia</div>
                  <select className="ic-cap-version" value={form.recur} onChange={e => upd('recur', e.target.value)}>
                    <option value="Una vez · ingreso">Una vez · ingreso</option>
                    <option value="Una vez">Una vez</option>
                    <option value="Anual">Anual</option>
                    <option value="Bienal">Bienal</option>
                    <option value="Trianual">Trianual</option>
                    <option value="Por cambio">Por cambio normativo</option>
                  </select>
                </div>
                <div className="ic-cap-field">
                  <div className="muted-xs">Norma origen</div>
                  <input className="ic-cap-version" type="text" value={form.norma} onChange={e => upd('norma', e.target.value)} placeholder="Resolución 0312 de 2019 · SG-SST (opcional)" />
                </div>
                <div className="ic-cap-field" style={{ gridColumn: '1 / -1' }}>
                  <div className="muted-xs">Descripción</div>
                  <textarea className="ic-cap-version" rows={3} value={form.description} onChange={e => upd('description', e.target.value)} placeholder="Operación segura de máquinas trituradoras: identificación de peligros, procedimiento LOTO, EPP térmico…" />
                </div>
                <div className="ic-cap-field" style={{ gridColumn: '1 / -1' }}>
                  <label style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 13 }}>
                    <input type="checkbox" checked={form.evaluacionRequerida} onChange={e => upd('evaluacionRequerida', e.target.checked)} />
                    Requiere evaluación al cierre de cada sesión
                  </label>
                </div>
              </div>
            </div>
          )}

          {/* Tab Banco de preguntas */}
          {tab === 'preguntas' && (
            <div className="ic-cap-section">
              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: 10 }}>
                <div>
                  <h3 className="ic-cap-h3" style={{ margin: 0 }}>Banco de preguntas · {preguntas.length}</h3>
                  <p className="muted" style={{ fontSize: 13, marginTop: 4 }}>
                    Estas preguntas alimentan la evaluación que aplica al cierre de las sesiones de los cursos vinculados.
                  </p>
                </div>
                <div style={{ display: 'flex', gap: 6 }}>
                  <button className="btn" onClick={() => setEditingPregunta('nueva')}>
                    <IconPlus size={12} /> Manual
                  </button>
                  <button className="btn primary" onClick={() => setEditingPregunta('ia')}>
                    <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><path d="M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83" /></svg>
                    IA
                  </button>
                </div>
              </div>

              {preguntas.length === 0 && !isNew && (
                <div className="muted" style={{ fontSize: 12.5, padding: 24, textAlign: 'center', background: 'var(--surface)', borderRadius: 10 }}>
                  Aún no hay preguntas en el banco para esta competencia.<br/>
                  Crea una <b>manual</b> o pulsa <b>Generar con IA</b> para producir 5 preguntas a partir de su descripción y norma.
                </div>
              )}

              <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
                {preguntas.map((p, i) => (
                  <div key={p.id} className="bp-card">
                    <div style={{ display: 'flex', gap: 8, alignItems: 'flex-start' }}>
                      <span className="mono muted" style={{ fontSize: 10.5, marginTop: 3, minWidth: 28 }}>#{String(i + 1).padStart(2, '0')}</span>
                      <div style={{ flex: 1 }}>
                        <div style={{ display: 'flex', gap: 4, flexWrap: 'wrap', marginBottom: 4 }}>
                          <span className={`pill ${p.dificultad === 'facil' ? 'green' : p.dificultad === 'media' ? 'amber-soft' : 'rose'}`} style={{ fontSize: 10 }}>{p.dificultad}</span>
                          <span className="pill navy-soft" style={{ fontSize: 10 }}>{p.tipo}</span>
                          {p._generadaPorIA && <span className="pill" style={{ fontSize: 10, background: 'linear-gradient(90deg, #C5E7E1, #fce7f3)', color: 'var(--navy-800)' }}>✨ IA</span>}
                        </div>
                        <div style={{ fontWeight: 600, fontSize: 13, lineHeight: 1.4 }}>{p.enunciado}</div>
                        <div style={{ marginTop: 6, fontSize: 11.5 }}>
                          {p.opciones.map((o, oi) => (
                            <div key={oi} style={{ color: oi === p.respuestaCorrecta ? 'var(--green-600)' : 'var(--text-muted)', display: 'flex', gap: 4 }}>
                              <span>{String.fromCharCode(65 + oi)})</span>
                              <span>{o}</span>
                              {oi === p.respuestaCorrecta && <IconCheck size={11} />}
                            </div>
                          ))}
                        </div>
                      </div>
                      <div style={{ display: 'flex', gap: 4 }}>
                        <button className="icon-btn" title="Editar" onClick={() => setEditingPregunta(p)}>
                          <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><path d="M12 20h9M16.5 3.5a2.121 2.121 0 1 1 3 3L7 19l-4 1 1-4 12.5-12.5z" /></svg>
                        </button>
                        <button className="icon-btn" title="Eliminar" onClick={() => { if (confirm('¿Eliminar pregunta?')) onDeletePregunta(p.id); }}>
                          <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><path d="M3 6h18M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2m3 0v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6" /></svg>
                        </button>
                      </div>
                    </div>
                  </div>
                ))}
              </div>
            </div>
          )}

          {/* Tab Evidencias requeridas */}
          {tab === 'evidencias' && (
            <div className="ic-cap-section">
              <h3 className="ic-cap-h3">Evidencias requeridas para validar la sesión</h3>
              <p className="muted" style={{ fontSize: 13, marginTop: -4 }}>
                Tipos de evidencia que debe cargar el instructor para que el cierre de la sesión sea auditable. Selecciona todas las que apliquen.
              </p>
              <div style={{ display: 'flex', flexDirection: 'column', gap: 6, marginTop: 12 }}>
                {EVIDENCE_OPTIONS.map(ev => {
                  const sel = form.evidenciasMinimas.includes(ev.id);
                  return (
                    <label key={ev.id} style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '10px 12px', background: sel ? '#F0FDF4' : 'var(--surface)', border: '1px solid var(--border)', borderRadius: 8, cursor: 'pointer' }}>
                      <input type="checkbox" checked={sel} onChange={() => toggleEvidencia(ev.id)} />
                      <span style={{ fontSize: 13, fontWeight: 500 }}>{ev.label}</span>
                      <span className="mono muted" style={{ fontSize: 10.5, marginLeft: 'auto' }}>{ev.id}</span>
                    </label>
                  );
                })}
              </div>
            </div>
          )}
        </div>

        <div className="modal-foot">
          <span className="muted" style={{ fontSize: 11.5, alignSelf: 'center' }}>{preguntas.length} preguntas en banco · {form.evidenciasMinimas.length} evidencias requeridas</span>
          <div style={{ flex: 1 }} />
          <button className="btn" onClick={onClose}>Cancelar</button>
          <button className="btn primary" disabled={!canSave} onClick={() => onSave(form)}>
            <IconCheck size={13} /> {isNew ? 'Crear competencia' : 'Guardar cambios'}
          </button>
        </div>
      </div>

      {editingPregunta && (
        <PreguntaEditorModal
          mode={editingPregunta === 'nueva' ? 'manual' : editingPregunta === 'ia' ? 'ia' : 'edit'}
          pregunta={editingPregunta && editingPregunta !== 'nueva' && editingPregunta !== 'ia' ? editingPregunta : null}
          competencia={form}
          onClose={() => setEditingPregunta(null)}
          onSave={(data) => {
            if (Array.isArray(data)) {
              data.forEach(p => onSavePregunta(p));
            } else {
              onSavePregunta(data);
            }
            setEditingPregunta(null);
          }}
        />
      )}
    </div>
  );
};

// ═══════════════════════════════════════════════════════════
// PreguntaEditorModal · manual o generar con IA
// ═══════════════════════════════════════════════════════════

const PreguntaEditorModal = ({ mode, pregunta, competencia, onClose, onSave }) => {
  const isIA = mode === 'ia';
  const isEdit = mode === 'edit';
  const [form, setForm] = _ic_useState(() => pregunta ? { ...pregunta } : {
    competenciaId: competencia?.id || '',
    tipo: 'multiple',
    enunciado: '',
    opciones: ['', '', '', ''],
    respuestaCorrecta: 0,
    dificultad: 'media',
    etiquetas: [],
  });

  // Estado IA · contexto = nombre de competencia + descripción
  const [iaPrompt, setIaPrompt] = _ic_useState(() => competencia
    ? `Genera 5 preguntas de evaluación sobre "${competencia.name}".${competencia.description ? ` Contexto: ${competencia.description}` : ''} Mezcla dificultades (fácil, media, difícil) y formatos (opción múltiple, V/F).`
    : ''
  );
  const [iaCantidad, setIaCantidad] = _ic_useState(5);
  const [iaLoading, setIaLoading] = _ic_useState(false);
  const [iaResult, setIaResult] = _ic_useState(null); // array de preguntas generadas
  const [iaSelected, setIaSelected] = _ic_useState({}); // { id: true } para aceptar

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

  const upd = (k, v) => setForm({ ...form, [k]: v });
  const updOpt = (i, v) => upd('opciones', form.opciones.map((x, idx) => idx === i ? v : x));

  const generar = async () => {
    setIaLoading(true);
    const result = await generarPreguntasIA(competencia, iaCantidad);
    setIaResult(result);
    setIaSelected(Object.fromEntries(result.map(p => [p.id, true])));
    setIaLoading(false);
  };

  const aceptarSeleccionadas = () => {
    if (!iaResult) return;
    const seleccionadas = iaResult.filter(p => iaSelected[p.id]);
    onSave(seleccionadas);
  };

  const canSaveManual = !!(form.enunciado && form.opciones.filter(o => o.trim()).length >= 2);

  return (
    <div className="modal-overlay" onMouseDown={e => { if (e.target === e.currentTarget) onClose(); }} style={{ zIndex: 200 }}>
      <div className="modal" style={{ maxWidth: 720 }}>
        <div className="modal-head">
          <div>
            <div className="muted mono" style={{ fontSize: 11 }}>{competencia?.id} · {competencia?.name}</div>
            <div className="modal-title">
              {isIA ? '✨ Generar preguntas con IA' : isEdit ? `Editar · ${(form.enunciado || '').slice(0, 40)}…` : 'Nueva pregunta · manual'}
            </div>
            <div className="modal-sub">
              {isIA
                ? 'La IA leerá el nombre, descripción y norma de la competencia para producir preguntas. Revisa y acepta las que quieras agregar al banco.'
                : 'Crea una pregunta de evaluación con sus opciones y la respuesta correcta.'}
            </div>
          </div>
          <button className="icon-btn" onClick={onClose}><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><path d="M18 6 6 18M6 6l12 12" /></svg></button>
        </div>

        <div className="modal-body" style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>

          {/* Modo IA */}
          {isIA && (
            <>
              <div style={{ padding: 12, background: 'linear-gradient(135deg, #E9F5F2, #fef3ff)', border: '1px solid var(--navy-100)', borderRadius: 10 }}>
                <div className="muted-xs" style={{ marginBottom: 6 }}>Contexto que recibirá el modelo</div>
                <div style={{ fontSize: 12.5, lineHeight: 1.5 }}>
                  <div><b>Competencia:</b> {competencia?.name || <span className="muted">(sin nombre)</span>}</div>
                  <div><b>Descripción:</b> {competencia?.description || <span className="muted">(sin descripción · agrega una para mejores preguntas)</span>}</div>
                </div>
              </div>

              <div className="ic-cap-field">
                <div className="muted-xs">Instrucción para la IA (puedes editarla)</div>
                <textarea className="ic-cap-version" rows={3} value={iaPrompt} onChange={e => setIaPrompt(e.target.value)} />
              </div>

              <div style={{ display: 'flex', gap: 10, alignItems: 'center' }}>
                <div className="ic-cap-field" style={{ flex: '0 0 auto' }}>
                  <div className="muted-xs">Cantidad</div>
                  <input className="ic-cap-version" type="number" min={1} max={10} value={iaCantidad} onChange={e => setIaCantidad(Math.max(1, Math.min(10, parseInt(e.target.value) || 1)))} style={{ width: 80 }} />
                </div>
                <div style={{ flex: 1 }} />
                {!iaResult && (
                  <button className="btn primary" onClick={generar} disabled={iaLoading} style={{ alignSelf: 'flex-end' }}>
                    {iaLoading
                      ? <><span className="sig-spinner" /> Generando…</>
                      : <>✨ Generar {iaCantidad} preguntas</>}
                  </button>
                )}
                {iaResult && (
                  <button className="btn" onClick={() => { setIaResult(null); setIaSelected({}); }} style={{ alignSelf: 'flex-end' }}>
                    Volver a generar
                  </button>
                )}
              </div>

              {iaResult && (
                <>
                  <div className="muted-xs" style={{ marginTop: 4 }}>
                    Resultado · {Object.values(iaSelected).filter(Boolean).length} de {iaResult.length} seleccionadas
                  </div>
                  <div style={{ display: 'flex', flexDirection: 'column', gap: 6, maxHeight: 360, overflow: 'auto' }}>
                    {iaResult.map(p => {
                      const sel = !!iaSelected[p.id];
                      return (
                        <label key={p.id} className={`bp-card ${sel ? '' : 'rejected'}`} style={{ cursor: 'pointer', opacity: sel ? 1 : 0.55 }}>
                          <div style={{ display: 'flex', gap: 10, alignItems: 'flex-start' }}>
                            <input type="checkbox" checked={sel} onChange={() => setIaSelected({ ...iaSelected, [p.id]: !sel })} style={{ marginTop: 4 }} />
                            <div style={{ flex: 1 }}>
                              <div style={{ display: 'flex', gap: 4, flexWrap: 'wrap', marginBottom: 4 }}>
                                <span className={`pill ${p.dificultad === 'facil' ? 'green' : p.dificultad === 'media' ? 'amber-soft' : 'rose'}`} style={{ fontSize: 10 }}>{p.dificultad}</span>
                                <span className="pill navy-soft" style={{ fontSize: 10 }}>{p.tipo}</span>
                                <span className="pill" style={{ fontSize: 10, background: 'linear-gradient(90deg, #C5E7E1, #fce7f3)', color: 'var(--navy-800)' }}>✨ IA</span>
                              </div>
                              <div style={{ fontWeight: 600, fontSize: 13 }}>{p.enunciado}</div>
                              <div style={{ marginTop: 6, fontSize: 11.5 }}>
                                {p.opciones.map((o, oi) => (
                                  <div key={oi} style={{ color: oi === p.respuestaCorrecta ? 'var(--green-600)' : 'var(--text-muted)', fontWeight: oi === p.respuestaCorrecta ? 600 : 400 }}>
                                    {String.fromCharCode(65 + oi)}) {o} {oi === p.respuestaCorrecta && '✓'}
                                  </div>
                                ))}
                              </div>
                            </div>
                          </div>
                        </label>
                      );
                    })}
                  </div>
                </>
              )}
            </>
          )}

          {/* Modo Manual */}
          {!isIA && (
            <>
              <div className="ic-cap-field">
                <div className="muted-xs">Tipo de pregunta</div>
                <div style={{ display: 'flex', gap: 6, marginTop: 4 }}>
                  {[['multiple','Opción múltiple'],['verdadero-falso','Verdadero/Falso']].map(([k, l]) => (
                    <button key={k} type="button"
                      className={`pill ${form.tipo === k ? 'navy-soft' : ''}`}
                      style={{ cursor: 'pointer', border: form.tipo === k ? '1px solid var(--navy-800)' : '1px solid var(--border)', fontSize: 11.5, padding: '5px 10px' }}
                      onClick={() => {
                        upd('tipo', k);
                        if (k === 'verdadero-falso') upd('opciones', ['Verdadero', 'Falso']);
                        else if (form.opciones.length < 4) upd('opciones', ['', '', '', '']);
                      }}>{l}</button>
                  ))}
                </div>
              </div>

              <div className="ic-cap-field">
                <div className="muted-xs">Enunciado *</div>
                <textarea className="ic-cap-version" rows={2} value={form.enunciado} onChange={e => upd('enunciado', e.target.value)} placeholder="¿Cuál es el primer paso del procedimiento LOTO?" />
              </div>

              <div className="ic-cap-field">
                <div className="muted-xs">Opciones · marca la correcta</div>
                <div style={{ display: 'flex', flexDirection: 'column', gap: 6, marginTop: 4 }}>
                  {form.opciones.map((opt, i) => (
                    <div key={i} style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                      <input type="radio" name="correct" checked={form.respuestaCorrecta === i} onChange={() => upd('respuestaCorrecta', i)} />
                      <span className="mono muted" style={{ fontSize: 11, minWidth: 18 }}>{String.fromCharCode(65 + i)}</span>
                      <input className="ic-cap-version" style={{ flex: 1, marginTop: 0 }} type="text" value={opt} onChange={e => updOpt(i, e.target.value)} placeholder={`Opción ${String.fromCharCode(65 + i)}`} />
                      {form.tipo === 'multiple' && form.opciones.length > 2 && (
                        <button className="icon-btn" onClick={() => upd('opciones', form.opciones.filter((_, idx) => idx !== i))}>
                          <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><path d="M18 6 6 18M6 6l12 12" /></svg>
                        </button>
                      )}
                    </div>
                  ))}
                  {form.tipo === 'multiple' && form.opciones.length < 6 && (
                    <button className="btn" style={{ alignSelf: 'flex-start', fontSize: 11 }} onClick={() => upd('opciones', [...form.opciones, ''])}>
                      <IconPlus size={11} /> Agregar opción
                    </button>
                  )}
                </div>
              </div>

              <div className="ic-cap-setup-grid">
                <div className="ic-cap-field">
                  <div className="muted-xs">Dificultad</div>
                  <select className="ic-cap-version" value={form.dificultad} onChange={e => upd('dificultad', e.target.value)}>
                    <option value="facil">Fácil</option>
                    <option value="media">Media</option>
                    <option value="dificil">Difícil</option>
                  </select>
                </div>
                <div className="ic-cap-field">
                  <div className="muted-xs">Etiquetas (separadas por coma)</div>
                  <input className="ic-cap-version" type="text" value={(form.etiquetas || []).join(', ')} onChange={e => upd('etiquetas', e.target.value.split(',').map(x => x.trim()).filter(Boolean))} placeholder="LOTO, seguridad" />
                </div>
              </div>
            </>
          )}
        </div>

        <div className="modal-foot">
          <div style={{ flex: 1 }} />
          <button className="btn" onClick={onClose}>Cancelar</button>
          {isIA && iaResult ? (
            <button className="btn primary" onClick={aceptarSeleccionadas} disabled={Object.values(iaSelected).filter(Boolean).length === 0}>
              <IconCheck size={13} /> Agregar {Object.values(iaSelected).filter(Boolean).length} al banco
            </button>
          ) : !isIA && (
            <button className="btn primary" onClick={() => onSave(form)} disabled={!canSaveManual}>
              <IconCheck size={13} /> {isEdit ? 'Guardar cambios' : 'Agregar al banco'}
            </button>
          )}
        </div>
      </div>
    </div>
  );
};

// ═══════════════════════════════════════════════════════════
// Tab: Matriz Cargo × Competencia · editable
// ═══════════════════════════════════════════════════════════

const MATRIZ_STATE = {
  obligatoria: { label: 'Obligatoria', symbol: '●', tone: 'rose' },
  opcional:    { label: 'Opcional',    symbol: '○', tone: 'amber-soft' },
  no_aplica:   { label: 'No aplica',   symbol: '—', tone: 'navy-soft' },
};

const MatrizTab = () => {
  // Estado local hidratado desde window.MATRIZ_CARGO_COMP. Mutable durante la sesión.
  // Convertimos el array a un map { 'CGO-001|CMP-01': { obligatoria, plazoDias } } para edición rápida.
  const [matriz, setMatriz] = _ic_useState(() => {
    const m = {};
    (window.MATRIZ_CARGO_COMP || []).forEach(t => {
      m[`${t.cargoId}|${t.competenciaId}`] = { obligatoria: t.obligatoria, plazoDias: t.plazoDias };
    });
    return m;
  });
  const [filterArea, setFilterArea] = _ic_useState('todas');
  const [filterCmp,  setFilterCmp]  = _ic_useState('todas');

  // Mantener window.MATRIZ_CARGO_COMP sincronizado para que el motor lo consuma
  React.useEffect(() => {
    window.MATRIZ_CARGO_COMP = Object.entries(matriz).map(([k, v]) => {
      const [cargoId, competenciaId] = k.split('|');
      return { cargoId, competenciaId, obligatoria: v.obligatoria, plazoDias: v.plazoDias };
    });
  }, [matriz]);

  const cargos = (window.CARGOS || []);
  const cmps   = (window.COMPETENCIAS_MAESTRAS || []);

  const cargosFiltrados = filterArea === 'todas' ? cargos : cargos.filter(c => c.area === filterArea);
  const cmpsFiltrados   = filterCmp === 'todas' ? cmps   : cmps.filter(c => c.cat === filterCmp);

  const stateOf = (cargoId, cmpId) => {
    const key = `${cargoId}|${cmpId}`;
    if (!matriz[key]) return 'no_aplica';
    return matriz[key].obligatoria ? 'obligatoria' : 'opcional';
  };

  const cycleState = (cargoId, cmpId) => {
    const key = `${cargoId}|${cmpId}`;
    const curr = stateOf(cargoId, cmpId);
    const next = curr === 'no_aplica' ? 'obligatoria' : curr === 'obligatoria' ? 'opcional' : 'no_aplica';
    setMatriz(prev => {
      const copy = { ...prev };
      if (next === 'no_aplica') {
        delete copy[key];
      } else {
        const cmp = cmps.find(c => c.id === cmpId);
        const defaultPlazo = cmp?.recur === 'Anual' ? 365 : cmp?.recur === 'Trianual' ? 1095 : 90;
        copy[key] = {
          obligatoria: next === 'obligatoria',
          plazoDias: prev[key]?.plazoDias || (next === 'obligatoria' ? 30 : 180),
        };
      }
      return copy;
    });
  };

  const setPlazo = (cargoId, cmpId, dias) => {
    const key = `${cargoId}|${cmpId}`;
    if (!matriz[key]) return;
    setMatriz(prev => ({ ...prev, [key]: { ...prev[key], plazoDias: Math.max(0, parseInt(dias) || 0) } }));
  };

  // KPIs
  const totalAsignaciones = Object.values(matriz).length;
  const totalObligatorias = Object.values(matriz).filter(v => v.obligatoria).length;
  const cargosCubiertos = new Set(Object.keys(matriz).map(k => k.split('|')[0])).size;
  const promCompCargo = cargosCubiertos ? (totalObligatorias / cargosCubiertos).toFixed(1) : 0;

  // Áreas y categorías para filtros
  const areas = Array.from(new Set(cargos.map(c => c.area)));
  const cats  = Array.from(new Set(cmps.map(c => c.cat)));

  return (
    <>
      <div className="ic-cmp-kpis">
        <div className="ic-cmp-kpi">
          <div className="muted-xs">Asignaciones definidas</div>
          <div className="ic-cmp-kpi-num">{totalAsignaciones}</div>
          <div className="muted" style={{ fontSize: 11 }}>Tuplas Cargo × Competencia</div>
        </div>
        <div className="ic-cmp-kpi">
          <div className="muted-xs">Obligatorias</div>
          <div className="ic-cmp-kpi-num" style={{ color: 'var(--rose-600)' }}>{totalObligatorias}</div>
          <div className="muted" style={{ fontSize: 11 }}>Asignación automática al ingreso</div>
        </div>
        <div className="ic-cmp-kpi">
          <div className="muted-xs">Cargos con matriz</div>
          <div className="ic-cmp-kpi-num">{cargosCubiertos} / {cargos.length}</div>
          <div className="muted" style={{ fontSize: 11 }}>Sin matriz no se generan asignaciones</div>
        </div>
        <div className="ic-cmp-kpi">
          <div className="muted-xs">Promedio competencias / cargo</div>
          <div className="ic-cmp-kpi-num">{promCompCargo}</div>
          <div className="muted" style={{ fontSize: 11 }}>Carga formativa media</div>
        </div>
      </div>

      <div className="ob-toolbar" style={{ marginBottom: 14 }}>
        <select value={filterArea} onChange={e => setFilterArea(e.target.value)}>
          <option value="todas">Todas las áreas</option>
          {areas.map(a => <option key={a}>{a}</option>)}
        </select>
        <select value={filterCmp} onChange={e => setFilterCmp(e.target.value)}>
          <option value="todas">Todas las competencias</option>
          {cats.map(c => <option key={c} value={c}>{CMP_CAT[c]?.label || c}</option>)}
        </select>
        <span className="muted" style={{ fontSize: 11.5, alignSelf: 'center' }}>
          Click en una celda para alternar: ● obligatoria · ○ opcional · — no aplica. Edita el plazo (días) en el input.
        </span>
        <div style={{ flex: 1 }} />
        <span className="muted mono" style={{ fontSize: 11, alignSelf: 'center' }}>
          {cargosFiltrados.length} × {cmpsFiltrados.length} celdas
        </span>
      </div>

      {/* Matriz */}
      <div className="ic-mtx-wrap">
        <table className="ic-mtx-table">
          <thead>
            <tr>
              <th className="ic-mtx-corner">Cargo · Competencia →</th>
              {cmpsFiltrados.map(c => (
                <th key={c.id} className="ic-mtx-cmp-head" title={c.name}>
                  <div className="mono" style={{ fontSize: 10.5 }}>{c.id}</div>
                  <div style={{ fontSize: 10, fontWeight: 500 }}>{CMP_CAT[c.cat]?.label || c.cat}</div>
                </th>
              ))}
            </tr>
          </thead>
          <tbody>
            {cargosFiltrados.map(cg => (
              <tr key={cg.id}>
                <td className="ic-mtx-cargo">
                  <div style={{ fontWeight: 600, fontSize: 12.5 }}>{cg.nombre}</div>
                  <div className="muted" style={{ fontSize: 11 }}>{cg.area} · {cg.headcount} colaboradores</div>
                </td>
                {cmpsFiltrados.map(cmp => {
                  const st = stateOf(cg.id, cmp.id);
                  const meta = MATRIZ_STATE[st];
                  const cell = matriz[`${cg.id}|${cmp.id}`];
                  return (
                    <td key={cmp.id} className={`ic-mtx-cell ic-mtx-${st}`}>
                      <button
                        className="ic-mtx-symbol"
                        onClick={() => cycleState(cg.id, cmp.id)}
                        title={`${meta.label}\n${cmp.name}\nClick para alternar`}
                      >
                        {meta.symbol}
                      </button>
                      {cell && (
                        <input
                          type="number"
                          className="ic-mtx-plazo"
                          value={cell.plazoDias}
                          onChange={e => setPlazo(cg.id, cmp.id, e.target.value)}
                          title="Plazo en días para completar"
                          min={0}
                        />
                      )}
                    </td>
                  );
                })}
              </tr>
            ))}
          </tbody>
        </table>
      </div>

      <div className="ic-cap-info" style={{ marginTop: 14 }}>
        <IconShield size={13} />
        <span>
          Esta matriz es la <b>fuente de verdad</b> que consume el motor de automatización (ver módulo <b>Automatización</b>).
          Al crear un empleado o cambiarle el cargo, se generan automáticamente asignaciones según las celdas marcadas.
          Los cambios persisten durante la sesión.
        </span>
      </div>
    </>
  );
};

// ═══════════════════════════════════════════════════════════
// Tab: Sesiones — programadas + capturables + cerradas
// ═══════════════════════════════════════════════════════════

// Simulador de webhook (Fase E.3) · genera certificados externos
// y marca la sesión como cerrada. Demuestra el flujo de sincronización
// proveedor → Atica sin construir backend real.
const simularWebhookProveedor = (sesion, curso) => {
  const cmp = (window.COMPETENCIAS_MAESTRAS || []).find(c => c.id === sesion.competenciaId);
  const proveedorRecord = (window.PROVEEDORES || []).find(p => p.razonSocial === sesion.instructorOrg);
  const issuedAt = new Date().toLocaleString('es-CO');
  const empresa = { name: 'Atica S.A.S.', nit: '900.123.456-7', logo: 'a' };
  const plataforma = { name: 'Atica SOMOS', logo: 'a' };

  (sesion.enrolled || []).forEach((emp, idx) => {
    const score = 75 + Math.floor(Math.random() * 20); // mock score realista
    const code = `CERT-WHK-${(curso.plataforma || 'EXT').toUpperCase().slice(0, 4)}-${sesion.id.slice(-4)}-${String(idx + 1).padStart(2, '0')}`;
    const vigDate = new Date(); vigDate.setFullYear(vigDate.getFullYear() + 1);
    const vigTo = vigDate.toLocaleDateString('es-CO');
    const newCert = {
      code,
      status: 'vigente',
      origen: 'externo',
      emp: { initials: emp.initials, name: emp.name, doc: `CC ${emp.empId} *** *** **`, cargo: emp.role, area: '—', site: sesion.site },
      empresa, plataforma,
      course: { name: sesion.courseName, code: sesion.courseId, duration: '—', mode: 'E-learning externo' },
      competencia: { id: sesion.competenciaId, name: cmp?.name || sesion.competenciaId, norma: cmp?.norma || '—' },
      session: { id: sesion.id, date: sesion.date, site: sesion.site, instructor: sesion.instructor, instructorOrg: sesion.instructorOrg, contentVersion: 'externo' },
      result: { score, minScore: sesion.minScore || 70, passed: score >= (sesion.minScore || 70), issuedAt },
      vigencia: { from: new Date().toLocaleDateString('es-CO'), to: vigTo, daysLeft: 365 },
      evidence: {
        attendanceSigned: false,
        attendanceMethod: `Sincronizado vía webhook · ${proveedorRecord?.razonSocial || curso.plataforma || 'plataforma del proveedor'}`,
        evaluationApplied: true,
        evaluationQuestions: null,
        evidencesCount: 1,
        evidencesList: ['Reporte de plataforma e-learning'],
        contentVersionLocked: 'externo',
      },
      hash: null,
      bitacoraEvents: [
        { when: issuedAt, text: 'Webhook recibido del proveedor · firma verificada', tone: 'green' },
        { when: issuedAt, text: `Certificado externo creado automáticamente · score ${score}/100`, tone: 'green' },
        { when: sesion.date, text: 'Curso completado en plataforma del proveedor', tone: 'navy' },
      ],
    };
    window.CERTIFICATES = [...(window.CERTIFICATES || []), newCert];
  });

  // Marcar sesión como cerrada en el store global
  window.SESSIONS_SCHED = (window.SESSIONS_SCHED || []).map(s =>
    s.id === sesion.id
      ? { ...s, state: 'cerrada', closedAt: issuedAt, closedBy: `Webhook · ${proveedorRecord?.razonSocial || curso.plataforma}`, evidencesCount: 1, enrolled: sesion.enrolled.length, passed: sesion.enrolled.length, sessionHash: null }
      : s
  );

  alert(`Webhook procesado · ${(sesion.enrolled || []).length} certificado${(sesion.enrolled || []).length === 1 ? '' : 's'} externo${(sesion.enrolled || []).length === 1 ? '' : 's'} creado${(sesion.enrolled || []).length === 1 ? '' : 's'}.\n\nLos certificados están disponibles en el verificador público y en el Modo Auditoría. La sesión quedó marcada como cerrada.`);
  // forzar re-render del padre via location.reload no es ideal; el padre escucha sessionsTick
  // por lo que tab cambia, requeriremos re-abrir sesiones. Como hack para demo: reload soft.
  setTimeout(() => window.location.reload(), 100);
};

const SesionesTab = ({ onCapture, onProgramar }) => {
  const [filter, setFilter] = _ic_useState('todas');
  const [filterPlan, setFilterPlan] = _ic_useState('todos');
  const sesiones = window.SESSIONS_SCHED || SESSIONS_SCHED;
  const planes = window.PLANES_FORMATIVOS || [];

  const counts = {
    todas: sesiones.length,
    programada: sesiones.filter(s => s.state === 'programada').length,
    cerrada: sesiones.filter(s => s.state === 'cerrada').length,
    anulada: 0,
  };
  const list = sesiones
    .filter(s => filter === 'todas' || s.state === filter)
    .filter(s => filterPlan === 'todos' || (filterPlan === 'sueltas' ? !s.planId : s.planId === filterPlan));

  return (
    <>
      <div className="sst-quickfilter">
        <button className={`sst-chip ${filter === 'todas' ? 'active' : ''}`}        onClick={() => setFilter('todas')}>Todas <span className="sst-chip-count">{counts.todas}</span></button>
        <button className={`sst-chip amber ${filter === 'programada' ? 'active' : ''}`} onClick={() => setFilter('programada')}>Programadas <span className="sst-chip-count">{counts.programada}</span></button>
        <button className={`sst-chip green ${filter === 'cerrada' ? 'active' : ''}`}    onClick={() => setFilter('cerrada')}>Cerradas <span className="sst-chip-count">{counts.cerrada}</span></button>
        <button className={`sst-chip rose ${filter === 'anulada' ? 'active' : ''}`}     onClick={() => setFilter('anulada')}>Anuladas <span className="sst-chip-count">{counts.anulada}</span></button>
        <select value={filterPlan} onChange={e => setFilterPlan(e.target.value)} style={{ marginLeft: 8 }}>
          <option value="todos">Todos los planes</option>
          <option value="sueltas">Sesiones sueltas (sin plan)</option>
          {planes.map(p => <option key={p.id} value={p.id}>{p.nombre}</option>)}
        </select>
        <div style={{ flex: 1 }} />
        <button className="btn primary" onClick={onProgramar}><IconPlus size={14} /> Programar sesión</button>
      </div>

      <div className="ic-ses-list">
        {list.map(s => {
          const cat = CMP_CAT[s.cat];
          const crit = CRITICALITY[s.criticality];
          const sst = STATE_SES[s.state];
          const plan = s.planId ? (window.PLANES_FORMATIVOS || []).find(p => p.id === s.planId) : null;
          const dias = window.sessionDayCount ? window.sessionDayCount(s) : 1;
          const dateStr = s.dateStart || s.date || '';
          return (
            <div key={s.id} className={`ic-ses-card ic-ses-${s.state}`}>
              <div className="ic-ses-head">
                <div className="ic-ses-rail">
                  <div className={`ic-ses-date ${s.state}`}>
                    <div className="ic-ses-date-d">{dateStr.split('/')[0]}</div>
                    <div className="ic-ses-date-m">{['Ene','Feb','Mar','Abr','May','Jun','Jul','Ago','Sep','Oct','Nov','Dic'][parseInt(dateStr.split('/')[1], 10) - 1]}</div>
                    <div className="ic-ses-date-y mono">{dateStr.split('/')[2]}</div>
                    {dias > 1 && <div className="mono" style={{ fontSize: 9, color: 'var(--rose-600)', fontWeight: 700, marginTop: 2 }}>{dias} días</div>}
                  </div>
                </div>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div className="ic-ses-meta-top">
                    <span className="muted mono" style={{ fontSize: 11 }}>{s.id} · {s.competenciaId}</span>
                    <span className={`tr-cat-badge ${cat.tone}`}>{cat.label}</span>
                    <span className={`pill ${crit.pill}`} style={{ fontSize: 10.5 }}><span className="status-dot" style={{ background: crit.dot }} />Crit. {crit.label}</span>
                    {plan && (
                      <span className="pill navy-soft" style={{ fontSize: 10 }} title={plan.nombre}>Plan · {plan.nombre.length > 28 ? plan.nombre.slice(0, 28) + '…' : plan.nombre}</span>
                    )}
                    {!plan && (s.planId === null || s.planId === undefined) && (
                      <span className="muted" style={{ fontSize: 10.5, fontStyle: 'italic' }}>sesión suelta</span>
                    )}
                  </div>
                  <div className="ic-ses-title">{s.courseName}</div>
                  <div className="ic-ses-grid">
                    <div><div className="muted-xs">{dias > 1 ? 'Período' : 'Horario'}</div><div className="ic-ses-val">{dias > 1 ? `${s.dateStart} → ${s.dateEnd}` : s.time}</div>{dias > 1 && <div className="muted" style={{ fontSize: 10.5 }}>{s.time} cada día</div>}</div>
                    <div><div className="muted-xs">Sede</div><div className="ic-ses-val">{s.site}</div></div>
                    <div><div className="muted-xs">Instructor</div><div className="ic-ses-val">{s.instructor}<div className="muted" style={{ fontSize: 10.5 }}>{s.instructorOrg}</div></div></div>
                    <div><div className="muted-xs">Versión aplicada</div><div className="ic-ses-val mono">{s.contentVersion}</div></div>
                  </div>
                </div>
                <div className="ic-ses-side">
                  <span className={`pill ${sst.pill}`}><span className="status-dot" style={{ background: sst.dot }} /> {sst.label}</span>
                  {s.state === 'cerrada' && (
                    <>
                      <div className="ic-ses-passed mono">
                        <b>{s.passed}</b><span className="muted">/{s.enrolled} aprobaron</span>
                      </div>
                      <div className="muted mono" style={{ fontSize: 10.5 }}>hash {s.sessionHash.slice(0, 10)}…</div>
                    </>
                  )}
                  {s.state === 'programada' && (
                    <div className="muted mono" style={{ fontSize: 11 }}>{s.enrolled.length} inscritos</div>
                  )}
                </div>
              </div>

              {s.state === 'programada' && (() => {
                const cursoCat = (window.COURSES || []).find(c => c.id === s.courseId);
                const esWebhookMock = cursoCat && cursoCat.mode === 'elearning-externo' && cursoCat.metodoSync === 'webhook-mock';
                return (
                  <div className="ic-ses-foot">
                    <div className="ic-ses-attendees">
                      {s.enrolled.slice(0, 5).map((e, i) => (
                        <span key={i} className="ic-ses-att" title={e.name}>{e.initials}</span>
                      ))}
                      {s.enrolled.length > 5 && <span className="ic-ses-att more">+{s.enrolled.length - 5}</span>}
                    </div>
                    <div style={{ flex: 1 }} />
                    <button className="btn"><IconExternal size={13} /> Ver detalle</button>
                    {esWebhookMock ? (
                      <button className="btn primary" onClick={() => simularWebhookProveedor(s, cursoCat)}>
                        <IconExternal size={13} /> Simular webhook proveedor
                      </button>
                    ) : (
                      <button className="btn primary" onClick={() => onCapture(s)}>
                        <IconRocket size={13} /> Iniciar captura
                      </button>
                    )}
                  </div>
                );
              })()}
              {s.state === 'cerrada' && (
                <div className="ic-ses-foot ic-ses-foot-closed">
                  <div className="ic-ses-evidence-strip">
                    <span className="ic-ses-evidence-tag"><IconCheck size={11} /> Asistencia firmada</span>
                    <span className="ic-ses-evidence-tag"><IconCheck size={11} /> Evaluación aplicada</span>
                    <span className="ic-ses-evidence-tag"><IconCheck size={11} /> {s.evidencesCount} evidencias</span>
                    <span className="ic-ses-evidence-tag"><IconCheck size={11} /> Certificados emitidos</span>
                  </div>
                  <div style={{ flex: 1 }} />
                  <span className="muted" style={{ fontSize: 11 }}>Cerrada {s.closedAt} · {s.closedBy}</span>
                  <button className="btn"><IconExternal size={13} /> Ver expediente</button>
                </div>
              )}
            </div>
          );
        })}
      </div>
    </>
  );
};

// ═══════════════════════════════════════════════════════════
// Tab: Proveedores · idoneidad de terceros que imparten capacitaciones
// ═══════════════════════════════════════════════════════════

const PROV_TIPO_META = {
  ARL:         { label: 'ARL',           tone: 'rose'  },
  SENA:        { label: 'SENA',          tone: 'green' },
  consultor:   { label: 'Consultor',     tone: 'navy'  },
  universidad: { label: 'Universidad',   tone: 'blue'  },
  elearning:   { label: 'E-learning',    tone: 'amber' },
  interno:     { label: 'Interno',       tone: 'navy-soft' },
};

// Estado de un soporte/licencia respecto a `hoy`
const docVigencia = (vence, hoy = new Date()) => {
  if (!vence) return { state: 'sin-vencimiento', label: 'Sin vencimiento', daysLeft: null };
  const [d, m, y] = vence.split('/').map(Number);
  const fin = new Date(y, m - 1, d);
  const days = Math.round((fin - hoy) / 86400000);
  if (days < 0)  return { state: 'vencida',     label: `Vencida hace ${Math.abs(days)} días`, daysLeft: days };
  if (days < 30) return { state: 'por-vencer',  label: `Vence en ${days} días`,                daysLeft: days };
  return { state: 'vigente', label: `Vigente · ${vence}`, daysLeft: days };
};

const ProveedoresTab = () => {
  // Estado local con los datos editables — se hidrata desde window.PROVEEDORES/INSTRUCTORES
  // pero permite agregar/editar dentro de la sesión sin tocar los mocks globales.
  const [provs, setProvs] = _ic_useState(() => [...(window.PROVEEDORES || [])]);
  const [insts, setInsts] = _ic_useState(() => [...(window.INSTRUCTORES || [])]);

  const [tipo, setTipo]     = _ic_useState('todos');
  const [search, setSearch] = _ic_useState('');
  const [picked, setPicked] = _ic_useState(null);
  const [edit, setEdit]     = _ic_useState(null); // proveedor en edición o 'nuevo'

  // Mantener window.PROVEEDORES / INSTRUCTORES actualizados para que SessionCapture y otros lo vean
  React.useEffect(() => { window.PROVEEDORES = provs; }, [provs]);
  React.useEffect(() => { window.INSTRUCTORES = insts; }, [insts]);

  const filtered = provs.filter(p => {
    if (tipo !== 'todos' && p.tipo !== tipo) return false;
    if (search && !p.razonSocial.toLowerCase().includes(search.toLowerCase())) return false;
    return true;
  });

  const totalInstructores = insts.length;
  const licenciasVencidas = insts.filter(i =>
    i.licenciaSST && docVigencia(i.licenciaSST.vence).state === 'vencida'
  ).length;
  const licenciasPorVencer = insts.filter(i =>
    i.licenciaSST && docVigencia(i.licenciaSST.vence).state === 'por-vencer'
  ).length;

  const saveProvider = (data) => {
    if (data.id && provs.some(p => p.id === data.id)) {
      setProvs(provs.map(p => p.id === data.id ? { ...p, ...data } : p));
      // Si el proveedor abierto en panel cambió, actualizarlo también
      if (picked && picked.id === data.id) setPicked({ ...picked, ...data });
    } else {
      const next = { ...data, id: data.id || `PRV-${String(provs.length + 1).padStart(3, '0')}` };
      setProvs([...provs, next]);
    }
    setEdit(null);
  };

  const saveInstructor = (data) => {
    if (data.id && insts.some(i => i.id === data.id)) {
      setInsts(insts.map(i => i.id === data.id ? { ...i, ...data } : i));
    } else {
      const next = { ...data, id: data.id || `INS-${String(insts.length + 1).padStart(3, '0')}` };
      setInsts([...insts, next]);
    }
  };

  const deleteInstructor = (id) => setInsts(insts.filter(i => i.id !== id));

  return (
    <>
      {/* KPIs cabecera */}
      <div className="ic-cmp-kpis">
        <div className="ic-cmp-kpi">
          <div className="muted-xs">Proveedores activos</div>
          <div className="ic-cmp-kpi-num">{provs.length}</div>
          <div className="muted" style={{ fontSize: 11 }}>ARL, SENA, consultores, universidades, LMS</div>
        </div>
        <div className="ic-cmp-kpi">
          <div className="muted-xs">Instructores acreditados</div>
          <div className="ic-cmp-kpi-num">{totalInstructores}</div>
          <div className="muted" style={{ fontSize: 11 }}>Cubren las 12 competencias maestras</div>
        </div>
        <div className="ic-cmp-kpi">
          <div className="muted-xs">Licencias SST por vencer</div>
          <div className="ic-cmp-kpi-num" style={{ color: licenciasPorVencer > 0 ? '#854D0E' : 'var(--green-600)' }}>{licenciasPorVencer}</div>
          <div className="muted" style={{ fontSize: 11 }}>Próximas a vencer en 30 días</div>
        </div>
        <div className="ic-cmp-kpi">
          <div className="muted-xs">Licencias SST vencidas</div>
          <div className="ic-cmp-kpi-num" style={{ color: licenciasVencidas > 0 ? 'var(--rose-600)' : 'var(--green-600)' }}>{licenciasVencidas}</div>
          <div className="muted" style={{ fontSize: 11 }}>Bloquean nueva sesión · Resolución 0312</div>
        </div>
      </div>

      {/* Toolbar: filtros + acción */}
      <div className="ob-toolbar" style={{ marginBottom: 14 }}>
        <div className="ob-search">
          <IconSearch size={15} />
          <input
            placeholder="Buscar proveedor por razón social…"
            value={search}
            onChange={e => setSearch(e.target.value)}
          />
        </div>
        <select value={tipo} onChange={e => setTipo(e.target.value)}>
          <option value="todos">Todos los tipos</option>
          <option value="ARL">ARL</option>
          <option value="SENA">SENA</option>
          <option value="consultor">Consultor</option>
          <option value="universidad">Universidad</option>
          <option value="elearning">E-learning</option>
          <option value="interno">Interno</option>
        </select>
        <span className="muted mono" style={{ fontSize: 11, alignSelf: 'center' }}>{filtered.length} resultados</span>
        <div style={{ flex: 1 }} />
        <button className="btn primary" onClick={() => setEdit('nuevo')}>
          <IconPlus size={14} /> Nuevo proveedor
        </button>
      </div>

      {/* Grid de proveedores */}
      <div className="ic-prov-grid">
        {filtered.map(p => {
          const provInsts = insts.filter(i => i.proveedorId === p.id);
          const venc = provInsts.filter(i => i.licenciaSST && docVigencia(i.licenciaSST.vence).state === 'vencida').length;
          const meta = PROV_TIPO_META[p.tipo] || { label: p.tipo, tone: 'navy' };
          return (
            <button key={p.id} className="ic-prov-card" onClick={() => setPicked(p)}>
              <div className="ic-prov-card-head">
                <span className={`pill ${meta.tone}`} style={{ fontSize: 10.5 }}>{meta.label}</span>
                <span className="muted mono" style={{ fontSize: 10.5 }}>{p.id}</span>
              </div>
              <div className="ic-prov-card-name">{p.razonSocial}</div>
              <div className="muted" style={{ fontSize: 11.5 }}>{p.cobertura}</div>
              <div className="ic-prov-card-meta">
                <div className="muted-xs">NIT</div>
                <div className="mono" style={{ fontSize: 11.5 }}>{p.nit}</div>
              </div>
              <div className="ic-prov-card-foot">
                <span className="muted" style={{ fontSize: 11 }}>
                  <IconUsers size={11} /> {provInsts.length} instructor{provInsts.length === 1 ? '' : 'es'}
                </span>
                {venc > 0 && (
                  <span className="pill rose" style={{ fontSize: 10.5 }}>
                    <IconAlert size={10} /> {venc} licencia{venc === 1 ? '' : 's'} vencida{venc === 1 ? '' : 's'}
                  </span>
                )}
                {venc === 0 && provInsts.some(i => i.licenciaSST) && (
                  <span className="pill green" style={{ fontSize: 10.5 }}>
                    <IconCheck size={10} /> Idoneidad vigente
                  </span>
                )}
              </div>
            </button>
          );
        })}
      </div>

      {picked && (
        <ProviderDetailPanel
          provider={picked}
          instructors={insts.filter(i => i.proveedorId === picked.id)}
          onClose={() => setPicked(null)}
          onEdit={() => setEdit(picked)}
          onSaveInstructor={saveInstructor}
          onDeleteInstructor={deleteInstructor}
        />
      )}
      {edit && (
        <ProviderEditModal
          provider={edit === 'nuevo' ? null : edit}
          onClose={() => setEdit(null)}
          onSave={saveProvider}
        />
      )}
    </>
  );
};

const ProviderDetailPanel = ({ provider, instructors, onClose, onEdit, onSaveInstructor, onDeleteInstructor }) => {
  const [editingInst, setEditingInst] = _ic_useState(null); // 'nuevo' | instructor | null

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

  const insts = instructors || [];
  const meta = PROV_TIPO_META[provider.tipo] || { label: provider.tipo, tone: 'navy' };

  return (
    <div className="tr-side-overlay" onMouseDown={e => { if (e.target === e.currentTarget) onClose(); }}>
      <div className="tr-side-panel" style={{ width: 720 }}>
        <div className="tr-side-head">
          <div style={{ minWidth: 0 }}>
            <div className="muted mono" style={{ fontSize: 11 }}>{provider.id} · <span className={`pill ${meta.tone}`} style={{ fontSize: 10 }}>{meta.label}</span></div>
            <div className="tr-side-title" style={{ marginTop: 4 }}>{provider.razonSocial}</div>
            <div className="muted" style={{ fontSize: 12, marginTop: 4 }}>{provider.cobertura}</div>
          </div>
          <div style={{ display: 'flex', gap: 6, alignItems: 'flex-start' }}>
            <button className="btn" onClick={onEdit}><IconPlus size={12} /> Editar</button>
            <button className="icon-btn" onClick={onClose}><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><path d="M18 6 6 18M6 6l12 12" /></svg></button>
          </div>
        </div>
        <div className="tr-side-body">
          {/* Identificación */}
          <section className="ic-prov-section">
            <h4 className="ic-cap-h3">Identificación</h4>
            <div className="ic-prov-info">
              <div><div className="muted-xs">NIT / Identificación</div><div className="mono">{provider.nit}</div></div>
              <div><div className="muted-xs">Contacto</div><div>{provider.contacto.nombre}</div></div>
              <div><div className="muted-xs">Email</div><div className="mono" style={{ fontSize: 11.5 }}>{provider.contacto.email}</div></div>
              <div><div className="muted-xs">Teléfono</div><div className="mono">{provider.contacto.tel}</div></div>
            </div>
          </section>

          {/* Soportes de idoneidad del proveedor */}
          <section className="ic-prov-section">
            <h4 className="ic-cap-h3">Soportes de idoneidad</h4>
            <p className="muted" style={{ fontSize: 12, marginTop: -4 }}>Documentos legales que avalan al proveedor para impartir capacitaciones. Soporta auditorías MinTrabajo / INVIMA / ICONTEC.</p>
            <div className="ic-prov-soportes">
              {provider.soportes.map((s, i) => {
                const v = docVigencia(s.vence);
                return (
                  <div key={i} className={`ic-prov-soporte ${v.state}`}>
                    <div className="ic-prov-soporte-icon"><IconShield size={16} /></div>
                    <div style={{ flex: 1 }}>
                      <div style={{ fontWeight: 600, fontSize: 12.5 }}>{s.tipo}</div>
                      <div className="muted mono" style={{ fontSize: 11 }}>{s.numero}</div>
                    </div>
                    <span className={`pill ${v.state === 'vencida' ? 'rose' : v.state === 'por-vencer' ? 'amber-soft' : 'green'}`} style={{ fontSize: 10.5 }}>
                      {v.label}
                    </span>
                  </div>
                );
              })}
            </div>
          </section>

          {/* Instructores del proveedor */}
          <section className="ic-prov-section">
            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
              <h4 className="ic-cap-h3" style={{ margin: 0 }}>Instructores acreditados · {insts.length}</h4>
              <button className="btn primary" onClick={() => setEditingInst('nuevo')}>
                <IconPlus size={12} /> Agregar instructor
              </button>
            </div>
            <p className="muted" style={{ fontSize: 12, marginTop: 4 }}>La licencia SST debe estar vigente para programar una sesión nueva (Resolución 0312 de 2019).</p>
            <div className="ic-prov-insts">
              {insts.length === 0 && (
                <div className="muted" style={{ fontSize: 12, padding: 16, textAlign: 'center' }}>Este proveedor no tiene instructores registrados.</div>
              )}
              {insts.map(ins => {
                const v = ins.licenciaSST ? docVigencia(ins.licenciaSST.vence) : { state: 'sin-vencimiento', label: 'No requiere licencia SST' };
                return (
                  <div key={ins.id} className={`ic-prov-inst ${v.state}`}>
                    <div className="ic-prov-inst-head">
                      <div>
                        <div style={{ fontWeight: 600, fontSize: 13 }}>{ins.nombre}</div>
                        <div className="muted" style={{ fontSize: 11.5 }}>{ins.profesion}</div>
                      </div>
                      <div style={{ display: 'flex', gap: 6, alignItems: 'flex-start' }}>
                        <span className={`pill ${v.state === 'vencida' ? 'rose' : v.state === 'por-vencer' ? 'amber-soft' : v.state === 'sin-vencimiento' ? 'navy' : 'green'}`} style={{ fontSize: 10.5 }}>
                          {v.state === 'vencida' && <IconAlert size={10} />}
                          {v.state === 'vigente' && <IconCheck size={10} />}
                          {' '}{v.label}
                        </span>
                        <button className="icon-btn" title="Editar" onClick={() => setEditingInst(ins)}>
                          <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><path d="M12 20h9M16.5 3.5a2.121 2.121 0 1 1 3 3L7 19l-4 1 1-4 12.5-12.5z" /></svg>
                        </button>
                        <button className="icon-btn" title="Eliminar" onClick={() => { if (confirm(`¿Eliminar instructor ${ins.nombre}?`)) onDeleteInstructor(ins.id); }}>
                          <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><path d="M3 6h18M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2m3 0v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6" /></svg>
                        </button>
                      </div>
                    </div>
                    <div className="ic-prov-inst-meta">
                      {ins.licenciaSST && (
                        <div><div className="muted-xs">Licencia SST</div><div className="mono" style={{ fontSize: 11.5 }}>{ins.licenciaSST.numero}</div><div className="muted" style={{ fontSize: 10.5 }}>Expedida por {ins.licenciaSST.expedidaPor}</div></div>
                      )}
                      {ins.tarjetaProfesional && (
                        <div><div className="muted-xs">Tarjeta profesional</div><div className="mono" style={{ fontSize: 11.5 }}>{ins.tarjetaProfesional}</div></div>
                      )}
                      <div>
                        <div className="muted-xs">Cubre competencias</div>
                        <div style={{ display: 'flex', flexWrap: 'wrap', gap: 4, marginTop: 2 }}>
                          {ins.competencias.map(cId => (
                            <span key={cId} className="pill navy-soft mono" style={{ fontSize: 10 }}>{cId}</span>
                          ))}
                        </div>
                      </div>
                    </div>
                  </div>
                );
              })}
            </div>
          </section>
        </div>
      </div>
      {editingInst && (
        <InstructorEditModal
          instructor={editingInst === 'nuevo' ? null : editingInst}
          proveedorId={provider.id}
          onClose={() => setEditingInst(null)}
          onSave={(data) => { onSaveInstructor(data); setEditingInst(null); }}
        />
      )}
    </div>
  );
};

// ─── Modal: crear/editar Proveedor ────────────────────────

const ProviderEditModal = ({ provider, onClose, onSave }) => {
  const isNew = !provider;
  const [form, setForm] = _ic_useState(() => provider ? { ...provider } : {
    id: '',
    tipo: 'consultor',
    razonSocial: '',
    nit: '',
    cobertura: '',
    estado: 'activo',
    contacto: { nombre: '', email: '', tel: '' },
    soportes: [],
  });

  React.useEffect(() => {
    document.body.style.overflow = 'hidden';
    const onEsc = (e) => { if (e.key === 'Escape') onClose(); };
    window.addEventListener('keydown', onEsc);
    return () => { document.body.style.overflow = ''; window.removeEventListener('keydown', onEsc); };
  }, []);

  const upd = (k, v) => setForm({ ...form, [k]: v });
  const updContacto = (k, v) => setForm({ ...form, contacto: { ...form.contacto, [k]: v } });

  const addSoporte = () => setForm({ ...form, soportes: [...(form.soportes || []), { tipo: '', numero: '', vence: null, url: '#' }] });
  const updSoporte = (i, k, v) => setForm({ ...form, soportes: form.soportes.map((s, idx) => idx === i ? { ...s, [k]: v } : s) });
  const delSoporte = (i) => setForm({ ...form, soportes: form.soportes.filter((_, idx) => idx !== i) });

  const canSave = form.razonSocial && form.nit && form.tipo;

  return (
    <div className="modal-overlay" onMouseDown={e => { if (e.target === e.currentTarget) onClose(); }}>
      <div className="modal" style={{ maxWidth: 720 }}>
        <div className="modal-head">
          <div>
            <div className="muted mono" style={{ fontSize: 11 }}>{isNew ? 'Nuevo proveedor' : form.id}</div>
            <div className="modal-title">{isNew ? 'Registrar nuevo proveedor' : `Editar · ${provider.razonSocial}`}</div>
            <div className="modal-sub">Define el proveedor que imparte capacitaciones y sus soportes legales de idoneidad.</div>
          </div>
          <button className="icon-btn" onClick={onClose}><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><path d="M18 6 6 18M6 6l12 12" /></svg></button>
        </div>
        <div className="modal-body" style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
          <div className="ic-cap-setup-grid">
            <div className="ic-cap-field">
              <div className="muted-xs">Tipo</div>
              <select className="ic-cap-version" value={form.tipo} onChange={e => upd('tipo', e.target.value)}>
                <option value="ARL">ARL</option>
                <option value="SENA">SENA</option>
                <option value="consultor">Consultor</option>
                <option value="universidad">Universidad</option>
                <option value="elearning">E-learning</option>
                <option value="interno">Interno</option>
              </select>
            </div>
            <div className="ic-cap-field">
              <div className="muted-xs">NIT / Identificación *</div>
              <input className="ic-cap-version" type="text" value={form.nit} onChange={e => upd('nit', e.target.value)} placeholder="900.123.456-7" />
            </div>
            <div className="ic-cap-field" style={{ gridColumn: '1 / -1' }}>
              <div className="muted-xs">Razón social *</div>
              <input className="ic-cap-version" type="text" value={form.razonSocial} onChange={e => upd('razonSocial', e.target.value)} placeholder="Ej. ARL Sura · Centro de capacitación" />
            </div>
            <div className="ic-cap-field" style={{ gridColumn: '1 / -1' }}>
              <div className="muted-xs">Cobertura</div>
              <input className="ic-cap-version" type="text" value={form.cobertura} onChange={e => upd('cobertura', e.target.value)} placeholder="Nacional · planta Cali" />
            </div>
          </div>

          <div>
            <h4 className="ic-cap-h3" style={{ fontSize: 13, marginBottom: 8 }}>Contacto</h4>
            <div className="ic-cap-setup-grid">
              <div className="ic-cap-field">
                <div className="muted-xs">Nombre</div>
                <input className="ic-cap-version" type="text" value={form.contacto?.nombre || ''} onChange={e => updContacto('nombre', e.target.value)} />
              </div>
              <div className="ic-cap-field">
                <div className="muted-xs">Email</div>
                <input className="ic-cap-version" type="email" value={form.contacto?.email || ''} onChange={e => updContacto('email', e.target.value)} />
              </div>
              <div className="ic-cap-field" style={{ gridColumn: '1 / -1' }}>
                <div className="muted-xs">Teléfono</div>
                <input className="ic-cap-version" type="text" value={form.contacto?.tel || ''} onChange={e => updContacto('tel', e.target.value)} placeholder="+57 314 555 4123" />
              </div>
            </div>
          </div>

          <div>
            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 8 }}>
              <h4 className="ic-cap-h3" style={{ fontSize: 13, margin: 0 }}>Soportes de idoneidad</h4>
              <button className="btn" onClick={addSoporte}><IconPlus size={12} /> Agregar soporte</button>
            </div>
            {(form.soportes || []).length === 0 && (
              <div className="muted" style={{ fontSize: 12, padding: 14, textAlign: 'center', background: 'var(--surface)', borderRadius: 8 }}>Sin soportes cargados todavía.</div>
            )}
            {(form.soportes || []).map((s, i) => (
              <div key={i} className="ic-cap-setup-grid" style={{ marginBottom: 8, padding: 10, background: 'var(--surface)', borderRadius: 8 }}>
                <div className="ic-cap-field">
                  <div className="muted-xs">Tipo de soporte</div>
                  <input className="ic-cap-version" type="text" value={s.tipo} onChange={e => updSoporte(i, 'tipo', e.target.value)} placeholder="Licencia SST entidad" />
                </div>
                <div className="ic-cap-field">
                  <div className="muted-xs">Número</div>
                  <input className="ic-cap-version" type="text" value={s.numero} onChange={e => updSoporte(i, 'numero', e.target.value)} />
                </div>
                <div className="ic-cap-field">
                  <div className="muted-xs">Vence (vacío = no vence)</div>
                  {window.DateInputCO
                    ? <window.DateInputCO className="ic-cap-version" value={s.vence || ''} onChange={(v) => updSoporte(i, 'vence', v || null)} />
                    : <input className="ic-cap-version" type="text" value={s.vence || ''} onChange={e => updSoporte(i, 'vence', e.target.value || null)} placeholder="31/12/2027" />}
                </div>
                <div className="ic-cap-field" style={{ alignSelf: 'end' }}>
                  <button className="btn" onClick={() => delSoporte(i)}>Quitar</button>
                </div>
              </div>
            ))}
          </div>
        </div>
        <div className="modal-foot">
          <div style={{ flex: 1 }} />
          <button className="btn" onClick={onClose}>Cancelar</button>
          <button className="btn primary" disabled={!canSave} onClick={() => onSave(form)}>
            <IconCheck size={13} /> {isNew ? 'Registrar proveedor' : 'Guardar cambios'}
          </button>
        </div>
      </div>
    </div>
  );
};

// ─── Modal: crear/editar Instructor ───────────────────────

const InstructorEditModal = ({ instructor, proveedorId, onClose, onSave }) => {
  const isNew = !instructor;
  const [form, setForm] = _ic_useState(() => instructor ? { ...instructor } : {
    id: '',
    proveedorId,
    nombre: '',
    profesion: '',
    licenciaSST: null,
    tarjetaProfesional: '',
    hojaDeVidaUrl: '',
    competencias: [],
  });
  const [reqLicencia, setReqLicencia] = _ic_useState(!!form.licenciaSST);

  React.useEffect(() => {
    document.body.style.overflow = 'hidden';
    const onEsc = (e) => { if (e.key === 'Escape') onClose(); };
    window.addEventListener('keydown', onEsc);
    return () => { document.body.style.overflow = ''; window.removeEventListener('keydown', onEsc); };
  }, []);

  const upd = (k, v) => setForm({ ...form, [k]: v });
  const updLic = (k, v) => setForm({ ...form, licenciaSST: { ...(form.licenciaSST || { numero: '', vence: '', expedidaPor: '' }), [k]: v } });

  const toggleComp = (cId) => {
    const has = form.competencias.includes(cId);
    upd('competencias', has ? form.competencias.filter(c => c !== cId) : [...form.competencias, cId]);
  };

  const canSave = form.nombre && form.profesion;

  const submit = () => {
    const final = { ...form };
    if (!reqLicencia) final.licenciaSST = null;
    onSave(final);
  };

  return (
    <div className="modal-overlay" onMouseDown={e => { if (e.target === e.currentTarget) onClose(); }}>
      <div className="modal" style={{ maxWidth: 640 }}>
        <div className="modal-head">
          <div>
            <div className="muted mono" style={{ fontSize: 11 }}>{isNew ? 'Nuevo instructor' : form.id}</div>
            <div className="modal-title">{isNew ? 'Agregar instructor' : `Editar · ${instructor.nombre}`}</div>
            <div className="modal-sub">Su licencia SST debe estar vigente para programar sesiones internas.</div>
          </div>
          <button className="icon-btn" onClick={onClose}><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><path d="M18 6 6 18M6 6l12 12" /></svg></button>
        </div>
        <div className="modal-body" style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
          <div className="ic-cap-setup-grid">
            <div className="ic-cap-field" style={{ gridColumn: '1 / -1' }}>
              <div className="muted-xs">Nombre completo *</div>
              <input className="ic-cap-version" type="text" value={form.nombre} onChange={e => upd('nombre', e.target.value)} />
            </div>
            <div className="ic-cap-field" style={{ gridColumn: '1 / -1' }}>
              <div className="muted-xs">Profesión / Cargo *</div>
              <input className="ic-cap-version" type="text" value={form.profesion} onChange={e => upd('profesion', e.target.value)} placeholder="Ingeniero industrial · especialista SST" />
            </div>
            <div className="ic-cap-field">
              <div className="muted-xs">Tarjeta profesional</div>
              <input className="ic-cap-version" type="text" value={form.tarjetaProfesional || ''} onChange={e => upd('tarjetaProfesional', e.target.value)} placeholder="COPNIA 2008-203-115443" />
            </div>
            <div className="ic-cap-field">
              <div className="muted-xs">URL hoja de vida</div>
              <input className="ic-cap-version" type="text" value={form.hojaDeVidaUrl || ''} onChange={e => upd('hojaDeVidaUrl', e.target.value)} placeholder="#mock-cv.pdf" />
            </div>
          </div>

          <label style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 13 }}>
            <input type="checkbox" checked={reqLicencia} onChange={e => setReqLicencia(e.target.checked)} />
            Requiere licencia SST vigente
          </label>

          {reqLicencia && (
            <div className="ic-cap-setup-grid">
              <div className="ic-cap-field">
                <div className="muted-xs">Número de licencia</div>
                <input className="ic-cap-version" type="text" value={form.licenciaSST?.numero || ''} onChange={e => updLic('numero', e.target.value)} placeholder="LIC-SST-RIS-08421" />
              </div>
              <div className="ic-cap-field">
                <div className="muted-xs">Vence</div>
                <window.DatePickerCO
                  value={form.licenciaSST?.vence || ''}
                  onChange={(v) => updLic('vence', v)}
                  presets={[
                    { label: 'En 1 año',  compute: (t) => { const d = new Date(t); d.setFullYear(d.getFullYear() + 1); return d; } },
                    { label: 'En 2 años', compute: (t) => { const d = new Date(t); d.setFullYear(d.getFullYear() + 2); return d; } },
                    { label: 'En 3 años', compute: (t) => { const d = new Date(t); d.setFullYear(d.getFullYear() + 3); return d; } },
                  ]}
                />
              </div>
              <div className="ic-cap-field" style={{ gridColumn: '1 / -1' }}>
                <div className="muted-xs">Expedida por</div>
                <input className="ic-cap-version" type="text" value={form.licenciaSST?.expedidaPor || ''} onChange={e => updLic('expedidaPor', e.target.value)} placeholder="SecSalud Valle del Cauca · MinTrabajo · INVIMA" />
              </div>
            </div>
          )}

          <div>
            <div className="muted-xs" style={{ marginBottom: 6 }}>Competencias que cubre</div>
            <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
              {(window.COMPETENCIAS_MAESTRAS || []).map(c => {
                const sel = form.competencias.includes(c.id);
                return (
                  <button
                    key={c.id}
                    onClick={() => toggleComp(c.id)}
                    className={`pill ${sel ? 'green' : 'navy-soft'} mono`}
                    style={{ fontSize: 11, cursor: 'pointer', border: 'none' }}
                    title={c.name}
                  >
                    {sel ? <IconCheck size={10} /> : null} {c.id}
                  </button>
                );
              })}
            </div>
          </div>
        </div>
        <div className="modal-foot">
          <div style={{ flex: 1 }} />
          <button className="btn" onClick={onClose}>Cancelar</button>
          <button className="btn primary" disabled={!canSave} onClick={submit}>
            <IconCheck size={13} /> {isNew ? 'Agregar instructor' : 'Guardar cambios'}
          </button>
        </div>
      </div>
    </div>
  );
};

// ═══════════════════════════════════════════════════════════
// Tab: Bitácora · auditoría inmutable
// ═══════════════════════════════════════════════════════════

const BitacoraTab = ({ currentRole = 'admin' }) => {
  const [filter, setFilter] = _ic_useState('all');
  const [migOpen, setMigOpen] = _ic_useState(false);
  const list = AUDIT_LOG;

  // Gate · solo Admin puede ver bitácora completa
  if (currentRole !== 'admin') {
    return <RoleRestricted allow={['admin']} currentRole={currentRole} label="la bitácora de auditoría">
      <div />
    </RoleRestricted>;
  }

  return (
    <>
      <HashMigrationBanner onOpen={() => setMigOpen(true)} />
      {migOpen && <HashMigrationModal onClose={() => setMigOpen(false)} />}

      <div className="ic-aud-banner">
        <div className="ic-aud-banner-icon"><IconShield size={16} /></div>
        <div style={{ flex: 1 }}>
          <div style={{ fontSize: 13.5, fontWeight: 700, color: 'var(--text)' }}>Bitácora append-only · acceso solo Admin</div>
          <div className="muted" style={{ fontSize: 12, marginTop: 2 }}>
            Cada acción del subsistema queda registrada con timestamp, usuario, IP y hash. <b>No se puede editar ni borrar.</b> Para corregir errores se anula y se crea un nuevo registro.
          </div>
        </div>
        <button className="btn"><IconExternal size={14} /> Exportar para auditor</button>
      </div>

      <div className="sst-quickfilter">
        <div className="ob-search" style={{ maxWidth: 300 }}>
          <IconSearch size={14} />
          <input placeholder="Buscar por entidad, usuario, ID…" />
        </div>
        <select>
          <option>Cualquier acción</option>
          <option>Sesión cerrada</option>
          <option>Curso publicado</option>
          <option>Evidencia cargada</option>
          <option>Firma electrónica</option>
          <option>Asignación generada</option>
        </select>
        <select>
          <option>Últimos 30 días</option>
          <option>Últimos 90 días</option>
          <option>Año en curso</option>
        </select>
        <div style={{ flex: 1 }} />
        <span className="muted mono" style={{ fontSize: 11 }}>{list.length} registros · ordenado desc.</span>
      </div>

      <div className="ic-aud-table">
        {list.map(l => (
          <div key={l.id} className={`ic-aud-row ic-aud-${l.tone}`}>
            <div className="ic-aud-id mono">{l.id}</div>
            <div className="ic-aud-time">
              <div className="mono" style={{ fontSize: 12, fontWeight: 700, color: 'var(--text)' }}>{l.when.split(' ')[0]}</div>
              <div className="mono" style={{ fontSize: 10.5, color: 'var(--text-muted)' }}>{l.when.split(' ')[1]}</div>
            </div>
            <div className="ic-aud-action">{l.action}</div>
            <div className="ic-aud-entity mono">{l.entity}</div>
            <div className="ic-aud-detail muted">{l.detail}</div>
            <div className="ic-aud-who">
              <div style={{ fontSize: 12, fontWeight: 600 }}>{l.who}</div>
              <div className="mono muted" style={{ fontSize: 10.5 }}>{l.ip}</div>
            </div>
          </div>
        ))}
      </div>
    </>
  );
};

// ═══════════════════════════════════════════════════════════
// Modal: Captura de sesión · 6 pasos
// ═══════════════════════════════════════════════════════════

const SessionCapture = ({ session, onClose }) => {
  const [step, setStep] = _ic_useState(1);
  // Step 1 — setup (todo viene precargado de la sesión)
  const [version, setVersion] = _ic_useState(session.contentVersion);
  // Origen del certificado · interno = Atica captura todo · externo = el proveedor emite el cert y solo lo archivamos
  const [origen, setOrigen] = _ic_useState(session.origenCertificado || 'interno');
  // Step 4 (variante externa) — datos del certificado emitido por el proveedor
  const [extCert, setExtCert] = _ic_useState({
    codigo: '',
    vigenciaFrom: session.date || '',
    vigenciaTo: '',
    archivoNombre: '',
  });
  // Step 2 — asistencia
  const [attendance, setAttendance] = _ic_useState(
    (session.enrolled || []).map(e => ({ ...e, present: false, signed: false, channel: 'email', code: '' }))
  );
  // Step 3 — evaluación
  const [responses, setResponses] = _ic_useState(
    Object.fromEntries((session.enrolled || []).map(e => [e.empId, {}]))
  );
  // Step 4 — evidencias
  const [evidences, setEvidences] = _ic_useState([
    { id: 'ev1', name: 'acta-instructor.pdf',  size: 268, kind: 'acta',  uploaded: true },
  ]);
  // Step 5 — instructor comments + manual override
  const [resultComments, setResultComments] = _ic_useState({});
  // Step 6 — certificate emission state
  const [closing, setClosing] = _ic_useState(false);
  const [closed, setClosed]   = _ic_useState(false);

  // Cruce con catálogos de Fase A — buscar instructor y proveedor de la sesión
  const instructorRecord = (window.INSTRUCTORES || []).find(i => i.nombre === session.instructor);
  const proveedorRecord  = (window.PROVEEDORES  || []).find(p => p.razonSocial === session.instructorOrg)
    || (instructorRecord && window.getProveedor ? window.getProveedor(instructorRecord.proveedorId) : null);
  const licencia = instructorRecord && instructorRecord.licenciaSST
    ? docVigencia(instructorRecord.licenciaSST.vence)
    : { state: 'sin-vencimiento', label: 'No requiere licencia SST' };
  const isExt = origen === 'externo';

  // Fase E.2 · Resolver preguntas de la evaluación del curso (si existe).
  // Cae al template legacy EVAL_TEMPLATE_C01 si no encuentra evaluación configurada.
  const evaluacionCurso = window.findEvaluacionByCourse ? window.findEvaluacionByCourse(session.courseId) : null;
  const preguntasResueltas = React.useMemo(() => {
    if (!evaluacionCurso || !window.resolverPreguntas) return null;
    const resolved = window.resolverPreguntas(evaluacionCurso);
    return resolved.length > 0 ? resolved : null;
  }, [session.courseId, evaluacionCurso?.id, evaluacionCurso?.modo]);
  // Banco efectivo: dinámico (E.2) si existe, o legacy de respaldo
  const PREGUNTAS_EFECTIVAS = preguntasResueltas
    ? preguntasResueltas.map(p => ({ id: p.id, text: p.enunciado, options: p.opciones, correct: p.respuestaCorrecta }))
    : EVAL_TEMPLATE_C01;

  React.useEffect(() => {
    document.body.style.overflow = 'hidden';
    const onEsc = (e) => { if (e.key === 'Escape') onClose(); };
    window.addEventListener('keydown', onEsc);
    return () => { document.body.style.overflow = ''; window.removeEventListener('keydown', onEsc); };
  }, []);

  const stepsMeta = [
    { n: 1, label: 'Sesión',      icon: 'IconRocket' },
    { n: 2, label: 'Asistencia',  icon: 'IconUsers' },
    { n: 3, label: 'Evaluación',  icon: 'IconStar' },
    { n: 4, label: 'Evidencias',  icon: 'IconDoc' },
    { n: 5, label: 'Resultados',  icon: 'IconCheck' },
    { n: 6, label: 'Cierre',      icon: 'IconShield' },
  ];

  const presentCount = attendance.filter(a => a.present).length;
  const signedCount  = attendance.filter(a => a.signed).length;

  // Compute per-attendee score
  const scoreFor = (empId) => {
    const r = responses[empId] || {};
    let correct = 0;
    PREGUNTAS_EFECTIVAS.forEach(q => {
      if (r[q.id] === q.correct) correct++;
    });
    return PREGUNTAS_EFECTIVAS.length ? Math.round((correct / PREGUNTAS_EFECTIVAS.length) * 100) : 0;
  };

  const evaluatedCount = attendance.filter(a => Object.keys(responses[a.empId] || {}).length === PREGUNTAS_EFECTIVAS.length).length;

  const requiredEvidence = evidences.filter(e => e.kind === 'acta').length > 0;
  const evidencesCount = evidences.length;

  // Validación del certificado externo (Step 4 cuando origen=externo)
  const extCertOk = !!(extCert.codigo && extCert.vigenciaFrom && extCert.vigenciaTo && extCert.archivoNombre);

  // Bloqueo: si el instructor tiene licencia VENCIDA y la sesión es interna, no se puede avanzar.
  // Cuando origen=externo, la idoneidad la garantiza el proveedor (asunto del tercero, no de Atica).
  const licenciaBloquea = !isExt && licencia.state === 'vencida';

  // Validation per step
  const canAdvance = {
    1: !licenciaBloquea,
    2: isExt ? true : (presentCount > 0 && signedCount === presentCount),
    3: isExt ? true : (evaluatedCount === presentCount),
    4: isExt ? extCertOk : (requiredEvidence && evidencesCount >= 2),
    5: true,
    6: false,
  };

  const stepValidation = {
    1: licenciaBloquea
       ? { ok: false, msg: `Licencia SST del instructor vencida · ${licencia.label}. Reemplaza el instructor o cambia el origen a externo.` }
       : { ok: true,  msg: isExt
           ? `Sesión externa · certificado lo emite ${proveedorRecord?.razonSocial || session.instructorOrg}`
           : 'Datos de la sesión confirmados · idoneidad del instructor verificada' },
    2: isExt
       ? { ok: true, msg: 'Asistencia capturada por el proveedor externo · no aplica registro interno' }
       : { ok: presentCount === signedCount && presentCount > 0,
           msg: signedCount === presentCount && presentCount > 0
             ? `${presentCount} colaboradores presentes · ${signedCount} firmaron electrónicamente`
             : `Faltan ${presentCount - signedCount} firmas electrónicas de los ${presentCount} presentes` },
    3: isExt
       ? { ok: true, msg: 'Evaluación aplicada por el proveedor externo · resultado en certificado' }
       : { ok: evaluatedCount === presentCount,
           msg: evaluatedCount === presentCount
             ? `Evaluación aplicada a los ${presentCount} asistentes`
             : `${evaluatedCount} de ${presentCount} asistentes han completado la evaluación` },
    4: isExt
       ? { ok: extCertOk,
           msg: extCertOk
             ? `Certificado externo ${extCert.codigo} archivado · vigencia hasta ${extCert.vigenciaTo}`
             : 'Falta cargar PDF + datos del certificado emitido por el proveedor' }
       : { ok: requiredEvidence && evidencesCount >= 2,
           msg: requiredEvidence
             ? `${evidencesCount} evidencias cargadas · acta del instructor incluida`
             : 'Falta acta del instructor (obligatoria)' },
    5: { ok: true, msg: isExt
         ? 'Resumen del certificado externo · listo para archivar'
         : 'Resultados calculados automáticamente desde nota ≥ ' + session.minScore },
    6: { ok: closed,
         msg: closed
           ? (isExt ? 'Certificado externo archivado · expediente inmutable' : 'Sesión cerrada e inmutable')
           : 'Pendiente cierre formal' },
  };

  const setAtt = (i, patch) => setAttendance(a => a.map((x, idx) => idx === i ? { ...x, ...patch } : x));
  const setResp = (empId, qId, optIdx) =>
    setResponses(r => ({ ...r, [empId]: { ...r[empId], [qId]: optIdx } }));

  const closeSession = () => {
    setClosing(true);
    setTimeout(() => {
      // Fase D · Empujar certificado(s) al store global para que cert-verify.jsx los pueda consultar
      const cmp = getCmp(session.competenciaId);
      const empresa = { name: 'Atica S.A.S.', nit: '900.123.456-7', logo: 'a' };
      const plataforma = { name: 'Atica SOMOS', logo: 'a' };
      const hoy = new Date().toLocaleDateString('es-CO');
      const issuedAt = new Date().toLocaleString('es-CO');

      if (isExt) {
        // Origen externo: 1 cert por inscrito · vigencia que dijo el proveedor
        (session.enrolled || []).forEach((emp, idx) => {
          const code = `CERT-EXT-${(proveedorRecord?.razonSocial || 'EXT').slice(0, 4).toUpperCase()}-${session.id.slice(-4)}-${String(idx + 1).padStart(2, '0')}`;
          const newCert = {
            code,
            status: 'vigente',
            origen: 'externo',
            emp: { initials: emp.initials, name: emp.name, doc: `CC ${emp.empId} *** *** **`, cargo: emp.role, area: '—', site: session.site },
            empresa, plataforma,
            course: { name: session.courseName, code: session.courseId, duration: '—', mode: 'Externo' },
            competencia: { id: session.competenciaId, name: cmp?.name || session.competenciaId, norma: cmp?.norma || '—' },
            session: { id: session.id, date: session.date, site: session.site, instructor: session.instructor, instructorOrg: session.instructorOrg, contentVersion: 'externo' },
            result: { score: null, minScore: session.minScore, passed: true, issuedAt },
            vigencia: { from: extCert.vigenciaFrom, to: extCert.vigenciaTo, daysLeft: 365 },
            evidence: {
              attendanceSigned: false,
              attendanceMethod: `Capturada por el proveedor (${proveedorRecord?.razonSocial || session.instructorOrg})`,
              evaluationApplied: true,
              evaluationQuestions: null,
              evidencesCount: 1,
              evidencesList: [extCert.archivoNombre || 'Certificado del proveedor'],
              contentVersionLocked: 'externo',
            },
            hash: null,
            bitacoraEvents: [
              { when: issuedAt, text: 'Certificado externo archivado · vinculado a hoja de vida', tone: 'green' },
              { when: issuedAt, text: `PDF cargado · ${extCert.archivoNombre || 'cert.pdf'}`, tone: 'navy' },
              { when: session.date, text: `Sesión impartida por ${proveedorRecord?.razonSocial || session.instructorOrg}`, tone: 'navy' },
            ],
          };
          window.CERTIFICATES = [...(window.CERTIFICATES || []), newCert];
        });
      } else {
        // Origen interno: 1 cert por aprobado
        attendance.filter(a => a.present && scoreFor(a.empId) >= session.minScore).forEach((emp, idx) => {
          const code = `CERT-${new Date().getFullYear()}-${session.courseId}-${String(Math.floor(Math.random() * 90000) + 10000)}`;
          const score = scoreFor(emp.empId);
          const vigDate = new Date(); vigDate.setFullYear(vigDate.getFullYear() + 1);
          const vigTo = vigDate.toLocaleDateString('es-CO');
          const newCert = {
            code,
            status: 'vigente',
            origen: 'interno',
            emp: { initials: emp.initials, name: emp.name, doc: `CC ${emp.empId} *** *** **`, cargo: emp.role, area: '—', site: session.site },
            empresa, plataforma,
            course: { name: session.courseName, code: session.courseId, duration: '—', mode: 'Interno' },
            competencia: { id: session.competenciaId, name: cmp?.name || session.competenciaId, norma: cmp?.norma || '—' },
            session: { id: session.id, date: session.date, site: session.site, instructor: session.instructor, instructorOrg: session.instructorOrg, contentVersion: version },
            result: { score, minScore: session.minScore, passed: true, issuedAt },
            vigencia: { from: hoy, to: vigTo, daysLeft: 365 },
            evidence: {
              attendanceSigned: true,
              attendanceMethod: `OTP · ${emp.channel}`,
              evaluationApplied: true,
              evaluationQuestions: PREGUNTAS_EFECTIVAS.length,
              evidencesCount: evidences.length,
              evidencesList: evidences.map(e => e.name),
              contentVersionLocked: version,
            },
            hash: 'a4f2c1e89b7d3f5e26c8d4b1a9f3e7c5d8b2a4f6e9c1d3b5a7f8e0c2d4b6a9f1',
            bitacoraEvents: [
              { when: issuedAt, text: 'Sesión cerrada · hash criptográfico generado', tone: 'green' },
              { when: issuedAt, text: 'Certificado emitido automáticamente al colaborador', tone: 'green' },
              { when: issuedAt, text: `Evaluación aprobada con ${score}/100 (mínimo ${session.minScore})`, tone: 'green' },
            ],
          };
          window.CERTIFICATES = [...(window.CERTIFICATES || []), newCert];
        });
      }

      setClosing(false); setClosed(true); setStep(6);
    }, 1800);
  };

  return (
    <div className="modal-overlay" onMouseDown={e => { if (e.target === e.currentTarget && !closing) onClose(); }}>
      <div className="modal ic-cap-modal" style={{ maxWidth: 1180, height: 'calc(100vh - 64px)' }}>
        <div className="modal-head ic-cap-head">
          <div>
            <div className="muted mono" style={{ fontSize: 11 }}>
              {session.id} · {session.competenciaId}
              {session.planId && (() => {
                const p = (window.PLANES_FORMATIVOS || []).find(x => x.id === session.planId);
                return p ? <> · plan: <b>{p.nombre}</b></> : null;
              })()}
            </div>
            <div className="modal-title">Captura de sesión · {session.courseName}</div>
            <div className="modal-sub">
              <b>{window.sessionDateLabel ? window.sessionDateLabel(session) : (session.dateStart || session.date)} · {session.site}</b>
              {' '} · instructor {session.instructor} ({session.instructorOrg}) · contenido {session.contentVersion}
              {(window.sessionDayCount && window.sessionDayCount(session) > 1) && (
                <span className="pill rose" style={{ fontSize: 10.5, marginLeft: 8 }}>
                  Sesión multi-día · {window.sessionDayCount(session)} jornadas · cierre al final del último día
                </span>
              )}
            </div>
          </div>
          <button className="icon-btn" onClick={onClose} title="Cerrar"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><path d="M18 6 6 18M6 6l12 12" /></svg></button>
        </div>

        {/* Stepper */}
        <div className="ic-cap-stepper">
          {stepsMeta.map(s => {
            const Ico = window[s.icon] || IconCheck;
            const done = step > s.n;
            const current = step === s.n;
            const cls = done ? 'done' : current ? 'current' : 'pending';
            return (
              <React.Fragment key={s.n}>
                <button className={`ic-cap-step ${cls}`} onClick={() => s.n <= step && setStep(s.n)} disabled={s.n > step}>
                  <span className="ic-cap-step-num">{done ? <IconCheck size={11} /> : s.n}</span>
                  <span className="ic-cap-step-label"><Ico size={11} /> {s.label}</span>
                </button>
                {s.n < stepsMeta.length && <div className={`ic-cap-step-line ${done ? 'done' : ''}`} />}
              </React.Fragment>
            );
          })}
        </div>

        <div className="modal-body ic-cap-body">

          {/* ───── Step 1 · Setup ───── */}
          {step === 1 && (
            <div className="ic-cap-section">
              <h3 className="ic-cap-h3">Datos de la sesión</h3>
              <p className="muted" style={{ fontSize: 13, marginTop: -4 }}>Confirma los datos antes de iniciar la captura. Define si el certificado lo emite Atica o lo aporta un tercero.</p>

              {/* Switch origen del certificado */}
              <div className="ic-cap-origen">
                <div className="muted-xs">Origen del certificado</div>
                <div className="ic-cap-origen-tabs">
                  <button
                    className={`ic-cap-origen-tab ${!isExt ? 'active' : ''}`}
                    onClick={() => setOrigen('interno')}
                  >
                    <IconShield size={13} />
                    <div>
                      <div style={{ fontWeight: 700, fontSize: 13 }}>Interno · Atica captura</div>
                      <div className="muted" style={{ fontSize: 11.5 }}>Asistencia, evaluación, evidencias y cierre con hash criptográfico.</div>
                    </div>
                  </button>
                  <button
                    className={`ic-cap-origen-tab ${isExt ? 'active' : ''}`}
                    onClick={() => setOrigen('externo')}
                  >
                    <IconExternal size={13} />
                    <div>
                      <div style={{ fontWeight: 700, fontSize: 13 }}>Externo · proveedor emite certificado</div>
                      <div className="muted" style={{ fontSize: 11.5 }}>Solo se archiva el PDF + datos del certificado emitido por ARL/SENA/etc.</div>
                    </div>
                  </button>
                </div>
              </div>

              <div className="ic-cap-setup-grid">
                <div className="ic-cap-field"><div className="muted-xs">Curso</div><div className="ic-cap-field-val">{session.courseName}</div></div>
                <div className="ic-cap-field"><div className="muted-xs">Competencia que cubre</div><div className="ic-cap-field-val">{getCmp(session.competenciaId)?.name || session.competenciaId}</div></div>
                <div className="ic-cap-field"><div className="muted-xs">Fecha y horario</div><div className="ic-cap-field-val mono">{session.date} · {session.time}</div></div>
                <div className="ic-cap-field"><div className="muted-xs">Sede</div><div className="ic-cap-field-val">{session.site}</div></div>
                <div className="ic-cap-field"><div className="muted-xs">Instructor</div><div className="ic-cap-field-val">{session.instructor}<div className="muted" style={{ fontSize: 11 }}>{session.instructorOrg}</div></div></div>
                <div className="ic-cap-field"><div className="muted-xs">Aprobación mínima</div><div className="ic-cap-field-val mono">{session.minScore} / 100</div></div>

                {/* Idoneidad del instructor (Fase B) */}
                <div className="ic-cap-field" style={{ gridColumn: '1 / -1' }}>
                  <div className="muted-xs">Idoneidad del instructor</div>
                  {instructorRecord ? (
                    <div className={`ic-cap-licencia ic-cap-licencia-${licencia.state}`}>
                      <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                        {licencia.state === 'vencida' ? <IconAlert size={14} /> : <IconCheck size={14} />}
                        <div>
                          <div style={{ fontWeight: 700, fontSize: 13 }}>{instructorRecord.profesion}</div>
                          {instructorRecord.licenciaSST ? (
                            <div className="muted mono" style={{ fontSize: 11 }}>
                              Licencia SST {instructorRecord.licenciaSST.numero} · {licencia.label}
                            </div>
                          ) : (
                            <div className="muted" style={{ fontSize: 11 }}>{licencia.label}</div>
                          )}
                          {proveedorRecord && (
                            <div className="muted" style={{ fontSize: 11 }}>{proveedorRecord.razonSocial} · NIT {proveedorRecord.nit}</div>
                          )}
                        </div>
                      </div>
                      {licenciaBloquea && (
                        <div className="ic-cap-licencia-warn">
                          <IconAlert size={12} /> Bloqueante para sesión interna. Reemplaza al instructor o marca origen externo.
                        </div>
                      )}
                    </div>
                  ) : (
                    <div className="muted" style={{ fontSize: 12, padding: '8px 12px', background: 'var(--surface)', borderRadius: 8 }}>
                      Instructor no encontrado en el catálogo · idoneidad no verificable automáticamente.
                    </div>
                  )}
                </div>

                {!isExt && (
                  <div className="ic-cap-field" style={{ gridColumn: '1 / -1' }}>
                    <div className="muted-xs">Versión del contenido a aplicar</div>
                    <select className="ic-cap-version" value={version} onChange={e => setVersion(e.target.value)}>
                      <option value="v3.2">v3.2 · 12/04/2026 · Carlos F. Vélez (vigente)</option>
                      <option value="v3.1">v3.1 · 03/02/2026 · Carlos F. Vélez</option>
                      <option value="v3.0">v3.0 · 15/12/2025 · Carolina Restrepo</option>
                    </select>
                    <div className="muted" style={{ fontSize: 11.5, marginTop: 4 }}>
                      <IconShield size={11} /> La versión se ancla a esta sesión y aparecerá en todos los certificados que emita.
                    </div>
                  </div>
                )}
              </div>
            </div>
          )}

          {/* ───── Step 2 · Asistencia (sesión externa: no aplica) ───── */}
          {step === 2 && isExt && (
            <div className="ic-cap-section">
              <h3 className="ic-cap-h3">Asistencia · capturada por el proveedor</h3>
              <p className="muted" style={{ fontSize: 13, marginTop: -4 }}>En sesiones externas la asistencia y firma de los participantes la registra <b>{proveedorRecord?.razonSocial || session.instructorOrg}</b> en su plataforma. Solo se archiva el certificado.</p>
              <div className="ic-cap-ext-skip">
                <IconExternal size={36} />
                <div>
                  <div style={{ fontWeight: 700, fontSize: 14 }}>Paso no aplicable</div>
                  <div className="muted" style={{ fontSize: 12, marginTop: 4 }}>
                    Avanza al paso de <b>Certificado del proveedor</b> para cargar el PDF emitido y los datos clave.
                  </div>
                </div>
              </div>
            </div>
          )}

          {/* ───── Step 2 · Asistencia (sesión interna) ───── */}
          {step === 2 && !isExt && (
            <div className="ic-cap-section">
              <h3 className="ic-cap-h3">Lista de asistencia</h3>
              <p className="muted" style={{ fontSize: 13, marginTop: -4 }}>
                Marca presencia, envía el código por email/SMS y confirma la firma electrónica de cada asistente. <b>Cada presente debe firmar.</b>
              </p>
              <div className="ic-cap-att-bar">
                <div className="ic-cap-att-stat"><b className="mono">{presentCount}</b> <span className="muted">de {attendance.length} presentes</span></div>
                <div className="ic-cap-att-stat"><b className="mono" style={{ color: 'var(--green-600)' }}>{signedCount}</b> <span className="muted">firmas verificadas</span></div>
                <div style={{ flex: 1 }} />
                <button className="btn" onClick={() => setAttendance(a => a.map(x => ({ ...x, present: true })))}>
                  Marcar todos presentes
                </button>
                <button className="btn primary" onClick={() => setAttendance(a => a.map(x => x.present ? { ...x, signed: true, code: '••••' } : x))}>
                  <IconShield size={13} /> Verificar OTP a todos los presentes
                </button>
              </div>

              <div className="ic-cap-att-list">
                <div className="ic-cap-att-head">
                  <div></div>
                  <div>Colaborador</div>
                  <div>Cargo</div>
                  <div>Canal de firma</div>
                  <div>Código OTP</div>
                  <div>Firma electrónica</div>
                </div>
                {attendance.map((a, i) => (
                  <div key={a.empId} className={`ic-cap-att-row ${a.present ? 'present' : ''} ${a.signed ? 'signed' : ''}`}>
                    <label className="ic-cap-att-check">
                      <input type="checkbox" checked={a.present} onChange={e => setAtt(i, { present: e.target.checked, signed: e.target.checked ? a.signed : false })} />
                    </label>
                    <div className="doc-cell-emp">
                      <div className="doc-avatar">{a.initials}</div>
                      <div>
                        <div style={{ fontWeight: 600, fontSize: 12.5 }}>{a.name}</div>
                        <div className="muted mono" style={{ fontSize: 10.5 }}>{a.empId}</div>
                      </div>
                    </div>
                    <div className="muted" style={{ fontSize: 12 }}>{a.role}</div>
                    <div>
                      <div className="ic-cap-channel">
                        <button className={a.channel === 'email' ? 'active' : ''} onClick={() => setAtt(i, { channel: 'email' })} disabled={!a.present}>email</button>
                        <button className={a.channel === 'sms' ? 'active' : ''}   onClick={() => setAtt(i, { channel: 'sms' })}   disabled={!a.present}>SMS</button>
                      </div>
                    </div>
                    <div>
                      {a.signed
                        ? <span className="mono" style={{ fontSize: 12 }}>{a.code}</span>
                        : a.present
                          ? <button className="btn" style={{ padding: '4px 10px', fontSize: 11.5 }} onClick={() => setAtt(i, { code: '——' })}>Enviar OTP</button>
                          : <span className="muted" style={{ fontSize: 11 }}>—</span>
                      }
                    </div>
                    <div>
                      {a.signed ? (
                        <span className="ic-cap-signed-tag"><IconCheck size={11} /> Firmado · {a.channel}</span>
                      ) : a.present ? (
                        <button className="btn primary" style={{ padding: '4px 10px', fontSize: 11.5 }} onClick={() => setAtt(i, { signed: true, code: '••••' })}>
                          Confirmar OTP
                        </button>
                      ) : (
                        <span className="muted" style={{ fontSize: 11 }}>Ausente</span>
                      )}
                    </div>
                  </div>
                ))}
              </div>

              <div className="ic-cap-info">
                <IconShield size={13} />
                <span>El colaborador recibe un código por <b>{attendance[0]?.channel === 'sms' ? 'SMS' : 'email'}</b>; al ingresarlo confirma su asistencia con firma electrónica conforme a Ley 527 de 1999.</span>
              </div>
            </div>
          )}

          {/* ───── Step 3 · Evaluación (sesión externa: no aplica) ───── */}
          {step === 3 && isExt && (
            <div className="ic-cap-section">
              <h3 className="ic-cap-h3">Evaluación · aplicada por el proveedor</h3>
              <p className="muted" style={{ fontSize: 13, marginTop: -4 }}>El resultado de la evaluación viene reflejado en el certificado que emite <b>{proveedorRecord?.razonSocial || session.instructorOrg}</b>. Atica no replica el examen.</p>
              <div className="ic-cap-ext-skip">
                <IconStar size={36} />
                <div>
                  <div style={{ fontWeight: 700, fontSize: 14 }}>Paso no aplicable</div>
                  <div className="muted" style={{ fontSize: 12, marginTop: 4 }}>
                    El certificado del proveedor es la verdad sobre la nota obtenida. Continúa al siguiente paso.
                  </div>
                </div>
              </div>
            </div>
          )}

          {/* ───── Step 3 · Evaluación (sesión interna) ───── */}
          {step === 3 && !isExt && (
            <div className="ic-cap-section">
              <h3 className="ic-cap-h3">Evaluación · {PREGUNTAS_EFECTIVAS.length} preguntas</h3>
              <p className="muted" style={{ fontSize: 13, marginTop: -4 }}>
                Captura las respuestas de cada asistente. La nota se calcula automáticamente.
                Aprobación requerida: <b>{session.minScore}/100</b>.
                {evaluacionCurso && <> · Modo <b>{evaluacionCurso.modo === 'pool-aleatorio' ? 'pool aleatorio' : 'preguntas fijas'}</b></>}
              </p>

              <div className="ic-cap-eval-grid">
                <div className="ic-cap-eval-questions">
                  <div className="muted-xs" style={{ marginBottom: 8 }}>
                    {evaluacionCurso
                      ? `Evaluación ${evaluacionCurso.id} · ${evaluacionCurso.modo === 'pool-aleatorio' ? 'set aleatorio' : 'preguntas fijas'} · ${PREGUNTAS_EFECTIVAS.length} preguntas`
                      : `Banco de preguntas · banco anclado a versión ${version}`}
                  </div>
                  {PREGUNTAS_EFECTIVAS.map((q, idx) => (
                    <div key={q.id} className="ic-cap-q">
                      <div className="ic-cap-q-num">{idx + 1}</div>
                      <div style={{ flex: 1 }}>
                        <div className="ic-cap-q-text">{q.text}</div>
                        <ol className="ic-cap-q-opts">
                          {q.options.map((o, oi) => (
                            <li key={oi} className={oi === q.correct ? 'correct' : ''}>{o}</li>
                          ))}
                        </ol>
                      </div>
                    </div>
                  ))}
                </div>

                <div className="ic-cap-eval-side">
                  <div className="muted-xs" style={{ marginBottom: 8 }}>Captura de respuestas por asistente</div>
                  {attendance.filter(a => a.present).map(a => {
                    const score = scoreFor(a.empId);
                    const answered = Object.keys(responses[a.empId] || {}).length;
                    const passed = answered === PREGUNTAS_EFECTIVAS.length && score >= session.minScore;
                    const failed = answered === PREGUNTAS_EFECTIVAS.length && score < session.minScore;
                    return (
                      <div key={a.empId} className="ic-cap-eval-att">
                        <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                          <div className="doc-avatar" style={{ width: 26, height: 26, fontSize: 10 }}>{a.initials}</div>
                          <div style={{ flex: 1, minWidth: 0 }}>
                            <div style={{ fontWeight: 600, fontSize: 12 }}>{a.name}</div>
                            <div className="muted" style={{ fontSize: 10.5 }}>{answered} / {PREGUNTAS_EFECTIVAS.length} respondidas</div>
                          </div>
                          {answered === PREGUNTAS_EFECTIVAS.length && (
                            <span className={`ic-cap-eval-score ${passed ? 'pass' : 'fail'}`}>{score}/100</span>
                          )}
                        </div>
                        <div className="ic-cap-eval-mini">
                          {PREGUNTAS_EFECTIVAS.map(q => (
                            <div key={q.id} className="ic-cap-eval-q-mini">
                              <span className="muted mono" style={{ fontSize: 10 }}>{q.id}</span>
                              <div className="ic-cap-eval-mini-opts">
                                {[0, 1, 2, 3].map(oi => (
                                  <button
                                    key={oi}
                                    className={`${responses[a.empId]?.[q.id] === oi ? 'picked' : ''} ${responses[a.empId]?.[q.id] === oi && oi === q.correct ? 'correct' : ''} ${responses[a.empId]?.[q.id] === oi && oi !== q.correct ? 'wrong' : ''}`}
                                    onClick={() => setResp(a.empId, q.id, oi)}
                                  >
                                    {String.fromCharCode(65 + oi)}
                                  </button>
                                ))}
                              </div>
                            </div>
                          ))}
                        </div>
                      </div>
                    );
                  })}
                  {presentCount === 0 && (
                    <div className="muted" style={{ fontSize: 12, textAlign: 'center', padding: 24 }}>
                      No hay asistentes marcados como presentes. Vuelve al paso 2.
                    </div>
                  )}
                </div>
              </div>
            </div>
          )}

          {/* ───── Step 4 · Certificado del proveedor (sesión externa) ───── */}
          {step === 4 && isExt && (
            <div className="ic-cap-section">
              <h3 className="ic-cap-h3">Certificado emitido por el proveedor</h3>
              <p className="muted" style={{ fontSize: 13, marginTop: -4 }}>
                Carga el PDF y captura los datos clave del certificado emitido por <b>{proveedorRecord?.razonSocial || session.instructorOrg}</b>. Estos datos quedarán archivados en la hoja de vida de los colaboradores aprobados.
              </p>

              <div className="ic-cap-ext-form">
                <div className="ic-cap-field">
                  <div className="muted-xs">Código del certificado emisor</div>
                  <input
                    type="text"
                    placeholder="Ej. SENA-2026-CCS-04821"
                    value={extCert.codigo}
                    onChange={e => setExtCert({ ...extCert, codigo: e.target.value })}
                    className="ic-cap-version"
                  />
                  <div className="muted" style={{ fontSize: 11, marginTop: 3 }}>El número o código que asigna el proveedor.</div>
                </div>

                <div className="ic-cap-field">
                  <div className="muted-xs">Vigencia · desde</div>
                  <window.DatePickerCO
                    value={extCert.vigenciaFrom}
                    onChange={(v) => setExtCert({ ...extCert, vigenciaFrom: v })}
                  />
                </div>

                <div className="ic-cap-field">
                  <div className="muted-xs">Vigencia · hasta</div>
                  <window.DatePickerCO
                    value={extCert.vigenciaTo}
                    onChange={(v) => setExtCert({ ...extCert, vigenciaTo: v })}
                    min={extCert.vigenciaFrom}
                    presets={[
                      { label: 'En 1 año',  compute: (t) => { const d = new Date(t); d.setFullYear(d.getFullYear() + 1); return d; } },
                      { label: 'En 2 años', compute: (t) => { const d = new Date(t); d.setFullYear(d.getFullYear() + 2); return d; } },
                      { label: 'En 3 años', compute: (t) => { const d = new Date(t); d.setFullYear(d.getFullYear() + 3); return d; } },
                    ]}
                  />
                  <div className="muted" style={{ fontSize: 11, marginTop: 3 }}>Según lo que dice el certificado del proveedor.</div>
                </div>

                <div className="ic-cap-field">
                  <div className="muted-xs">Emitido por</div>
                  <div className="ic-cap-field-val">
                    {proveedorRecord?.razonSocial || session.instructorOrg}
                    {proveedorRecord && (
                      <div className="muted mono" style={{ fontSize: 11 }}>NIT {proveedorRecord.nit}</div>
                    )}
                  </div>
                </div>

                <div className="ic-cap-field" style={{ gridColumn: '1 / -1' }}>
                  <div className="muted-xs">PDF del certificado</div>
                  {extCert.archivoNombre ? (
                    <div className={`ic-cap-ev ic-cap-ev-acta`}>
                      <div className="ic-cap-ev-icon"><IconDoc size={20} /></div>
                      <div style={{ flex: 1, minWidth: 0 }}>
                        <div className="ic-cap-ev-name">{extCert.archivoNombre}</div>
                        <div className="muted mono" style={{ fontSize: 11 }}>Certificado externo · emitido por {proveedorRecord?.razonSocial || session.instructorOrg}</div>
                      </div>
                      <button className="icon-action" title="Quitar" onClick={() => setExtCert({ ...extCert, archivoNombre: '' })}>
                        <IconAlert size={13} />
                      </button>
                    </div>
                  ) : (
                    <button
                      className="ic-cap-ev-add"
                      onClick={() => setExtCert({ ...extCert, archivoNombre: `cert-${(proveedorRecord?.razonSocial || 'externo').toLowerCase().replace(/\s+/g, '-').slice(0,20)}-${session.id.slice(-4)}.pdf` })}
                      style={{ minHeight: 100 }}
                    >
                      <IconPlus size={18} />
                      <div className="muted" style={{ fontSize: 12, fontWeight: 600 }}>Cargar PDF emitido por el proveedor</div>
                      <div className="muted" style={{ fontSize: 11 }}>El certificado original será archivado y consultable.</div>
                    </button>
                  )}
                </div>
              </div>

              <div className="ic-cap-info" style={{ marginTop: 14 }}>
                <IconShield size={13} />
                <span>Al cerrar la sesión se archivará el certificado en la hoja de vida de los colaboradores inscritos. La verificación de idoneidad ya se hizo al programar la sesión con el proveedor.</span>
              </div>
            </div>
          )}

          {/* ───── Step 4 · Evidencias (sesión interna) ───── */}
          {step === 4 && !isExt && (
            <div className="ic-cap-section">
              <h3 className="ic-cap-h3">Evidencias · obligatorio para cierre</h3>
              <p className="muted" style={{ fontSize: 13, marginTop: -4 }}>
                Carga al menos un <b>acta del instructor firmada</b>. Se recomienda agregar foto del aula, lista escaneada de asistencia y material entregado.
              </p>

              <div className="ic-cap-ev-required">
                <div className={`ic-cap-ev-req ${requiredEvidence ? 'ok' : ''}`}>
                  {requiredEvidence ? <IconCheck size={13} /> : <IconAlert size={13} />}
                  Acta del instructor firmada {requiredEvidence ? '· cargada' : '· falta'}
                </div>
                <div className={`ic-cap-ev-req ${evidencesCount >= 2 ? 'ok' : ''}`}>
                  {evidencesCount >= 2 ? <IconCheck size={13} /> : <IconAlert size={13} />}
                  Mínimo 2 evidencias adjuntas {evidencesCount >= 2 ? `· ${evidencesCount} cargadas` : `· ${evidencesCount} cargada${evidencesCount === 1 ? '' : 's'}`}
                </div>
              </div>

              <div className="ic-cap-ev-grid">
                {evidences.map(e => (
                  <div key={e.id} className={`ic-cap-ev ic-cap-ev-${e.kind}`}>
                    <div className="ic-cap-ev-icon"><IconDoc size={20} /></div>
                    <div style={{ flex: 1, minWidth: 0 }}>
                      <div className="ic-cap-ev-name">{e.name}</div>
                      <div className="muted mono" style={{ fontSize: 11 }}>{e.size} KB · {e.kind === 'acta' ? 'Acta firmada' : e.kind === 'foto' ? 'Foto' : e.kind === 'lista' ? 'Lista escaneada' : 'Material'}</div>
                    </div>
                    <button className="icon-action" title="Quitar" onClick={() => setEvidences(es => es.filter(x => x.id !== e.id))}>
                      <IconAlert size={13} />
                    </button>
                  </div>
                ))}

                {[
                  { kind: 'foto',     label: 'Foto del aula / capacitación', icon: 'IconExternal' },
                  { kind: 'lista',    label: 'Lista de asistencia escaneada', icon: 'IconDoc' },
                  { kind: 'material', label: 'Material entregado', icon: 'IconBook' },
                ].filter(t => !evidences.some(e => e.kind === t.kind)).map(t => (
                  <button key={t.kind} className="ic-cap-ev-add" onClick={() => setEvidences(es => [...es, { id: 'ev-' + Date.now(), name: `${t.kind}-sesion-${session.id.slice(-4)}.pdf`, size: 180 + Math.floor(Math.random() * 800), kind: t.kind, uploaded: true }])}>
                    <IconPlus size={14} />
                    <div className="muted" style={{ fontSize: 11.5, fontWeight: 600 }}>{t.label}</div>
                    <div className="muted" style={{ fontSize: 10.5 }}>Arrastra o haz clic</div>
                  </button>
                ))}
              </div>
            </div>
          )}

          {/* ───── Step 5 · Resultados (sesión externa) ───── */}
          {step === 5 && isExt && (
            <div className="ic-cap-section">
              <h3 className="ic-cap-h3">Resumen del certificado externo</h3>
              <p className="muted" style={{ fontSize: 13, marginTop: -4 }}>
                Estos son los datos que quedarán archivados. Al cerrar, el certificado se vinculará a la hoja de vida de cada colaborador inscrito.
              </p>

              <div className="ic-cap-ext-summary">
                <div className="ic-cap-ext-summary-grid">
                  <div><div className="muted-xs">Código</div><div className="mono" style={{ fontWeight: 700 }}>{extCert.codigo || '—'}</div></div>
                  <div><div className="muted-xs">Emitido por</div><div style={{ fontWeight: 600 }}>{proveedorRecord?.razonSocial || session.instructorOrg}</div></div>
                  <div><div className="muted-xs">Vigencia</div><div className="mono">{extCert.vigenciaFrom} → {extCert.vigenciaTo}</div></div>
                  <div><div className="muted-xs">Competencia cubierta</div><div>{getCmp(session.competenciaId)?.name || session.competenciaId}</div></div>
                  <div><div className="muted-xs">PDF</div><div className="mono" style={{ fontSize: 11.5 }}>{extCert.archivoNombre || '—'}</div></div>
                  <div><div className="muted-xs">Norma origen</div><div style={{ fontSize: 12 }}>{getCmp(session.competenciaId)?.norma || '—'}</div></div>
                </div>
              </div>

              <h4 className="ic-cap-h3" style={{ fontSize: 14, marginTop: 18 }}>Colaboradores que recibirán el certificado · {(session.enrolled || []).length}</h4>
              <div className="ic-cap-res-list">
                <div className="ic-cap-res-head" style={{ gridTemplateColumns: '2.4fr 1.6fr 1.4fr 1fr' }}>
                  <div>Colaborador</div>
                  <div>Cargo</div>
                  <div>Vigencia</div>
                  <div>Estado</div>
                </div>
                {(session.enrolled || []).map(a => (
                  <div key={a.empId} className="ic-cap-res-row pass" style={{ gridTemplateColumns: '2.4fr 1.6fr 1.4fr 1fr' }}>
                    <div className="doc-cell-emp">
                      <div className="doc-avatar">{a.initials}</div>
                      <div>
                        <div style={{ fontWeight: 600, fontSize: 12.5 }}>{a.name}</div>
                        <div className="muted mono" style={{ fontSize: 10.5 }}>{a.empId}</div>
                      </div>
                    </div>
                    <div className="muted" style={{ fontSize: 12 }}>{a.role}</div>
                    <div className="mono" style={{ fontSize: 12 }}>{extCert.vigenciaTo || '—'}</div>
                    <div>
                      <span className="pill green" style={{ fontSize: 11 }}>
                        <IconCheck size={10} /> Archivar
                      </span>
                    </div>
                  </div>
                ))}
              </div>
            </div>
          )}

          {/* ───── Step 5 · Resultados (sesión interna) ───── */}
          {step === 5 && !isExt && (
            <div className="ic-cap-section">
              <h3 className="ic-cap-h3">Resultados individuales</h3>
              <p className="muted" style={{ fontSize: 13, marginTop: -4 }}>
                Cálculo automático: aprobado = nota ≥ {session.minScore}. Puedes agregar comentarios del instructor antes del cierre.
              </p>

              <div className="ic-cap-res-summary">
                <div><div className="muted-xs">Inscritos</div><div className="ic-cap-res-num">{attendance.length}</div></div>
                <div><div className="muted-xs">Presentes</div><div className="ic-cap-res-num">{presentCount}</div></div>
                <div><div className="muted-xs">Firmaron</div><div className="ic-cap-res-num">{signedCount}</div></div>
                <div><div className="muted-xs">Aprobaron</div><div className="ic-cap-res-num pos">{attendance.filter(a => a.present && scoreFor(a.empId) >= session.minScore).length}</div></div>
                <div><div className="muted-xs">No aprobaron</div><div className="ic-cap-res-num neg">{attendance.filter(a => a.present && scoreFor(a.empId) < session.minScore).length}</div></div>
              </div>

              <div className="ic-cap-res-list">
                <div className="ic-cap-res-head">
                  <div>Colaborador</div>
                  <div>Asistencia</div>
                  <div>Nota</div>
                  <div>Resultado</div>
                  <div>Comentarios del instructor</div>
                  <div>Certificado</div>
                </div>
                {attendance.map(a => {
                  const score = a.present ? scoreFor(a.empId) : null;
                  const pass = a.present && score >= session.minScore;
                  return (
                    <div key={a.empId} className={`ic-cap-res-row ${pass ? 'pass' : a.present ? 'fail' : 'absent'}`}>
                      <div className="doc-cell-emp">
                        <div className="doc-avatar">{a.initials}</div>
                        <div>
                          <div style={{ fontWeight: 600, fontSize: 12.5 }}>{a.name}</div>
                          <div className="muted mono" style={{ fontSize: 10.5 }}>{a.empId}</div>
                        </div>
                      </div>
                      <div>
                        {a.present
                          ? <span className="pill green" style={{ fontSize: 11 }}><IconCheck size={10} /> Asistió{a.signed && ' + firmó'}</span>
                          : <span className="pill rose" style={{ fontSize: 11 }}>Ausente</span>}
                      </div>
                      <div className="mono" style={{ fontWeight: 700, fontSize: 14 }}>
                        {score !== null ? `${score}/100` : '—'}
                      </div>
                      <div>
                        {a.present
                          ? <span className={`pill ${pass ? 'green' : 'rose'}`}>{pass ? 'Aprobado' : 'No aprobado'}</span>
                          : <span className="muted" style={{ fontSize: 11.5 }}>N/A</span>}
                      </div>
                      <div>
                        <input
                          type="text"
                          className="ic-cap-res-comment"
                          placeholder={pass ? 'Buen desempeño' : 'Justificar reasignación…'}
                          value={resultComments[a.empId] || ''}
                          onChange={e => setResultComments(c => ({ ...c, [a.empId]: e.target.value }))}
                        />
                      </div>
                      <div>
                        {pass
                          ? <span className="muted mono" style={{ fontSize: 11 }}>Se emitirá al cerrar</span>
                          : <span className="muted" style={{ fontSize: 11 }}>—</span>}
                      </div>
                    </div>
                  );
                })}
              </div>
            </div>
          )}

          {/* ───── Step 6 · Cierre ───── */}
          {step === 6 && (
            <div className="ic-cap-section">
              {!closed ? (
                <>
                  <h3 className="ic-cap-h3">{isExt ? 'Archivar certificado externo' : 'Cierre de la sesión'}</h3>
                  <p className="muted" style={{ fontSize: 13, marginTop: -4 }}>
                    {isExt
                      ? <>Al confirmar se archivará el certificado emitido por <b>{proveedorRecord?.razonSocial || session.instructorOrg}</b> y se vinculará a la hoja de vida de los <b>{(session.enrolled || []).length} colaboradores inscritos</b>. El registro queda <b>inmutable</b>.</>
                      : <>Revisa el resumen final. Al cerrar se generará el <b>hash criptográfico del expediente</b> (asistencia + evaluación + evidencias + versión), se emitirán los certificados a los aprobados, y el registro queda <b>inmutable</b>.</>
                    }
                  </p>

                  <div className="ic-cap-close-summary">
                    <div className="ic-cap-close-grid">
                      {isExt ? (
                        <>
                          <div className="ic-cap-close-item ok">
                            <IconCheck size={14} /> Idoneidad del proveedor
                            <div className="muted" style={{ fontSize: 11 }}>{proveedorRecord?.razonSocial || session.instructorOrg}</div>
                          </div>
                          <div className="ic-cap-close-item ok">
                            <IconCheck size={14} /> Datos del certificado
                            <div className="muted mono" style={{ fontSize: 11 }}>{extCert.codigo}</div>
                          </div>
                          <div className="ic-cap-close-item ok">
                            <IconCheck size={14} /> PDF cargado
                            <div className="muted mono" style={{ fontSize: 11 }}>{extCert.archivoNombre}</div>
                          </div>
                          <div className="ic-cap-close-item ok">
                            <IconCheck size={14} /> Vigencia
                            <div className="muted mono" style={{ fontSize: 11 }}>{extCert.vigenciaFrom} → {extCert.vigenciaTo}</div>
                          </div>
                        </>
                      ) : (
                        <>
                          <div className="ic-cap-close-item ok">
                            <IconCheck size={14} /> Asistencia capturada
                            <div className="muted" style={{ fontSize: 11 }}>{presentCount} presentes · {signedCount} firmaron OTP</div>
                          </div>
                          <div className="ic-cap-close-item ok">
                            <IconCheck size={14} /> Evaluación aplicada
                            <div className="muted" style={{ fontSize: 11 }}>{evaluatedCount} respondieron las {PREGUNTAS_EFECTIVAS.length} preguntas</div>
                          </div>
                          <div className="ic-cap-close-item ok">
                            <IconCheck size={14} /> Evidencias cargadas
                            <div className="muted" style={{ fontSize: 11 }}>{evidencesCount} archivos · acta firmada incluida</div>
                          </div>
                          <div className="ic-cap-close-item ok">
                            <IconCheck size={14} /> Resultados consolidados
                            <div className="muted" style={{ fontSize: 11 }}>{attendance.filter(a => a.present && scoreFor(a.empId) >= session.minScore).length} aprobaron · {attendance.filter(a => a.present && scoreFor(a.empId) < session.minScore).length} no</div>
                          </div>
                        </>
                      )}
                    </div>

                    <div className="ic-cap-close-warn">
                      <IconShield size={14} />
                      <div>
                        <div style={{ fontWeight: 700, fontSize: 13, color: 'var(--rose-600)' }}>{isExt ? 'Archivado irreversible' : 'Cierre irreversible'}</div>
                        <div className="muted" style={{ fontSize: 12, marginTop: 2, lineHeight: 1.5 }}>
                          {isExt
                            ? 'El certificado externo queda vinculado a los colaboradores inscritos y aparecerá en su hoja de vida. Para corregir errores se anula y se carga uno nuevo.'
                            : <>La sesión queda anclada a la versión <b>{version}</b> del contenido. No podrá editarse después. Para corregir errores se anula y se programa una nueva.</>
                          }
                        </div>
                      </div>
                    </div>

                    <button className="btn primary" style={{ width: '100%', padding: '14px 18px', fontSize: 14, marginTop: 14 }} onClick={closeSession} disabled={closing}>
                      {closing
                        ? <><span className="sig-spinner" /> {isExt ? 'Archivando certificado…' : 'Generando hash y certificados…'}</>
                        : <><IconShield size={14} /> {isExt ? 'Archivar certificado y vincular a colaboradores' : 'Cerrar sesión y emitir certificados'}</>
                      }
                    </button>
                  </div>
                </>
              ) : (
                <div className="ic-cap-closed">
                  <div className="mi-stamp">
                    <div className="mi-stamp-mark">{isExt ? 'ARCHIVADO' : 'CERRADA'}<span>{session.date}</span></div>
                  </div>
                  <h3 className="ic-cap-h3" style={{ textAlign: 'center', marginTop: 18 }}>
                    {isExt ? 'Certificado externo archivado' : 'Sesión cerrada e inmutable'}
                  </h3>
                  <p className="muted" style={{ fontSize: 13, textAlign: 'center', maxWidth: 520, margin: '0 auto' }}>
                    {isExt
                      ? <>El certificado emitido por <b>{proveedorRecord?.razonSocial || session.instructorOrg}</b> quedó vinculado a los <b>{(session.enrolled || []).length}</b> colaboradores inscritos. Aparecerá en su hoja de vida con el badge "Emitido por {proveedorRecord?.razonSocial?.split('·')[0]?.trim() || 'tercero'}".</>
                      : 'El expediente quedó archivado con su hash criptográfico. Los certificados fueron emitidos automáticamente y enviados al portal de cada colaborador aprobado.'
                    }
                  </p>
                  <div className="ic-cap-closed-evidence">
                    <div className="ic-cap-closed-row"><span className="muted-xs">Sesión</span><span className="mono">{session.id}</span></div>
                    <div className="ic-cap-closed-row"><span className="muted-xs">{isExt ? 'Archivada el' : 'Cerrada el'}</span><span className="mono">{new Date().toLocaleString('es-CO')}</span></div>
                    {isExt ? (
                      <>
                        <div className="ic-cap-closed-row"><span className="muted-xs">Código del certificado</span><span className="mono">{extCert.codigo}</span></div>
                        <div className="ic-cap-closed-row"><span className="muted-xs">Vigencia</span><span className="mono">{extCert.vigenciaFrom} → {extCert.vigenciaTo}</span></div>
                        <div className="ic-cap-closed-row"><span className="muted-xs">Emitido por</span><span>{proveedorRecord?.razonSocial || session.instructorOrg}</span></div>
                        <div className="ic-cap-closed-row"><span className="muted-xs">Vinculado a</span><span>{(session.enrolled || []).length} colaboradores</span></div>
                      </>
                    ) : (
                      <>
                        <div className="ic-cap-closed-row"><span className="muted-xs">Versión aplicada</span><span className="mono">{version}</span></div>
                        <div className="ic-cap-closed-row"><span className="muted-xs">Hash SHA-256</span><span className="mono" style={{ fontSize: 10.5 }}>a4f2c1e89b7d3f5e26c8d4b1a9f3e7c5d8b2a4f6e9c1d3b5a7f8e0c2d4b6a9f1</span></div>
                        <div className="ic-cap-closed-row"><span className="muted-xs">Certificados</span><span>{attendance.filter(a => a.present && scoreFor(a.empId) >= session.minScore).length} emitidos</span></div>
                      </>
                    )}
                  </div>
                  <div style={{ display: 'flex', gap: 8, justifyContent: 'center' }}>
                    <button className="btn"><IconExternal size={13} /> Ver expediente</button>
                    <button className="btn primary" onClick={onClose}>Volver a sesiones</button>
                  </div>
                </div>
              )}
            </div>
          )}
        </div>

        {/* Foot · validación + nav */}
        {!closed && (
          <div className="modal-foot ic-cap-foot">
            <div className={`ic-cap-validation ${stepValidation[step].ok ? 'ok' : 'warn'}`}>
              {stepValidation[step].ok ? <IconCheck size={13} /> : <IconAlert size={13} />}
              <span>{stepValidation[step].msg}</span>
            </div>
            <div style={{ flex: 1 }} />
            {step > 1 && step < 6 && (
              <button className="btn" onClick={() => setStep(step - 1)} disabled={closing}>
                <IconChevronLeft size={13} /> Volver
              </button>
            )}
            {step < 5 && (
              <button className="btn primary" onClick={() => setStep(step + 1)} disabled={!canAdvance[step]}>
                Continuar <IconChevronRight size={13} />
              </button>
            )}
            {step === 5 && (
              <button className="btn primary" onClick={() => setStep(6)}>
                <IconShield size={13} /> Ir al cierre
              </button>
            )}
          </div>
        )}
      </div>
    </div>
  );
};

// ═══════════════════════════════════════════════════════════
// Profile section · "Competencias y Formación" para ficha
// ═══════════════════════════════════════════════════════════

const TrainingProfile = ({ empId = 'E0205', cargo }) => {
  const data = COMPETENCIAS_BY_EMP[empId] || COMPETENCIAS_BY_EMP['E0205'];

  // Si recibimos el cargo, las competencias requeridas vienen de la matriz real (Fase C).
  // Si no, fallback al mock embebido en data.required (compat hacia atrás).
  const matrizPorCargo = cargo && window.competenciasPorCargo ? window.competenciasPorCargo(cargo) : [];
  const requiredIds = matrizPorCargo.length > 0
    ? matrizPorCargo.filter(m => m.obligatoria).map(m => m.competenciaId)
    : data.required;
  const requiredCmps = requiredIds.map(id => getCmp(id)).filter(Boolean);
  const completedMap = Object.fromEntries(data.completed.map(c => [c.cmpId, c]));
  const inProgressMap = Object.fromEntries(data.inProgress.map(c => [c.cmpId, c]));
  const expiredMap = Object.fromEntries(data.expired.map(c => [c.cmpId, c]));

  const totalReq = requiredCmps.length;
  const completedCount = data.completed.filter(c => requiredCmps.some(r => r.id === c.cmpId)).length;
  const expiredCount   = data.expired.filter(c => requiredCmps.some(r => r.id === c.cmpId)).length;
  const inProgressCount = data.inProgress.length;
  const pendingCount   = totalReq - completedCount - inProgressCount - expiredCount;
  const compliancePct  = totalReq ? Math.round((completedCount / totalReq) * 100) : 0;

  // Build inmutable timeline (sorted desc)
  const timeline = [
    ...data.completed.map(c => ({ kind: 'completed', cmpId: c.cmpId, date: c.date, score: c.score, sessionId: c.sessionId, version: c.version, certId: c.certId })),
    ...data.inProgress.map(c => ({ kind: 'inProgress', cmpId: c.cmpId, date: c.date, sessionId: c.sessionId, progress: c.progress })),
    ...data.expired.map(c => ({ kind: 'expired', cmpId: c.cmpId, date: c.date, sessionId: c.sessionId, daysExpired: c.daysExpired, newSessionId: c.newSessionId })),
  ].sort((a, b) => {
    const [da, ma, ya] = a.date.split('/').map(Number);
    const [db, mb, yb] = b.date.split('/').map(Number);
    return new Date(yb, mb - 1, db) - new Date(ya, ma - 1, da);
  });

  return (
    <div className="ic-prof">
      {/* ── Banda de cumplimiento ── */}
      <div className="ic-prof-hero">
        <div className="ic-prof-donut">
          <ComplianceDonut pct={compliancePct} />
        </div>
        <div className="ic-prof-stats">
          <div className="ic-prof-stat">
            <div className="ic-prof-stat-num">{totalReq}</div>
            <div className="ic-prof-stat-lbl">Competencias requeridas por cargo</div>
          </div>
          <div className="ic-prof-stat green">
            <div className="ic-prof-stat-num">{completedCount}</div>
            <div className="ic-prof-stat-lbl">Vigentes</div>
          </div>
          <div className="ic-prof-stat amber">
            <div className="ic-prof-stat-num">{inProgressCount}</div>
            <div className="ic-prof-stat-lbl">En curso</div>
          </div>
          <div className="ic-prof-stat rose">
            <div className="ic-prof-stat-num">{expiredCount + pendingCount}</div>
            <div className="ic-prof-stat-lbl">Vencidas o pendientes</div>
          </div>
        </div>
        <div className="ic-prof-actions">
          <button className="btn primary"><IconPlus size={13} /> Asignar capacitación</button>
          <button className="btn"><IconDoc size={13} /> Exportar historial PDF</button>
        </div>
      </div>

      {/* ── Matriz de competencias ── */}
      <div className="ic-prof-section">
        <div className="ic-prof-section-head">
          <h3>Matriz de competencias del cargo</h3>
          <span className="muted" style={{ fontSize: 12 }}>Lo que <b>Operario de Clasificación</b> debe demostrar</span>
        </div>
        <div className="ic-prof-cmp-grid">
          {requiredCmps.map(c => {
            const cat = CMP_CAT[c.cat];
            const completed = completedMap[c.id];
            const inProg    = inProgressMap[c.id];
            const expired   = expiredMap[c.id];
            const status = completed ? 'vigente' : inProg ? 'curso' : expired ? 'vencida' : 'pendiente';
            return (
              <div key={c.id} className={`ic-prof-cmp ic-prof-cmp-${status}`}>
                <div className="ic-prof-cmp-status">
                  {status === 'vigente'    && <><IconCheck size={11} /> Vigente</>}
                  {status === 'curso'      && <><IconClock size={11} /> En curso</>}
                  {status === 'vencida'    && <><IconAlert size={11} /> Vencida</>}
                  {status === 'pendiente'  && <><IconClock size={11} /> Pendiente</>}
                </div>
                <div className="muted mono" style={{ fontSize: 10 }}>{c.id}</div>
                <div className="ic-prof-cmp-name">{c.name}</div>
                <span className={`tr-cat-badge ${cat.tone}`} style={{ fontSize: 10 }}>{cat.label}</span>
                {completed && (
                  <div className="ic-prof-cmp-foot">
                    <span className="mono">Nota {completed.score}/100</span>
                    <span className="muted mono" style={{ fontSize: 10 }}>vence {completed.vigencia}</span>
                  </div>
                )}
                {expired && (
                  <div className="ic-prof-cmp-foot">
                    <span className="mono" style={{ color: 'var(--rose-600)' }}>vencida hace {expired.daysExpired}d</span>
                    <span className="muted mono" style={{ fontSize: 10 }}>reasignada</span>
                  </div>
                )}
                {inProg && (
                  <div className="ic-prof-cmp-foot">
                    <span className="mono" style={{ color: 'var(--amber-600)' }}>{inProg.progress}% de avance</span>
                  </div>
                )}
              </div>
            );
          })}
        </div>
      </div>

      {/* ── Capacitaciones realizadas ── */}
      <div className="ic-prof-section">
        <div className="ic-prof-section-head">
          <h3>Capacitaciones realizadas · {data.completed.length}</h3>
          <span className="muted" style={{ fontSize: 12 }}>Histórico con nota, certificado y evidencias</span>
        </div>
        <div className="doc-list-card">
          <table className="doc-table">
            <thead>
              <tr>
                <th>Competencia</th>
                <th>Curso · sesión</th>
                <th style={{ textAlign: 'center' }}>Fecha</th>
                <th style={{ textAlign: 'center' }}>Nota</th>
                <th style={{ textAlign: 'center' }}>Versión</th>
                <th>Vigencia</th>
                <th>Evidencias</th>
                <th></th>
              </tr>
            </thead>
            <tbody>
              {data.completed.map((c, i) => {
                const cmp = getCmp(c.cmpId);
                return (
                  <tr key={i}>
                    <td data-label="Competencia">
                      <div style={{ fontWeight: 600, fontSize: 12.5 }}>{cmp?.name || c.cmpId}</div>
                      <div className="muted mono" style={{ fontSize: 10.5 }}>{c.cmpId}</div>
                    </td>
                    <td data-label="Curso" className="muted" style={{ fontSize: 12 }}>
                      <span className="mono">{c.courseId}</span> · <span className="mono">{c.sessionId}</span>
                    </td>
                    <td data-label="Fecha" className="mono" style={{ textAlign: 'center', fontSize: 12 }}>{c.date}</td>
                    <td data-label="Nota" style={{ textAlign: 'center' }}>
                      <span className={`mono ${c.score >= 90 ? 'ic-prof-score-top' : 'ic-prof-score-ok'}`} style={{ fontWeight: 800, fontSize: 14 }}>
                        {c.score}/100
                      </span>
                    </td>
                    <td data-label="Versión" className="mono" style={{ textAlign: 'center', fontSize: 11 }}>
                      <span className="pill navy" style={{ fontSize: 10.5 }}>{c.version}</span>
                    </td>
                    <td data-label="Vigencia" className="mono" style={{ fontSize: 11.5 }}>{c.vigencia}</td>
                    <td data-label="Evidencias">
                      <span className="ic-prof-evidence-tag">
                        {c.signedAttendance && <IconCheck size={10} />} {c.evidences} archivos
                      </span>
                    </td>
                    <td>
                      <div style={{ display: 'flex', gap: 4 }}>
                        <button className="icon-action" title="Ver certificado"><IconStar size={14} /></button>
                        <button className="icon-action" title="Ver expediente"><IconExternal size={14} /></button>
                      </div>
                    </td>
                  </tr>
                );
              })}
            </tbody>
          </table>
        </div>
      </div>

      {/* ── Certificados emitidos por Atica · interno + externo + autocertificado ── */}
      {(() => {
        const allCerts = window.CERTIFICATES || [];
        const empCerts = allCerts.filter(c => c.emp?.cargo === cargo);
        if (empCerts.length === 0) return null;
        const internos       = empCerts.filter(c => c.origen === 'interno').length;
        const externos       = empCerts.filter(c => c.origen === 'externo').length;
        const autocertificados = empCerts.filter(c => c.origen === 'externo-autocertificado').length;
        return (
          <div className="ic-prof-section">
            <div className="ic-prof-section-head">
              <h3>Certificados emitidos por Atica · {empCerts.length}</h3>
              <span className="muted" style={{ fontSize: 12 }}>
                {internos} internos · {externos} externos · {autocertificados} auto-certificados
              </span>
            </div>
            <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
              {empCerts.map(c => {
                const tone = c.origen === 'interno' ? 'green' : c.origen === 'externo' ? 'navy' : 'amber';
                const originLabel = c.origen === 'interno'
                  ? 'Capturado por Atica'
                  : c.origen === 'externo'
                    ? `Emitido por ${c.session?.instructorOrg || 'proveedor'}`
                    : `Auto-certificado · plataforma ${c.session?.site || 'externa'}`;
                const verUrl = (window.location?.pathname || '').replace(/[^/]*$/, '') + 'HCM Atica Verificar Certificado.html?codigo=' + encodeURIComponent(c.code);
                return (
                  <div key={c.code} style={{ display: 'flex', gap: 12, alignItems: 'flex-start', padding: 14, background: 'white', border: '1px solid var(--border)', borderRadius: 10 }}>
                    <div style={{ flexShrink: 0, padding: 10, background: tone === 'green' ? '#F0FDF4' : tone === 'navy' ? '#E5EBF7' : '#FEF9C3', borderRadius: 8 }}>
                      <IconShield size={18} />
                    </div>
                    <div style={{ flex: 1, minWidth: 0 }}>
                      <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap', marginBottom: 4 }}>
                        <span className="mono" style={{ fontSize: 10.5, color: 'var(--text-muted)' }}>{c.code}</span>
                        <span className={`pill ${tone}`} style={{ fontSize: 10 }}>{originLabel}</span>
                        {c.origen === 'externo-autocertificado' && (
                          <span className="pill amber-soft" style={{ fontSize: 10 }}>✓ declaración del colaborador</span>
                        )}
                      </div>
                      <div style={{ fontWeight: 700, fontSize: 13.5 }}>{c.course?.name}</div>
                      <div className="muted" style={{ fontSize: 11.5, marginTop: 2 }}>
                        {c.competencia?.norma}
                      </div>
                      <div style={{ display: 'flex', gap: 12, marginTop: 8, fontSize: 11.5 }}>
                        <span><span className="muted">Emitido:</span> <span className="mono">{c.result?.issuedAt?.split(',')[0] || c.session?.date}</span></span>
                        {c.result?.score !== null && c.result?.score !== undefined && (
                          <span><span className="muted">Nota:</span> <b className="mono" style={{ color: 'var(--green-600)' }}>{c.result.score}/100</b></span>
                        )}
                        <span><span className="muted">Vigencia:</span> <span className="mono">{c.vigencia?.from} → {c.vigencia?.to}</span></span>
                      </div>
                    </div>
                    <button className="btn" style={{ fontSize: 11.5 }} onClick={() => window.open(verUrl, '_blank')}>
                      <IconExternal size={12} /> Ver certificado
                    </button>
                  </div>
                );
              })}
            </div>
          </div>
        );
      })()}

      {/* ── Timeline inmutable ── */}
      <div className="ic-prof-section">
        <div className="ic-prof-section-head">
          <h3>Línea de tiempo inmutable · expediente del colaborador</h3>
          <span className="ic-prof-immut">
            <IconShield size={12} /> Append-only · sin edición · entregable a auditoría
          </span>
        </div>
        <div className="ic-prof-timeline">
          {timeline.map((t, i) => {
            const cmp = getCmp(t.cmpId);
            const cls = t.kind === 'completed' ? 'completed' : t.kind === 'expired' ? 'expired' : 'inProgress';
            return (
              <div key={i} className={`ic-prof-tl ic-prof-tl-${cls}`}>
                <div className="ic-prof-tl-rail">
                  <div className="ic-prof-tl-dot">
                    {t.kind === 'completed' ? <IconCheck size={11} /> : t.kind === 'expired' ? <IconAlert size={11} /> : <IconClock size={11} />}
                  </div>
                  {i < timeline.length - 1 && <div className="ic-prof-tl-line" />}
                </div>
                <div className="ic-prof-tl-body">
                  <div className="ic-prof-tl-head">
                    <div>
                      <div className="ic-prof-tl-label">
                        {t.kind === 'completed' && `Aprobó "${cmp?.name}" con ${t.score}/100`}
                        {t.kind === 'expired'   && `Capacitación "${cmp?.name}" venció — reasignada automáticamente`}
                        {t.kind === 'inProgress' && `En curso: "${cmp?.name}" (${t.progress}% de avance)`}
                      </div>
                      <div className="muted" style={{ fontSize: 11.5, marginTop: 2 }}>
                        Sesión <span className="mono">{t.sessionId}</span>
                        {t.version && <> · contenido versión <span className="mono">{t.version}</span></>}
                        {t.certId && <> · certificado <span className="mono">{t.certId}</span></>}
                        {t.newSessionId && <> · nueva <span className="mono">{t.newSessionId}</span></>}
                      </div>
                    </div>
                    <div className="muted mono" style={{ fontSize: 11, fontWeight: 700, color: 'var(--text)' }}>{t.date}</div>
                  </div>
                </div>
              </div>
            );
          })}
        </div>
      </div>
    </div>
  );
};

// ─── Compliance donut · used in profile hero ──────────────

const ComplianceDonut = ({ pct }) => {
  const r = 52, c = 2 * Math.PI * r;
  const dash = (pct / 100) * c;
  const stroke = pct >= 80 ? 'var(--green-600)' : pct >= 50 ? 'var(--amber-600)' : 'var(--rose-600)';
  return (
    <svg viewBox="0 0 140 140" className="ic-prof-donut-svg">
      <circle cx="70" cy="70" r={r} fill="none" stroke="var(--surface-2)" strokeWidth="14" />
      <circle cx="70" cy="70" r={r} fill="none" stroke={stroke} strokeWidth="14"
              strokeDasharray={`${dash} ${c}`} strokeLinecap="round"
              transform="rotate(-90 70 70)" />
      <text x="70" y="68" textAnchor="middle" fontSize="11" fill="var(--text-muted)" fontFamily="Inter" fontWeight="600">Cumplimiento</text>
      <text x="70" y="90" textAnchor="middle" fontSize="28" fill="var(--text)" fontFamily="Inter" fontWeight="800" letterSpacing="-1">{pct}%</text>
    </svg>
  );
};

window.CompetenciasTab = CompetenciasTab;
window.MatrizTab       = MatrizTab;
window.SesionesTab     = SesionesTab;
window.ProveedoresTab  = ProveedoresTab;

// Hidratación del SESSIONS_SCHED · agrega dateStart/dateEnd y planId (refactor · plan ≠ sesión)
// Si la sesión solo trae `date`, se asume jornada de un día (dateStart === dateEnd === date).
window.SESSIONS_SCHED = SESSIONS_SCHED.map(s => ({
  ...s,
  dateStart: s.dateStart || s.date,
  dateEnd:   s.dateEnd   || s.date,
  planId:    s.planId || null,
}));

// Helpers de sesión multi-día
window.sessionDayCount = (s) => {
  if (!s.dateStart || !s.dateEnd) return 1;
  const [d1, m1, y1] = s.dateStart.split('/').map(Number);
  const [d2, m2, y2] = s.dateEnd.split('/').map(Number);
  if (!d1 || !d2) return 1;
  return Math.round((new Date(y2, m2-1, d2) - new Date(y1, m1-1, d1)) / 86400000) + 1;
};
window.sessionDateLabel = (s) => {
  const dias = window.sessionDayCount(s);
  if (dias <= 1) return s.dateStart || s.date;
  return `${s.dateStart} → ${s.dateEnd} · ${dias} días`;
};
window.BitacoraTab     = BitacoraTab;
window.SessionCapture  = SessionCapture;
window.TrainingProfile = TrainingProfile;

// Datos compartidos por otros módulos (Fase A — fundación de datos)
window.COMPETENCIAS_MAESTRAS = COMPETENCIAS_MAESTRAS;
window.EVIDENCE_TYPES        = EVIDENCE_TYPES;
window.getCmp                = getCmp;
