// ATS module — Reclutamiento y Selección
// Kanban pipeline + vacancy detail + candidate side panel + offer modal
const { useState: _ats_useState, useMemo: _ats_useMemo } = React;

// ─── Master data ───────────────────────────────────────────

const STAGES = [
  { id: 'postulado', label: 'Postulados',           hint: 'Nuevos candidatos' },
  { id: 'cv',        label: 'Hoja de vida',         hint: 'Revisión' },
  { id: 'pruebas',   label: 'Pruebas',              hint: 'Filtros técnicos' },
  { id: 'entr-rrhh', label: 'Entrevista RRHH',      hint: 'Cultural fit' },
  { id: 'entr-jefe', label: 'Entrevista jefe',      hint: 'Validación cargo' },
  { id: 'oferta',    label: 'Oferta enviada',       hint: 'En decisión' },
  { id: 'final',     label: 'Resultado',            hint: 'Contratado / Descartado' },
];

const SOURCE_META = {
  linkedin: { label: 'LinkedIn',        tone: 'blue' },
  referido: { label: 'Referido',        tone: 'green' },
  portal:   { label: 'Portal web',      tone: 'navy' },
  bolsa:    { label: 'Bolsa empleo',    tone: 'amber' },
  manual:   { label: 'Registro manual', tone: 'navy' },
};

const DOC_TYPES_CANDIDATO = ['Cédula de ciudadanía', 'Cédula de extranjería', 'Permiso por Protección Temporal', 'Pasaporte'];

const VINCULACION_OPTIONS = [
  { value: 'bolsa',    label: 'Bolsa de empleo' },
  { value: 'portal',   label: 'Portal web' },
  { value: 'linkedin', label: 'LinkedIn' },
  { value: 'referido', label: 'Referido' },
];

// Empleados a cargo de quien crea la vacante (Carolina Restrepo · Jefe de RRHH) — usados en el flujo de Reemplazo
const REPORTES_A_CARGO = [
  {
    empId: 'E0198', doc: 'CC 1.098.443.221', name: 'María Fernanda López', empresa: 'ECOENTORNO', role: 'Op. Aprovechamiento', city: 'Cali', estado: 'activo',
    area: 'Aprovechamiento', jefeDirecto: 'Carolina Restrepo S. · Jefe de RRHH', salarioMinimo: 1300000, centroCosto: 'CC-045 Aprovechamiento',
    descripcion: 'Ejecuta la separación, clasificación y empaque de material aprovechable conforme a los estándares de calidad y productividad de la línea.',
    requerimientos: 'Bachiller · experiencia mínima de 6 meses en procesos de aprovechamiento o manufactura · disponibilidad para trabajo en planta.',
    rodamiento: 'No', transporte: 'No', licencia: 'No aplica',
  },
  {
    empId: 'E0094', doc: 'CC 42.887.665', name: 'María Trujillo Henao', empresa: 'ASEI', role: 'Aux. Recursos Humanos', city: 'Cali', estado: 'activo',
    area: 'Administración', jefeDirecto: 'Carolina Restrepo S. · Jefe de RRHH', salarioMinimo: 1600000, centroCosto: 'CC-010 Administración',
    descripcion: 'Apoya los procesos administrativos de gestión humana: archivo, afiliaciones, comunicación con colaboradores y soporte documental al equipo de RRHH.',
    requerimientos: 'Técnico o tecnólogo en Gestión Humana o afines · manejo de herramientas ofimáticas · experiencia mínima de 1 año en procesos administrativos.',
    rodamiento: 'No', transporte: 'No', licencia: 'No aplica',
  },
  {
    empId: 'E0156', doc: 'CC 1.087.665.443', name: 'Paula Valencia Ríos', empresa: 'ECOLOGISTICA', role: 'Téc. Mantenimiento', city: 'Cali', estado: 'activo',
    area: 'Mantenimiento', jefeDirecto: 'Carolina Restrepo S. · Jefe de RRHH', salarioMinimo: 1800000, centroCosto: 'CC-050 Mantenimiento',
    descripcion: 'Ejecuta el mantenimiento preventivo y correctivo de la maquinaria industrial de planta, garantizando la disponibilidad operativa de los equipos.',
    requerimientos: 'Técnico o tecnólogo en Mantenimiento Industrial o Electromecánica · certificación vigente de trabajo en alturas · experiencia mínima de 1 año.',
    rodamiento: 'Sí — $ 150.000', transporte: 'Sí', licencia: 'Sí — Categoría B1',
  },
  {
    empId: 'E0298', doc: 'CC 32.998.221', name: 'Adriana Restrepo', empresa: 'IA4 SAS', role: 'Aux. Calidad', city: 'Cali', estado: 'activo',
    area: 'Calidad', jefeDirecto: 'Carolina Restrepo S. · Jefe de RRHH', salarioMinimo: 1400000, centroCosto: 'CC-060 Calidad',
    descripcion: 'Realiza inspecciones de calidad por muestreo sobre el material clasificado y aprovechado, documentando hallazgos y desviaciones del proceso.',
    requerimientos: 'Técnico o tecnólogo en Calidad, Ambiental o afines · manejo de indicadores y formatos de inspección · experiencia mínima de 6 meses.',
    rodamiento: 'No', transporte: 'No', licencia: 'No aplica',
  },
  {
    empId: 'E0011', doc: 'CC 16.087.443', name: 'Fernando Henao', empresa: 'ECOENTORNO', role: 'Operario de Clasificación', city: 'Cali', estado: 'inactivo',
    area: 'Operaciones', jefeDirecto: 'Carolina Restrepo S. · Jefe de RRHH', salarioMinimo: 1300000, centroCosto: 'CC-040 Operaciones',
    descripcion: 'Recibe, inspecciona y clasifica los residuos que ingresan a la línea de producción según su categoría, cumpliendo los estándares de calidad y seguridad de planta.',
    requerimientos: 'Bachiller · disponibilidad para turnos rotativos · no se requiere experiencia previa.',
    rodamiento: 'No', transporte: 'No', licencia: 'No aplica',
  },
];

const REEMPLAZO_TABS = [
  { id: 'vacante',     label: 'Vacante' },
  { id: 'incapacidad', label: 'Incapacidad' },
  { id: 'vacaciones',  label: 'Vacaciones' },
];

const VACANCIES = [
  {
    id: 'V-2026-008',
    role: 'Operario de Clasificación',
    area: 'Operaciones',
    site: 'Cali',
    contract: 'Indefinido',
    salary: '$1.450.000 – $1.620.000 COP',
    opened: '20/04/2026',
    closing: '20/05/2026',
    owner: 'Carolina Restrepo',
    leader: 'Luis Miguel Marín',
    state: 'abierta',
    description: 'Operación de trituradora industrial de 240 kg, control de proceso de clasificación, registro de lotes y mantenimiento básico de la zona. Turnos rotativos 6×1.',
    requirements: [
      'Bachiller graduado · técnico en alimentos deseable',
      'Experiencia mínima 1 año en planta de procesos (preferiblemente material o alimentos)',
      'Curso de manipulación de alimentos vigente',
      'Disponibilidad para turnos rotativos',
      'Residencia en Cali o municipios cercanos',
    ],
    activity: [
      { when: '02/05/2026 14:30', text: 'Carolina Restrepo movió a Andrés Mejía a Entrevista jefe' },
      { when: '01/05/2026 11:08', text: 'Patricia Gómez agregó nota a Andrés Mejía: "Excelente actitud, manejo de turnos OK"' },
      { when: '30/04/2026 16:42', text: 'Carolina Restrepo movió a Daniela Salazar a Pruebas' },
      { when: '28/04/2026 09:15', text: 'Llegaron 4 nuevos postulados desde el portal web' },
      { when: '25/04/2026 10:00', text: 'Vacante publicada en LinkedIn (alcance 12.4k)' },
    ],
  },
  {
    id: 'V-2026-007',
    role: 'Líder de Aprovechamiento',
    area: 'Aprovechamiento',
    site: 'Cali',
    contract: 'Indefinido',
    salary: '$2.800.000 – $3.200.000 COP',
    opened: '15/04/2026',
    closing: '15/05/2026',
    owner: 'Carolina Restrepo',
    leader: 'Carlos A. Gómez',
    state: 'abierta',
    description: 'Liderar el equipo de la línea de aprovechamiento (12 personas), garantizar metas de productividad y calidad, gestionar turnos y soporte al equipo.',
    requirements: [
      'Tecnólogo o profesional en operaciones industrial · ingeniería',
      'Mínimo 3 años liderando equipos de planta',
      'Manejo de indicadores OEE',
      'Liderazgo situacional · manejo de conflictos',
    ],
    activity: [],
  },
  {
    id: 'V-2026-006',
    role: 'Auxiliar de Calidad',
    area: 'Calidad',
    site: 'Barranquilla',
    contract: 'Fijo · 1 año',
    salary: '$1.620.000 – $1.850.000 COP',
    opened: '10/04/2026',
    closing: '30/04/2026',
    owner: 'Patricia Gómez',
    leader: 'Sandra Milena Ortiz',
    state: 'abierta',
    description: 'Apoyar el área de calidad en muestreo, análisis sensorial básico, control documental ISO y gestión de no conformidades.',
    requirements: [
      'Técnico o tecnólogo en calidad / alimentos',
      'Experiencia 1+ año en laboratorio de calidad',
      'Conocimientos en HACCP / ISO 22000',
      'Análisis de laboratorio ambiental (deseable)',
    ],
    activity: [],
  },
  {
    id: 'V-2026-005',
    role: 'Conductor (transporte primario)',
    area: 'Recolección',
    site: 'Barranquilla',
    contract: 'Indefinido',
    salary: '$1.850.000 – $2.100.000 COP',
    opened: '05/04/2026',
    closing: '05/05/2026',
    owner: 'Carolina Restrepo',
    leader: 'Jorge Iván Patiño',
    state: 'abierta',
    description: 'Operación de camión NHR para transporte primario Barranquilla–Cali–Bogotá. Turnos diurnos.',
    requirements: [
      'Licencia C2 vigente con mínimo 3 años',
      'Experiencia comprobada en transporte intermunicipal',
      'Sin comparendos en últimos 2 años',
      'Curso defensivo vigente',
    ],
    activity: [],
  },
];

// candidates — vacancyId, stage, etc.
const CANDIDATES = [
  // V-2026-008 — Operario Clasificación
  { id: 'C-401', initials: 'AM', name: 'Andrés Mejía Cardona',     vacancyId: 'V-2026-008', stage: 'entr-jefe', source: 'referido', days: 14, rating: 5, nextAction: 'Entrevista jefe · 06/05 10:00', email: 'amejia.cardona@gmail.com', phone: '+57 311 456 7821', city: 'Cali', age: 28, exp: '3 años', notes: 'Referido por Carlos Vélez. Excelente fit cultural · Carolina Restrepo' },
  { id: 'C-402', initials: 'DS', name: 'Daniela Salazar Ruiz',     vacancyId: 'V-2026-008', stage: 'pruebas',   source: 'linkedin', days: 9,  rating: 4, nextAction: 'Prueba técnica · 07/05 14:00', email: 'dani.salazar@outlook.com', phone: '+57 320 887 4456', city: 'Cali', age: 25, exp: '2 años', notes: '' },
  { id: 'C-403', initials: 'OQ', name: 'Óscar Quintero Henao',     vacancyId: 'V-2026-008', stage: 'entr-rrhh', source: 'portal',   days: 11, rating: 4, nextAction: 'Entrevista RRHH · 06/05 09:00', email: 'oquintero@hotmail.com', phone: '+57 314 224 8901', city: 'Cali', age: 31, exp: '4 años', notes: 'Buen perfil técnico, falta validar disponibilidad turnos' },
  { id: 'C-404', initials: 'SR', name: 'Sebastián Ríos Mejía',     vacancyId: 'V-2026-008', stage: 'cv',        source: 'portal',   days: 5,  rating: 3, nextAction: 'Revisión hoja de vida', email: 'sebas.rios@gmail.com', phone: '+57 318 998 2345', city: 'Cartago', age: 23, exp: '1 año', notes: '' },
  { id: 'C-405', initials: 'JC', name: 'Juliana Cárdenas Toro',    vacancyId: 'V-2026-008', stage: 'cv',        source: 'linkedin', days: 4,  rating: 4, nextAction: 'Revisión hoja de vida', email: 'julicardenas@protonmail.com', phone: '+57 311 776 5544', city: 'Barranquilla', age: 27, exp: '2 años', notes: '' },
  { id: 'C-406', initials: 'EM', name: 'Esteban Marín Patiño',     vacancyId: 'V-2026-008', stage: 'postulado', source: 'bolsa',    days: 2,  rating: 0, nextAction: 'Asignar revisor', email: 'esteban.marin@gmail.com', phone: '+57 322 445 1180', city: 'Cali', age: 24, exp: '6 meses', notes: '' },
  { id: 'C-407', initials: 'NT', name: 'Natalia Trujillo López',   vacancyId: 'V-2026-008', stage: 'postulado', source: 'portal',   days: 1,  rating: 0, nextAction: 'Asignar revisor', email: 'natitrujillo@yahoo.com', phone: '+57 320 882 9911', city: 'Cali', age: 26, exp: '1 año', notes: '' },
  { id: 'C-408', initials: 'LV', name: 'Luis Vélez Restrepo',      vacancyId: 'V-2026-008', stage: 'oferta',    days: 18, source: 'referido', rating: 5, nextAction: 'Esperando aceptación · enviada 02/05', email: 'luisvelez@gmail.com', phone: '+57 314 552 3340', city: 'Cali', age: 30, exp: '5 años', notes: 'Aprobado por jefe directo. Carta enviada el 02/05.' },
  { id: 'C-409', initials: 'AF', name: 'Andrés Felipe Mejía',      vacancyId: 'V-2026-008', stage: 'final',     days: 22, source: 'referido', rating: 5, finalState: 'contratado', nextAction: 'Contratado · vinculado 02/05/2026', email: 'andres.mejia@atica.co', phone: '+57 311 456 7821', city: 'Cali', age: 29, exp: '4 años', notes: 'Contratado el 02/05. ID empleado E0247.' },
  { id: 'C-410', initials: 'CG', name: 'Carolina Giraldo Pérez',   vacancyId: 'V-2026-008', stage: 'final',     days: 19, source: 'linkedin', rating: 2, finalState: 'descartado', nextAction: 'Descartada · no pasó prueba técnica', email: 'carogiraldo@outlook.com', phone: '+57 318 220 6677', city: 'Cali', age: 26, exp: '1 año', notes: '' },

  // V-2026-007 — Líder de Aprovechamiento
  { id: 'C-411', initials: 'MA', name: 'María Alejandra Ospina',   vacancyId: 'V-2026-007', stage: 'entr-jefe', days: 16, source: 'linkedin', rating: 5, nextAction: 'Entrevista jefe · 08/05 11:00', email: 'maleospina@gmail.com', phone: '+57 311 887 5566', city: 'Cali', age: 34, exp: '8 años', notes: 'Top candidata · pasó RRHH y prueba técnica con 92/100' },
  { id: 'C-412', initials: 'JG', name: 'Javier Gómez Restrepo',    vacancyId: 'V-2026-007', stage: 'entr-rrhh', days: 12, source: 'referido', rating: 4, nextAction: 'Entrevista RRHH · 07/05 15:00', email: 'javier.gomez@outlook.com', phone: '+57 314 442 1199', city: 'Barranquilla', age: 36, exp: '10 años', notes: '' },
  { id: 'C-413', initials: 'KM', name: 'Karen Murillo Salazar',    vacancyId: 'V-2026-007', stage: 'pruebas',   days: 10, source: 'linkedin', rating: 4, nextAction: 'Prueba liderazgo · 07/05 09:00', email: 'kmurillo@gmail.com', phone: '+57 320 778 3300', city: 'Cali', age: 30, exp: '6 años', notes: '' },

  // V-2026-006 — Aux Calidad
  { id: 'C-414', initials: 'PC', name: 'Paula Castaño Henao',      vacancyId: 'V-2026-006', stage: 'oferta',    days: 14, source: 'portal',   rating: 5, nextAction: 'Esperando aceptación · enviada 03/05', email: 'paulacastano@protonmail.com', phone: '+57 318 332 1100', city: 'Barranquilla', age: 25, exp: '2 años', notes: '' },
  { id: 'C-415', initials: 'DR', name: 'Daniel Restrepo Marín',    vacancyId: 'V-2026-006', stage: 'entr-rrhh', days: 9,  source: 'linkedin', rating: 4, nextAction: 'Entrevista RRHH · 08/05 10:00', email: 'drestrepo@gmail.com', phone: '+57 311 552 4477', city: 'Barranquilla', age: 28, exp: '3 años', notes: '' },
  { id: 'C-416', initials: 'LS', name: 'Laura Suárez Cárdenas',    vacancyId: 'V-2026-006', stage: 'cv',        days: 3,  source: 'bolsa',    rating: 3, nextAction: 'Revisión hoja de vida', email: 'lsuarez@hotmail.com', phone: '+57 314 998 1122', city: 'Barranquilla', age: 24, exp: '1 año', notes: '' },

  // V-2026-005 — Conductor
  { id: 'C-417', initials: 'NM', name: 'Néstor Muñoz Cardona',     vacancyId: 'V-2026-005', stage: 'entr-jefe', days: 8,  source: 'referido', rating: 5, nextAction: 'Entrevista jefe · 09/05 08:30', email: 'nmunoz@gmail.com', phone: '+57 320 110 4488', city: 'Barranquilla', age: 38, exp: '12 años', notes: 'Sin comparendos. C2 vigente. Top opción.' },
  { id: 'C-418', initials: 'WC', name: 'William Cárdenas Pérez',   vacancyId: 'V-2026-005', stage: 'pruebas',   days: 6,  source: 'portal',   rating: 3, nextAction: 'Prueba defensivo · 06/05 14:00', email: 'wcardenas@outlook.com', phone: '+57 311 998 2233', city: 'Cali', age: 42, exp: '15 años', notes: '' },
];

const RATING_STARS = (r) => Array.from({ length: 5 }, (_, i) => i < r);

// ─── Solicitudes de vacante ──────────────────────────────────
// Historial de solicitudes creadas desde "Nueva vacante" / "Reemplazar".
// Reutiliza el vocabulario de tipos (VACANCY_TYPE_SECTIONS) y de empresas
// (Sociedades, en Configuración) ya establecido en otros flujos de ATS.

const SOLICITUDES = [
  {
    codigo: 'SOL-2026-0201', cargo: 'Operario de Clasificación', empresa: 'ASEI', fecha: '10/08/2026', tipo: 'Reemplazo', estado: 'pendiente',
    tipoEspecifico: 'Reemplazo por retiro voluntario',
    infoLaboral: { area: 'Operaciones', jefeDirecto: 'Carolina Restrepo S. · Jefe de RRHH', salario: 1300000, centroCosto: 'CC-040 Operaciones', municipio: 'Cali', tipoContrato: 'Término fijo' },
    empleadoAReemplazar: { nombre: 'Fernando Henao', cargoActual: 'Operario de Clasificación', fechaRetiro: '05/08/2026', motivoRetiro: 'Retiro voluntario', antiguedad: '7 años' },
  },
  {
    codigo: 'SOL-2026-0198', cargo: 'Op. Aprovechamiento', empresa: 'ECOENTORNO', fecha: '08/08/2026', tipo: 'Reemplazo', estado: 'pendiente',
    tipoEspecifico: 'Reemplazo por incapacidad prolongada',
    infoLaboral: { area: 'Aprovechamiento', jefeDirecto: 'Carolina Restrepo S. · Jefe de RRHH', salario: 1300000, centroCosto: 'CC-045 Aprovechamiento', municipio: 'Cali', tipoContrato: 'Indefinido' },
    empleadoAReemplazar: { nombre: 'María Fernanda López', cargoActual: 'Op. Aprovechamiento', fechaRetiro: '—', motivoRetiro: 'Incapacidad médica prolongada (> 180 días)', antiguedad: '3 años' },
  },
  {
    codigo: 'SOL-2026-0195', cargo: 'Téc. Mantenimiento', empresa: 'ECOLOGISTICA', fecha: '05/08/2026', tipo: 'Ampliación de nómina', estado: 'pendiente',
    tipoEspecifico: 'Nueva posición aprobada por crecimiento operativo del área de mantenimiento',
    infoLaboral: { area: 'Mantenimiento', jefeDirecto: 'Carolina Restrepo S. · Jefe de RRHH', salario: 1800000, centroCosto: 'CC-050 Mantenimiento', municipio: 'Cali', tipoContrato: 'Indefinido' },
  },
  {
    codigo: 'SOL-2026-0190', cargo: 'Aux. Calidad', empresa: 'IA4 SAS', fecha: '30/07/2026', tipo: 'Ascenso', estado: 'aprobada',
    tipoEspecifico: 'Ascenso a Coordinador de Calidad por evaluación de desempeño sobresaliente',
    infoLaboral: { area: 'Calidad', jefeDirecto: 'Carolina Restrepo S. · Jefe de RRHH', salario: 2100000, centroCosto: 'CC-060 Calidad', municipio: 'Cali', tipoContrato: 'Indefinido' },
  },
  {
    codigo: 'SOL-2026-0187', cargo: 'Conductor', empresa: 'BYC', fecha: '25/07/2026', tipo: 'Contrataciones', estado: 'en_proceso',
    tipoEspecifico: 'Contratación para operación de recolección internacional',
    infoLaboral: { area: 'Logística', jefeDirecto: 'Estuardo Xocop Morales · Líder de Sede', salario: 1500000, centroCosto: 'CC-070 Logística Internacional', municipio: 'Ciudad de Guatemala', tipoContrato: 'Fijo' },
  },
  {
    codigo: 'SOL-2026-0182', cargo: 'Coord. Compras', empresa: 'ASEI', fecha: '18/07/2026', tipo: 'Traslado', estado: 'rechazada',
    tipoEspecifico: 'Traslado de sede Cali a Bogotá por reestructuración de compras',
    infoLaboral: { area: 'Compras', jefeDirecto: 'Carolina Restrepo S. · Jefe de RRHH', salario: 3200000, centroCosto: 'CC-020 Compras', municipio: 'Bogotá', tipoContrato: 'Indefinido' },
  },
  {
    codigo: 'SOL-2026-0178', cargo: 'Jefe de RRHH', empresa: 'ASEI', fecha: '10/07/2026', tipo: 'Cambios y aumentos', estado: 'aprobada',
    tipoEspecifico: 'Ajuste salarial por revisión anual de banda salarial',
    infoLaboral: { area: 'Administración', jefeDirecto: 'Gerencia General', salario: 4500000, centroCosto: 'CC-010 Administración', municipio: 'Cali', tipoContrato: 'Indefinido' },
  },
  {
    codigo: 'SOL-2026-0170', cargo: 'Operario de Clasificación', empresa: 'ECOENTORNO', fecha: '28/06/2026', tipo: 'Retiros', estado: 'cerrada',
    tipoEspecifico: 'Retiro por terminación de contrato a término fijo',
    infoLaboral: { area: 'Operaciones', jefeDirecto: 'Carolina Restrepo S. · Jefe de RRHH', salario: 1300000, centroCosto: 'CC-040 Operaciones', municipio: 'Cali', tipoContrato: 'Término fijo' },
  },
  {
    codigo: 'SOL-2026-0165', cargo: 'Op. Aprovechamiento', empresa: 'ECOENTORNO', fecha: '15/06/2026', tipo: 'Reemplazo', estado: 'cerrada',
    tipoEspecifico: 'Reemplazo por renuncia',
    infoLaboral: { area: 'Aprovechamiento', jefeDirecto: 'Carolina Restrepo S. · Jefe de RRHH', salario: 1300000, centroCosto: 'CC-045 Aprovechamiento', municipio: 'Cali', tipoContrato: 'Indefinido' },
    empleadoAReemplazar: { nombre: 'Diego Andrés Toro', cargoActual: 'Aux. Aprovechamiento', fechaRetiro: '10/06/2026', motivoRetiro: 'Renuncia voluntaria', antiguedad: '1 año' },
  },
  {
    codigo: 'SOL-2026-0160', cargo: 'Líder de Aprovechamiento', empresa: 'ECOENTORNO', fecha: '02/06/2026', tipo: 'Nueva operación', estado: 'rechazada',
    tipoEspecifico: 'Apertura de nueva línea de aprovechamiento en sede Cali',
    infoLaboral: { area: 'Aprovechamiento', jefeDirecto: 'Carolina Restrepo S. · Jefe de RRHH', salario: 2550000, centroCosto: 'CC-045 Aprovechamiento', municipio: 'Cali', tipoContrato: 'Indefinido' },
  },
];

const STATE_SOLICITUD = {
  pendiente:  { label: 'Pendiente',  pill: 'amber-soft' },
  en_proceso: { label: 'En proceso', pill: 'navy' },
  aprobada:   { label: 'Aprobada',   pill: 'green' },
  rechazada:  { label: 'Rechazada',  pill: 'rose' },
  cerrada:    { label: 'Cerrada',    pill: 'navy-soft' },
};

const SolicitudesTable = ({ rows, onIniciarReclutamiento }) => {
  const [detalle, setDetalle] = _ats_useState(null);
  return (
    <>
      <div className="doc-list-card">
        <table className="doc-table">
          <thead>
            <tr>
              <th>Código de solicitud</th><th>Cargo</th><th>Empresa</th><th>Fecha de creación</th><th>Tipo de solicitud</th><th>Estado</th><th></th>
            </tr>
          </thead>
          <tbody>
            {rows.map(s => {
              const st = STATE_SOLICITUD[s.estado];
              return (
                <tr key={s.codigo}>
                  <td className="mono muted">{s.codigo}</td>
                  <td style={{ fontWeight: 500 }}>{s.cargo}</td>
                  <td>{s.empresa}</td>
                  <td className="mono">{s.fecha}</td>
                  <td>{s.tipo}</td>
                  <td><span className={`pill ${st.pill}`}>{st.label}</span></td>
                  <td>
                    <div style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}>
                      <button className="icon-action" title="Ver detalle" onClick={() => setDetalle(s)}><IconExternal size={14} /></button>
                      {s.estado === 'pendiente' && (
                        <button className="btn primary" style={{ padding: '4px 10px', fontSize: 12 }} onClick={() => onIniciarReclutamiento(s)}>
                          Iniciar reclutamiento
                        </button>
                      )}
                    </div>
                  </td>
                </tr>
              );
            })}
            {rows.length === 0 && (
              <tr><td colSpan={7} className="muted" style={{ textAlign: 'center', padding: 20, fontSize: 12.5 }}>No hay solicitudes en este momento.</td></tr>
            )}
          </tbody>
        </table>
      </div>
      {detalle && (
        <SolicitudDetailModal
          solicitud={detalle}
          onClose={() => setDetalle(null)}
          onIniciarReclutamiento={() => { onIniciarReclutamiento(detalle); setDetalle(null); }}
        />
      )}
    </>
  );
};

const SolicitudDetailModal = ({ solicitud: s, onClose, onIniciarReclutamiento }) => {
  const st = STATE_SOLICITUD[s.estado];
  const lab = s.infoLaboral;
  const emp = s.empleadoAReemplazar;
  return (
    <div className="modal-overlay" onMouseDown={(ev) => { if (ev.target === ev.currentTarget) onClose(); }}>
      <div className="modal" style={{ maxWidth: 560, maxHeight: 'calc(100vh - 64px)' }}>
        <div className="modal-head">
          <div>
            <div className="muted mono" style={{ fontSize: 11 }}>{s.codigo}</div>
            <div className="modal-title">Detalle de la solicitud</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>
        <div className="modal-body">
          <div className="field-grid">
            <ReadOnlyField label="Cargo" value={s.cargo} />
            <ReadOnlyField label="Empresa" value={s.empresa} />
            <ReadOnlyField label="Fecha de creación" value={s.fecha} />
            <ReadOnlyField label="Tipo de solicitud" value={s.tipo} />
          </div>
          <div style={{ marginTop: 16 }}>
            <div className="field-lbl">Estado</div>
            <div style={{ marginTop: 4 }}><span className={`pill ${st.pill}`}>{st.label}</span></div>
          </div>

          {s.tipoEspecifico && (
            <div style={{ marginTop: 20 }}>
              <ReadOnlyField label="Tipo específico de la solicitud" value={s.tipoEspecifico} />
            </div>
          )}

          {lab && (
            <div style={{ marginTop: 20 }}>
              <div className="muted-xs" style={{ marginBottom: 8 }}>INFORMACIÓN LABORAL</div>
              <div className="field-grid">
                <ReadOnlyField label="Área" value={lab.area} />
                <ReadOnlyField label="Jefe directo" value={lab.jefeDirecto} />
                <ReadOnlyField label="Salario" value={formatCOPAts(lab.salario)} />
                <ReadOnlyField label="Centro de costos" value={lab.centroCosto} />
                <ReadOnlyField label="Municipio" value={lab.municipio} />
                <ReadOnlyField label="Tipo de contrato" value={lab.tipoContrato} />
              </div>
            </div>
          )}

          {emp && (
            <div style={{ marginTop: 20 }}>
              <div className="muted-xs" style={{ marginBottom: 8 }}>EMPLEADO A REEMPLAZAR</div>
              <div className="field-grid">
                <ReadOnlyField label="Nombre" value={emp.nombre} />
                <ReadOnlyField label="Cargo actual" value={emp.cargoActual} />
                <ReadOnlyField label="Fecha de retiro" value={emp.fechaRetiro} />
                <ReadOnlyField label="Motivo de retiro" value={emp.motivoRetiro} />
                <ReadOnlyField label="Antigüedad" value={emp.antiguedad} />
              </div>
            </div>
          )}
        </div>
        <div className="modal-foot">
          <button className="btn" onClick={onClose}>Cerrar</button>
          {s.estado === 'pendiente' && (
            <button className="btn primary" onClick={onIniciarReclutamiento}><IconRocket size={14} /> Iniciar reclutamiento</button>
          )}
        </div>
      </div>
    </div>
  );
};

// ─── Root ──────────────────────────────────────────────────

const ATS = () => {
  const [vacancyId, setVacancyId] = _ats_useState('V-2026-008');
  const [expandedVacId, setExpandedVacId] = _ats_useState(null); // solicitud desplegada en "Solicitudes activas"
  const [showAll, setShowAll] = _ats_useState(false);
  const [candidate, setCandidate] = _ats_useState(null);
  const [offerFor, setOfferFor] = _ats_useState(null);
  const [candidatesList, setCandidatesList] = _ats_useState(CANDIDATES);
  const [openNewCandidate, setOpenNewCandidate] = _ats_useState(false);
  const [mainTab, setMainTab] = _ats_useState('activas'); // activas | pendientes | historico
  const [solicitudes, setSolicitudes] = _ats_useState(SOLICITUDES);

  const iniciarReclutamiento = (s) => {
    setSolicitudes(list => list.map(x => x.codigo === s.codigo ? { ...x, estado: 'en_proceso' } : x));
  };

  const vacancy = VACANCIES.find(v => v.id === vacancyId);
  const visible = showAll ? candidatesList : candidatesList.filter(c => c.vacancyId === vacancyId);

  const totals = _ats_useMemo(() => {
    const pool = candidatesList;
    return {
      postulados: pool.length,
      proceso:    pool.filter(c => !['oferta', 'final'].includes(c.stage)).length,
      ofertas:    pool.filter(c => c.stage === 'oferta').length,
      contratados: pool.filter(c => c.finalState === 'contratado').length,
    };
  }, [candidatesList]);

  const addCandidate = (data) => {
    const parts = data.name.trim().split(/\s+/);
    const initials = ((parts[0]?.[0] || '') + (parts[1]?.[0] || parts[0]?.[1] || '')).toUpperCase();
    const vac = VACANCIES.find(v => v.id === data.vacancyId);
    setCandidatesList(list => [...list, {
      id: `C-${Date.now()}`,
      initials,
      name: data.name.trim(),
      vacancyId: data.vacancyId,
      stage: 'postulado',
      source: data.source,
      days: 0,
      rating: 0,
      nextAction: 'Asignar revisor',
      email: '',
      phone: '',
      city: vac ? vac.site : '',
      age: data.age,
      exp: data.exp,
      docType: data.docType,
      doc: data.doc,
      cvFileName: data.cvFileName,
      notes: data.notes,
    }]);
    setOpenNewCandidate(false);
  };

  return (
    <div className="main" data-screen-label="Reclutamiento">
      <div className="page-head">
        <div>
          <h1 className="page-title">Reclutamiento y Selección</h1>
          <div className="page-subtitle">
            <b>{VACANCIES.length} vacantes activas</b> · {totals.postulados} candidatos en pipeline · pipeline configurable de 7 etapas
          </div>
        </div>
        <div style={{ display: 'flex', gap: 8 }}>
          <button className="btn" onClick={() => setShowAll(s => !s)}>
            <IconUsers size={14} /> {showAll ? 'Ver una vacante' : 'Ver todos los candidatos'}
          </button>
        </div>
      </div>

      {/* KPIs */}
      <div className="ats-kpis">
        <div className="ats-kpi">
          <div className="ats-kpi-icon navy"><IconUsers size={16} /></div>
          <div>
            <div className="ats-kpi-num">{totals.postulados}</div>
            <div className="ats-kpi-lbl">Total postulados</div>
          </div>
        </div>
        <div className="ats-kpi">
          <div className="ats-kpi-icon blue"><IconClock size={16} /></div>
          <div>
            <div className="ats-kpi-num">{totals.proceso}</div>
            <div className="ats-kpi-lbl">En proceso</div>
          </div>
        </div>
        <div className="ats-kpi">
          <div className="ats-kpi-icon amber"><IconMegaphone size={16} /></div>
          <div>
            <div className="ats-kpi-num">{totals.ofertas}</div>
            <div className="ats-kpi-lbl">Ofertas activas</div>
          </div>
        </div>
        <div className="ats-kpi">
          <div className="ats-kpi-icon green"><IconCheck size={16} /></div>
          <div>
            <div className="ats-kpi-num">{totals.contratados}</div>
            <div className="ats-kpi-lbl">Contratados este mes</div>
          </div>
        </div>
      </div>

      <div className="doc-tabs" style={{ marginTop: 18 }}>
        <button className={`doc-tab ${mainTab === 'activas' ? 'active' : ''}`} onClick={() => setMainTab('activas')}>Solicitudes activas</button>
        <button className={`doc-tab ${mainTab === 'pendientes' ? 'active' : ''}`} onClick={() => setMainTab('pendientes')}>Solicitudes pendientes</button>
        <button className={`doc-tab ${mainTab === 'historico' ? 'active' : ''}`} onClick={() => setMainTab('historico')}>Histórico de solicitudes</button>
      </div>

      {mainTab === 'activas' && (
        <>
          {showAll ? (
            <Pipeline candidates={visible} onOpen={(c) => setCandidate(c)} showRole onAddCandidate={() => setOpenNewCandidate(true)} />
          ) : (
            <div className="ats-vac-list">
              {VACANCIES.map(v => (
                <VacancySolicitudRow
                  key={v.id}
                  vacancy={v}
                  expanded={expandedVacId === v.id}
                  onToggle={() => setExpandedVacId(id => id === v.id ? null : v.id)}
                  candidates={candidatesList.filter(c => c.vacancyId === v.id)}
                  onOpen={(c) => setCandidate(c)}
                  onAddCandidate={() => { setVacancyId(v.id); setOpenNewCandidate(true); }}
                />
              ))}
            </div>
          )}
        </>
      )}

      {mainTab === 'pendientes' && (
        <SolicitudesTable rows={solicitudes.filter(s => s.estado === 'pendiente')} onIniciarReclutamiento={iniciarReclutamiento} />
      )}

      {mainTab === 'historico' && (
        <SolicitudesTable rows={solicitudes} onIniciarReclutamiento={iniciarReclutamiento} />
      )}

      {candidate && (
        <CandidatePanel
          candidate={candidate}
          vacancy={VACANCIES.find(v => v.id === candidate.vacancyId)}
          onClose={() => setCandidate(null)}
          onOffer={() => { setOfferFor(candidate); }}
        />
      )}
      {offerFor && (
        <OfferModal
          candidate={offerFor}
          vacancy={VACANCIES.find(v => v.id === offerFor.vacancyId)}
          onClose={() => setOfferFor(null)}
        />
      )}
      {openNewCandidate && (
        <NewCandidateModal
          defaultVacancyId={vacancyId}
          onClose={() => setOpenNewCandidate(false)}
          onSave={addCandidate}
        />
      )}
    </div>
  );
};

// Extraído para poder colocar el botón "Nueva vacante" en otro módulo
// (Solicitudes de personal) con exactamente el mismo comportamiento.
const NuevaVacanteFlow = ({ label = 'Nueva vacante' }) => {
  const [vacancyFlowStep, setVacancyFlowStep] = _ats_useState(null); // null | 'type' | 'reemplazo' | otros tipos

  return (
    <>
      <button className="btn primary" onClick={() => setVacancyFlowStep('type')}><IconPlus size={14} /> {label}</button>
      {vacancyFlowStep === 'type' && (
        <NewVacancyTypeModal
          onClose={() => setVacancyFlowStep(null)}
          onContinue={(type) => setVacancyFlowStep(type)}
        />
      )}
      {vacancyFlowStep === 'reemplazo' && (
        <ReemplazoModal
          onBack={() => setVacancyFlowStep('type')}
          onViewEmployee={(empId) => {
            setVacancyFlowStep(null);
            window.__navigateToEmployee && window.__navigateToEmployee(empId);
          }}
          onFinish={() => setVacancyFlowStep(null)}
        />
      )}
      {vacancyFlowStep && vacancyFlowStep !== 'type' && vacancyFlowStep !== 'reemplazo' && (
        <VacancyStubModal
          type={vacancyFlowStep}
          onBack={() => setVacancyFlowStep('type')}
          onClose={() => setVacancyFlowStep(null)}
        />
      )}
    </>
  );
};

// ─── Pipeline (kanban) ────────────────────────────────────

const Pipeline = ({ candidates, onOpen, showRole, onAddCandidate }) => {
  return (
    <div className="ats-board">
      {STAGES.map(stg => {
        const cards = candidates.filter(c => c.stage === stg.id);
        return (
          <div key={stg.id} className={`ats-col ats-col-${stg.id}`}>
            <div className="ats-col-head">
              <div>
                <div className="ats-col-title">{stg.label}</div>
                <div className="ats-col-hint">{stg.hint}</div>
              </div>
              <span className="ats-col-count">{cards.length}</span>
            </div>
            <div className="ats-col-body">
              {stg.id === 'postulado' && (
                <button className="btn" style={{ width: '100%', justifyContent: 'center', marginBottom: 10 }} onClick={onAddCandidate}>
                  <IconPlus size={13} /> Nuevo postulado
                </button>
              )}
              {cards.length === 0 && <div className="ats-col-empty">— sin candidatos —</div>}
              {cards.map(c => <CandidateCard key={c.id} c={c} onOpen={() => onOpen(c)} showRole={showRole} />)}
            </div>
          </div>
        );
      })}
    </div>
  );
};

const CandidateCard = ({ c, onOpen, showRole }) => {
  const src = SOURCE_META[c.source];
  const finalCls = c.finalState ? `final-${c.finalState}` : '';
  const vac = VACANCIES.find(v => v.id === c.vacancyId);
  return (
    <div className={`ats-card ${finalCls}`} onClick={onOpen}>
      <div className="ats-card-head">
        <div className="ats-card-avatar">{c.initials}</div>
        <div style={{ flex: 1, minWidth: 0 }}>
          <div className="ats-card-name">{c.name}</div>
          {showRole
            ? <div className="ats-card-role muted">{vac.role} · {vac.site}</div>
            : <div className="ats-card-role muted">{c.city} · {c.exp}</div>
          }
        </div>
        {c.finalState === 'contratado' && <span className="ats-final-tag green"><IconCheck size={11} /></span>}
        {c.finalState === 'descartado' && <span className="ats-final-tag rose">×</span>}
      </div>
      <div className="ats-card-meta">
        <span className={`ats-source-pill ${src.tone}`}>{src.label}</span>
        <span className="ats-rating">
          {RATING_STARS(c.rating).map((on, i) => (
            <svg key={i} width="11" height="11" viewBox="0 0 24 24" className={on ? 'on' : ''}>
              <path d="M12 2.5l2.95 6 6.6.96-4.78 4.66 1.13 6.58L12 17.6l-5.9 3.1 1.13-6.58L2.45 9.46l6.6-.96L12 2.5z" />
            </svg>
          ))}
        </span>
      </div>
      <div className="ats-card-foot">
        <span className="ats-card-action">
          <IconClock size={11} /> {c.nextAction}
        </span>
        <span className="ats-card-days mono">{c.days}d</span>
      </div>
    </div>
  );
};

// ─── Vacancy detail ───────────────────────────────────────

const VacancyDetail = ({ vacancy, candidates, onOpen }) => {
  const byStage = STAGES.map(s => ({
    ...s,
    cards: candidates.filter(c => c.stage === s.id),
  }));
  const total = candidates.length;
  return (
    <div className="ats-vac-detail">
      <div className="ats-vac-card">
        <div className="ats-vac-head">
          <div>
            <div className="ats-vac-id mono">{vacancy.id}</div>
            <h2 className="ats-vac-title">{vacancy.role}</h2>
            <div className="muted" style={{ fontSize: 13, marginTop: 4 }}>
              {vacancy.area} · {vacancy.site} · responsable {vacancy.owner}
            </div>
          </div>
          <span className="pill green">{vacancy.state === 'abierta' ? 'Abierta' : vacancy.state}</span>
        </div>

        <div className="ats-vac-grid">
          <div><div className="muted-xs">Tipo de contrato</div><div className="ats-vac-val">{vacancy.contract}</div></div>
          <div><div className="muted-xs">Rango salarial</div><div className="ats-vac-val mono">{vacancy.salary}</div></div>
          <div><div className="muted-xs">Apertura</div><div className="ats-vac-val mono">{vacancy.opened}</div></div>
          <div><div className="muted-xs">Cierre estimado</div><div className="ats-vac-val mono">{vacancy.closing}</div></div>
          <div><div className="muted-xs">Jefe directo</div><div className="ats-vac-val">{vacancy.leader}</div></div>
          <div><div className="muted-xs">Total candidatos</div><div className="ats-vac-val" style={{ color: 'var(--navy-800)' }}>{total} en pipeline</div></div>
        </div>

        <div className="ats-vac-section">
          <div className="muted-xs">Descripción del cargo</div>
          <p style={{ marginTop: 6, fontSize: 13.5, lineHeight: 1.55 }}>{vacancy.description}</p>
        </div>

        <div className="ats-vac-section">
          <div className="muted-xs">Requisitos</div>
          <ul className="ats-vac-req">
            {vacancy.requirements.map((r, i) => (
              <li key={i}><IconCheck size={12} /> {r}</li>
            ))}
          </ul>
        </div>
      </div>

      <div className="ats-vac-card">
        <div className="ats-vac-section-title">Mini pipeline · candidatos por etapa</div>
        <div className="ats-mini-pipe">
          {byStage.map((s, i) => {
            const isFinal = s.id === 'final';
            return (
              <div key={s.id} className="ats-mini-col">
                <div className="ats-mini-col-head">
                  <div>
                    <div className="ats-mini-title">{s.label}</div>
                    <div className="ats-mini-hint muted">{s.hint}</div>
                  </div>
                  <span className="ats-mini-count">{s.cards.length}</span>
                </div>
                <div className="ats-mini-body">
                  {s.cards.length === 0
                    ? <div className="ats-mini-empty">—</div>
                    : s.cards.slice(0, 4).map(c => (
                        <button key={c.id} className={`ats-mini-card ${c.finalState ? `final-${c.finalState}` : ''}`} onClick={() => onOpen(c)}>
                          <span className="ats-card-avatar sm">{c.initials}</span>
                          <span style={{ flex: 1, minWidth: 0, textAlign: 'left' }}>
                            <span className="ats-mini-name">{c.name}</span>
                            <span className="ats-mini-meta muted mono">{c.days}d · {SOURCE_META[c.source].label}</span>
                          </span>
                        </button>
                      ))
                  }
                  {s.cards.length > 4 && <div className="muted" style={{ fontSize: 11, padding: '4px 0' }}>+{s.cards.length - 4} más</div>}
                </div>
              </div>
            );
          })}
        </div>
      </div>

      <div className="ats-vac-card">
        <div className="ats-vac-section-title">Historial de actividad</div>
        {vacancy.activity.length === 0
          ? <div className="muted" style={{ fontSize: 12.5 }}>Sin actividad registrada para esta vacante.</div>
          : (
            <div className="ats-activity">
              {vacancy.activity.map((a, i) => (
                <div key={i} className="ats-activity-row">
                  <div className="ats-activity-dot" />
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div style={{ fontSize: 12.5 }}>{a.text}</div>
                    <div className="muted mono" style={{ fontSize: 11, marginTop: 2 }}>{a.when}</div>
                  </div>
                </div>
              ))}
            </div>
          )
        }
      </div>
    </div>
  );
};

// ─── Solicitud activa — fila expandible (detalle + pipeline propio) ──

const VacancySolicitudRow = ({ vacancy, expanded, onToggle, candidates, onOpen, onAddCandidate }) => (
  <div className="card" style={{ marginBottom: 12, padding: 0, overflow: 'hidden' }}>
    <button
      onClick={onToggle}
      style={{
        width: '100%', display: 'flex', alignItems: 'center', gap: 12,
        padding: '14px 18px', background: 'none', border: 'none', cursor: 'pointer', textAlign: 'left', font: 'inherit', color: 'inherit',
      }}
    >
      <IconChevronRight
        size={16}
        style={{ transform: expanded ? 'rotate(90deg)' : 'none', transition: 'transform .15s', flexShrink: 0, color: 'var(--text-muted)' }}
      />
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ fontWeight: 700, fontSize: 14 }}>{vacancy.role}</div>
        <div className="muted" style={{ fontSize: 12, marginTop: 2 }}>{vacancy.site} · {vacancy.area} · <span className="mono">{vacancy.id}</span></div>
      </div>
      <div style={{ textAlign: 'right' }}>
        <div className="muted-xs">Candidatos</div>
        <div style={{ fontWeight: 700, fontSize: 14 }}>{candidates.length}</div>
      </div>
      <span className="pill green" style={{ marginLeft: 8 }}>{vacancy.state === 'abierta' ? 'Abierta' : vacancy.state}</span>
    </button>

    {expanded && (
      <div style={{ borderTop: '1px solid var(--border)', padding: '18px' }}>
        <VacancyDetail vacancy={vacancy} candidates={candidates} onOpen={onOpen} />
        <div style={{ marginTop: 20 }}>
          <div className="muted-xs" style={{ marginBottom: 8 }}>Pipeline de esta solicitud</div>
          <Pipeline candidates={candidates} onOpen={onOpen} showRole={false} onAddCandidate={onAddCandidate} />
        </div>
      </div>
    )}
  </div>
);

// ─── Candidate side panel ─────────────────────────────────

const CandidatePanel = ({ candidate: c, vacancy, onClose, onOffer }) => {
  const src = SOURCE_META[c.source];
  // build a simple stage timeline up to current stage
  const reachedIdx = STAGES.findIndex(s => s.id === c.stage);
  const TIMELINE = STAGES.slice(0, reachedIdx + 1).map((s, i) => ({
    label: i === reachedIdx ? `Etapa actual · ${s.label}` : `Avanzó a ${s.label}`,
    when: i === reachedIdx ? 'En curso' : `${formatDaysAgo(c.days, reachedIdx, i)}`,
  })).reverse();

  return (
    <div className="tr-side-overlay" onMouseDown={(e) => { if (e.target === e.currentTarget) onClose(); }}>
      <div className="tr-side-panel" style={{ width: 680 }}>
        <div className="tr-side-head">
          <div style={{ display: 'flex', gap: 12, minWidth: 0, flex: 1 }}>
            <div className="ats-card-avatar lg">{c.initials}</div>
            <div style={{ minWidth: 0 }}>
              <div className="tr-side-title">{c.name}</div>
              <div className="muted" style={{ fontSize: 12, marginTop: 4 }}>
                {vacancy.role} · {vacancy.site} · <span className="mono">{c.id}</span>
              </div>
              <div style={{ display: 'flex', gap: 8, marginTop: 8, alignItems: 'center', flexWrap: 'wrap' }}>
                <span className={`ats-source-pill ${src.tone}`}>{src.label}</span>
                <span className="ats-rating">
                  {RATING_STARS(c.rating).map((on, i) => (
                    <svg key={i} width="12" height="12" viewBox="0 0 24 24" className={on ? 'on' : ''}>
                      <path d="M12 2.5l2.95 6 6.6.96-4.78 4.66 1.13 6.58L12 17.6l-5.9 3.1 1.13-6.58L2.45 9.46l6.6-.96L12 2.5z" />
                    </svg>
                  ))}
                </span>
                <span className="muted mono" style={{ fontSize: 11 }}>{c.days} días en proceso</span>
              </div>
            </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>

        <div className="tr-side-body">

          <section className="tr-side-section">
            <div className="muted-xs">Datos de contacto</div>
            <div className="ats-cand-grid">
              <div><div className="muted-xs">Email</div><div className="ats-cand-val mono">{c.email}</div></div>
              <div><div className="muted-xs">Teléfono</div><div className="ats-cand-val mono">{c.phone}</div></div>
              <div><div className="muted-xs">Ciudad</div><div className="ats-cand-val">{c.city}</div></div>
              <div><div className="muted-xs">Edad · experiencia</div><div className="ats-cand-val">{c.age} años · {c.exp}</div></div>
            </div>
          </section>

          <section className="tr-side-section">
            <div className="muted-xs">Hoja de vida adjunta</div>
            <div className="ats-cv-row">
              <div className="ats-cv-thumbs">
                {[1, 2, 3].map(p => (
                  <div key={p} className="doc-page-thumb">
                    <div className="doc-page-lines">
                      {Array.from({ length: 14 }).map((_, i) => (
                        <span key={i} style={{ width: `${65 + ((i * 7 + p) % 30)}%` }} />
                      ))}
                    </div>
                    <div className="doc-page-num">{p}</div>
                  </div>
                ))}
              </div>
              <div className="ats-cv-meta">
                <div style={{ fontWeight: 600, fontSize: 13 }}>HV-{c.id}.pdf</div>
                <div className="muted mono" style={{ fontSize: 11.5, marginTop: 3 }}>3 páginas · 412 KB · cargado hace {c.days}d</div>
                <button className="btn" style={{ marginTop: 10, width: '100%' }}><IconExternal size={13} /> Abrir CV completo</button>
              </div>
            </div>
          </section>

          <section className="tr-side-section">
            <div className="muted-xs">Evaluaciones y notas</div>
            {c.notes
              ? (
                <div className="ats-note">
                  <div className="ats-note-head">
                    <div className="doc-avatar">CR</div>
                    <div>
                      <div style={{ fontWeight: 600, fontSize: 12.5 }}>Carolina Restrepo</div>
                      <div className="muted" style={{ fontSize: 11 }}>Jefe de RRHH · 02/05/2026</div>
                    </div>
                  </div>
                  <div style={{ fontSize: 13, lineHeight: 1.5, marginTop: 8 }}>{c.notes}</div>
                </div>
              )
              : <div className="muted" style={{ fontSize: 12.5, marginTop: 6 }}>Sin notas registradas. Agrega una desde "Avanzar etapa" o "Descartar".</div>
            }
            <button className="btn" style={{ marginTop: 10 }}><IconPlus size={13} /> Agregar nota / evaluación</button>
          </section>

          <section className="tr-side-section">
            <div className="muted-xs">Línea de tiempo del proceso</div>
            <div className="doc-timeline" style={{ marginTop: 6 }}>
              {TIMELINE.map((t, i) => (
                <div key={i} className={`doc-tl-row doc-tl-${i === 0 ? 'pending' : 'signed'}`}>
                  <div className="doc-tl-rail">
                    <div className="doc-tl-dot">{i === 0 ? <IconClock size={11} /> : <IconCheck size={11} />}</div>
                    {i < TIMELINE.length - 1 && <div className="doc-tl-line" />}
                  </div>
                  <div className="doc-tl-body">
                    <div className="doc-tl-label">{t.label}</div>
                    <div className="doc-tl-detail muted">{t.when}</div>
                  </div>
                </div>
              ))}
            </div>
          </section>
        </div>

        <div className="tr-side-foot">
          {c.finalState === 'contratado' ? (
            <>
              <span className="muted" style={{ fontSize: 12, marginRight: 'auto' }}>
                <IconCheck size={12} style={{ color: 'var(--green-600)' }} /> Contratado · vinculado al sistema
              </span>
              <button className="btn primary"><IconExternal size={13} /> Ver ficha de empleado</button>
            </>
          ) : c.finalState === 'descartado' ? (
            <>
              <span className="muted" style={{ fontSize: 12, marginRight: 'auto' }}>
                Candidato descartado · histórico
              </span>
              <button className="btn">Reabrir proceso</button>
            </>
          ) : (
            <>
              <button className="btn ats-danger" style={{ marginRight: 'auto' }}>Descartar</button>
              {c.stage === 'oferta' ? (
                <button className="btn primary"><IconExternal size={13} /> Iniciar vinculación</button>
              ) : c.stage === 'entr-jefe' ? (
                <>
                  <button className="btn"><IconCheck size={13} /> Avanzar etapa</button>
                  <button className="btn primary" onClick={onOffer}>
                    <IconMegaphone size={13} /> Enviar oferta
                  </button>
                </>
              ) : (
                <button className="btn primary"><IconCheck size={13} /> Avanzar a {STAGES[Math.min(STAGES.findIndex(s => s.id === c.stage) + 1, STAGES.length - 1)].label}</button>
              )}
            </>
          )}
        </div>
      </div>
    </div>
  );
};

// helper for fake timeline timestamps
const formatDaysAgo = (totalDays, currentIdx, eventIdx) => {
  const back = Math.max(1, Math.round(totalDays * (currentIdx - eventIdx) / Math.max(1, currentIdx)));
  return `hace ${back} día${back === 1 ? '' : 's'}`;
};

// ─── Offer modal ──────────────────────────────────────────

const OfferModal = ({ candidate, vacancy, onClose }) => {
  const [stage, setStage] = _ats_useState('preview'); // preview | sent | accepted
  const [salary, setSalary] = _ats_useState(vacancy.salary.split('–')[0].trim());
  const [start, setStart] = _ats_useState('15/05/2026');
  const [contract, setContract] = _ats_useState(vacancy.contract);

  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 send = () => {
    setStage('sent');
    // simulate acceptance after a beat
    setTimeout(() => setStage('accepted'), 1800);
  };

  if (stage === 'sent') {
    return (
      <div className="modal-overlay" onMouseDown={(e) => { if (e.target === e.currentTarget) onClose(); }}>
        <div className="modal" style={{ maxWidth: 520 }}>
          <div className="ats-offer-state">
            <div className="ats-offer-spinner" />
            <div style={{ fontSize: 17, fontWeight: 700, marginTop: 14 }}>Enviando oferta…</div>
            <div className="muted" style={{ fontSize: 13, marginTop: 4 }}>
              Se está enviando la carta de oferta a <b>{candidate.email}</b>
            </div>
          </div>
        </div>
      </div>
    );
  }

  if (stage === 'accepted') {
    return (
      <div className="modal-overlay" onMouseDown={(e) => { if (e.target === e.currentTarget) onClose(); }}>
        <div className="modal" style={{ maxWidth: 540 }}>
          <div className="ats-offer-state">
            <div className="sig-success-icon"><IconCheck size={30} /></div>
            <div style={{ fontSize: 19, fontWeight: 700, marginTop: 14, letterSpacing: '-0.01em' }}>
              ¡Oferta aceptada por {candidate.name.split(' ')[0]}!
            </div>
            <div className="muted" style={{ fontSize: 13.5, marginTop: 6, maxWidth: 380 }}>
              Confirmó vía email y firmó electrónicamente la carta. Ya puedes iniciar la vinculación al sistema — se creará la ficha de empleado y se activarán las matrices automáticas.
            </div>
            <div className="ats-offer-evidence">
              <div className="ats-offer-ev-row"><span className="muted-xs">Aceptada el</span><span className="mono" style={{ fontSize: 12 }}>05/05/2026 14:42:08</span></div>
              <div className="ats-offer-ev-row"><span className="muted-xs">Cargo</span><span style={{ fontSize: 12.5 }}>{vacancy.role}</span></div>
              <div className="ats-offer-ev-row"><span className="muted-xs">Salario</span><span className="mono" style={{ fontSize: 12 }}>{salary}</span></div>
              <div className="ats-offer-ev-row"><span className="muted-xs">Inicio</span><span className="mono" style={{ fontSize: 12 }}>{start}</span></div>
            </div>
            <div style={{ display: 'flex', gap: 8, marginTop: 16, width: '100%' }}>
              <button className="btn" style={{ flex: 1 }} onClick={onClose}>Cerrar</button>
              <button className="btn primary" style={{ flex: 1 }}>
                <IconRocket size={14} /> Iniciar vinculación
              </button>
            </div>
          </div>
        </div>
      </div>
    );
  }

  return (
    <div className="modal-overlay" onMouseDown={(e) => { if (e.target === e.currentTarget) onClose(); }}>
      <div className="modal" style={{ maxWidth: 1020 }}>
        <div className="modal-head">
          <div>
            <div className="modal-title">Enviar oferta — {candidate.name}</div>
            <div className="modal-sub">Resumen de la oferta y vista previa de la carta. Al enviar, se notificará al candidato vía email.</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>

        <div className="modal-body">
          <div className="ats-offer-grid">
            {/* Form column */}
            <div className="ats-offer-col">
              <div className="muted-xs">Resumen de la oferta</div>

              <div className="ats-offer-summary">
                <div className="ats-offer-summary-row">
                  <div className="muted-xs">Candidato</div>
                  <div style={{ fontSize: 13.5, fontWeight: 600 }}>{candidate.name}</div>
                </div>
                <div className="ats-offer-summary-row">
                  <div className="muted-xs">Email destino</div>
                  <div className="mono" style={{ fontSize: 12.5 }}>{candidate.email}</div>
                </div>
                <div className="ats-offer-summary-row">
                  <div className="muted-xs">Cargo</div>
                  <div style={{ fontSize: 13 }}>{vacancy.role} · {vacancy.area}</div>
                </div>
                <div className="ats-offer-summary-row">
                  <div className="muted-xs">Sede de trabajo</div>
                  <div style={{ fontSize: 13 }}>{vacancy.site}</div>
                </div>
              </div>

              <div className="ats-offer-fields">
                <div>
                  <label className="muted-xs">Tipo de contrato</label>
                  <select value={contract} onChange={(e) => setContract(e.target.value)}>
                    <option>Indefinido</option>
                    <option>Fijo · 1 año</option>
                    <option>Fijo · 6 meses</option>
                    <option>Obra o labor</option>
                    <option>Aprendizaje</option>
                  </select>
                </div>
                <div>
                  <label className="muted-xs">Salario base mensual</label>
                  <input type="text" value={salary} onChange={(e) => setSalary(e.target.value)} />
                </div>
                <div>
                  <label className="muted-xs">Fecha de inicio</label>
                  <window.DatePickerCO value={start} onChange={setStart} />
                </div>
                <div>
                  <label className="muted-xs">Vence el</label>
                  <input type="text" value="12/05/2026" disabled />
                </div>
              </div>

              <div className="ats-offer-template">
                <div className="muted-xs">Plantilla de carta</div>
                <div className="ats-offer-template-row">
                  <IconDoc size={14} />
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div style={{ fontSize: 12.5, fontWeight: 600 }}>Carta de oferta laboral · v2.4</div>
                    <div className="muted mono" style={{ fontSize: 11 }}>plantilla-oferta-aguila.docx · 7 variables</div>
                  </div>
                  <button className="link-btn" style={{ marginLeft: 0 }}>Cambiar</button>
                </div>
              </div>
            </div>

            {/* Letter preview */}
            <div className="ats-offer-col">
              <div className="muted-xs">Vista previa de la carta</div>
              <div className="ats-offer-letter">
                <div className="ats-offer-letter-head">
                  <div className="sig-brand-mark">a</div>
                  <div>
                    <div style={{ fontWeight: 700, fontSize: 12 }}>ATICA S.A.S.</div>
                    <div className="muted mono" style={{ fontSize: 10.5 }}>NIT 900.123.456-7 · Cali</div>
                  </div>
                  <div className="muted mono" style={{ marginLeft: 'auto', fontSize: 10.5, textAlign: 'right' }}>
                    Cali, 05/05/2026<br />Ref: OFR-{candidate.id}
                  </div>
                </div>
                <h3 style={{ fontSize: 13.5, fontWeight: 700, marginBottom: 12 }}>Estimado/a {candidate.name},</h3>
                <p style={{ fontSize: 12, lineHeight: 1.7, marginBottom: 10 }}>
                  Nos complace formalizar la presente oferta de vinculación laboral con <strong>Atica S.A.S.</strong> en el cargo de <strong>{vacancy.role}</strong>, en nuestra sede de <strong>{vacancy.site}</strong>, conforme a los siguientes términos:
                </p>
                <ul style={{ fontSize: 12, lineHeight: 1.65, paddingLeft: 18, marginBottom: 12 }}>
                  <li><strong>Tipo de contrato:</strong> {contract}</li>
                  <li><strong>Salario mensual:</strong> {salary}</li>
                  <li><strong>Fecha de inicio:</strong> {start}</li>
                  <li><strong>Jornada:</strong> 48 h/semana · turnos según operación</li>
                  <li><strong>Período de prueba:</strong> 2 meses</li>
                </ul>
                <p style={{ fontSize: 12, lineHeight: 1.7, marginBottom: 10 }}>
                  La presente oferta tiene vigencia hasta el <strong>12/05/2026</strong>. Para aceptarla, basta con responder este correo electrónico o firmar electrónicamente desde el botón al final de la carta.
                </p>
                <p style={{ fontSize: 12, lineHeight: 1.7, marginBottom: 24 }}>
                  Quedamos atentos a su confirmación.
                </p>
                <div style={{ borderTop: '1px solid var(--border)', paddingTop: 12 }}>
                  <div style={{ fontSize: 12, fontWeight: 700 }}>Carolina Restrepo Salazar</div>
                  <div className="muted" style={{ fontSize: 11 }}>Jefe de Recursos Humanos</div>
                </div>
              </div>
            </div>
          </div>
        </div>

        <div className="modal-foot">
          <span className="muted" style={{ fontSize: 12, marginRight: 'auto' }}>
            <IconShield size={12} /> Carta firmada electrónicamente desde tu nombre
          </span>
          <button className="btn" onClick={onClose}>Cancelar</button>
          <button className="btn">Guardar borrador</button>
          <button className="btn primary" onClick={send}>
            <IconMegaphone size={14} /> Enviar oferta
          </button>
        </div>
      </div>
    </div>
  );
};

// ─── New candidate modal (Postulados) ─────────────────────

const NewCandidateModal = ({ defaultVacancyId, onClose, onSave }) => {
  const [form, setForm] = _ats_useState({
    name: '', docType: DOC_TYPES_CANDIDATO[0], doc: '', age: '', exp: '',
    cvFileName: '', notes: '', vacancyId: defaultVacancyId, source: VINCULACION_OPTIONS[0].value,
  });
  const set = (k, v) => setForm(f => ({ ...f, [k]: v }));
  const canSave = form.name.trim() && form.doc.trim() && form.age && form.vacancyId;

  return (
    <Modal open onClose={onClose}
      title="Nuevo postulado"
      subtitle="Registra un candidato directamente en la etapa Postulados"
      width={640}
      footer={
        <>
          <button className="btn" onClick={onClose}>Cancelar</button>
          <button className="btn primary" disabled={!canSave} onClick={() => onSave(form)}>
            <IconPlus size={14} /> Crear postulado
          </button>
        </>
      }>
      <div className="form-grid">
        <FormField label="Nombre completo" span={6}>
          <input className="form-input" placeholder="Ej. María Fernanda López" value={form.name} onChange={e => set('name', e.target.value)} />
        </FormField>
        <FormField label="Tipo de documento" span={3}>
          <select className="form-input" value={form.docType} onChange={e => set('docType', e.target.value)}>
            {DOC_TYPES_CANDIDATO.map(t => <option key={t} value={t}>{t}</option>)}
          </select>
        </FormField>
        <FormField label="Número de documento" span={3}>
          <input className="form-input" placeholder="1.087.456.231" value={form.doc} onChange={e => set('doc', e.target.value)} />
        </FormField>
        <FormField label="Edad" span={3}>
          <input className="form-input" type="number" min="18" max="70" placeholder="28" value={form.age} onChange={e => set('age', e.target.value)} />
        </FormField>
        <FormField label="Experiencia" span={3}>
          <input className="form-input" placeholder="Ej. 2 años" value={form.exp} onChange={e => set('exp', e.target.value)} />
        </FormField>
        <FormField label="Vacante a la que se postula" span={3}>
          <select className="form-input" value={form.vacancyId} onChange={e => set('vacancyId', e.target.value)}>
            {VACANCIES.map(v => <option key={v.id} value={v.id}>{v.role} · {v.site}</option>)}
          </select>
        </FormField>
        <FormField label="Forma de vinculación" span={3}>
          <select className="form-input" value={form.source} onChange={e => set('source', e.target.value)}>
            {VINCULACION_OPTIONS.map(o => <option key={o.value} value={o.value}>{o.label}</option>)}
          </select>
        </FormField>
        <FormField label="Hoja de vida (PDF)" span={6}>
          <input className="form-input" type="file" accept="application/pdf" onChange={e => set('cvFileName', e.target.files[0]?.name || '')} />
          {form.cvFileName && <div className="muted mono" style={{ fontSize: 11.5, marginTop: 4 }}>{form.cvFileName}</div>}
        </FormField>
        <FormField label="Observación" span={6}>
          <textarea className="form-input" rows={3} placeholder="Notas sobre el candidato o el proceso de postulación" value={form.notes} onChange={e => set('notes', e.target.value)} />
        </FormField>
      </div>
    </Modal>
  );
};

// ─── New vacancy — type selection modal ───────────────────

const VACANCY_TYPE_SECTIONS = [
  {
    id: 'reclutamiento', label: 'Reclutamiento',
    types: [
      { id: 'reemplazo', label: 'Reemplazo',           sub: 'Sustituye a un colaborador que salió de la posición' },
      { id: 'nomina',    label: 'Ampliación de nómina', sub: 'Posición nueva, aumenta el headcount aprobado' },
    ],
  },
  {
    id: 'movimiento', label: 'Movimiento de personal',
    types: [
      { id: 'ascenso',  label: 'Ascenso',  sub: 'Promoción de un colaborador a un cargo de mayor nivel' },
      { id: 'traslado', label: 'Traslado', sub: 'Movimiento interno de un colaborador a esta posición' },
    ],
  },
  {
    id: 'retiros', label: 'Retiros de personal',
    types: [
      { id: 'retiro', label: 'Retiros', sub: 'Registro de salida o desvinculación de un colaborador' },
    ],
  },
  {
    id: 'otros', label: 'Otros',
    types: [
      { id: 'nueva-operacion',   label: 'Nueva operación',    sub: 'Apertura de una nueva línea, sede o proceso' },
      { id: 'contrataciones',    label: 'Contrataciones',     sub: 'Solicitud general de contratación' },
      { id: 'cambios-aumentos',  label: 'Cambios y aumentos', sub: 'Cambios de condiciones o aumentos salariales' },
      { id: 'descargos',         label: 'Descargos',          sub: 'Proceso disciplinario o descargo formal' },
      { id: 'recordatorios',     label: 'Recordatorios',      sub: 'Recordatorios y notificaciones internas' },
      { id: 'paz-salvos',        label: 'Paz y salvos',       sub: 'Solicitud de paz y salvo para un colaborador' },
    ],
  },
];

const NewVacancyTypeModal = ({ onClose, onContinue }) => {
  const [type, setType] = _ats_useState('reemplazo');

  return (
    <Modal open onClose={onClose}
      title="Nueva vacante"
      subtitle="Selecciona el tipo de solicitud para continuar"
      width={560}
      footer={
        <>
          <button className="btn" onClick={onClose}>Cancelar</button>
          <button className="btn primary" onClick={() => onContinue(type)}>Continuar <IconChevronRight size={14} /></button>
        </>
      }>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 20 }}>
        {VACANCY_TYPE_SECTIONS.map(sec => (
          <div key={sec.id}>
            <div className="muted-xs" style={{ marginBottom: 8 }}>{sec.label.toUpperCase()}</div>
            <div className="range-list">
              {sec.types.map(t => (
                <label key={t.id} className={`range-row ${type === t.id ? 'active' : ''}`}>
                  <input type="radio" name="vac-type" checked={type === t.id} onChange={() => setType(t.id)} />
                  <div>
                    <div style={{ fontSize: 13, fontWeight: 500 }}>{t.label}</div>
                    <div className="muted" style={{ fontSize: 11.5 }}>{t.sub}</div>
                  </div>
                </label>
              ))}
            </div>
          </div>
        ))}
      </div>
    </Modal>
  );
};

const findVacancyTypeLabel = (id) => {
  for (const sec of VACANCY_TYPE_SECTIONS) {
    const t = sec.types.find(x => x.id === id);
    if (t) return t.label;
  }
  return 'Solicitud';
};

const VacancyStubModal = ({ type, onBack, onClose }) => (
  <Modal open onClose={onClose}
    title={findVacancyTypeLabel(type)}
    subtitle="Flujo en construcción"
    width={480}
    footer={<button className="btn" onClick={onBack}><IconChevronLeft size={14} /> Volver</button>}>
    <EmptyState
      icon="IconRocket"
      title="Este flujo se define en una próxima iteración"
      description="La selección de tipo ya está habilitada. El formulario específico para esta solicitud se construirá a continuación."
    />
  </Modal>
);

// ─── New vacancy — Reemplazo (paso 2) ──────────────────────

const REEMPLAZO_SORT_COLUMNS = [
  ['doc', 'Documento'], ['name', 'Nombre'], ['empresa', 'Empresa'],
  ['role', 'Cargo'], ['city', 'Ciudad'], ['estado', 'Estado'],
];

const ReemplazoModal = ({ onBack, onViewEmployee, onFinish }) => {
  const [tab, setTab] = _ats_useState('vacante');
  const [search, setSearch] = _ats_useState('');
  const [estadoFilter, setEstadoFilter] = _ats_useState('todos');
  const [empresaFilter, setEmpresaFilter] = _ats_useState('todas');
  const [sortKey, setSortKey] = _ats_useState('name');
  const [sortDir, setSortDir] = _ats_useState('asc');
  const [replaceTarget, setReplaceTarget] = _ats_useState(null);

  const empresas = [...new Set(REPORTES_A_CARGO.map(p => p.empresa))];

  const toggleSort = (key) => {
    if (sortKey === key) setSortDir(d => d === 'asc' ? 'desc' : 'asc');
    else { setSortKey(key); setSortDir('asc'); }
  };

  const rows = REPORTES_A_CARGO
    .filter(p => !search || p.name.toLowerCase().includes(search.toLowerCase()) || p.doc.toLowerCase().includes(search.toLowerCase()))
    .filter(p => estadoFilter === 'todos' || p.estado === estadoFilter)
    .filter(p => empresaFilter === 'todas' || p.empresa === empresaFilter)
    .sort((a, b) => {
      const av = String(a[sortKey]).toLowerCase();
      const bv = String(b[sortKey]).toLowerCase();
      if (av < bv) return sortDir === 'asc' ? -1 : 1;
      if (av > bv) return sortDir === 'asc' ? 1 : -1;
      return 0;
    });

  return (
    <>
      <Modal open onClose={onBack}
        title="Seleccionar tipo de Reemplazo"
        subtitle="Elige a quién reemplaza esta vacante entre los colaboradores a tu cargo"
        width={1040}
        footer={
          <>
            <button className="btn" onClick={onBack}><IconChevronLeft size={14} /> Volver</button>
          </>
        }>
        <div style={{ display: 'flex', justifyContent: 'center', marginBottom: 20 }}>
          <div className="seg-pill">
            {REEMPLAZO_TABS.map(t => (
              <button
                key={t.id}
                className={`seg-pill-btn ${tab === t.id ? 'active' : ''}`}
                onClick={() => setTab(t.id)}
              >
                {t.label}
              </button>
            ))}
          </div>
        </div>

        <div className="sst-quickfilter" style={{ marginBottom: 14 }}>
          <input
            type="text" placeholder="Buscar por nombre o documento…" value={search} onChange={e => setSearch(e.target.value)}
            style={{ fontSize: 12.5, padding: '6px 10px', border: '1px solid var(--border)', borderRadius: 8, minWidth: 200 }}
          />
          {['todos', 'activo', 'inactivo'].map(s => (
            <button key={s} className={`sst-chip ${estadoFilter === s ? 'active' : ''}`} onClick={() => setEstadoFilter(s)}>
              {s === 'todos' ? 'Todos' : s === 'activo' ? 'Activos' : 'Inactivos'}
            </button>
          ))}
          <select value={empresaFilter} onChange={e => setEmpresaFilter(e.target.value)} style={{ fontSize: 12.5, padding: '6px 10px' }}>
            <option value="todas">Todas las empresas</option>
            {empresas.map(e => <option key={e} value={e}>{e}</option>)}
          </select>
        </div>

        <table className="data-table">
          <thead>
            <tr>
              {REEMPLAZO_SORT_COLUMNS.map(([key, label]) => (
                <th key={key} onClick={() => toggleSort(key)} style={{ cursor: 'pointer', userSelect: 'none' }}>
                  {label} {sortKey === key && (sortDir === 'asc' ? '↑' : '↓')}
                </th>
              ))}
              <th style={{ textAlign: 'right' }}>Funciones</th>
            </tr>
          </thead>
          <tbody>
            {rows.map(p => (
              <tr key={p.empId}>
                <td className="mono muted">{p.doc}</td>
                <td style={{ fontWeight: 500 }}>{p.name}</td>
                <td>{p.empresa}</td>
                <td>{p.role}</td>
                <td>{p.city}</td>
                <td><span className={`pill ${p.estado === 'activo' ? 'green' : ''}`}>{p.estado === 'activo' ? 'Activo' : 'Inactivo'}</span></td>
                <td style={{ textAlign: 'right' }}>
                  <div style={{ display: 'inline-flex', gap: 6 }}>
                    <button className="btn" style={{ padding: '4px 10px', fontSize: 12 }} onClick={() => onViewEmployee(p.empId)}>Ver empleado</button>
                    <button className="btn primary" style={{ padding: '4px 10px', fontSize: 12 }} onClick={() => setReplaceTarget(p)}>Reemplazar</button>
                  </div>
                </td>
              </tr>
            ))}
            {rows.length === 0 && (
              <tr><td colSpan={7} className="muted" style={{ textAlign: 'center', padding: 20, fontSize: 12.5 }}>Ningún colaborador coincide con los filtros.</td></tr>
            )}
          </tbody>
        </table>
      </Modal>

      {replaceTarget && (
        <ReplaceConfirmModal
          employee={replaceTarget}
          showVacanteQuestions={tab === 'vacante'}
          showIncapacidadFields={tab === 'incapacidad'}
          showVacacionesFields={tab === 'vacaciones'}
          onCancel={() => setReplaceTarget(null)}
          onConfirm={() => { setReplaceTarget(null); onFinish(); }}
        />
      )}
    </>
  );
};

// Se dibuja a mano (sin el componente Modal compartido) porque va anidada
// sobre ReemplazoModal, que ya está abierto: dos Modal a la vez registrarían
// dos listeners de tecla Escape globales y un Escape aquí también cerraría
// el wizard de fondo. Este overlay solo se cierra con los botones o clic afuera.
const formatCOPAts = (n) => '$ ' + Number(n).toLocaleString('es-CO');

const ReadOnlyField = ({ label, value }) => (
  <div className="field">
    <div className="field-lbl">{label}</div>
    <div className="field-val">{value}</div>
  </div>
);

// Se dibuja a mano (sin el componente Modal compartido) porque va anidada
// sobre ReemplazoModal, que ya está abierto: dos Modal a la vez registrarían
// dos listeners de tecla Escape globales y un Escape aquí también cerraría
// el wizard de fondo. Este overlay solo se cierra con los botones o clic afuera.
const MIN_DIAS_INCAPACIDAD = 15;

const computeDiasEntreFechas = (inicio, fin) => {
  if (!inicio || !fin || fin <= inicio) return null;
  return Math.round((new Date(fin) - new Date(inicio)) / 86400000);
};

// Bloque de fecha inicio / fecha fin / días calculados, reutilizado para
// Incapacidad y Vacaciones — misma mecánica, distintas etiquetas y mensajes.
const DateRangeDaysField = ({ labelInicio, labelFin, labelDias, fechaInicio, onInicioChange, fechaFin, onFinChange, dias, warningMessage }) => {
  const fechaFinInvalida = fechaInicio && fechaFin && fechaFin <= fechaInicio;
  const insuficiente = dias !== null && dias < MIN_DIAS_INCAPACIDAD;
  const minFechaFin = fechaInicio
    ? new Date(new Date(fechaInicio).getTime() + 86400000).toISOString().slice(0, 10)
    : undefined;

  return (
    <div style={{ marginTop: 20 }}>
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 14 }}>
        <div className="furat-row">
          <label className="furat-label">{labelInicio}</label>
          <input type="date" value={fechaInicio} onChange={ev => onInicioChange(ev.target.value)} />
        </div>
        <div className="furat-row">
          <label className="furat-label">{labelFin}</label>
          <input type="date" value={fechaFin} min={minFechaFin} onChange={ev => onFinChange(ev.target.value)} />
        </div>
        <div className="furat-row">
          <label className="furat-label">{labelDias}</label>
          <input type="text" disabled value={dias !== null ? `${dias} día${dias === 1 ? '' : 's'}` : '—'} />
        </div>
      </div>
      {fechaFinInvalida && (
        <div className="sst-restr" style={{ marginTop: 10, background: 'var(--rose-100)' }}>
          <IconAlert size={12} /> <span>La fecha fin debe ser posterior a la fecha de inicio.</span>
        </div>
      )}
      {insuficiente && (
        <div className="sst-restr" style={{ marginTop: 10, background: 'var(--rose-100)' }}>
          <IconAlert size={12} /> <span>{warningMessage}</span>
        </div>
      )}
    </div>
  );
};

const PERSONAS_ENCARGADAS = [
  'Carolina Restrepo S. · Jefe de RRHH',
  'María Trujillo Henao · Aux. Recursos Humanos',
  'Patricia Gómez Lara · RRHH',
];

const ReplaceConfirmModal = ({ employee: e, showVacanteQuestions, showIncapacidadFields, showVacacionesFields, onCancel, onConfirm }) => {
  const [observaciones, setObservaciones] = _ats_useState('');
  const [personaEncargada] = _ats_useState(() => PERSONAS_ENCARGADAS[Math.floor(Math.random() * PERSONAS_ENCARGADAS.length)]);
  // 'ninguno' | 'cargoNuevo' | 'aumentoSalario' — mutuamente excluyentes:
  // elegir Sí en una pregunta pone la otra en No automáticamente.
  const [vacanteTipo, setVacanteTipo] = _ats_useState('ninguno');
  const [nuevoCargo, setNuevoCargo] = _ats_useState('');
  const [nuevoSalario, setNuevoSalario] = _ats_useState('');

  const [fechaInicioIncap, setFechaInicioIncap] = _ats_useState('');
  const [fechaFinIncap, setFechaFinIncap] = _ats_useState('');
  const [fechaInicioVac, setFechaInicioVac] = _ats_useState('');
  const [fechaFinVac, setFechaFinVac] = _ats_useState('');

  const diasIncapacidad = computeDiasEntreFechas(fechaInicioIncap, fechaFinIncap);
  const diasVacaciones = computeDiasEntreFechas(fechaInicioVac, fechaFinVac);

  const incapacidadInsuficiente = showIncapacidadFields && diasIncapacidad !== null && diasIncapacidad < MIN_DIAS_INCAPACIDAD;
  const incapacidadIncompleta = showIncapacidadFields && diasIncapacidad === null;
  const vacacionesInsuficiente = showVacacionesFields && diasVacaciones !== null && diasVacaciones < MIN_DIAS_INCAPACIDAD;
  const vacacionesIncompleta = showVacacionesFields && diasVacaciones === null;
  const solicitarDisabled = incapacidadInsuficiente || incapacidadIncompleta || vacacionesInsuficiente || vacacionesIncompleta;

  return (
    <div className="modal-overlay" style={{ zIndex: 210 }} onMouseDown={(ev) => { if (ev.target === ev.currentTarget) onCancel(); }}>
      <div className="modal" style={{ maxWidth: 680, maxHeight: 'calc(100vh - 64px)' }}>
        <div className="modal-head">
          <div>
            <div className="modal-title">Solicitud de reemplazo</div>
            <div className="modal-sub">Reemplaza a {e.name} · los datos se toman por defecto del sistema, solo Observaciones es editable</div>
          </div>
          <button className="icon-btn" onClick={onCancel} 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>
        <div className="modal-body">
          {showVacanteQuestions && (
            <div style={{ marginBottom: 20 }}>
              <div className="furat-grid-2">
                <div className="furat-row">
                  <label className="furat-label">¿Cargo nuevo?</label>
                  <div className="furat-segment compact">
                    <button className={`furat-seg ${vacanteTipo === 'cargoNuevo' ? 'active green' : ''}`} type="button"
                      onClick={() => setVacanteTipo('cargoNuevo')}>Sí</button>
                    <button className={`furat-seg ${vacanteTipo !== 'cargoNuevo' ? 'active' : ''}`} type="button"
                      onClick={() => setVacanteTipo(t => t === 'cargoNuevo' ? 'ninguno' : t)}>No</button>
                  </div>
                </div>
                <div className="furat-row">
                  <label className="furat-label">¿Aumento de salario?</label>
                  <div className="furat-segment compact">
                    <button className={`furat-seg ${vacanteTipo === 'aumentoSalario' ? 'active green' : ''}`} type="button"
                      onClick={() => setVacanteTipo('aumentoSalario')}>Sí</button>
                    <button className={`furat-seg ${vacanteTipo !== 'aumentoSalario' ? 'active' : ''}`} type="button"
                      onClick={() => setVacanteTipo(t => t === 'aumentoSalario' ? 'ninguno' : t)}>No</button>
                  </div>
                </div>
              </div>
              {vacanteTipo === 'cargoNuevo' && (
                <div className="furat-row" style={{ marginTop: 12 }}>
                  <label className="furat-label">Nuevo cargo</label>
                  <input type="text" value={nuevoCargo} onChange={ev => setNuevoCargo(ev.target.value)} placeholder="Ej: Coordinador de Aprovechamiento" />
                </div>
              )}
              {vacanteTipo === 'aumentoSalario' && (
                <div className="furat-row" style={{ marginTop: 12 }}>
                  <label className="furat-label">Nuevo salario</label>
                  <input type="text" value={nuevoSalario} onChange={ev => setNuevoSalario(ev.target.value)} placeholder="$ 0" />
                </div>
              )}
            </div>
          )}

          <div className="field-grid">
            <ReadOnlyField label="Empresa" value={e.empresa} />
            <ReadOnlyField label="Nombre de colaborador" value={e.name} />
            <ReadOnlyField label="Área" value={e.area} />
            <ReadOnlyField label="Cargo" value={e.role} />
            <ReadOnlyField label="Jefe directo" value={e.jefeDirecto} />
            <ReadOnlyField label="Salario mínimo" value={formatCOPAts(e.salarioMinimo)} />
            <ReadOnlyField label="Municipio" value={e.city} />
            <ReadOnlyField label="Rodamiento" value={e.rodamiento} />
            <ReadOnlyField label="Requiere medio de transporte" value={e.transporte} />
            <ReadOnlyField label="Licencia de conducción" value={e.licencia} />
            <ReadOnlyField label="Centro de costos" value={e.centroCosto} />
          </div>

          <div style={{ marginTop: 20 }}>
            <ReadOnlyField label="Descripción" value={e.descripcion} />
          </div>
          <div style={{ marginTop: 16 }}>
            <ReadOnlyField label="Requerimientos" value={e.requerimientos} />
          </div>

          <div className="furat-row" style={{ marginTop: 20 }}>
            <label className="furat-label">Observaciones</label>
            <textarea
              rows={3} value={observaciones} onChange={ev => setObservaciones(ev.target.value)}
              placeholder="Agrega cualquier observación relevante para esta solicitud…"
            />
          </div>

          {showIncapacidadFields && (
            <DateRangeDaysField
              labelInicio="Fecha inicio incapacidad" labelFin="Fecha fin incapacidad" labelDias="Días de incapacidad"
              fechaInicio={fechaInicioIncap}
              onInicioChange={v => { setFechaInicioIncap(v); if (fechaFinIncap && fechaFinIncap <= v) setFechaFinIncap(''); }}
              fechaFin={fechaFinIncap} onFinChange={setFechaFinIncap}
              dias={diasIncapacidad}
              warningMessage="No se puede solicitar un reemplazo por una incapacidad inferior a 15 días"
            />
          )}

          {showVacacionesFields && (
            <DateRangeDaysField
              labelInicio="Fecha inicio vacaciones" labelFin="Fecha fin vacaciones" labelDias="Días de vacaciones"
              fechaInicio={fechaInicioVac}
              onInicioChange={v => { setFechaInicioVac(v); if (fechaFinVac && fechaFinVac <= v) setFechaFinVac(''); }}
              fechaFin={fechaFinVac} onFinChange={setFechaFinVac}
              dias={diasVacaciones}
              warningMessage="No se puede solicitar un reemplazo por vacaciones inferiores a 15 días"
            />
          )}

          <div style={{ marginTop: 20 }}>
            <ReadOnlyField label="Persona encargada" value={personaEncargada} />
          </div>
        </div>
        <div className="modal-foot">
          <button className="btn" onClick={onCancel}>Cancelar</button>
          <button className="btn primary" disabled={solicitarDisabled} onClick={onConfirm}><IconCheck size={14} /> Solicitar</button>
        </div>
      </div>
    </div>
  );
};

window.ATS = ATS;

// Reutilizados por components/solicitudes-personal.jsx
window.SOLICITUDES = SOLICITUDES;
window.SolicitudesTable = SolicitudesTable;
window.NuevaVacanteFlow = NuevaVacanteFlow;
