const { useState, useEffect, useRef, useCallback } = React;

/* ═══════════════════════════════════════════════════════════════
   VELIANTEX S.A.S. — Sistema de Cotización v5.1
   Reconstrucción completa desde mapa general
   ═══════════════════════════════════════════════════════════════ */

// ─── LOGO ───
const LOGO = "data:image/png;base64,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";

// ─── CONSTANTS ───
const UNIDADES_EST = ["cm\u00b2", "metro", "unidad", "puntadas(miles)", "pliego"];
const CATS_INSUMO = ["Botones","Cierres","Cordones","Ojaletes","Entretelas","Resortes","Marquillas","Etiquetas","Plastiflecha","Bolsas","Hilos","Cintas reflectivas","Otros"];
const TIPOS_PROV = ["Textil","Corte","Confección","Estampado","Sublimación","Bordado","Empaque","Transporte","Moldería","Patronaje","Otro"];
const TIPOS_NEGOCIO = ["Promocionales", "Clubes Sociales y Deportivos / Institucional", "B2B2C (Página Web)", "B2C", "Licitaciones"];
const ESTADOS_COT = ["Borrador","En revisión","Enviada","Aprobada","No aprobada","Vencida","Cancelada"];
const RAZONES_NO = ["Precio alto","No responde","Eligió otro proveedor","Tiempo entrega","Calidad percibida","Cambió proyecto","Sin presupuesto","Otro"];
const DIAS_ENT = Array.from({length:90},(_, i) => i+1);
const GARANTIA = [0,1,2,3,6,12];

// ─── UTILITIES ───
const fmt = n => new Intl.NumberFormat("es-CO",{style:"currency",currency:"COP",minimumFractionDigits:0}).format(n||0);
const fmtN = n => new Intl.NumberFormat("es-CO").format(n||0);
const uid = () => Date.now().toString(36)+Math.random().toString(36).slice(2,7);
const hoy = () => new Date().toISOString().split("T")[0];
const nid = arr => arr.length ? Math.max(...arr.map(a=>a.id))+1 : 1;

// ─── MOTOR DE COSTEO ───
function calcCostoEstampado(e) {
  const u = e.unidadCobro || "cm\u00b2";
  if (u === "cm\u00b2") return ((e.anchoCm||0)*(e.altoCm||0))*(e.costoUnitario||0);
  if (u === "metro") return (e.metros||0)*(e.costoUnitario||0);
  if (u === "unidad") return (e.cantidadUds||1)*(e.costoUnitario||0);
  if (u === "puntadas(miles)") return (e.puntadasMiles||0)*(e.costoUnitario||0);
  if (u === "pliego") return (e.pliegos||1)*(e.costoUnitario||0);
  return 0;
}
function sumEstampados(es) { return (es||[]).reduce((s,e)=>s+calcCostoEstampado(e),0); }

function motorCosteo(it, insumos) {
  // B1: Costos directos
  const cTextil = (it.precioTextil||0)*(it.consumoTextil||0)*(1-(it.dctoTextil||0)/100);
  const cInsumos = (it.insumosItems||it.insumosIds||[]).reduce((s,x)=>{
    const id = typeof x === "object" ? x.id : x;
    const qty = typeof x === "object" ? (x.qty||1) : 1;
    const i=insumos.find(z=>z.id===id);
    return s+(i?i.costo*qty:0);
  },0);
  const cEstampados = sumEstampados(it.estampados);
  const cCorte = it.costoCorte||0;
  const cConf = it.costoConfeccion||0;
  const cEmpaque = it.costoEmpaque||0;
  const cTrazo = it.costoTrazo||0;
  const cOtros = it.costoOtros||0;
  const subDirecto = cTextil+cInsumos+cEstampados+cTrazo+cCorte+cConf+cEmpaque+cOtros;
  // B2: Margen bruto o precio manual
  const mb = it.margenBruto||35;
  let precioBase;
  if (it.precioManual && it.precioManual > 0) {
    precioBase = it.precioManual;
  } else {
    precioBase = mb>=100 ? subDirecto : Math.ceil(subDirecto/(1-mb/100));
  }
  // B3: Cargos adicionales
  let totalCargos = 0;
  const cargosC = (it.cargos||[]).map(c => {
    const v = c.modo==="porcentaje" ? Math.round(precioBase*(c.valor||0)/100) : (c.valor||0);
    totalCargos += v;
    return {...c, _calc: v};
  });
  // B4: Precio final
  const netoSinIVA = precioBase + totalCargos;
  const ivaR = it.ivaRate !== undefined ? it.ivaRate : 19;
  const iva = Math.round(netoSinIVA*ivaR/100);
  const precioFinal = netoSinIVA + iva;
  const utilidad = netoSinIVA - subDirecto;
  const margenReal = netoSinIVA>0 ? (utilidad/netoSinIVA)*100 : 0;

  return {
    ...it,
    _cTextil:cTextil, _cInsumos:cInsumos, _cEstampados:cEstampados,
    _subDirecto:subDirecto, _mb:mb, _precioBase:precioBase,
    _cargosC:cargosC, _totalCargos:totalCargos,
    _netoSinIVA:netoSinIVA, _iva:iva, _precioFinal:precioFinal,
    _utilidad:utilidad, _margenReal:margenReal,
  };
}

// ═══════════════════════════════════════════════════════════════
// B. MAESTROS — Datos iniciales
// ═══════════════════════════════════════════════════════════════
const INIT = {
  // B.3 Textiles
  textiles: [
    {id:1,codigo:"TX-001",nombre:"Dry-Fit Premium",proveedor:"Textiles del Sur",composicion:"100% Poliéster",gramaje:150,ancho:150,unidad:"Metro",precio:18500,iva:19,dctoPP:5,dctoVol:8,volMin:100,diasAbast:5,colores:"Blanco,Negro,Azul,Rojo",ref:"DF-150",obs:"",estado:"Activo"},
    {id:2,codigo:"TX-002",nombre:"Algodón 30/1",proveedor:"Textiles del Sur",composicion:"100% Algodón",gramaje:180,ancho:160,unidad:"Metro",precio:14200,iva:19,dctoPP:3,dctoVol:5,volMin:50,diasAbast:3,colores:"Blanco,Negro,Gris",ref:"ALG-301",obs:"",estado:"Activo"},
    {id:3,codigo:"TX-003",nombre:"Licra Deportiva",proveedor:"Textiles del Sur",composicion:"80% Nylon 20% Elastano",gramaje:220,ancho:145,unidad:"Metro",precio:22800,iva:19,dctoPP:0,dctoVol:10,volMin:80,diasAbast:7,colores:"Negro,Azul marino",ref:"LCD-220",obs:"",estado:"Activo"},
  ],
  // B.4 Insumos
  insumos: [
    {id:1,codigo:"IN-001",nombre:"Marquilla tejida",cat:"Marquillas",unidad:"Unidad",costo:350,iva:19,proveedor:"Marquillas Express",consumo:1,variante:"Estándar",tipo:"fijo",obs:""},
    {id:2,codigo:"IN-002",nombre:"Etiqueta talla",cat:"Etiquetas",unidad:"Unidad",costo:120,iva:19,proveedor:"Marquillas Express",consumo:1,variante:"",tipo:"fijo",obs:""},
    {id:3,codigo:"IN-003",nombre:"Bolsa polipropileno",cat:"Bolsas",unidad:"Unidad",costo:250,iva:19,proveedor:"Empaques JM",consumo:1,variante:"30x40cm",tipo:"fijo",obs:""},
    {id:4,codigo:"IN-004",nombre:"Hilo poliéster",cat:"Hilos",unidad:"Metro",costo:8,iva:19,proveedor:"Hilaturas Col",consumo:120,variante:"Colores varios",tipo:"cantidad",obs:""},
    {id:5,codigo:"IN-005",nombre:"Resorte cintura 3cm",cat:"Resortes",unidad:"Metro",costo:1200,iva:19,proveedor:"Elásticos SA",consumo:0.8,variante:"Negro/Blanco",tipo:"talla",obs:""},
  ],
  // B.5 Servicios de maquila
  // Trazo: precio por metro (según consumo del textil)
  // Corte: precio por unidad con escalas por cantidad
  // Confección: precio único por proveedor + tipo de prenda
  maquila: [
    { id: 1, tipo: "Trazo", proveedor: "Cortes Precisión", precioMetro: 1500, obs: "Precio por metro de trazo impreso", estado: "Activo" },
    { id: 2, tipo: "Trazo", proveedor: "Textiles del Sur", precioMetro: 1200, obs: "", estado: "Activo" },
    { id: 3, tipo: "Corte", proveedor: "Cortes Precisión", escalas: [{ desde: 1, hasta: 50, precio: 1800 }, { desde: 51, hasta: 200, precio: 1500 }, { desde: 201, hasta: 9999, precio: 1200 }], obs: "", estado: "Activo" },
    { id: 4, tipo: "Confección", proveedor: "Confecciones Élite", precios: [{ prenda: "Camiseta", precio: 6500 }, { prenda: "Polo", precio: 8500 }, { prenda: "Pantaloneta", precio: 5500 }, { prenda: "Sudadera", precio: 12000 }, { prenda: "Chaqueta", precio: 14000 }, { prenda: "Legging", precio: 7000 }], obs: "", estado: "Activo" },
  ],
  // B.10 Técnicas de estampación y acabados
  tecnicas: [
    {id:1,nombre:"DTF",unidadCobro:"cm\u00b2",costoBase:35,proveedor:"Estampar YA",desc:"Transfer digital",estado:"Activo"},
    {id:2,nombre:"Vinilo",unidadCobro:"cm\u00b2",costoBase:55,proveedor:"Estampar YA",desc:"Vinilo textil",estado:"Activo"},
    {id:3,nombre:"Bordado",unidadCobro:"puntadas(miles)",costoBase:4500,proveedor:"",desc:"Bordado industrial",estado:"Activo"},
    {id:4,nombre:"Sublimación",unidadCobro:"metro",costoBase:18000,proveedor:"",desc:"Full print",estado:"Activo"},
    {id:5,nombre:"Screen",unidadCobro:"unidad",costoBase:2500,proveedor:"",desc:"Serigrafía",estado:"Activo"},
  ],
  // B.6 Tipos de prenda + B.7 Consumos por prenda
  tiposPrenda: [
    {id:1,codigo:"TP-CAM",nombre:"Camiseta",cat:"Superior",desc:"Manga corta o larga",consumoDefault:0.65,insumosDefault:[1,2,3,4],procesosDefault:{corte:1500,confeccion:6500,empaque:800},estado:"Activo"},
    {id:2,codigo:"TP-POL",nombre:"Polo",cat:"Superior",desc:"Con cuello y botones",consumoDefault:0.80,insumosDefault:[1,2,3,4],procesosDefault:{corte:1800,confeccion:8500,empaque:800},estado:"Activo"},
    {id:3,codigo:"TP-PAN",nombre:"Pantaloneta",cat:"Inferior",desc:"Deportiva",consumoDefault:0.55,insumosDefault:[1,2,3,4,5],procesosDefault:{corte:1200,confeccion:5500,empaque:800},estado:"Activo"},
    {id:4,codigo:"TP-SUD",nombre:"Sudadera",cat:"Superior",desc:"Con o sin capota",consumoDefault:1.20,insumosDefault:[1,2,3,4],procesosDefault:{corte:2200,confeccion:12000,empaque:1000},estado:"Activo"},
    {id:5,codigo:"TP-CHA",nombre:"Chaqueta",cat:"Superior",desc:"Deportiva",consumoDefault:1.40,insumosDefault:[1,2,3,4],procesosDefault:{corte:2500,confeccion:14000,empaque:1000},estado:"Activo"},
    {id:6,codigo:"TP-LEG",nombre:"Legging",cat:"Inferior",desc:"Deportivo",consumoDefault:0.90,insumosDefault:[1,2,3,5],procesosDefault:{corte:1500,confeccion:7000,empaque:800},estado:"Activo"},
  ],
  // B.8 Moldería
  moldes: [
    {id:1,codigo:"MO-CAM-01",tipoPrenda:"Camiseta",version:"v2",patronista:"Diana R.",escala:"XS-XL",obs:"Manga ranglan",estado:"Activo"},
  ],
  // B.9 Tallas
  tallas: ["XS","S","M","L","XL","XXL"],
  // B.1 Clientes
  clientes: [
    {id:1,tipo:"Empresa",razon:"Deportivos El Triunfo S.A.S.",nit:"900123456-1",comercial:"El Triunfo",contacto:"María López",correo:"maria@eltriunfo.co",tel:"3001234567",dir:"Cra 45 #32-10",ciudad:"Cali",dirFactura:"Cra 45 #32-10",dirEntrega:"Cra 45 #32-10",condiciones:"50% anticipo",obs:"",estado:"Activo"},
  ],
  // B.2 Proveedores
  proveedores: [
    {id:1,nombre:"Textiles del Sur",nit:"800111222-3",contacto:"Carlos Ruiz",direccion:"",ciudad:"Cali",email:"",celular:"",tipo:"Textil",estado:"Activo"},
    {id:2,nombre:"Confecciones Élite",nit:"900333444-5",contacto:"Ana Gómez",direccion:"",ciudad:"Cali",email:"",celular:"",tipo:"Confección",estado:"Activo"},
    {id:3,nombre:"Estampar YA",nit:"800555666-7",contacto:"Pedro Díaz",direccion:"",ciudad:"Envigado",email:"",celular:"",tipo:"Estampado",estado:"Activo"},
  ],
  // A.4 Plantillas de cargos comerciales
  plantillasCargos: [
    {id:1,nombre:"Cliente directo",cargos:[{nombre:"Administrativo",modo:"porcentaje",valor:2}]},
    {id:2,nombre:"Canal comercial",cargos:[{nombre:"Comisión venta",modo:"porcentaje",valor:8},{nombre:"Administrativo",modo:"porcentaje",valor:2},{nombre:"Financiero",modo:"porcentaje",valor:1.5}]},
  ],
  metodosPago: ["50% anticipo, 50% contra entrega", "100% anticipado", "30 días", "45 días", "60 días", "90 días", "Contado contra entrega"],
  // C+D+E
  catalogo: [],
  pdfRequests: [],
  cotizaciones: [],
  pedidos: [],
  // F. Usuarios
  usuarios: [
    { id: 1, nombre: "Juan Carlos Velasco", correo: "admin@veliantex.com", clave: "admin123", nivel: 3, verCostos: true, area: "Gerencia", estado: "activo", fechaCreacion: "2026-04-01" }
  ],
};

// ═══════════════════════════════════════════════════════════════
// UI PRIMITIVES
// ═══════════════════════════════════════════════════════════════
const Ic = ({name,size=17}) => {
  const d={chart:"M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z",users:"M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197",box:"M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4",doc:"M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z",cart:"M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 100 4 2 2 0 000-4z",plus:"M12 4v16m8-8H4",edit:"M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z",trash:"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16",check:"M5 13l4 4L19 7",x:"M6 18L18 6M6 6l12 12",gear:"M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.066 2.573c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.573 1.066c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.066-2.573c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z M15 12a3 3 0 11-6 0 3 3 0 016 0z",copy:"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z",truck:"M13 16V6a1 1 0 00-1-1H4a1 1 0 00-1 1v10a1 1 0 001 1h1m8-1a1 1 0 01-1 1H9m4-1V8a1 1 0 011-1h2.586a1 1 0 01.707.293l3.414 3.414a1 1 0 01.293.707V16a1 1 0 01-1 1h-1m-6-1a1 1 0 001 1h1M5 17a2 2 0 104 0m-4 0a2 2 0 114 0m6 0a2 2 0 104 0m-4 0a2 2 0 114 0",factory:"M19.428 15.428a2 2 0 00-1.022-.547l-2.387-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 1v5.172a2 2 0 00.586 1.414l5 5c1.26 1.26.367 3.414-1.415 3.414H4.828c-1.782 0-2.674-2.154-1.414-3.414l5-5A2 2 0 009 10.172V5L8 4z",download:"M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4",alert:"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4.5c-.77-.833-2.694-.833-3.464 0L3.34 16.5c-.77.833.192 2.5 1.732 2.5z",tag:"M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z",scissors:"M20 4L8.12 15.88M14.47 14.48L20 20M8.12 8.12L12 12 M6 3a3 3 0 100 6 3 3 0 000-6z M6 15a3 3 0 100 6 3 3 0 000-6z",send:"M22 2L11 13 M22 2l-7 20-4-9-9-4 20-7z"};
  return <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">{(d[name]||"").split(" M").map((p,i) => <path key={i} d={i===0?p:"M"+p}/>)}</svg>;
};

const S = {
  app:{display:"flex",height:"100vh",fontFamily:"'Libre Franklin','Segoe UI',sans-serif",background:"#f5f3ef",color:"#1a1a2e",fontSize:13},
  sidebar:{width:215,background:"linear-gradient(180deg,#0a2540,#0d3256)",color:"#c4d0db",display:"flex",flexDirection:"column",flexShrink:0,overflowY:"auto"},
  si:a=>({display:"flex",alignItems:"center",gap:9,padding:"9px 14px",cursor:"pointer",background:a?"rgba(232,213,183,0.12)":"transparent",color:a?"#e8d5b7":"#8faabe",borderLeft:a?"3px solid #e8d5b7":"3px solid transparent",fontSize:12.5,transition:"all .15s"}),
  main:{flex:1,display:"flex",flexDirection:"column",overflow:"hidden"},
  topbar:{background:"#fff",borderBottom:"1px solid #e5e1d8",padding:"10px 22px",display:"flex",alignItems:"center",justifyContent:"space-between",boxShadow:"0 1px 3px rgba(0,0,0,.04)"},
  content:{flex:1,overflow:"auto",padding:22},
  card:{background:"#fff",borderRadius:10,border:"1px solid #e5e1d8",padding:18,marginBottom:14,boxShadow:"0 1px 4px rgba(0,0,0,.04)"},
  g2:{display:"grid",gridTemplateColumns:"1fr 1fr",gap:10},
  g3:{display:"grid",gridTemplateColumns:"1fr 1fr 1fr",gap:10},
  g4:{display:"grid",gridTemplateColumns:"1fr 1fr 1fr 1fr",gap:10},
  g5:{display:"grid",gridTemplateColumns:"1fr 1fr 1fr 1fr 1fr",gap:8},
  lb:{display:"block",fontSize:10.5,fontWeight:600,color:"#6b7280",marginBottom:2,textTransform:"uppercase",letterSpacing:"0.5px"},
  inp:{width:"100%",padding:"6px 9px",border:"1px solid #d1cdc4",borderRadius:5,fontSize:12.5,background:"#faf9f7",outline:"none",boxSizing:"border-box"},
  sel:{width:"100%",padding:"6px 9px",border:"1px solid #d1cdc4",borderRadius:5,fontSize:12.5,background:"#faf9f7",outline:"none",boxSizing:"border-box"},
  btn:{background:"#0a2540",color:"#e8d5b7",border:"none",padding:"7px 16px",borderRadius:5,cursor:"pointer",fontSize:11.5,fontWeight:600,display:"inline-flex",alignItems:"center",gap:5},
  btn2:{background:"#f0ece4",color:"#0a2540",border:"1px solid #d1cdc4",padding:"6px 12px",borderRadius:5,cursor:"pointer",fontSize:11.5,display:"inline-flex",alignItems:"center",gap:4},
  btnD:{background:"#fef2f2",color:"#b91c1c",border:"1px solid #fecaca",padding:"4px 10px",borderRadius:5,cursor:"pointer",fontSize:11,display:"inline-flex",alignItems:"center",gap:3},
  btnG:{background:"#064e3b",color:"#d1fae5",border:"none",padding:"6px 13px",borderRadius:5,cursor:"pointer",fontSize:11.5,fontWeight:600,display:"inline-flex",alignItems:"center",gap:4},
  th:{textAlign:"left",padding:"7px 9px",background:"#f9f7f3",borderBottom:"2px solid #e5e1d8",fontSize:10.5,fontWeight:700,color:"#6b7280",textTransform:"uppercase",letterSpacing:"0.5px"},
  td:{padding:"7px 9px",borderBottom:"1px solid #f0ece4",fontSize:12},
  badge:c=>({display:"inline-block",padding:"2px 7px",borderRadius:99,fontSize:10,fontWeight:600,background:c==="green"?"#d1fae5":c==="red"?"#fee2e2":c==="blue"?"#dbeafe":c==="yellow"?"#fef3c7":c==="amber"?"#fef3c7":c==="gray"?"#f3f4f6":"#f3f4f6",color:c==="green"?"#065f46":c==="red"?"#991b1b":c==="blue"?"#1e40af":c==="yellow"?"#92400e":c==="amber"?"#92400e":c==="gray"?"#6b7280":"#374151"}),
  tabs:{display:"flex",gap:0,borderBottom:"2px solid #e5e1d8",marginBottom:14},
  tab:a=>({padding:"7px 16px",cursor:"pointer",fontWeight:a?700:400,color:a?"#0a2540":"#9ca3af",borderBottom:a?"2px solid #0a2540":"2px solid transparent",marginBottom:-2,fontSize:12.5}),
  stat:{background:"#fff",borderRadius:10,border:"1px solid #e5e1d8",padding:16,textAlign:"center"},
  modal:{position:"fixed",inset:0,background:"rgba(10,37,64,0.5)",display:"flex",alignItems:"center",justifyContent:"center",zIndex:1000,backdropFilter:"blur(2px)"},
  mc:{background:"#fff",borderRadius:12,padding:22,width:"92%",maxWidth:850,maxHeight:"88vh",overflow:"auto",boxShadow:"0 20px 60px rgba(0,0,0,0.2)"},
  secT:{margin:"14px 0 7px",color:"#0a2540",fontSize:11,textTransform:"uppercase",letterSpacing:1,fontWeight:700},
  alertB:c=>({padding:"7px 10px",borderRadius:5,fontSize:11,fontWeight:600,display:"flex",alignItems:"center",gap:5,marginTop:5,background:c==="red"?"#fef2f2":c==="yellow"?"#fffbeb":"#f0fdf4",color:c==="red"?"#991b1b":c==="yellow"?"#92400e":"#065f46",border:`1px solid ${c==="red"?"#fecaca":c==="yellow"?"#fde68a":"#bbf7d0"}`}),
};
const F = ({l,children,span}) => <div style={span?{gridColumn:`span ${span}`}:{}}><label style={S.lb}>{l}</label>{children}</div>;

// ─── STORAGE (PostgreSQL via API) ───
async function ld(){
  try {
    const r = await fetch('/api/data', { credentials: 'include' });
    if (r.status === 401) return null;
    if (!r.ok) throw new Error('Error cargando datos');
    return await r.json();
  } catch(e) { console.error('ld():', e); return null; }
}
async function sv(d){
  try {
    await fetch('/api/data', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      credentials: 'include',
      body: JSON.stringify(d),
    });
  } catch(e) { console.error('sv():', e); }
}

// ═══════════════════════════════════════════════════════════════
// C.6 PDF GENERATION
// ═══════════════════════════════════════════════════════════════
function buildPDF(cot, data) {
  const items = cot.items || [];
  const cl = data.clientes.find(c => c.id === cot.clienteId) || {};
  const diasTotal = cot.diasEntrega || 30;

  // Calcular totales
  const totales = items.reduce((a, it) => {
    const rc = motorCosteo(it, data.insumos);
    a.sub += rc._netoSinIVA * it.cantidad;
    return a;
  }, { sub: 0, iva: 0, total: 0 });
  totales.iva = Math.round(totales.sub * 0.19);
  totales.total = totales.sub + totales.iva;

  // Filas de ítems
  const itemRows = items.map((it, i) => {
    const rc = motorCosteo(it, data.insumos);
    const estTxt = (it.estampados || []).map(e => e.tecnica + (e.ubicacion ? " (" + e.ubicacion + ")" : "")).join(", ");
    const imgRow = it.mockupImg ? `<tr><td colspan="6" style="padding:6px 8px 10px"><img src="${it.mockupImg}" style="max-width:180px;max-height:140px;border-radius:6px;border:1px solid #e5e1d8" /></td></tr>` : "";
    return `
      <tr>
        <td class="tc">${i + 1}</td>
        <td><strong>${it.nombreComercial || it.tipoPrenda || "—"}</strong></td>
        <td class="desc">${it.tipoPrenda || ""}${it.textilNombre ? " en " + it.textilNombre : ""}${estTxt ? "<br/>Estampado: " + estTxt : ""}${it.empaque ? "<br/>Empaque: " + it.empaque : ""}</td>
        <td class="tc">${fmtN(it.cantidad)}</td>
        <td class="tr">${fmt(rc._netoSinIVA)}</td>
        <td class="tr">${fmt(rc._netoSinIVA * it.cantidad)}</td>
      </tr>${imgRow}`;
  }).join("");

  // Timeline chevron: distribuir los días proporcionalmente (ajuste exacto)
  const etapas = [
    { nombre: "Compra MP", pct: 0.22 },
    { nombre: "Trazo y Corte", pct: 0.11 },
    { nombre: "Estampado", pct: 0.17 },
    { nombre: "Confección", pct: 0.28 },
    { nombre: "Acabados", pct: 0.11 },
    { nombre: "Despacho", pct: 0.11 },
  ];
  // Distribute days ensuring they sum exactly to diasTotal
  const rawDays = etapas.map(et => Math.max(1, Math.round(diasTotal * et.pct)));
  let sumRaw = rawDays.reduce((a, b) => a + b, 0);
  // Adjust last item to fix any rounding difference
  while (sumRaw > diasTotal && rawDays.length > 0) { const maxI = rawDays.reduce((mi, v, i) => v > rawDays[mi] ? i : mi, 0); rawDays[maxI]--; sumRaw--; }
  while (sumRaw < diasTotal) { const minI = rawDays.reduce((mi, v, i) => v < rawDays[mi] ? i : mi, 0); rawDays[minI]++; sumRaw++; }
  let dAcum = 0;
  const tlItems = etapas.map((et, i) => {
    const d = rawDays[i];
    const desde = dAcum + 1; dAcum += d;
    return { ...et, d, desde, hasta: dAcum };
  });
  const colors = ["#0a2540", "#1e3a5f", "#2a5f8f", "#3b82b0", "#4a9ec8", "#6bb8d6"];
  const chevronH = 40;
  const chevronW = 100 / tlItems.length;
  const arrowPx = 8;
  const chevronHTML = `<div style="display:flex;width:100%;margin:8px 0">` + tlItems.map((et, i) => {
    const bg = colors[i];
    const isLast = i === tlItems.length - 1;
    return `<div style="flex:1;position:relative;height:${chevronH}px;background:${bg};display:flex;flex-direction:column;align-items:center;justify-content:center;color:white;font-size:8px;${i === 0 ? "border-radius:4px 0 0 4px;" : ""}${isLast ? "border-radius:0 4px 4px 0;" : ""}margin-right:${isLast ? 0 : 2}px;">
      <div style="font-weight:bold;font-size:9px;line-height:1.1">${et.nombre}</div>
      <div style="font-size:7px;opacity:0.85;margin-top:1px">Día ${et.desde}-${et.hasta} (${et.d}d)</div>
    </div>`;
  }).join("") + `</div>`;

  return `<!DOCTYPE html>
<html><head><meta charset="utf-8">
<title>${cot.nombreCot || "COT-" + cot.numero}</title>
<style>
  @page { size: letter; margin: 16mm 14mm; }
  * { box-sizing: border-box; }
  body { font-family: 'Georgia', 'Times New Roman', serif; color: #1a1a1a; font-size: 11.5px; line-height: 1.5; margin: 0; padding: 28px; }
  .header { display: flex; justify-content: space-between; align-items: flex-start; border-bottom: 2.5px solid #0a2540; padding-bottom: 12px; margin-bottom: 18px; }
  .header-right { text-align: right; font-size: 10.5px; color: #555; }
  .cot-num { font-size: 20px; font-weight: bold; color: #0a2540; letter-spacing: 0.5px; }
  .empresa-info { font-size: 8.5px; color: #777; margin-top: 4px; line-height: 1.4; }
  .section { background: #0a2540; color: #e8d5b7; padding: 5px 14px; font-size: 9px; letter-spacing: 2.5px; text-transform: uppercase; margin: 16px 0 8px; font-weight: 600; }
  table { width: 100%; border-collapse: collapse; }
  th { background: #f5f3ef; padding: 6px 8px; text-align: left; font-size: 9px; font-weight: 700; border-bottom: 2px solid #0a2540; color: #555; text-transform: uppercase; letter-spacing: 0.5px; }
  td { padding: 7px 8px; border-bottom: 1px solid #e5e1d8; font-size: 11px; vertical-align: top; }
  .tc { text-align: center; }
  .tr { text-align: right; }
  .desc { font-size: 10px; color: #555; }
  .totals { margin-top: 10px; text-align: right; }
  .totals div { font-size: 11.5px; padding: 1px 0; }
  .totals .grand { font-size: 17px; font-weight: bold; color: #0a2540; padding-top: 4px; border-top: 2px solid #0a2540; margin-top: 4px; }
  .cond { font-size: 10px; line-height: 1.6; }
  .cond h4 { color: #0a2540; margin: 6px 0 1px; font-size: 10px; font-weight: 700; }
  .cond p { margin: 0 0 4px; }
  .firma-box { margin-top: 24px; border: 1px dashed #aaa; padding: 14px; border-radius: 4px; }
  .firma-box p { font-size: 9.5px; margin: 0 0 16px; color: #555; }
  .firma-line { border-top: 1px solid #333; width: 46%; padding-top: 4px; font-size: 8.5px; color: #555; }
  .footer { text-align: center; font-size: 7.5px; color: #999; margin-top: 18px; border-top: 1px solid #e5e1d8; padding-top: 6px; }
  @media print { body { padding: 0; } }
</style>
</head><body>

<!-- HEADER -->
<div class="header">
  <div>
    <img src="${LOGO}" style="height:46px;border-radius:4px;" />
    <div class="empresa-info">
      Veliantex S.A.S. — Vitality Sportswear<br/>
      NIT: 901.187.332-1<br/>
      Cra 24 C # 4 - 54 B/ Miraflores — Cali, Colombia<br/>
      WhatsApp: (+57) 316 4489436 / 300 4893285<br/>
      Rep. Legal: Juan Carlos Velasco Lian
    </div>
  </div>
  <div class="header-right">
    <div class="cot-num">${cot.nombreCot || "COT-" + cot.numero}</div>
    Fecha: ${cot.fecha || "—"}<br/>
    Vigencia: ${cot.vigencia || "15 días"}<br/>
    Comercial: ${cot.comercial || "—"}
  </div>
</div>

<!-- CLIENTE -->
<div class="section">Datos del Cliente</div>
<table>
  <tr>
    <td style="width:50%">
      <strong style="font-size:12.5px">${cl.razon || cot.clienteNombre || "—"}</strong><br/>
      NIT: ${cl.nit || "—"}<br/>
      Contacto: ${cl.contacto || "—"}
    </td>
    <td>
      Correo: ${cl.correo || "—"}<br/>
      Tel: ${cl.tel || "—"}<br/>
      Ciudad: ${cl.ciudad || "—"}
    </td>
  </tr>
</table>
${cot.proyecto ? `<p style="margin:6px 0;font-size:10.5px"><strong>Proyecto:</strong> ${cot.proyecto}</p>` : ""}

<!-- ÍTEMS -->
<div class="section">Detalle de Ítems</div>
<table>
  <thead>
    <tr>
      <th style="width:30px">#</th>
      <th>Referencia</th>
      <th>Descripción</th>
      <th style="text-align:center">Cant.</th>
      <th style="text-align:right">Vr. Unitario</th>
      <th style="text-align:right">Total</th>
    </tr>
  </thead>
  <tbody>
    ${itemRows}
  </tbody>
</table>

<!-- TOTALES -->
<div class="totals">
  <div>Subtotal: ${fmt(totales.sub)}</div>
  <div>IVA 19%: ${fmt(totales.iva)}</div>
  <div class="grand">TOTAL: ${fmt(totales.total)}</div>
</div>

<!-- LÍNEA DE TIEMPO -->
<div class="section">Proceso de Producción (${diasTotal} días hábiles)</div>
${chevronHTML}
<p style="font-size:8px;color:#777;margin:4px 0 0;font-style:italic;">* Tiempos estimados contados a partir de la aprobación del anticipo y recepción de información completa.</p>

<!-- CONDICIONES -->
<div class="section">Condiciones Comerciales</div>
<div class="cond">
  <h4>Forma de pago</h4>
  <p>${cot.metodoPago || "50% anticipo, 50% contra entrega."}</p>
  <h4>Tiempo de entrega</h4>
  <p>${diasTotal} días hábiles contados a partir de la aprobación del anticipo, aprobación de muestra y recepción de información completa.</p>
  <h4>Flete</h4>
  <p>${cot.flete || "No incluido"}</p>
  <h4>Garantía</h4>
  <p>${cot.garantiaMeses || 0} mes(es) sobre defectos de fabricación. No aplica sobre uso inadecuado o desgaste normal.</p>
  <h4>Desarrollo de muestra</h4>
  <p>El desarrollo de muestra se elabora únicamente si el cliente lo requiere. De requerirlo, se acordará su respectivo precio de acuerdo al detalle del requerimiento.</p>
  <h4>Ajustes</h4>
  <p>Una ronda de ajustes incluida. Cambios adicionales podrán generar recotización.</p>
  <h4>Entregas parciales</h4>
  <p>No se contemplan salvo acuerdo expreso entre las partes.</p>
  <h4>Fuerza mayor</h4>
  <p>En eventos de caso fortuito o fuerza mayor (como emergencias sanitarias, manifestaciones sociales) que conlleven a paras en la cadena de suministro o bloqueos de las transportadoras, los tiempos de entrega pueden verse retrasados.</p>
</div>

<!-- FIRMA -->
<div class="firma-box">
  <p>Acepto las condiciones descritas en esta cotización y autorizo el inicio del proceso de producción.</p>
  <div style="display:flex;justify-content:space-between;">
    <div class="firma-line">Firma del cliente — Nombre — C.C./NIT</div>
    <div class="firma-line">Fecha de aceptación</div>
  </div>
</div>

<!-- FOOTER -->
<div class="footer">
  VELIANTEX S.A.S. — Vitality Sportswear — Cali, Colombia — NIT: 901.187.332-1
</div>
<script>window.onload=function(){setTimeout(function(){window.print()},600)}</script>
</body></html>`;
}
function buildPedidoPDF(ped, data) {
  const cl = data.clientes.find(c => c.id === ped.clienteId) || {};
  const itemsHtml = (ped.items || []).map((it, i) => {
    const tallas = it.tallasDetalle ? Object.entries(it.tallasDetalle).filter(([,v]) => v > 0).map(([t,v]) => `${t}:${v}`).join(", ") : "Pendiente";
    const estampados = (it.estampados || []).map(e => `${e.tecnica} (${e.proveedor || ""}) - ${e.ubicacion || ""}`).join(", ");
    return `<tr><td style="padding:6px;border:1px solid #ddd">${i+1}</td><td style="padding:6px;border:1px solid #ddd"><strong>${it.nombreComercial || it.tipoPrenda || ""}</strong></td><td style="padding:6px;border:1px solid #ddd">${it.textilNombre || ""}</td><td style="padding:6px;border:1px solid #ddd;text-align:center">${it.cantidad}</td><td style="padding:6px;border:1px solid #ddd;font-size:10px">${tallas}</td><td style="padding:6px;border:1px solid #ddd;font-size:10px">${estampados || "—"}</td><td style="padding:6px;border:1px solid #ddd;font-size:10px">${it.empaque || it.empaqueD || ""}</td></tr>`;
  }).join("");
  const provsHtml = (ped.items || []).map((it, i) => `<tr><td style="padding:5px;border:1px solid #ddd">${it.nombreComercial || it.tipoPrenda}</td><td style="padding:5px;border:1px solid #ddd">${it.provCorte || "—"}</td><td style="padding:5px;border:1px solid #ddd">${it.provConf || "—"}</td><td style="padding:5px;border:1px solid #ddd">${it.provEst || "—"}</td><td style="padding:5px;border:1px solid #ddd">${it.provEmp || "—"}</td></tr>`).join("");
  return `<!DOCTYPE html><html><head><meta charset="utf-8"><title>${ped.nombrePed || ped.numero}</title><style>body{font-family:Georgia,serif;font-size:12px;color:#1a1a1a;padding:28px;margin:0}h2{color:#0a2540;border-bottom:2px solid #0a2540;padding-bottom:4px;font-size:16px}h3{color:#0a2540;font-size:13px;margin:16px 0 6px;background:#f0f0f0;padding:4px 10px}table{width:100%;border-collapse:collapse;margin-bottom:12px;font-size:11px}th{background:#0a2540;color:#e8d5b7;padding:6px;text-align:left;font-size:10px}.info{display:flex;gap:20px;margin-bottom:12px}.info div{flex:1}.lbl{font-size:9px;color:#888;text-transform:uppercase;font-weight:600}.val{font-size:12px;margin-top:1px}@media print{body{padding:0}}</style></head><body>
<div style="display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:16px;border-bottom:2px solid #0a2540;padding-bottom:10px"><div><img src="${LOGO}" style="height:44px"/><div style="font-size:8px;color:#777;margin-top:3px">Veliantex S.A.S. — Vitality Sportswear<br/>NIT: 901.187.332-1 · Cali, Colombia</div></div><div style="text-align:right"><div style="font-size:18px;font-weight:bold;color:#0a2540">${ped.nombrePed || ped.numero}</div><div style="font-size:10px;color:#555">Fecha: ${ped.fecha} · COT-${ped.cotNum || ""}</div></div></div>
<h3>Información General</h3>
<div class="info"><div><div class="lbl">Cliente</div><div class="val"><strong>${ped.clienteNombre || "—"}</strong></div></div><div><div class="lbl">Contacto</div><div class="val">${ped.contacto || "—"} · ${ped.telContacto || ""}</div></div><div><div class="lbl">OC Cliente</div><div class="val">${ped.ocCliente || "—"}</div></div><div><div class="lbl">Responsable</div><div class="val">${ped.responsable || "—"}</div></div></div>
<div class="info"><div><div class="lbl">Dir. Entrega</div><div class="val">${ped.dirEntrega || "—"}</div></div><div><div class="lbl">Contacto recibe</div><div class="val">${ped.contactoRecibe || "—"} · ${ped.telRecibe || ""}</div></div><div><div class="lbl">Fecha compromiso</div><div class="val">${ped.fechaCompromiso || "—"}</div></div></div>
<h3>Ítems del Pedido</h3>
<table><thead><tr><th>#</th><th>Referencia</th><th>Textil</th><th>Cant.</th><th>Tallas</th><th>Estampados</th><th>Empaque</th></tr></thead><tbody>${itemsHtml}</tbody></table>
<h3>Asignación de Proveedores</h3>
<table><thead><tr><th>Ítem</th><th>Corte</th><th>Confección</th><th>Estampado</th><th>Empaque</th></tr></thead><tbody>${provsHtml}</tbody></table>
${(ped.items || []).some(it => it.notas) ? `<h3>Notas de Producción</h3>${(ped.items || []).filter(it => it.notas).map(it => `<p style="font-size:11px"><strong>${it.nombreComercial || it.tipoPrenda}:</strong> ${it.notas}</p>`).join("")}` : ""}
<div style="margin-top:20px;border-top:1px solid #ddd;padding-top:6px;text-align:center;font-size:8px;color:#999">VELIANTEX S.A.S. — Vitality Sportswear — Cali, Colombia — NIT: 901.187.332-1</div>
</body></html>`;
}

// PDF: mostrar en modal de vista previa (descarga bloqueada en sandbox)
// Se maneja dentro del App con estado pdfPreview

// ═══════════════════════════════════════════════════════════════
// APP
// ═══════════════════════════════════════════════════════════════
function App() {
  const [D, setD] = useState(INIT);
  const [view, setView] = useState("dashboard");
  const [modal, setModal] = useState(null);
  const [edt, setEdt] = useState(null);
  const [search, setSearch] = useState("");
  const [stab, setStab] = useState(0);
  const [ok, setOk] = useState(false);
  const [saved, setSaved] = useState("");
  const [pdfHtml, setPdfHtml] = useState(null);
  const [pdfCot, setPdfCot] = useState(null);
  const [emailModal, setEmailModal] = useState(null);
  const [session, setSession] = useState({ id: 1, nombre: "Juan Carlos Velasco", nivel: 3, verCostos: true }); // Login desactivado — auto-admin
  const [loginErr, setLoginErr] = useState("");

  // Permission helper
  const puede = (accion) => {
    if (!session) return false;
    if (session.nivel >= 3) return true; // Admin = todo
    if (accion === "ver") return session.nivel >= 1;
    if (accion === "editar" || accion === "crear" || accion === "eliminar" || accion === "exportar") return session.nivel >= 2;
    if (accion === "costos") return session.verCostos || false;
    if (accion === "config" || accion === "usuarios" || accion === "aprobar") return false;
    return session.nivel >= 2;
  };
  const nivelLabel = n => n >= 3 ? "Admin" : n >= 2 ? "Editor" : "Viewer";
  const nivelColor = n => n >= 3 ? "#b91c1c" : n >= 2 ? "#1e40af" : "#6b7280";

  // Login handler
  const doLogin = (correo, clave) => {
    const user = (D.usuarios || []).find(u => u.correo === correo && u.clave === clave && u.estado === "activo");
    if (user) { setSession(user); setLoginErr(""); }
    else setLoginErr("Correo o contraseña incorrectos");
  };

  // Login screen
  const LoginScreen = () => {
    const [lc, setLc] = useState("");
    const [lp, setLp] = useState("");
    return (
      <div style={{ minHeight: "100vh", display: "flex", alignItems: "center", justifyContent: "center", background: "linear-gradient(135deg, #0a2540, #1a3a5c)", fontFamily: "'Libre Franklin','Segoe UI',sans-serif" }}>
        <div style={{ background: "#fff", borderRadius: 16, padding: 36, width: 380, boxShadow: "0 20px 60px rgba(0,0,0,.3)" }}>
          <div style={{ textAlign: "center", marginBottom: 24 }}>
            <div style={{ fontSize: 22, fontWeight: 800, color: "#0a2540", fontFamily: "Georgia,serif" }}>VELIANTEX</div>
            <div style={{ fontSize: 11, color: "#9ca3af", marginTop: 2 }}>Sistema de Cotización v5</div>
          </div>
          <div style={{ marginBottom: 12 }}>
            <label style={{ fontSize: 11, fontWeight: 600, color: "#6b7280", display: "block", marginBottom: 3 }}>Correo</label>
            <input style={{ width: "100%", padding: "10px 12px", border: "1px solid #d1cdc4", borderRadius: 8, fontSize: 13, outline: "none", boxSizing: "border-box" }} type="email" value={lc} onChange={e => setLc(e.target.value)} placeholder="correo@veliantex.com" onKeyDown={e => e.key === "Enter" && doLogin(lc, lp)} autoFocus />
          </div>
          <div style={{ marginBottom: 16 }}>
            <label style={{ fontSize: 11, fontWeight: 600, color: "#6b7280", display: "block", marginBottom: 3 }}>Contraseña</label>
            <input style={{ width: "100%", padding: "10px 12px", border: "1px solid #d1cdc4", borderRadius: 8, fontSize: 13, outline: "none", boxSizing: "border-box" }} type="password" value={lp} onChange={e => setLp(e.target.value)} onKeyDown={e => e.key === "Enter" && doLogin(lc, lp)} />
          </div>
          {loginErr && <div style={{ background: "#fee2e2", color: "#991b1b", padding: "8px 12px", borderRadius: 6, fontSize: 11, marginBottom: 12 }}>{loginErr}</div>}
          <button style={{ width: "100%", background: "#0a2540", color: "#e8d5b7", border: "none", padding: "12px", borderRadius: 8, fontSize: 14, fontWeight: 700, cursor: "pointer" }} onClick={() => doLogin(lc, lp)}>Iniciar sesión</button>
          <div style={{ textAlign: "center", marginTop: 16, fontSize: 10, color: "#9ca3af" }}>Usuarios registrados: {(D.usuarios || []).filter(u => u.estado === "activo").length}</div>
        </div>
      </div>
    );
  };

  // User management page (Admin only)
  const PgUsuarios = () => {
    const [eu, setEu] = useState(null); // editing user
    const [nu, setNu] = useState(null); // new user form
    const saveUser = (user) => {
      if (user.id) { setD(d => ({ ...d, usuarios: d.usuarios.map(u => u.id === user.id ? user : u) })); }
      else { const id = nid(D.usuarios || []); setD(d => ({ ...d, usuarios: [...d.usuarios, { ...user, id, fechaCreacion: hoy() }] })); }
      setEu(null); setNu(null); setSaved("✓ Usuario guardado"); setTimeout(() => setSaved(""), 3000);
    };
    const form = eu || nu;
    return (
      <div>
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 14 }}>
          <h2 style={{ margin: 0, color: "#0a2540", fontFamily: "Georgia,serif" }}>Usuarios y roles</h2>
          <button style={S.btn} onClick={() => setNu({ nombre: "", correo: "", clave: "", nivel: 1, verCostos: false, area: "", estado: "activo" })}><Ic name="plus" size={14} /> Nuevo usuario</button>
        </div>
        <div style={S.card}>
          <table style={{ width: "100%", borderCollapse: "collapse" }}>
            <thead><tr><th style={S.th}>Nombre</th><th style={S.th}>Correo</th><th style={S.th}>Rol</th><th style={S.th}>Área</th><th style={S.th}>Estado</th><th style={S.th}></th></tr></thead>
            <tbody>
              {(D.usuarios || []).map(u => (
                <tr key={u.id}>
                  <td style={S.td}><strong>{u.nombre}</strong>{session && session.id === u.id && <span style={{ marginLeft: 6, fontSize: 9, padding: "1px 5px", borderRadius: 3, background: "#dbeafe", color: "#1e40af" }}>Tú</span>}</td>
                  <td style={S.td}>{u.correo}</td>
                  <td style={S.td}><span style={{ padding: "2px 8px", borderRadius: 4, fontSize: 10, fontWeight: 700, background: u.nivel >= 3 ? "#fee2e2" : u.nivel >= 2 ? "#dbeafe" : "#f3f4f6", color: nivelColor(u.nivel) }}>{nivelLabel(u.nivel)}</span></td>
                  <td style={S.td}>{u.area || "—"}</td>
                  <td style={S.td}><span style={{ color: u.estado === "activo" ? "#065f46" : "#b91c1c", fontSize: 11, fontWeight: 600 }}>{u.estado}</span></td>
                  <td style={S.td}><button style={S.btn2} onClick={() => setEu({ ...u })}><Ic name="edit" size={12} /></button></td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
        {form && (
          <div style={S.modal} onMouseDown={e => { if (e.target === e.currentTarget) { setEu(null); setNu(null); } }}>
            <div style={{ ...S.mc, maxWidth: 480 }} onMouseDown={e => e.stopPropagation()}>
              <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 14 }}>
                <h3 style={{ margin: 0, color: "#0a2540", fontFamily: "Georgia,serif", fontSize: 16 }}>{form.id ? "Editar" : "Nuevo"} usuario</h3>
                <button style={S.btn} onClick={() => saveUser(form)}><Ic name="check" size={13} /> Guardar</button>
              </div>
              <div style={S.g2}>
                <F l="Nombre completo"><input style={S.inp} value={form.nombre} onChange={e => { const v = e.target.value; eu ? setEu({ ...eu, nombre: v }) : setNu({ ...nu, nombre: v }); }} /></F>
                <F l="Correo"><input style={S.inp} type="email" value={form.correo} onChange={e => { const v = e.target.value; eu ? setEu({ ...eu, correo: v }) : setNu({ ...nu, correo: v }); }} /></F>
              </div>
              <div style={{ ...S.g3, marginTop: 6 }}>
                <F l="Contraseña"><input style={S.inp} type="text" value={form.clave} onChange={e => { const v = e.target.value; eu ? setEu({ ...eu, clave: v }) : setNu({ ...nu, clave: v }); }} /></F>
                <F l="Rol"><select style={S.sel} value={form.nivel} onChange={e => { const v = parseInt(e.target.value); eu ? setEu({ ...eu, nivel: v }) : setNu({ ...nu, nivel: v }); }}>
                  <option value={1}>Viewer (solo lectura)</option>
                  <option value={2}>Editor (crear/editar)</option>
                  <option value={3}>Admin (todo)</option>
                </select></F>
                <F l="Área"><input style={S.inp} value={form.area || ""} onChange={e => { const v = e.target.value; eu ? setEu({ ...eu, area: v }) : setNu({ ...nu, area: v }); }} placeholder="Gerencia, Operaciones..." /></F>
              </div>
              <div style={{ ...S.g2, marginTop: 6 }}>
                <F l="Estado"><select style={S.sel} value={form.estado} onChange={e => { const v = e.target.value; eu ? setEu({ ...eu, estado: v }) : setNu({ ...nu, estado: v }); }}>
                  <option value="activo">Activo</option>
                  <option value="inactivo">Inactivo</option>
                </select></F>
                <F l="Ver costos/margen"><select style={S.sel} value={form.verCostos ? "si" : "no"} onChange={e => { const v = e.target.value === "si"; eu ? setEu({ ...eu, verCostos: v }) : setNu({ ...nu, verCostos: v }); }}>
                  <option value="no">No</option>
                  <option value="si">Sí</option>
                </select></F>
              </div>
              {form.nivel < 3 && <div style={{ marginTop: 8, padding: "8px 10px", background: "#f0fdf4", borderRadius: 5, fontSize: 10, color: "#065f46", border: "1px solid #bbf7d0" }}>
                {form.nivel === 2 ? "Editor: puede crear, editar, exportar, importar masivos. NO puede ver costos (salvo flag), configuración ni gestionar usuarios." : "Viewer: solo lectura. No puede crear, editar ni eliminar nada."}
              </div>}
            </div>
          </div>
        )}
      </div>
    );
  }; // { tipo, numero, para, asunto, mensaje, data }
  const sr = useRef(null);
  const verPDF = (cot) => { setPdfHtml(buildPDF(cot, D)); setPdfCot(cot); };
  const openEmail = (tipo, doc) => {
    const cli = D.clientes.find(c => c.id === doc.clienteId) || {};
    const num = doc.numero || "—";
    setEmailModal({
      tipo, numero: num, data: doc,
      para: cli.correo || doc.correo || "",
      asunto: `${tipo === "cot" ? "Cotización" : "Pedido"} ${num} — Veliantex S.A.S.`,
      mensaje: `Cordial saludo ${cli.contacto || cli.razon || ""},\n\nAdjunto encontrará ${tipo === "cot" ? "la cotización" : "el pedido"} ${num}.\n\nQuedamos atentos a sus comentarios.\n\nJuan Carlos Velasco Lian\nVeliantex S.A.S.\n(+57) 316 4489436`
    });
  };

  useEffect(() => { ld().then(s => { if (s) { Object.keys(INIT).forEach(k => { if (!(k in s)) s[k] = INIT[k]; }); setD(s); } setOk(true); }); }, []);
  useEffect(() => { if (!ok) return; if (sr.current) clearTimeout(sr.current); sr.current = setTimeout(() => { sv(D).then(() => { setSaved("\u2713"); setTimeout(() => setSaved(""), 2000); }); }, 1500); return () => { if (sr.current) clearTimeout(sr.current); }; }, [D, ok]);

  const up = (k, v) => setD(d => ({ ...d, [k]: v }));
  const filt = arr => { if (!search) return arr; const s = search.toLowerCase(); return arr.filter(x => JSON.stringify(x).toLowerCase().includes(s)); };
  const del = (t, id) => { up(t, D[t].filter(x => x.id !== id)); };
  const openNew = (t, tpl) => { setEdt({ ...tpl, id: 0, _t: t }); setModal("form"); };
  const openEd = (t, item) => { setEdt({ ...item, _t: t }); setModal("form"); };
  const saveForm = (directItem) => { const ei = directItem || edt; if (!ei) return; const t = ei._t; const { _t, ...item } = ei; if (item.id === 0) { item.id = nid(D[t]); up(t, [...D[t], item]); } else up(t, D[t].map(x => x.id === item.id ? item : x)); setModal(null); setEdt(null); };

  const menu = [
    { k: "dashboard", ic: "chart", l: "Dashboard" },
    { k: "textiles", ic: "box", l: "Textiles" },
    { k: "insumos", ic: "box", l: "Insumos" },
    { k: "maquila", ic: "factory", l: "Servicios Maquila" },
    { k: "tecnicas", ic: "scissors", l: "T\u00e9cnicas Estampado" },
    { k: "prendas", ic: "box", l: "Tipos de Prenda" },
    { k: "moldes", ic: "factory", l: "Molder\u00eda" },
    { k: "clientes", ic: "users", l: "Clientes" },
    { k: "proveedores", ic: "truck", l: "Proveedores" },
    { k: "plantillas", ic: "tag", l: "Plantillas Cargos" },
    { k: "catalogo", ic: "box", l: "Catálogo Productos" },
    { k: "cotizaciones", ic: "doc", l: "Cotizaciones" },
    { k: "pedidos", ic: "cart", l: "Pedidos" },
  ];
  const menuFull = puede("usuarios") ? [...menu, { k: "usuarios", ic: "users", l: "Usuarios y Roles" }] : menu;

  // ─── Cotización helpers ───
  const cotNombre = cn => cn || "Sin cliente";
  const genCotNum = (clienteId, clienteNombre) => {
    const year = new Date().getFullYear();
    const inicial = (clienteNombre || "X").charAt(0).toUpperCase();
    const cotCliente = D.cotizaciones.filter(c => c.clienteId === clienteId);
    const seq = String(cotCliente.length + 1).padStart(3, "0");
    return `${inicial}-${seq}-${year}`;
  };
  const nuevaCot = () => {
    const year = new Date().getFullYear();
    const seq = String(D.cotizaciones.length + 1).padStart(3, "0");
    const c = { id: nid(D.cotizaciones), numero: `COT-${seq}-${year}`, nombreCot: "", fecha: hoy(), vigencia: "15 d\u00edas", comercial: "", clienteId: null, clienteNombre: "", proyecto: "", flete: "No incluido", metodoPago: D.metodosPago[0] || "", diasEntrega: 30, garantiaMeses: 0, estado: "Borrador", razonNo: "", obsInt: "", obsExt: "", items: [], margenGlobal: 35 };
    up("cotizaciones", [...D.cotizaciones, c]); setView("cotizaciones"); setEdt(c); setModal("cotizador");
  };
  const saveCot = c => { up("cotizaciones", D.cotizaciones.map(x => x.id === c.id ? c : x)); setModal(null); setEdt(null); };
  const [convertCot, setConvertCot] = useState(null); // cotización pending conversion
  const [convertTipo, setConvertTipo] = useState(TIPOS_NEGOCIO[0]);
  const cotAPedido = (cot, tipoNegocio) => {
    const cl = D.clientes.find(c => c.id === cot.clienteId) || {};
    const tallasObj = (D.tallas || []).reduce((a, t) => ({ ...a, [t]: 0 }), {});
    const ped = { id: nid(D.pedidos), numero: `PED-${new Date().getFullYear()}-${String(D.pedidos.length + 1).padStart(3, "0")}`, nombrePed: `${cl.razon || cot.clienteNombre || "Pedido"}-${hoy()}`, fecha: hoy(), cotId: cot.id, cotNum: cot.numero, clienteId: cot.clienteId, clienteNombre: cot.clienteNombre || cl.razon || "", contacto: cl.contacto || "", telContacto: cl.tel || "", dirEntrega: cl.dirEntrega || "", dirFactura: cl.dirFactura || "", contactoRecibe: "", telRecibe: "", fechaCompromiso: "", responsable: "", ocCliente: "", tipoNegocio: tipoNegocio || "",
      items: (cot.items || []).map(it => ({ ...it, tallasDetalle: { ...tallasObj }, tallasCustom: [], molde: "", patronista: "", provCorte: "", provConf: "", provEst: "", provEmp: "", renderUrl: "", empaqueD: it.empaque || "Individual", insumosD: (it.insumosItems || it.insumosIds || []).map(x => { const id = typeof x === "object" ? x.id : x; const qty = typeof x === "object" ? x.qty : 1; const ins = D.insumos.find(z => z.id === id); return ins ? `${ins.nombre}${qty > 1 ? " ×" + qty : ""}` : ""; }).filter(Boolean).join(", "), notas: "" })),
      estado: "Pendiente", obs: cot.obsExt || "" };
    up("pedidos", [...D.pedidos, ped]);
    up("cotizaciones", D.cotizaciones.map(c => c.id === cot.id ? { ...c, estado: "Aprobada" } : c));
    setConvertCot(null);
    setSaved("✓ Pedido creado"); setTimeout(() => setSaved(""), 3000);
  };
  const tbl = (cols, rows, acts) => <div style={{ overflowX: "auto" }}><table style={{ width: "100%", borderCollapse: "collapse" }}><thead><tr>{cols.map(c => <th key={c.l} style={{ ...S.th, ...(c.a ? { textAlign: c.a } : {}) }}>{c.l}</th>)}{acts && <th style={S.th}></th>}</tr></thead><tbody>{rows.length === 0 ? <tr><td colSpan={cols.length + (acts ? 1 : 0)} style={{ textAlign: "center", padding: 36, color: "#9ca3af", fontSize: 12 }}>Sin registros</td></tr> : rows}</tbody></table></div>;

  // ═══════════════════════════════════════════════════════════════
  // F. DASHBOARD
  // ═══════════════════════════════════════════════════════════════
  const PgDash = () => {
    // ─── Date range filter ───
    const [dDesde, setDDesde] = useState("");
    const [dHasta, setDHasta] = useState("");
    const preset = (tipo) => {
      const h = new Date();
      const yy = h.getFullYear(); const mm = h.getMonth(); const dd = h.getDate();
      const f = d => d.toISOString().split("T")[0];
      if (tipo === "mes") { setDDesde(f(new Date(yy, mm, 1))); setDHasta(f(h)); }
      else if (tipo === "trim") { setDDesde(f(new Date(yy, mm - 2, 1))); setDHasta(f(h)); }
      else if (tipo === "anio") { setDDesde(`${yy}-01-01`); setDHasta(f(h)); }
      else { setDDesde(""); setDHasta(""); }
    };
    const inRange = (fecha) => {
      if (!fecha) return true;
      if (dDesde && fecha < dDesde) return false;
      if (dHasta && fecha > dHasta) return false;
      return true;
    };

    // ─── Data aggregation (filtered by date range) ───
    const cots = (D.cotizaciones || []).filter(c => inRange(c.fecha));
    const aprobadas = cots.filter(c => c.estado === "Aprobada");
    const noAprobadas = cots.filter(c => c.estado === "No aprobada");
    const enviadas = cots.filter(c => ["Enviada", "Aprobada", "No aprobada"].includes(c.estado));

    // Compute all items with costs
    const allItems = cots.flatMap(c => (c.items || []).map(it => {
      const rc = motorCosteo(it, D.insumos);
      return { ...rc, _cotEstado: c.estado, _cotId: c.id, _cotNum: c.numero, _cliente: c.clienteNombre || "", _clienteId: c.clienteId, _fecha: c.fecha };
    }));
    const apItems = allItems.filter(x => x._cotEstado === "Aprobada");

    // B1 — Ventas generales
    const totalIngresos = apItems.reduce((s, it) => s + it._netoSinIVA * it.cantidad, 0);
    const totalIVA = apItems.reduce((s, it) => s + it._iva * it.cantidad, 0);
    const totalUnidades = apItems.reduce((s, it) => s + it.cantidad, 0);
    const ticketProm = aprobadas.length > 0 ? Math.round(totalIngresos / aprobadas.length) : 0;
    const pctConv = enviadas.length > 0 ? ((aprobadas.length / enviadas.length) * 100).toFixed(1) : "0.0";

    // B2 — Producto más vendido (por nombre comercial o tipo prenda)
    const prodMap = {};
    apItems.forEach(it => {
      const k = it.nombreComercial || it.tipoPrenda || "Sin nombre";
      if (!prodMap[k]) prodMap[k] = { nombre: k, unidades: 0, ingresos: 0, utilidad: 0, cotizados: 0, aprobados: 0, costoTextil: 0, costoConf: 0, costoEst: 0, costoIns: 0, costoMaq: 0, costoCargos: 0 };
      prodMap[k].unidades += it.cantidad;
      prodMap[k].ingresos += it._netoSinIVA * it.cantidad;
      prodMap[k].utilidad += it._utilidad * it.cantidad;
      prodMap[k].aprobados++;
      prodMap[k].costoTextil += (it._cTextil || 0) * it.cantidad;
      prodMap[k].costoConf += (it.costoConfeccion || 0) * it.cantidad;
      prodMap[k].costoEst += (it._cEstampados || 0) * it.cantidad;
      prodMap[k].costoIns += (it._cInsumos || 0) * it.cantidad;
      prodMap[k].costoMaq += ((it.costoTrazo || 0) + (it.costoCorte || 0) + (it.costoEmpaque || 0)) * it.cantidad;
      prodMap[k].costoCargos += (it._totalCargos || 0) * it.cantidad;
    });
    // Count all cotizados (incl. no aprobadas)
    allItems.forEach(it => {
      const k = it.nombreComercial || it.tipoPrenda || "Sin nombre";
      if (!prodMap[k]) prodMap[k] = { nombre: k, unidades: 0, ingresos: 0, utilidad: 0, cotizados: 0, aprobados: 0, costoTextil: 0, costoConf: 0, costoEst: 0, costoIns: 0, costoMaq: 0, costoCargos: 0 };
      prodMap[k].cotizados++;
    });
    const prods = Object.values(prodMap);
    const topVendidos = [...prods].sort((a, b) => b.unidades - a.unidades).slice(0, 5);
    const topRentables = [...prods].sort((a, b) => b.utilidad - a.utilidad).slice(0, 5);
    const topIngresos = [...prods].sort((a, b) => b.ingresos - a.ingresos).slice(0, 5);

    // B4 — Conversión por producto
    const convProds = prods.filter(p => p.cotizados > 0).map(p => ({ ...p, convPct: p.cotizados > 0 ? ((p.aprobados / p.cotizados) * 100).toFixed(0) : 0 })).sort((a, b) => a.convPct - b.convPct);

    // B5 — Estructura de costos (global aprobadas)
    const totalCostoDir = apItems.reduce((s, it) => s + it._subDirecto * it.cantidad, 0);
    const pctCosto = (v) => totalCostoDir > 0 ? ((v / totalCostoDir) * 100).toFixed(1) : "0";
    const cGlobalTextil = apItems.reduce((s, it) => s + (it._cTextil || 0) * it.cantidad, 0);
    const cGlobalConf = apItems.reduce((s, it) => s + (it.costoConfeccion || 0) * it.cantidad, 0);
    const cGlobalEst = apItems.reduce((s, it) => s + (it._cEstampados || 0) * it.cantidad, 0);
    const cGlobalIns = apItems.reduce((s, it) => s + (it._cInsumos || 0) * it.cantidad, 0);
    const cGlobalMaq = apItems.reduce((s, it) => s + ((it.costoTrazo || 0) + (it.costoCorte || 0) + (it.costoEmpaque || 0)) * it.cantidad, 0);

    // B6 — Cliente
    const cliMap = {};
    // All cotizaciones per client (for conversion)
    cots.forEach(c => {
      const k = c.clienteNombre || (D.clientes.find(x => x.id === c.clienteId) || {}).razon || "Sin cliente";
      if (!cliMap[k]) cliMap[k] = { nombre: k, ingresos: 0, utilidad: 0, unidades: 0, cotsAprobadas: 0, cotsTotal: 0, cotsEnviadas: 0, topProducto: {} };
      cliMap[k].cotsTotal++;
      if (c.estado === "Aprobada") cliMap[k].cotsAprobadas++;
      if (["Enviada", "Aprobada", "No aprobada"].includes(c.estado)) cliMap[k].cotsEnviadas++;
    });
    apItems.forEach(it => {
      const k = it._cliente || "Sin cliente";
      if (!cliMap[k]) cliMap[k] = { nombre: k, ingresos: 0, utilidad: 0, unidades: 0, cotsAprobadas: 0, cotsTotal: 0, cotsEnviadas: 0, topProducto: {} };
      cliMap[k].ingresos += it._netoSinIVA * it.cantidad;
      cliMap[k].utilidad += it._utilidad * it.cantidad;
      cliMap[k].unidades += it.cantidad;
      const prod = it.nombreComercial || it.tipoPrenda || "—";
      cliMap[k].topProducto[prod] = (cliMap[k].topProducto[prod] || 0) + it.cantidad;
    });
    const clientes = Object.values(cliMap).map(c => {
      const topProd = Object.entries(c.topProducto).sort((a, b) => b[1] - a[1])[0];
      return { ...c, margen: c.ingresos > 0 ? ((c.utilidad / c.ingresos) * 100).toFixed(1) : 0, convPct: c.cotsEnviadas > 0 ? ((c.cotsAprobadas / c.cotsEnviadas) * 100).toFixed(0) : "—", topProdNombre: topProd ? topProd[0] : "—" };
    }).sort((a, b) => b.ingresos - a.ingresos);

    // B8 — Razones de pérdida
    const razones = {};
    noAprobadas.forEach(c => { if (c.razonNo) razones[c.razonNo] = (razones[c.razonNo] || 0) + 1; });
    const razonesArr = Object.entries(razones).sort((a, b) => b[1] - a[1]);
    const totalRazones = razonesArr.reduce((s, [, n]) => s + n, 0);

    // B10 — Alertas
    const alertas = [];
    prods.filter(p => p.cotizados >= 2 && p.aprobados / p.cotizados < 0.3).forEach(p => alertas.push({ tipo: "danger", msg: `${p.nombre}: baja conversión (${((p.aprobados / p.cotizados) * 100).toFixed(0)}%)`, insight: "Revisar precio o propuesta" }));
    prods.filter(p => p.ingresos > 0 && (p.utilidad / p.ingresos) < 0.10).forEach(p => alertas.push({ tipo: "warning", msg: `${p.nombre}: margen bajo (${((p.utilidad / p.ingresos) * 100).toFixed(1)}%)`, insight: "Revisar estructura de costos" }));
    prods.filter(p => p.cotizados >= 3 && p.aprobados === 0).forEach(p => alertas.push({ tipo: "danger", msg: `${p.nombre}: cotizado ${p.cotizados} veces, nunca aprobado`, insight: "Considerar eliminar o recostear" }));
    // Cotizaciones sin seguimiento (Enviadas hace más de 7 días)
    const hoyMs = new Date().getTime();
    cots.filter(c => c.estado === "Enviada" || c.estado === "Borrador").forEach(c => {
      const dias = Math.floor((hoyMs - new Date(c.fecha).getTime()) / 86400000);
      if (dias > 7) alertas.push({ tipo: "warning", msg: `COT ${c.numero} (${c.clienteNombre || "—"}): ${dias} días sin respuesta`, insight: "Hacer seguimiento comercial" });
    });
    // Clientes poco rentables
    clientes.filter(c => parseFloat(c.margen) < 10 && c.ingresos > 0).forEach(c => alertas.push({ tipo: "warning", msg: `${c.nombre}: margen promedio ${c.margen}%`, insight: "Revisar precios para este cliente" }));

    // B — Insights: productos con alto volumen cotizado pero baja venta
    const insightProds = prods.filter(p => p.cotizados >= 2).map(p => {
      const conv = p.cotizados > 0 ? p.aprobados / p.cotizados : 0;
      const mg = p.ingresos > 0 ? p.utilidad / p.ingresos : 0;
      let tag = null;
      if (conv < 0.3 && p.cotizados >= 3) tag = { color: "#b91c1c", bg: "#fee2e2", text: "Se cotiza mucho pero no convierte" };
      else if (mg > 0.30 && p.unidades >= 10) tag = { color: "#065f46", bg: "#dcfce7", text: "Alta rentabilidad + buen volumen" };
      else if (p.unidades > 0 && mg < 0.10) tag = { color: "#854d0e", bg: "#fef9c3", text: "Rota bien pero margen bajo" };
      else if (p.utilidad > 0 && p.unidades <= 5) tag = { color: "#1e40af", bg: "#dbeafe", text: "Buena utilidad pero poca rotación" };
      return { ...p, conv: (conv * 100).toFixed(0), mg: (mg * 100).toFixed(1), tag };
    }).filter(p => p.tag).slice(0, 5);

    // Peor margen
    const peorMargen = prods.filter(p => p.ingresos > 0).sort((a, b) => (a.utilidad / a.ingresos) - (b.utilidad / b.ingresos)).slice(0, 3);

    // ─── Styles ───
    const kpi = { ...S.card, textAlign: "center", padding: "16px 10px" };
    const kpiN = { fontSize: 24, fontWeight: 800, fontFamily: "Georgia,serif" };
    const kpiL = { fontSize: 10, color: "#9ca3af", marginTop: 2 };
    const secT = { margin: "18px 0 8px", fontSize: 13, fontWeight: 600, color: "#0a2540", fontFamily: "Georgia,serif", borderBottom: "1px solid #e5e1d8", paddingBottom: 4 };
    const bar = (pct, color) => ({ height: 8, borderRadius: 4, background: `linear-gradient(90deg, ${color} ${pct}%, #e5e1d8 ${pct}%)` });

    return (
      <div>
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 10 }}>
          <h2 style={{ margin: 0, color: "#0a2540", fontFamily: "Georgia,serif", fontSize: 18 }}>Dashboard</h2>
          <button style={S.btn} onClick={nuevaCot}><Ic name="plus" size={14} /> Nueva Cotización</button>
        </div>
        {/* Filtro de fechas */}
        <div style={{ ...S.card, padding: "8px 12px", marginBottom: 10, display: "flex", alignItems: "center", gap: 10, flexWrap: "wrap" }}>
          <span style={{ fontSize: 11, fontWeight: 600, color: "#0a2540" }}>Período:</span>
          <input style={{ ...S.inp, width: 130, fontSize: 11 }} type="date" value={dDesde} onChange={e => setDDesde(e.target.value)} />
          <span style={{ fontSize: 11, color: "#9ca3af" }}>a</span>
          <input style={{ ...S.inp, width: 130, fontSize: 11 }} type="date" value={dHasta} onChange={e => setDHasta(e.target.value)} />
          <div style={{ display: "flex", gap: 4 }}>
            <button style={{ ...S.btn2, fontSize: 10, fontWeight: dDesde && new Date(dDesde).getDate() === 1 && new Date(dDesde).getMonth() === new Date().getMonth() ? 700 : 400 }} onClick={() => preset("mes")}>Este mes</button>
            <button style={{ ...S.btn2, fontSize: 10 }} onClick={() => preset("trim")}>Último trimestre</button>
            <button style={{ ...S.btn2, fontSize: 10 }} onClick={() => preset("anio")}>Este año</button>
            <button style={{ ...S.btn2, fontSize: 10, fontWeight: !dDesde && !dHasta ? 700 : 400 }} onClick={() => preset("todo")}>Todo</button>
          </div>
          {(dDesde || dHasta) && <span style={{ fontSize: 10, color: "#6b7280" }}>Mostrando {cots.length} cotizaciones · {(D.pedidos || []).filter(p => inRange(p.fecha)).length} pedidos</span>}
        </div>

        {/* KPIs principales */}
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr 1fr 1fr", gap: 8 }}>
          <div style={kpi}><div style={{ ...kpiN, color: "#0a2540" }}>{cots.length}</div><div style={kpiL}>Cotizaciones</div></div>
          <div style={kpi}><div style={{ ...kpiN, color: "#065f46" }}>{fmt(totalIngresos)}</div><div style={kpiL}>Ingresos aprobados</div></div>
          <div style={kpi}><div style={{ ...kpiN, color: "#1e40af" }}>{pctConv}%</div><div style={kpiL}>Conversión</div></div>
          <div style={kpi}><div style={{ ...kpiN, color: "#0a2540" }}>{fmtN(totalUnidades)}</div><div style={kpiL}>Unidades vendidas</div></div>
          <div style={kpi}><div style={{ ...kpiN, color: "#b91c1c" }}>{noAprobadas.length}</div><div style={kpiL}>No aprobadas</div></div>
        </div>

        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 8, marginTop: 8 }}>
          <div style={kpi}><div style={{ ...kpiN, fontSize: 16, color: "#065f46" }}>{fmt(ticketProm)}</div><div style={kpiL}>Ticket promedio</div></div>
          <div style={kpi}><div style={{ ...kpiN, fontSize: 16, color: "#065f46" }}>{fmt(apItems.reduce((s, it) => s + it._utilidad * it.cantidad, 0))}</div><div style={kpiL}>Utilidad total</div></div>
          <div style={kpi}><div style={{ ...kpiN, fontSize: 16, color: "#1e40af" }}>{totalIngresos > 0 ? ((apItems.reduce((s, it) => s + it._utilidad * it.cantidad, 0) / totalIngresos) * 100).toFixed(1) : 0}%</div><div style={kpiL}>Margen promedio</div></div>
        </div>

        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 8 }}>
          {/* Top vendidos */}
          <div>
            <div style={secT}>Top por volumen (uds)</div>
            <div style={S.card}>{topVendidos.length === 0 ? <p style={{ color: "#9ca3af", fontSize: 11 }}>Sin datos</p> : topVendidos.map((p, i) => (
              <div key={i} style={{ display: "flex", justifyContent: "space-between", alignItems: "center", padding: "5px 0", borderBottom: "1px solid #f0ede6", fontSize: 12 }}>
                <div><strong style={{ fontSize: 11 }}>{i + 1}. {p.nombre}</strong></div>
                <div style={{ textAlign: "right" }}><strong>{fmtN(p.unidades)}</strong> uds</div>
              </div>
            ))}</div>
          </div>

          {/* Top ingresos */}
          <div>
            <div style={secT}>Top por ingresos ($)</div>
            <div style={S.card}>{topIngresos.length === 0 ? <p style={{ color: "#9ca3af", fontSize: 11 }}>Sin datos</p> : topIngresos.map((p, i) => (
              <div key={i} style={{ display: "flex", justifyContent: "space-between", alignItems: "center", padding: "5px 0", borderBottom: "1px solid #f0ede6", fontSize: 12 }}>
                <div><strong style={{ fontSize: 11 }}>{i + 1}. {p.nombre}</strong></div>
                <div style={{ textAlign: "right" }}><strong style={{ color: "#065f46" }}>{fmt(p.ingresos)}</strong></div>
              </div>
            ))}</div>
          </div>

          {/* Top rentables */}
          <div>
            <div style={secT}>Top por utilidad ($)</div>
            <div style={S.card}>{topRentables.length === 0 ? <p style={{ color: "#9ca3af", fontSize: 11 }}>Sin datos</p> : topRentables.map((p, i) => {
              const mg = p.ingresos > 0 ? ((p.utilidad / p.ingresos) * 100).toFixed(1) : 0;
              return (
                <div key={i} style={{ display: "flex", justifyContent: "space-between", alignItems: "center", padding: "5px 0", borderBottom: "1px solid #f0ede6", fontSize: 12 }}>
                  <div><strong style={{ fontSize: 11 }}>{i + 1}. {p.nombre}</strong></div>
                  <div style={{ textAlign: "right" }}><strong style={{ color: "#065f46" }}>{fmt(p.utilidad)}</strong> <span style={{ fontSize: 10, color: "#9ca3af" }}>{mg}%</span></div>
                </div>
              );
            })}</div>
          </div>
        </div>

        {/* Insights estratégicos */}
        {insightProds.length > 0 && <>
          <div style={secT}>Insights de producto</div>
          <div style={S.card}>
            {insightProds.map((p, i) => (
              <div key={i} style={{ display: "flex", justifyContent: "space-between", alignItems: "center", padding: "6px 0", borderBottom: "1px solid #f0ede6", fontSize: 11 }}>
                <div>
                  <strong>{p.nombre}</strong>
                  <span style={{ marginLeft: 8, padding: "1px 6px", borderRadius: 4, fontSize: 9, background: p.tag.bg, color: p.tag.color }}>{p.tag.text}</span>
                </div>
                <div style={{ textAlign: "right", fontSize: 10, color: "#6b7280" }}>{fmtN(p.unidades)} uds · Conv {p.conv}% · Margen {p.mg}%</div>
              </div>
            ))}
          </div>
        </>}

        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 8 }}>
          {/* Estructura de costos */}
          <div>
            <div style={secT}>Estructura de costos</div>
            <div style={S.card}>
              {totalCostoDir === 0 ? <p style={{ color: "#9ca3af", fontSize: 11 }}>Sin datos</p> : <>
                {[["Textil", cGlobalTextil, "#3b82f6"], ["Confección", cGlobalConf, "#8b5cf6"], ["Estampado", cGlobalEst, "#f59e0b"], ["Insumos", cGlobalIns, "#10b981"], ["Maquila", cGlobalMaq, "#ef4444"]].map(([label, val, color]) => (
                  <div key={label} style={{ marginBottom: 6 }}>
                    <div style={{ display: "flex", justifyContent: "space-between", fontSize: 11 }}><span>{label}</span><strong>{pctCosto(val)}%</strong></div>
                    <div style={bar(parseFloat(pctCosto(val)), color)}></div>
                  </div>
                ))}
              </>}
            </div>
          </div>

          {/* Razones de pérdida */}
          <div>
            <div style={secT}>Razones de pérdida</div>
            <div style={S.card}>
              {razonesArr.length === 0 ? <p style={{ color: "#9ca3af", fontSize: 11 }}>Sin datos</p> : razonesArr.map(([r, n]) => (
                <div key={r} style={{ marginBottom: 6 }}>
                  <div style={{ display: "flex", justifyContent: "space-between", fontSize: 11 }}><span>{r}</span><strong>{n} ({totalRazones > 0 ? ((n / totalRazones) * 100).toFixed(0) : 0}%)</strong></div>
                  <div style={bar(totalRazones > 0 ? (n / totalRazones) * 100 : 0, "#ef4444")}></div>
                </div>
              ))}
            </div>
          </div>

          {/* Peor margen */}
          <div>
            <div style={secT}>Productos con peor margen</div>
            <div style={S.card}>
              {peorMargen.length === 0 ? <p style={{ color: "#9ca3af", fontSize: 11 }}>Sin datos</p> : peorMargen.map((p, i) => {
                const mg = p.ingresos > 0 ? ((p.utilidad / p.ingresos) * 100).toFixed(1) : 0;
                return (
                  <div key={i} style={{ padding: "5px 0", borderBottom: "1px solid #f0ede6", fontSize: 11 }}>
                    <div style={{ display: "flex", justifyContent: "space-between" }}><strong>{p.nombre}</strong><span style={{ color: parseFloat(mg) < 10 ? "#b91c1c" : "#854d0e", fontWeight: 700 }}>{mg}%</span></div>
                    <div style={{ fontSize: 10, color: "#9ca3af" }}>Utilidad: {fmt(p.utilidad)} · {fmtN(p.unidades)} uds</div>
                  </div>
                );
              })}
            </div>
          </div>
        </div>

        <div style={S.g2}>
          {/* Conversión por producto */}
          <div>
            <div style={secT}>Conversión por producto</div>
            <div style={S.card}>{convProds.length === 0 ? <p style={{ color: "#9ca3af", fontSize: 11 }}>Sin datos</p> : convProds.slice(0, 8).map((p, i) => {
              const pct = parseInt(p.convPct);
              const color = pct >= 60 ? "#065f46" : pct >= 30 ? "#854d0e" : "#b91c1c";
              return (
                <div key={i} style={{ display: "flex", justifyContent: "space-between", alignItems: "center", padding: "4px 0", borderBottom: "1px solid #f0ede6", fontSize: 11 }}>
                  <span>{p.nombre}</span>
                  <div><span style={{ color, fontWeight: 700 }}>{p.convPct}%</span> <span style={{ color: "#9ca3af", fontSize: 10 }}>({p.aprobados}/{p.cotizados})</span></div>
                </div>
              );
            })}</div>
          </div>

          {/* Clientes */}
          <div>
            <div style={secT}>Clientes</div>
            <div style={S.card}>{clientes.length === 0 ? <p style={{ color: "#9ca3af", fontSize: 11 }}>Sin datos</p> : clientes.slice(0, 6).map((c, i) => (
              <div key={i} style={{ padding: "5px 0", borderBottom: "1px solid #f0ede6", fontSize: 11 }}>
                <div style={{ display: "flex", justifyContent: "space-between" }}><strong>{c.nombre}</strong><span style={{ color: "#065f46" }}>{fmt(c.ingresos)}</span></div>
                <div style={{ display: "flex", gap: 8, fontSize: 10, color: "#9ca3af", marginTop: 1, flexWrap: "wrap" }}>
                  <span>{c.cotsAprobadas}/{c.cotsTotal} cot.</span>
                  <span>Conv: <strong style={{ color: parseInt(c.convPct) >= 50 ? "#065f46" : parseInt(c.convPct) >= 30 ? "#854d0e" : "#b91c1c" }}>{c.convPct}%</strong></span>
                  <span>Margen: {c.margen}%</span>
                  <span>Top: {c.topProdNombre}</span>
                </div>
              </div>
            ))}</div>
          </div>
        </div>

        {/* Alertas */}
        {alertas.length > 0 && <>
          <div style={secT}>Alertas y seguimiento ({alertas.length})</div>
          <div style={S.card}>
            {alertas.map((a, i) => (
              <div key={i} style={{ display: "flex", alignItems: "flex-start", gap: 8, padding: "6px 0", borderBottom: "1px solid #f0ede6", fontSize: 11 }}>
                <span style={{ ...S.badge(a.tipo === "danger" ? "red" : "yellow"), fontSize: 9, flexShrink: 0, marginTop: 1 }}>{a.tipo === "danger" ? "ALERTA" : "AVISO"}</span>
                <div><div>{a.msg}</div>{a.insight && <div style={{ fontSize: 10, color: "#6b7280", fontStyle: "italic", marginTop: 1 }}>{a.insight}</div>}</div>
              </div>
            ))}
          </div>
        </>}

        {/* ═══ Unidades de Negocio ═══ */}
        {(() => {
          const peds = (D.pedidos || []).filter(p => inRange(p.fecha));
          if (peds.length === 0) return null;
          const negMap = {};
          peds.forEach(p => {
            const tipo = p.tipoNegocio || "Sin clasificar";
            if (!negMap[tipo]) negMap[tipo] = { tipo, pedidos: 0, unidades: 0, ingresos: 0, utilidad: 0, costoTotal: 0, entregados: 0 };
            negMap[tipo].pedidos++;
            if (p.estado === "Entregado") negMap[tipo].entregados++;
            (p.items || []).forEach(it => {
              const rc = motorCosteo(it, D.insumos);
              negMap[tipo].unidades += it.cantidad;
              negMap[tipo].ingresos += rc._netoSinIVA * it.cantidad;
              negMap[tipo].utilidad += rc._utilidad * it.cantidad;
              negMap[tipo].costoTotal += rc._subDirecto * it.cantidad;
            });
          });
          const negs = Object.values(negMap).sort((a, b) => b.ingresos - a.ingresos);
          const maxIng = Math.max(...negs.map(n => n.ingresos), 1);
          const totalIngNeg = negs.reduce((s, n) => s + n.ingresos, 0);
          const totalUtilNeg = negs.reduce((s, n) => s + n.utilidad, 0);
          const totalUdsNeg = negs.reduce((s, n) => s + n.unidades, 0);
          const totalPedsNeg = negs.reduce((s, n) => s + n.pedidos, 0);
          const colores = { "Promocionales": "#3b82f6", "Clubes Sociales y Deportivos / Institucional": "#10b981", "B2B2C (Página Web)": "#f59e0b", "B2C": "#8b5cf6", "Licitaciones": "#ef4444", "Sin clasificar": "#9ca3af" };
          return <>
            <div style={secT}>Unidades de negocio ({negs.length})</div>
            {/* KPIs globales de pedidos */}
            <div style={{ display: "grid", gridTemplateColumns: "repeat(4, 1fr)", gap: 8, marginBottom: 10 }}>
              <div style={{ ...S.card, textAlign: "center", padding: 10 }}><div style={{ fontSize: 20, fontWeight: 700, color: "#0a2540" }}>{totalPedsNeg}</div><div style={{ fontSize: 10, color: "#9ca3af" }}>Pedidos totales</div></div>
              <div style={{ ...S.card, textAlign: "center", padding: 10 }}><div style={{ fontSize: 20, fontWeight: 700, color: "#0a2540" }}>{fmt(totalIngNeg)}</div><div style={{ fontSize: 10, color: "#9ca3af" }}>Ingresos totales</div></div>
              <div style={{ ...S.card, textAlign: "center", padding: 10 }}><div style={{ fontSize: 20, fontWeight: 700, color: "#065f46" }}>{fmt(totalUtilNeg)}</div><div style={{ fontSize: 10, color: "#9ca3af" }}>Utilidad total</div></div>
              <div style={{ ...S.card, textAlign: "center", padding: 10 }}><div style={{ fontSize: 20, fontWeight: 700, color: "#0a2540" }}>{fmtN(totalUdsNeg)}</div><div style={{ fontSize: 10, color: "#9ca3af" }}>Unidades totales</div></div>
            </div>
            {/* Cards por tipo de negocio */}
            <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 8 }}>
              {negs.map(n => {
                const margen = n.ingresos > 0 ? ((n.utilidad / n.ingresos) * 100).toFixed(1) : "0.0";
                const ticket = n.pedidos > 0 ? Math.round(n.ingresos / n.pedidos) : 0;
                const pctIng = totalIngNeg > 0 ? ((n.ingresos / totalIngNeg) * 100).toFixed(1) : "0";
                const color = colores[n.tipo] || "#6b7280";
                return (
                  <div key={n.tipo} style={{ ...S.card, borderLeft: `3px solid ${color}` }}>
                    <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start", marginBottom: 8 }}>
                      <div>
                        <div style={{ fontSize: 12, fontWeight: 700, color: "#0a2540" }}>{n.tipo}</div>
                        <div style={{ fontSize: 10, color: "#9ca3af" }}>{n.pedidos} pedido(s) · {fmtN(n.unidades)} uds</div>
                      </div>
                      <div style={{ textAlign: "right" }}>
                        <div style={{ fontSize: 14, fontWeight: 800, color: "#0a2540" }}>{fmt(n.ingresos)}</div>
                        <div style={{ fontSize: 10, color: "#9ca3af" }}>{pctIng}% del total</div>
                      </div>
                    </div>
                    {/* Barra de participación */}
                    <div style={{ background: "#f3f4f6", borderRadius: 4, height: 6, marginBottom: 8 }}>
                      <div style={{ background: color, height: 6, borderRadius: 4, width: `${(n.ingresos / maxIng) * 100}%`, transition: "width 0.3s" }} />
                    </div>
                    <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr 1fr", gap: 6 }}>
                      <div><div style={{ fontSize: 9, color: "#9ca3af", textTransform: "uppercase" }}>Utilidad</div><div style={{ fontSize: 12, fontWeight: 700, color: "#065f46" }}>{fmt(n.utilidad)}</div></div>
                      <div><div style={{ fontSize: 9, color: "#9ca3af", textTransform: "uppercase" }}>Margen</div><div style={{ fontSize: 12, fontWeight: 700, color: parseFloat(margen) >= 25 ? "#065f46" : parseFloat(margen) >= 15 ? "#92400e" : "#b91c1c" }}>{margen}%</div></div>
                      <div><div style={{ fontSize: 9, color: "#9ca3af", textTransform: "uppercase" }}>Ticket prom.</div><div style={{ fontSize: 12, fontWeight: 700 }}>{fmt(ticket)}</div></div>
                      <div><div style={{ fontSize: 9, color: "#9ca3af", textTransform: "uppercase" }}>Entregados</div><div style={{ fontSize: 12, fontWeight: 700 }}>{n.entregados}/{n.pedidos}</div></div>
                    </div>
                  </div>
                );
              })}
            </div>
          </>;
        })()}

        {/* Maestros */}
        <div style={{ ...secT, marginTop: 18 }}>Maestros registrados</div>
        <div style={{ display: "grid", gridTemplateColumns: "repeat(6, 1fr)", gap: 8 }}>
          {[["textiles", "Textiles"], ["insumos", "Insumos"], ["tecnicas", "Técnicas"], ["tiposPrenda", "Prendas"], ["clientes", "Clientes"], ["proveedores", "Proveedores"]].map(([k, l]) => <div key={k} style={{ ...S.card, textAlign: "center", padding: 10 }}><div style={{ fontSize: 20, fontWeight: 700 }}>{(D[k] || []).length}</div><div style={{ fontSize: 10, color: "#9ca3af" }}>{l}</div></div>)}
        </div>
      </div>
    );
  };

  // ═══════════════════════════════════════════════════════════════
  // D. COTIZACIONES LIST
  // ═══════════════════════════════════════════════════════════════
  const PgCot = () => {
    const grouped = {};
    D.cotizaciones.forEach(c => {
      const cName = c.clienteNombre || (D.clientes.find(x => x.id === c.clienteId) || {}).razon || "Sin cliente";
      if (!grouped[cName]) grouped[cName] = [];
      grouped[cName].push(c);
    });
    const clients = Object.keys(grouped).sort();
    return (
    <div>
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 14 }}><h2 style={{ margin: 0, color: "#0a2540", fontFamily: "Georgia,serif" }}>Cotizaciones</h2><button style={S.btn} onClick={nuevaCot}><Ic name="plus" size={14} /> Nueva</button></div>
      {D.cotizaciones.length === 0 ? <div style={{ ...S.card, textAlign: "center", padding: 36, color: "#9ca3af" }}>Crea tu primera cotización.</div> :
        clients.map(cName => (
          <div key={cName} style={{ marginBottom: 16 }}>
            <h3 style={{ margin: "0 0 6px", fontSize: 13, color: "#0a2540", borderBottom: "1px solid #e5e1d8", paddingBottom: 4 }}>{cName} ({grouped[cName].length})</h3>
            <div style={S.card}>{tbl([{ l: "#" }, { l: "Nombre" }, { l: "Ítems" }, { l: "Subtotal", a: "right" }, { l: "Estado" }],
              grouped[cName].map(c => {
                const tots = (() => { const r = (c.items || []).reduce((a, it) => { const rc = motorCosteo(it, D.insumos); a.sub += rc._netoSinIVA * it.cantidad; return a; }, { sub: 0, iva: 0, total: 0 }); r.iva = Math.round(r.sub * 0.19); r.total = r.sub + r.iva; return r; })();
                const col = c.estado === "Aprobada" ? "green" : c.estado === "No aprobada" ? "red" : c.estado === "Enviada" ? "blue" : "gray";
                return (
                  <tr key={c.id}>
                    <td style={S.td}><strong>{c.numero}</strong><br /><span style={{ fontSize: 10, color: "#9ca3af" }}>{c.fecha}</span></td>
                    <td style={S.td}><strong style={{ color: "#0a2540" }}>{c.nombreCot || "—"}</strong>{c.proyecto && <div style={{ fontSize: 10, color: "#9ca3af" }}>{c.proyecto}</div>}</td>
                    <td style={S.td}>{(c.items || []).length}</td>
                    <td style={{ ...S.td, textAlign: "right" }}><div style={{ fontWeight: 600 }}>{fmt(tots.sub)}</div><div style={{ fontSize: 9, color: "#9ca3af" }}>+IVA {fmt(tots.total)}</div></td>
                    <td style={S.td}><span style={S.badge(col)}>{c.estado}</span>{c.estado === "No aprobada" && c.razonNo && <div style={{ fontSize: 10, color: "#b91c1c" }}>{c.razonNo}</div>}</td>
                    <td style={S.td}><div style={{ display: "flex", gap: 3, flexWrap: "wrap" }}>
                      <button style={S.btn2} onClick={() => { setEdt(c); setModal("cotizador"); }}><Ic name="edit" size={12} /></button>
                      <button style={S.btn2} onClick={() => verPDF(c)}><Ic name="download" size={12} /></button>
                      <button style={{ ...S.btn2, color: "#1e40af" }} onClick={() => openEmail("cot", c)} title="Enviar por correo"><Ic name="send" size={12} /></button>
                      {!["Aprobada"].includes(c.estado) && <button style={S.btnG} onClick={() => { setConvertCot(c); setConvertTipo(TIPOS_NEGOCIO[0]); }}><Ic name="check" size={12} /></button>}
                      <button style={S.btnD} onClick={() => del("cotizaciones", c.id)}><Ic name="trash" size={12} /></button>
                    </div></td>
                  </tr>);
              }), true)}</div>
          </div>
        ))
      }
    </div>);
  };

  // ═══════════════════════════════════════════════════════════════
  // E. PEDIDOS
  // ═══════════════════════════════════════════════════════════════
  const ESTADOS_PED = ["Pendiente", "En producción", "En despacho", "Entregado", "Cancelado"];
  const pedBadge = est => est === "Entregado" ? "green" : est === "En producción" ? "blue" : est === "En despacho" ? "amber" : est === "Cancelado" ? "red" : "gray";
  const PgPed = () => {
    const [filtroEst, setFiltroEst] = useState("todos");
    const peds = filtroEst === "todos" ? D.pedidos : D.pedidos.filter(p => p.estado === filtroEst);
    const hoyMs = new Date().getTime();
    const conteos = ESTADOS_PED.reduce((a, e) => ({ ...a, [e]: D.pedidos.filter(p => p.estado === e).length }), {});
    return (
      <div>
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 14 }}>
          <h2 style={{ margin: 0, color: "#0a2540", fontFamily: "Georgia,serif" }}>Pedidos ({D.pedidos.length})</h2>
        </div>
        <div style={{ display: "flex", gap: 5, marginBottom: 12, flexWrap: "wrap" }}>
          <button style={{ ...S.btn2, fontWeight: filtroEst === "todos" ? 700 : 400, background: filtroEst === "todos" ? "#0a2540" : undefined, color: filtroEst === "todos" ? "#e8d5b7" : undefined }} onClick={() => setFiltroEst("todos")}>Todos ({D.pedidos.length})</button>
          {ESTADOS_PED.map(e => conteos[e] > 0 && <button key={e} style={{ ...S.btn2, fontWeight: filtroEst === e ? 700 : 400, background: filtroEst === e ? "#0a2540" : undefined, color: filtroEst === e ? "#e8d5b7" : undefined }} onClick={() => setFiltroEst(e)}>{e} ({conteos[e]})</button>)}
        </div>
        {peds.length === 0 ? <div style={{ ...S.card, textAlign: "center", padding: 36, color: "#9ca3af" }}>{D.pedidos.length === 0 ? "Aprueba una cotización para generar un pedido." : "No hay pedidos con este filtro."}</div> :
          peds.map(p => {
            const vencido = p.fechaCompromiso && new Date(p.fechaCompromiso).getTime() < hoyMs && p.estado !== "Entregado" && p.estado !== "Cancelado";
            const totalUds = (p.items || []).reduce((s, it) => s + (it.cantidad || 0), 0);
            return (
              <div key={p.id} style={{ ...S.card, borderLeft: `3px solid ${vencido ? "#ef4444" : p.estado === "Entregado" ? "#22c55e" : p.estado === "En producción" ? "#3b82f6" : p.estado === "En despacho" ? "#f59e0b" : "#d1cdc4"}` }}>
                <div style={{ display: "flex", justifyContent: "space-between", marginBottom: 8 }}>
                  <div>
                    <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
                      <h3 style={{ margin: 0, color: "#0a2540", fontSize: 14 }}>{p.nombrePed || p.numero}</h3>
                      <span style={S.badge(pedBadge(p.estado))}>{p.estado}</span>
                      {vencido && <span style={{ ...S.badge("red"), fontSize: 9 }}>VENCIDO</span>}
                    </div>
                    <div style={{ fontSize: 11, color: "#6b7280", marginTop: 2 }}>
                      COT-{p.cotNum} · {p.clienteNombre} · {p.fecha}
                      {p.tipoNegocio && <span style={{ marginLeft: 8, padding: "1px 6px", borderRadius: 4, fontSize: 9, background: "#ede9fe", color: "#5b21b6", fontWeight: 600 }}>{p.tipoNegocio}</span>}
                      {p.fechaCompromiso && <span style={{ marginLeft: 8, color: vencido ? "#b91c1c" : "#065f46", fontWeight: 600 }}>Entrega: {p.fechaCompromiso}</span>}
                      {p.responsable && <span style={{ marginLeft: 8 }}>Resp: {p.responsable}</span>}
                    </div>
                    {p.dirEntrega && <div style={{ fontSize: 10.5, color: "#9ca3af" }}>Entrega: {p.dirEntrega}{p.contactoRecibe ? ` · ${p.contactoRecibe} ${p.telRecibe || ""}` : ""}</div>}
                  </div>
                  <div style={{ display: "flex", gap: 4, alignItems: "flex-start" }}>
                    <button style={S.btn} onClick={() => { setEdt(p); setModal("pedido"); }}><Ic name="edit" size={12} /></button>
                    <button style={{ ...S.btn2, color: "#1e40af" }} onClick={() => openEmail("ped", p)}><Ic name="send" size={12} /></button>
                    <button style={S.btnD} onClick={() => del("pedidos", p.id)}><Ic name="trash" size={12} /></button>
                  </div>
                </div>
                <table style={{ width: "100%", borderCollapse: "collapse" }}>
                  <thead><tr><th style={S.th}>Prenda</th><th style={S.th}>Cant.</th><th style={S.th}>Textil</th><th style={S.th}>Color</th><th style={S.th}>Estampados</th><th style={S.th}>Tallas</th></tr></thead>
                  <tbody>{(p.items || []).map((it, i) => (
                    <tr key={i}>
                      <td style={S.td}><strong>{it.nombreComercial || it.tipoPrenda}</strong></td>
                      <td style={S.td}>{it.cantidad}</td>
                      <td style={S.td}>{it.textilNombre || "—"}</td>
                      <td style={S.td}>{it.color || "—"}</td>
                      <td style={S.td}>{(it.estampados || []).map(e => e.tecnica).join(", ") || "—"}</td>
                      <td style={S.td}>{it.tallasDetalle ? Object.entries(it.tallasDetalle).filter(([, v]) => v > 0).map(([t, v]) => `${t}:${v}`).join(", ") || "Pendiente" : "—"}</td>
                    </tr>
                  ))}</tbody>
                </table>
                <div style={{ display: "flex", justifyContent: "flex-end", gap: 12, marginTop: 6, fontSize: 10.5, color: "#6b7280" }}>
                  <span>{(p.items || []).length} ítem(s)</span>
                  <span>{totalUds} unidades</span>
                  {p.ocCliente && <span>OC: {p.ocCliente}</span>}
                </div>
              </div>
            );
          })
        }
      </div>
    );
  };

  // ═══════════════════════════════════════════════════════════════
  // MASTER PAGE GENERIC
  // ═══════════════════════════════════════════════════════════════
  const MPg = ({ title, type, tpl, cols, rr, extra }) => (
    <div>
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 14 }}>
        <h2 style={{ margin: 0, color: "#0a2540", fontFamily: "Georgia,serif" }}>{title}</h2>
        <div style={{ display: "flex", gap: 6, alignItems: "center" }}>
          <input style={{ ...S.inp, width: 220 }} placeholder={"Buscar " + title.toLowerCase() + "..."} value={search} onChange={e => setSearch(e.target.value)} />
          {extra}
          <button style={S.btn} onClick={() => openNew(type, tpl)}><Ic name="plus" size={14} /> Nuevo</button>
        </div>
      </div>
      <div style={S.card}>{tbl(cols, filt(D[type]).map(rr), true)}</div>
      {type === "textiles" && showBulk && (
        <div style={S.card}>
          <h4 style={{ margin: "0 0 6px", color: "#0a2540", fontSize: 12 }}>Importar Textiles (masivo)</h4>
          <p style={{ fontSize: 11, color: "#7c8594", margin: "0 0 6px" }}>16 columnas: Código, Nombre, Proveedor, Composición, Gramaje, Ancho, Unidad, Precio, IVA%, DctoPP%, DctoVol%, VolMín, DíasAbast, Colores, Ref.Prov, Estado — <strong>Guardar como CSV desde Excel</strong></p>
          <div style={{ display: "flex", gap: 6, marginBottom: 6, flexWrap: "wrap" }}>
            <button style={S.btn2} onClick={() => setBulkText("TX-001;Nombre textil;Proveedor;Composición;150;150;Metro;10000;19;5;0;0;5;Colores;Ref;Activo")}><Ic name="download" size={12} /> Ver formato ejemplo</button>
            <label style={{ ...S.btn2, cursor: "pointer", display: "inline-flex", alignItems: "center", gap: 4 }}><Ic name="download" size={12} /> Cargar CSV<input type="file" accept=".csv,.tsv,.txt,.xls" style={{ display: "none" }} onChange={e => { const f = e.target.files[0]; if (!f) return; if (f.name.endsWith(".xlsx")) { setBulkMsg("Archivo .xlsx no soportado. Guárdalo como CSV."); setTimeout(() => setBulkMsg(""), 5000); return; } const reader = new FileReader(); reader.onload = ev => { const txt = ev.target.result.replace(/^\uFEFF/, ""); const lines = txt.split(/\r?\n/).filter(l => l.trim()); setBulkText(lines.slice(1).join("\n")); }; reader.readAsText(f, "utf-8"); e.target.value = ""; }} /></label>
          </div>
          {bulkMsg && <div style={{ background: "#fef2f2", border: "1px solid #fca5a5", borderRadius: 5, padding: "6px 10px", fontSize: 11, color: "#991b1b", marginBottom: 6 }}>{bulkMsg}</div>}
          <textarea style={{ ...S.inp, minHeight: 100, fontFamily: "monospace", fontSize: 11 }} value={bulkText} onChange={e => setBulkText(e.target.value)} placeholder={"TX-010\tDry-Fit Premium\tTextiles del Sur\tMetro\t18500\t5"} />
          <div style={{ display: "flex", gap: 6, marginTop: 6, alignItems: "center" }}>
            <button style={S.btnG} onClick={doBulkImport}><Ic name="check" size={12} /> Importar</button>
            <button style={S.btn2} onClick={() => setShowBulk(false)}>Cancelar</button>
            {bulkText && <span style={{ fontSize: 10, color: "#6b7280" }}>{bulkText.trim().split("\n").length} fila(s)</span>}
          </div>
        </div>
      )}
      {type === "insumos" && showBulkIns && (
        <div style={S.card}>
          <h4 style={{ margin: "0 0 6px", color: "#0a2540", fontSize: 12 }}>Importar Insumos (masivo)</h4>
          <p style={{ fontSize: 11, color: "#7c8594", margin: "0 0 6px" }}>Columnas: Código, Nombre, Categoría, Unidad, Costo, IVA%, Proveedor, Variante — <strong>Guardar como CSV</strong></p>
          <div style={{ display: "flex", gap: 6, marginBottom: 6, flexWrap: "wrap" }}>
            <button style={S.btn2} onClick={() => setBulkTextIns("IN-001;Nombre insumo;Categoría;Unidad;1000;19;Proveedor;Variante")}><Ic name="download" size={12} /> Ver formato ejemplo</button>
            <label style={{ ...S.btn2, cursor: "pointer", display: "inline-flex", alignItems: "center", gap: 4 }}><Ic name="download" size={12} /> Cargar CSV<input type="file" accept=".csv,.tsv,.txt,.xls" style={{ display: "none" }} onChange={e => { const f = e.target.files[0]; if (!f) return; if (f.name.endsWith(".xlsx")) { setBulkMsg("Archivo .xlsx no soportado. Guárdalo como CSV."); setTimeout(() => setBulkMsg(""), 5000); return; } const reader = new FileReader(); reader.onload = ev => { const txt = ev.target.result.replace(/^\uFEFF/, ""); const lines = txt.split(/\r?\n/).filter(l => l.trim()); setBulkTextIns(lines.slice(1).join("\n")); }; reader.readAsText(f, "utf-8"); e.target.value = ""; }} /></label>
          </div>
          {bulkMsg && <div style={{ background: "#fef2f2", border: "1px solid #fca5a5", borderRadius: 5, padding: "6px 10px", fontSize: 11, color: "#991b1b", marginBottom: 6 }}>{bulkMsg}</div>}
          <textarea style={{ ...S.inp, minHeight: 100, fontFamily: "monospace", fontSize: 11 }} value={bulkTextIns} onChange={e => setBulkTextIns(e.target.value)} placeholder={"IN-010\tBotón 4H\tBotones\tInsumos Cali\tUnidad\t250"} />
          <div style={{ display: "flex", gap: 6, marginTop: 6, alignItems: "center" }}>
            <button style={S.btnG} onClick={doBulkImportIns}><Ic name="check" size={12} /> Importar</button>
            <button style={S.btn2} onClick={() => setShowBulkIns(false)}>Cancelar</button>
            {bulkTextIns && <span style={{ fontSize: 10, color: "#6b7280" }}>{bulkTextIns.trim().split("\n").length} fila(s)</span>}
          </div>
        </div>
      )}
      {type === "proveedores" && showBulkProv && (
        <div style={S.card}>
          <h4 style={{ margin: "0 0 6px", color: "#0a2540", fontSize: 12 }}>Importar Proveedores (masivo)</h4>
          <p style={{ fontSize: 11, color: "#7c8594", margin: "0 0 6px" }}>Columnas: Nombre, NIT, Contacto, Dirección, Ciudad, Email, Celular, Tipo — <strong>Guardar como CSV</strong></p>
          <div style={{ display: "flex", gap: 6, marginBottom: 6, flexWrap: "wrap" }}>
            <button style={S.btn2} onClick={() => setBulkTextProv("Textiles del Sur;800111222-3;Carlos Ruiz;Cra 5 # 12-34;Cali;carlos@textiles.com;3151234567;Textil")}><Ic name="download" size={12} /> Ver formato ejemplo</button>
            <label style={{ ...S.btn2, cursor: "pointer", display: "inline-flex", alignItems: "center", gap: 4 }}><Ic name="download" size={12} /> Cargar CSV<input type="file" accept=".csv,.tsv,.txt,.xls" style={{ display: "none" }} onChange={e => { const f = e.target.files[0]; if (!f) return; if (f.name.endsWith(".xlsx")) { setBulkMsg("Archivo .xlsx no soportado. Guárdalo como CSV."); setTimeout(() => setBulkMsg(""), 5000); return; } const reader = new FileReader(); reader.onload = ev => { const txt = ev.target.result.replace(/^\uFEFF/, ""); const lines = txt.split(/\r?\n/).filter(l => l.trim()); setBulkTextProv(lines.slice(1).join("\n")); }; reader.readAsText(f, "utf-8"); e.target.value = ""; }} /></label>
          </div>
          {bulkMsg && <div style={{ background: "#fef2f2", border: "1px solid #fca5a5", borderRadius: 5, padding: "6px 10px", fontSize: 11, color: "#991b1b", marginBottom: 6 }}>{bulkMsg}</div>}
          <textarea style={{ ...S.inp, minHeight: 100, fontFamily: "monospace", fontSize: 11 }} value={bulkTextProv} onChange={e => setBulkTextProv(e.target.value)} placeholder={"Textiles del Sur;800111222;Carlos Ruiz;Cra 5;Cali;carlos@mail.com;3151234567;Textil"} />
          <div style={{ display: "flex", gap: 6, marginTop: 6, alignItems: "center" }}>
            <button style={S.btnG} onClick={doBulkImportProv}><Ic name="check" size={12} /> Importar</button>
            <button style={S.btn2} onClick={() => setShowBulkProv(false)}>Cancelar</button>
            {bulkTextProv && <span style={{ fontSize: 10, color: "#6b7280" }}>{bulkTextProv.trim().split("\n").length} fila(s)</span>}
          </div>
        </div>
      )}
    </div>
  );
  // ═══════════════════════════════════════════════════════════════
  // B. MASTER CONFIGS
  // ═══════════════════════════════════════════════════════════════
  const mc = {
    textiles: { title: "Textiles", tpl: { codigo: "", nombre: "", proveedor: "", composicion: "", gramaje: 0, ancho: 150, unidad: "Metro", precio: 0, iva: 19, dctoPP: 0, dctoVol: 0, volMin: 0, diasAbast: 5, colores: "", ref: "", obs: "", estado: "Activo" }, cols: [{ l: "Código" }, { l: "Nombre" }, { l: "Precio", a: "right" }, { l: "Estado" }], rr: t => <tr key={t.id}><td style={S.td}><strong>{t.codigo}</strong></td><td style={S.td}>{t.nombre}<br /><span style={{ fontSize: 10, color: "#9ca3af" }}>{t.proveedor} · {t.gramaje}g/m² · {t.ancho}cm</span></td><td style={{ ...S.td, textAlign: "right", fontWeight: 600 }}>{fmt(t.precio)}</td><td style={S.td}><span style={S.badge(t.estado === "Activo" ? "green" : "red")}>{t.estado}</span></td><td style={S.td}><div style={{ display: "flex", gap: 3 }}><button style={S.btn2} onClick={() => openEd("textiles", t)}><Ic name="edit" size={12} /></button><button style={S.btnD} onClick={() => del("textiles", t.id)}><Ic name="trash" size={12} /></button></div></td></tr> },
    insumos: { title: "Insumos", tpl: { codigo: "", nombre: "", cat: "General", unidad: "Unidad", costo: 0, iva: 19, proveedor: "", consumo: 1, variante: "", obs: "" }, cols: [{ l: "Código" }, { l: "Nombre" }, { l: "Categoría" }, { l: "Costo", a: "right" }, { l: "Proveedor" }], rr: i => <tr key={i.id}><td style={S.td}><strong>{i.codigo}</strong></td><td style={S.td}>{i.nombre}</td><td style={S.td}><span style={S.badge("blue")}>{i.cat}</span></td><td style={{ ...S.td, textAlign: "right" }}>{fmt(i.costo)}/{i.unidad}</td><td style={S.td}>{i.proveedor || "—"}</td><td style={S.td}><div style={{ display: "flex", gap: 3 }}><button style={S.btn2} onClick={() => openEd("insumos", i)}><Ic name="edit" size={12} /></button><button style={S.btnD} onClick={() => del("insumos", i.id)}><Ic name="trash" size={12} /></button></div></td></tr> },
    tecnicas: { title: "Técnicas de Estampado", type: "tecnicas", tpl: { nombre: "", unidadCobro: "cm\u00b2", costoBase: 0, proveedor: "", desc: "", estado: "Activo" }, cols: [{ l: "Nombre" }, { l: "Unidad" }, { l: "Costo Base", a: "right" }, { l: "Proveedor" }, { l: "Estado" }], rr: t => <tr key={t.id}><td style={S.td}><strong>{t.nombre}</strong><br /><span style={{ fontSize: 10, color: "#9ca3af" }}>{t.desc}</span></td><td style={S.td}><span style={S.badge("blue")}>{t.unidadCobro}</span></td><td style={{ ...S.td, textAlign: "right", fontWeight: 600 }}>{fmt(t.costoBase)}/{t.unidadCobro}</td><td style={S.td}>{t.proveedor || "\u2014"}</td><td style={S.td}><span style={S.badge(t.estado === "Activo" ? "green" : "red")}>{t.estado}</span></td><td style={S.td}><div style={{ display: "flex", gap: 3 }}><button style={S.btn2} onClick={() => openEd("tecnicas", t)}><Ic name="edit" size={12} /></button><button style={S.btnD} onClick={() => del("tecnicas", t.id)}><Ic name="trash" size={12} /></button></div></td></tr> },
    prendas: { title: "Tipos de Prenda", type: "tiposPrenda", tpl: { codigo: "", nombre: "", cat: "Superior", desc: "", consumoDefault: 0.65, insumosDefault: [], procesosDefault: { corte: 1500, confeccion: 6500, empaque: 800 }, estado: "Activo" }, cols: [{ l: "Código" }, { l: "Nombre" }, { l: "Cat." }, { l: "Consumo", a: "right" }, { l: "Estado" }], rr: p => <tr key={p.id}><td style={S.td}><strong>{p.codigo}</strong></td><td style={S.td}>{p.nombre}<br /><span style={{ fontSize: 10, color: "#9ca3af" }}>{p.desc}</span></td><td style={S.td}><span style={S.badge("blue")}>{p.cat}</span></td><td style={{ ...S.td, textAlign: "right" }}>{p.consumoDefault}m</td><td style={S.td}><span style={S.badge(p.estado === "Activo" ? "green" : "red")}>{p.estado}</span></td><td style={S.td}><div style={{ display: "flex", gap: 3 }}><button style={S.btn2} onClick={() => openEd("tiposPrenda", p)}><Ic name="edit" size={12} /></button><button style={S.btnD} onClick={() => del("tiposPrenda", p.id)}><Ic name="trash" size={12} /></button></div></td></tr> },
    moldes: { title: "Moldería", tpl: { codigo: "", tipoPrenda: "", version: "v1", patronista: "", escala: "XS-XL", obs: "", estado: "Activo" }, cols: [{ l: "Código" }, { l: "Prenda" }, { l: "Patronista" }, { l: "Escala" }, { l: "Estado" }], rr: m => <tr key={m.id}><td style={S.td}><strong>{m.codigo}</strong></td><td style={S.td}>{m.tipoPrenda}</td><td style={S.td}>{m.patronista}</td><td style={S.td}>{m.escala}</td><td style={S.td}><span style={S.badge(m.estado === "Activo" ? "green" : "red")}>{m.estado}</span></td><td style={S.td}><div style={{ display: "flex", gap: 3 }}><button style={S.btn2} onClick={() => openEd("moldes", m)}><Ic name="edit" size={12} /></button><button style={S.btnD} onClick={() => del("moldes", m.id)}><Ic name="trash" size={12} /></button></div></td></tr> },
    clientes: { title: "Clientes", tpl: { tipo: "Empresa", razon: "", nit: "", comercial: "", contacto: "", correo: "", tel: "", dir: "", ciudad: "Cali", dirFactura: "", dirEntrega: "", condiciones: "", obs: "", estado: "Activo" }, cols: [{ l: "Razón Social" }, { l: "NIT" }, { l: "Contacto" }, { l: "Ciudad" }], rr: c => <tr key={c.id}><td style={S.td}><strong>{c.razon}</strong><br /><span style={{ fontSize: 10, color: "#9ca3af" }}>{c.tipo} · {c.comercial}</span></td><td style={S.td}>{c.nit}</td><td style={S.td}>{c.contacto}<br /><span style={{ fontSize: 10, color: "#6b7280" }}>{c.correo}</span></td><td style={S.td}>{c.ciudad}</td><td style={S.td}><div style={{ display: "flex", gap: 3 }}><button style={S.btn2} onClick={() => openEd("clientes", c)}><Ic name="edit" size={12} /></button><button style={S.btnD} onClick={() => del("clientes", c.id)}><Ic name="trash" size={12} /></button></div></td></tr> },
    proveedores: { title: "Proveedores", tpl: { nombre: "", nit: "", contacto: "", direccion: "", ciudad: "Cali", email: "", celular: "", tipo: "Textil", estado: "Activo" }, cols: [{ l: "Nombre" }, { l: "NIT" }, { l: "Contacto" }, { l: "Celular" }, { l: "Ciudad" }, { l: "Tipo" }], rr: p => <tr key={p.id}><td style={S.td}><strong>{p.nombre}</strong>{p.email && <div style={{ fontSize: 10, color: "#6b7280" }}>{p.email}</div>}</td><td style={S.td}>{p.nit || "—"}</td><td style={S.td}>{p.contacto || "—"}</td><td style={S.td}>{p.celular || "—"}</td><td style={S.td}>{p.ciudad}</td><td style={S.td}><span style={S.badge("blue")}>{p.tipo}</span></td><td style={S.td}><div style={{ display: "flex", gap: 3 }}><button style={S.btn2} onClick={() => openEd("proveedores", p)}><Ic name="edit" size={12} /></button><button style={S.btnD} onClick={() => del("proveedores", p.id)}><Ic name="trash" size={12} /></button></div></td></tr> },
  };

  // ─── Maquila price helpers ───
  const maqTrazo = (provNombre, consumoM, cantidad) => {
    const m = (D.maquila || []).find(x => x.tipo === "Trazo" && x.proveedor === provNombre && x.estado === "Activo");
    return m && cantidad > 0 ? Math.round((m.precioMetro || 0) * (consumoM || 0) * 3 / cantidad) : 0;
  };
  const maqCorte = (provNombre, cantidad) => {
    const m = (D.maquila || []).find(x => x.tipo === "Corte" && x.proveedor === provNombre && x.estado === "Activo");
    if (!m || !m.escalas) return 0;
    const esc = m.escalas.find(e => cantidad >= e.desde && cantidad <= e.hasta) || m.escalas[m.escalas.length - 1];
    return esc ? esc.precio : 0;
  };
  const maqConfeccion = (provNombre, prendaNombre) => {
    const m = (D.maquila || []).find(x => x.tipo === "Confección" && x.proveedor === provNombre && x.estado === "Activo");
    if (!m || !m.precios) return 0;
    const p = m.precios.find(x => x.prenda === prendaNombre);
    return p ? p.precio : 0;
  };
  const maqProvs = (tipo) => [...new Set((D.maquila || []).filter(x => x.tipo === tipo && x.estado === "Activo").map(x => x.proveedor))];

  // ─── Maquila Page ───
  const PgMaquila = () => {
    const [editMq, setEditMq] = useState(null);
    const provNames = (D.proveedores || []).map(p => p.nombre);
    const prendaNames = (D.tiposPrenda || []).map(p => p.nombre);
    const tiposMaq = ["Trazo", "Corte", "Confección"];

    const newMaq = (tipo) => {
      const base = { id: nid(D.maquila || []), tipo, proveedor: "", obs: "", estado: "Activo" };
      if (tipo === "Trazo") base.precioMetro = 0;
      else if (tipo === "Corte") base.escalas = [{ desde: 1, hasta: 100, precio: 0 }];
      else if (tipo === "Confección") base.precios = prendaNames.map(p => ({ prenda: p, precio: 0 }));
      up("maquila", [...(D.maquila || []), base]);
      setEditMq(base);
    };

    return (
      <div>
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 14 }}>
          <h2 style={{ margin: 0, color: "#0a2540", fontFamily: "Georgia,serif", fontSize: 18 }}>Servicios de Maquila</h2>
          <div style={{ display: "flex", gap: 4 }}>
            {tiposMaq.map(t => <button key={t} style={S.btn} onClick={() => newMaq(t)}><Ic name="plus" size={13} /> {t}</button>)}
          </div>
        </div>
        {tiposMaq.map(tipo => {
          const items = (D.maquila || []).filter(m => m.tipo === tipo);
          if (items.length === 0) return null;
          return (
            <div key={tipo}>
              <h3 style={{ margin: "14px 0 8px", color: "#0a2540", fontSize: 14, borderBottom: "1px solid #e5e1d8", paddingBottom: 4 }}>{tipo}</h3>
              {items.map(mq => {
                const isE = editMq && editMq.id === mq.id;
                const m = isE ? editMq : mq;
                return (
                  <div key={mq.id} style={{ ...S.card, borderLeft: isE ? "3px solid #0a2540" : "" }}>
                    <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 6 }}>
                      {isE ? (
                        <div style={{ ...S.g2, flex: 1, marginRight: 10 }}>
                          <F l="Proveedor"><select style={S.sel} value={m.proveedor} onChange={e => setEditMq({ ...m, proveedor: e.target.value })}><option value="">—</option>{provNames.map(p => <option key={p}>{p}</option>)}</select></F>
                          <F l="Estado"><select style={S.sel} value={m.estado} onChange={e => setEditMq({ ...m, estado: e.target.value })}><option>Activo</option><option>Inactivo</option></select></F>
                        </div>
                      ) : (
                        <div><strong>{mq.proveedor || "Sin proveedor"}</strong> <span style={S.badge(mq.estado === "Activo" ? "green" : "red")}>{mq.estado}</span></div>
                      )}
                      <div style={{ display: "flex", gap: 4 }}>
                        {isE ? <button style={S.btnG} onClick={() => { up("maquila", D.maquila.map(x => x.id === m.id ? m : x)); setEditMq(null); }}><Ic name="check" size={12} /></button> : <button style={S.btn2} onClick={() => setEditMq({ ...mq, escalas: mq.escalas ? [...mq.escalas] : undefined, precios: mq.precios ? mq.precios.map(p => ({ ...p })) : undefined })}><Ic name="edit" size={12} /></button>}
                        <button style={S.btnD} onClick={() => { up("maquila", D.maquila.filter(x => x.id !== mq.id)); if (isE) setEditMq(null); }}><Ic name="trash" size={12} /></button>
                      </div>
                    </div>

                    {/* Trazo: precio por metro */}
                    {tipo === "Trazo" && (
                      isE ? <F l="Precio por metro"><input style={S.inp} type="number" value={m.precioMetro || 0} onChange={e => setEditMq({ ...m, precioMetro: parseFloat(e.target.value) || 0 })} /></F>
                        : <div style={{ fontSize: 12 }}>Precio: <strong>{fmt(mq.precioMetro)}/metro</strong></div>
                    )}

                    {/* Corte: escalas por cantidad */}
                    {tipo === "Corte" && (<>
                      <table style={{ width: "100%", borderCollapse: "collapse", fontSize: 12 }}>
                        <thead><tr><th style={S.th}>Desde</th><th style={S.th}>Hasta</th><th style={{ ...S.th, textAlign: "right" }}>$/ud</th>{isE && <th style={S.th}></th>}</tr></thead>
                        <tbody>{(m.escalas || []).map((esc, ei) => (
                          <tr key={ei}>{isE ? (<>
                            <td style={S.td}><input style={S.inp} type="number" value={esc.desde} onChange={e => { const es = [...m.escalas]; es[ei] = { ...es[ei], desde: parseInt(e.target.value) || 0 }; setEditMq({ ...m, escalas: es }); }} /></td>
                            <td style={S.td}><input style={S.inp} type="number" value={esc.hasta} onChange={e => { const es = [...m.escalas]; es[ei] = { ...es[ei], hasta: parseInt(e.target.value) || 0 }; setEditMq({ ...m, escalas: es }); }} /></td>
                            <td style={S.td}><input style={S.inp} type="number" value={esc.precio} onChange={e => { const es = [...m.escalas]; es[ei] = { ...es[ei], precio: parseFloat(e.target.value) || 0 }; setEditMq({ ...m, escalas: es }); }} /></td>
                            <td style={S.td}><button style={{ ...S.btnD, padding: "1px 6px" }} onClick={() => setEditMq({ ...m, escalas: m.escalas.filter((_, j) => j !== ei) })}>✕</button></td>
                          </>) : (<>
                            <td style={S.td}>{fmtN(esc.desde)}</td><td style={S.td}>{fmtN(esc.hasta)}</td><td style={{ ...S.td, textAlign: "right", fontWeight: 600 }}>{fmt(esc.precio)}</td>
                          </>)}</tr>
                        ))}</tbody>
                      </table>
                      {isE && <button style={{ ...S.btn2, marginTop: 4 }} onClick={() => setEditMq({ ...m, escalas: [...m.escalas, { desde: (m.escalas.length > 0 ? m.escalas[m.escalas.length - 1].hasta + 1 : 1), hasta: 9999, precio: 0 }] })}><Ic name="plus" size={11} /> Escala</button>}
                    </>)}

                    {/* Confección: precio por tipo de prenda */}
                    {tipo === "Confección" && (<>
                      <table style={{ width: "100%", borderCollapse: "collapse", fontSize: 12 }}>
                        <thead><tr><th style={S.th}>Tipo de Prenda</th><th style={{ ...S.th, textAlign: "right" }}>Precio/ud</th></tr></thead>
                        <tbody>{(m.precios || []).map((pr, pi) => (
                          <tr key={pi}>
                            <td style={S.td}>{pr.prenda}</td>
                            <td style={S.td}>{isE ? <input style={{ ...S.inp, textAlign: "right" }} type="number" value={pr.precio} onChange={e => { const ps = m.precios.map((p, j) => j === pi ? { ...p, precio: parseFloat(e.target.value) || 0 } : p); setEditMq({ ...m, precios: ps }); }} /> : <strong style={{ float: "right" }}>{fmt(pr.precio)}</strong>}</td>
                          </tr>
                        ))}</tbody>
                      </table>
                      {isE && <div style={{ display: "flex", gap: 4, marginTop: 4 }}><select style={{ ...S.sel, width: "auto" }} value="" onChange={e => { if (e.target.value) setEditMq({ ...m, precios: [...m.precios, { prenda: e.target.value, precio: 0 }] }); e.target.value = ""; }}><option value="">+ Agregar prenda...</option>{(D.tiposPrenda || []).filter(tp => !(m.precios || []).some(pr => pr.prenda === tp.nombre)).map(tp => <option key={tp.id} value={tp.nombre}>{tp.nombre}</option>)}</select></div>}
                    </>)}
                  </div>
                );
              })}
            </div>
          );
        })}
      </div>
    );
  };

  // ─── Textiles Bulk Import ───
  const [showBulk, setShowBulk] = useState(false);
  const [bulkText, setBulkText] = useState("");
  const [bulkMsg, setBulkMsg] = useState("");
  const splitLine = (line) => { const l = line.replace(/^\uFEFF/, ""); if (l.split("\t").length > 1) return l.split("\t"); if (l.split(";").length > 1) return l.split(";"); return l.split(","); };
  const doBulkImport = () => {
    const lines = bulkText.trim().split("\n").filter(l => l.trim() && !l.startsWith("\u0000"));
    const nuevos = []; let nextCode = nid(D.textiles);
    const p = (s) => (s || "").trim();
    const n = (s) => parseFloat((s || "0").trim().replace(/[^0-9.]/g, "")) || 0;
    lines.forEach(line => {
      const c = splitLine(line);
      if (c.length >= 2 && p(c[1])) {
        nuevos.push({ id: nextCode++, codigo: p(c[0]) || `TX-${String(nextCode).padStart(3,"0")}`, nombre: p(c[1]), proveedor: p(c[2]), composicion: p(c[3]), gramaje: n(c[4]), ancho: n(c[5]) || 150, unidad: p(c[6]) || "Metro", precio: n(c[7]), iva: n(c[8]) || 19, dctoPP: n(c[9]), dctoVol: n(c[10]), volMin: n(c[11]), diasAbast: n(c[12]) || 5, colores: p(c[13]), ref: p(c[14]), estado: p(c[15]) || "Activo", obs: "" });
      }
    });
    if (nuevos.length > 0) {
      up("textiles", [...D.textiles, ...nuevos]);
      setBulkText(""); setShowBulk(false);
      setSaved(`✓ ${nuevos.length} textiles importados`); setTimeout(() => setSaved(""), 3000);
    } else { setBulkMsg("No se detectaron datos válidos. Verifica el formato."); setTimeout(() => setBulkMsg(""), 4000); }
  };

  // ─── Insumos Bulk Import ───
  const [showBulkIns, setShowBulkIns] = useState(false);
  const [bulkTextIns, setBulkTextIns] = useState("");
  const doBulkImportIns = () => {
    const lines = bulkTextIns.trim().split("\n").filter(l => l.trim() && !l.startsWith("\u0000"));
    const nuevos = []; let nextCode = nid(D.insumos);
    const p = (s) => (s || "").trim();
    const n = (s) => parseFloat((s || "0").trim().replace(/[^0-9.]/g, "")) || 0;
    lines.forEach(line => {
      const c = splitLine(line);
      if (c.length >= 2 && p(c[1])) {
        nuevos.push({ id: nextCode++, codigo: p(c[0]) || `IN-${String(nextCode).padStart(3, "0")}`, nombre: p(c[1]), cat: p(c[2]) || "General", unidad: p(c[3]) || "Unidad", costo: n(c[4]), iva: n(c[5]) || 19, proveedor: p(c[6]), variante: p(c[7]), consumo: 1, obs: "", estado: "Activo" });
      }
    });
    if (nuevos.length > 0) {
      up("insumos", [...D.insumos, ...nuevos]);
      setBulkTextIns(""); setShowBulkIns(false);
      setSaved(`✓ ${nuevos.length} insumos importados`); setTimeout(() => setSaved(""), 3000);
    } else { setBulkMsg("No se detectaron datos válidos. Verifica el formato."); setTimeout(() => setBulkMsg(""), 4000); }
  };

  // ─── Proveedores Bulk Import ───
  const [showBulkProv, setShowBulkProv] = useState(false);
  const [bulkTextProv, setBulkTextProv] = useState("");
  const doBulkImportProv = () => {
    const lines = bulkTextProv.trim().split("\n").filter(l => l.trim() && !l.startsWith("\u0000"));
    const nuevos = []; let nextCode = nid(D.proveedores);
    const p = (s) => (s || "").trim();
    lines.forEach(line => {
      const c = splitLine(line);
      if (c.length >= 1 && p(c[0])) {
        nuevos.push({ id: nextCode++, nombre: p(c[0]), nit: p(c[1]), contacto: p(c[2]), direccion: p(c[3]), ciudad: p(c[4]) || "Cali", email: p(c[5]), celular: p(c[6]), tipo: p(c[7]) || "Textil", estado: "Activo" });
      }
    });
    if (nuevos.length > 0) {
      up("proveedores", [...D.proveedores, ...nuevos]);
      setBulkTextProv(""); setShowBulkProv(false);
      setSaved(`✓ ${nuevos.length} proveedores importados`); setTimeout(() => setSaved(""), 3000);
    } else { setBulkMsg("No se detectaron datos válidos. Verifica el formato."); setTimeout(() => setBulkMsg(""), 4000); }
  };

  // Plantillas Cargos page
  const PgPl = () => {
    const [ep, setEp] = useState(null);
    const [newPago, setNewPago] = useState("");
    return (
      <div>
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 14 }}><h2 style={{ margin: 0, color: "#0a2540", fontFamily: "Georgia,serif" }}>Plantillas de Cargos</h2><button style={S.btn} onClick={() => { const np = { id: nid(D.plantillasCargos), nombre: "Nueva plantilla", cargos: [] }; up("plantillasCargos", [...D.plantillasCargos, np]); setEp(np); }}><Ic name="plus" size={14} /> Nueva</button></div>
        {D.plantillasCargos.map(pl => (
          <div key={pl.id} style={{ ...S.card, borderLeft: ep && ep.id === pl.id ? "3px solid #0a2540" : "" }}>
            <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 6 }}>
              {ep && ep.id === pl.id ? <input style={{ ...S.inp, fontWeight: 700, fontSize: 13, width: 280 }} value={ep.nombre} onChange={e => setEp({ ...ep, nombre: e.target.value })} /> : <h4 style={{ margin: 0, color: "#0a2540", fontSize: 13 }}>{pl.nombre}</h4>}
              <div style={{ display: "flex", gap: 4 }}>
                {ep && ep.id === pl.id ? <button style={S.btnG} onClick={() => { up("plantillasCargos", D.plantillasCargos.map(p => p.id === ep.id ? ep : p)); setEp(null); }}><Ic name="check" size={12} /></button> : <button style={S.btn2} onClick={() => setEp({ ...pl })}><Ic name="edit" size={12} /></button>}
                <button style={S.btnD} onClick={() => { up("plantillasCargos", D.plantillasCargos.filter(p => p.id !== pl.id)); if (ep && ep.id === pl.id) setEp(null); }}><Ic name="trash" size={12} /></button>
              </div>
            </div>
            {ep && ep.id === pl.id ? <div>{ep.cargos.map((c, i) => <div key={i} style={{ display: "flex", gap: 6, alignItems: "center", marginBottom: 4 }}><input style={{ ...S.inp, flex: 2 }} value={c.nombre} onChange={e => { const cg = [...ep.cargos]; cg[i] = { ...cg[i], nombre: e.target.value }; setEp({ ...ep, cargos: cg }); }} placeholder="Nombre" /><select style={{ ...S.sel, flex: 1 }} value={c.modo} onChange={e => { const cg = [...ep.cargos]; cg[i] = { ...cg[i], modo: e.target.value }; setEp({ ...ep, cargos: cg }); }}><option value="porcentaje">%</option><option value="fijo">$ Fijo</option></select><input style={{ ...S.inp, flex: 1 }} type="number" value={c.valor} onChange={e => { const cg = [...ep.cargos]; cg[i] = { ...cg[i], valor: parseFloat(e.target.value) || 0 }; setEp({ ...ep, cargos: cg }); }} /><button style={S.btnD} onClick={() => setEp({ ...ep, cargos: ep.cargos.filter((_, j) => j !== i) })}>✕</button></div>)}<button style={S.btn2} onClick={() => setEp({ ...ep, cargos: [...ep.cargos, { nombre: "", modo: "porcentaje", valor: 0 }] })}><Ic name="plus" size={11} /> Cargo</button></div> : <div style={{ fontSize: 11, color: "#6b7280" }}>{pl.cargos.length === 0 ? "Sin cargos" : pl.cargos.map(c => `${c.nombre}: ${c.modo === "porcentaje" ? c.valor + "%" : fmt(c.valor)}`).join(" · ")}</div>}
          </div>))}
        <div style={{ ...S.card, background: "#faf9f7" }}>
          <h4 style={{ margin: "0 0 6px", fontSize: 11, color: "#0a2540" }}>Métodos de Pago</h4>
          <div style={{ display: "flex", flexWrap: "wrap", gap: 5 }}>
            {(D.metodosPago || []).map((m, i) => <div key={i} style={{ display: "flex", alignItems: "center", gap: 3, background: "#fff", border: "1px solid #d1cdc4", borderRadius: 5, padding: "3px 8px", fontSize: 11 }}>{m}<button style={{ background: "none", border: "none", cursor: "pointer", color: "#b91c1c", fontSize: 13, padding: 0 }} onClick={() => up("metodosPago", D.metodosPago.filter((_, j) => j !== i))}>✕</button></div>)}
            <button style={S.btn2} onClick={() => { const v = newPago.trim(); if (v) { up("metodosPago", [...(D.metodosPago || []), v]); setNewPago(""); } }}><Ic name="plus" size={11} /> Agregar</button>
            <input style={{ ...S.inp, width: 180 }} value={newPago} onChange={e => setNewPago(e.target.value)} placeholder="Nuevo método de pago" onKeyDown={e => { if (e.key === "Enter" && newPago.trim()) { up("metodosPago", [...(D.metodosPago || []), newPago.trim()]); setNewPago(""); } }} />
          </div>
        </div>
      </div>
    );
  };

  // ═══════════════════════════════════════════════════════════════
  // CATÁLOGO DE PRODUCTOS
  // ═══════════════════════════════════════════════════════════════
  const PgCatalogo = () => (
    <div>
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 14 }}>
        <h2 style={{ margin: 0, color: "#0a2540", fontFamily: "Georgia,serif" }}>Catálogo de Productos</h2>
      </div>
      {(D.catalogo || []).length === 0 ? <div style={{ ...S.card, textAlign: "center", padding: 36, color: "#9ca3af" }}>Aún no hay productos. Guarda ítems desde el cotizador con el botón "★ Guardar en Catálogo".</div> :
        <div style={S.card}>{tbl([{ l: "Producto" }, { l: "Tipo" }, { l: "Textil" }, { l: "Estampados" }, { l: "Costo", a: "right" }],
          (D.catalogo || []).map(p => {
            const rc = motorCosteo(p, D.insumos);
            return (
              <tr key={p.id}>
                <td style={S.td}><strong>{p.nombreComercial || p.tipoPrenda}</strong>{p.desc ? <br /> : ""}{p.desc && <span style={{ fontSize: 10, color: "#9ca3af" }}>{p.desc}</span>}</td>
                <td style={S.td}>{p.tipoPrenda}</td>
                <td style={S.td}>{p.textilNombre || "—"}</td>
                <td style={S.td}><span style={{ fontSize: 10 }}>{(p.estampados || []).map(e => e.tecnica).join(", ") || "—"}</span></td>
                <td style={{ ...S.td, textAlign: "right" }}>{fmt(rc._subDirecto)}</td>
                <td style={S.td}><button style={S.btnD} onClick={() => up("catalogo", D.catalogo.filter(x => x.id !== p.id))}><Ic name="trash" size={12} /></button></td>
              </tr>);
          }), true)}</div>
      }
    </div>
  );

  // ═══════════════════════════════════════════════════════════════
  // C. COTIZADOR MODAL
  // ═══════════════════════════════════════════════════════════════
  const MCot = () => {
    const [c, setC] = useState(edt);
    const [itab, setItab] = useState(0);
    const [qa, setQa] = useState(null);
    const [showImport, setShowImport] = useState(null); // "catalogo" | "cotizacion" | null
    const u = (k, v) => setC(x => ({ ...x, [k]: v }));

    const getTecs = () => (D.tecnicas || []).filter(t => t.estado === "Activo");
    const getTecInfo = n => (D.tecnicas || []).find(x => x.nombre === n) || {};

    // ─── Import from catalog or previous cotización ───
    const addFromCatalog = (prod) => {
      setC(prev => {
        const items = [...(prev.items || []), { ...prod, _id: uid(), cantidad: 1 }];
        setItab(items.length - 1); setShowImport(null);
        return { ...prev, items };
      });
    };
    const addFromCot = (cotId, itemIdx) => {
      const cot = D.cotizaciones.find(x => x.id === cotId);
      if (!cot || !cot.items[itemIdx]) return;
      const src = cot.items[itemIdx];
      setC(prev => {
        const items = [...(prev.items || []), { ...src, _id: uid(), cantidad: src.cantidad }];
        setItab(items.length - 1); setShowImport(null);
        return { ...prev, items };
      });
    };
    const saveToCatalog = (idx) => {
      const it = c.items[idx];
      const { _id, cantidad, ...rest } = it;
      const prod = { ...rest, id: nid(D.catalogo || []) };
      setD(d => ({ ...d, catalogo: [...(d.catalogo || []), prod] }));
      setSaved("✓ Guardado en catálogo"); setTimeout(() => setSaved(""), 3000);
    };

    // ─── Quick-add helpers: create in master + select (use setD to avoid state conflicts) ───
    const qaCliente = () => setQa({ type: "cliente", data: { razon: "", nit: "", contacto: "", correo: "", tel: "", ciudad: "Cali" } });
    const qaSaveCliente = () => { const cl = { ...qa.data, id: nid(D.clientes), tipo: "Empresa", comercial: qa.data.razon, dir: "", dirFactura: "", dirEntrega: "", condiciones: "", obs: "", estado: "Activo" }; const num = genCotNum(cl.id, cl.razon); setQa(null); setD(d => ({ ...d, clientes: [...d.clientes, cl] })); setC(prev => ({ ...prev, clienteId: cl.id, clienteNombre: cl.razon, numero: num })); };
    const qaPrenda = () => setQa({ type: "prenda", data: { nombre: "", consumoDefault: 0.65 } });
    const qaSavePrenda = (idx) => { const tp = { id: nid(D.tiposPrenda), codigo: "TP-" + String(D.tiposPrenda.length + 1).padStart(3, "0"), nombre: qa.data.nombre, cat: "Superior", desc: "", consumoDefault: qa.data.consumoDefault, insumosDefault: [], procesosDefault: { corte: 1500, confeccion: 6500, empaque: 800 }, estado: "Activo" }; setQa(null); setD(d => ({ ...d, tiposPrenda: [...d.tiposPrenda, tp] })); if (idx !== undefined) urc(idx, { tipoPrenda: tp.nombre, consumoTextil: tp.consumoDefault }); };
    const qaTextil = () => setQa({ type: "textil", data: { nombre: "", precio: 0, proveedor: "" } });
    const qaSaveTextil = (idx) => { const tx = { id: nid(D.textiles), codigo: "TX-" + String(D.textiles.length + 1).padStart(3, "0"), nombre: qa.data.nombre, proveedor: qa.data.proveedor, precio: qa.data.precio, composicion: "", gramaje: 0, ancho: 150, unidad: "Metro", iva: 19, dctoPP: 0, dctoVol: 0, volMin: 0, diasAbast: 5, colores: "", ref: "", obs: "", estado: "Activo" }; setQa(null); setD(d => ({ ...d, textiles: [...d.textiles, tx] })); if (idx !== undefined) urc(idx, { textilId: tx.id, textilNombre: tx.nombre, precioTextil: tx.precio, dctoTextil: 0 }); };
    const qaMaquila = () => setQa({ type: "maquila", data: { tipo: "Trazo", proveedor: "", precioMetro: 0 } });
    const qaSaveMaquila = () => { const mq = { id: nid(D.maquila || []), tipo: qa.data.tipo, proveedor: qa.data.proveedor, precioMetro: qa.data.precioMetro || 0, escalas: qa.data.tipo === "Corte" ? [{ desde: 1, hasta: 9999, precio: 0 }] : undefined, precios: qa.data.tipo === "Confección" ? [] : undefined, obs: "", estado: "Activo" }; setQa(null); setD(d => ({ ...d, maquila: [...(d.maquila || []), mq] })); };

    // C.2 Constructor de ítems — auto-fill consumo + costos desde tipo prenda
    const addItem = () => {
      setC(prev => {
        const newItem = { _id: uid(), tipoPrenda: "", nombreComercial: "", desc: "", cantidad: 1, textilId: null, textilNombre: "", consumoTextil: 0.65, precioTextil: 0, dctoTextil: 0, insumosItems: [], estampados: [], costoCorte: 1500, costoConfeccion: 6500, costoEmpaque: 800, costoOtros: 0, margenBruto: prev.margenGlobal || 35, ivaRate: 19, cargos: [], empaque: "Individual" };
        const items = [...(prev.items || []), newItem];
        setItab(items.length - 1);
        return { ...prev, items };
      });
    };
    const urc = (idx, ch) => { setC(prev => { const items = [...prev.items]; items[idx] = motorCosteo({ ...items[idx], ...ch }, D.insumos); return { ...prev, items }; }); };
    const selTipo = (idx, nombre) => {
      const tp = D.tiposPrenda.find(x => x.nombre === nombre);
      if (tp) urc(idx, { tipoPrenda: nombre, consumoTextil: tp.consumoDefault || 0.65, insumosItems: (tp.insumosDefault || []).map(id => ({ id, qty: 1 })), costoCorte: (tp.procesosDefault || {}).corte || 1500, costoConfeccion: (tp.procesosDefault || {}).confeccion || 6500, costoEmpaque: (tp.procesosDefault || {}).empaque || 800 });
      else urc(idx, { tipoPrenda: nombre });
    };
    const selTex = (idx, tid) => { const t = D.textiles.find(x => x.id === tid); if (t) urc(idx, { textilId: t.id, textilNombre: t.nombre, precioTextil: t.precio, dctoTextil: t.dctoPP }); };
    const addEst = idx => { const tecs = getTecs(); const f = tecs[0] || { nombre: "DTF", costoBase: 35, unidadCobro: "cm\u00b2", proveedor: "" }; const est = [...(c.items[idx].estampados || []), { _id: uid(), tecnica: f.nombre, unidadCobro: f.unidadCobro || "cm\u00b2", costoUnitario: f.costoBase || 0, proveedor: f.proveedor || "", anchoCm: f.unidadCobro === "cm\u00b2" ? 10 : 0, altoCm: f.unidadCobro === "cm\u00b2" ? 10 : 0, metros: 0, cantidadUds: 1, puntadasMiles: 0, pliegos: 1, ubicacion: "Frente" }]; urc(idx, { estampados: est }); };
    const rmEst = (ii, ei) => urc(ii, { estampados: c.items[ii].estampados.filter((_, i) => i !== ei) });
    const upEst = (ii, ei, ch) => { const est = [...c.items[ii].estampados]; est[ei] = { ...est[ei], ...ch }; if (ch.tecnica) { const ti = getTecInfo(ch.tecnica); est[ei].unidadCobro = ti.unidadCobro || est[ei].unidadCobro; est[ei].costoUnitario = ti.costoBase || est[ei].costoUnitario; est[ei].proveedor = ti.proveedor || est[ei].proveedor; if ((ti.unidadCobro || "").toLowerCase() === "metro") { est[ei].metros = c.items[ii].consumoTextil || 0; } } urc(ii, { estampados: est }); };
    const rmItem = idx => { setC(prev => { const items = prev.items.filter((_, i) => i !== idx); if (itab >= items.length) setItab(Math.max(0, items.length - 1)); return { ...prev, items }; }); };
    const dupItem = idx => { setC(prev => { const src = prev.items[idx]; const items = [...prev.items, { ...src, _id: uid(), estampados: (src.estampados || []).map(e => ({ ...e, _id: uid() })), cargos: (src.cargos || []).map(x => ({ ...x })) }]; setItab(items.length - 1); return { ...prev, items }; }); };
    const addCargo = idx => urc(idx, { cargos: [...(c.items[idx].cargos || []), { nombre: "", modo: "porcentaje", valor: 0 }] });
    const rmCargo = (ii, ci) => urc(ii, { cargos: c.items[ii].cargos.filter((_, i) => i !== ci) });
    const upCargo = (ii, ci, ch) => { const cg = [...c.items[ii].cargos]; cg[ci] = { ...cg[ci], ...ch }; urc(ii, { cargos: cg }); };
    const applyPl = (idx, plId) => { const pl = D.plantillasCargos.find(p => p.id === plId); if (pl) urc(idx, { cargos: pl.cargos.map(x => ({ ...x })) }); };

    const ci = c.items && c.items[itab];
    const tots = (() => { const r = (c.items || []).reduce((a, it) => { const rc = motorCosteo(it, D.insumos); a.n += rc._netoSinIVA * it.cantidad; a.u += rc._utilidad * it.cantidad; return a; }, { n: 0, i: 0, f: 0, u: 0 }); r.i = Math.round(r.n * 0.19); r.f = r.n + r.i; return r; })();

    return (
      <div style={S.modal} onMouseDown={e => { if (e.target === e.currentTarget) saveCot(c); }}>
        <div style={{ ...S.mc, maxWidth: 1100, width: "96%" }} onMouseDown={e => e.stopPropagation()} onClick={e => e.stopPropagation()}>
          {/* Header */}
          <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 14 }}>
            <div>
              <div style={{ fontSize: 10, color: "#9ca3af", marginBottom: 2 }}>Nombre de la cotización (editable)</div>
              <input style={{ ...S.inp, fontWeight: 700, fontSize: 15, width: 380, background: "#faf9f7", padding: "4px 8px", color: "#0a2540" }} value={c.nombreCot || ""} onChange={e => u("nombreCot", e.target.value)} placeholder="Escribe un nombre para esta cotización..." />
              <div style={{ fontSize: 10.5, color: "#9ca3af", marginTop: 2 }}>COT-{c.numero}</div>
            </div>
            <div style={{ display: "flex", gap: 6 }}><button style={S.btn2} onClick={() => verPDF(c)}><Ic name="download" size={13} /> PDF</button><button style={{ ...S.btn2, color: "#1e40af" }} onClick={() => openEmail("cot", c)}><Ic name="send" size={13} /> Correo</button><button style={S.btn} onClick={() => saveCot(c)}><Ic name="check" size={13} /> Guardar</button></div>
          </div>
          {/* C.1 Encabezado */}
          <div style={{ ...S.card, background: "#faf9f7" }}>
            <div style={S.g4}>
              <F l="Fecha"><input style={S.inp} type="date" value={c.fecha} onChange={e => u("fecha", e.target.value)} /></F>
              <F l="Vigencia"><input style={S.inp} value={c.vigencia} onChange={e => u("vigencia", e.target.value)} /></F>
              <F l="Comercial"><input style={S.inp} value={c.comercial} onChange={e => u("comercial", e.target.value)} /></F>
              <F l="Estado"><select style={S.sel} value={c.estado} onChange={e => u("estado", e.target.value)}>{ESTADOS_COT.map(e => <option key={e}>{e}</option>)}</select></F>
            </div>
            {c.estado === "No aprobada" && <div style={{ marginTop: 7, display: "flex", flexWrap: "wrap", gap: 4 }}><span style={{ ...S.lb, marginBottom: 0, marginRight: 4 }}>Razón:</span>{RAZONES_NO.map(r => <button key={r} style={{ ...S.btn2, fontSize: 10.5, padding: "3px 8px", ...(c.razonNo === r ? { background: "#fee2e2", borderColor: "#fca5a5", color: "#991b1b" } : {}) }} onClick={() => u("razonNo", r)}>{r}</button>)}</div>}
            <div style={{ ...S.g3, marginTop: 7 }}>
              <F l="Cliente">
                <div style={{ position: "relative" }}>
                  <input style={S.inp} placeholder="Buscar cliente..." value={c._clienteSearch !== undefined ? c._clienteSearch : (D.clientes.find(x => x.id === c.clienteId)?.razon || c.clienteNombre || "")}
                    onChange={e => { u("_clienteSearch", e.target.value); }}
                    onFocus={e => { if (!c._clienteSearch && c._clienteSearch !== "") u("_clienteSearch", ""); }}
                    onBlur={() => { setTimeout(() => u("_clienteSearch", undefined), 200); }}
                  />
                  {c._clienteSearch !== undefined && (() => {
                    const q = (c._clienteSearch || "").toLowerCase();
                    const matches = D.clientes.filter(x => !q || x.razon.toLowerCase().includes(q) || (x.comercial||"").toLowerCase().includes(q) || (x.nit||"").toLowerCase().includes(q)).slice(0, 12);
                    return (
                      <div style={{ position: "absolute", top: "100%", left: 0, right: 0, background: "#fff", border: "1px solid #d1cdc4", borderRadius: "0 0 5px 5px", maxHeight: 200, overflowY: "auto", zIndex: 50, boxShadow: "0 4px 12px rgba(0,0,0,0.12)" }}>
                        {matches.map(x => <div key={x.id} style={{ padding: "6px 9px", fontSize: 12, cursor: "pointer", borderBottom: "1px solid #f0ece4" }}
                          onMouseDown={() => { const num = genCotNum(x.id, x.razon); u("clienteId", x.id); u("clienteNombre", x.razon); u("numero", num); u("_clienteSearch", undefined); }}>
                          <strong>{x.razon}</strong>{x.comercial ? <span style={{ color: "#9ca3af" }}> · {x.comercial}</span> : ""}{x.nit ? <span style={{ color: "#9ca3af" }}> · {x.nit}</span> : ""}
                        </div>)}
                        <div style={{ padding: "6px 9px", fontSize: 11, cursor: "pointer", color: "#1e40af", fontWeight: 600, background: "#f0f9ff" }}
                          onMouseDown={() => { qaCliente(); u("_clienteSearch", undefined); }}>
                          + Nuevo Cliente...
                        </div>
                      </div>
                    );
                  })()}
                </div>
              </F>
              <F l="Proyecto"><input style={S.inp} value={c.proyecto} onChange={e => u("proyecto", e.target.value)} /></F>
              <F l="Margen bruto global %"><input style={S.inp} type="number" value={c.margenGlobal} onChange={e => u("margenGlobal", parseFloat(e.target.value) || 0)} /></F>
            </div>
            <div style={{ ...S.g4, marginTop: 7 }}>
              <F l="Flete"><select style={S.sel} value={c.flete} onChange={e => u("flete", e.target.value)}><option>No incluido</option><option>Incluido</option></select></F>
              <F l="Método pago"><select style={S.sel} value={c.metodoPago} onChange={e => u("metodoPago", e.target.value)}><option value="">—</option>{(D.metodosPago || []).map(m => <option key={m}>{m}</option>)}</select></F>
              <F l="Días entrega"><select style={S.sel} value={c.diasEntrega} onChange={e => u("diasEntrega", parseInt(e.target.value))}>{DIAS_ENT.map(d => <option key={d} value={d}>{d} días</option>)}</select></F>
              <F l="Garantía"><select style={S.sel} value={c.garantiaMeses} onChange={e => u("garantiaMeses", parseInt(e.target.value))}>{GARANTIA.map(m => <option key={m} value={m}>{m} mes(es)</option>)}</select></F>
            </div>
            <div style={{ ...S.g2, marginTop: 7 }}>
              <F l="Obs. interna"><textarea style={{ ...S.inp, minHeight: 28 }} value={c.obsInt} onChange={e => u("obsInt", e.target.value)} /></F>
              <F l="Obs. externa (PDF)"><textarea style={{ ...S.inp, minHeight: 28 }} value={c.obsExt} onChange={e => u("obsExt", e.target.value)} /></F>
            </div>
          </div>

          {/* C.2 Constructor ítems */}
          <div style={S.card}>
            <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 8 }}>
              <h4 style={{ margin: 0, fontSize: 11, textTransform: "uppercase", letterSpacing: 1, color: "#0a2540" }}>Constructor de Ítems</h4>
              <div style={{ display: "flex", gap: 4 }}>
                <button style={S.btn} onClick={addItem}><Ic name="plus" size={13} /> Nuevo</button>
                <button style={S.btn2} onClick={() => setShowImport(showImport === "catalogo" ? null : "catalogo")}>Catálogo</button>
                <button style={S.btn2} onClick={() => setShowImport(showImport === "cotizacion" ? null : "cotizacion")}>Desde Cotización</button>
              </div>
            </div>
            {/* Import panels */}
            {showImport === "catalogo" && (
              <div style={{ background: "#f0fdf4", border: "1px solid #bbf7d0", borderRadius: 7, padding: 10, marginBottom: 8 }}>
                <div style={{ fontSize: 11, fontWeight: 700, color: "#065f46", marginBottom: 6 }}>Seleccionar producto del catálogo</div>
                {(D.catalogo || []).length === 0 ? <p style={{ fontSize: 11, color: "#9ca3af" }}>Catálogo vacío. Guarda ítems desde el cotizador.</p> :
                  (D.catalogo || []).map(p => <div key={p.id} style={{ display: "flex", justifyContent: "space-between", alignItems: "center", padding: "4px 0", borderBottom: "1px solid #d1fae5", fontSize: 11 }}><span><strong>{p.nombreComercial || p.tipoPrenda}</strong> — {p.textilNombre || "—"} · {(p.estampados || []).length} est.</span><button style={S.btn2} onClick={() => addFromCatalog(p)}>Agregar</button></div>)
                }
              </div>
            )}
            {showImport === "cotizacion" && (
              <div style={{ background: "#eff6ff", border: "1px solid #bfdbfe", borderRadius: 7, padding: 10, marginBottom: 8, maxHeight: 250, overflow: "auto" }}>
                <div style={{ fontSize: 11, fontWeight: 700, color: "#1e40af", marginBottom: 6 }}>Importar ítem desde cotización anterior</div>
                {D.cotizaciones.filter(ct => ct.id !== c.id && (ct.items || []).length > 0).length === 0 ? <p style={{ fontSize: 11, color: "#9ca3af" }}>No hay cotizaciones previas con ítems.</p> :
                  D.cotizaciones.filter(ct => ct.id !== c.id && (ct.items || []).length > 0).map(ct => (
                    <div key={ct.id} style={{ marginBottom: 6 }}>
                      <div style={{ fontSize: 10, fontWeight: 700, color: "#1e40af" }}>{ct.numero} — {ct.clienteNombre || "—"} ({ct.fecha})</div>
                      {ct.items.map((it, ii) => <div key={ii} style={{ display: "flex", justifyContent: "space-between", alignItems: "center", padding: "2px 0 2px 10px", fontSize: 11 }}><span>{it.nombreComercial || it.tipoPrenda} · {it.cantidad}ud · {it.textilNombre || "—"}</span><button style={S.btn2} onClick={() => addFromCot(ct.id, ii)}>Importar</button></div>)}
                    </div>
                  ))
                }
              </div>
            )}
            {(c.items || []).length === 0 ? <p style={{ color: "#9ca3af", textAlign: "center", padding: 18, fontSize: 12 }}>Agrega ítems</p> : (<>
              <div style={{ ...S.tabs, flexWrap: "wrap" }}>{c.items.map((it, i) => <div key={it._id || i} style={S.tab(itab === i)} onClick={() => setItab(i)}>#{i + 1}{it.nombreComercial ? ` ${it.nombreComercial}` : ""}</div>)}</div>
              {ci && (() => { const idx = itab; const it = motorCosteo(ci, D.insumos);
                // Estampado cost helper
                const estCost = e => { const uc = e.unidadCobro || "cm\u00b2"; if (uc === "cm\u00b2") return ((e.anchoCm || 0) * (e.altoCm || 0)) * (e.costoUnitario || 0); if (uc === "metro") return (e.metros || 0) * (e.costoUnitario || 0); if (uc === "unidad") return (e.cantidadUds || 1) * (e.costoUnitario || 0); if (uc === "puntadas(miles)") return (e.puntadasMiles || 0) * (e.costoUnitario || 0); if (uc === "pliego") return (e.pliegos || 1) * (e.costoUnitario || 0); return 0; };
                const estLabel = e => { const uc = e.unidadCobro || "cm\u00b2"; if (uc === "cm\u00b2") return `${((e.anchoCm || 0) * (e.altoCm || 0)).toFixed(1)} cm\u00b2`; if (uc === "metro") return `${e.metros || 0} m`; if (uc === "unidad") return `${e.cantidadUds || 1} ud`; if (uc === "puntadas(miles)") return `${e.puntadasMiles || 0} mil punt.`; if (uc === "pliego") return `${e.pliegos || 1} pliego(s)`; return ""; };
              return (
                <div>
                  <div style={{ display: "flex", gap: 5, marginBottom: 8 }}><button style={S.btn2} onClick={() => dupItem(idx)}><Ic name="copy" size={12} /> Duplicar</button><button style={{ ...S.btn2, color: "#92400e" }} onClick={() => saveToCatalog(idx)}>★ Catálogo</button><button style={S.btnD} onClick={() => rmItem(idx)}><Ic name="trash" size={11} /></button></div>
                  <div style={S.g3}>
                    <F l="Tipo prenda"><select style={S.sel} value={it.tipoPrenda} onChange={e => { if (e.target.value === "_new") { qaPrenda(); return; } selTipo(idx, e.target.value); }}><option value="">—</option>{D.tiposPrenda.filter(x => x.estado === "Activo").map(tp => <option key={tp.id} value={tp.nombre}>{tp.nombre} ({tp.consumoDefault}m)</option>)}<option value="_new">+ Nuevo Tipo...</option></select></F>
                    <F l="Nombre comercial"><input style={S.inp} value={it.nombreComercial} onChange={e => urc(idx, { nombreComercial: e.target.value })} /></F>
                    <F l="Cantidad"><input style={S.inp} type="number" value={it.cantidad} onChange={e => urc(idx, { cantidad: parseInt(e.target.value) || 0 })} /></F>
                  </div>
                  <div style={{ display: "flex", gap: 10, marginTop: 6, marginBottom: 6 }}>
                    <F l="Imagen / Mockup del producto">
                      <input type="file" accept="image/*" onChange={e => { const file = e.target.files[0]; if (!file) return; const reader = new FileReader(); reader.onload = ev => urc(idx, { mockupImg: ev.target.result }); reader.readAsDataURL(file); }} style={{ fontSize: 11 }} />
                    </F>
                    {it.mockupImg && <div style={{ position: "relative" }}>
                      <img src={it.mockupImg} alt="Mockup" style={{ maxWidth: 180, maxHeight: 120, borderRadius: 8, border: "1px solid #e5e1d8", objectFit: "cover" }} />
                      <button style={{ position: "absolute", top: 2, right: 2, background: "#fee2e2", border: "none", borderRadius: "50%", width: 18, height: 18, fontSize: 10, cursor: "pointer", color: "#991b1b", fontWeight: 700, display: "flex", alignItems: "center", justifyContent: "center" }} onClick={() => urc(idx, { mockupImg: null })}>x</button>
                    </div>}
                  </div>

                  {/* B1: Costos directos */}
                  <h5 style={S.secT}>1. Costos Directos</h5>
                  <div style={S.g4}>
                    <F l="Textil"><select style={S.sel} value={it.textilId || ""} onChange={e => { if (e.target.value === "_new") { qaTextil(); return; } selTex(idx, parseInt(e.target.value)); }}><option value="">—</option>{D.textiles.filter(x => x.estado === "Activo").map(t => <option key={t.id} value={t.id}>{t.nombre} — {fmt(t.precio)}/m</option>)}<option value="_new">+ Nuevo Textil...</option></select></F>
                    <F l="Consumo (m)"><input style={S.inp} type="number" step="0.01" value={it.consumoTextil || ""} onChange={e => urc(idx, { consumoTextil: parseFloat(e.target.value) || 0 })} /></F>
                    <F l="Precio/m"><input style={S.inp} type="number" value={it.precioTextil || ""} onChange={e => urc(idx, { precioTextil: parseFloat(e.target.value) || 0 })} /></F>
                    <F l="Dcto %"><input style={S.inp} type="number" value={it.dctoTextil || ""} onChange={e => urc(idx, { dctoTextil: parseFloat(e.target.value) || 0 })} /></F>
                  </div>
                  <F l="Insumos"><div style={{ display: "flex", flexWrap: "wrap", gap: 3, marginTop: 3 }}>{D.insumos.map(ins => { const items = it.insumosItems || it.insumosIds || []; const entry = items.find(x => (typeof x === "object" ? x.id : x) === ins.id); const sel = !!entry; const qty = sel && typeof entry === "object" ? entry.qty : 1; return <div key={ins.id} style={{ display: "flex", alignItems: "center", gap: 2, padding: "2px 7px", borderRadius: 5, background: sel ? "#dbeafe" : "#f3f4f6", fontSize: 10.5 }}><input type="checkbox" checked={sel} onChange={() => { const cur = (it.insumosItems || []).filter(x => (typeof x === "object" ? x.id : x) !== ins.id); if (!sel) cur.push({ id: ins.id, qty: 1 }); urc(idx, { insumosItems: cur }); }} /><span>{ins.nombre} ({fmt(ins.costo)})</span>{sel && <input style={{ ...S.inp, width: 36, textAlign: "center", padding: "1px 2px", marginLeft: 3 }} type="number" min="1" value={qty} onChange={e => { const cur = (it.insumosItems || []).map(x => (typeof x === "object" ? x.id : x) === ins.id ? { id: ins.id, qty: parseInt(e.target.value) || 1 } : x); urc(idx, { insumosItems: cur }); }} />}</div>; })}</div></F>

                  {/* Estampados */}
                  <div style={{ margin: "10px 0 5px", display: "flex", justifyContent: "space-between", alignItems: "center" }}>
                    <span style={{ fontSize: 10.5, fontWeight: 600, color: "#6b7280" }}>ESTAMPADOS ({(it.estampados || []).length})</span>
                    <button style={S.btn2} onClick={() => addEst(idx)}><Ic name="plus" size={11} /> Agregar</button>
                  </div>
                  {(it.estampados || []).map((est, ei) => { const uc = est.unidadCobro || "cm\u00b2"; const cE = estCost(est); return (
                    <div key={est._id || ei} style={{ background: "#fefce8", border: "1px solid #fde68a", borderRadius: 7, padding: 9, marginBottom: 5 }}>
                      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 3 }}>
                        <span style={{ fontSize: 10.5, fontWeight: 700, color: "#92400e" }}>#{ei + 1} · {uc}</span>
                        <button style={{ ...S.btnD, padding: "1px 7px", fontSize: 10 }} onClick={() => rmEst(idx, ei)}>✕</button>
                      </div>
                      <div style={S.g3}>
                        <F l="Técnica"><select style={S.sel} value={est.tecnica} onChange={e => upEst(idx, ei, { tecnica: e.target.value })}>{getTecs().map(t => <option key={t.id} value={t.nombre}>{t.nombre}</option>)}</select></F>
                        <F l="Proveedor"><select style={S.sel} value={est.proveedor || ""} onChange={e => upEst(idx, ei, { proveedor: e.target.value })}><option value="">—</option>{(D.proveedores || []).map(p => <option key={p.id} value={p.nombre}>{p.nombre}</option>)}</select></F>
                        <F l="Ubicación"><input style={S.inp} value={est.ubicacion || ""} onChange={e => upEst(idx, ei, { ubicacion: e.target.value })} placeholder="Frente, Espalda..." /></F>
                      </div>
                      <div style={{ display: "grid", gridTemplateColumns: uc === "cm\u00b2" ? "1fr 1fr 1fr 1fr" : "1fr 1fr 1fr", gap: 7, marginTop: 5 }}>
                        {uc === "cm\u00b2" && <F l="Ancho cm"><input style={S.inp} type="number" step="0.5" value={est.anchoCm || ""} onChange={e => upEst(idx, ei, { anchoCm: parseFloat(e.target.value) || 0 })} /></F>}
                        {uc === "cm\u00b2" && <F l="Alto cm"><input style={S.inp} type="number" step="0.5" value={est.altoCm || ""} onChange={e => upEst(idx, ei, { altoCm: parseFloat(e.target.value) || 0 })} /></F>}
                        {uc === "metro" && <F l="Metros"><input style={S.inp} type="number" step="0.01" value={est.metros || 0} onChange={e => upEst(idx, ei, { metros: parseFloat(e.target.value) || 0 })} /></F>}
                        {uc === "unidad" && <F l="Cantidad"><input style={S.inp} type="number" value={est.cantidadUds || 1} onChange={e => upEst(idx, ei, { cantidadUds: parseInt(e.target.value) || 1 })} /></F>}
                        {uc === "puntadas(miles)" && <F l="Miles punt."><input style={S.inp} type="number" step="0.5" value={est.puntadasMiles || 0} onChange={e => upEst(idx, ei, { puntadasMiles: parseFloat(e.target.value) || 0 })} /></F>}
                        {uc === "pliego" && <F l="Pliegos"><input style={S.inp} type="number" value={est.pliegos || 1} onChange={e => upEst(idx, ei, { pliegos: parseInt(e.target.value) || 1 })} /></F>}
                        <F l={`$/${uc}`}><input style={S.inp} type="number" value={est.costoUnitario || 0} onChange={e => upEst(idx, ei, { costoUnitario: parseFloat(e.target.value) || 0 })} /></F>
                        <F l="Unidad"><select style={S.sel} value={uc} onChange={e => upEst(idx, ei, { unidadCobro: e.target.value })}>{UNIDADES_EST.map(x => <option key={x}>{x}</option>)}</select></F>
                      </div>
                      <div style={{ marginTop: 3, fontSize: 10.5, color: "#6b7280" }}><strong>{estLabel(est)}</strong> × {fmt(est.costoUnitario || 0)}/{uc} = <strong style={{ color: "#0a2540" }}>{fmt(cE)}</strong>/ud{est.proveedor ? ` · ${est.proveedor}` : ""}</div>
                    </div>); })}

                  {/* Maquila / Transformación */}
                  <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}><h5 style={{ ...S.secT, margin: "10px 0 4px" }}>Maquila / Transformación</h5><button style={{ ...S.btn2, fontSize: 10 }} onClick={() => qaMaquila()}>+ Proveedor Maquila</button></div>
                  <div style={S.g3}>
                    <F l="Proveedor Trazo">
                      <select style={S.sel} value={it._provTrazo || ""} onChange={e => { const prov = e.target.value; const precio = maqTrazo(prov, it.consumoTextil, it.cantidad); urc(idx, { _provTrazo: prov, costoTrazo: precio }); }}>
                        <option value="">— Manual —</option>
                        {maqProvs("Trazo").map(p => <option key={p} value={p}>{p} — {fmt(maqTrazo(p, it.consumoTextil, it.cantidad))}/ud ($/m × {it.consumoTextil}m × 3 ÷ {it.cantidad}uds)</option>)}
                      </select>
                    </F>
                    <F l="Proveedor Corte">
                      <select style={S.sel} value={it._provCorte || ""} onChange={e => { const prov = e.target.value; const precio = maqCorte(prov, it.cantidad); urc(idx, { _provCorte: prov, costoCorte: precio }); }}>
                        <option value="">— Manual —</option>
                        {maqProvs("Corte").map(p => <option key={p} value={p}>{p} — {fmt(maqCorte(p, it.cantidad))}/ud ({it.cantidad} uds)</option>)}
                      </select>
                    </F>
                    <F l="Proveedor Confección">
                      <select style={S.sel} value={it._provConf || ""} onChange={e => { const prov = e.target.value; const precio = maqConfeccion(prov, it.tipoPrenda); urc(idx, { _provConf: prov, costoConfeccion: precio }); }}>
                        <option value="">— Manual —</option>
                        {maqProvs("Confección").map(p => <option key={p} value={p}>{p} — {fmt(maqConfeccion(p, it.tipoPrenda))}/ud ({it.tipoPrenda || "?"})</option>)}
                      </select>
                    </F>
                  </div>
                  <div style={{ ...S.g4, marginTop: 6 }}>
                    <F l="Trazo"><input style={S.inp} type="number" value={it.costoTrazo || ""} onChange={e => urc(idx, { costoTrazo: parseFloat(e.target.value) || 0 })} /></F>
                    <F l="Corte"><input style={S.inp} type="number" value={it.costoCorte} onChange={e => urc(idx, { costoCorte: parseFloat(e.target.value) || 0 })} /></F>
                    <F l="Confección"><input style={S.inp} type="number" value={it.costoConfeccion} onChange={e => urc(idx, { costoConfeccion: parseFloat(e.target.value) || 0 })} /></F>
                    <F l="Empaque"><input style={S.inp} type="number" value={it.costoEmpaque} onChange={e => urc(idx, { costoEmpaque: parseFloat(e.target.value) || 0 })} /></F>
                  </div>
                  <div style={{ background: "#f9f7f3", padding: 8, borderRadius: 5, marginTop: 6, fontSize: 12 }}>
                    <strong>Subtotal directo: {fmt(it._subDirecto)}</strong>
                    <span style={{ marginLeft: 10, fontSize: 10.5, color: "#6b7280" }}>Tex {fmt(it._cTextil)} + Ins {fmt(it._cInsumos)} + Est {fmt(it._cEstampados)} + Maquila {fmt((it.costoTrazo||0)+(it.costoCorte||0)+(it.costoConfeccion||0)+(it.costoEmpaque||0))}</span>
                  </div>

                  {/* B2: Margen / Precio */}
                  <h5 style={S.secT}>2. Margen y Precio</h5>
                  <div style={S.g4}>
                    <F l="Margen bruto %"><input style={{ ...S.inp, fontWeight: 700, fontSize: 14 }} type="number" value={it.margenBruto || ""} onChange={e => urc(idx, { margenBruto: parseFloat(e.target.value) || 0, precioManual: 0 })} /></F>
                    <F l="Precio venta manual (sin IVA)"><input style={{ ...S.inp, fontWeight: 700, fontSize: 14, background: it.precioManual > 0 ? "#fefce8" : "#faf9f7" }} type="number" value={it.precioManual || ""} onChange={e => urc(idx, { precioManual: parseFloat(e.target.value) || 0 })} placeholder="Dejar vacío = por margen" /></F>
                    <div><div style={S.lb}>Precio base resultante</div><div style={{ fontSize: 16, fontWeight: 700, color: "#0a2540", padding: "4px 0" }}>{fmt(it._precioBase)}</div>{it.precioManual > 0 && <div style={{ fontSize: 10, color: "#92400e" }}>Precio fijado manualmente</div>}</div>
                    <div><div style={S.lb}>Margen bruto real</div><div style={{ fontSize: 14, fontWeight: 700, color: it._subDirecto > 0 ? ((it._precioBase - it._subDirecto) / it._precioBase * 100 < 15 ? "#b91c1c" : "#065f46") : "#0a2540", padding: "4px 0" }}>{it._precioBase > 0 ? ((it._precioBase - it._subDirecto) / it._precioBase * 100).toFixed(1) : "0.0"}%</div></div>
                  </div>

                  {/* B3: Cargos */}
                  <h5 style={S.secT}>3. Cargos Adicionales</h5>
                  <div style={{ display: "flex", gap: 6, marginBottom: 6, flexWrap: "wrap" }}>
                    <button style={S.btn2} onClick={() => addCargo(idx)}><Ic name="plus" size={11} /> Manual</button>
                    <select style={{ ...S.sel, width: "auto", fontSize: 11 }} value="" onChange={e => { if (e.target.value) applyPl(idx, parseInt(e.target.value)); e.target.value = ""; }}><option value="">Plantilla...</option>{D.plantillasCargos.map(p => <option key={p.id} value={p.id}>{p.nombre}</option>)}</select>
                  </div>
                  {(it._cargosC || it.cargos || []).map((cg, ci) => (
                    <div key={ci} style={{ display: "flex", gap: 6, alignItems: "center", marginBottom: 3 }}>
                      <input style={{ ...S.inp, flex: 2 }} value={cg.nombre} onChange={e => upCargo(idx, ci, { nombre: e.target.value })} placeholder="Nombre" />
                      <select style={{ ...S.sel, flex: 1 }} value={cg.modo} onChange={e => upCargo(idx, ci, { modo: e.target.value })}><option value="porcentaje">%</option><option value="fijo">$ Fijo</option></select>
                      <input style={{ ...S.inp, flex: 1 }} type="number" value={cg.valor} onChange={e => upCargo(idx, ci, { valor: parseFloat(e.target.value) || 0 })} />
                      <span style={{ fontSize: 11, fontWeight: 600, minWidth: 70, textAlign: "right" }}>{fmt(cg._calc || 0)}</span>
                      <button style={{ ...S.btnD, padding: "1px 5px" }} onClick={() => rmCargo(idx, ci)}>✕</button>
                    </div>))}
                  {it._totalCargos > 0 && <div style={{ textAlign: "right", fontWeight: 600, fontSize: 12, marginTop: 3 }}>Subtotal cargos: {fmt(it._totalCargos)}</div>}

                  {/* B4: Precio final */}
                  <h5 style={S.secT}>4. Precio Final</h5>
                  <div style={{ ...S.g4, background: "#f0fdf4", padding: 12, borderRadius: 7, border: "1px solid #bbf7d0" }}>
                    <div><div style={S.lb}>Neto sin IVA</div><div style={{ fontSize: 14, fontWeight: 700 }}>{fmt(it._netoSinIVA)}</div></div>
                    <div><div style={S.lb}>IVA {it.ivaRate || 19}%</div><div style={{ fontSize: 14, fontWeight: 600 }}>{fmt(it._iva)}</div></div>
                    <div><div style={S.lb}>Precio final</div><div style={{ fontSize: 17, fontWeight: 800, color: "#065f46" }}>{fmt(it._precioFinal)}</div></div>
                    <div><div style={S.lb}>Total ítem</div><div style={{ fontSize: 15, fontWeight: 700, color: "#0a2540" }}>{fmt(it._precioFinal * it.cantidad)}</div></div>
                  </div>
                  <div style={{ ...S.g4, marginTop: 6, background: "#faf9f7", padding: 8, borderRadius: 5, fontSize: 10.5 }}>
                    <div><span style={{ color: "#6b7280" }}>Costo directo:</span><br /><strong>{fmt(it._subDirecto)}</strong></div>
                    <div><span style={{ color: "#6b7280" }}>Utilidad:</span><br /><strong style={{ color: it._utilidad > 0 ? "#065f46" : "#b91c1c" }}>{fmt(it._utilidad)}</strong></div>
                    <div><span style={{ color: "#6b7280" }}>Margen real:</span><br /><strong style={{ color: it._margenReal < 10 ? "#b91c1c" : it._margenReal < 20 ? "#92400e" : "#065f46" }}>{it._margenReal.toFixed(1)}%</strong></div>
                    <div><span style={{ color: "#6b7280" }}>Precio mín:</span><br /><strong>{fmt(it._netoSinIVA)}</strong></div>
                  </div>
                  {it._margenReal < 10 && <div style={S.alertB("red")}><Ic name="alert" size={13} /> Margen real &lt; 10%</div>}
                  {it._utilidad < 0 && <div style={S.alertB("red")}><Ic name="alert" size={13} /> Utilidad NEGATIVA</div>}
                  {it._totalCargos > it._precioBase * 0.15 && <div style={S.alertB("yellow")}><Ic name="alert" size={13} /> Cargos &gt; 15% del precio base</div>}
                </div>); })()}
            </>)}
          </div>
          {/* C.5 Resumen económico */}
          <div style={{ ...S.card, background: "#0a2540", color: "#e8d5b7" }}>
            <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
              <div><div style={{ fontSize: 10.5, textTransform: "uppercase", letterSpacing: 1 }}>Resumen</div><div style={{ fontSize: 10.5, color: "#8faabe", marginTop: 1 }}>{(c.items || []).length} ítem(s) · {(c.items || []).reduce((s, it) => s + it.cantidad, 0)} uds</div></div>
              <div style={{ textAlign: "right" }}><div style={{ fontSize: 11.5 }}>Neto: {fmt(tots.n)} · IVA: {fmt(tots.i)}</div><div style={{ fontSize: 22, fontWeight: 800, fontFamily: "Georgia,serif" }}>{fmt(tots.f)}</div><div style={{ fontSize: 10.5, color: "#8faabe" }}>Utilidad: {fmt(tots.u)}</div></div>
            </div>
          </div>
        </div>
        {/* Quick-add popup */}
        {qa && (
          <div style={{ position: "fixed", inset: 0, zIndex: 1500, background: "rgba(0,0,0,0.35)", display: "flex", alignItems: "center", justifyContent: "center" }} onMouseDown={e => { if (e.target === e.currentTarget) setQa(null); }}>
            <div style={{ background: "#fff", borderRadius: 12, padding: 20, width: qa.type === "cliente" ? 480 : 400, boxShadow: "0 12px 40px rgba(0,0,0,.2)", border: "2px solid #e8d5b7" }} onMouseDown={e => e.stopPropagation()}>
              <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 12 }}>
                <h3 style={{ margin: 0, fontSize: 14, color: "#0a2540", fontFamily: "Georgia,serif" }}>
                  {qa.type === "cliente" && "Nuevo cliente"}
                  {qa.type === "prenda" && "Nuevo tipo de prenda"}
                  {qa.type === "textil" && "Nuevo textil"}
                  {qa.type === "maquila" && "Nuevo servicio maquila"}
                </h3>
                <button style={{ background: "none", border: "none", fontSize: 18, cursor: "pointer", color: "#9ca3af" }} onClick={() => setQa(null)}>✕</button>
              </div>

              {qa.type === "cliente" && <>
                <div style={S.g2}>
                  <F l="Razón Social"><input style={S.inp} value={qa.data.razon} onChange={e => setQa({ ...qa, data: { ...qa.data, razon: e.target.value } })} autoFocus /></F>
                  <F l="NIT"><input style={S.inp} value={qa.data.nit} onChange={e => setQa({ ...qa, data: { ...qa.data, nit: e.target.value } })} /></F>
                </div>
                <div style={{ ...S.g3, marginTop: 6 }}>
                  <F l="Contacto"><input style={S.inp} value={qa.data.contacto} onChange={e => setQa({ ...qa, data: { ...qa.data, contacto: e.target.value } })} /></F>
                  <F l="Correo"><input style={S.inp} value={qa.data.correo} onChange={e => setQa({ ...qa, data: { ...qa.data, correo: e.target.value } })} /></F>
                  <F l="Tel"><input style={S.inp} value={qa.data.tel} onChange={e => setQa({ ...qa, data: { ...qa.data, tel: e.target.value } })} /></F>
                </div>
                <div style={{ display: "flex", gap: 6, marginTop: 10 }}>
                  <button style={S.btnG} onClick={qaSaveCliente}><Ic name="check" size={12} /> Crear cliente</button>
                  <button style={S.btn2} onClick={() => setQa(null)}>Cancelar</button>
                </div>
              </>}

              {qa.type === "prenda" && <>
                <div style={{ display: "flex", gap: 8 }}>
                  <F l="Nombre de prenda"><input style={S.inp} value={qa.data.nombre} onChange={e => setQa({ ...qa, data: { ...qa.data, nombre: e.target.value } })} autoFocus /></F>
                  <F l="Consumo default (m)"><input style={S.inp} type="number" step="0.01" value={qa.data.consumoDefault || ""} onChange={e => setQa({ ...qa, data: { ...qa.data, consumoDefault: parseFloat(e.target.value) || 0 } })} /></F>
                </div>
                <div style={{ display: "flex", gap: 6, marginTop: 10 }}>
                  <button style={S.btnG} onClick={() => qaSavePrenda(itab)}><Ic name="check" size={12} /> Crear prenda</button>
                  <button style={S.btn2} onClick={() => setQa(null)}>Cancelar</button>
                </div>
              </>}

              {qa.type === "textil" && <>
                <div style={{ display: "flex", gap: 8 }}>
                  <F l="Nombre textil"><input style={S.inp} value={qa.data.nombre} onChange={e => setQa({ ...qa, data: { ...qa.data, nombre: e.target.value } })} autoFocus /></F>
                  <F l="Precio/m"><input style={S.inp} type="number" value={qa.data.precio || ""} onChange={e => setQa({ ...qa, data: { ...qa.data, precio: parseFloat(e.target.value) || 0 } })} /></F>
                  <F l="Proveedor"><select style={S.sel} value={qa.data.proveedor} onChange={e => setQa({ ...qa, data: { ...qa.data, proveedor: e.target.value } })}><option value="">—</option>{(D.proveedores || []).map(p => <option key={p.id} value={p.nombre}>{p.nombre}</option>)}</select></F>
                </div>
                <div style={{ display: "flex", gap: 6, marginTop: 10 }}>
                  <button style={S.btnG} onClick={() => qaSaveTextil(itab)}><Ic name="check" size={12} /> Crear textil</button>
                  <button style={S.btn2} onClick={() => setQa(null)}>Cancelar</button>
                </div>
              </>}

              {qa.type === "maquila" && <>
                <div style={{ display: "flex", gap: 8 }}>
                  <F l="Tipo"><select style={S.sel} value={qa.data.tipo} onChange={e => setQa({ ...qa, data: { ...qa.data, tipo: e.target.value } })}><option>Trazo</option><option>Corte</option><option>Confección</option></select></F>
                  <F l="Proveedor"><select style={S.sel} value={qa.data.proveedor} onChange={e => setQa({ ...qa, data: { ...qa.data, proveedor: e.target.value } })}><option value="">—</option>{(D.proveedores || []).map(p => <option key={p.id} value={p.nombre}>{p.nombre}</option>)}</select></F>
                  {qa.data.tipo === "Trazo" && <F l="$/metro"><input style={S.inp} type="number" value={qa.data.precioMetro || ""} onChange={e => setQa({ ...qa, data: { ...qa.data, precioMetro: parseFloat(e.target.value) || 0 } })} /></F>}
                </div>
                <div style={{ display: "flex", gap: 6, marginTop: 10 }}>
                  <button style={S.btnG} onClick={qaSaveMaquila}><Ic name="check" size={12} /> Crear servicio</button>
                  <button style={S.btn2} onClick={() => setQa(null)}>Cancelar</button>
                </div>
              </>}
            </div>
          </div>
        )}
      </div>
    );
  };

  // ═══════════════════════════════════════════════════════════════
  // E. PEDIDO MODAL
  // ═══════════════════════════════════════════════════════════════
  const MPed = () => {
    const [p, setP] = useState(edt);
    const [newTalla, setNewTalla] = useState("");
    const u = (k, v) => setP(x => ({ ...x, [k]: v }));
    const ui = (idx, k, v) => { const items = [...p.items]; items[idx] = { ...items[idx], [k]: v }; setP(x => ({ ...x, items })); };
    const ut = (idx, t, v) => { const items = [...p.items]; items[idx] = { ...items[idx], tallasDetalle: { ...items[idx].tallasDetalle, [t]: parseInt(v) || 0 } }; setP(x => ({ ...x, items })); };
    const addT = (idx) => { if (!newTalla.trim()) return; const key = newTalla.trim().toUpperCase(); const items = [...p.items]; items[idx] = { ...items[idx], tallasDetalle: { ...items[idx].tallasDetalle, [key]: 0 }, tallasCustom: [...(items[idx].tallasCustom || []), key] }; setP(x => ({ ...x, items })); setNewTalla(""); };
    const rmT = (idx, t) => { const items = [...p.items]; const td = { ...items[idx].tallasDetalle }; delete td[t]; items[idx] = { ...items[idx], tallasDetalle: td, tallasCustom: (items[idx].tallasCustom || []).filter(x => x !== t) }; setP(x => ({ ...x, items })); };
    const handleImg = (idx, e) => { const file = e.target.files[0]; if (!file) return; const reader = new FileReader(); reader.onload = (ev) => ui(idx, "renderImg", ev.target.result); reader.readAsDataURL(file); };
    const save = () => { up("pedidos", D.pedidos.map(x => x.id === p.id ? p : x)); setModal(null); setEdt(null); };
    const allT = it => [...new Set([...(D.tallas || []), ...(it.tallasCustom || [])])];
    const [activeTab, setActiveTab] = useState(0);
    return (
      <div style={S.modal} onMouseDown={e => { if (e.target === e.currentTarget) save(); }}><div style={{ ...S.mc, maxWidth: 1050 }} onMouseDown={e => e.stopPropagation()}>
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 14 }}>
          <div>
            <input style={{ ...S.inp, fontWeight: 700, fontSize: 15, width: 340, border: "none", background: "transparent", padding: 0, color: "#0a2540" }} value={p.nombrePed || ""} onChange={e => u("nombrePed", e.target.value)} placeholder={p.numero} />
            <div style={{ fontSize: 10.5, color: "#9ca3af", display: "flex", alignItems: "center", gap: 6 }}>{p.numero} <span style={S.badge(pedBadge(p.estado))}>{p.estado}</span></div>
          </div>
          <div style={{ display: "flex", gap: 6 }}>
            <select style={{ ...S.sel, width: 140, fontSize: 11, fontWeight: 600 }} value={p.estado} onChange={e => u("estado", e.target.value)}>
              {ESTADOS_PED.map(e => <option key={e} value={e}>{e}</option>)}
            </select>
            <button style={S.btnG} onClick={() => { save(); setPdfHtml(buildPedidoPDF(p, D)); setPdfCot(p); }}>PDF</button>
            <button style={{ ...S.btn2, color: "#1e40af" }} onClick={() => openEmail("ped", p)}><Ic name="send" size={13} /></button>
            <button style={S.btn} onClick={save}><Ic name="check" size={13} /> Guardar</button>
          </div>
        </div>
        {/* Encabezado del pedido */}
        <div style={{ ...S.card, background: "#faf9f7" }}>
          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr 1fr 1fr 1fr", gap: 8 }}>
            <F l="Fecha del pedido"><input style={{ ...S.inp, background: "#e5e1d8", fontWeight: 600 }} value={p.fecha} readOnly /></F>
            <F l="OC Cliente"><input style={S.inp} value={p.ocCliente} onChange={e => u("ocCliente", e.target.value)} placeholder="# orden de compra" /></F>
            <F l="Fecha compromiso"><input style={S.inp} type="date" value={p.fechaCompromiso} onChange={e => u("fechaCompromiso", e.target.value)} /></F>
            <F l="Responsable"><input style={S.inp} value={p.responsable} onChange={e => u("responsable", e.target.value)} /></F>
            <F l="Cliente"><input style={S.inp} value={p.clienteNombre} readOnly /></F>
            <F l="Tipo de negocio"><select style={{ ...S.sel, background: "#ede9fe", fontWeight: 600, color: "#5b21b6" }} value={p.tipoNegocio || ""} onChange={e => u("tipoNegocio", e.target.value)}>
              <option value="">— Seleccionar —</option>
              {TIPOS_NEGOCIO.map(t => <option key={t} value={t}>{t}</option>)}
            </select></F>
          </div>
          <div style={{ ...S.g4, marginTop: 7 }}>
            <F l="Dir. entrega"><input style={S.inp} value={p.dirEntrega} onChange={e => u("dirEntrega", e.target.value)} /></F>
            <F l="Dir. facturación"><input style={S.inp} value={p.dirFactura} onChange={e => u("dirFactura", e.target.value)} /></F>
            <F l="Contacto recibe"><input style={S.inp} value={p.contactoRecibe} onChange={e => u("contactoRecibe", e.target.value)} /></F>
            <F l="Tel. recibe"><input style={S.inp} value={p.telRecibe} onChange={e => u("telRecibe", e.target.value)} /></F>
          </div>
          <div style={{ marginTop: 7 }}>
            <F l="Archivo OC (adjuntar)">
              <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
                <input type="file" accept=".pdf,.jpg,.jpeg,.png,.doc,.docx" onChange={e => { const file = e.target.files[0]; if (!file) return; const reader = new FileReader(); reader.onload = ev => u("ocArchivo", { nombre: file.name, tipo: file.type, data: ev.target.result }); reader.readAsDataURL(file); }} style={{ fontSize: 11 }} />
                {p.ocArchivo && <div style={{ display: "flex", alignItems: "center", gap: 6, padding: "4px 10px", background: "#d1fae5", borderRadius: 5, fontSize: 11, color: "#065f46" }}>
                  <strong>{p.ocArchivo.nombre}</strong>
                  <button style={{ background: "none", border: "none", cursor: "pointer", color: "#b91c1c", fontSize: 11, padding: 0 }} onClick={() => u("ocArchivo", null)}>x</button>
                </div>}
              </div>
              {p.ocArchivo && p.ocArchivo.tipo && p.ocArchivo.tipo.startsWith("image/") && <img src={p.ocArchivo.data} alt="OC" style={{ maxWidth: 300, maxHeight: 150, marginTop: 6, borderRadius: 6, border: "1px solid #e5e1d8" }} />}
            </F>
          </div>
        </div>
        {/* Item tabs */}
        {(p.items || []).length > 1 && <div style={{ ...S.tabs, flexWrap: "wrap", marginBottom: 6 }}>{p.items.map((it, i) => <div key={i} style={S.tab(activeTab === i)} onClick={() => setActiveTab(i)}>#{i + 1} {it.nombreComercial || it.tipoPrenda}</div>)}</div>}
        {/* Item detail */}
        {(p.items || []).map((it, idx) => activeTab === idx && (
          <div key={idx} style={{ ...S.card, borderLeft: "3px solid #0a2540" }}>
            <h4 style={{ margin: "0 0 10px", color: "#0a2540", fontSize: 13 }}>Ítem {idx + 1}: {it.nombreComercial || it.tipoPrenda} — <span style={{ fontWeight: 400, color: "#6b7280" }}>{it.cantidad} uds</span></h4>
            <div style={S.g4}>
              <F l="Textil"><input style={S.inp} value={it.textilNombre || ""} onChange={e => ui(idx, "textilNombre", e.target.value)} /></F>
              <F l="Color"><input style={S.inp} value={it.color || ""} onChange={e => ui(idx, "color", e.target.value)} placeholder="Ej: Azul marino, Blanco" /></F>
              <F l="Estampados"><input style={S.inp} value={(it.estampados || []).map(e => `${e.tecnica} (${e.ubicacion || ""})`).join(", ")} readOnly /></F>
              <F l="Empaque"><input style={S.inp} value={it.empaqueD || ""} onChange={e => ui(idx, "empaqueD", e.target.value)} /></F>
            </div>
            <div style={{ ...S.g2, marginTop: 7 }}>
              <F l="Imagen del diseño / render">
                <input type="file" accept="image/*" onChange={e => handleImg(idx, e)} style={{ fontSize: 11 }} />
                {it.renderImg && <img src={it.renderImg} alt="Render" style={{ maxWidth: 200, maxHeight: 120, marginTop: 6, borderRadius: 6, border: "1px solid #e5e1d8" }} />}
              </F>
              <div style={S.g2}><F l="Molde"><input style={S.inp} value={it.molde || ""} onChange={e => ui(idx, "molde", e.target.value)} /></F><F l="Patronista"><input style={S.inp} value={it.patronista || ""} onChange={e => ui(idx, "patronista", e.target.value)} /></F></div>
            </div>
            <F l="Insumos"><input style={{ ...S.inp, marginTop: 4 }} value={it.insumosD || ""} onChange={e => ui(idx, "insumosD", e.target.value)} /></F>
            {/* Tallas */}
            <div style={{ marginTop: 8 }}>
              <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
                <div style={S.lb}>Tallas (Total: {it.cantidad})</div>
                <div style={{ display: "flex", gap: 4, alignItems: "center" }}>
                  <input style={{ ...S.inp, width: 70 }} value={newTalla} onChange={e => setNewTalla(e.target.value)} placeholder="Ej: 3XL" onKeyDown={e => { if (e.key === "Enter") addT(idx); }} />
                  <button style={S.btn2} onClick={() => addT(idx)}><Ic name="plus" size={11} /> Talla</button>
                </div>
              </div>
              <div style={{ display: "flex", gap: 5, marginTop: 3, flexWrap: "wrap" }}>
                {allT(it).map(t => <div key={t} style={{ textAlign: "center" }}><div style={{ fontSize: 9.5, fontWeight: 600, color: "#6b7280", display: "flex", alignItems: "center", gap: 1 }}>{t}{(it.tallasCustom || []).includes(t) && <button style={{ background: "none", border: "none", cursor: "pointer", color: "#b91c1c", fontSize: 9, padding: 0 }} onClick={() => rmT(idx, t)}>x</button>}</div><input style={{ ...S.inp, width: 44, textAlign: "center" }} type="number" value={(it.tallasDetalle || {})[t] || ""} onChange={e => ut(idx, t, e.target.value)} /></div>)}
                <div style={{ textAlign: "center" }}><div style={{ fontSize: 9.5, fontWeight: 600 }}>Total</div><div style={{ padding: "6px 5px", fontWeight: 700, color: Object.values(it.tallasDetalle || {}).reduce((a, b) => a + b, 0) === it.cantidad ? "#065f46" : "#b91c1c" }}>{Object.values(it.tallasDetalle || {}).reduce((a, b) => a + b, 0)}/{it.cantidad}</div></div>
              </div>
            </div>
            {/* Proveedores */}
            <h5 style={{ margin: "10px 0 5px", fontSize: 10.5, textTransform: "uppercase", color: "#0a2540" }}>Proveedores por proceso</h5>
            <div style={S.g5}>
              <F l="Trazo"><select style={S.sel} value={it.provTrazo || ""} onChange={e => ui(idx, "provTrazo", e.target.value)}><option value="">—</option>{D.proveedores.filter(x => x.tipo === "Corte" || x.tipo === "Trazo").map(x => <option key={x.id} value={x.nombre}>{x.nombre}</option>)}</select></F>
              <F l="Corte"><select style={S.sel} value={it.provCorte || ""} onChange={e => ui(idx, "provCorte", e.target.value)}><option value="">—</option>{D.proveedores.filter(x => x.tipo === "Corte").map(x => <option key={x.id} value={x.nombre}>{x.nombre}</option>)}</select></F>
              <F l="Confección"><select style={S.sel} value={it.provConf || ""} onChange={e => ui(idx, "provConf", e.target.value)}><option value="">—</option>{D.proveedores.filter(x => x.tipo === "Confección").map(x => <option key={x.id} value={x.nombre}>{x.nombre}</option>)}</select></F>
              <F l="Estampado"><select style={S.sel} value={it.provEst || ""} onChange={e => ui(idx, "provEst", e.target.value)}><option value="">—</option>{D.proveedores.filter(x => ["Estampado", "Sublimación", "Bordado"].includes(x.tipo)).map(x => <option key={x.id} value={x.nombre}>{x.nombre}</option>)}</select></F>
              <F l="Empaque"><select style={S.sel} value={it.provEmp || ""} onChange={e => ui(idx, "provEmp", e.target.value)}><option value="">—</option>{D.proveedores.filter(x => x.tipo === "Empaque").map(x => <option key={x.id} value={x.nombre}>{x.nombre}</option>)}</select></F>
            </div>
            <F l="Notas de producción"><textarea style={{ ...S.inp, minHeight: 36, marginTop: 5 }} value={it.notas || ""} onChange={e => ui(idx, "notas", e.target.value)} placeholder="Indicaciones especiales para este ítem..." /></F>
          </div>))}
        {/* Observaciones generales */}
        <div style={{ ...S.card, background: "#faf9f7", marginTop: 6 }}>
          <F l="Observaciones generales del pedido"><textarea style={{ ...S.inp, minHeight: 40 }} value={p.obs || ""} onChange={e => u("obs", e.target.value)} placeholder="Notas generales, acuerdos especiales..." /></F>
        </div>
      </div></div>
    );
  };

  // ═══════════════════════════════════════════════════════════════
  // GENERIC FORM MODAL
  // ═══════════════════════════════════════════════════════════════
  const MForm = () => {
    const [item, setItem] = useState(edt);
    const u = (k, v) => setItem(i => ({ ...i, [k]: v }));
    const t = item._t;
    const fm = {
      textiles: [["Código", "codigo"], ["Nombre", "nombre"], ["Proveedor", "proveedor", "select", "_proveedores"], ["Composición", "composicion"], ["Gramaje", "gramaje", "number"], ["Ancho cm", "ancho", "number"], ["Unidad", "unidad", "select", ["Metro", "Kilo", "Rollo"]], ["Precio", "precio", "number"], ["IVA %", "iva", "number"], ["Dcto PP %", "dctoPP", "number"], ["Dcto Vol %", "dctoVol", "number"], ["Vol Mín", "volMin", "number"], ["Días abast.", "diasAbast", "number"], ["Colores", "colores"], ["Ref. Prov.", "ref"], ["Estado", "estado", "select", ["Activo", "Inactivo"]]],
      insumos: [["Código", "codigo"], ["Nombre", "nombre"], ["Categoría", "cat", "select", CATS_INSUMO], ["Unidad", "unidad", "select", ["Unidad", "Metro", "Kilo", "Litro", "Rollo"]], ["Costo", "costo", "number"], ["IVA %", "iva", "number"], ["Proveedor", "proveedor", "select", "_proveedores"], ["Variante", "variante"]],
      tecnicas: [["Nombre", "nombre"], ["Unidad cobro", "unidadCobro", "select", UNIDADES_EST], ["Costo base", "costoBase", "number"], ["Proveedor", "proveedor", "select", "_proveedores"], ["Descripción", "desc"], ["Estado", "estado", "select", ["Activo", "Inactivo"]]],
      tiposPrenda: [["Código", "codigo"], ["Nombre", "nombre"], ["Categoría", "cat", "select", ["Superior", "Inferior", "Enterizo", "Accesorios"]], ["Descripción", "desc"], ["Consumo default (m)", "consumoDefault", "number"], ["Estado", "estado", "select", ["Activo", "Inactivo"]]],
      moldes: [["Código", "codigo"], ["Tipo prenda", "tipoPrenda"], ["Versión", "version"], ["Patronista", "patronista"], ["Escala", "escala"], ["Obs.", "obs"], ["Estado", "estado", "select", ["Activo", "Inactivo"]]],
      clientes: [["Tipo", "tipo", "select", ["Empresa", "Persona"]], ["Razón Social", "razon"], ["NIT", "nit"], ["Nombre Comercial", "comercial"], ["Contacto", "contacto"], ["Correo", "correo"], ["Teléfono", "tel"], ["Dirección", "dir"], ["Ciudad", "ciudad"], ["Dir. Facturación", "dirFactura"], ["Dir. Entrega", "dirEntrega"], ["Condiciones", "condiciones"], ["Estado", "estado", "select", ["Activo", "Inactivo"]]],
      proveedores: [["Nombre", "nombre"], ["NIT", "nit"], ["Contacto", "contacto"], ["Dirección", "direccion"], ["Ciudad", "ciudad"], ["Email", "email"], ["Celular", "celular"], ["Tipo", "tipo", "select", TIPOS_PROV], ["Estado", "estado", "select", ["Activo", "Inactivo"]]],
    };
    const fields = fm[t] || [];
    const titles = { textiles: "Textil", insumos: "Insumo", tecnicas: "Técnica", tiposPrenda: "Tipo de Prenda", moldes: "Molde", clientes: "Cliente", proveedores: "Proveedor" };
    const getOpts = (o) => o === "_proveedores" ? (D.proveedores || []).map(p => p.nombre) : (o || []);
    return (
      <div style={S.modal} onMouseDown={e => { if (e.target === e.currentTarget) { setModal(null); setEdt(null); } }}><div style={S.mc} onMouseDown={e => e.stopPropagation()}>
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 14 }}><h2 style={{ margin: 0, color: "#0a2540", fontFamily: "Georgia,serif", fontSize: 16 }}>{item.id === 0 ? "Nuevo" : "Editar"} {titles[t]}</h2><button style={S.btn} onClick={() => saveForm(item)}><Ic name="check" size={13} /> Guardar</button></div>
        <div style={S.g2}>{fields.map(([l, k, tp, o]) => <F key={k} l={l}>{tp === "select" ? <select style={S.sel} value={item[k] || ""} onChange={e => u(k, e.target.value)}><option value="">—</option>{getOpts(o).map(x => <option key={x}>{x}</option>)}</select> : <input style={S.inp} type={tp || "text"} value={item[k] ?? ""} onChange={e => u(k, tp === "number" ? parseFloat(e.target.value) || 0 : e.target.value)} />}</F>)}</div>
      </div></div>
    );
  };

  // ═══════════════════════════════════════════════════════════════
  // RENDER
  // ═══════════════════════════════════════════════════════════════
  const renderPage = () => {
    if (view === "dashboard") return <PgDash />;
    if (view === "cotizaciones") return <PgCot />;
    if (view === "pedidos") return <PgPed />;
    if (view === "plantillas") return <PgPl />;
    if (view === "maquila") return <PgMaquila />;
    if (view === "catalogo") return <PgCatalogo />;
    if (view === "usuarios" && puede("usuarios")) return <PgUsuarios />;
    const cfg = mc[view];
    if (cfg) {
      const dt = cfg.type || view;
      const extra = puede("crear") ? (dt === "textiles" ? <button style={S.btn2} onClick={() => setShowBulk(!showBulk)}><Ic name="download" size={12} /> Importar masivo</button> : dt === "insumos" ? <button style={S.btn2} onClick={() => setShowBulkIns(!showBulkIns)}><Ic name="download" size={12} /> Importar masivo</button> : dt === "proveedores" ? <button style={S.btn2} onClick={() => setShowBulkProv(!showBulkProv)}><Ic name="download" size={12} /> Importar masivo</button> : null) : null;
      return <MPg title={cfg.title} type={dt} tpl={cfg.tpl} cols={cfg.cols} rr={cfg.rr} extra={extra} />;
    }
    return <PgDash />;
  };

  if (!ok) return <div style={{ display: "flex", alignItems: "center", justifyContent: "center", height: "100vh", fontFamily: "Georgia,serif", color: "#0a2540", fontSize: 16 }}>Cargando...</div>;
  // if (!session) return <LoginScreen />; // Login desactivado

  return (
    <div style={S.app}>
      <link href="https://fonts.googleapis.com/css2?family=Libre+Franklin:wght@300;400;500;600;700;800&display=swap" rel="stylesheet" />
      <div style={S.sidebar}>
        <div style={{ padding: "16px 14px 10px", borderBottom: "1px solid rgba(255,255,255,0.08)" }}><img src={LOGO} alt="V" style={{ width: "100%", borderRadius: 5 }} /><div style={{ fontSize: 8.5, color: "#8faabe", marginTop: 5, textAlign: "center", letterSpacing: 2, textTransform: "uppercase" }}>Cotización v5</div></div>
        <div style={{ flex: 1, padding: "6px 0" }}>{menuFull.map(m => <div key={m.k} style={S.si(view === m.k)} onClick={() => { setView(m.k); setSearch(""); setStab(0); }}><Ic name={m.ic} size={15} /><span>{m.l}</span></div>)}</div>
        {session && <div style={{ padding: "8px 14px", borderTop: "1px solid rgba(255,255,255,0.08)" }}>
          <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
            <div><div style={{ fontSize: 11, color: "#e8d5b7", fontWeight: 600 }}>{session.nombre.split(" ")[0]}</div><div style={{ fontSize: 9, color: "#8faabe" }}><span style={{ padding: "1px 5px", borderRadius: 3, background: session.nivel >= 3 ? "rgba(239,68,68,0.2)" : session.nivel >= 2 ? "rgba(59,130,246,0.2)" : "rgba(255,255,255,0.1)", color: session.nivel >= 3 ? "#fca5a5" : session.nivel >= 2 ? "#93c5fd" : "#8faabe", fontWeight: 600 }}>{nivelLabel(session.nivel)}</span></div></div>
            <button style={{ background: "rgba(255,255,255,0.08)", border: "none", padding: "4px 8px", borderRadius: 4, color: "#8faabe", fontSize: 10, cursor: "pointer" }} onClick={() => setSession(null)}>Salir</button>
          </div>
        </div>}
        <div style={{ padding: 10, borderTop: "1px solid rgba(255,255,255,0.08)", fontSize: 9, color: "#6b8299", textAlign: "center" }}>Veliantex S.A.S.<br/>Vitality Sportswear</div>
      </div>
      <div style={S.main}>
        <div style={S.topbar}>
          <div style={{ display: "flex", alignItems: "center", gap: 10 }}><span style={{ fontSize: 15, fontWeight: 700, color: "#0a2540", fontFamily: "Georgia,serif" }}>{menuFull.find(m => m.k === view)?.l || "Dashboard"}</span>{saved && <span style={{ fontSize: 10.5, color: "#065f46", background: "#d1fae5", padding: "2px 7px", borderRadius: 4 }}>{saved} Guardado</span>}</div>
          <div style={{ display: "flex", alignItems: "center", gap: 10 }}><input style={{ ...S.inp, width: 180 }} placeholder="Buscar..." value={search} onChange={e => setSearch(e.target.value)} />{puede("crear") && <button style={S.btn} onClick={nuevaCot}><Ic name="plus" size={13} /> Cotización</button>}</div>
        </div>
        <div style={S.content}>{renderPage()}</div>
      </div>
      {modal === "form" && edt && <MForm />}
      {modal === "cotizador" && edt && <MCot />}
      {modal === "pedido" && edt && <MPed />}
      {/* Conversion popup - tipo de negocio */}
      {convertCot && (
        <div style={S.modal} onMouseDown={e => { if (e.target === e.currentTarget) setConvertCot(null); }}>
          <div style={{ background: "#fff", borderRadius: 14, padding: 24, width: 420, boxShadow: "0 12px 40px rgba(0,0,0,.2)", border: "2px solid #e8d5b7" }} onMouseDown={e => e.stopPropagation()}>
            <h3 style={{ margin: "0 0 4px", fontSize: 16, color: "#0a2540", fontFamily: "Georgia,serif" }}>Aprobar cotización</h3>
            <div style={{ fontSize: 11, color: "#6b7280", marginBottom: 14 }}>COT-{convertCot.numero} · {convertCot.clienteNombre || "—"}</div>
            <F l="Tipo de negocio">
              <select style={{ ...S.sel, fontSize: 13, padding: "10px 12px" }} value={convertTipo} onChange={e => setConvertTipo(e.target.value)}>
                {TIPOS_NEGOCIO.map(t => <option key={t} value={t}>{t}</option>)}
              </select>
            </F>
            <div style={{ fontSize: 10, color: "#9ca3af", margin: "8px 0 14px", padding: "6px 10px", background: "#f0fdf4", borderRadius: 5, border: "1px solid #bbf7d0" }}>
              La cotización se marcará como <strong style={{ color: "#065f46" }}>Aprobada</strong> y se creará un pedido nuevo.
            </div>
            <div style={{ display: "flex", gap: 6 }}>
              <button style={{ ...S.btn, background: "#065f46", flex: 1 }} onClick={() => cotAPedido(convertCot, convertTipo)}>Aprobar y crear pedido</button>
              <button style={S.btn2} onClick={() => setConvertCot(null)}>Cancelar</button>
            </div>
          </div>
        </div>
      )}
      {pdfHtml && (() => {
        const previewHtml = pdfHtml.replace(/<script>.*?<\/script>/g, "");
        const descargarPDF = () => {
          const filename = (pdfCot ? pdfCot.numero || 'Documento' : 'Documento').replace(/\s+/g, '_') + '.pdf';
          const container = document.createElement('div');
          container.innerHTML = pdfHtml;
          container.style.width = '850px';
          document.body.appendChild(container);
          html2pdf().set({
            margin: [5, 5, 5, 5],
            filename: filename,
            html2canvas: { scale: 2, useCORS: true },
            jsPDF: { unit: 'mm', format: 'letter', orientation: 'portrait' },
            pagebreak: { mode: ['avoid-all', 'css', 'legacy'] }
          }).from(container).save().then(() => {
            document.body.removeChild(container);
          });
          setPdfHtml(null); setPdfCot(null);
          setSaved("✓ Descargando PDF..."); setTimeout(() => setSaved(""), 3000);
        };
        return (
          <div style={{ position: "fixed", inset: 0, zIndex: 2000, background: "#fff", overflow: "auto" }}>
            <div style={{ position: "sticky", top: 0, zIndex: 10, background: "#0a2540", padding: "10px 20px", display: "flex", justifyContent: "space-between", alignItems: "center" }}>
              <span style={{ color: "#e8d5b7", fontWeight: 700, fontSize: 13 }}>Vista previa</span>
              <div style={{ display: "flex", gap: 8 }}>
                <button style={{ background: "#e8d5b7", color: "#0a2540", border: "none", padding: "8px 20px", borderRadius: 5, fontSize: 12, fontWeight: 700, cursor: "pointer" }} onClick={descargarPDF}>Descargar PDF</button>
                <button style={{ background: "#3b82f6", color: "#fff", border: "none", padding: "8px 20px", borderRadius: 5, fontSize: 12, fontWeight: 700, cursor: "pointer" }} onClick={() => { openEmail("cot", pdfCot); setPdfHtml(null); setPdfCot(null); }}>Enviar por correo</button>
                <button style={{ background: "rgba(255,255,255,0.15)", color: "#e8d5b7", border: "none", padding: "7px 14px", borderRadius: 5, cursor: "pointer", fontSize: 12 }} onClick={() => { setPdfHtml(null); setPdfCot(null); }}>✕ Cerrar</button>
              </div>
            </div>
            <div dangerouslySetInnerHTML={{ __html: previewHtml }} style={{ maxWidth: 850, margin: "0 auto", padding: "20px 0 40px" }} />
          </div>
        );
      })()}

      {/* Email modal */}
      {emailModal && (
        <div style={S.modal} onMouseDown={e => { if (e.target === e.currentTarget) setEmailModal(null); }}>
          <div style={{ ...S.mc, maxWidth: 520 }} onMouseDown={e => e.stopPropagation()} onClick={e => e.stopPropagation()}>
            <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 14 }}>
              <h2 style={{ margin: 0, color: "#0a2540", fontFamily: "Georgia,serif", fontSize: 16 }}>Enviar {emailModal.tipo === "cot" ? "cotización" : "pedido"} por correo</h2>
              <button style={{ background: "none", border: "none", fontSize: 18, cursor: "pointer", color: "#9ca3af" }} onClick={() => setEmailModal(null)}>✕</button>
            </div>
            <div style={{ fontSize: 11, color: "#9ca3af", marginBottom: 10, padding: "6px 10px", background: "#f0fdf4", borderRadius: 5, border: "1px solid #bbf7d0" }}>
              Documento: <strong style={{ color: "#065f46" }}>{emailModal.numero}</strong>
            </div>
            <F l="Para (correo del cliente)"><input style={S.inp} value={emailModal.para} onChange={e => setEmailModal({ ...emailModal, para: e.target.value })} placeholder="correo@cliente.com" /></F>
            <F l="Asunto"><input style={S.inp} value={emailModal.asunto} onChange={e => setEmailModal({ ...emailModal, asunto: e.target.value })} /></F>
            <F l="Mensaje"><textarea style={{ ...S.inp, minHeight: 120, fontFamily: "inherit", fontSize: 11 }} value={emailModal.mensaje} onChange={e => setEmailModal({ ...emailModal, mensaje: e.target.value })} /></F>
            <div style={{ display: "flex", gap: 6, marginTop: 10 }}>
              <button style={{ ...S.btn, background: "#1e40af", color: "#fff" }} onClick={async () => {
                try {
                  setSaved("Enviando...");
                  const pdfData = emailModal.data ? (emailModal.tipo === "cot" ? buildPDF(emailModal.data, D) : buildPedidoPDF(emailModal.data, D)) : "";
                  const r = await fetch('/api/email/send', {
                    method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'include',
                    body: JSON.stringify({ para: emailModal.para, asunto: emailModal.asunto, mensaje: emailModal.mensaje, html: pdfData })
                  });
                  const res = await r.json();
                  if (!r.ok) throw new Error(res.error || 'Error enviando');
                  setSaved("✓ Correo enviado"); setTimeout(() => setSaved(""), 4000);
                  setEmailModal(null);
                } catch (err) {
                  setSaved("✗ " + err.message); setTimeout(() => setSaved(""), 6000);
                }
              }}>Enviar correo</button>
              <button style={S.btn2} onClick={() => setEmailModal(null)}>Cancelar</button>
            </div>
          </div>
        </div>
      )}
    </div>
  );
}

/* ═══════════════════════════════════════════════════════════════
   LOGIN WRAPPER — usa la misma tabla users del Panel de Control
   ═══════════════════════════════════════════════════════════════ */
function LoginScreen({ onLogin }) {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [err, setErr] = useState('');
  const [loading, setLoading] = useState(false);
  const [googleReady, setGoogleReady] = useState(false);
  const googleBtnRef = useRef(null);

  // Inicializar Google Identity Services
  useEffect(() => {
    let attempts = 0;
    const initGoogle = () => {
      if (typeof google !== 'undefined' && google.accounts) {
        fetch('/api/config/google-client-id').then(r => r.json()).then(cfg => {
          if (!cfg.clientId) return;
          google.accounts.id.initialize({
            client_id: cfg.clientId,
            callback: handleGoogleResponse,
          });
          if (googleBtnRef.current) {
            google.accounts.id.renderButton(googleBtnRef.current, {
              theme: 'outline', size: 'large', width: 288, text: 'signin_with',
              logo_alignment: 'center',
            });
          }
          setGoogleReady(true);
        });
      } else if (attempts < 20) {
        attempts++;
        setTimeout(initGoogle, 300);
      }
    };
    initGoogle();
  }, []);

  const handleGoogleResponse = async (response) => {
    setErr('');
    setLoading(true);
    try {
      const r = await fetch('/api/auth/google-login', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        credentials: 'include',
        body: JSON.stringify({ credential: response.credential }),
      });
      const data = await r.json();
      if (!r.ok) throw new Error(data.error || 'Error con Google');
      onLogin(data.user);
    } catch (e) {
      setErr(e.message);
    } finally {
      setLoading(false);
    }
  };

  const submit = async (e) => {
    e.preventDefault();
    setErr('');
    setLoading(true);
    try {
      const r = await fetch('/api/auth/login', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        credentials: 'include',
        body: JSON.stringify({ email, password }),
      });
      const data = await r.json();
      if (!r.ok) throw new Error(data.error || 'Error de autenticacion');
      onLogin(data.user);
    } catch (e) {
      setErr(e.message);
    } finally {
      setLoading(false);
    }
  };

  return (
    <div className="login-wrap">
      <form className="login-card" onSubmit={submit}>
        <img src={LOGO} alt="Veliantex" style={{ display: "block", height: 40, margin: "0 auto 16px", borderRadius: 4 }} />
        <h2>Cotizador Veliantex</h2>
        <p>Solo usuarios master — Ingresa con tu cuenta</p>
        {err && <div className="login-err" style={{ display: "block" }}>{err}</div>}
        <div ref={googleBtnRef} style={{ marginBottom: googleReady ? 16 : 0 }}></div>
        {googleReady && <div style={{ textAlign: "center", fontSize: 11, color: "#9ca3af", margin: "-8px 0 12px" }}>o ingresa con correo y contrasena</div>}
        <label>Correo</label>
        <input type="email" value={email} onChange={e => setEmail(e.target.value)} required autoFocus />
        <label>Contrasena</label>
        <input type="password" value={password} onChange={e => setPassword(e.target.value)} required />
        <button type="submit" disabled={loading}>{loading ? 'Ingresando...' : 'Ingresar'}</button>
      </form>
    </div>
  );
}

function Root() {
  const [user, setUser] = useState(null);
  const [checking, setChecking] = useState(true);

  useEffect(() => {
    fetch('/api/auth/me', { credentials: 'include' })
      .then(r => r.ok ? r.json() : Promise.reject())
      .then(d => setUser(d.user))
      .catch(() => setUser(null))
      .finally(() => setChecking(false));
  }, []);

  if (checking) return <div style={{ display: "flex", alignItems: "center", justifyContent: "center", height: "100vh", fontFamily: "'Libre Franklin', sans-serif", color: "#6b7280" }}>Cargando...</div>;
  if (!user) return <LoginScreen onLogin={setUser} />;
  return <App />;
}

ReactDOM.createRoot(document.getElementById('root')).render(<Root />);
