// src/screen-machotes.jsx — PROPUESTA v3: LA FOTO COMPLETA de los machotes (2026-09-22).
//
// v2 (2026-09-21) resolvió el machote de cobranza: el agente trae el diseño, Piixi
// garantiza la obligación. Miguel pidió después ver el pastel completo: cómo se
// gestionan TODOS los machotes cuando la topología de productos se complica
// (RC Profesional de GNP trae documentos propios; Vida se parte en protección,
// retiro e inversión y cada una dice cosas distintas).
//
// LO QUE ENSEÑA LA INVESTIGACIÓN DE MERCADO (2026-09-22): nadie indexa machotes por
// producto. Los sistemas de agencias los organizan por FLUJO y meten el ramo como
// filtro de una regla; como no tienen condicionales en el cuerpo, terminan con una
// plantilla por variante. Las plataformas de las aseguradoras (Messagepoint, Quadient,
// Exstream) usan un maestro con variantes y BLOQUES compartidos que se incluyen por
// atributo. Eso es lo que esta pantalla propone, dicho en el vocabulario de Piixi:
//
//   1. DISEÑO      — cómo se ve. Del despacho, uno solo, independiente del flujo.
//   2. MACHOTE     — qué dice. Uno por flujo, con variantes por el eje que ESE flujo
//                    necesita: momento del recibo en cobranza; ramo en renovación,
//                    entrega y alta. Una variante que no se escribe HEREDA la genérica.
//   3. PIEZAS      — lo que un producto agrega: párrafos y adjuntos. Cuelgan del
//                    catálogo de productos (aseguradora → ramo → familia → producto),
//                    que ya hereda así para gracia y anticipación. Un producto NO tiene
//                    machote propio: tiene piezas que se meten en el machote del flujo.
//   4. PROBAR      — eliges una póliza real y ves el correo resuelto, con la traza de
//                    dónde salió cada parte. Es la prueba de que el pastel cierra.
//
// Navegación: Ajustes → Machotes (tres pestañas: Por flujo · Diseño · Piezas por
// producto) + «Probar con una póliza». Cada pestaña es una capa; la prueba las junta.

// ═══════════════════════════════════════════════════════════════════════════
// DATOS DE LA PROPUESTA
// ═══════════════════════════════════════════════════════════════════════════

// ── Flujos y su eje de variantes ─────────────────────────────────────────────
const MACHOTE_FLUJOS = [
  { id: 'cobranza',   nombre: 'Cobranza',          eje: 'momento', ejeEtiqueta: 'varía por momento del recibo', instalado: true,
    usadoEn: 'Cobranza · paso «Notificar»', proposito: 'Cobrar un recibo antes de que venza, en gracia y ya vencido.',
    icono: 'mail', cabecera: 'Aviso de cobranza' },
  { id: 'renovacion', nombre: 'Renovación',        eje: 'ramo',    ejeEtiqueta: 'varía por ramo', instalado: true,
    usadoEn: 'Renovación · paso «Avisar»', proposito: 'Avisar que la póliza vence y qué sigue para renovarla.',
    icono: 'refresh', cabecera: 'Aviso de renovación' },
  { id: 'entrega',    nombre: 'Entrega de póliza', eje: 'ramo',    ejeEtiqueta: 'varía por ramo', instalado: true,
    usadoEn: 'Entrega · paso «Enviar póliza»', proposito: 'Entregar la póliza emitida y decir qué revisar.',
    icono: 'pdf', cabecera: 'Tu póliza' },
  { id: 'alta',       nombre: 'Alta de personas',  eje: 'ramo',    ejeEtiqueta: 'varía por ramo', instalado: false,
    usadoEn: 'Alta · paso «Pedir datos»', proposito: 'Pedirle al contratante lo que falta para darlo de alta.',
    icono: 'user', cabecera: 'Documentos para tu póliza' },
];

const MACHOTE_RAMOS = ['GMM', 'AUTOS', 'VIDA', 'DANOS'];
const MACHOTE_RAMO_NOMBRE = { GMM: 'Gastos médicos', AUTOS: 'Autos', VIDA: 'Vida', DANOS: 'Daños' };

// ── Datos obligatorios por flujo: lo que el aviso TIENE que decir ─────────────
const MACHOTE_OBLIGATORIOS = {
  cobranza: [
    { llave: 'recibo_numero',      etiqueta: 'Número de recibo',     porque: 'Sin él, el asegurado no sabe cuál de sus recibos le estás cobrando.' },
    { llave: 'poliza_numero',      etiqueta: 'Número de póliza',     porque: 'Identifica el contrato ante la aseguradora.' },
    { llave: 'recibo_importe',     etiqueta: 'Importe',              porque: 'Cuánto se debe.' },
    { llave: 'recibo_moneda',      etiqueta: 'Moneda',               porque: 'Una cifra sin moneda es ambigua en una cartera con dólares.' },
    { llave: 'recibo_vencimiento', etiqueta: 'Fecha de vencimiento', porque: 'Para cuándo, y de ahí sale el plazo de gracia.' },
  ],
  renovacion: [
    { llave: 'poliza_numero',    etiqueta: 'Número de póliza',  porque: 'Identifica el contrato ante la aseguradora.' },
    { llave: 'fin_vigencia',     etiqueta: 'Fecha en que vence', porque: 'Es el reloj de la renovación.' },
    { llave: 'prima_renovacion', etiqueta: 'Prima de renovación', porque: 'Cuánto costará seguir cubierto.' },
    { llave: 'moneda',           etiqueta: 'Moneda',            porque: 'Una cifra sin moneda es ambigua.' },
  ],
  entrega: [
    { llave: 'poliza_numero',   etiqueta: 'Número de póliza', porque: 'Identifica el contrato.' },
    { llave: 'producto',        etiqueta: 'Producto',         porque: 'Qué contrató, con el nombre de la aseguradora.' },
    { llave: 'inicio_vigencia', etiqueta: 'Inicio de vigencia', porque: 'Desde cuándo está cubierto.' },
    { llave: 'fin_vigencia',    etiqueta: 'Fin de vigencia',  porque: 'Hasta cuándo.' },
  ],
  alta: [
    { llave: 'documentos', etiqueta: 'Lista de documentos', porque: 'Sin la lista, el contratante no sabe qué mandar.' },
  ],
};

// ── Variantes por MOMENTO (cobranza) ─────────────────────────────────────────
const MACHOTE_MOMENTOS = [
  {
    id: 'anticipado', etiqueta: 'Por vencer', pista: 'Faltan días', tono: 'info',
    asunto: 'Tu recibo {recibo_numero} vence el {recibo_vencimiento}',
    deber: 'Te recuerdo que el recibo {recibo_numero} de tu póliza {poliza_numero} vence el {recibo_vencimiento}, por {recibo_importe} {recibo_moneda}.',
    saludo: 'Hola, {nombre_pila}.',
    cierre: 'Si ya lo pagaste, mándame el comprobante y lo registro.',
  },
  {
    id: 'recordatorio', etiqueta: 'En gracia', pista: 'Venció, hay gracia', tono: 'warn',
    asunto: 'Tu recibo {recibo_numero} venció — todavía estás a tiempo',
    deber: 'El recibo {recibo_numero} de tu póliza {poliza_numero}, por {recibo_importe} {recibo_moneda}, venció el {recibo_vencimiento} y sigue sin pago registrado. Todavía estás dentro del plazo de gracia de tu póliza.',
    saludo: 'Hola, {nombre_pila}.',
    cierre: 'Ponte al corriente antes de que se agote el plazo y tu cobertura no se interrumpe. Si ya lo pagaste, mándame el comprobante.',
  },
  {
    id: 'vencido', etiqueta: 'Vencido', pista: 'Se agotó la gracia', tono: 'danger',
    asunto: 'Tu cobertura puede estar interrumpida — recibo {recibo_numero}',
    deber: 'El recibo {recibo_numero} de tu póliza {poliza_numero}, por {recibo_importe} {recibo_moneda}, venció el {recibo_vencimiento} y no tengo su pago registrado. El plazo de gracia ya se agotó, así que tu cobertura puede estar interrumpida.',
    saludo: 'Hola, {nombre_pila}.',
    cierre: 'Escríbeme hoy para ver cómo lo regularizamos. Si ya lo pagaste, mándame el comprobante y lo aclaro con la aseguradora.',
  },
];

// ── Variantes por RAMO (renovación, entrega, alta) ───────────────────────────
// `generico` siempre existe. Un ramo sin entrada HEREDA el genérico: se ve en la
// pantalla como «hereda» y se puede convertir en propio con un clic.
const MACHOTE_POR_RAMO = {
  renovacion: {
    generico: {
      asunto: 'Tu póliza {poliza_numero} vence el {fin_vigencia}',
      deber: 'Tu póliza {poliza_numero} de {producto} vence el {fin_vigencia}. Para que tu cobertura no se interrumpa, la aseguradora ya emitió la renovación con una prima de {prima_renovacion} {moneda}.',
      saludo: 'Hola, {nombre_pila}.',
      cierre: 'Dime si renovamos igual o si quieres que revisemos coberturas antes.',
    },
    GMM: {
      asunto: 'Tu póliza {poliza_numero} vence el {fin_vigencia} — conserva tu antigüedad',
      deber: 'Tu póliza {poliza_numero} de {producto} vence el {fin_vigencia}. La aseguradora ya emitió la renovación con una prima de {prima_renovacion} {moneda}. Renovar a tiempo conserva tu antigüedad: los padecimientos que ya están cubiertos siguen cubiertos.',
      saludo: 'Hola, {nombre_pila}.',
      cierre: 'Si hubo algún cambio de salud en la familia este año, cuéntamelo antes de renovar.',
    },
    AUTOS: {
      asunto: 'Tu póliza {poliza_numero} vence el {fin_vigencia}',
      deber: 'Tu póliza {poliza_numero} de {producto} vence el {fin_vigencia}. La aseguradora ya emitió la renovación con una prima de {prima_renovacion} {moneda}. Si el auto cambió de uso o de conductor habitual, hay que decirlo antes de renovar.',
      saludo: 'Hola, {nombre_pila}.',
      cierre: 'Dime si renovamos igual o si quieres cotizar otra cobertura.',
    },
  },
  entrega: {
    generico: {
      asunto: 'Tu póliza {poliza_numero} — {producto}',
      deber: 'Te comparto tu póliza {poliza_numero} de {producto}, vigente del {inicio_vigencia} al {fin_vigencia}. Revisa que tus datos estén correctos: cualquier error se corrige sin costo dentro de los primeros 30 días.',
      saludo: 'Hola, {nombre_pila}.',
      cierre: 'Guarda este correo: aquí está tu póliza y cómo localizarme.',
    },
    VIDA: {
      asunto: 'Tu póliza de vida {poliza_numero} — {producto}',
      deber: 'Te comparto tu póliza {poliza_numero} de {producto}, vigente del {inicio_vigencia} al {fin_vigencia}. Revisa sobre todo la suma asegurada y los beneficiarios que designaste: son lo que más importa en una póliza de vida.',
      saludo: 'Hola, {nombre_pila}.',
      cierre: 'Si cambia tu situación familiar, avísame y actualizamos beneficiarios.',
    },
  },
  alta: {
    generico: {
      asunto: 'Documentos para dar de alta tu póliza',
      deber: 'Para dar de alta tu póliza necesito: {documentos}. Puedes responder este correo con fotos legibles o traerlos al despacho.',
      saludo: 'Hola, {nombre_pila}.',
      cierre: 'En cuanto los tenga, la emito el mismo día.',
    },
  },
};

// ── Diseños: los que trae Piixi + el que sube el agente ──────────────────────
const MACHOTE_DISENOS = [
  { id: 'sobrio',   nombre: 'Sobrio',       nota: 'El que trae Piixi. Texto limpio, sin adornos.', origen: 'piixi', papel: '#FFFFFF', tinta: '#1A1A1A', acento: '#0F7B4F', membrete: false },
  { id: 'membrete', nombre: 'Con membrete', nota: 'Encabezado con el nombre de tu despacho.',      origen: 'piixi', papel: '#FFFFFF', tinta: '#1A1A1A', acento: '#1B4F8C', membrete: true  },
  { id: 'calido',   nombre: 'Cálido',       nota: 'Más cercano, para carteras de trato personal.', origen: 'piixi', papel: '#FDF9F3', tinta: '#2A2117', acento: '#B06A2C', membrete: true  },
  { id: 'propio',   nombre: 'El tuyo',      nota: 'Sube tu HTML. Lo verificamos y lo usamos tal cual.', origen: 'agente', papel: '#FFFFFF', tinta: '#1A1A1A', acento: '#0F7B4F', membrete: true, vacio: true },
];

// ── Familias de Vida: el atributo del producto que decide sus piezas ─────────
const MACHOTE_FAMILIAS = {
  proteccion: { id: 'proteccion', nombre: 'Protección', nota: 'Vida pura: suma asegurada y beneficiarios.' },
  retiro:     { id: 'retiro',     nombre: 'Retiro',     nota: 'Aportaciones periódicas para una edad de retiro.' },
  inversion:  { id: 'inversion',  nombre: 'Inversión',  nota: 'Con fondos y valor de rescate.' },
};
const MACHOTE_FAMILIA_DE_PRODUCTO = {
  'Privilegio Plus': 'inversion', 'Vida Inteligente': 'proteccion',
  'Vida Total': 'proteccion', 'Protección Familiar': 'proteccion',
  'Met Vida Plus': 'proteccion', 'Met Total': 'inversion', 'Met Patrimonial': 'retiro',
  'Vida Asegurada': 'proteccion',
};
// Productos del catálogo que la cartera de prueba no trae pero el despacho vende.
const MACHOTE_PRODUCTOS_EXTRA = { gnp: { DANOS: ['RC Profesional'] } };

// ── Piezas: lo que cada nivel del catálogo agrega al machote del flujo ───────
// nivel: aseguradora | ramo | familia | producto. tipo: parrafo | adjunto.
const MACHOTE_PIEZAS = [
  { id: 'gnp_guia',          nivel: 'aseguradora', aseguradora: 'gnp', tipo: 'adjunto', nombre: 'Guía de servicio GNP.pdf', flujos: ['entrega'] },
  { id: 'autos_siniestro',   nivel: 'ramo', ramo: 'AUTOS', tipo: 'parrafo', nombre: 'Qué hacer en un siniestro', flujos: ['entrega'],
    texto: 'Si tienes un percance, llama primero a la aseguradora al número de tu tarjeta de asistencia y después a mí. No firmes nada en el lugar.' },
  { id: 'autos_asistencia',  nivel: 'ramo', ramo: 'AUTOS', tipo: 'adjunto', nombre: 'Tarjeta de asistencia vial.pdf', flujos: ['entrega'] },
  { id: 'gmm_espera',        nivel: 'ramo', ramo: 'GMM',   tipo: 'parrafo', nombre: 'Periodos de espera', flujos: ['entrega'],
    texto: 'Algunas coberturas tienen periodo de espera: revísalo en las condiciones antes de programar cualquier procedimiento.' },
  { id: 'gmm_antiguedad',    nivel: 'ramo', ramo: 'GMM',   tipo: 'parrafo', nombre: 'Antigüedad reconocida', flujos: ['renovacion'],
    texto: 'Al renovar sin interrupción conservas la antigüedad reconocida por la aseguradora.' },
  { id: 'vida_beneficiarios',nivel: 'ramo', ramo: 'VIDA',  tipo: 'parrafo', nombre: 'Beneficiarios al día', flujos: ['renovacion'],
    texto: 'Aprovecha la renovación para confirmar que tus beneficiarios siguen siendo los que quieres.' },
  { id: 'retiro_aportaciones', nivel: 'familia', ramo: 'VIDA', familia: 'retiro', tipo: 'parrafo', nombre: 'Aportaciones y retiro', flujos: ['entrega', 'renovacion'],
    texto: 'Tu plan acumula aportaciones para tu edad de retiro. Si cambias el monto o la periodicidad, avísame para ajustar la póliza.' },
  { id: 'inversion_rescate', nivel: 'familia', ramo: 'VIDA', familia: 'inversion', tipo: 'parrafo', nombre: 'Valor de rescate y fondos', flujos: ['entrega'],
    texto: 'Tu póliza tiene un componente de inversión: los fondos y el valor de rescate aparecen en tu estado de cuenta, no en la carátula.' },
  { id: 'inversion_estado',  nivel: 'familia', ramo: 'VIDA', familia: 'inversion', tipo: 'adjunto', nombre: 'Estado de cuenta de fondos.pdf', flujos: ['renovacion'] },
  { id: 'rcp_actividad',     nivel: 'producto', aseguradora: 'gnp', ramo: 'DANOS', producto: 'RC Profesional', tipo: 'parrafo', nombre: 'Declaración de actividad', flujos: ['entrega', 'renovacion'],
    texto: 'Tu cobertura de responsabilidad civil profesional depende de la actividad que declaraste. Si cambió, hay que actualizarla antes de que ocurra un reclamo.' },
  { id: 'rcp_cuestionario',  nivel: 'producto', aseguradora: 'gnp', ramo: 'DANOS', producto: 'RC Profesional', tipo: 'adjunto', nombre: 'Cuestionario de actividad profesional.pdf', flujos: ['renovacion'] },
];

const MACHOTE_NIVEL_ETIQUETA = { aseguradora: 'Aseguradora', ramo: 'Ramo', familia: 'Familia', producto: 'Producto' };

const MACHOTE_EJEMPLO_RECIBO = {
  nombre_pila: 'Fabiola',
  para: 'Fabiola Medina',
  recibo_numero: '00000513547257',
  poliza_numero: '553830159',
  recibo_vencimiento: '14 de octubre de 2026',
  recibo_importe: '6,941.28',
  recibo_moneda: 'MXN',
};

// ═══════════════════════════════════════════════════════════════════════════
// RESOLUCIÓN — la misma herencia que el catálogo de productos usa para gracia
// y anticipación: aseguradora → ramo → familia → producto.
// ═══════════════════════════════════════════════════════════════════════════

function machoteRellenar(texto, datos) {
  return String(texto || '').replace(/\{([a-z_]+)\}/g, (m, k) => datos[k] != null ? datos[k] : m);
}

function machoteFamiliaDe(poliza) {
  if (!poliza || poliza.ramo !== 'VIDA') return null;
  return MACHOTE_FAMILIA_DE_PRODUCTO[poliza.producto] || 'proteccion';
}

function machotePiezasPara(poliza, flujoId) {
  const familia = machoteFamiliaDe(poliza);
  return MACHOTE_PIEZAS.filter((pz) => {
    if (!pz.flujos.includes(flujoId)) return false;
    if (pz.nivel === 'aseguradora') return pz.aseguradora === poliza.carrier;
    if (pz.nivel === 'ramo')        return pz.ramo === poliza.ramo;
    if (pz.nivel === 'familia')     return pz.ramo === poliza.ramo && pz.familia === familia;
    if (pz.nivel === 'producto')    return pz.aseguradora === poliza.carrier && pz.ramo === poliza.ramo && pz.producto === poliza.producto;
    return false;
  });
}

function machoteDatosDe(poliza, flujoId, momentoId) {
  const c = AdmixData.contratanteById(poliza.contratante);
  const nombre = c ? c.nombre : 'Contratante';
  const base = {
    nombre_pila: nombre.split(' ')[0],
    para: nombre,
    poliza_numero: poliza.polizaNumero,
    producto: poliza.producto,
    inicio_vigencia: AdmixData.formatDateES(poliza.inicioVigencia),
    fin_vigencia: AdmixData.formatDateES(poliza.finVigencia),
    prima_renovacion: AdmixData.formatMoney(Math.round(poliza.primaTotal * 1.06)),
    moneda: poliza.moneda,
    documentos: 'identificación oficial, comprobante de domicilio y constancia de situación fiscal',
  };
  if (flujoId === 'cobranza') {
    return { ...base,
      recibo_numero: '0000051' + poliza.polizaNumero.replace(/\D/g, '').slice(-6),
      recibo_vencimiento: AdmixData.formatDateES(poliza.finVigencia),
      recibo_importe: Math.round(poliza.primaTotal / 4).toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 }),
      recibo_moneda: poliza.moneda,
    };
  }
  return base;
}

// Devuelve la traza completa: diseño, machote+variante (propia o heredada),
// piezas con su nivel de origen, y los datos ya resueltos.
function machoteResolver(poliza, flujoId, momentoId, propias) {
  const flujo = MACHOTE_FLUJOS.find((f) => f.id === flujoId);
  const familia = machoteFamiliaDe(poliza);
  let variante, varianteEtiqueta, heredada = false;
  if (flujo.eje === 'momento') {
    variante = MACHOTE_MOMENTOS.find((m) => m.id === momentoId) || MACHOTE_MOMENTOS[0];
    varianteEtiqueta = variante.etiqueta;
  } else {
    const tabla = MACHOTE_POR_RAMO[flujoId] || {};
    const tienePropia = propias ? propias.has(poliza.ramo) : !!tabla[poliza.ramo];
    variante = (tienePropia && tabla[poliza.ramo]) || tabla.generico;
    heredada = !tienePropia;
    varianteEtiqueta = heredada ? 'Genérico' : MACHOTE_RAMO_NOMBRE[poliza.ramo];
  }
  const piezas = machotePiezasPara(poliza, flujoId);
  const cadena = [
    { nivel: 'aseguradora', etiqueta: (AdmixData.carrierById(poliza.carrier) || {}).name || poliza.carrier },
    { nivel: 'ramo',        etiqueta: MACHOTE_RAMO_NOMBRE[poliza.ramo] || poliza.ramo },
    familia ? { nivel: 'familia', etiqueta: MACHOTE_FAMILIAS[familia].nombre } : null,
    { nivel: 'producto',    etiqueta: poliza.producto },
  ].filter(Boolean);
  return {
    flujo, variante, varianteEtiqueta, heredada, familia, cadena,
    parrafos: piezas.filter((p) => p.tipo === 'parrafo'),
    adjuntos: piezas.filter((p) => p.tipo === 'adjunto'),
    datos: machoteDatosDe(poliza, flujoId, momentoId),
  };
}

// La tabla de datos del correo, por flujo.
function machoteTablaDe(flujoId, datos) {
  if (flujoId === 'cobranza') return [
    ['Póliza', datos.poliza_numero], ['Recibo', datos.recibo_numero],
    ['Importe', '$' + datos.recibo_importe + ' ' + datos.recibo_moneda], ['Vence', datos.recibo_vencimiento],
  ];
  if (flujoId === 'renovacion') return [
    ['Póliza', datos.poliza_numero], ['Producto', datos.producto],
    ['Vence', datos.fin_vigencia], ['Prima renovada', datos.prima_renovacion + ' ' + datos.moneda],
  ];
  if (flujoId === 'entrega') return [
    ['Póliza', datos.poliza_numero], ['Producto', datos.producto],
    ['Vigencia', datos.inicio_vigencia + ' → ' + datos.fin_vigencia],
  ];
  return [['Póliza', datos.poliza_numero], ['Documentos', datos.documentos]];
}

// Conteo de piezas por nodo del catálogo (para la pestaña de piezas).
function machotePiezasDeNodo(nodo) {
  return MACHOTE_PIEZAS.filter((pz) => {
    if (nodo.nivel === 'aseguradora') return pz.nivel === 'aseguradora' && pz.aseguradora === nodo.aseguradora;
    if (nodo.nivel === 'ramo')        return pz.nivel === 'ramo' && pz.ramo === nodo.ramo;
    if (nodo.nivel === 'familia')     return pz.nivel === 'familia' && pz.familia === nodo.familia;
    if (nodo.nivel === 'producto')    return pz.nivel === 'producto' && pz.aseguradora === nodo.aseguradora && pz.producto === nodo.producto;
    return false;
  });
}

// ═══════════════════════════════════════════════════════════════════════════
// PANTALLA — el router interno
// ═══════════════════════════════════════════════════════════════════════════
function MachotesScreen() {
  const { match, navigate } = useHashRoute();
  if (match('/ajustes/machotes/probar'))  return <MachotesProbar/>;
  if (match('/ajustes/machotes/diseno'))  return <MachotesInicio pestana="diseno"/>;
  if (match('/ajustes/machotes/piezas'))  return <MachotesInicio pestana="piezas"/>;
  const d = match('/ajustes/machotes/:id');
  if (d && MACHOTE_FLUJOS.some((f) => f.id === d.params.id)) {
    return <MachoteEditor flujoId={d.params.id} onBack={() => navigate('/ajustes/machotes')}/>;
  }
  return <MachotesInicio pestana="flujos"/>;
}

const MACHOTE_PESTANAS = [
  { id: 'flujos', ruta: '/ajustes/machotes',        etiqueta: 'Por flujo',           num: 2 },
  { id: 'diseno', ruta: '/ajustes/machotes/diseno', etiqueta: 'Diseño',              num: 1 },
  { id: 'piezas', ruta: '/ajustes/machotes/piezas', etiqueta: 'Piezas por producto', num: 3 },
];

function MachotesInicio({ pestana }) {
  const { navigate } = useHashRoute();
  return (
    <div style={{ flex: 1, overflow: 'auto' }}>
      <AxPageHeader
        breadcrumb={<AxCrumb items={[{ label: 'Ajustes', to: '/ajustes' }, { label: 'Machotes' }]}/>}
        title="Machotes"
        subtitle="Lo que Piixi le escribe a tus asegurados: cómo se ve, qué dice cada flujo y qué agrega cada producto."
        actions={<AxButton variant="primary" icon="eye" onClick={() => navigate('/ajustes/machotes/probar')}>Probar con una póliza</AxButton>}
      />
      <div style={{ padding: '18px 32px 44px', maxWidth: 1080, display: 'flex', flexDirection: 'column', gap: 18 }}>
        <MachotesComoFunciona activa={pestana}/>
        <MachotesPestanas activa={pestana}/>
        {pestana === 'flujos' ? <MachotesPorFlujo/> : null}
        {pestana === 'diseno' ? <MachotesDiseno/> : null}
        {pestana === 'piezas' ? <MachotesPiezas/> : null}
      </div>
    </div>
  );
}

// ── «Cómo funciona»: las cuatro capas, como una fórmula que se lee de izquierda a derecha ──
function MachotesComoFunciona({ activa }) {
  const { navigate } = useHashRoute();
  const capas = [
    { id: 'diseno', num: 1, titulo: 'Diseño',   ruta: '/ajustes/machotes/diseno', icono: 'edit',
      linea: 'Cómo se ve. Uno para todo el despacho.' },
    { id: 'flujos', num: 2, titulo: 'Machote',  ruta: '/ajustes/machotes', icono: 'mail',
      linea: 'Qué dice. Uno por flujo, con variantes.' },
    { id: 'piezas', num: 3, titulo: 'Piezas',   ruta: '/ajustes/machotes/piezas', icono: 'tag',
      linea: 'Lo que agrega el producto: párrafos y adjuntos.' },
    { id: 'probar', num: 4, titulo: 'El correo', ruta: '/ajustes/machotes/probar', icono: 'eye',
      linea: 'Piixi los junta para cada póliza. Pruébalo.' },
  ];
  return (
    <div style={{
      display: 'grid', gridTemplateColumns: '1fr 22px 1fr 22px 1fr 22px 1fr', alignItems: 'stretch', gap: 0,
    }}>
      {capas.map((c, i) => {
        const on = c.id === activa;
        return (
          <React.Fragment key={c.id}>
            {i > 0 ? (
              <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', color: 'var(--ax-fg-faint)', fontSize: 14 }}>
                {i === 3 ? '=' : '+'}
              </div>
            ) : null}
            <button onClick={() => navigate(c.ruta)} style={{
              appearance: 'none', textAlign: 'left', cursor: 'default',
              background: on ? 'var(--ax-bg-3)' : 'var(--ax-bg-2)',
              border: '1px solid ' + (on ? 'var(--ax-accent-line)' : 'var(--ax-border)'),
              borderRadius: 9, padding: '11px 13px', display: 'flex', gap: 10, alignItems: 'flex-start',
            }}>
              <span style={{
                width: 22, height: 22, borderRadius: 6, flexShrink: 0,
                background: on ? 'var(--ax-accent-soft)' : 'var(--ax-bg-3)',
                border: '1px solid ' + (on ? 'var(--ax-accent-line)' : 'var(--ax-border-strong)'),
                color: on ? 'var(--ax-accent)' : 'var(--ax-fg-faint)',
                display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
                fontFamily: 'var(--font-mono)', fontSize: 11,
              }}>{c.num}</span>
              <span style={{ minWidth: 0 }}>
                <span style={{ display: 'block', fontSize: 13, color: 'var(--ax-fg-strong)', fontWeight: 500 }}>{c.titulo}</span>
                <span style={{ display: 'block', marginTop: 2, fontSize: 11, color: 'var(--ax-fg-muted)', lineHeight: 1.45 }}>{c.linea}</span>
              </span>
            </button>
          </React.Fragment>
        );
      })}
    </div>
  );
}

function MachotesPestanas({ activa }) {
  const { navigate } = useHashRoute();
  return (
    <div style={{ display: 'flex', gap: 2, borderBottom: '1px solid var(--ax-border)' }}>
      {MACHOTE_PESTANAS.map((p) => {
        const on = p.id === activa;
        return (
          <button key={p.id} onClick={() => navigate(p.ruta)} style={{
            appearance: 'none', cursor: 'default', background: 'transparent', border: 0,
            padding: '8px 14px', fontSize: 13, color: on ? 'var(--ax-fg-strong)' : 'var(--ax-fg-muted)',
            borderBottom: '2px solid ' + (on ? 'var(--ax-accent)' : 'transparent'), marginBottom: -1,
            display: 'flex', alignItems: 'center', gap: 7,
          }}>
            <span className="ax-mono" style={{ fontSize: 10, color: on ? 'var(--ax-accent)' : 'var(--ax-fg-faint)' }}>{p.num}</span>
            {p.etiqueta}
          </button>
        );
      })}
    </div>
  );
}

// ═══════════════════════════════════════════════════════════════════════════
// PESTAÑA 2 · POR FLUJO — un machote por flujo, con su eje de variantes
// ═══════════════════════════════════════════════════════════════════════════
function MachotesPorFlujo() {
  const { navigate } = useHashRoute();
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
      <div style={{ fontSize: 12, color: 'var(--ax-fg-muted)', lineHeight: 1.6, maxWidth: 720 }}>
        Un machote por flujo. Cada uno varía por el eje que <span style={{ color: 'var(--ax-fg-strong)' }}>ese</span> flujo
        necesita: en cobranza manda el reloj del recibo; en renovación y entrega, el ramo. Una variante que no escribes
        hereda la genérica — no hay que llenar la tabla completa.
      </div>
      {MACHOTE_FLUJOS.map((f) => {
        const porRamo = MACHOTE_POR_RAMO[f.id] || {};
        const propias = f.eje === 'ramo' ? MACHOTE_RAMOS.filter((r) => porRamo[r]) : [];
        const heredan = f.eje === 'ramo' ? MACHOTE_RAMOS.filter((r) => !porRamo[r]) : [];
        return (
          <button key={f.id} onClick={() => navigate('/ajustes/machotes/' + f.id)} style={{
            appearance: 'none', textAlign: 'left', width: '100%', cursor: 'default',
            background: 'var(--ax-bg-2)',
            border: '1px solid ' + (f.instalado ? 'var(--ax-border-strong)' : 'var(--ax-border)'),
            borderRadius: 10, padding: '14px 17px',
            display: 'grid', gridTemplateColumns: '32px 1fr auto 20px', alignItems: 'center', gap: 14,
            opacity: f.instalado ? 1 : 0.85,
          }}>
            <div style={{
              width: 32, height: 32, borderRadius: 7,
              background: f.instalado ? 'var(--ax-accent-soft)' : 'var(--ax-bg-3)',
              border: '1px solid ' + (f.instalado ? 'var(--ax-accent-line)' : 'var(--ax-border-strong)'),
              color: f.instalado ? 'var(--ax-accent)' : 'var(--ax-fg-faint)',
              display: 'flex', alignItems: 'center', justifyContent: 'center',
            }}><AxIcon name={f.icono} size={15}/></div>
            <div style={{ minWidth: 0 }}>
              <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                <span style={{ fontSize: 14, color: 'var(--ax-fg-strong)', fontWeight: 500 }}>{f.nombre}</span>
                <span style={{ fontSize: 11, color: 'var(--ax-fg-faint)' }}>· {f.ejeEtiqueta}</span>
              </div>
              <div style={{ marginTop: 4, fontSize: 12, color: 'var(--ax-fg-muted)' }}>{f.proposito}</div>
              <div style={{ marginTop: 7, display: 'flex', alignItems: 'center', gap: 6, flexWrap: 'wrap' }}>
                {f.eje === 'momento'
                  ? MACHOTE_MOMENTOS.map((m) => <MachoteChip key={m.id} tono={m.tono}>{m.etiqueta}</MachoteChip>)
                  : (
                    <>
                      <MachoteChip tono="accent">Genérico</MachoteChip>
                      {propias.map((r) => <MachoteChip key={r} tono="accent">{MACHOTE_RAMO_NOMBRE[r]}</MachoteChip>)}
                      {heredan.map((r) => <MachoteChip key={r} tono="hereda">{MACHOTE_RAMO_NOMBRE[r]} · hereda</MachoteChip>)}
                    </>
                  )}
              </div>
            </div>
            <div style={{ textAlign: 'right' }}>
              {f.instalado
                ? <AxPill tone="accent" dot>En uso</AxPill>
                : <AxPill tone="ghost">Por escribir</AxPill>}
              <div style={{ marginTop: 5, fontSize: 10.5, color: 'var(--ax-fg-faint)' }}>{f.usadoEn}</div>
            </div>
            <AxIcon name="chevron-right" size={14} style={{ color: 'var(--ax-fg-faint)' }}/>
          </button>
        );
      })}
    </div>
  );
}

function MachoteChip({ tono, children }) {
  const est = {
    info:   { color: 'var(--ax-info)',   border: 'var(--ax-border-strong)', bg: 'transparent' },
    warn:   { color: 'var(--ax-warm)',   border: 'var(--ax-border-strong)', bg: 'transparent' },
    danger: { color: 'var(--ax-danger)', border: 'var(--ax-border-strong)', bg: 'transparent' },
    accent: { color: 'var(--ax-fg)',     border: 'var(--ax-border-strong)', bg: 'var(--ax-bg-3)' },
    hereda: { color: 'var(--ax-fg-faint)', border: 'var(--ax-border)', bg: 'transparent', dashed: true },
  }[tono] || { color: 'var(--ax-fg-muted)', border: 'var(--ax-border)', bg: 'transparent' };
  return (
    <span style={{
      fontSize: 10.5, padding: '2px 8px', borderRadius: 999, color: est.color, background: est.bg,
      border: '1px ' + (est.dashed ? 'dashed' : 'solid') + ' ' + est.border,
    }}>{children}</span>
  );
}

// ═══════════════════════════════════════════════════════════════════════════
// PESTAÑA 1 · DISEÑO — del despacho, uno solo
// ═══════════════════════════════════════════════════════════════════════════
function MachotesDiseno() {
  const { state, pushToast } = useAxStore();
  const [disenoId, setDisenoId] = useState('membrete');
  const [subiendo, setSubiendo] = useState(false);
  const [propioCargado, setPropioCargado] = useState(false);
  const [firma, setFirma] = useState(state.user.name);
  const [despacho, setDespacho] = useState(state.user.orgName);
  const diseno = MACHOTE_DISENOS.find((d) => d.id === disenoId);
  const usandoPropio = disenoId === 'propio';

  return (
    <div style={{ display: 'grid', gridTemplateColumns: 'minmax(320px, 400px) 1fr', gap: 28, alignItems: 'start' }}>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 22 }}>
        <div style={{ fontSize: 12, color: 'var(--ax-fg-muted)', lineHeight: 1.6 }}>
          El diseño es del <span style={{ color: 'var(--ax-fg-strong)' }}>despacho</span>, no del flujo: cobranza,
          renovación y entrega salen con el mismo membrete y la misma firma. Cambiarlo aquí cambia todos los avisos.
        </div>
        <MachoteBloque titulo="Diseño en uso" nota="Cómo se ve el correo que recibe tu asegurado.">
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8 }}>
            {MACHOTE_DISENOS.map((d) => (
              <MachoteMiniatura key={d.id} diseno={d} activo={d.id === disenoId}
                cargado={d.id === 'propio' ? propioCargado : true}
                onClick={() => { setDisenoId(d.id); if (d.id === 'propio' && !propioCargado) setSubiendo(true); }}/>
            ))}
          </div>
          {usandoPropio ? (
            <div style={{ marginTop: 10 }}>
              <AxButton variant="secondary" size="sm" icon="upload" onClick={() => setSubiendo(true)}>
                {propioCargado ? 'Reemplazar mi HTML' : 'Subir mi HTML'}
              </AxButton>
            </div>
          ) : null}
        </MachoteBloque>
        <MachoteBloque titulo="Firma" nota="Aparece al pie de todo lo que Piixi redacta por ti.">
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
            <AxField label="Firma"><AxInput value={firma} onChange={(e) => setFirma(e.target.value)}/></AxField>
            <AxField label="Despacho"><AxInput value={despacho} onChange={(e) => setDespacho(e.target.value)}/></AxField>
          </div>
        </MachoteBloque>
        <div>
          <AxButton variant="primary" icon="check" disabled={usandoPropio && !propioCargado}
            onClick={() => pushToast({ kind: 'success', message: 'Diseño guardado. Todos los avisos salen así desde ahora.' })}>
            Guardar diseño
          </AxButton>
        </div>
      </div>

      <MachotePrevia
        diseno={diseno} firma={firma} despacho={despacho} correo={state.user.email}
        cabecera="Aviso de cobranza" etiqueta="Así se ve con el machote de cobranza · por vencer"
        asunto={machoteRellenar(MACHOTE_MOMENTOS[0].asunto, MACHOTE_EJEMPLO_RECIBO)}
        saludo={machoteRellenar(MACHOTE_MOMENTOS[0].saludo, MACHOTE_EJEMPLO_RECIBO)}
        cuerpo={machoteRellenar(MACHOTE_MOMENTOS[0].deber, MACHOTE_EJEMPLO_RECIBO)}
        cierre={MACHOTE_MOMENTOS[0].cierre}
        para={MACHOTE_EJEMPLO_RECIBO.para}
        tabla={[
          ['Póliza', MACHOTE_EJEMPLO_RECIBO.poliza_numero],
          ['Recibo', MACHOTE_EJEMPLO_RECIBO.recibo_numero],
          ['Importe', '$' + MACHOTE_EJEMPLO_RECIBO.recibo_importe + ' ' + MACHOTE_EJEMPLO_RECIBO.recibo_moneda],
          ['Vence', MACHOTE_EJEMPLO_RECIBO.recibo_vencimiento],
        ]}
        bloqueado={usandoPropio && !propioCargado}
        onSubir={() => setSubiendo(true)}
        compacto
      />

      <MachoteSubirModal
        open={subiendo}
        onClose={() => { setSubiendo(false); if (!propioCargado) setDisenoId('membrete'); }}
        onListo={() => { setPropioCargado(true); setSubiendo(false); setDisenoId('propio');
          pushToast({ kind: 'success', message: 'Tu diseño pasó la verificación: nombra {saludo}, {deber}, {cierre} y {firma}.' }); }}
      />
    </div>
  );
}

// ═══════════════════════════════════════════════════════════════════════════
// PESTAÑA 3 · PIEZAS POR PRODUCTO — el árbol del catálogo con lo que cada nivel agrega
// ═══════════════════════════════════════════════════════════════════════════
function MachotesPiezas() {
  const { navigate } = useHashRoute();
  const [abiertos, setAbiertos] = useState(() => new Set(['gnp', 'gnp|VIDA', 'gnp|DANOS', 'metlife', 'metlife|VIDA']));
  const [sel, setSel] = useState({ nivel: 'producto', aseguradora: 'gnp', ramo: 'VIDA', familia: 'inversion', producto: 'Privilegio Plus' });
  const toggle = (k) => setAbiertos((s) => { const n = new Set(s); n.has(k) ? n.delete(k) : n.add(k); return n; });

  const polizasDe = (filtro) => AdmixData.POLICIES.filter(filtro).length;

  return (
    <div style={{ display: 'grid', gridTemplateColumns: 'minmax(360px, 460px) 1fr', gap: 24, alignItems: 'start' }}>
      <div>
        <div style={{ fontSize: 12, color: 'var(--ax-fg-muted)', lineHeight: 1.6, marginBottom: 12 }}>
          Es el mismo catálogo de productos que ya decide la gracia y la anticipación de cada póliza, con una columna
          más: <span style={{ color: 'var(--ax-fg-strong)' }}>qué agrega al aviso</span>. Un producto no tiene machote
          propio — tiene piezas. Lo que no declara, lo hereda de arriba.
        </div>
        <div style={{ border: '1px solid var(--ax-border-strong)', borderRadius: 10, background: 'var(--ax-bg-2)', overflow: 'hidden' }}>
          {AdmixData.CARRIERS.map((c) => {
            const kA = c.id;
            const nodoA = { nivel: 'aseguradora', aseguradora: c.id };
            const ramosCon = MACHOTE_RAMOS.filter((r) => ((AdmixData.PRODUCTS[c.id] || {})[r] || []).length || ((MACHOTE_PRODUCTOS_EXTRA[c.id] || {})[r] || []).length);
            return (
              <div key={c.id} style={{ borderTop: '1px solid var(--ax-border)' }}>
                <MachoteNodoFila nivel={0} abierto={abiertos.has(kA)} onToggle={() => toggle(kA)}
                  seleccionado={mismoNodo(sel, nodoA)} onSelect={() => setSel(nodoA)}
                  icono={<AxCarrierAvatar id={c.id} size={18}/>} titulo={c.name}
                  piezas={machotePiezasDeNodo(nodoA)} polizas={polizasDe((p) => p.carrier === c.id)}/>
                {abiertos.has(kA) ? ramosCon.map((r) => {
                  const kR = c.id + '|' + r;
                  const nodoR = { nivel: 'ramo', aseguradora: c.id, ramo: r };
                  const productos = [ ...(((AdmixData.PRODUCTS[c.id] || {})[r]) || []), ...(((MACHOTE_PRODUCTOS_EXTRA[c.id] || {})[r]) || []) ];
                  return (
                    <div key={kR}>
                      <MachoteNodoFila nivel={1} abierto={abiertos.has(kR)} onToggle={() => toggle(kR)}
                        seleccionado={mismoNodo(sel, nodoR)} onSelect={() => setSel(nodoR)}
                        icono={<AxRamoTag code={r}/>} titulo={MACHOTE_RAMO_NOMBRE[r]}
                        piezas={machotePiezasDeNodo(nodoR)} polizas={polizasDe((p) => p.carrier === c.id && p.ramo === r)}
                        nota="ramo: aplica a todas las aseguradoras"/>
                      {abiertos.has(kR) ? productos.map((prod) => {
                        const fam = r === 'VIDA' ? (MACHOTE_FAMILIA_DE_PRODUCTO[prod] || 'proteccion') : null;
                        const nodoP = { nivel: 'producto', aseguradora: c.id, ramo: r, familia: fam, producto: prod };
                        const piezasFam = fam ? machotePiezasDeNodo({ nivel: 'familia', familia: fam }) : [];
                        return (
                          <MachoteNodoFila key={prod} nivel={2}
                            seleccionado={mismoNodo(sel, nodoP)} onSelect={() => setSel(nodoP)}
                            icono={<AxIcon name="tag" size={12} style={{ color: 'var(--ax-fg-faint)' }}/>} titulo={prod}
                            familia={fam ? MACHOTE_FAMILIAS[fam].nombre : null}
                            piezas={machotePiezasDeNodo(nodoP)} piezasHeredadas={piezasFam}
                            polizas={polizasDe((p) => p.carrier === c.id && p.producto === prod)}/>
                        );
                      }) : null}
                    </div>
                  );
                }) : null}
              </div>
            );
          })}
        </div>
      </div>

      <MachotePiezasDetalle nodo={sel} onProbar={() => navigate('/ajustes/machotes/probar')}/>
    </div>
  );
}

function mismoNodo(a, b) {
  return a && b && a.nivel === b.nivel && a.aseguradora === b.aseguradora && (a.ramo || null) === (b.ramo || null) && (a.producto || null) === (b.producto || null);
}

function MachoteNodoFila({ nivel, abierto, onToggle, seleccionado, onSelect, icono, titulo, familia, nota, piezas, piezasHeredadas, polizas }) {
  const nParr = piezas.filter((p) => p.tipo === 'parrafo').length;
  const nAdj  = piezas.filter((p) => p.tipo === 'adjunto').length;
  const hered = (piezasHeredadas || []).length;
  return (
    <div onClick={onSelect} style={{
      display: 'grid', gridTemplateColumns: '18px auto 1fr auto', alignItems: 'center', gap: 8,
      padding: '8px 12px 8px ' + (12 + nivel * 18) + 'px',
      background: seleccionado ? 'var(--ax-bg-3)' : 'transparent',
      borderLeft: '2px solid ' + (seleccionado ? 'var(--ax-accent)' : 'transparent'),
      cursor: 'default',
    }}>
      {onToggle
        ? <button onClick={(e) => { e.stopPropagation(); onToggle(); }} style={{ appearance: 'none', background: 'transparent', border: 0, padding: 0, color: 'var(--ax-fg-faint)', cursor: 'default' }}>
            <AxIcon name="chevron-right" size={11} style={{ transform: abierto ? 'rotate(90deg)' : 'none', transition: 'transform 120ms' }}/>
          </button>
        : <span/>}
      {icono}
      <div style={{ minWidth: 0, display: 'flex', alignItems: 'center', gap: 8 }}>
        <span style={{ fontSize: nivel === 0 ? 13 : 12.5, color: 'var(--ax-fg-strong)', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{titulo}</span>
        {familia ? <MachoteChip tono="accent">{familia}</MachoteChip> : null}
      </div>
      <div style={{ display: 'flex', alignItems: 'center', gap: 6, fontSize: 10.5, color: 'var(--ax-fg-faint)', whiteSpace: 'nowrap' }}>
        {nParr ? <span style={{ color: 'var(--ax-fg-muted)' }}>{nParr} párrafo{nParr > 1 ? 's' : ''}</span> : null}
        {nAdj ? <span style={{ color: 'var(--ax-fg-muted)' }}>{nAdj} adjunto{nAdj > 1 ? 's' : ''}</span> : null}
        {hered ? <span>+{hered} de la familia</span> : null}
        {!nParr && !nAdj && !hered ? <span>hereda</span> : null}
        <span className="ax-mono" style={{ marginLeft: 4, minWidth: 34, textAlign: 'right' }}>{polizas} pól.</span>
      </div>
    </div>
  );
}

function MachotePiezasDetalle({ nodo, onProbar }) {
  const { pushToast } = useAxStore();
  const propias = machotePiezasDeNodo(nodo);
  const heredadas = [];
  if (nodo.nivel === 'producto') {
    if (nodo.familia) heredadas.push(...machotePiezasDeNodo({ nivel: 'familia', familia: nodo.familia }).map((p) => ({ ...p, de: 'Familia ' + MACHOTE_FAMILIAS[nodo.familia].nombre })));
    heredadas.push(...machotePiezasDeNodo({ nivel: 'ramo', ramo: nodo.ramo }).map((p) => ({ ...p, de: 'Ramo ' + MACHOTE_RAMO_NOMBRE[nodo.ramo] })));
    heredadas.push(...machotePiezasDeNodo({ nivel: 'aseguradora', aseguradora: nodo.aseguradora }).map((p) => ({ ...p, de: (AdmixData.carrierById(nodo.aseguradora) || {}).name })));
  } else if (nodo.nivel === 'ramo') {
    heredadas.push(...machotePiezasDeNodo({ nivel: 'aseguradora', aseguradora: nodo.aseguradora }).map((p) => ({ ...p, de: (AdmixData.carrierById(nodo.aseguradora) || {}).name })));
  }
  const titulo = nodo.nivel === 'producto' ? nodo.producto
    : nodo.nivel === 'ramo' ? MACHOTE_RAMO_NOMBRE[nodo.ramo]
    : (AdmixData.carrierById(nodo.aseguradora) || {}).name;

  return (
    <div style={{ border: '1px solid var(--ax-border-strong)', borderRadius: 10, background: 'var(--ax-bg-2)', overflow: 'hidden', position: 'sticky', top: 0 }}>
      <div style={{ padding: '13px 16px', borderBottom: '1px solid var(--ax-border)', display: 'flex', alignItems: 'center', gap: 10 }}>
        <div style={{ flex: 1, minWidth: 0 }}>
          <AxEyebrow>{MACHOTE_NIVEL_ETIQUETA[nodo.nivel]}</AxEyebrow>
          <div style={{ marginTop: 3, fontSize: 15, color: 'var(--ax-fg-strong)', fontWeight: 500 }}>{titulo}</div>
        </div>
        <AxButton variant="tertiary" size="sm" icon="eye" onClick={onProbar}>Ver un correo</AxButton>
      </div>
      <div style={{ padding: 16, display: 'flex', flexDirection: 'column', gap: 18 }}>
        {nodo.nivel === 'producto' && nodo.ramo === 'VIDA' ? (
          <MachoteBloque titulo="Familia" nota="El atributo del producto que decide sus piezas. Se declara una vez.">
            <div style={{ display: 'flex', gap: 6 }}>
              {Object.values(MACHOTE_FAMILIAS).map((f) => {
                const on = f.id === nodo.familia;
                return (
                  <button key={f.id} title={f.nota} style={{
                    appearance: 'none', cursor: 'default', fontSize: 12, padding: '6px 11px', borderRadius: 7,
                    background: on ? 'var(--ax-accent-soft)' : 'transparent',
                    border: '1px solid ' + (on ? 'var(--ax-accent-line)' : 'var(--ax-border-strong)'),
                    color: on ? 'var(--ax-fg-strong)' : 'var(--ax-fg-muted)',
                  }}>{f.nombre}</button>
                );
              })}
            </div>
          </MachoteBloque>
        ) : null}

        <MachoteBloque titulo={'Piezas de ' + (nodo.nivel === 'producto' ? 'este producto' : nodo.nivel === 'ramo' ? 'este ramo' : 'esta aseguradora')}
          nota="Se insertan en el machote del flujo donde apliquen. Un párrafo entra en el cuerpo; un adjunto viaja con el correo.">
          {propias.length ? (
            <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
              {propias.map((p) => <MachotePiezaFila key={p.id} pieza={p}/>)}
            </div>
          ) : (
            <div style={{ fontSize: 12, color: 'var(--ax-fg-faint)', padding: '8px 0' }}>
              No agrega nada propio: lo que sale es lo que hereda.
            </div>
          )}
          <div style={{ marginTop: 10, display: 'flex', gap: 8 }}>
            <AxButton variant="secondary" size="sm" icon="plus" onClick={() => pushToast({ kind: 'info', message: 'Un párrafo nuevo se escribe aquí y aparece en los flujos que marques.' })}>Párrafo</AxButton>
            <AxButton variant="secondary" size="sm" icon="upload" onClick={() => pushToast({ kind: 'info', message: 'El archivo se guarda cifrado y viaja con cada correo del flujo.' })}>Adjunto</AxButton>
          </div>
        </MachoteBloque>

        {heredadas.length ? (
          <MachoteBloque titulo="Lo que hereda" nota="Viene de arriba en el catálogo. Se cambia allá, una sola vez.">
            <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
              {heredadas.map((p) => <MachotePiezaFila key={p.id + p.de} pieza={p} de={p.de} tenue/>)}
            </div>
          </MachoteBloque>
        ) : null}
      </div>
    </div>
  );
}

function MachotePiezaFila({ pieza: p, de, tenue }) {
  return (
    <div style={{
      display: 'grid', gridTemplateColumns: '18px 1fr auto', gap: 9, alignItems: 'start',
      padding: '8px 10px', borderRadius: 7,
      border: '1px ' + (tenue ? 'dashed' : 'solid') + ' var(--ax-border)',
      background: tenue ? 'transparent' : 'var(--ax-bg-3)', opacity: tenue ? 0.85 : 1,
    }}>
      <AxIcon name={p.tipo === 'adjunto' ? 'pdf' : 'edit'} size={13} style={{ color: 'var(--ax-fg-faint)', marginTop: 2 }}/>
      <div style={{ minWidth: 0 }}>
        <div style={{ fontSize: 12.5, color: 'var(--ax-fg-strong)' }}>{p.nombre}</div>
        {p.texto ? <div style={{ marginTop: 2, fontSize: 11, color: 'var(--ax-fg-muted)', lineHeight: 1.5 }}>{p.texto}</div> : null}
        <div style={{ marginTop: 4, display: 'flex', gap: 5, flexWrap: 'wrap' }}>
          {p.flujos.map((f) => <MachoteChip key={f} tono="accent">{(MACHOTE_FLUJOS.find((x) => x.id === f) || {}).nombre}</MachoteChip>)}
          {de ? <MachoteChip tono="hereda">de {de}</MachoteChip> : null}
        </div>
      </div>
      <span style={{ fontSize: 10, color: 'var(--ax-fg-faint)', textTransform: 'uppercase', letterSpacing: '0.08em' }}>{p.tipo}</span>
    </div>
  );
}

// ═══════════════════════════════════════════════════════════════════════════
// PROBAR — ¿qué le llegaría a esta póliza? La traza de cómo se armó, y el correo.
// ═══════════════════════════════════════════════════════════════════════════
function MachotesProbar() {
  const { state } = useAxStore();
  const { navigate } = useHashRoute();
  const [flujoId, setFlujoId] = useState('entrega');
  const [momentoId, setMomentoId] = useState('anticipado');
  const [ramoFiltro, setRamoFiltro] = useState('VIDA');
  const [busca, setBusca] = useState('');
  const polizas = useMemo(() => AdmixData.POLICIES.filter((p) => {
    if (ramoFiltro && p.ramo !== ramoFiltro) return false;
    if (!busca) return true;
    const c = AdmixData.contratanteById(p.contratante);
    const hay = (p.polizaNumero + ' ' + p.producto + ' ' + (c ? c.nombre : '')).toLowerCase();
    return hay.includes(busca.toLowerCase());
  }).slice(0, 40), [ramoFiltro, busca]);
  const [polizaId, setPolizaId] = useState(() => {
    const p = AdmixData.POLICIES.find((x) => x.carrier === 'gnp' && x.producto === 'Privilegio Plus') || AdmixData.POLICIES[0];
    return p.id;
  });
  const poliza = AdmixData.policyById(polizaId) || AdmixData.POLICIES[0];
  const r = machoteResolver(poliza, flujoId, momentoId);
  const diseno = MACHOTE_DISENOS[1];
  const tabla = machoteTablaDe(flujoId, r.datos);

  return (
    <div style={{ flex: 1, overflow: 'auto' }}>
      <AxPageHeader
        breadcrumb={<AxCrumb items={[{ label: 'Ajustes', to: '/ajustes' }, { label: 'Machotes', to: '/ajustes/machotes' }, { label: 'Probar' }]}/>}
        title="¿Qué le llegaría a esta póliza?"
        subtitle="Escoge un flujo y una póliza real de tu cartera. Piixi te enseña el correo y de dónde salió cada parte."
        actions={<AxButton variant="tertiary" icon="arrow-left" onClick={() => navigate('/ajustes/machotes')}>Volver a machotes</AxButton>}
      />
      <div style={{ display: 'grid', gridTemplateColumns: '300px 330px 1fr', gap: 0, minHeight: 0 }}>
        {/* ── 1. La póliza ───────────────────────────────────────── */}
        <div style={{ borderRight: '1px solid var(--ax-border)', padding: '18px 18px 40px', display: 'flex', flexDirection: 'column', gap: 16 }}>
          <MachoteBloque titulo="Flujo" nota="Qué aviso quieres ver.">
            <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
              {MACHOTE_FLUJOS.map((f) => {
                const on = f.id === flujoId;
                return (
                  <button key={f.id} onClick={() => setFlujoId(f.id)} style={{
                    appearance: 'none', cursor: 'default', textAlign: 'left', display: 'flex', alignItems: 'center', gap: 9,
                    padding: '7px 10px', borderRadius: 7,
                    background: on ? 'var(--ax-bg-3)' : 'transparent',
                    border: '1px solid ' + (on ? 'var(--ax-border-strong)' : 'transparent'),
                  }}>
                    <AxIcon name={f.icono} size={13} style={{ color: on ? 'var(--ax-accent)' : 'var(--ax-fg-faint)' }}/>
                    <span style={{ fontSize: 13, color: on ? 'var(--ax-fg-strong)' : 'var(--ax-fg-muted)' }}>{f.nombre}</span>
                  </button>
                );
              })}
            </div>
            {flujoId === 'cobranza' ? (
              <div style={{ marginTop: 8, display: 'flex', gap: 5 }}>
                {MACHOTE_MOMENTOS.map((m) => (
                  <button key={m.id} onClick={() => setMomentoId(m.id)} style={{
                    appearance: 'none', cursor: 'default', fontSize: 11, padding: '4px 9px', borderRadius: 999,
                    background: m.id === momentoId ? 'var(--ax-bg-3)' : 'transparent',
                    border: '1px solid ' + (m.id === momentoId ? 'var(--ax-border-strong)' : 'var(--ax-border)'),
                    color: m.id === momentoId ? 'var(--ax-fg-strong)' : 'var(--ax-fg-muted)',
                  }}>{m.etiqueta}</button>
                ))}
              </div>
            ) : null}
          </MachoteBloque>

          <MachoteBloque titulo="Póliza" nota="De tu cartera. Filtra por ramo o busca por nombre.">
            <div style={{ display: 'flex', gap: 5, marginBottom: 8, flexWrap: 'wrap' }}>
              {[['', 'Todas'], ...MACHOTE_RAMOS.map((x) => [x, MACHOTE_RAMO_NOMBRE[x]])].map(([v, et]) => (
                <button key={v} onClick={() => setRamoFiltro(v)} style={{
                  appearance: 'none', cursor: 'default', fontSize: 11, padding: '4px 9px', borderRadius: 999,
                  background: v === ramoFiltro ? 'var(--ax-bg-3)' : 'transparent',
                  border: '1px solid ' + (v === ramoFiltro ? 'var(--ax-border-strong)' : 'var(--ax-border)'),
                  color: v === ramoFiltro ? 'var(--ax-fg-strong)' : 'var(--ax-fg-muted)',
                }}>{et}</button>
              ))}
            </div>
            <AxInput placeholder="Nombre, producto o número" value={busca} onChange={(e) => setBusca(e.target.value)}/>
            <div style={{ marginTop: 8, display: 'flex', flexDirection: 'column', gap: 3, maxHeight: 380, overflow: 'auto' }}>
              {polizas.map((p) => {
                const c = AdmixData.contratanteById(p.contratante);
                const on = p.id === polizaId;
                const fam = machoteFamiliaDe(p);
                return (
                  <button key={p.id} onClick={() => setPolizaId(p.id)} style={{
                    appearance: 'none', cursor: 'default', textAlign: 'left',
                    display: 'grid', gridTemplateColumns: '18px 1fr', gap: 8, alignItems: 'center',
                    padding: '7px 9px', borderRadius: 7,
                    background: on ? 'var(--ax-bg-3)' : 'transparent',
                    border: '1px solid ' + (on ? 'var(--ax-border-strong)' : 'transparent'),
                  }}>
                    <AxCarrierAvatar id={p.carrier} size={18}/>
                    <div style={{ minWidth: 0 }}>
                      <div style={{ fontSize: 12.5, color: on ? 'var(--ax-fg-strong)' : 'var(--ax-fg)', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{c ? c.nombre : '—'}</div>
                      <div style={{ fontSize: 10.5, color: 'var(--ax-fg-faint)', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
                        {p.producto}{fam ? ' · ' + MACHOTE_FAMILIAS[fam].nombre : ''} · {p.polizaNumero}
                      </div>
                    </div>
                  </button>
                );
              })}
              {!polizas.length ? <div style={{ fontSize: 12, color: 'var(--ax-fg-faint)', padding: 8 }}>Ninguna póliza casa.</div> : null}
            </div>
          </MachoteBloque>
        </div>

        {/* ── 2. Cómo se armó ────────────────────────────────────── */}
        <div style={{ borderRight: '1px solid var(--ax-border)', padding: '18px 18px 40px', display: 'flex', flexDirection: 'column', gap: 12 }}>
          <div>
            <AxEyebrow>Cómo se armó</AxEyebrow>
            <div style={{ marginTop: 4, fontSize: 11, color: 'var(--ax-fg-faint)', lineHeight: 1.5 }}>Cada parte dice de dónde salió y dónde se cambia.</div>
          </div>

          <MachoteTrazaPaso num={1} titulo="Diseño" valor={diseno.nombre} nota="Del despacho. Igual para todos los flujos."
            onIr={() => navigate('/ajustes/machotes/diseno')}/>

          <MachoteTrazaPaso num={2} titulo={'Machote de ' + r.flujo.nombre.toLowerCase()}
            valor={'Variante ' + r.varianteEtiqueta + (r.heredada ? ' (hereda)' : '')}
            nota={r.flujo.eje === 'momento'
              ? 'La eligió el reloj del recibo.'
              : r.heredada
                ? MACHOTE_RAMO_NOMBRE[poliza.ramo] + ' no tiene texto propio en este flujo: sale el genérico.'
                : 'Este flujo tiene texto propio para ' + MACHOTE_RAMO_NOMBRE[poliza.ramo] + '.'}
            onIr={() => navigate('/ajustes/machotes/' + r.flujo.id)}/>

          <MachoteTrazaPaso num={3} titulo="Piezas del producto"
            valor={(r.parrafos.length + r.adjuntos.length) ? (r.parrafos.length + ' párrafo(s) · ' + r.adjuntos.length + ' adjunto(s)') : 'Ninguna'}
            nota="Se buscan de abajo hacia arriba en el catálogo:" onIr={() => navigate('/ajustes/machotes/piezas')}>
            <div style={{ marginTop: 8, display: 'flex', flexDirection: 'column', gap: 4 }}>
              {[...r.cadena].reverse().map((n) => {
                const piezas = [...r.parrafos, ...r.adjuntos].filter((p) => p.nivel === n.nivel);
                return (
                  <div key={n.nivel} style={{ display: 'grid', gridTemplateColumns: '78px 1fr', gap: 8, alignItems: 'baseline', fontSize: 11.5 }}>
                    <span style={{ color: 'var(--ax-fg-faint)' }}>{MACHOTE_NIVEL_ETIQUETA[n.nivel]}</span>
                    <span>
                      <span style={{ color: 'var(--ax-fg-strong)' }}>{n.etiqueta}</span>
                      {piezas.length
                        ? piezas.map((p) => <span key={p.id} style={{ display: 'block', color: 'var(--ax-fg-muted)' }}>+ {p.nombre}</span>)
                        : <span style={{ color: 'var(--ax-fg-faint)' }}> · nada propio</span>}
                    </span>
                  </div>
                );
              })}
            </div>
          </MachoteTrazaPaso>

          <MachoteTrazaPaso num={4} titulo="Datos de la póliza" valor={r.datos.para}
            nota="Piixi sustituye cada marcador con lo que sabe de esta póliza. Si un dato obligatorio falta, no redacta: te avisa."/>
        </div>

        {/* ── 3. El correo ───────────────────────────────────────── */}
        <MachotePrevia
          diseno={diseno} firma={state.user.name} despacho={state.user.orgName} correo={state.user.email}
          cabecera={r.flujo.cabecera} etiqueta="Como lo va a ver tu asegurado"
          asunto={machoteRellenar(r.variante.asunto, r.datos)}
          saludo={machoteRellenar(r.variante.saludo, r.datos)}
          cuerpo={machoteRellenar(r.variante.deber, r.datos)}
          parrafos={r.parrafos}
          cierre={machoteRellenar(r.variante.cierre, r.datos)}
          adjuntos={r.adjuntos.map((a) => a.nombre).concat(flujoId === 'entrega' ? [poliza.sourcePdf] : [])}
          para={r.datos.para}
          tabla={tabla}
        />
      </div>
    </div>
  );
}

function MachoteTrazaPaso({ num, titulo, valor, nota, onIr, children }) {
  return (
    <div style={{ border: '1px solid var(--ax-border)', borderRadius: 8, background: 'var(--ax-bg-2)', padding: '10px 12px' }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
        <span className="ax-mono" style={{ fontSize: 10, color: 'var(--ax-accent)' }}>{num}</span>
        <span style={{ flex: 1, fontSize: 12, color: 'var(--ax-fg-muted)' }}>{titulo}</span>
        {onIr ? (
          <button onClick={onIr} style={{ appearance: 'none', background: 'transparent', border: 0, padding: 0, fontSize: 11, color: 'var(--ax-accent)', cursor: 'default', display: 'inline-flex', alignItems: 'center', gap: 3 }}>
            cambiar <AxIcon name="arrow-right" size={10}/>
          </button>
        ) : null}
      </div>
      <div style={{ marginTop: 4, fontSize: 13, color: 'var(--ax-fg-strong)', fontWeight: 500 }}>{valor}</div>
      {nota ? <div style={{ marginTop: 3, fontSize: 11, color: 'var(--ax-fg-faint)', lineHeight: 1.5 }}>{nota}</div> : null}
      {children}
    </div>
  );
}

// ═══════════════════════════════════════════════════════════════════════════
// EDITOR DE UN FLUJO — variantes por su eje; texto = deber + tu voz; verificación
// ═══════════════════════════════════════════════════════════════════════════
function MachoteEditor({ flujoId, onBack }) {
  const { state, pushToast } = useAxStore();
  const { navigate } = useHashRoute();
  const flujo = MACHOTE_FLUJOS.find((f) => f.id === flujoId);
  const porMomento = flujo.eje === 'momento';
  const tabla = MACHOTE_POR_RAMO[flujoId] || {};

  const [momentoId, setMomentoId] = useState('anticipado');
  const [ramoSel, setRamoSel] = useState('generico');
  const [propias, setPropias] = useState(() => new Set(MACHOTE_RAMOS.filter((r) => tabla[r])));
  const [textos, setTextos] = useState(() => {
    const o = {};
    if (porMomento) MACHOTE_MOMENTOS.forEach((m) => { o[m.id] = { ...m }; });
    else { o.generico = { ...tabla.generico }; MACHOTE_RAMOS.forEach((r) => { o[r] = { ...(tabla[r] || tabla.generico) }; }); }
    return o;
  });

  const claveActiva = porMomento ? momentoId : ramoSel;
  const heredando = !porMomento && ramoSel !== 'generico' && !propias.has(ramoSel);
  const t = heredando ? textos.generico : textos[claveActiva];
  const setT = (k, v) => setTextos((s) => ({ ...s, [claveActiva]: { ...s[claveActiva], [k]: v } }));
  const obligatorios = MACHOTE_OBLIGATORIOS[flujoId];
  const diseno = MACHOTE_DISENOS[1];

  const polizaEjemplo = useMemo(() => {
    if (porMomento) return null;
    const ramo = ramoSel === 'generico' ? 'GMM' : ramoSel;
    return AdmixData.POLICIES.find((p) => p.ramo === ramo) || AdmixData.POLICIES[0];
  }, [ramoSel, porMomento]);
  const datos = porMomento ? MACHOTE_EJEMPLO_RECIBO : machoteDatosDe(polizaEjemplo, flujoId);
  const piezas = porMomento ? [] : machotePiezasPara(polizaEjemplo, flujoId);

  return (
    <div style={{ flex: 1, overflow: 'auto' }}>
      <AxPageHeader
        breadcrumb={<AxCrumb items={[{ label: 'Ajustes', to: '/ajustes' }, { label: 'Machotes', to: '/ajustes/machotes' }, { label: flujo.nombre }]}/>}
        title={'Machote de ' + flujo.nombre.toLowerCase()}
        subtitle={flujo.proposito + ' Piixi garantiza que diga lo obligatorio; tú decides cómo suena.'}
        actions={
          <>
            <AxButton variant="tertiary" icon="arrow-left" onClick={onBack}>Volver</AxButton>
            <AxButton variant="primary" icon="check"
              onClick={() => pushToast({ kind: 'success', message: 'Machote guardado. Los próximos avisos ya salen así — sin actualizar la app.' })}>
              Guardar
            </AxButton>
          </>
        }
      />
      <div style={{ display: 'grid', gridTemplateColumns: 'minmax(320px, 400px) 1fr', alignItems: 'stretch' }}>
        <div style={{ borderRight: '1px solid var(--ax-border)', padding: '20px 22px 40px', display: 'flex', flexDirection: 'column', gap: 22, overflow: 'auto' }}>
          {porMomento ? (
            <MachoteBloque titulo="Momento" nota="El reloj del recibo elige cuál sale.">
              <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
                {MACHOTE_MOMENTOS.map((m) => {
                  const on = m.id === momentoId;
                  const color = m.tono === 'danger' ? 'var(--ax-danger)' : m.tono === 'warn' ? 'var(--ax-warm)' : 'var(--ax-info)';
                  return (
                    <button key={m.id} onClick={() => setMomentoId(m.id)} style={{
                      appearance: 'none', cursor: 'default', textAlign: 'left', display: 'flex', alignItems: 'center', gap: 10,
                      padding: '9px 11px', borderRadius: 7,
                      background: on ? 'var(--ax-bg-3)' : 'transparent',
                      border: '1px solid ' + (on ? 'var(--ax-border-strong)' : 'transparent'),
                    }}>
                      <span style={{ width: 7, height: 7, borderRadius: '50%', background: color, flexShrink: 0 }}/>
                      <span style={{ flex: 1, fontSize: 13, color: on ? 'var(--ax-fg-strong)' : 'var(--ax-fg-muted)' }}>{m.etiqueta}</span>
                      <span style={{ fontSize: 10, color: 'var(--ax-fg-faint)', fontFamily: 'var(--font-mono)' }}>{m.pista}</span>
                    </button>
                  );
                })}
              </div>
            </MachoteBloque>
          ) : (
            <MachoteBloque titulo="Variante por ramo" nota="El ramo de la póliza elige cuál sale. Lo que no escribas hereda el genérico.">
              <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
                {[['generico', 'Genérico'], ...MACHOTE_RAMOS.map((r) => [r, MACHOTE_RAMO_NOMBRE[r]])].map(([id, et]) => {
                  const on = id === ramoSel;
                  const propia = id === 'generico' || propias.has(id);
                  return (
                    <button key={id} onClick={() => setRamoSel(id)} style={{
                      appearance: 'none', cursor: 'default', textAlign: 'left', display: 'flex', alignItems: 'center', gap: 10,
                      padding: '9px 11px', borderRadius: 7,
                      background: on ? 'var(--ax-bg-3)' : 'transparent',
                      border: '1px ' + (propia ? 'solid' : 'dashed') + ' ' + (on ? 'var(--ax-border-strong)' : 'transparent'),
                    }}>
                      <span style={{ width: 7, height: 7, borderRadius: '50%', background: propia ? 'var(--ax-accent)' : 'var(--ax-fg-faint)', opacity: propia ? 1 : 0.5, flexShrink: 0 }}/>
                      <span style={{ flex: 1, fontSize: 13, color: on ? 'var(--ax-fg-strong)' : 'var(--ax-fg-muted)' }}>{et}</span>
                      <span style={{ fontSize: 10, color: 'var(--ax-fg-faint)', fontFamily: 'var(--font-mono)' }}>{id === 'generico' ? 'base' : propia ? 'propio' : 'hereda'}</span>
                    </button>
                  );
                })}
              </div>
              {heredando ? (
                <div style={{ marginTop: 10, border: '1px dashed var(--ax-border-strong)', borderRadius: 7, padding: '10px 12px', fontSize: 11.5, color: 'var(--ax-fg-muted)', lineHeight: 1.55 }}>
                  <span style={{ color: 'var(--ax-fg-strong)' }}>{MACHOTE_RAMO_NOMBRE[ramoSel]} hereda el genérico.</span> Abajo ves ese texto.
                  <div style={{ marginTop: 8 }}>
                    <AxButton variant="secondary" size="sm" icon="edit" onClick={() => { setPropias((s) => new Set([...s, ramoSel])); setTextos((s) => ({ ...s, [ramoSel]: { ...s.generico } })); }}>
                      Escribir uno propio para {MACHOTE_RAMO_NOMBRE[ramoSel]}
                    </AxButton>
                  </div>
                </div>
              ) : (ramoSel !== 'generico' ? (
                <div style={{ marginTop: 8 }}>
                  <AxButton variant="tertiary" size="sm" onClick={() => setPropias((s) => { const n = new Set(s); n.delete(ramoSel); return n; })}>
                    Volver a heredar el genérico
                  </AxButton>
                </div>
              ) : null)}
            </MachoteBloque>
          )}

          <MachoteBloque titulo="Lo que dice" nota="El deber es lo que hay obligación de comunicar; tu voz es cómo lo dices.">
            <div style={{ display: 'flex', flexDirection: 'column', gap: 12, opacity: heredando ? 0.6 : 1 }}>
              <AxField label="Asunto"><AxInput value={t.asunto} disabled={heredando} onChange={(e) => setT('asunto', e.target.value)}/></AxField>
              <AxField label="Saludo" helper="{nombre_pila} se reemplaza con el nombre de la persona.">
                <AxInput value={t.saludo} disabled={heredando} onChange={(e) => setT('saludo', e.target.value)}/>
              </AxField>
              <AxField label="Deber" helper="Los marcadores entre llaves son datos obligatorios; Piixi no deja guardar sin ellos.">
                <AxTextarea rows={5} value={t.deber} disabled={heredando} onChange={(e) => setT('deber', e.target.value)}/>
              </AxField>
              <AxField label="Cierre" helper="Lo que le pides que haga.">
                <AxTextarea rows={2} value={t.cierre} disabled={heredando} onChange={(e) => setT('cierre', e.target.value)}/>
              </AxField>
            </div>
          </MachoteBloque>

          <MachoteVerificacion deber={t.deber} obligatorios={obligatorios}/>

          <div style={{ fontSize: 11, color: 'var(--ax-fg-faint)', lineHeight: 1.55, borderTop: '1px solid var(--ax-border)', paddingTop: 12 }}>
            El diseño y la firma no se editan aquí: son del despacho.{' '}
            <button onClick={() => navigate('/ajustes/machotes/diseno')} style={{ appearance: 'none', background: 'transparent', border: 0, padding: 0, color: 'var(--ax-accent)', fontSize: 11, cursor: 'default' }}>Cambiar diseño →</button>
            {!porMomento ? (
              <> Las piezas de cada producto tampoco: se insertan solas.{' '}
                <button onClick={() => navigate('/ajustes/machotes/piezas')} style={{ appearance: 'none', background: 'transparent', border: 0, padding: 0, color: 'var(--ax-accent)', fontSize: 11, cursor: 'default' }}>Ver piezas →</button>
              </>
            ) : null}
          </div>
        </div>

        <MachotePrevia
          diseno={diseno} firma={state.user.name} despacho={state.user.orgName} correo={state.user.email}
          cabecera={flujo.cabecera}
          etiqueta={porMomento ? 'Como lo va a ver tu asegurado · con un recibo real' : 'Como lo va a ver tu asegurado · con una póliza de ' + MACHOTE_RAMO_NOMBRE[polizaEjemplo.ramo]}
          asunto={machoteRellenar(t.asunto, datos)}
          saludo={machoteRellenar(t.saludo, datos)}
          cuerpo={machoteRellenar(t.deber, datos)}
          parrafos={piezas.filter((p) => p.tipo === 'parrafo')}
          cierre={machoteRellenar(t.cierre, datos)}
          adjuntos={piezas.filter((p) => p.tipo === 'adjunto').map((a) => a.nombre)}
          para={datos.para}
          tabla={machoteTablaDe(flujoId, datos)}
        />
      </div>
    </div>
  );
}

// ═══════════════════════════════════════════════════════════════════════════
// PIEZAS COMPARTIDAS
// ═══════════════════════════════════════════════════════════════════════════
function MachoteBloque({ titulo, nota, children }) {
  return (
    <div>
      <div style={{ marginBottom: 10 }}>
        <AxEyebrow>{titulo}</AxEyebrow>
        {nota ? <div style={{ marginTop: 4, fontSize: 11, color: 'var(--ax-fg-faint)', lineHeight: 1.5 }}>{nota}</div> : null}
      </div>
      {children}
    </div>
  );
}

// Miniatura: un papelito que insinúa cómo se ve el correo.
function MachoteMiniatura({ diseno: d, activo, cargado, onClick }) {
  return (
    <button onClick={onClick} style={{
      appearance: 'none', cursor: 'default', padding: 0, textAlign: 'left',
      border: '1px solid ' + (activo ? 'var(--ax-accent)' : 'var(--ax-border-strong)'),
      borderRadius: 8, overflow: 'hidden', background: 'var(--ax-bg-2)',
      boxShadow: activo ? '0 0 0 2px var(--ax-accent-soft)' : 'none',
    }}>
      <div style={{ height: 68, background: d.papel, padding: 8, position: 'relative' }}>
        {d.vacio && !cargado ? (
          <div style={{ position: 'absolute', inset: 0, display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#9A9A9A', gap: 6, fontSize: 10 }}>
            <AxIcon name="upload" size={13}/> HTML
          </div>
        ) : (
          <>
            {d.membrete ? <div style={{ height: 6, background: d.acento, borderRadius: 1, marginBottom: 6, width: '46%' }}/> : null}
            <div style={{ height: 3, background: d.tinta, opacity: 0.68, borderRadius: 1, marginBottom: 4, width: '72%' }}/>
            <div style={{ height: 3, background: d.tinta, opacity: 0.22, borderRadius: 1, marginBottom: 3 }}/>
            <div style={{ height: 3, background: d.tinta, opacity: 0.22, borderRadius: 1, marginBottom: 3, width: '88%' }}/>
            <div style={{ height: 3, background: d.tinta, opacity: 0.22, borderRadius: 1, width: '60%' }}/>
            <div style={{ height: 8, background: d.acento, opacity: 0.85, borderRadius: 2, width: '34%', marginTop: 7 }}/>
          </>
        )}
      </div>
      <div style={{ padding: '7px 9px 9px', borderTop: '1px solid var(--ax-border)' }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
          <span style={{ fontSize: 12, color: activo ? 'var(--ax-fg-strong)' : 'var(--ax-fg)' }}>{d.nombre}</span>
          {activo ? <AxPill tone="accent">en uso</AxPill> : null}
          {d.origen === 'agente' ? <AxPill tone="ghost">tuyo</AxPill> : null}
        </div>
        <div style={{ marginTop: 2, fontSize: 10, color: 'var(--ax-fg-faint)', lineHeight: 1.4 }}>{d.nota}</div>
      </div>
    </button>
  );
}

// La capa `deber`, dicha como lo que sirve: una verificación dato por dato.
function MachoteVerificacion({ deber, obligatorios }) {
  const faltantes = obligatorios.filter((o) => !String(deber || '').includes('{' + o.llave + '}'));
  const ok = faltantes.length === 0;
  return (
    <div style={{
      border: '1px solid ' + (ok ? 'var(--ax-accent-line)' : 'var(--ax-warm-line)'),
      background: ok ? 'var(--ax-accent-soft)' : 'var(--ax-warm-soft)',
      borderRadius: 8, padding: '13px 14px',
    }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 8 }}>
        <AxIcon name={ok ? 'shield' : 'alert'} size={14} style={{ color: ok ? 'var(--ax-accent)' : 'var(--ax-warm)' }}/>
        <span style={{ fontSize: 12, color: 'var(--ax-fg-strong)', fontWeight: 500 }}>
          {ok ? 'Dice todo lo obligatorio' : faltantes.length + ' dato(s) obligatorios faltan'}
        </span>
      </div>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 5 }}>
        {obligatorios.map((o) => {
          const presente = String(deber || '').includes('{' + o.llave + '}');
          return (
            <div key={o.llave} title={o.porque} style={{ display: 'flex', alignItems: 'center', gap: 7, fontSize: 11 }}>
              <AxIcon name={presente ? 'check' : 'x'} size={11} style={{ color: presente ? 'var(--ax-accent)' : 'var(--ax-warm)', flexShrink: 0 }}/>
              <span style={{ color: presente ? 'var(--ax-fg-muted)' : 'var(--ax-fg-strong)' }}>{o.etiqueta}</span>
            </div>
          );
        })}
      </div>
      <div style={{ marginTop: 9, fontSize: 10.5, color: 'var(--ax-fg-muted)', lineHeight: 1.55 }}>
        Piixi no te deja guardar un aviso al que le falte un dato obligatorio, venga de donde venga el diseño.
      </div>
    </div>
  );
}

// ── La vista previa: un correo de verdad, sobre papel ────────────────────────
function MachotePrevia({ diseno: d, firma, despacho, correo, cabecera, etiqueta, asunto, saludo, cuerpo, parrafos, cierre, adjuntos, para, tabla, bloqueado, onSubir, compacto }) {
  return (
    <div style={{ background: 'var(--ax-bg)', padding: compacto ? '0 0 20px' : '22px 28px 44px', overflow: 'auto', display: 'flex', flexDirection: 'column', gap: 14 }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
        <AxEyebrow>{etiqueta || 'Como lo va a ver tu asegurado'}</AxEyebrow>
      </div>

      {bloqueado ? (
        <div style={{ border: '1px dashed var(--ax-border-strong)', borderRadius: 12, padding: '56px 24px', display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 12, textAlign: 'center', maxWidth: 620 }}>
          <AxIcon name="upload" size={26} style={{ color: 'var(--ax-fg-faint)' }}/>
          <div style={{ fontSize: 14, color: 'var(--ax-fg-strong)' }}>Sube tu HTML</div>
          <div style={{ fontSize: 12, color: 'var(--ax-fg-muted)', maxWidth: 380, lineHeight: 1.6 }}>
            Ármalo donde quieras —con tu modelo de inteligencia artificial, con tu diseñador, o copiando el de tu agencia—. Piixi lo verifica y lo usa tal cual.
          </div>
          <AxButton variant="secondary" icon="upload" onClick={onSubir}>Escoger archivo</AxButton>
        </div>
      ) : (
        <div style={{ maxWidth: 620, borderRadius: 12, overflow: 'hidden', boxShadow: '0 18px 50px rgba(0,0,0,0.5)' }}>
          <div style={{ background: '#F2F2F2', borderBottom: '1px solid #DEDEDE', padding: '11px 18px' }}>
            <div style={{ fontSize: 13, color: '#111', fontWeight: 600 }}>{asunto}</div>
            <div style={{ marginTop: 3, fontSize: 11, color: '#666' }}>
              de <span style={{ color: '#333' }}>{firma} &lt;{correo}&gt;</span> · para {para}
            </div>
            {adjuntos && adjuntos.length ? (
              <div style={{ marginTop: 6, display: 'flex', gap: 6, flexWrap: 'wrap' }}>
                {adjuntos.map((a) => (
                  <span key={a} style={{ display: 'inline-flex', alignItems: 'center', gap: 5, fontSize: 10.5, color: '#333', background: '#FFF', border: '1px solid #D4D4D4', borderRadius: 4, padding: '2px 7px' }}>
                    <AxIcon name="pdf" size={11}/> {a}
                  </span>
                ))}
              </div>
            ) : null}
          </div>

          <div style={{ background: d.papel, color: d.tinta, padding: '0 0 26px' }}>
            {d.membrete ? (
              <div style={{ background: d.acento, color: '#FFF', padding: '13px 26px', display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
                <span style={{ fontSize: 14, fontWeight: 600, letterSpacing: '-0.01em' }}>{despacho}</span>
                <span style={{ fontSize: 10, opacity: 0.85, letterSpacing: '0.1em', textTransform: 'uppercase' }}>{cabecera}</span>
              </div>
            ) : <div style={{ height: 26 }}/>}

            <div style={{ padding: '22px 26px 0' }}>
              <div style={{ fontSize: 14, lineHeight: 1.7 }}>{saludo}</div>
              <div style={{ marginTop: 14, fontSize: 14, lineHeight: 1.75 }}>{cuerpo}</div>

              <table style={{ width: '100%', marginTop: 18, borderCollapse: 'collapse', border: '1px solid ' + d.tinta + '1A', borderRadius: 6, fontSize: 13 }}>
                <tbody>
                  {(tabla || []).map(([k, val], i) => (
                    <tr key={k} style={{ borderTop: i ? '1px solid ' + d.tinta + '12' : 'none' }}>
                      <td style={{ padding: '8px 12px', color: d.tinta, opacity: 0.55, width: 110, verticalAlign: 'top' }}>{k}</td>
                      <td style={{ padding: '8px 12px', fontWeight: 600 }}>{val}</td>
                    </tr>
                  ))}
                </tbody>
              </table>

              {(parrafos || []).map((p) => (
                <div key={p.id} style={{ marginTop: 14, fontSize: 14, lineHeight: 1.75, position: 'relative' }}>
                  {p.texto}
                  <span title={'Pieza del ' + MACHOTE_NIVEL_ETIQUETA[p.nivel].toLowerCase()} style={{
                    display: 'inline-block', marginLeft: 8, fontSize: 9, letterSpacing: '0.08em', textTransform: 'uppercase',
                    color: d.acento, border: '1px solid ' + d.acento + '55', borderRadius: 3, padding: '1px 5px', verticalAlign: 'middle',
                  }}>pieza · {MACHOTE_NIVEL_ETIQUETA[p.nivel]}</span>
                </div>
              ))}

              <div style={{ marginTop: 18, fontSize: 14, lineHeight: 1.75 }}>{cierre}</div>

              <div style={{ marginTop: 22, paddingTop: 16, borderTop: '1px solid ' + d.tinta + '18', fontSize: 13, lineHeight: 1.6 }}>
                <div style={{ fontWeight: 600 }}>{firma}</div>
                <div style={{ opacity: 0.6, fontSize: 12 }}>{despacho}</div>
                <div style={{ opacity: 0.6, fontSize: 12 }}>{correo}</div>
              </div>
            </div>
          </div>
        </div>
      )}

      <div style={{ maxWidth: 620, fontSize: 11.5, color: 'var(--ax-fg-muted)', lineHeight: 1.65, borderLeft: '2px solid var(--ax-border-strong)', paddingLeft: 12 }}>
        Piixi <span style={{ color: 'var(--ax-fg-strong)' }}>redacta</span> y te lo deja listo.
        Tú decides cuándo se entrega — no manda nada a tu nombre sin que lo apruebes.
      </div>
    </div>
  );
}

// ── Subir el HTML del agente: el momento donde Piixi hace su trabajo ─────────
function MachoteSubirModal({ open, onClose, onListo }) {
  const [paso, setPaso] = useState('soltar');
  useEffect(() => { if (open) setPaso('soltar'); }, [open]);
  const verificar = () => { setPaso('verificando'); setTimeout(() => setPaso('listo'), 900); };
  const marcadores = ['{saludo}', '{deber}', '{cierre}', '{firma}'];

  return (
    <AxModal open={open} onClose={onClose} width={540} title="Subir tu diseño"
      footer={
        paso === 'listo'
          ? <><AxButton variant="tertiary" onClick={onClose}>Cancelar</AxButton>
              <AxButton variant="primary" icon="check" onClick={onListo}>Usar este diseño</AxButton></>
          : <AxButton variant="tertiary" onClick={onClose}>Cancelar</AxButton>
      }>
      {paso === 'soltar' ? (
        <div>
          <div style={{ fontSize: 13, color: 'var(--ax-fg-muted)', lineHeight: 1.7, marginBottom: 14 }}>
            Piixi no es un editor de diseño y no va a pretender serlo. Arma tu machote donde quieras y súbelo aquí como HTML.
            Un solo diseño sirve para todos los flujos: el texto lo pone Piixi.
          </div>
          <button onClick={verificar} style={{
            appearance: 'none', cursor: 'default', width: '100%', border: '1px dashed var(--ax-border-strong)', borderRadius: 10,
            background: 'var(--ax-bg-3)', padding: '30px 20px', display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 9, color: 'var(--ax-fg-muted)',
          }}>
            <AxIcon name="upload" size={22}/>
            <span style={{ fontSize: 13, color: 'var(--ax-fg-strong)' }}>Arrastra tu archivo .html</span>
            <span style={{ fontSize: 11 }}>o haz clic para escogerlo</span>
          </button>
          <div style={{ marginTop: 14, fontSize: 11.5, color: 'var(--ax-fg-muted)', lineHeight: 1.65, background: 'var(--ax-bg-3)', border: '1px solid var(--ax-border)', borderRadius: 7, padding: '11px 13px' }}>
            <span style={{ color: 'var(--ax-fg-strong)' }}>Lo único que tiene que traer: </span>
            cuatro marcadores donde quieras que aparezca cada parte —
            <span className="ax-mono" style={{ fontSize: 10.5 }}> {marcadores.join(' ')}</span>.
            Piixi mete en ellos el texto del flujo, las piezas del producto y tu firma.
          </div>
        </div>
      ) : paso === 'verificando' ? (
        <div style={{ padding: '30px 0', display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 12 }}>
          <span className="ax-spinner" style={{ width: 20, height: 20, color: 'var(--ax-accent)' }}/>
          <div style={{ fontSize: 13, color: 'var(--ax-fg-muted)' }}>Verificando que nombre las cuatro partes…</div>
        </div>
      ) : (
        <div>
          <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 14, background: 'var(--ax-accent-soft)', border: '1px solid var(--ax-accent-line)', borderRadius: 7, padding: '11px 13px' }}>
            <AxIcon name="check" size={16} style={{ color: 'var(--ax-accent)' }}/>
            <div style={{ fontSize: 13, color: 'var(--ax-fg-strong)' }}>diseno-despacho.html · nombra las 4 partes</div>
          </div>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
            {marcadores.map((m) => (
              <div key={m} style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 12 }}>
                <AxIcon name="check" size={12} style={{ color: 'var(--ax-accent)', flexShrink: 0 }}/>
                <span className="ax-mono" style={{ color: 'var(--ax-fg-strong)', fontSize: 11 }}>{m}</span>
              </div>
            ))}
          </div>
          <div style={{ marginTop: 14, fontSize: 11.5, color: 'var(--ax-fg-muted)', lineHeight: 1.6 }}>
            Piixi tampoco deja pasar scripts ni imágenes remotas dentro de tu HTML: un aviso que carga algo de fuera delata a tu asegurado ante quien sirva ese archivo.
          </div>
        </div>
      )}
    </AxModal>
  );
}

Object.assign(window, { MachotesScreen, MACHOTE_FLUJOS, MACHOTE_PIEZAS, machoteResolver });
