1/* ======================================== 2 kob.dev â ConsultorÃa IA â Client-side JS 3 ======================================== */ 4 5document.addEventListener('DOMContentLoaded', () => { 6 initI18n(); 7 initScrollAnimations(); 8 initContactForm(); 9 initSmoothScroll(); 10 initServicePreselect(); 11 initAudioPlayers(); 12 initTemaPreselect(); 13 initAudiolibroForm(); 14 initPrimerAgenteForm(); 15}); 16 17/* ---------------------------------------- 18 Preselección del servicio vÃa ?s=<valor> 19 (p. ej. la página de Oferta enlaza a /?s=audit#contacto) 20 ---------------------------------------- */ 21function initServicePreselect() { 22 const params = new URLSearchParams(window.location.search); 23 const s = params.get('s'); 24 if (!s) return; 25 const select = document.getElementById('tipo_servicio'); 26 if (select && [...select.options].some((o) => o.value === s)) { 27 select.value = s; 28 } 29} 30 31/* ---------------------------------------- 32 Internationalization (ES / EN) 33 ---------------------------------------- */ 34const I18N = { 35 es: { 36 'meta.title': 'kob.dev â ConsultorÃa en IA Generativa para Empresas', 37 'meta.desc': 'ConsultorÃa en inteligencia artificial generativa: cómo aplicarla en tu empresa y cómo construir agentes de IA a medida. Diagnóstico y acompañamiento experto.', 38 39 'hero.badge': 'ð¨ð± ConsultorÃa en IA generativa para empresas', 40 'hero.title': 'Aprovecha la IA generativa<br><span class="gradient-text">en tu negocio, de verdad.</span>', 41 'hero.subtitle': 'Te ayudamos a entender <strong>cómo aplicar la inteligencia artificial generativa en tu empresa</strong> y a <strong>construir agentes de IA a medida</strong> que resuelven problemas concretos. Sin humo, con foco en resultados.', 42 'hero.cta1': 'Quiero una consultorÃa', 43 'hero.cta2': 'Ver la oferta', 44 'hero.stat1num': '+20', 45 'hero.stat1label': 'Años en tecnologÃa', 46 'hero.stat3num': 'Agentes', 47 'hero.stat3label': 'de IA a medida', 48 49 'svc.tag': 'Servicios', 50 'svc.h2': 'Tres formas de trabajar juntos', 51 'svc.desc': 'Ya sea que estés empezando a explorar la IA generativa o que quieras construir algo concreto, tenemos una consultorÃa para ti.', 52 'svc.c1title': 'IA generativa aplicada a tu empresa', 53 'svc.c1p': 'Analizamos tus procesos y detectamos <strong>dónde la IA generativa genera valor real</strong>: qué automatizar, qué herramientas usar, cómo integrarla a tu operación y cómo capacitar a tu equipo. Sales con un plan claro y priorizado.', 54 'svc.c1r': 'â Estrategia de adopción de IA con foco en ROI', 55 'svc.c2title': 'Creación de agentes con IA generativa', 56 'svc.c2p': 'Diseñamos y construimos <strong>agentes de IA a medida</strong> para tu negocio. Tenemos experiencia creando asistentes expertos que resuelven consultas, agentes de pentesting, automatizaciones inteligentes e integraciones con tus sistemas â desde la arquitectura hasta la puesta en marcha.', 57 'svc.c2r': 'â Agentes funcionando, integrados a tus herramientas', 58 'svc.c3badge': 'Para startups', 59 'svc.c3title': 'Soluciones end-to-end para startups', 60 'svc.c3p': 'Si tienes una startup, construimos tu solución con IA <strong>de punta a punta</strong>, en plazos cortos y con un precio acorde a tu etapa. Para validar rápido y avanzar sin comprometer tu runway.', 61 'svc.c3r': 'â De la idea a producto en semanas, a precio de startup', 62 'svc.offercta': 'â Ver la oferta: Automatización con Agentes de IA', 63 64 'ag.tag': 'Nuestros agentes', 65 'ag.h2': 'Agentes de IA, a la medida de tu empresa', 66 'ag.desc': 'No son productos cerrados: cada agente se diseña e implementa a la medida de cada cliente, integrado a tus sistemas. Cotiza el que se ajuste a tu necesidad â o cuéntanos uno nuevo.', 67 'ag.a1title': 'Agente de Ley de Protección de Datos (Chile)', 68 'ag.a1p': 'Se conecta a tus sistemas, audita el cumplimiento de la Ley de Protección de Datos de Chile y genera reportes de auditorÃa bajo demanda. Ideal para llegar preparado a la entrada en vigencia de la ley.', 69 'ag.a2title': 'Agente Experto', 70 'ag.a2p': 'Consume grandes volúmenes de información sobre un dominio o nicho âinterno o externoâ y se vuelve experto en él. El objetivo: resolver <strong>más del 90% de las consultas</strong>, liberando el tiempo de tus expertos reales para lo que de verdad lo requiere.', 71 'ag.cta': 'Cotizar', 72 'ag.pymecta': 'â Quiero un agente para mi PYME, desde $49.000 + IVA', 73 74 'how.tag': 'Cómo Funciona', 75 'how.h2': 'De la conversación a resultados concretos', 76 'how.desc': 'Un proceso simple y directo, pensado para que tomes decisiones informadas sobre IA.', 77 'how.c1title': 'Conversación inicial', 78 'how.c1p': 'Nos cuentas tu contexto, tus objetivos y qué te gustarÃa lograr con IA. <strong>Sin costo y sin compromiso.</strong>', 79 'how.c1r': 'â Vemos si podemos ayudarte', 80 'how.c2title': 'Diagnóstico y propuesta', 81 'how.c2p': 'Analizamos tu caso y te presentamos un <strong>plan concreto</strong>: oportunidades, alcance, tiempos y forma de trabajo.', 82 'how.c2r': 'â Un plan claro y accionable', 83 'how.c3title': 'ConsultorÃa o construcción',
84 'how.c3p': 'Trabajamos juntos: asesorÃa estratégica, capacitación a tu equipo o <strong>construcción del agente de IA</strong>, según lo que necesites.', 85 'how.c3r': 'â Avanzas con acompañamiento experto', 86 'how.c4title': 'AutonomÃa', 87 'how.c4p': 'Te dejamos con el conocimiento, la documentación y las herramientas para que <strong>sigas por tu cuenta</strong> si asà lo prefieres.', 88 'how.c4r': 'â Tu equipo empoderado con IA', 89 90 'nos.tag': 'Nosotros', 91 'nos.h2': 'Trayectoria real en tecnologÃa', 92 'nos.p': 'Detrás de kob.dev hay personas con <strong>más de 20 años de experiencia en tecnologÃa</strong>, que combinan seguridad, años creando agentes de IA generativa y una trayectoria construyendo áreas de seguridad, DevOps y software desde cero.', 93 'nos.c1t': '+20 años en tecnologÃa', 94 'nos.c1p': 'Dos décadas diseñando, construyendo y operando sistemas en producción.', 95 'nos.c3t': '3 años en IA generativa', 96 'nos.c3p': 'Diseñando y desplegando agentes de IA generativa para casos reales.', 97 'nos.c4t': 'Seguridad desde cero', 98 'nos.c4p': 'Certificación OSCP y experiencia montando y liderando áreas de seguridad completas dentro de empresas.', 99 'nos.c5t': 'DevOps e infraestructura', 100 'nos.c5p': 'Automatización, nube, CI/CD y operación confiable a escala.', 101 'nos.c6t': 'Desarrollo de software', 102 'nos.c6p': 'IngenierÃa sólida con enfoque Spec-Driven Development.', 103 104 'pil.tag': 'Nuestros pilares', 105 'pil.h2': 'Calidad, seguridad y responsabilidad', 106 'pil.desc': 'Tres pilares que sostienen cada agente que construimos.', 107 'pil.c1t': 'Calidad', 108 'pil.c1p': 'La calidad de un agente no se declara: se mide. Evaluamos cada agente con <strong>evals</strong> (baterÃas de pruebas automatizadas) que verifican precisión y consistencia. Iteramos con métricas, no con impresiones.', 109 'pil.c2t': 'Seguridad', 110 'pil.c2p': 'Seguridad en el ADN del equipo, con <strong>certificación OSCP</strong>. Construimos siguiendo el <strong>OWASP Top 10 for LLM</strong>, mitigando riesgos como la inyección de prompts y la fuga de información sensible.', 111 'pil.c3t': 'Responsabilidad', 112 'pil.c3p': 'Construimos tecnologÃa que <strong>amplÃa las capacidades de las personas</strong>, no que las reemplaza. IA generativa usada de forma responsable, con un impacto positivo para las personas.', 113 114 'why.tag': 'Por qué kob.dev', 115 'why.h2': 'ConsultorÃa práctica, no teorÃa', 116 'why.p': 'La IA generativa cambia rápido y está llena de ruido. Nuestro trabajo es <strong>traducir todo eso en decisiones y sistemas que funcionan en tu realidad</strong>, con un enfoque directo y sin tecnicismos innecesarios.', 117 'why.f1t': 'Foco en tu caso', 118 'why.f1p': 'Nada de recetas genéricas. Trabajamos sobre tus procesos y tus objetivos reales.', 119 'why.f2t': 'De la estrategia al código', 120 'why.f2p': 'Podemos asesorarte y también construir el agente. No nos quedamos en la teorÃa.', 121 'why.f3t': 'Seguridad como base', 122 'why.f3p': 'La seguridad de tus datos es una prioridad en cada solución que construimos.', 123 'why.f4t': 'Transferencia de conocimiento', 124 'why.f4p': 'Te dejamos capacitado para que tu equipo pueda seguir avanzando por su cuenta.', 125 'why.s1span': 'Paso 1', 126 'why.s1p': 'Conversación inicial sin costo', 127 'why.s2span': 'Paso 2', 128 'why.s2p': 'Diagnóstico y propuesta concreta', 129 'why.s3span': 'Paso 3', 130 'why.s3p': 'ConsultorÃa o construcción del agente', 131 'why.s4span': 'Paso 4', 132 'why.s4p': 'Tu equipo autónomo con IA', 133 134 'cli.tag': 'Clientes', 135 'cli.h2': 'Empresas que confÃan en kob.dev', 136 'cli.desc': 'Organizaciones que confÃan en kob.dev para impulsar su tecnologÃa e IA.', 137 138 // Página Oferta 139 'off.metaTitle': 'Oferta: Automatización con Agentes de IA â kob.dev', 140 'off.metaDesc': 'Detectamos qué de tu operación es automatizable con IA y construimos los agentes que lo hacen: del diagnóstico a agentes funcionando en producción, integrados a tus sistemas.', 141 'off.back': 'â Volver al inicio', 142 'off.tag': 'Oferta', 143 'off.h2': 'Automatización con Agentes de IA', 144 'off.lead': 'Detectamos qué parte de tu operación es automatizable con IA y construimos los agentes que lo hacen. Del diagnóstico a agentes funcionando en producción â bien hechos y traspasados.', 145 146 'off.who.t': '¿Para quién es?', 147 'off.who.p': 'Empresas con trabajo manual y repetitivo que sospechan que <strong>parte de su operación se puede automatizar con IA</strong>, pero no saben qué conviene automatizar, con qué prioridad, ni cómo llevarlo a producción de forma confiable.', 148 'off.prob.t': 'El problema que resolvemos', 149 'off.prob.p': 'Hay mucho trabajo automatizable, pero no está claro <strong>qué conviene automatizar ni con qué prioridad</strong>. Comprar herramientas sueltas no resuelve el proceso, y llevar un agente a producción âintegrado, confiable y seguroâ es otra historia. Falta alguien que haga el diagnóstico y, además, lo construya.', 150 'off.what.t': 'Qué es', 151 'off.what.p': 'Primero un <strong>diagnóstico</strong> que mapea qué de tu operación es automatizable y lo prioriza por impacto. Luego <strong>construimos e implementamos los agentes a medida</strong>, integrados a tus sistemas y funcionando en producción â construidos con seguridad desde el diseño. Del plan a algo que funciona.', 152 153 'off.steps.tag': 'Cómo funciona', 154 'off.steps.h2': 'Dos pasos: primero el diagnóstico, luego los agentes', 155 'off.steps.desc': 'Empiezas por un diagnóstico acotado. Si el plan te hace sentido, construimos e implementamos los agentes.', 156 'off.s1.badge': 'Paso 1', 157 'off.s1.hours': 'Alcance fijo', 158 'off.s1.title': 'Diagnóstico de Automatización', 159 'off.s1.p': 'Revisamos tus procesos y detectamos <strong>qué es automatizable con agentes de IA</strong> y qué te falta. Priorizamos por impacto y esfuerzo, y te entregamos un <strong>plan concreto</strong>: qué automatizar, en qué orden, con qué enfoque y una estimación de los costos de terceros (modelos, infraestructura). Sin obligación de continuar.', 160 'off.s1.r': 'â Un roadmap de automatización claro y priorizado', 161 'off.s2.badge': 'Paso 2', 162 'off.s2.hours': 'Cotizado según proyecto', 163 'off.s2.title': 'Implementación de Agentes', 164 'off.s2.p': 'Diseñamos, construimos e integramos los <strong>agentes a medida</strong> del plan: conectados a tus sistemas, con seguridad desde el diseño y funcionando en producción. Te dejamos la documentación y el traspaso para que tu equipo los opere. El alcance se define en el diagnóstico.', 165 'off.s2.r': 'â Agentes funcionando, integrados a tus herramientas', 166 167 'off.not.t': 'Qué NO incluye (a propósito)', 168 'off.not.1': 'Mantención continua ni soporte permanente (se puede contratar aparte).', 169 'off.not.2': 'Los costos de terceros âAPIs de modelos, infraestructura, licenciasâ van por tu cuenta; los estimamos en el diagnóstico.', 170 'off.not.3': 'Automatizar lo que no conviene: te decimos con honestidad qu
170é proceso no vale la pena llevar a un agente.', 171 'off.add.t': 'Add-ons opcionales (no incluidos)', 172 'off.add.1': 'Mantención y evolución de los agentes (mensual).', 173 'off.add.2': 'Nuevos agentes o automatizaciones adicionales.', 174 'off.add.3': 'Capacitación a tu equipo para operar y sacarle provecho a los agentes.', 175 'off.time': 'Plazos: el <strong>diagnóstico se hace en dÃas</strong>. La implementación depende del alcance que definamos en el diagnóstico â con hitos y tiempos acordados antes de empezar.', 176 177 'off.inv.tag': 'Inversión', 178 'off.inv.h2': 'Diagnóstico a precio fijo; implementación cotizada', 179 'off.inv.p': 'El <strong>diagnóstico</strong> es un trabajo acotado con <strong>precio fijo</strong>. La <strong>implementación</strong> se cotiza según el alcance que definamos juntos â un número acordado, no un medidor corriendo. Con precios preferentes para <strong>startups y fundaciones</strong>. El precio se entrega en la conversación inicial.', 180 'off.inv.cta': 'Solicitar diagnóstico', 181 'off.why.t': 'Por qué kob.dev', 182 'off.why.p': 'Más de 20 años en tecnologÃa y 3 años construyendo agentes de IA en producción, con un background de seguridad (OSCP). No solo te decimos qué automatizar: lo construimos, lo integramos y te decimos la verdad sobre qué conviene y qué no.', 183 'off.inv.note': 'Precio del diagnóstico fijo y acordado antes de empezar. Sin sorpresas.', 184 185 'fund.tag': 'Fundación', 186 'fund.h2': 'Fundación kob.dev', 187 'fund.lead': 'Creemos que la IA generativa deberÃa ampliar las capacidades humanas, no debilitarlas.', 188 'fund.p': 'Investigamos cómo usar la inteligencia artificial generativa para <strong>potenciar el pensamiento, el aprendizaje y el criterio</strong> de las personas, y cómo evitar el deterioro cognitivo asociado a su mal uso. No es una preocupación abstracta: la investigación reciente ya documenta una «deuda cognitiva» por el uso excesivo de IA, con efectos en la memoria y el pensamiento crÃtico.', 189 'fund.ref': 'Referencia: <a href="https://www.media.mit.edu/projects/your-brain-on-chatgpt/" target="_blank" rel="noopener">«Your Brain on ChatGPT», MIT Media Lab (2025)</a>', 190 'fund.cta': 'Conocer la Fundación', 191 192 'ct.tag': 'Hablemos', 193 'ct.h2': 'Cuéntanos tu proyecto', 194 'ct.p': 'Si te interesa aplicar IA generativa en tu empresa o construir un agente a medida, <strong>escrÃbenos y te respondemos personalmente</strong>. La conversación inicial es sin costo y sin compromiso.', 195 196 'form.nombre': 'Nombre *', 197 'form.email': 'Correo electrónico *', 198 'form.tipo': '¿Qué tipo de consultorÃa te interesa? *', 199 'form.razon': 'Razón de contacto *', 200 'form.ph.nombre': 'Ej: MarÃa González', 201 'form.ph.email': '[email protected]', 202 'form.ph.razon': 'Cuéntame sobre tu empresa, qué problema quieres resolver y qué te gustarÃa lograr con IA.', 203 'form.opt0': 'Selecciona una opción', 204 'form.opt1': 'Aplicar IA generativa en mi empresa', 205 'form.opt2': 'Creación de agentes con IA generativa',
206 'form.opt3': 'Solución end-to-end para startups', 207 'form.opt4': 'Fundación kob.dev', 208 'form.opt5': 'Automatización con Agentes de IA', 209 'form.opt6': 'Cotizar Agente Protección de Datos', 210 'form.opt7': 'Cotizar Agente Experto', 211 'form.submit': 'Enviar mensaje', 212 'form.loading': 'Enviando...', 213 'form.msg.success': '¡Mensaje enviado! Te responderemos lo antes posible.', 214 'form.msg.error': 'Hubo un error. Intenta de nuevo o escrÃbeme directamente a [email protected].', 215 216 'foot.tagline': 'ConsultorÃa en IA generativa.', 217 'foot.l1': 'Servicios', 218 'foot.lagentes': 'Nuestros agentes', 219 'foot.l2': 'Nosotros', 220 'foot.l3': 'Cómo Funciona', 221 'foot.l4': 'Nuestros pilares', 222 'foot.loferta': 'Oferta', 223 'foot.lprimer': 'Tu primer agente', 224 'foot.lmineria': 'Proveedores de la minerÃa', 225 'foot.lquimica': 'Industria quÃmica', 226 'foot.ltransporte': 'Transporte y logÃstica', 227 'foot.lconstruccion': 'Construcción industrial y minera', 228 'foot.lclientes': 'Clientes', 229 'foot.lfund': 'Fundación', 230 'foot.l5': 'Contacto', 231 'foot.lteam': 'Acerca de', 232 'foot.bottom': '© 2026 kob.dev â Santiago, Chile ð¨ð±', 233 234 // Página Acerca de 235 'team.metaTitle': 'Acerca de â kob.dev', 236 'team.metaDesc': 'Conoce quién está detrás de kob.dev. Nicolás Mery, fundador: lÃder tecnológico con más de 20 años en tecnologÃa y más de 8 como CTO y Chief Architect en IA, seguridad y fintech.', 237 'team.back': 'â Volver al inicio', 238 'team.tag': 'Acerca de', 239 'team.h2': 'Quién está detrás de kob.dev', 240 'team.desc': 'Trayectoria real construyendo tecnologÃa, seguridad e IA generativa.', 241 'team.name': 'Nicolás Mery', 242 'team.role': 'Fundador', 243 'team.bio': 'LÃder tecnológico con <strong>más de 20 años en tecnologÃa y más de 8 como CTO y Chief Architect</strong> en fintech, cripto, pagos, ciberseguridad y SaaS. Hoy enfocado en construir <strong>sistemas de agentes de IA generativa</strong> en producción. Certificado OSCP y alumni del programa CTO del MIT.', 244 'team.chip1': '+8 años como CTO', 245 'team.chip2': 'IA generativa', 246 'team.chip3': 'OSCP', 247 'team.chip4': 'MIT CTO Program', 248 'team.cta1': 'Hablemos', 249 'team.cta2': 'Ver servicios', 250 251 // Audios para saber más (/i-know-kung-fu) 252 'foot.laudios': 'Audios para saber más', 253 'kf.metaTitle': 'Audios para saber más â kob.dev', 254 'kf.metaDesc': 'Investigaciones propias del equipo kob.dev en tres profundidades: un resumen de 3 minutos, una versión de 30 minutos y el audiolibro completo.', 255 'kf.back': 'â kob.dev', 256 'kf.tag': 'Investigación', 257 'kf.h1': 'Audios para saber más', 258 'kf.subtitle': 'Investigaciones propias del equipo kob.dev, narradas en audio. Cada tema está en <strong>tres profundidades</strong>: escucha los resúmenes aquà mismo y, si el tema te atrapa, pide el audiolibro completo.', 259 'kf.n1.dur': '3 minutos', 260 'kf.n1.h': '¿Me interesa esto?', 261 'kf.n1.p': 'Lo justo para saber de qué va el tema y decidir si merece tu tiempo.', 262 'kf.n2.dur': '30 minutos', 263 'kf.n2.h': 'Quiero entenderlo', 264 'kf.n2.p': 'La versión que deja con criterio formado: los conceptos, los fallos y las decisiones que importan.', 265 'kf.n3.dur': 'Audiolibro', 266 'kf.n3.h': 'Quiero investigar', 267 'kf.n3.p': 'La investigación completa, de una a tres horas: el estado del arte con sus fuentes y sus zonas grises.', 268 'kf.aviso': '<strong>Los audios están narrados en inglés.</strong> Es una consecuencia del pipeline con el que los generamos, no una decisión editorial. Estamos trabajando en versiones en español y otros idiomas. La investigación escrita está en español.', 269 'kf.t1.h': 'IngenierÃa de contexto', 270 'kf.t1.meta': '44 notas · ~32.000 palabras · estado del arte a julio de 2026', 271 'kf.t1.desc': 'Cómo se decide qué entra en la ventana de contexto de un modelo y qué se queda fuera. Cubre los modos de fallo â<em>context rot</em>, <em>lost in the middle</em>â, las cuatro estrategias de escribir, seleccionar, comprimir y aislar, la memoria de agentes, el debate entre RAG y contexto largo, el diseño de herramientas y las arquitecturas multi-agente.', 272 'kf.t2.h': 'Loop engineering', 273 'kf.t2.meta': '26 notas · ~17.500 palabras · estado del arte a julio de 2026', 274 'kf.t2.desc': 'El diseño de bucles agénticos: cuándo dejar que un agente itere solo y, sobre todo, cómo saber que debe detenerse. Cubre los cinco niveles de verificación, las condiciones de parada, las arquitecturas <em>maker-checker</em>, los guardrails y presupuestos, y los riesgos âdeuda de comprensión, rendición cognitiva, <em>reward hacking</em>.', 275 'kf.t2.nota': 'La investigación advierte que la base empÃrica del campo todavÃa es delgada, y marca nota por nota de dónde viene cada afirmación.', 276 'kf.f.corto': 'Resumen de 3 minutos', 277 'kf.f.medio': 'Versión profunda', 278 'kf.f.libro': 'Audiolibro completo', 279 'kf.f.pronto': 'Aún en preparación', 280 'kf.descargar': 'Descargar', 281 'kf.pedir': 'Pedir el audiolibro', 282 'kf.pronto': 'Próximamente', 283 'kf.mas': 'Seguimos investigando. Iremos publicando aquà los temas nuevos a medida que estén listos.', 284 'kf.a11y.play': 'Reproducir', 285 'kf.a11y.pause': 'Pausar', 286 'kf.p.tag': 'Audiolibro', 287 'kf.p.h2': 'Pide el audiolibro completo', 288 'kf.p.p': 'Los audiolibros son archivos grandes y los enviamos a mano, uno por uno. <strong>Déjanos tus datos y te escribimos con el enlace de descarga.</strong> Incluye el MP3 y el M4B con capÃtulos navegables.', 289 'kf.p.tema': '¿Qué audiolibro quieres? *', 290 'kf.p.ph.tema': 'Ej: IngenierÃa de contexto', 291 'kf.p.hint.tema': 'Si el tema que buscas todavÃa no está publicado, escrÃbelo igual: nos sirve para saber qué investigar.', 292 'kf.p.motivo': '¿Para qué lo quieres? (opcional)', 293 'kf.p.ph.motivo': 'Nos ayuda a saber qué investigar después.', 294 'kf.p.submit': 'Pedir el audiolibro', 295 'kf.p.msg.success': '¡Listo! Te escribiremos a tu correo con el enlace de descarga.', 296 'kf.p.msg.error': 'No pudimos registrar tu pedido. Vuelve a intentarlo o escrÃbenos a [email protected].', 297 298 // Landing "Tu primer agente" (/tu-primer-agente). Va sólo en español âel segmento 299 // es la PYME chilenaâ, pero necesita estas claves porque applyLang() fija el 300 // <title>
300, la meta description y los mensajes del formulario en cada carga. 301 'pa.metaTitle': 'Tu primer agente, desde $49.000 â kob.dev', 302 'pa.metaDesc': 'Construyo la primera versión de un agente que hace esa tarea repetitiva que le quita horas a tu empresa, con tus datos reales. En una semana, y si no sigues el código es tuyo.', 303 'pa.msg.success': '¡Listo! Recibà tu caso. Te respondo en 48 horas, sea que sirva o no.', 304 'pa.msg.error': 'No pudimos enviar tu caso. Vuelve a intentarlo o escrÃbeme a [email protected].', 305 'pa.msg.herramientas': 'Marca al menos una herramienta en la pregunta 5.', 306 }, 307 en: { 308 'meta.title': 'kob.dev â Generative AI Consulting for Companies', 309 'meta.desc': 'Generative AI consulting: how to apply it in your company and how to build custom AI agents. Expert diagnosis and hands-on guidance.', 310 311 'hero.badge': 'ð¨ð± Generative AI consulting for companies', 312 'hero.title': 'Put generative AI to work<br><span class="gradient-text">in your business, for real.</span>', 313 'hero.subtitle': 'We help you understand <strong>how to apply generative AI in your company</strong> and <strong>build custom AI agents</strong> that solve real problems. No hype â just results.', 314 'hero.cta1': 'I want a consultation', 315 'hero.cta2': 'See the offer', 316 'hero.stat1num': '+20', 317 'hero.stat1label': 'Years in technology', 318 'hero.stat3num': 'Agents', 319 'hero.stat3label': 'custom-built AI', 320 321 'svc.tag': 'Services', 322 'svc.h2': 'Three ways to work together', 323 'svc.desc': 'Whether you are just starting to explore generative AI or you want to build something concrete, we have a consulting track for you.', 324 'svc.c1title': 'Generative AI applied to your company', 325 'svc.c1p': 'We analyze your processes and pinpoint <strong>where generative AI creates real value</strong>: what to automate, which tools to use, how to integrate it into your operation and how to train your team. You leave with a clear, prioritized plan.', 326 'svc.c1r': 'â AI adoption strategy focused on ROI', 327 'svc.c2title': 'Building agents with generative AI', 328 'svc.c2p': 'We design and build <strong>custom AI agents</strong> for your business. We have experience creating expert assistants that resolve queries, pentesting agents, intelligent automations and integrations with your systems â from architecture to going live.', 329 'svc.c2r': 'â Agents running, integrated with your tools', 330 'svc.c3badge': 'For startups', 331 'svc.c3title': 'End-to-end solutions for startups', 332 'svc.c3p': 'If you\'re a startup, we build your AI solution <strong>end-to-end</strong>, in short timeframes and at a price that fits your stage. To validate fast and move forward without burning your runway.', 333 'svc.c3r': 'â From idea to product in weeks, at startup pricing', 334 'svc.offercta': 'â See the offer: Automation with AI Agents', 335 336 'ag.tag': 'Our agents', 337 'ag.h2': 'AI agents, tailored to your company', 338 'ag.desc': 'They are not off-the-shelf products: each agent is designed and implemented to fit each client, integrated with your systems. Get a quote for the one that fits your need â or tell us about a new one.', 339 'ag.a1title': 'Data Protection Law Agent (Chile)', 340 'ag.a1p': 'It connects to your systems, audits compliance with Chile\'s Data Protection Law and generates audit reports on demand. Ideal to arrive prepared for the law coming into force.', 341 'ag.a2title': 'Expert Agent', 342 'ag.a2p': 'It ingests large volumes of information about a domain or niche âinternal or externalâ and becomes an expert in it. The goal: resolve <strong>more than 90% of queries</strong>, freeing up your real experts\' time for what truly needs it.', 343 'ag.cta': 'Get a quote', 344 'ag.pymecta': 'â I want an agent for my small business, from CLP $49,000 + VAT', 345 346 'how.tag': 'How It Works', 347 'how.h2': 'From conversation to concrete results', 348 'how.desc': 'A simple, direct process designed to help you make informed decisions about AI.', 349 'how.c1title': 'Initial conversation',
350 'how.c1p': 'You tell us your context, your goals and what you would like to achieve with AI. <strong>Free and with no commitment.</strong>', 351 'how.c1r': 'â We find out if we can help you', 352 'how.c2title': 'Diagnosis and proposal', 353 'how.c2p': 'We analyze your case and present a <strong>concrete plan</strong>: opportunities, scope, timelines and how we would work.', 354 'how.c2r': 'â A clear, actionable plan', 355 'how.c3title': 'Consulting or building', 356 'how.c3p': 'We work together: strategic advisory, training for your team or <strong>building the AI agent</strong>, depending on what you need.', 357 'how.c3r': 'â You move forward with expert support', 358 'how.c4title': 'Autonomy', 359 'how.c4p': 'We leave you with the knowledge, documentation and tools so your team can <strong>carry on independently</strong> if you prefer.', 360 'how.c4r': 'â Your team empowered with AI', 361 362 'nos.tag': 'About us', 363 'nos.h2': 'A real track record in technology', 364 'nos.p': 'Behind kob.dev are people with <strong>more than 20 years of experience in technology</strong>, combining security, years building generative AI agents and a track record of building security functions, DevOps and software from scratch.', 365 'nos.c1t': '20+ years in technology', 366 'nos.c1p': 'Two decades designing, building and operating systems in production.', 367 'nos.c3t': '3 years in generative AI', 368 'nos.c3p': 'Designing and deploying generative AI agents for real use cases.', 369 'nos.c4t': 'Security from scratch', 370 'nos.c4p': 'OSCP certification and experience building and leading complete security functions inside companies.',
371 'nos.c5t': 'DevOps & infrastructure', 372 'nos.c5p': 'Automation, cloud, CI/CD and reliable operation at scale.', 373 'nos.c6t': 'Software development', 374 'nos.c6p': 'Solid engineering with a Spec-Driven Development approach.', 375 376 'pil.tag': 'Our pillars', 377 'pil.h2': 'Quality, security and responsibility', 378 'pil.desc': 'Three pillars behind every agent we build.', 379 'pil.c1t': 'Quality', 380 'pil.c1p': 'An agent\'s quality isn\'t claimed: it\'s measured. We evaluate every agent with <strong>evals</strong> (automated test suites) that check accuracy and consistency. We iterate with metrics, not impressions.', 381 'pil.c2t': 'Security', 382 'pil.c2p': 'Security is in the team\'s DNA, with <strong>OSCP certification</strong>. We build following the <strong>OWASP Top 10 for LLM</strong>, mitigating risks like prompt injection and sensitive data leakage.', 383 'pil.c3t': 'Responsibility', 384 'pil.c3p': 'We build technology that <strong>expands people\'s capabilities</strong>, not replaces them. Generative AI used responsibly, with a positive impact on people.', 385 386 'why.tag': 'Why kob.dev', 387 'why.h2': 'Practical consulting, not theory', 388 'why.p': 'Generative AI moves fast and is full of noise. Our job is to <strong>turn all of that into decisions and systems that work in your reality</strong>, with a direct approach and no unnecessary jargon.', 389 'why.f1t': 'Focused on your case', 390 'why.f1p': 'No generic recipes. We work on your processes and your real goals.', 391 'why.f2t': 'From strategy to code', 392 'why.f2p': 'We can advise you and also build the agent. We don\'t stop at theory.', 393 'why.f3t': 'Security by default', 394 'why.f3p': 'The security of your data is a priority in every solution we build.', 395 'why.f4t': 'Knowledge transfer', 396 'why.f4p': 'We leave you trained so your team can keep moving forward on its own.', 397 'why.s1span': 'Step 1', 398 'why.s1p': 'Free initial conversation', 399 'why.s2span': 'Step 2', 400 'why.s2p': 'Diagnosis and concrete proposal', 401 'why.s3span': 'Step 3', 402 'why.s3p': 'Consulting or building the agent', 403 'why.s4span': 'Step 4', 404 'why.s4p': 'Your team autonomous with AI', 405 406 'cli.tag': 'Clients', 407 'cli.h2': 'Companies that trust kob.dev', 408 'cli.desc': 'Organizations that trust kob.dev to drive their technology and AI.', 409 410 // Offer page 411 'off.metaTitle': 'Offer: Automation with AI Agents â kob.dev', 412 'off.metaDesc': 'We find what in your operation can be automated with AI and build the agents that do it: from diagnosis to agents running in production, integrated with your systems.', 413 'off.back': 'â Back to home', 414 'off.tag': 'Offer', 415 'off.h2': 'Automation with AI Agents', 416 'off.lead': 'We find what part of your operation can be automated with AI and build the agents that do it. From diagnosis to agents running in production â done well and handed over.', 417 418 'off.who.t': 'Who it\'s for', 419 'off.who.p': 'Companies with manual, repetitive work who suspect <strong>part of their operation can be automated with AI</strong>, but don\'t know what to automate, in what priority, or how to take it to production reliably.', 420 'off.prob.t': 'The problem we solve', 421 'off.prob.p': 'There\'s a lot of automatable work, but it\'s not clear <strong>what\'s worth automating or in what order</strong>. Buying scattered tools doesn\'t fix the process, and getting an agent into production âintegrated, reliable and secureâ is another story. What\'s missing is someone who does the diagnosis and also builds it.', 422 'off.what.t': 'What it is', 423 'off.what.p': 'First a <strong>diagnosis</strong> that maps what in your operation can be automated and prioritizes it by impact. Then we <strong>build and deploy the custom agents</strong>, integrated with your systems and running in production â built secure by design. From the plan to something that works.', 424 425 'off.steps.tag': 'How it works', 426 'off.steps.h2': 'Two steps: first the diagnosis, then the agents', 427 'off.steps.desc': 'You start with a scoped diagnosis. If the plan makes sense, we build and deploy the agents.', 428 'off.s1.badge': 'Step 1', 429 'off.s1.hours': 'Fixed scope', 430 'off.s1.title': 'Automation Diagnosis', 431 'off.s1.p': 'We review your processes and find <strong>what can be automated with AI agents</strong> and what you\'re missing. We prioritize by impact and effort and hand you a <strong>concrete plan</strong>: what to automate, in what order, with what approach, and an estimate of third-party costs (models, infrastru
431cture). No obligation to continue.', 432 'off.s1.r': 'â A clear, prioritized automation roadmap', 433 'off.s2.badge': 'Step 2', 434 'off.s2.hours': 'Scoped per project', 435 'off.s2.title': 'Agent Implementation', 436 'off.s2.p': 'We design, build and integrate the <strong>custom agents</strong> from the plan: connected to your systems, secure by design and running in production. We leave you documentation and a handover so your team can operate them. The scope is defined in the diagnosis.', 437 'off.s2.r': 'â Agents running, integrated with your tools', 438 439 'off.not.t': 'What it does NOT include (on purpose)', 440 'off.not.1': 'Ongoing maintenance or permanent support (available separately).', 441 'off.not.2': 'Third-party costs âmodel APIs, infrastructure, licensesâ are on you; we estimate them in the diagnosis.', 442 'off.not.3': 'Automating what isn\'t worth it: we honestly tell you which process shouldn\'t become an agent.', 443 'off.add.t': 'Optional add-ons (not bundled)', 444 'off.add.1': 'Maintenance and evolution of the agents (monthly).', 445 'off.add.2': 'New agents or additional automations.', 446 'off.add.3': 'Training for your team to operate and get the most out of the agents.', 447 'off.time': 'Timeline: the <strong>diagnosis takes days</strong>. Implementation depends on the scope we define in the diagnosis â with milestones and timings agreed before we start.', 448 449 'off.inv.tag': 'Investment', 450 'off.inv.h2': 'Fixed-price diagnosis; scoped implementation', 451 'off.inv.p': 'The <strong>diagnosis</strong> is a scoped engagement at a <strong>fixed price</strong>. <strong>Implementation</strong> is quoted based on the scope we define together â one agreed number, not a running meter. With preferential pricing for <strong>startups and foundations</strong>. The price is shared in the initial conversation.', 452 'off.inv.cta': 'Request a diagnosis', 453 'off.why.t': 'Why kob.dev', 454 'off.why.p': '20+ years in tech and 3 years building AI agents in production, with a security background (OSCP). We don\'t just tell you what to automate: we build it, integrate it and tell you the truth about what\'s worth it and what isn\'t.', 455 'off.inv.note': 'Diagnosis price fixed and agreed before we start. No surprises.', 456 457 'fund.tag': 'Foundation', 458 'fund.h2': 'kob.dev Foundation', 459 'fund.lead': 'We believe generative AI should expand human capabilities, not weaken them.', 460 'fund.p': 'We research how to use generative AI to <strong>strengthen people\'s thinking, learning and judgment</strong>, and how to avoid the cognitive decline linked to its misuse. This is not an abstract concern: recent research already documents a âcognitive debtâ from overusing AI, with effects on memory and critical thinking.', 461 'fund.ref': 'Reference: <a href="https://www.media.mit.edu/projects/your-brain-on-chatgpt/" target="_blank" rel="noopener">âYour Brain on ChatGPTâ, MIT Media Lab (2025)</a>', 462 'fund.cta': 'About the Foundation', 463 464 'ct.tag': 'Let\'s talk', 465 'ct.h2': 'Tell us about your project', 466 'ct.p': 'If you are interested in applying generative AI in your company or building a custom agent, <strong>write to us and we\'ll reply personally</strong>. The initial conversation is free and with no commitment.', 467 468 'form.nombre': 'Name *', 469 'form.email': 'Email *', 470 'form.tipo': 'What kind of consulting are you interested in? *', 471 'form.razon': 'Reason for contact *', 472 'form.ph.nombre': 'E.g. Maria Gonzalez', 473 'form.ph.email': '[email protected]', 474 'form.ph.razon': 'Tell me about your company, what problem you want to solve and what you would like to achieve with AI.', 475 'form.opt0': 'Select an option', 476 'form.opt1': 'Apply generative AI in my company', 477 'form.opt2': 'Build agents with generative AI', 478 'form.opt3': 'End-to-end solution for startups', 479 'form.opt4': 'kob.dev Foundation', 480 'form.opt5': 'Automation with AI Agents', 481 'form.opt6': 'Quote: Data Protection Agent', 482 'form.opt7': 'Quote: Expert Agent', 483 'form.submit': 'Send message', 484 'form.loading': 'Sending...', 485 'form.msg.success': 'Message sent! We\'ll get back to you as soon as possible.', 486 'form.msg.error': 'Something went wrong. Please try again or email me directly at [email protected].', 487 488 'foot.tagline': 'Generative AI consulting.', 489 'foot.l1': 'Services', 490 'foot.lagentes': 'Our agents', 491 'foot.l2': 'About us', 492 'foot.l3': 'How It Works', 493 'foot.l4': 'Our pillars', 494 'foot.loferta': 'Offer', 495 'foot.lprimer': 'Your first agent', 496 'foot.lmineria': 'Mining suppliers', 497 'foot.lquimica': 'Chemical industry', 498 'foot.ltransporte': 'Transport and logistics', 499 'foot.lconstruccion': 'Industrial and mining construction', 500 'foot.lclientes': 'Clients', 501 'foot.lfund': 'Foundation', 502 'foot.l5': 'Contact', 503 'foot.lteam': 'About us', 504 'foot.bottom': '© 2026 kob.dev â Santiago, Chile ð¨ð±', 505 506 // About us page 507 'team.metaTitle': 'About us â kob.dev', 508 'team.metaDesc': 'Meet who is behind kob.dev. Nicolás Mery, founder: technology leader with 20+ years in tech and 8+ as CTO and Chief Architect in AI, security and fintech.', 509 'team.back': 'â Back to home', 510 'team.tag': 'About us', 511 'team.h2': 'Who is behind kob.dev', 512 'team.desc': 'A real track record building technology, security and generative AI.', 513 'team.name': 'Nicolás Mery', 514 'team.role': 'Founder', 515 'team.bio': 'Technology leader with <strong>20+ years in tech and 8+ as CTO and Chief Architect</strong> across fintech, crypto, payments, cybersecurity and SaaS. Now focused on building production <strong>generative AI agent systems</strong>. OSCP-certified and MIT CTO Program alumnus.', 516 'team.chip1': '8+ years as CTO', 517 'team.chip2': 'Generative AI', 518 'team.chip3': 'OSCP', 519 'team.chip4': 'MIT CTO Program', 520 'team.cta1': "Let's talk", 521 'team.cta2': 'See services', 522 523 // Audios para saber más (/i-know-kung-fu) 524 'foot.laudios': 'Audio to go deeper', 525 'kf.metaTitle': 'Audio to go deeper â kob.dev', 526 'kf.metaDesc': "Our own research at three depths: a 3-minute summary, a 30-minute version and the full audiobook.", 527 'kf.back': 'â kob.dev', 528 'kf.tag': 'Research', 529 'kf.h1': 'Audio to go deeper', 530 'kf.subtitle': "The kob.dev team's own research, narrated. Every topic comes at <strong>three depths</strong>: listen to the summaries right here and, if the topic grabs you, request the full audiobook.", 531 'kf.n1.dur': '3 minutes', 532 'kf.n1.h': 'Is this for me?', 533 'kf.n1.p': "Just enough to know what the topic is about and decide whether it's worth your time.", 534 'kf.n2.dur': '30 minutes', 535 'kf.n2.h': 'I want to understand it', 536 'kf.n2.p': 'The version that leaves you with real judgement: the concepts, the failure modes and the decisions that matter.', 537 'kf.n3.dur': 'Audiobook', 538 'kf.n3.h': 'I want to dig in', 539 'kf.n3.p': 'The full research, one to three hours: the state of the art with its sources and its grey areas.', 540 'kf.aviso': '<strong>The audio is narrated in English.</strong> That comes from the pipeline we generate it with, not from an editorial choice. We are working on Spanish and other languages. The written research is in Spanish.', 541 'kf.t1.h': 'Context engineering', 542 'kf.t1.meta': '44 notes · ~32,000 words · state of the art as of July 2026', 543 'kf.t1.desc': 'How you decide what goes into a model\'s context window and what stays out. Covers the failure modes â<em>context rot</em>, <em>lost in the middle</em>â, the four strategies of write, select, compress and isolate, agent memory, the RAG versus long-context debate, tool design and multi-agent architectures.', 544 'kf.t2.h': 'Loop engineering', 545 'kf.t2.meta': '26 notes · ~17,500 words · state of the art as of July 2026', 546 'kf.t2.desc': 'The design of agentic loops: when to let an agent iterate on its own and, above all, how to know it should stop. Covers the five levels of verification, stopping conditions, <em>maker-checker</em> architectures, guardrails and budgets, and the risks âcomprehension debt,
546cognitive surrender, <em>reward hacking</em>.', 547 'kf.t2.nota': 'The research warns that the empirical base of this field is still thin, and flags note by note where each claim comes from.', 548 'kf.f.corto': '3-minute summary', 549 'kf.f.medio': 'In-depth version', 550 'kf.f.libro': 'Full audiobook', 551 'kf.f.pronto': 'Still in production', 552 'kf.descargar': 'Download', 553 'kf.pedir': 'Request the audiobook', 554 'kf.pronto': 'Coming soon', 555 'kf.mas': "We keep researching. New topics will show up here as they're ready.", 556 'kf.a11y.play': 'Play', 557 'kf.a11y.pause': 'Pause', 558 'kf.p.tag': 'Audiobook', 559 'kf.p.h2': 'Request the full audiobook', 560 'kf.p.p': 'Audiobooks are large files and we send them by hand, one by one. <strong>Leave us your details and we will email you the download link.</strong> Includes the MP3 and the M4B with navigable chapters.', 561 'kf.p.tema': 'Which audiobook do you want? *', 562 'kf.p.ph.tema': 'e.g. Context engineering', 563 'kf.p.hint.tema': "If the topic you're after isn't published yet, write it anyway â it tells us what to research next.", 564 'kf.p.motivo': 'What do you want it for? (optional)', 565 'kf.p.ph.motivo': 'It helps us know what to research next.', 566 'kf.p.submit': 'Request the audiobook', 567 'kf.p.msg.success': "Done! We'll email you the download link.", 568 'kf.p.msg.error': 'We could not register your request. Please try again or write to [email protected].', 569 570 // La landing "Tu primer agente" no se traduce: si el visitante tiene el sitio 571 // en inglés y llega igual, ve la página en español, que es como está escrita. 572 'pa.metaTitle': 'Tu primer agente, desde $49.000 â kob.dev', 573 'pa.metaDesc': 'Construyo la primera versión de un agente que hace esa tarea repetitiva que le quita horas a tu empresa, con tus datos reales. En una semana, y si no sigues el código es tuyo.', 574 'pa.msg.success': '¡Listo! Recibà tu caso. Te respondo en 48 horas, sea que sirva o no.', 575 'pa.msg.error': 'No pudimos enviar tu caso. Vuelve a intentarlo o escrÃbeme a [email protected].', 576 'pa.msg.herramientas': 'Marca al menos una herramienta en la pregunta 5.', 577 }, 578}; 579 580// Current active language (used by the contact form for its messages). 581let currentLang = 'es'; 582 583function t(key) { 584 const dict = I18N[currentLang] || I18N.es; 585 return dict[key] != null ? dict[key] : (I18N.es[key] != null ? I18N.es[key] : key); 586} 587 588// El sitio parte siempre en español (mercado chileno y versión que indexan los 589// buscadores). El inglés se muestra solo si la persona lo eligió con el botón. 590function detectLang() { 591 const saved = localStorage.getItem('lang'); 592 return saved === 'en' ? 'en' : 'es'; 593} 594 595function applyLang(lang, persist = false) { 596 currentLang = I18N[lang] ? lang : 'es'; 597 598 // Text / inline-HTML content 599 document.querySelectorAll('[data-i18n]').forEach((el) => { 600 const key = el.getAttribute('data-i18n'); 601 const val = t(key); 602 if (val != null) el.innerHTML = val; 603 }); 604 605 // Placeholders 606 document.querySelectorAll('[data-i18n-ph]').forEach((el) => { 607 const key = el.getAttribute('data-i18n-ph'); 608 const val = t(key); 609 if (val != null) el.setAttribute('placeholder', val); 610 }); 611 612 // aria-labels (p. ej. el botón de reproducir del reproductor de audio) 613 document.querySelectorAll('[data-i18n-aria]').forEach((el) => { 614 const key = el.getAttribute('data-i18n-aria'); 615 const val = t(key); 616 if (val != null) el.setAttribute('aria-label', val); 617 }); 618 619 // Document metadata (each page may override which keys to use via data attributes on <body>) 620 document.documentElement.setAttribute('lang', currentLang === 'en' ? 'en' : 'es-CL'); 621 const titleKey = document.body.dataset.titleKey || 'meta.title'; 622 const descKey = document.body.dataset.descKey || 'meta.desc'; 623 document.title = t(titleKey); 624 const metaDesc = document.getElementById('meta-desc'); 625 if (metaDesc) metaDesc.setAttribute('content', t(descKey)); 626 627 // Switcher active state 628 document.querySelectorAll('#lang-switch button').forEach((btn) => { 629 btn.classList.toggle('active', btn.getAttribute('data-lang') === currentLang); 630 btn.setAttribute('aria-pressed', String(btn.getAttribute('data-lang') === currentLang)); 631 }); 632 633 if (persist) localStorage.setItem('lang', currentLang); 634} 635 636function initI18n() { 637 // Páginas de un solo idioma (p. ej. las landings por industria) lo declaran con 638 // <body data-single-lang="es">: conservan su <title>, su descripción y su lang 639 // del HTML, sin depender del idioma del navegador de quien las visita. 640 const singleLang = document.body.dataset.singleLang; 641 if (singleLang) { 642 currentLang = I18N[singleLang] ? singleLang : 'es'; 643 return; 644 } 645 646 applyLang(detectLang()); 647 648 const sw = document.getElementById('lang-switch'); 649 if (sw) { 650 sw.addEventListener('click', (e) => { 651 const btn = e.target.closest('button[data-lang]'); 652 if (!btn) return; 653 applyLang(btn.getAttribute('data-lang'), true); 654 }); 655 } 656} 657 658/* ---------------------------------------- 659 Scroll Animations (IntersectionObserver) 660 ---------------------------------------- */ 661function initScrollAnimations() { 662 const elements = document.querySelectorAll('.animate-on-scroll'); 663 if (!elements.length) return; 664 665 const observer = new IntersectionObserver((entries) => {
666 entries.forEach((entry, index) => { 667 if (entry.isIntersecting) { 668 // Stagger the animation slightly for each card 669 setTimeout(() => { 670 entry.target.classList.add('visible'); 671 }, index * 80); 672 observer.unobserve(entry.target); 673 } 674 }); 675 }, { 676 threshold: 0.15, 677 rootMargin: '0px 0px -40px 0px' 678 }); 679 680 elements.forEach(el => observer.observe(el)); 681} 682 683/* ---------------------------------------- 684 Smooth Scroll for anchor links 685 ---------------------------------------- */ 686function initSmoothScroll() { 687 document.querySelectorAll('a[href^="#"]').forEach(anchor => { 688 anchor.addEventListener('click', (e) => { 689 e.preventDefault(); 690 const target = document.querySelector(anchor.getAttribute('href')); 691 if (target) { 692 target.scrollIntoView({ behavior: 'smooth', block: 'start' }); 693 } 694 }); 695 }); 696} 697 698/* ---------------------------------------- 699 Contact Form Submission 700 ---------------------------------------- */ 701function initContactForm() { 702 const form = document.getElementById('contact-form'); 703 if (!form) return; 704 705 form.addEventListener('submit', async (e) => { 706 e.preventDefault(); 707 708 const submitBtn = document.getElementById('submit-btn'); 709 const btnText = submitBtn.querySelector('.btn-text'); 710 const btnLoading = submitBtn.querySelector('.btn-loading'); 711 const formMessage = document.getElementById('form-message'); 712 713 // Disable button and show loading 714 submitBtn.disabled = true; 715 btnText.style.display = 'none'; 716 btnLoading.style.display = 'inline'; 717 formMessage.style.display = 'none'; 718 719 const formData = new FormData(form); 720 const data = Object.fromEntries(formData.entries()); 721 // Cloudflare Turnstile injects a hidden input named "cf-turnstile-response". 722 // Send it under a clearer key for the server. 723 data.turnstileToken = data['cf-turnstile-response'] || ''; 724 // Tell the server which language the visitor is using (optional context). 725 data.lang = currentLang; 726 727 try { 728 const response = await fetch('/api/contact', { 729 method: 'POST', 730 headers: { 'Content-Type': 'application/json' }, 731 body: JSON.stringify(data) 732 }); 733 734 if (!response.ok) { 735 const err = await response.json().catch(() => ({})); 736 throw new Error(err.error || t('form.msg.error')); 737 } 738 739 // Success 740 formMessage.textContent = t('form.msg.success'); 741 formMessage.className = 'form-message success'; 742 formMessage.style.display = 'block'; 743 form.reset(); 744 745 } catch (error) { 746 formMessage.textContent = error.message || t('form.msg.error'); 747 formMessage.className = 'form-message error'; 748 formMessage.style.display = 'block'; 749 } finally { 750 submitBtn.disabled = false; 751 btnText.style.display = 'inline'; 752 btnLoading.style.display = 'none'; 753 // Reset the Turnstile widget so a new token is issued for the next submit. 754 if (window.turnstile) { 755 try { window.turnstile.reset(); } catch (e) { /* noop */ } 756 } 757 } 758 }); 759} 760 761/* ---------------------------------------- 762 Reproductores de audio (/i-know-kung-fu) 763 764 El <audio> se crea recién al primer play: asà abrir la página no 765 descarga ningún MP3. Sólo puede sonar uno a la vez. 766 ---------------------------------------- */ 767function initAudioPlayers() { 768 const players = document.querySelectorAll('.kf-player'); 769 if (!players.length) return; 770 771 players.forEach((player) => setupAudioPlayer(player, players)); 772} 773 774function setupAudioPlayer(player, allPlayers) { 775 const btn = player.querySelector('.kf-play'); 776 const progress = player.querySelector('.kf-progress'); 777 const fill = player.querySelector('.kf-progress-fill'); 778 const timeEl = player.querySelector('.kf-time'); 779 const src = player.getAttribute('data-src'); 780 // Duración conocida de antemano: permite mostrar el tiempo y aceptar 781 // saltos en la barra antes de que el archivo se haya cargado. 782 const knownDuration = parseFloat(player.getAttribute('data-dur')) || 0; 783 784 if (!btn || !src) return; 785 786 let audio = null; 787 let pendingSeek = null; 788 789 function duration() { 790 return audio && isFinite(audio.duration) && audio.duration > 0 ? audio.duration : knownDuration; 791 } 792 793 function render() { 794 const total = duration(); 795 const current = audio ? audio.currentTime : 0; 796 const pct = total > 0 ? Math.min(100, (current / total) * 100) : 0; 797 if (fill) fill.style.width = pct + '%'; 798 if (timeEl) timeEl.textContent = `${formatTime(current)} / ${formatTime(total)}`; 799 if (progress) { 800 progress.setAttribute('aria-valuenow', String(Math.round(pct))); 801 progress.setAttribute('aria-valuemin', '0'); 802 progress.setAttribute('aria-valuemax', '100'); 803 } 804 } 805 806 function ensureAudio() { 807 if (audio) return audio; 808 809 audio = new Audio(); 810 audio.preload = 'metadata'; 811 audio.src = src; 812 813 audio.addEventListener('timeupdate', render); 814 audio.addEventListener('loadedmetadata', () => { 815 if (pendingSeek != null) { 816 audio.currentTime = pendingSeek * duration(); 817 pendingSeek = null; 818 } 819 render(); 820 }); 821 audio.addEventListener('playing', () => { 822 btn.classList.remove('is-loading'); 823 player.classList.add('is-playing'); 824 btn.setAttribute('aria-label', t('kf.a11y.pause')); 825 }); 826 audio.addEventListener('pause', () => { 827 player.classList.remove('is-playing'); 828 btn.setAttribute('aria-label', t('kf.a11y.play')); 829 }); 830 audio.addEventListener('ended', () => { 831 player.classList.remove('is-playing'); 832 btn.setAttribute('aria-label', t('kf.a11y.play')); 833 audio.currentTime = 0; 834 render(); 835 }); 836 audio.addEventListener('error', () => { 837 btn.classList.remove('is-loading'); 838 player.classList.remove('is-playing'); 839 }); 840 841 return audio; 842 } 843 844 btn.addEventListener('click', () => { 845 const a = ensureAudio(); 846 847 if (!a.paused) { 848 a.pause(); 849 return; 850 } 851 852 // Un solo audio sonando a la vez.
853 allPlayers.forEach((other) => { 854 if (other !== player) other.dispatchEvent(new CustomEvent('kf:pause')); 855 }); 856 857 btn.classList.add('is-loading'); 858 const played = a.play(); 859 if (played && typeof played.catch === 'function') { 860 played.catch(() => btn.classList.remove('is-loading')); 861 } 862 }); 863 864 // Otro reproductor pide que este se detenga. 865 player.addEventListener('kf:pause', () => { 866 if (audio && !audio.paused) audio.pause(); 867 }); 868 869 // Saltar a un punto pinchando la barra de progreso. 870 if (progress) { 871 progress.addEventListener('click', (e) => { 872 const rect = progress.getBoundingClientRect(); 873 if (!rect.width) return; 874 const ratio = Math.min(1, Math.max(0, (e.clientX - rect.left) / rect.width)); 875 const a = ensureAudio(); 876 877 if (isFinite(a.duration) && a.duration > 0) { 878 a.currentTime = ratio * a.duration; 879 render(); 880 } else { 881 // TodavÃa no hay metadatos: se aplica en cuanto lleguen. 882 pendingSeek = ratio; 883 if (fill) fill.style.width = ratio * 100 + '%'; 884 } 885 }); 886 887 // Flechas izquierda/derecha para moverse 10 segundos. 888 progress.addEventListener('keydown', (e) => { 889 if (e.key !== 'ArrowLeft' && e.key !== 'ArrowRight') return; 890 e.preventDefault(); 891 const a = ensureAudio(); 892 const total = duration(); 893 if (!total) return; 894 const delta = e.key === 'ArrowRight' ? 10 : -10; 895 a.currentTime = Math.min(total, Math.max(0, a.currentTime + delta)); 896 render(); 897 }); 898 } 899 900 render(); 901} 902 903function formatTime(seconds) { 904 if (!isFinite(seconds) || seconds < 0) seconds = 0; 905 const total = Math.floor(seconds); 906 const h = Math.floor(total / 3600); 907 const m = Math.floor((total % 3600) / 60); 908 const s = total % 60; 909 const pad = (n) => String(n).padStart(2, '0'); 910 return h > 0 ? `${h}:${pad(m)}:${pad(s)}` : `${m}:${pad(s)}`; 911} 912 913/* ---------------------------------------- 914 Al pulsar «Pedir el audiolibro», rellena el campo del formulario 915 con el nombre del tema. El campo es libre: se puede editar o pedir 916 un tema que todavÃa no publicamos. 917 ---------------------------------------- */ 918function initTemaPreselect() { 919 const botones = document.querySelectorAll('a[data-tema-key]'); 920 if (!botones.length) return; 921 922 botones.forEach((boton) => { 923 boton.addEventListener('click', () => { 924 const campo = document.getElementById('ab-tema'); 925 if (campo) campo.value = t(boton.getAttribute('data-tema-key')); 926 }); 927 }); 928} 929 930/* ---------------------------------------- 931 Formulario de pedido del audiolibro 932 ---------------------------------------- */ 933function initAudiolibroForm() { 934 const form = document.getElementById('audiolibro-form'); 935 if (!form) return; 936 937 form.addEventListener('submit', async (e) => { 938 e.preventDefault(); 939 940 const submitBtn = document.getElementById('ab-submit-btn'); 941 const btnText = submitBtn.querySelector('.btn-text'); 942 const btnLoading = submitBtn.querySelector('.btn-loading'); 943 const formMessage = document.getElementById('ab-form-message'); 944 945 submitBtn.disabled = true; 946 btnText.style.display = 'none'; 947 btnLoading.style.display = 'inline'; 948 formMessage.style.display = 'none'; 949 950 const data = Object.fromEntries(new FormData(form).entries()); 951 data.turnstileToken = data['cf-turnstile-response'] || ''; 952 data.lang = currentLang; 953 954 try { 955 const response = await fetch('/api/audiolibro', { 956 method: 'POST', 957 headers: { 'Content-Type': 'application/json' }, 958 body: JSON.stringify(data), 959 }); 960 961 if (!response.ok) { 962 const err = await response.json().catch(() => ({})); 963 throw new Error(err.error || t('kf.p.msg.error')); 964 } 965 966 formMessage.textContent = t('kf.p.msg.success'); 967 formMessage.className = 'form-message success'; 968 formMessage.style.display = 'block'; 969 form.reset(); 970 971 } catch (error) { 972 formMessage.textContent = error.message || t('kf.p.msg.error'); 973 formMessage.className = 'form-message error'; 974 formMessage.style.display = 'block'; 975 } finally { 976 submitBtn.disabled = false;
977 btnText.style.display = 'inline'; 978 btnLoading.style.display = 'none'; 979 if (window.turnstile) { 980 try { window.turnstile.reset(); } catch (e) { /* noop */ } 981 } 982 } 983 }); 984} 985 986/* ---------------------------------------- 987 Formulario de "Tu primer agente" (/tu-primer-agente) 988 989 La pregunta 5 admite varias respuestas, asà que el payload no se puede armar 990 con Object.fromEntries: hay que sacar las marcas con getAll(). 991 ---------------------------------------- */ 992function initPrimerAgenteForm() { 993 const form = document.getElementById('primer-agente-form'); 994 if (!form) return; 995 996 // "Otra" abre un campo de texto obligatorio: sin saber cuál es la herramienta, 997 // la respuesta no sirve para evaluar el caso. 998 const checkOtra = document.getElementById('pa-check-otra'); 999 const wrapOtra = document.getElementById('pa-otra-wrap'); 1000 const inputOtra = document.getElementById('pa-otra'); 1001 1002 if (checkOtra && wrapOtra && inputOtra) { 1003 const sincronizarOtra = () => { 1004 wrapOtra.style.display = checkOtra.checked ? 'block' : 'none'; 1005 if (checkOtra.checked) { 1006 inputOtra.setAttribute('required', 'required'); 1007 inputOtra.focus(); 1008 } else { 1009 inputOtra.removeAttribute('required'); 1010 inputOtra.value = ''; 1011 } 1012 }; 1013 checkOtra.addEventListener('change', sincronizarOtra); 1014 } 1015 1016 form.addEventListener('submit', async (e) => { 1017 e.preventDefault(); 1018 1019 const submitBtn = document.getElementById('pa-submit-btn'); 1020 const btnText = submitBtn.querySelector('.btn-text'); 1021 const btnLoading = submitBtn.querySelector('.btn-loading'); 1022 const formMessage = document.getElementById('pa-form-message'); 1023 1024 const formData = new FormData(form); 1025 const herramientas = formData.getAll('herramientas'); 1026 1027 // Los checkboxes no tienen validación nativa de "al menos uno". 1028 if (!herramientas.length) { 1029 formMessage.textContent = t('pa.msg.herramientas'); 1030 formMessage.className = 'form-message error'; 1031 formMessage.style.display = 'block'; 1032 document.getElementById('pa-herramientas').scrollIntoView({ behavior: 'smooth', block: 'center' }); 1033 return; 1034 } 1035 1036 submitBtn.disabled = true; 1037 btnText.style.display = 'none'; 1038 btnLoading.style.display = 'inline'; 1039 formMessage.style.display = 'none'; 1040 1041 const data = Object.fromEntries(formData.entries()); 1042 data.herramientas = herramientas; 1043 data.turnstileToken = data['cf-turnstile-response'] || ''; 1044 1045 try { 1046 const response = await fetch('/api/primer-agente', { 1047 method: 'POST', 1048 headers: { 'Content-Type': 'application/json' }, 1049 body: JSON.stringify(data), 1050 }); 1051 1052 if (!response.ok) { 1053 const err = await response.json().catch(() => ({})); 1054 throw new Error(err.error || t('pa.msg.error')); 1055 } 1056 1057 formMessage.textContent = t('pa.msg.success'); 1058 formMessage.className = 'form-message success'; 1059 formMessage.style.display = 'block'; 1060 form.reset(); 1061 // form.reset() desmarca el checkbox pero no vuelve a ocultar su campo. 1062 if (wrapOtra && inputOtra) { 1063 wrapOtra.style.display = 'none'; 1064 inputOtra.removeAttribute('required'); 1065 } 1066 1067 } catch (error) { 1068 formMessage.textContent = error.message || t('pa.msg.error'); 1069 formMessage.className = 'form-message error'; 1070 formMessage.style.display = 'block'; 1071 } finally { 1072 submitBtn.disabled = false; 1073 btnText.style.display = 'inline'; 1074 btnLoading.style.display = 'none'; 1075 if (window.turnstile) { 1076 try { window.turnstile.reset(); } catch (e) { /* noop */ } 1077 } 1078 } 1079 }); 1080}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.