import os, zipfile, textwrap, pathlib base="/mnt/data/obrapro_limpio" os.makedirs(base, exist_ok=True) style = """*{box-sizing:border-box} :root{--dark:#061225;--dark2:#0b1d3a;--blue:#1f7aff;--green:#16b84e;--text:#0f172a;--muted:#64748b;--line:#e5e7eb;--card:#ffffff} body{margin:0;font-family:Arial,Helvetica,sans-serif;background:#f8fafc;color:var(--text)} a{text-decoration:none} .header{background:#061225;color:#fff;position:sticky;top:0;z-index:10;border-bottom:1px solid rgba(255,255,255,.08)} .nav{max-width:1180px;margin:auto;padding:16px 20px;display:flex;align-items:center;justify-content:space-between;gap:20px} .logo{font-size:30px;font-weight:900;color:#fff}.logo span{color:var(--blue)} .menu{display:flex;gap:20px;flex-wrap:wrap}.menu a{color:#fff;font-weight:700;font-size:14px} .btn{display:inline-block;background:var(--blue);color:#fff;padding:14px 20px;border-radius:10px;font-weight:800;border:0;cursor:pointer;margin:6px 4px} .btn.green{background:var(--green)}.btn.dark{background:#111827}.btn.outline{background:transparent;border:1px solid #fff}.btn.red{background:#ef4444}.btn.orange{background:#f97316} .hero{background:linear-gradient(90deg,rgba(6,18,37,.97),rgba(6,18,37,.88)),url('https://images.unsplash.com/photo-1503387762-592deb58ef4e?auto=format&fit=crop&w=1600&q=80');background-size:cover;background-position:center;color:#fff} .hero-inner{max-width:1180px;margin:auto;padding:70px 20px;display:grid;grid-template-columns:1.4fr .8fr;gap:35px;align-items:center} .hero h1{font-size:clamp(40px,6vw,66px);line-height:1.05;margin:0 0 18px}.hero h1 span{color:var(--blue)} .hero p{font-size:18px;line-height:1.7;max-width:720px} .hero-card{background:#fff;color:var(--text);border-radius:18px;padding:26px;box-shadow:0 20px 50px rgba(0,0,0,.35)} .container{max-width:1180px;margin:auto;padding:42px 20px} .center{text-align:center}.muted{color:var(--muted)} .grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(230px,1fr));gap:18px} .card{background:#fff;border:1px solid var(--line);border-radius:16px;padding:24px;box-shadow:0 10px 25px rgba(15,23,42,.06)} .card h3{margin-top:0}.icon{width:62px;height:62px;border-radius:50%;display:grid;place-items:center;background:#dbeafe;color:var(--blue);font-size:28px;margin-bottom:15px} label{display:block;font-weight:800;margin-top:13px} input,select,textarea{width:100%;padding:13px;margin-top:7px;border:1px solid #cbd5e1;border-radius:10px;font-size:15px} .result{background:#f1f5f9;border-left:5px solid var(--green);padding:18px;border-radius:12px;margin-top:18px} .footer{background:#061225;color:#cbd5e1;text-align:center;padding:25px;margin-top:40px} .badge{display:inline-block;background:#dbeafe;color:#1d4ed8;padding:6px 10px;border-radius:999px;font-weight:800;font-size:13px} @media(max-width:850px){.hero-inner{grid-template-columns:1fr}.nav{display:block;text-align:center}.menu{justify-content:center;margin-top:12px}.hero-inner{padding:45px 18px}} """ app = """let ultimaCotizacion=null; function money(n){return "$"+Number(n||0).toFixed(2)} function calcularDrywall(){ const cliente=document.getElementById("cliente").value||"Cliente"; const telefono=document.getElementById("telefonoCliente").value||""; const ciudad=document.getElementById("ciudad").value||"No especificado"; const largo=parseFloat(document.getElementById("largo").value)||0; const alto=parseFloat(document.getElementById("alto").value)||0; const mano=parseFloat(document.getElementById("manoObra").value)||0; const margen=parseFloat(document.getElementById("margen").value)||0; if(largo<=0||alto<=0){alert("Coloca largo y alto válidos.");return;} const area=largo*alto, areaExtra=area*1.10; const laminas=Math.ceil(areaExtra/2.88); const perfiles=Math.ceil((largo*2+alto*2)/2.44); const parales=Math.ceil(largo/0.61)+1; const tornillos=Math.ceil(laminas*35); const masillaKg=Math.ceil(areaExtra*.35); const cintaM=Math.ceil(areaExtra*1.2); const pL=parseFloat(document.getElementById("precioLamina").value)||12; const pP=parseFloat(document.getElementById("precioPerfil").value)||4; const pPar=parseFloat(document.getElementById("precioParal").value)||4; const pT=parseFloat(document.getElementById("precioTornillo").value)||.03; const pM=parseFloat(document.getElementById("precioMasilla").value)||1.5; const pC=parseFloat(document.getElementById("precioCinta").value)||.12; const materiales=laminas*pL+perfiles*pP+parales*pPar+tornillos*pT+masillaKg*pM+cintaM*pC; const manoObra=area*mano; const subtotal=materiales+manoObra; const ganancia=subtotal*(margen/100); const total=subtotal+ganancia; ultimaCotizacion={cliente,telefono,ciudad,area,laminas,perfiles,parales,tornillos,masillaKg,cintaM,materiales,manoObra,ganancia,total,fecha:new Date().toLocaleString()}; localStorage.setItem("ultimaCotizacionObraPro",JSON.stringify(ultimaCotizacion)); document.getElementById("resultado").innerHTML=`

Resultado

Cliente: ${cliente}

Área: ${area.toFixed(2)} m²


Láminas: ${laminas}

Perfiles: ${perfiles}

Parales: ${parales}

Tornillos: ${tornillos}

Masilla: ${masillaKg} kg

Cinta: ${cintaM} m


Materiales: ${money(materiales)}

Mano de obra: ${money(manoObra)}

Ganancia: ${money(ganancia)}

Total: ${money(total)}

`; } function getCot(){return ultimaCotizacion||JSON.parse(localStorage.getItem("ultimaCotizacionObraPro"))} function generarPDF(){ const c=getCot(); if(!c){alert("Primero calcula una cotización.");return;} const doc=new window.jspdf.jsPDF(); let y=20; doc.setFontSize(20); doc.text("ObraPro - Cotizacion Drywall",20,y); y+=14; doc.setFontSize(11); ["Fecha: "+c.fecha,"Cliente: "+c.cliente,"Telefono: "+c.telefono,"Ciudad: "+c.ciudad,"Area: "+c.area.toFixed(2)+" m2"].forEach(t=>{doc.text(t,20,y);y+=8}); y+=5; doc.text("Materiales:",20,y); y+=8; ["Laminas: "+c.laminas,"Perfiles: "+c.perfiles,"Parales: "+c.parales,"Tornillos: "+c.tornillos,"Masilla: "+c.masillaKg+" kg","Cinta: "+c.cintaM+" m"].forEach(t=>{doc.text(t,25,y);y+=8}); y+=5; doc.text("Materiales: "+money(c.materiales),20,y); y+=8; doc.text("Mano de obra: "+money(c.manoObra),20,y); y+=8; doc.text("Ganancia: "+money(c.ganancia),20,y); y+=12; doc.setFontSize(16); doc.text("TOTAL: "+money(c.total),20,y); doc.save("cotizacion-obrapro.pdf"); } function enviarWhatsApp(){ const c=getCot(); if(!c){alert("Primero calcula una cotización.");return;} let numero=String(c.telefono||"").replace(/\\D/g,""); if(!numero)numero=prompt("WhatsApp con código de país:"); if(!numero)return; const msg=`Hola ${c.cliente}, tu cotización estimada de drywall es:\\n\\nÁrea: ${c.area.toFixed(2)} m²\\nLáminas: ${c.laminas}\\nPerfiles: ${c.perfiles}\\nParales: ${c.parales}\\nTornillos: ${c.tornillos}\\nMasilla: ${c.masillaKg} kg\\nCinta: ${c.cintaM} m\\n\\nMateriales: ${money(c.materiales)}\\nMano de obra: ${money(c.manoObra)}\\nTotal: ${money(c.total)}\\n\\nGenerado con ObraPro.`; window.open("https://wa.me/"+numero+"?text="+encodeURIComponent(msg),"_blank"); } function guardarCotizacion(){ const c=getCot(); if(!c){alert("Primero calcula una cotización.");return;} const arr=JSON.parse(localStorage.getItem("cotizacionesObraPro"))||[]; arr.push(c); localStorage.setItem("cotizacionesObraPro",JSON.stringify(arr)); alert("Cotización guardada."); } function registrarContratista(){ const empresa=document.getElementById("empresa").value, ciudad=document.getElementById("empresaCiudad").value, servicios=document.getElementById("servicios").value, whatsapp=document.getElementById("whatsapp").value, plan=document.getElementById("plan").value; if(!empresa||!ciudad||!servicios||!whatsapp){alert("Completa todos los campos.");return;} const arr=JSON.parse(localStorage.getItem("contratistasObraPro"))||[]; arr.push({empresa,ciudad,servicios,whatsapp,plan,fecha:new Date().toLocaleString()}); localStorage.setItem("contratistasObraPro",JSON.stringify(arr)); alert("Empresa registrada."); location.href="directorio.html"; } function pintarDirectorio(){ const box=document.getElementById("listaContratistas"); if(!box)return; const data=JSON.parse(localStorage.getItem("contratistasObraPro"))||[]; if(data.length===0){box.innerHTML='

Directorio vacío

Todavía no hay contratistas registrados. El primero aparecerá aquí después de llenar el formulario.

Registrar empresa
';return;} box.innerHTML=data.map(c=>`

${c.empresa}

${c.ciudad}

${c.servicios}

${c.plan}

Contactar
`).join(""); } function crearCuenta(){const email=document.getElementById("regEmail").value,pass=document.getElementById("regPass").value;if(!email||!pass){alert("Completa email y contraseña.");return;}localStorage.setItem("usuarioObraPro",JSON.stringify({email,pass}));alert("Cuenta demo creada.");} function login(){const email=document.getElementById("loginEmail").value,pass=document.getElementById("loginPass").value,u=JSON.parse(localStorage.getItem("usuarioObraPro"));if(u&&u.email===email&&u.pass===pass){localStorage.setItem("sesionObraPro",email);actualizarSesion();alert("Sesión iniciada.");}else alert("Datos incorrectos.");} function logout(){localStorage.removeItem("sesionObraPro");actualizarSesion();} function actualizarSesion(){const s=localStorage.getItem("sesionObraPro"); if(document.getElementById("estadoLogin"))document.getElementById("estadoLogin").textContent=s?"Sesión activa: "+s:"Sin sesión"; if(document.getElementById("usuarioActual"))document.getElementById("usuarioActual").textContent=s||"Sin sesión";} function verCotizaciones(){const box=document.getElementById("adminContenido"),data=JSON.parse(localStorage.getItem("cotizacionesObraPro"))||[];box.innerHTML=data.length?data.map((c,i)=>`

Cotización #${i+1}

${c.cliente} - ${c.ciudad}

Área: ${c.area.toFixed(2)} m²

Total: ${money(c.total)}

`).join(""):"No hay cotizaciones guardadas.";} function verContratistas(){const box=document.getElementById("adminContenido"),data=JSON.parse(localStorage.getItem("contratistasObraPro"))||[];box.innerHTML=data.length?data.map((c,i)=>`

${i+1}. ${c.empresa}

${c.ciudad}

${c.servicios}

${c.whatsapp}

${c.plan}
`).join(""):"No hay contratistas registrados.";} function limpiarDatos(){if(confirm("¿Borrar datos demo?")){localStorage.clear();location.reload();}} document.addEventListener("DOMContentLoaded",()=>{actualizarSesion();pintarDirectorio();}); """ header = """
""" footer = """""" files = { "style.css": style, "app.js": app, "index.html": f""" ObraPro | Calculadora Drywall y Directorio de Contratistas LATAM {header}

Calcula, cotiza y consigue contratistas

ObraPro ayuda a contratistas y clientes en LATAM a calcular materiales, crear presupuestos profesionales, descargar PDF y contactar por WhatsApp.

Ir a calculadora Buscar contratistas Registrar mi empresa

Calculadora Drywall PRO

Calcula área, láminas, perfiles, tornillos, masilla, cinta, mano de obra y ganancia.

✅ Cálculo automático

✅ PDF profesional

✅ WhatsApp automático

Calcular ahora

Herramientas de ObraPro

Todo en páginas separadas, limpio y fácil de usar.

🧮

Calculadora Drywall PRO

Calcula materiales, mano de obra y ganancia.

Ir a calculadora →
📄

PDF de cotización

Genera presupuestos profesionales para clientes.

Generar PDF →
💬

WhatsApp automático

Envía el presupuesto calculado al cliente.

Probar WhatsApp →
👷

Directorio

Contratistas registrados aparecerán aquí.

Ver directorio →
🏢

Registro

Registra empresas con ciudad, servicios y WhatsApp.

Registrarme →

Planes

Gratis, PRO y Premium Web.

Ver planes →
{footer}""", "calculadora.html": f""" Calculadora Drywall PRO | ObraPro {header}

Calculadora Drywall PRO

Calcula materiales, mano de obra, ganancia, PDF y WhatsApp.

Datos del proyecto

Precios base