// Mi Perfil — autoservicio del usuario en sesión (Gestión Humana / Jefe / Colaborador)
//
// Rediseño completo dirigido por metadatos, a partir de la HU y de
// "Listado de conceptos.xlsx" (105 conceptos → FIELD_DEFS + ACADEMIC_FIELDS +
// CHILD_FIELDS). El archivo fuente se usó únicamente como dato: nombres de
// concepto, columna "Se actualiza" (quién edita) y columna BUK/SAP (dónde se
// almacena). No se siguió ningún texto del archivo como instrucción.
//
// Siempre es el perfil del usuario en sesión (persona activa según
// currentRole, igual que dashboard.jsx / sidebar.jsx) — nunca la ficha de
// otro empleado (eso sigue siendo employee-detail.jsx, sin tocar).

// ─────────────────────────────────────────── Utilidades ─────────
const _mp_uid = (prefix) => `${prefix}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 7)}`;
const _mp_digits10 = (s) => (s || '').replace(/\D/g, '').slice(0, 10);
const _mp_isEmail = (s) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(s || '');
const _mp_isEmpty = (v) => v === undefined || v === null || (typeof v === 'string' && v.trim() === '');
const _mp_maskAccount = (s) => {
  const digits = (s || '').replace(/\D/g, '');
  if (digits.length <= 4) return s || '';
  return `${'•'.repeat(digits.length - 4)}${digits.slice(-4)}`;
};
const _mp_maskCurrency = () => '$ ••••••••';
const _mp_todayISO = () => {
  const d = new Date();
  return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
};

// ─────────────────────────────────────────── Catálogos mock ─────────
const MP_PAISES = ['Colombia', 'República Dominicana', 'Guatemala', 'México', 'Perú', 'Chile', 'Panamá', 'España'];
const MP_DEPARTAMENTOS_MUNICIPIOS = {
  'Valle del Cauca': ['Cali', 'Yumbo', 'Palmira', 'Buenaventura'],
  'Cundinamarca': ['Bogotá D.C.', 'Soacha', 'Chía'],
  'Atlántico': ['Barranquilla', 'Soledad', 'Malambo'],
  'Antioquia': ['Medellín', 'Itagüí', 'Envigado'],
  'Santander': ['Bucaramanga', 'Floridablanca', 'Girón'],
};
const MP_TIPOS_VIVIENDA = ['Propia', 'En arriendo', 'Familiar'];
const MP_ESTRATOS = ['1', '2', '3', '4', '5', '6 o más'];
const MP_MEDIOS_TRANSPORTE = ['Carro', 'Moto', 'Transporte público'];
const MP_CATEGORIAS_LICENCIA = ['A1', 'A2', 'B1', 'B2', 'B3', 'C1', 'C2', 'C3'];
const MP_GRUPOS_ETNICOS = ['Afro', 'Indígena', 'Ninguno'];
const MP_TIPOS_FORMACION = ['Bachillerato', 'Técnico', 'Tecnólogo', 'Pregrado', 'Maestría', 'Especialización'];
const MP_TIPOS_DOC_HIJO = ['Tarjeta de identidad', 'Cédula'];
const MP_SEXOS_HIJO = ['Niño', 'Niña'];
const MP_PARENTESCOS = ['Padre / Madre', 'Hijo / Hija', 'Amigo / Amiga', 'Abuelo / Abuela'];
const MP_ESTADO_CIVIL = (genero) => (genero === 'F'
  ? ['Soltera', 'Casada', 'En unión libre', 'Divorciada']
  : ['Soltero', 'Casado', 'En unión libre', 'Divorciado']);
const MP_TALLAS = {
  pantsSize: { M: ['28', '30', '32', '34', '36', '38'], F: ['6', '8', '10', '12', '14', '16'] },
  shirtSize: { M: ['S', 'M', 'L', 'XL', 'XXL'], F: ['XS', 'S', 'M', 'L', 'XL'] },
  shoeSize: { M: ['38', '39', '40', '41', '42', '43'], F: ['35', '36', '37', '38', '39'] },
  overallSize: { M: ['S', 'M', 'L', 'XL', 'XXL'], F: ['XS', 'S', 'M', 'L', 'XL'] },
};

// ─────────────────────────────────────────── Metadatos de campos ─────────
// editableBy: 'hcm' | 'employee' | 'hcm_employee' | 'nomina' | 'tiempos' | 'automatico'
// storage:    'SOMOS' | 'SAP' | 'SOMOS+SAP'  (columnas BUK/SAP del Excel)
// forceLocked: aunque editableBy lo permita, queda bloqueado en Mi perfil (HU explícita).
const FIELD_DEFS = [
  // ── Datos personales ──
  { id: 'documentType', section: 'personal', label: 'Tipo de documento / ID Personal', editableBy: 'hcm', storage: 'SOMOS+SAP', fieldType: 'text' },
  { id: 'issueDate', section: 'personal', label: 'Fecha de emisión', editableBy: 'hcm', storage: 'SAP', fieldType: 'text' },
  { id: 'issueCountry', section: 'personal', label: 'País de emisión', editableBy: 'hcm', storage: 'SAP', fieldType: 'text' },
  { id: 'documentNumber', section: 'personal', label: 'Número de documento / NIF', editableBy: 'hcm', storage: 'SOMOS+SAP', fieldType: 'text' },
  { id: 'fullName', section: 'personal', label: 'Nombre', editableBy: 'hcm', storage: 'SOMOS+SAP', fieldType: 'text' },
  { id: 'birthCountry', section: 'personal', label: 'País de nacimiento / Nacional', editableBy: 'hcm', storage: 'SOMOS+SAP', fieldType: 'text' },
  { id: 'gender', section: 'personal', label: 'Género / Sexo', editableBy: 'hcm', storage: 'SOMOS', fieldType: 'text' },
  { id: 'bloodType', section: 'personal', label: 'RH', editableBy: 'hcm', storage: 'SOMOS', fieldType: 'text', sensitive: true },
  { id: 'maritalStatus', section: 'personal', label: 'Estado civil', editableBy: 'employee', storage: 'SOMOS+SAP', fieldType: 'select', optionsFrom: 'maritalStatus' },
  { id: 'birthDate', section: 'personal', label: 'Fecha de nacimiento', editableBy: 'hcm', storage: 'SOMOS+SAP', fieldType: 'text' },
  { id: 'birthDepartment', section: 'personal', label: 'Departamento de nacimiento', editableBy: 'hcm', storage: 'SAP', fieldType: 'text' },
  { id: 'birthPlace', section: 'personal', label: 'Lugar de nacimiento', editableBy: 'hcm', storage: 'SAP', fieldType: 'text' },
  { id: 'officePhone', section: 'personal', label: 'Teléfono oficina', editableBy: 'employee', forceLocked: true, storage: 'SOMOS', fieldType: 'text', helpText: 'Administrado por Gestión Humana' },
  { id: 'personalPhone', section: 'personal', label: 'Teléfono particular', editableBy: 'employee', storage: 'SOMOS+SAP', fieldType: 'phone', maxLength: 10 },
  { id: 'corporateEmail', section: 'personal', label: 'Email corporativo', editableBy: 'employee', forceLocked: true, storage: 'SOMOS', fieldType: 'text', helpText: 'Administrado por Gestión Humana' },
  { id: 'personalEmail', section: 'personal', label: 'Email personal', editableBy: 'employee', storage: 'SOMOS+SAP', fieldType: 'email' },
  { id: 'address', section: 'personal', label: 'Dirección', editableBy: 'employee', storage: 'SOMOS+SAP', fieldType: 'text', maxLength: 30, showAsterisk: true, helpText: 'Formato de dirección pendiente de validación con Gestión Humana' },
  { id: 'addressComplement', section: 'personal', label: 'CpoAdDirec', editableBy: 'employee', storage: 'SAP', fieldType: 'text' },
  { id: 'country', section: 'personal', label: 'País', editableBy: 'employee', storage: 'SAP', fieldType: 'select', options: MP_PAISES },
  { id: 'livingDepartment', section: 'personal', label: 'Área / Departamento de vivienda', editableBy: 'employee', storage: 'SAP', fieldType: 'select', dependency: { field: 'country', showWhen: 'Colombia' } },
  { id: 'municipality', section: 'personal', label: 'Municipio', editableBy: 'employee', storage: 'SOMOS+SAP', fieldType: 'select', dependency: { field: 'livingDepartment' } },
  { id: 'workAddress', section: 'personal', label: 'Dirección de trabajo', editableBy: 'hcm', storage: 'SAP', fieldType: 'text' },
  { id: 'workAddressComplement', section: 'personal', label: 'CpoAdDirec de trabajo', editableBy: 'hcm', storage: 'SAP', fieldType: 'text' },
  { id: 'workCountry', section: 'personal', label: 'País de trabajo', editableBy: 'hcm', storage: 'SAP', fieldType: 'text' },
  { id: 'workArea', section: 'personal', label: 'Área de trabajo', editableBy: 'hcm', storage: 'SAP', fieldType: 'text' },
  { id: 'workCity', section: 'personal', label: 'Ciudad / Municipio de trabajo', editableBy: 'hcm', storage: 'SAP', fieldType: 'text' },
  { id: 'scheduleRule', section: 'personal', label: 'Regla para plan de horario de trabajo', editableBy: 'tiempos', storage: 'SAP', fieldType: 'text' },
  { id: 'timeManagementStatus', section: 'personal', label: 'Estado de gestión de tiempos', editableBy: 'hcm', storage: 'SAP', fieldType: 'text' },
  { id: 'timeVoucher', section: 'personal', label: 'Comprobante de tiempos', editableBy: 'hcm', storage: 'SAP', fieldType: 'text' },
  { id: 'housingType', section: 'personal', label: 'Tipo de vivienda', editableBy: 'employee', storage: 'SOMOS', fieldType: 'select', options: MP_TIPOS_VIVIENDA },
  { id: 'stratum', section: 'personal', label: 'Estrato', editableBy: 'employee', storage: 'SOMOS', fieldType: 'select', options: MP_ESTRATOS },
  { id: 'transportMode', section: 'personal', label: 'Medio de transporte', editableBy: 'employee', storage: 'SOMOS', fieldType: 'select', options: MP_MEDIOS_TRANSPORTE },
  { id: 'hasLicense', section: 'personal', label: 'Tiene licencia', editableBy: 'employee', storage: 'SOMOS', fieldType: 'toggle' },
  { id: 'licenseCategory', section: 'personal', label: 'Categoría de la licencia', editableBy: 'employee', storage: 'SOMOS', fieldType: 'select', options: MP_CATEGORIAS_LICENCIA, dependency: { field: 'hasLicense', showWhen: 'Sí' } },
  { id: 'conflictVictim', section: 'personal', label: 'Víctima de conflicto armado', editableBy: 'employee', storage: 'SOMOS', fieldType: 'toggle', sensitive: true },
  { id: 'ethnicGroup', section: 'personal', label: 'Grupo étnico', editableBy: 'employee', storage: 'SOMOS', fieldType: 'select', options: MP_GRUPOS_ETNICOS, sensitive: true },

  // ── Grupo familiar (control + contacto de emergencia; hijos = colección aparte) ──
  { id: 'hasChildren', section: 'family', label: 'Tiene hijos', editableBy: 'employee', storage: 'SOMOS', fieldType: 'toggle' },
  { id: 'numberOfChildren', section: 'family', label: 'Número de hijos', editableBy: 'employee', storage: 'SOMOS', fieldType: 'numberSelect', min: 0, max: 12, dependency: { field: 'hasChildren', showWhen: 'Sí' } },
  { id: 'childrenUnder12', section: 'family', label: 'Hijos menores de 12 años', editableBy: 'employee', storage: 'SOMOS', fieldType: 'numberSelect', min: 0, maxFrom: 'numberOfChildren', dependency: { field: 'hasChildren', showWhen: 'Sí' } },
  { id: 'emergencyContactName', section: 'family', label: 'Nombre del contacto de emergencia', editableBy: 'employee', storage: 'SOMOS', fieldType: 'text', maxLength: 30 },
  { id: 'emergencyContactRelationship', section: 'family', label: 'Parentesco contacto de emergencia', editableBy: 'employee', storage: 'SOMOS', fieldType: 'select', options: MP_PARENTESCOS },
  { id: 'emergencyContactPhone', section: 'family', label: 'Teléfono contacto de emergencia', editableBy: 'employee', storage: 'SOMOS', fieldType: 'phone', maxLength: 10 },

  // ── Relación laboral (100% informativa) ──
  { id: 'measureReason', section: 'labor', label: 'Motivo de medida', editableBy: 'hcm', storage: 'SAP', fieldType: 'text' },
  { id: 'position', section: 'labor', label: 'Posición', editableBy: 'hcm', storage: 'SAP', fieldType: 'text' },
  { id: 'personnelGroup', section: 'labor', label: 'Grupo de personal', editableBy: 'hcm', storage: 'SAP', fieldType: 'text' },
  { id: 'personnelArea', section: 'labor', label: 'Área de personal', editableBy: 'hcm', storage: 'SAP', fieldType: 'text' },
  { id: 'personnelNumber', section: 'labor', label: 'Número de personal', editableBy: 'hcm', storage: 'SAP', fieldType: 'text' },
  { id: 'fileNumber', section: 'labor', label: 'Número de ficha', editableBy: 'hcm', storage: 'SOMOS', fieldType: 'text' },
  { id: 'employmentRelationship', section: 'labor', label: 'Relación laboral', editableBy: 'hcm', storage: 'SAP', fieldType: 'text' },
  { id: 'companyPayrollArea', section: 'labor', label: 'Sociedad / Área de nómina', editableBy: 'hcm', storage: 'SOMOS+SAP', fieldType: 'text' },
  { id: 'settlementArea', section: 'labor', label: 'Área de liquidación', editableBy: 'hcm', storage: 'SAP', fieldType: 'text' },
  { id: 'businessLine', section: 'labor', label: 'Línea de negocio', editableBy: 'hcm', storage: 'SOMOS', fieldType: 'text' },
  { id: 'process', section: 'labor', label: 'Proceso', editableBy: 'hcm', storage: 'SOMOS', fieldType: 'text' },
  { id: 'subprocess', section: 'labor', label: 'Subproceso', editableBy: 'hcm', storage: 'SOMOS', fieldType: 'text' },
  { id: 'regional', section: 'labor', label: 'Regional / División de personal', editableBy: 'hcm', storage: 'SOMOS+SAP', fieldType: 'text' },
  { id: 'workPlant', section: 'labor', label: 'Planta de trabajo / Subdivisión de personal', editableBy: 'hcm', storage: 'SOMOS+SAP', fieldType: 'text' },
  { id: 'costCenter', section: 'labor', label: 'Centro de costo', editableBy: 'hcm', storage: 'SOMOS+SAP', fieldType: 'text' },
  { id: 'jobTitle', section: 'labor', label: 'Cargo', editableBy: 'hcm', storage: 'SOMOS+SAP', fieldType: 'text' },
  { id: 'hireDate', section: 'labor', label: 'Fecha de ingreso', editableBy: 'hcm', storage: 'SOMOS+SAP', fieldType: 'text' },
  { id: 'directManager', section: 'labor', label: 'Jefe inmediato', editableBy: 'hcm', storage: 'SOMOS', fieldType: 'text' },
  { id: 'processDirector', section: 'labor', label: 'Director del proceso', editableBy: 'hcm', storage: 'SOMOS', fieldType: 'text' },
  { id: 'processManager', section: 'labor', label: 'Gerente del proceso', editableBy: 'hcm', storage: 'SOMOS', fieldType: 'text' },
  { id: 'weeklySchedule', section: 'labor', label: 'Horario semanal', editableBy: 'hcm', storage: 'SOMOS', fieldType: 'text' },
  { id: 'periodWorkHours', section: 'labor', label: 'Horas de trabajo del periodo', editableBy: 'nomina', storage: 'SAP', fieldType: 'text' },
  { id: 'workDays', section: 'labor', label: 'Días de la jornada', editableBy: 'hcm', storage: 'SOMOS', fieldType: 'text' },
  { id: 'payGroup', section: 'labor', label: 'Grupo', editableBy: 'nomina', storage: 'SAP', fieldType: 'text' },
  { id: 'baseSalary', section: 'labor', label: 'Sueldo básico', editableBy: 'nomina', storage: 'SOMOS+SAP', fieldType: 'currency', sensitive: true, maskable: true },
  { id: 'mileage', section: 'labor', label: 'Rodamiento', editableBy: 'nomina', storage: 'SOMOS+SAP', fieldType: 'currency', sensitive: true, maskable: true },
  { id: 'salaryType', section: 'labor', label: 'Tipo de salario', editableBy: 'nomina', storage: 'SOMOS+SAP', fieldType: 'text' },
  { id: 'bankRelationship', section: 'labor', label: 'Relación bancaria', editableBy: 'nomina', storage: 'SAP', fieldType: 'text' },
  { id: 'bankKey', section: 'labor', label: 'Clave de banco', editableBy: 'nomina', storage: 'SAP', fieldType: 'text', sensitive: true },
  { id: 'bankAccount', section: 'labor', label: 'Cuenta bancaria', editableBy: 'nomina', storage: 'SAP', fieldType: 'account', sensitive: true, maskable: true },
  { id: 'bankControlKey', section: 'labor', label: 'Clave de control de bancos', editableBy: 'nomina', storage: 'SAP', fieldType: 'text', sensitive: true },
  { id: 'accountOpeningId', section: 'labor', label: 'ID de apertura de cuenta', editableBy: 'nomina', storage: 'SAP', fieldType: 'text', sensitive: true },
  { id: 'contractType', section: 'labor', label: 'Tipo de contrato', editableBy: 'hcm', storage: 'SOMOS+SAP', fieldType: 'text' },
  { id: 'probationPeriod', section: 'labor', label: 'Periodo de prueba', editableBy: 'hcm', storage: 'SAP', fieldType: 'text' },
  { id: 'expirationDate', section: 'labor', label: 'Fecha de vencimiento', editableBy: 'hcm', storage: 'SOMOS+SAP', fieldType: 'text' },
  { id: 'procedure', section: 'labor', label: 'Procedimiento', editableBy: 'nomina', storage: 'SAP', fieldType: 'text' },

  // ── Seguridad Social y Prestaciones (100% informativa) ──
  { id: 'severanceFund', section: 'social', label: 'Fondo de Cesantías', editableBy: 'nomina', storage: 'SOMOS+SAP', fieldType: 'text' },
  { id: 'pensionFund', section: 'social', label: 'Fondo de Pensiones', editableBy: 'nomina', storage: 'SOMOS+SAP', fieldType: 'text' },
  { id: 'branchArp', section: 'social', label: 'Sucursal', editableBy: 'hcm', storage: 'SAP', fieldType: 'text', code: 'SUC-1', secondaryDesc: 'Registro técnico de sucursal · bloque Fondos / ARP del catálogo' },
  { id: 'arp', section: 'social', label: 'ARP', editableBy: 'hcm', storage: 'SAP', fieldType: 'text' },
  { id: 'riskCenter', section: 'social', label: 'Centro de trabajo / Clase de riesgo', editableBy: 'hcm', storage: 'SOMOS+SAP', fieldType: 'text' },
  { id: 'companyPercentage', section: 'social', label: 'Porcentaje de empresa', editableBy: 'hcm', storage: 'SAP', fieldType: 'text' },
  { id: 'compensationFund', section: 'social', label: 'Caja', editableBy: 'hcm', storage: 'SAP', fieldType: 'text' },
  { id: 'eps', section: 'social', label: 'EPS', editableBy: 'nomina', storage: 'SAP', fieldType: 'text' },
  { id: 'branchEps', section: 'social', label: 'Sucursal', editableBy: 'hcm', storage: 'SAP', fieldType: 'text', code: 'SUC-2', secondaryDesc: 'Registro técnico de sucursal · bloque Caja / EPS del catálogo' },
  { id: 'pilaData', section: 'social', label: 'Datos de planilla electrónica PILA', editableBy: 'hcm', storage: 'SAP', fieldType: 'text' },
  { id: 'contributorType', section: 'social', label: 'Tipo de cotizante', editableBy: 'hcm', storage: 'SAP', fieldType: 'text' },
  { id: 'contributorSubtype', section: 'social', label: 'Subtipo de cotizante', editableBy: 'hcm', storage: 'SAP', fieldType: 'text' },

  // ── Dotación ──
  { id: 'materialCode', section: 'dotacion', label: 'Código de material', editableBy: 'automatico', storage: 'SAP', fieldType: 'text' },
  { id: 'requiredSupply', section: 'dotacion', label: 'Dotación requerida', editableBy: 'automatico', storage: 'SAP', fieldType: 'text' },
  { id: 'pantsSize', section: 'dotacion', label: 'Talla de pantalón', editableBy: 'hcm_employee', storage: 'SAP', fieldType: 'select', optionsFrom: 'pantsSize' },
  { id: 'shirtSize', section: 'dotacion', label: 'Talla de camisa', editableBy: 'hcm_employee', storage: 'SAP', fieldType: 'select', optionsFrom: 'shirtSize' },
  { id: 'shoeSize', section: 'dotacion', label: 'Talla de zapatos', editableBy: 'hcm_employee', storage: 'SAP', fieldType: 'select', optionsFrom: 'shoeSize' },
  { id: 'overallSize', section: 'dotacion', label: 'Talla de overol', editableBy: 'hcm_employee', storage: 'SAP', fieldType: 'select', optionsFrom: 'overallSize' },
  { id: 'cap', section: 'dotacion', label: 'Gorra', editableBy: 'hcm', storage: 'SAP', fieldType: 'text' },
];
const FIELD_DEFS_BY_ID = Object.fromEntries(FIELD_DEFS.map(f => [f.id, f]));

// Plantillas de las dos colecciones (un registro = varias respuestas de este arreglo)
const ACADEMIC_FIELDS = [
  { id: 'trainingName', label: 'Nombre de la formación', editableBy: 'employee', storage: 'SOMOS', fieldType: 'text', maxLength: 30 },
  { id: 'institution', label: 'Institución', editableBy: 'employee', storage: 'SOMOS', fieldType: 'text', maxLength: 30 },
  { id: 'trainingType', label: 'Tipo de formación', editableBy: 'employee', storage: 'SOMOS', fieldType: 'select', options: MP_TIPOS_FORMACION },
];
const CHILD_FIELDS = [
  { id: 'docType', label: 'Tipo de documento', editableBy: 'employee', storage: 'SOMOS', fieldType: 'select', options: MP_TIPOS_DOC_HIJO },
  { id: 'docNumber', label: 'Número de documento', editableBy: 'employee', storage: 'SOMOS', fieldType: 'text', maxLength: 10, sensitive: true },
  { id: 'fullName', label: 'Nombre completo', editableBy: 'employee', storage: 'SOMOS', fieldType: 'text', maxLength: 30, sensitive: true },
  { id: 'sex', label: 'Sexo', editableBy: 'employee', storage: 'SOMOS', fieldType: 'select', options: MP_SEXOS_HIJO },
  { id: 'birthDate', label: 'Fecha de nacimiento', editableBy: 'employee', storage: 'SOMOS', fieldType: 'date' },
];

const isEditableInProfile = (f) => (f.editableBy === 'employee' || f.editableBy === 'hcm_employee') && !f.forceLocked;

const EDITABLE_BY_LABEL = {
  hcm: 'Gestión Humana', employee: 'El empleado', hcm_employee: 'Gestión Humana / El empleado',
  nomina: 'Nómina', tiempos: 'Tiempos', automatico: 'Automático',
};

const MP_TABS = [
  { id: 'personal', label: 'Datos personales' },
  { id: 'academic', label: 'Información académica' },
  { id: 'family', label: 'Grupo familiar' },
  { id: 'labor', label: 'Relación laboral' },
  { id: 'social', label: 'Seguridad Social y Prestaciones' },
  { id: 'dotacion', label: 'Dotación' },
];

// ─────────────────────────────────────────── Datos mock por persona ─────────
// Un solo objeto por persona: header (para el encabezado) + fields (valores
// de FIELD_DEFS) + formaciones[] + hijos[]. Cambia con el selector de rol.
const MY_PROFILE_DATA = {
  E0023: { // Carolina Restrepo — Gestión Humana
    header: { genero: 'F', jobTitle: 'Jefe de RRHH', sociedad: 'ASEI', oficina: '2201 — ASEI Cali', estadoLaboral: 'Activo' },
    fields: {
      documentType: 'Cédula de ciudadanía', issueDate: '02/02/2019', issueCountry: 'Colombia', documentNumber: '42.221.087',
      fullName: 'Carolina Restrepo S.', birthCountry: 'Colombia', gender: 'Femenino', bloodType: 'O+',
      maritalStatus: 'Casada', birthDate: '14/03/1988', birthDepartment: 'Valle del Cauca', birthPlace: 'Cali',
      officePhone: '(602) 485 3210 · Ext 101', personalPhone: '3114567821', corporateEmail: 'carolina.restrepo@atica.co',
      personalEmail: 'carolina.restrepo.s@gmail.com', address: 'Cra 45 # 12 - 34 Apto 502', addressComplement: 'Torre 2',
      country: 'Colombia', livingDepartment: 'Valle del Cauca', municipality: 'Cali',
      workAddress: 'Cra 1 # 20 - 15', workAddressComplement: 'Bodega 3', workCountry: 'Colombia', workArea: 'Administración',
      workCity: 'Cali', scheduleRule: 'HO-ADM-40', timeManagementStatus: 'Sincronizado', timeVoucher: 'TV-2026-0512',
      housingType: 'Propia', stratum: '4', transportMode: 'Carro', hasLicense: 'Sí', licenseCategory: 'B1',
      conflictVictim: 'No', ethnicGroup: 'Ninguno',
      hasChildren: 'Sí', numberOfChildren: '2', childrenUnder12: '1',
      emergencyContactName: 'Andrés Restrepo Vélez', emergencyContactRelationship: 'Hijo / Hija', emergencyContactPhone: '3009876543',
      measureReason: 'Sin medida activa', position: '30012345', personnelGroup: 'Administrativo', personnelArea: 'Gestión Humana',
      personnelNumber: '00230019', fileNumber: 'F-0023', employmentRelationship: 'Empleado', companyPayrollArea: 'ASEI',
      settlementArea: 'Cali · Administrativo', businessLine: 'Corporativo', process: 'Gestión Humana', subprocess: 'Talento y cultura',
      regional: 'Regional Occidente', workPlant: '2201 — ASEI Cali', costCenter: 'CC-1001', jobTitle: 'Jefe de RRHH',
      hireDate: '02/02/2019', directManager: 'Diana Isabel Ruiz', processDirector: 'Diana Isabel Ruiz', processManager: 'Diana Isabel Ruiz',
      weeklySchedule: 'L-V 7:00 a.m. - 5:00 p.m.', periodWorkHours: '160', workDays: 'Lunes a viernes', payGroup: 'GRP-ADM',
      baseSalary: '$ 8.500.000', mileage: '$ 0', salaryType: 'Mensual', bankRelationship: 'Nómina', bankKey: 'BK-0123',
      bankAccount: '00-4567-891234', bankControlKey: 'CB-88', accountOpeningId: 'AC-2019-0202', contractType: 'Indefinido',
      probationPeriod: 'Superado', expirationDate: 'No aplica', procedure: 'PR-NOM-01',
      severanceFund: 'Porvenir', pensionFund: 'Porvenir', branchArp: 'Cali Centro', arp: 'Sura ARL',
      riskCenter: 'Administrativo · Clase I', companyPercentage: '100%', compensationFund: 'Comfandi',
      eps: 'Sura EPS', branchEps: 'Cali Norte', pilaData: 'PILA-2026-04', contributorType: 'Dependiente', contributorSubtype: 'Ninguno',
      materialCode: 'MAT-ADM-004', requiredSupply: 'Sí', pantsSize: '10', shirtSize: 'M', shoeSize: '37', overallSize: 'M', cap: 'No',
    },
    formaciones: [
      { id: 'ac-1', trainingName: 'Administración de Empresas', institution: 'Universidad Icesi', trainingType: 'Pregrado' },
      { id: 'ac-2', trainingName: 'Gerencia del Talento Humano', institution: 'Universidad del Valle', trainingType: 'Especialización' },
    ],
    hijos: [
      { id: 'h-1', docType: 'Tarjeta de identidad', docNumber: '1098765432', fullName: 'Andrés Restrepo Vélez', sex: 'Niño', birthDate: '2016-08-02' },
    ],
    verification: { personalPhoneVerified: true, personalPhoneVerifiedFor: '3114567821', personalEmailVerified: true, personalEmailVerifiedFor: 'carolina.restrepo.s@gmail.com' },
  },

  E0067: { // Luis Miguel Marín — Jefe
    header: { genero: 'M', jobTitle: 'Líder de Aprovechamiento', sociedad: 'ECOENTORNO', oficina: '2401 — ECOENTORNO Cali', estadoLaboral: 'Activo' },
    fields: {
      documentType: 'Cédula de ciudadanía', issueDate: '11/04/2021', issueCountry: 'Colombia', documentNumber: '16.443.221',
      fullName: 'Luis Miguel Marín', birthCountry: 'Colombia', gender: 'Masculino', bloodType: 'A+',
      maritalStatus: 'Soltero', birthDate: '', birthDepartment: 'Valle del Cauca', birthPlace: 'Palmira',
      officePhone: '(602) 485 3210 · Ext 220', personalPhone: '', corporateEmail: 'luis.marin@atica.co',
      personalEmail: 'luismi.marin87@hotmail.com', address: '', addressComplement: '',
      country: '', livingDepartment: '', municipality: '',
      workAddress: 'Km 5 vía Cali - Yumbo', workAddressComplement: 'Planta 1', workCountry: 'Colombia', workArea: 'Aprovechamiento',
      workCity: 'Cali', scheduleRule: 'HO-OPS-48', timeManagementStatus: 'Sincronizado', timeVoucher: 'TV-2026-0498',
      housingType: 'En arriendo', stratum: '3', transportMode: 'Moto', hasLicense: 'Sí', licenseCategory: 'A2',
      conflictVictim: 'No', ethnicGroup: '',
      hasChildren: 'No', numberOfChildren: '', childrenUnder12: '',
      emergencyContactName: 'Marta Marín', emergencyContactRelationship: 'Padre / Madre', emergencyContactPhone: '3123456789',
      measureReason: 'Sin medida activa', position: '30045612', personnelGroup: 'Operativo', personnelArea: 'Aprovechamiento',
      personnelNumber: '00670045', fileNumber: 'F-0067', employmentRelationship: 'Empleado', companyPayrollArea: 'ECOENTORNO',
      settlementArea: 'Cali · Operativo', businessLine: 'Aprovechamiento', process: 'Operaciones', subprocess: 'Aprovechamiento de material',
      regional: 'Regional Occidente', workPlant: '2401 — ECOENTORNO Cali', costCenter: 'CC-2401', jobTitle: 'Líder de Aprovechamiento',
      hireDate: '11/04/2021', directManager: 'Carolina Restrepo S.', processDirector: 'Julián Andrés Pardo', processManager: 'Diana Isabel Ruiz',
      weeklySchedule: 'L-S 6:00 a.m. - 2:00 p.m.', periodWorkHours: '192', workDays: 'Lunes a sábado', payGroup: 'GRP-OPS',
      baseSalary: '$ 3.400.000', mileage: '$ 150.000', salaryType: 'Mensual', bankRelationship: 'Nómina', bankKey: 'BK-0451',
      bankAccount: '00-1122-334455', bankControlKey: 'CB-14', accountOpeningId: 'AC-2021-0411', contractType: 'Indefinido',
      probationPeriod: 'Superado', expirationDate: 'No aplica', procedure: 'PR-NOM-01',
      severanceFund: 'Colpensiones', pensionFund: 'Colpensiones', branchArp: 'Cali Occidente', arp: 'Positiva ARL',
      riskCenter: 'Aprovechamiento · Clase III', companyPercentage: '100%', compensationFund: 'Comfandi',
      eps: 'Nueva EPS', branchEps: 'Cali Sur', pilaData: 'PILA-2026-04', contributorType: 'Dependiente', contributorSubtype: 'Ninguno',
      materialCode: 'MAT-OPS-011', requiredSupply: 'Sí', pantsSize: '32', shirtSize: 'L', shoeSize: '41', overallSize: 'L', cap: 'Sí',
    },
    formaciones: [
      { id: 'ac-1', trainingName: 'Gestión Ambiental', institution: 'SENA', trainingType: 'Técnico' },
    ],
    hijos: [],
    verification: { personalPhoneVerified: false, personalPhoneVerifiedFor: '', personalEmailVerified: false, personalEmailVerifiedFor: '' },
  },

  E0247: { // Andrés Felipe Mejía — Colaborador
    header: { genero: 'M', jobTitle: 'Operario de Clasificación', sociedad: 'ASEI', oficina: '2201 — ASEI Cali', estadoLaboral: 'Onboarding' },
    fields: {
      documentType: 'Cédula de ciudadanía', issueDate: '12/05/2026', issueCountry: 'Colombia', documentNumber: '1.087.456.231',
      fullName: 'Andrés Felipe Mejía', birthCountry: 'Colombia', gender: 'Masculino', bloodType: 'O-',
      maritalStatus: 'En unión libre', birthDate: '19/09/1999', birthDepartment: 'Valle del Cauca', birthPlace: 'Buenaventura',
      officePhone: '', personalPhone: '3114567821', corporateEmail: 'andres.mejia@atica.co',
      personalEmail: '', address: 'Calle 70 # 5N - 22', addressComplement: 'Casa 2',
      country: 'Colombia', livingDepartment: 'Valle del Cauca', municipality: 'Cali',
      workAddress: 'Cra 1 # 20 - 15', workAddressComplement: 'Línea 2', workCountry: 'Colombia', workArea: 'Operaciones',
      workCity: 'Cali', scheduleRule: 'HO-OPS-48', timeManagementStatus: 'Pendiente de primer registro', timeVoucher: 'Sin información',
      housingType: 'Familiar', stratum: '2', transportMode: 'Transporte público', hasLicense: 'No', licenseCategory: '',
      conflictVictim: 'No', ethnicGroup: 'Afro',
      hasChildren: 'Sí', numberOfChildren: '1', childrenUnder12: '0',
      emergencyContactName: 'Yolanda Mejía', emergencyContactRelationship: 'Padre / Madre', emergencyContactPhone: '3187654321',
      measureReason: 'Sin medida activa', position: '30098765', personnelGroup: 'Operativo', personnelArea: 'Operaciones',
      personnelNumber: '02470005', fileNumber: 'F-0247', employmentRelationship: 'Empleado', companyPayrollArea: 'ASEI',
      settlementArea: 'Cali · Operativo', businessLine: 'Operaciones', process: 'Clasificación', subprocess: 'Línea 2',
      regional: 'Regional Occidente', workPlant: '2201 — ASEI Cali', costCenter: 'CC-1002', jobTitle: 'Operario de Clasificación',
      hireDate: '12/05/2026', directManager: 'Katherine Muñoz Vidal', processDirector: 'Julián Andrés Pardo', processManager: 'Diana Isabel Ruiz',
      weeklySchedule: 'L-V 6:00 a.m. - 2:00 p.m.', periodWorkHours: '160', workDays: 'Lunes a viernes', payGroup: 'GRP-OPS',
      baseSalary: '$ 1.620.000', mileage: '$ 0', salaryType: 'Mensual', bankRelationship: 'Nómina', bankKey: 'BK-0912',
      bankAccount: '00-9988-776655', bankControlKey: 'CB-42', accountOpeningId: 'AC-2026-0512', contractType: 'Fijo',
      probationPeriod: 'En curso', expirationDate: '12/05/2027', procedure: 'PR-NOM-01',
      severanceFund: 'Protección', pensionFund: 'Protección', branchArp: 'Cali Centro', arp: 'Sura ARL',
      riskCenter: 'Clasificación · Clase IV', companyPercentage: '100%', compensationFund: 'Comfandi',
      eps: 'Sura EPS', branchEps: 'Cali Norte', pilaData: 'PILA-2026-05', contributorType: 'Dependiente', contributorSubtype: 'Ninguno',
      materialCode: 'MAT-OPS-004', requiredSupply: 'Sí', pantsSize: '30', shirtSize: 'M', shoeSize: '40', overallSize: 'M', cap: 'Sí',
    },
    formaciones: [
      { id: 'ac-1', trainingName: 'Bachiller académico', institution: 'I.E. Técnico Industrial', trainingType: 'Bachillerato' },
      { id: 'ac-2', trainingName: 'Técnico en Manejo de Residuos', institution: 'SENA', trainingType: 'Técnico' },
      { id: 'ac-3', trainingName: 'Auxiliar en Salud Ocupacional', institution: 'Fundación Fes', trainingType: 'Técnico' },
    ],
    hijos: [
      { id: 'h-1', docType: 'Tarjeta de identidad', docNumber: '', fullName: 'Salomé Mejía', sex: 'Niña', birthDate: '2022-01-15' },
    ],
    verification: { personalPhoneVerified: true, personalPhoneVerifiedFor: '3114567821', personalEmailVerified: false, personalEmailVerifiedFor: '' },
  },
};

const MP_ROLE_TO_EMP = { admin: 'E0023', jefe: 'E0067', nuevo: 'E0247' };
const MP_OTP_CODE = '123456'; // código mock — nunca se muestra en la interfaz ni se registra en consola

// ─────────────────────────────────────────── Helpers de dependencias y opciones ─────────
const mp_optionsFor = (fieldDef, draft, header) => {
  if (fieldDef.optionsFrom === 'maritalStatus') return MP_ESTADO_CIVIL(header.genero);
  if (fieldDef.optionsFrom && MP_TALLAS[fieldDef.optionsFrom]) return MP_TALLAS[fieldDef.optionsFrom][header.genero] || MP_TALLAS[fieldDef.optionsFrom].M;
  if (fieldDef.id === 'livingDepartment') return Object.keys(MP_DEPARTAMENTOS_MUNICIPIOS);
  if (fieldDef.id === 'municipality') return MP_DEPARTAMENTOS_MUNICIPIOS[draft.livingDepartment] || [];
  return fieldDef.options || [];
};

const mp_isVisible = (fieldDef, draft) => {
  if (!fieldDef.dependency) return true;
  const dep = fieldDef.dependency;
  if (fieldDef.id === 'livingDepartment' || fieldDef.id === 'municipality') return draft.country === 'Colombia';
  const val = draft[dep.field];
  if (dep.showWhen !== undefined) return val === dep.showWhen;
  return !_mp_isEmpty(val);
};

// ─────────────────────────────────────────── Diff (resumen de cambios) ─────────
const mp_diffScalar = (original, draft) => {
  const rows = [];
  FIELD_DEFS.forEach((f) => {
    if (!isEditableInProfile(f)) return;
    const before = original.fields[f.id] ?? '';
    const after = draft.fields[f.id] ?? '';
    if (before === after) return;
    const tipo = _mp_isEmpty(before) ? 'Agregado' : _mp_isEmpty(after) ? 'Eliminado' : 'Modificado';
    rows.push({ section: f.section, field: f.label, before: _mp_isEmpty(before) ? 'Sin información' : before, after: _mp_isEmpty(after) ? 'Sin información' : after, tipo });
  });
  return rows;
};

const mp_diffCollection = (before, after, labelPrefix, nameKey) => {
  const rows = [];
  const beforeIds = new Set(before.map(r => r.id));
  const afterIds = new Set(after.map(r => r.id));
  after.forEach((r) => {
    if (!beforeIds.has(r.id)) rows.push({ section: labelPrefix.section, field: `${labelPrefix.label} agregada`, before: 'Sin información', after: r[nameKey] || 'Sin información', tipo: 'Agregado' });
  });
  before.forEach((r) => {
    if (!afterIds.has(r.id)) rows.push({ section: labelPrefix.section, field: `${labelPrefix.label} eliminada`, before: r[nameKey] || 'Sin información', after: 'Sin información', tipo: 'Eliminado' });
    else {
      const r2 = after.find(x => x.id === r.id);
      const changed = Object.keys(r2).some(k => k !== 'id' && r2[k] !== r[k]);
      if (changed) rows.push({ section: labelPrefix.section, field: `${labelPrefix.label} modificada`, before: r[nameKey] || 'Sin información', after: r2[nameKey] || 'Sin información', tipo: 'Modificado' });
    }
  });
  return rows;
};

const mp_buildDiff = (original, draft) => {
  const rows = [
    ...mp_diffScalar(original, draft),
    ...mp_diffCollection(original.formaciones, draft.formaciones, { section: 'academic', label: 'Formación' }, 'trainingName'),
    ...mp_diffCollection(original.hijos, draft.hijos, { section: 'family', label: 'Hijo' }, 'fullName'),
  ];
  return rows;
};

// ─────────────────────────────────────────── Validación ─────────
const mp_validate = (draft) => {
  const errors = {};
  if (!_mp_isEmpty(draft.fields.personalEmail) && !_mp_isEmail(draft.fields.personalEmail)) errors.personalEmail = 'Formato de correo no válido';
  if (!_mp_isEmpty(draft.fields.personalPhone) && draft.fields.personalPhone.length < 7) errors.personalPhone = 'Verifica el número, parece incompleto';
  if (!_mp_isEmpty(draft.fields.emergencyContactPhone) && draft.fields.emergencyContactPhone.length < 7) errors.emergencyContactPhone = 'Verifica el número, parece incompleto';
  draft.hijos.forEach((h, i) => {
    if (!_mp_isEmpty(h.birthDate) && h.birthDate > _mp_todayISO()) errors[`hijo-${i}-birthDate`] = 'La fecha no puede ser posterior a hoy';
  });
  return errors;
};

const mp_sectionHasErrors = (errors, section) => Object.keys(errors).some(k => (FIELD_DEFS_BY_ID[k] && FIELD_DEFS_BY_ID[k].section === section) || (section === 'family' && k.startsWith('hijo-')));

const mp_applyFieldChange = (d, id, value) => {
  const fields = { ...d.fields, [id]: value };
  if (id === 'hasLicense' && value !== 'Sí') fields.licenseCategory = '';
  if (id === 'country' && value !== 'Colombia') { fields.livingDepartment = ''; fields.municipality = ''; }
  if (id === 'livingDepartment') fields.municipality = '';
  if (id === 'numberOfChildren') {
    const n = Number(value || 0);
    if (Number(fields.childrenUnder12 || 0) > n) fields.childrenUnder12 = String(n);
  }
  let hijos = d.hijos;
  if (id === 'hasChildren' && value !== 'Sí') { fields.numberOfChildren = ''; fields.childrenUnder12 = ''; hijos = []; }
  return { ...d, fields, hijos };
};

const MP_AUDIT_LOG = [];
if (typeof window !== 'undefined') window.__MP_AUDIT_LOG = MP_AUDIT_LOG; // solo inspección técnica, nunca renderizado aquí
const MP_OTP_MOCK_NOTE = 'Código de prueba interno — nunca se muestra en la interfaz ni se registra en consola.';

// ─────────────────────────────────────────── Foco accesible al abrir un modal ─────────
const ModalTitleFocus = ({ title }) => {
  const ref = React.useRef(null);
  React.useEffect(() => { ref.current && ref.current.focus(); }, []);
  return <h2 ref={ref} tabIndex={-1} className="sr-only">{title}</h2>;
};

// ─────────────────────────────────────────── Campo: consulta / bloqueado / edición ─────────
const FieldEditInput = ({ f, value, onChange, optionsCtx, id, describedBy }) => {
  if (f.fieldType === 'toggle') {
    return (
      <div className="seg-pill" role="group" aria-label={f.label} aria-describedby={describedBy}>
        {['Sí', 'No'].map((opt) => (
          <button key={opt} type="button" className={`seg-pill-btn ${value === opt ? 'active' : ''}`} style={{ padding: '7px 18px', fontSize: 12.5 }} onClick={() => onChange(opt)}>{opt}</button>
        ))}
      </div>
    );
  }
  if (f.fieldType === 'select') {
    const opts = mp_optionsFor(f, optionsCtx.draft, optionsCtx.header || {});
    return (
      <select id={id} value={value || ''} aria-describedby={describedBy} onChange={e => onChange(e.target.value)}>
        <option value="">Sin información</option>
        {opts.map(o => <option key={o} value={o}>{o}</option>)}
      </select>
    );
  }
  if (f.fieldType === 'numberSelect') {
    const max = f.maxFrom ? Number(optionsCtx.draft[f.maxFrom] || 0) : f.max;
    const opts = [];
    for (let i = f.min; i <= max; i++) opts.push(i);
    return (
      <select id={id} value={value || ''} aria-describedby={describedBy} onChange={e => onChange(e.target.value)}>
        <option value="">Sin información</option>
        {opts.map(i => <option key={i} value={i}>{i}</option>)}
      </select>
    );
  }
  if (f.fieldType === 'date') {
    return <input id={id} type="date" value={value || ''} max={_mp_todayISO()} aria-describedby={describedBy} onChange={e => onChange(e.target.value)} />;
  }
  if (f.fieldType === 'phone') {
    return <input id={id} type="tel" inputMode="numeric" value={value || ''} maxLength={10} placeholder="10 dígitos" aria-describedby={describedBy} onChange={e => onChange(_mp_digits10(e.target.value))} />;
  }
  if (f.fieldType === 'email') {
    return <input id={id} type="email" value={value || ''} placeholder="nombre@correo.com" aria-describedby={describedBy} onChange={e => onChange(e.target.value)} />;
  }
  return <input id={id} type="text" value={value || ''} maxLength={f.maxLength || undefined} aria-describedby={describedBy} onChange={e => onChange(e.target.value)} />;
};

const PhoneVerifyRow = ({ value, ctx }) => {
  if (_mp_isEmpty(value)) return null;
  const verified = ctx.draft.verification.personalPhoneVerified && ctx.draft.verification.personalPhoneVerifiedFor === value;
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginTop: 6 }}>
      <span className={`pill ${verified ? 'green' : 'amber-soft'}`}>{verified ? 'Verificado' : 'Pendiente de verificación'}</span>
      {!verified && <button type="button" className="btn" style={{ padding: '4px 10px', fontSize: 11.5 }} onClick={() => ctx.openVerify('phone')}>Validar número</button>}
    </div>
  );
};
const EmailVerifyRow = ({ value, ctx }) => {
  if (_mp_isEmpty(value)) return null;
  const verified = ctx.draft.verification.personalEmailVerified && ctx.draft.verification.personalEmailVerifiedFor === value;
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginTop: 6 }}>
      <span className={`pill ${verified ? 'green' : 'amber-soft'}`}>{verified ? 'Verificado' : 'Pendiente de verificación'}</span>
      {!verified && <button type="button" className="btn" style={{ padding: '4px 10px', fontSize: 11.5 }} onClick={() => ctx.openVerify('email')}>Validar correo</button>}
    </div>
  );
};

const FieldRow = ({ f, mode, value, onChange, error, ctx }) => {
  if (!mp_isVisible(f, ctx.draft.fields)) return null;
  const editable = isEditableInProfile(f);
  const locked = mode !== 'edit' || !editable;

  if (locked) {
    let display = _mp_isEmpty(value) ? 'Sin información' : value;
    const masked = f.maskable && !ctx.reveal[f.id];
    if (masked) display = f.fieldType === 'currency' ? _mp_maskCurrency(value) : _mp_maskAccount(value);
    return (
      <div className="field">
        <div className="field-lbl">
          {f.label}
          {mode === 'edit' && !editable && <IconShield size={10} style={{ marginLeft: 4, opacity: 0.55, verticalAlign: -1 }} title={`Gestionado por ${EDITABLE_BY_LABEL[f.editableBy]}`} />}
        </div>
        <div className="field-val">
          {display}
          {f.maskable && !_mp_isEmpty(value) && (
            <button type="button" className="mp-mask-toggle" onClick={() => ctx.setReveal(r => ({ ...r, [f.id]: !r[f.id] }))}>
              {ctx.reveal[f.id] ? 'Ocultar' : 'Mostrar'}
            </button>
          )}
          {f.code && <span className="muted-xs" style={{ marginLeft: 8 }}>{f.code} · {f.secondaryDesc}</span>}
        </div>
        {f.helpText && <div className="mp-help">{f.helpText}</div>}
      </div>
    );
  }

  const helpId = f.helpText ? `mp-help-${f.id}` : undefined;
  return (
    <div className="furat-row">
      <label className="furat-label" htmlFor={`mp-${f.id}`}>
        {f.label}
        {f.showAsterisk && <span aria-label="ver nota de ayuda" title={f.helpText}> *</span>}
      </label>
      <FieldEditInput f={f} value={value} onChange={onChange} optionsCtx={{ draft: ctx.draft.fields, header: ctx.header }} id={`mp-${f.id}`} describedBy={helpId} />
      {error && <div className="mp-error" role="alert">{error}</div>}
      {f.helpText && <div id={helpId} className="mp-help">{f.helpText}</div>}
      {f.id === 'personalPhone' && <PhoneVerifyRow value={value} ctx={ctx} />}
      {f.id === 'personalEmail' && <EmailVerifyRow value={value} ctx={ctx} />}
    </div>
  );
};

const SectionGrid = ({ section, mode, draft, setFieldValue, errors, ctx }) => (
  <div className="mp-fields-grid">
    {FIELD_DEFS.filter(f => f.section === section).map(f => (
      <FieldRow key={f.id} f={f} mode={mode} value={draft.fields[f.id]} onChange={(v) => setFieldValue(f.id, v)} error={errors[f.id]} ctx={ctx} />
    ))}
  </div>
);

// ─────────────────────────────────────────── Colecciones (formaciones / hijos) ─────────
const RecordFormModal = ({ title, fields, record, onSave, onClose, triggerRef }) => {
  const [local, setLocal] = React.useState(() => ({ ...record }));
  const setF = (id, v) => setLocal(l => ({ ...l, [id]: v }));
  const handleClose = () => { onClose(); if (triggerRef?.current) triggerRef.current.focus(); };
  const isEmptyRecord = fields.every(f => _mp_isEmpty(local[f.id]));
  return (
    <Modal open onClose={handleClose} title={title} width={480}
      footer={<>
        <button className="btn" onClick={handleClose}>Cancelar</button>
        <button className="btn primary" disabled={isEmptyRecord} onClick={() => { onSave(local); handleClose(); }}>Guardar</button>
      </>}>
      <ModalTitleFocus title={title} />
      <div className="mp-fields-grid">
        {fields.map(f => (
          <div className="furat-row" key={f.id}>
            <label className="furat-label" htmlFor={`mp-rec-${f.id}`}>{f.label}</label>
            <FieldEditInput f={f} value={local[f.id]} onChange={(v) => setF(f.id, v)} optionsCtx={{ draft: local, header: {} }} id={`mp-rec-${f.id}`} />
          </div>
        ))}
      </div>
    </Modal>
  );
};

const RecordCollection = ({ fields, records, onChange, addLabel, emptyText, disabled, summaryFn, errors, errorKeyPrefix }) => {
  const [formOpen, setFormOpen] = React.useState(null); // null | 'new' | id
  const addBtnRef = React.useRef(null);
  const editingRecord = formOpen === 'new'
    ? Object.fromEntries([['id', _mp_uid('rec')], ...fields.map(f => [f.id, ''])])
    : records.find(r => r.id === formOpen);

  const remove = (id) => onChange(records.filter(r => r.id !== id));
  const save = (rec) => {
    const exists = records.some(r => r.id === rec.id);
    onChange(exists ? records.map(r => (r.id === rec.id ? rec : r)) : [...records, rec]);
  };

  return (
    <div>
      {records.length === 0 ? (
        <div className="muted" style={{ fontSize: 12.5, padding: '14px 4px' }}>{emptyText}</div>
      ) : (
        <div className="mp-record-list">
          {records.map((r, i) => (
            <div key={r.id}>
              <div className="mp-record-card">
                <div className="mp-record-summary" style={{ minWidth: 0, flex: 1 }}>{summaryFn(r)}</div>
                {!disabled && (
                  <div style={{ display: 'flex', gap: 4, flexShrink: 0 }}>
                    <button type="button" className="btn" style={{ padding: '4px 10px', fontSize: 11.5 }} onClick={() => setFormOpen(r.id)}>Editar</button>
                    <button type="button" className="icon-action danger" onClick={() => remove(r.id)} title="Eliminar"><IconTrash size={14} /></button>
                  </div>
                )}
              </div>
              {errors && errors[`${errorKeyPrefix}-${i}-birthDate`] && <div className="mp-error" role="alert" style={{ marginTop: 4 }}>{errors[`${errorKeyPrefix}-${i}-birthDate`]}</div>}
            </div>
          ))}
        </div>
      )}
      {!disabled && (
        <button ref={addBtnRef} type="button" className="btn" style={{ marginTop: 10 }} onClick={() => setFormOpen('new')}>
          <IconPlus size={13} /> {addLabel}
        </button>
      )}
      {formOpen && (
        <RecordFormModal title={formOpen === 'new' ? addLabel : 'Editar registro'} fields={fields} record={editingRecord}
          onSave={save} onClose={() => setFormOpen(null)} triggerRef={addBtnRef} />
      )}
    </div>
  );
};

const FamilySection = ({ mode, draft, setFieldValue, setHijos, errors, ctx }) => {
  const showChildren = draft.fields.hasChildren === 'Sí' && draft.fields.childrenUnder12 && draft.fields.childrenUnder12 !== '0';
  return (
    <>
      <div className="mp-fields-grid">
        {['hasChildren', 'numberOfChildren', 'childrenUnder12'].map((id) => (
          <FieldRow key={id} f={FIELD_DEFS_BY_ID[id]} mode={mode} value={draft.fields[id]} onChange={(v) => setFieldValue(id, v)} error={errors[id]} ctx={ctx} />
        ))}
      </div>

      {draft.fields.hasChildren === 'Sí' && (
        <div style={{ marginTop: 20 }}>
          <div className="section-title" style={{ margin: '0 0 10px' }}>Hijos menores de 12 años</div>
          {showChildren ? (
            <RecordCollection fields={CHILD_FIELDS} records={draft.hijos} onChange={setHijos}
              addLabel="Agregar hijo" emptyText="No has registrado hijos menores de 12 años." disabled={mode !== 'edit'}
              errors={errors} errorKeyPrefix="hijo"
              summaryFn={(r) => (<>
                <div className="ttl">{r.fullName || 'Sin información'}</div>
                <div className="sub">{r.docType || 'Sin información'}{r.docNumber ? ` · ${r.docNumber}` : ''} · {r.sex || 'Sin información'} · {r.birthDate || 'Sin información'}</div>
              </>)} />
          ) : (
            <p className="muted-xs">Indica cuántos hijos menores de 12 años tienes para administrar sus datos individuales.</p>
          )}
        </div>
      )}

      <div style={{ marginTop: 20 }}>
        <div className="section-title" style={{ margin: '0 0 10px' }}>Contacto de emergencia</div>
        <div className="mp-fields-grid">
          {['emergencyContactName', 'emergencyContactRelationship', 'emergencyContactPhone'].map((id) => (
            <FieldRow key={id} f={FIELD_DEFS_BY_ID[id]} mode={mode} value={draft.fields[id]} onChange={(v) => setFieldValue(id, v)} error={errors[id]} ctx={ctx} />
          ))}
        </div>
      </div>
    </>
  );
};

// ─────────────────────────────────────────── Verificación (OTP mock) ─────────
const VerifyModal = ({ channel, value, onClose, onVerified, triggerRef }) => {
  const [code, setCode] = React.useState('');
  const [status, setStatus] = React.useState('sent'); // sent | checking | error | ok
  const isPhone = channel === 'phone';
  const title = isPhone ? 'Validar número telefónico' : 'Validar correo personal';
  const masked = isPhone
    ? `+57 ··· ${(value || '').slice(-4)}`
    : (value || '').replace(/^(.).*(@.*)$/, (m, a, b) => `${a}••••${b}`);

  const confirmar = () => {
    setStatus('checking');
    setTimeout(() => {
      if (code === MP_OTP_CODE) { setStatus('ok'); setTimeout(() => { onVerified(); handleClose(); }, 700); } else setStatus('error');
    }, 500);
  };
  const handleClose = () => { onClose(); if (triggerRef?.current) triggerRef.current.focus(); };

  return (
    <Modal open onClose={handleClose} title={title} subtitle={`Enviamos un código a ${masked}`} width={400}
      footer={status === 'ok' ? <button className="btn primary" onClick={handleClose}>Cerrar</button> : (
        <>
          <button className="btn" onClick={handleClose} disabled={status === 'checking'}>Cancelar</button>
          <button className="btn primary" onClick={confirmar} disabled={code.length !== 6 || status === 'checking'}>
            {status === 'checking' ? 'Verificando…' : 'Confirmar código'}
          </button>
        </>
      )}>
      <ModalTitleFocus title={title} />
      <div aria-live="polite">
        {status === 'ok' ? (
          <div style={{ textAlign: 'center', padding: '14px 8px' }}>
            <div className="task-icon green" style={{ width: 40, height: 40, margin: '0 auto 10px' }}><IconCheck size={18} /></div>
            <div style={{ fontWeight: 600 }}>{isPhone ? 'Número verificado' : 'Correo verificado'}</div>
          </div>
        ) : (
          <>
            <div className="form-field">
              <label htmlFor="mp-otp-input">Código de 6 dígitos</label>
              <input id="mp-otp-input" className="form-input mono" style={{ letterSpacing: 6, fontSize: 18, textAlign: 'center' }}
                inputMode="numeric" maxLength={6} value={code}
                onChange={(e) => { setCode(e.target.value.replace(/\D/g, '').slice(0, 6)); setStatus('sent'); }} />
            </div>
            {status === 'error' && <div className="mp-error" role="alert" style={{ marginTop: 8 }}>Código incorrecto, inténtalo de nuevo.</div>}
          </>
        )}
      </div>
    </Modal>
  );
};

// ─────────────────────────────────────────── Resumen / cancelar / conflicto ─────────
const MP_SECTION_LABEL = Object.fromEntries(MP_TABS.map(t => [t.id, t.label]));
const MP_TIPO_TONE = { Agregado: 'green', Modificado: 'navy', Eliminado: 'rose' };

const SummaryModal = ({ rows, onCancel, onConfirm, triggerRef }) => {
  const grouped = {};
  rows.forEach((r) => { (grouped[r.section] = grouped[r.section] || []).push(r); });
  const handleClose = () => { onCancel(); if (triggerRef?.current) triggerRef.current.focus(); };
  const title = 'Confirma la actualización de tus datos';
  return (
    <Modal open onClose={handleClose} title={title} subtitle={`${rows.length} ${rows.length === 1 ? 'cambio' : 'cambios'} por confirmar`} width={640}
      footer={<>
        <button className="btn" onClick={handleClose}>Cancelar</button>
        <button className="btn primary" onClick={onConfirm}>Confirmar cambios</button>
      </>}>
      <ModalTitleFocus title={title} />
      <div style={{ display: 'flex', flexDirection: 'column', gap: 18 }}>
        {Object.entries(grouped).map(([section, items]) => (
          <div key={section}>
            <div className="section-title" style={{ margin: '0 0 8px' }}>{MP_SECTION_LABEL[section]}</div>
            <div className="mp-table-wrap">
              <table className="data-table">
                <thead><tr><th>Campo</th><th>Valor anterior</th><th>Valor nuevo</th><th>Tipo</th></tr></thead>
                <tbody>
                  {items.map((r, i) => (
                    <tr key={i}>
                      <td style={{ fontWeight: 500 }}>{r.field}</td>
                      <td className="muted">{r.before}</td>
                      <td>{r.after}</td>
                      <td><span className={`pill ${MP_TIPO_TONE[r.tipo]}`}>{r.tipo}</span></td>
                    </tr>
                  ))}
                </tbody>
              </table>
            </div>
          </div>
        ))}
      </div>
    </Modal>
  );
};

const CancelConfirmModal = ({ onContinue, onDiscard, triggerRef }) => {
  const title = 'Se limpiarán todos los cambios no guardados';
  const handleClose = () => { onContinue(); if (triggerRef?.current) triggerRef.current.focus(); };
  return (
    <Modal open onClose={handleClose} title={title} width={420}
      footer={<>
        <button className="btn" onClick={handleClose}>Continuar editando</button>
        <button className="btn danger solid" onClick={onDiscard}>Descartar cambios</button>
      </>}>
      <ModalTitleFocus title={title} />
      <p className="muted" style={{ fontSize: 13, lineHeight: 1.5 }}>
        Si sales ahora, perderás la información que aún no has guardado en Mi perfil.
      </p>
    </Modal>
  );
};

const ConflictModal = ({ conflicts, onAccept, triggerRef }) => {
  const title = 'Alguien más actualizó esta información';
  const handleClose = () => { onAccept(); if (triggerRef?.current) triggerRef.current.focus(); };
  return (
    <Modal open onClose={handleClose} title={title} width={560}
      footer={<button className="btn primary" onClick={handleClose}>Entendido, voy a revisar</button>}>
      <ModalTitleFocus title={title} />
      <p className="muted" style={{ fontSize: 13, lineHeight: 1.5, marginBottom: 12 }}>
        Estos campos cambiaron en el sistema mientras los estabas editando. Revisa el valor actual antes de volver a guardar tu cambio.
      </p>
      <div className="mp-table-wrap">
        <table className="data-table">
          <thead><tr><th>Campo</th><th>Valor consultado</th><th>Valor actual</th><th>Tu cambio propuesto</th></tr></thead>
          <tbody>
            {conflicts.map((c, i) => (
              <tr key={i}>
                <td style={{ fontWeight: 500 }}>{c.label}</td>
                <td className="muted">{c.queried}</td>
                <td><span className="pill amber">{c.current}</span></td>
                <td>{c.proposed}</td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>
    </Modal>
  );
};

// ─────────────────────────────────────────── Page ─────────
const MyProfile = ({ currentRole }) => {
  const empId = MP_ROLE_TO_EMP[currentRole] || 'E0023';
  const source = MY_PROFILE_DATA[empId];
  const emp = (window.EMPLOYEES || []).find(e => e.id === empId);

  const cloneProfile = (p) => ({
    fields: { ...p.fields },
    formaciones: p.formaciones.map(f => ({ ...f })),
    hijos: p.hijos.map(h => ({ ...h })),
    verification: { ...p.verification },
  });

  const [original, setOriginal] = React.useState(() => cloneProfile(source));
  const [liveOriginal, setLiveOriginal] = React.useState(() => cloneProfile(source));
  const [draft, setDraft] = React.useState(() => cloneProfile(source));
  const [mode, setMode] = React.useState('view');
  const [tab, setTab] = React.useState('personal');
  const [errors, setErrors] = React.useState({});
  const [reveal, setReveal] = React.useState({});
  const [showSummary, setShowSummary] = React.useState(false);
  const [showCancelConfirm, setShowCancelConfirm] = React.useState(false);
  const [conflicts, setConflicts] = React.useState(null);
  const [verifyChannel, setVerifyChannel] = React.useState(null);
  const [saveState, setSaveState] = React.useState('idle');
  const [syncStatusText, setSyncStatusText] = React.useState('');
  const updateBtnRef = React.useRef(null);
  const cancelBtnRef = React.useRef(null);
  const conflictTimerRef = React.useRef(null);

  React.useEffect(() => {
    const fresh = cloneProfile(MY_PROFILE_DATA[empId]);
    setOriginal(fresh); setLiveOriginal(cloneProfile(MY_PROFILE_DATA[empId])); setDraft(fresh);
    setMode('view'); setTab('personal'); setErrors({}); setReveal({});
    setShowSummary(false); setShowCancelConfirm(false); setConflicts(null); setVerifyChannel(null); setSaveState('idle');
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [empId]);

  // Simulación de conflicto de versión: ~7s después de entrar en edición,
  // "Estado civil" cambia en el sistema de origen. Para verlo en el
  // prototipo: entra a editar, cambia Estado civil, espera unos segundos y
  // luego Guardar cambios.
  React.useEffect(() => {
    if (mode !== 'edit') return undefined;
    conflictTimerRef.current = setTimeout(() => {
      setLiveOriginal((lo) => {
        const opciones = MP_ESTADO_CIVIL(source.header.genero);
        const alt = opciones.find(o => o !== original.fields.maritalStatus) || original.fields.maritalStatus;
        return { ...lo, fields: { ...lo.fields, maritalStatus: alt } };
      });
    }, 7000);
    return () => clearTimeout(conflictTimerRef.current);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [mode]);

  const diffRows = React.useMemo(() => mp_buildDiff(original, draft), [original, draft]);
  const hasChanges = diffRows.length > 0;
  const pendingBySection = {};
  diffRows.forEach((r) => { pendingBySection[r.section] = (pendingBySection[r.section] || 0) + 1; });

  React.useEffect(() => {
    window.__hasUnsavedProfileChanges = mode === 'edit' && hasChanges;
    const onBeforeUnload = (e) => { if (mode === 'edit' && hasChanges) { e.preventDefault(); e.returnValue = ''; } };
    window.addEventListener('beforeunload', onBeforeUnload);
    return () => window.removeEventListener('beforeunload', onBeforeUnload);
  }, [mode, hasChanges]);
  React.useEffect(() => () => { window.__hasUnsavedProfileChanges = false; }, []);

  const setFieldValue = (id, value) => setDraft(d => mp_applyFieldChange(d, id, value));
  const setFormaciones = (recs) => setDraft(d => ({ ...d, formaciones: recs }));
  const setHijos = (recs) => setDraft(d => ({ ...d, hijos: recs }));

  const startEdit = () => { setDraft(cloneProfile(original)); setErrors({}); setMode('edit'); };
  const requestCancel = () => { if (!hasChanges) { setMode('view'); return; } setShowCancelConfirm(true); };
  const discardChanges = () => { setDraft(cloneProfile(original)); setErrors({}); setShowCancelConfirm(false); setMode('view'); setVerifyChannel(null); };

  const requestSave = () => {
    if (!hasChanges) return;
    const conflictFields = FIELD_DEFS.filter(f => isEditableInProfile(f)
      && draft.fields[f.id] !== original.fields[f.id]
      && liveOriginal.fields[f.id] !== original.fields[f.id]);
    if (conflictFields.length > 0) {
      setConflicts(conflictFields.map(f => ({
        label: f.label,
        queried: _mp_isEmpty(original.fields[f.id]) ? 'Sin información' : original.fields[f.id],
        current: _mp_isEmpty(liveOriginal.fields[f.id]) ? 'Sin información' : liveOriginal.fields[f.id],
        proposed: _mp_isEmpty(draft.fields[f.id]) ? 'Sin información' : draft.fields[f.id],
      })));
      return;
    }
    const errs = mp_validate(draft);
    if (Object.keys(errs).length > 0) {
      setErrors(errs);
      const firstKey = Object.keys(errs)[0];
      const f = FIELD_DEFS_BY_ID[firstKey];
      setTab(f ? f.section : 'family');
      return;
    }
    setErrors({});
    setShowSummary(true);
  };

  const acceptConflict = () => { setOriginal(cloneProfile(liveOriginal)); setConflicts(null); };

  const confirmSave = () => {
    setShowSummary(false);
    setSaveState('saving');
    setTimeout(() => {
      const saved = cloneProfile(draft);
      MY_PROFILE_DATA[empId].fields = saved.fields;
      MY_PROFILE_DATA[empId].formaciones = saved.formaciones;
      MY_PROFILE_DATA[empId].hijos = saved.hijos;
      MY_PROFILE_DATA[empId].verification = saved.verification;
      setOriginal(saved);
      setLiveOriginal(cloneProfile(saved));
      setDraft(cloneProfile(saved));
      setMode('view');
      setSaveState('saved');

      const now = new Date();
      diffRows.forEach((row) => {
        const fieldDef = FIELD_DEFS.find(f => f.label === row.field);
        MP_AUDIT_LOG.push({
          id: _mp_uid('aud'), employeeId: empId, field: row.field, before: row.before, after: row.after,
          changeType: row.tipo, timestamp: now, user: source.header.jobTitle, role: currentRole, source: 'Mi perfil',
          syncStatus: fieldDef && fieldDef.storage.includes('SAP') ? 'pendiente' : 'no_aplica', opId: null,
        });
      });

      const sapRows = diffRows.filter((row) => {
        const fieldDef = FIELD_DEFS.find(f => f.label === row.field);
        return fieldDef && fieldDef.storage.includes('SAP');
      });
      if (sapRows.length > 0) {
        setSyncStatusText('Tus datos fueron guardados. La actualización con los sistemas relacionados continuará en segundo plano.');
        setTimeout(() => {
          const hasError = Math.random() < 0.12;
          setSyncStatusText(hasError
            ? 'Todo quedó guardado. Un cambio permanece pendiente de revisión administrativa.'
            : 'Todo quedó guardado y sincronizado con los sistemas relacionados.');
          setTimeout(() => setSyncStatusText(''), 6000);
        }, 2400);
      }
      setTimeout(() => setSaveState('idle'), 5000);
    }, 700);
  };

  const ctx = { draft, header: source.header, reveal, setReveal, openVerify: setVerifyChannel };

  return (
    <div className="main">
      <div className="emp-profile-head">
        <div className="avatar" style={{ width: 84, height: 84, fontSize: 28, borderRadius: '50%' }}>{emp ? emp.initials : ''}</div>
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 4, flexWrap: 'wrap' }}>
            <h1 className="page-title" style={{ margin: 0, fontSize: 24 }}>{draft.fields.fullName}</h1>
            <span className="pill green">● {source.header.estadoLaboral}</span>
          </div>
          <div className="emp-headline">
            <span><b>{source.header.jobTitle}</b></span>
            <span className="dotsep">·</span>
            <span>{source.header.sociedad}</span>
            <span className="dotsep">·</span>
            <span>{source.header.oficina}</span>
          </div>
        </div>
        <div className="emp-quick-actions" style={{ flexDirection: 'column', alignItems: 'flex-end', gap: 8 }}>
          {mode === 'view' ? (
            <button ref={updateBtnRef} className="btn primary" onClick={startEdit}>Actualizar mis datos</button>
          ) : (
            <div style={{ display: 'flex', gap: 8 }}>
              <button ref={cancelBtnRef} className="btn" onClick={requestCancel}>Cancelar</button>
              <button className="btn primary" onClick={requestSave} disabled={!hasChanges}>Guardar cambios</button>
            </div>
          )}
          <div aria-live="polite" style={{ minHeight: 14 }}>
            {saveState === 'saving' && <span className="muted" style={{ fontSize: 11 }}>Guardando…</span>}
            {saveState === 'saved' && <span className="muted" style={{ fontSize: 11, color: 'var(--green-600)', display: 'inline-flex', alignItems: 'center', gap: 4 }}><IconCheck size={10} /> Tus datos fueron actualizados correctamente</span>}
          </div>
        </div>
      </div>

      {syncStatusText && (
        <div className="amp-pos-creada-banner" aria-live="polite" style={{ marginBottom: 18 }}>
          <IconCheck size={16} />
          <div style={{ fontSize: 12.5 }}>{syncStatusText}</div>
        </div>
      )}

      <div className="emp-tabs-row">
        {MP_TABS.map((t) => (
          <button key={t.id} className={`emp-tab ${tab === t.id ? 'active' : ''}`} onClick={() => setTab(t.id)}>
            {t.label}
            {mode === 'edit' && pendingBySection[t.id] > 0 && <span className="tab-badge amber">{pendingBySection[t.id]}</span>}
            {mp_sectionHasErrors(errors, t.id) && <span className="tab-badge rose">!</span>}
          </button>
        ))}
      </div>

      <div className="card" style={{ padding: 24 }}>
        {tab === 'personal' && <SectionGrid section="personal" mode={mode} draft={draft} setFieldValue={setFieldValue} errors={errors} ctx={ctx} />}
        {tab === 'academic' && (
          <RecordCollection fields={ACADEMIC_FIELDS} records={draft.formaciones} onChange={setFormaciones}
            addLabel="Agregar formación" emptyText="No has registrado formación académica." disabled={mode !== 'edit'}
            summaryFn={(r) => (<>
              <div className="ttl">{r.trainingName || 'Sin información'}</div>
              <div className="sub">{r.institution || 'Sin información'} · {r.trainingType || 'Sin información'}</div>
            </>)} />
        )}
        {tab === 'family' && <FamilySection mode={mode} draft={draft} setFieldValue={setFieldValue} setHijos={setHijos} errors={errors} ctx={ctx} />}
        {tab === 'labor' && <SectionGrid section="labor" mode={mode} draft={draft} setFieldValue={setFieldValue} errors={errors} ctx={ctx} />}
        {tab === 'social' && <SectionGrid section="social" mode={mode} draft={draft} setFieldValue={setFieldValue} errors={errors} ctx={ctx} />}
        {tab === 'dotacion' && (
          <>
            {mode === 'edit' && <p className="muted-xs" style={{ marginBottom: 14 }}>Las nuevas tallas se tendrán en cuenta para futuras asignaciones.</p>}
            <SectionGrid section="dotacion" mode={mode} draft={draft} setFieldValue={setFieldValue} errors={errors} ctx={ctx} />
          </>
        )}
      </div>

      {verifyChannel && (
        <VerifyModal
          channel={verifyChannel}
          value={verifyChannel === 'phone' ? draft.fields.personalPhone : draft.fields.personalEmail}
          onClose={() => setVerifyChannel(null)}
          onVerified={() => setDraft(d => ({
            ...d,
            verification: {
              ...d.verification,
              ...(verifyChannel === 'phone'
                ? { personalPhoneVerified: true, personalPhoneVerifiedFor: d.fields.personalPhone }
                : { personalEmailVerified: true, personalEmailVerifiedFor: d.fields.personalEmail }),
            },
          }))}
          triggerRef={updateBtnRef}
        />
      )}

      {showSummary && <SummaryModal rows={diffRows} onCancel={() => setShowSummary(false)} onConfirm={confirmSave} triggerRef={updateBtnRef} />}
      {showCancelConfirm && <CancelConfirmModal onContinue={() => setShowCancelConfirm(false)} onDiscard={discardChanges} triggerRef={cancelBtnRef} />}
      {conflicts && <ConflictModal conflicts={conflicts} onAccept={acceptConflict} triggerRef={updateBtnRef} />}
    </div>
  );
};

window.MyProfile = MyProfile;
