1(function () { 2 "use strict"; 3 4 function escapeHtml(str) { 5 return String(str) 6 .replace(/&/g, "&") 7 .replace(/</g, "<") 8 .replace(/>/g, ">") 9 .replace(/\"/g, """) 10 .replace(/'/g, "'"); 11 } 12 13 function slugToTitle(slug) { 14 return slug 15 .split("-") 16 .map(function (part) { 17 return part.charAt(0).toUpperCase() + part.slice(1); 18 }) 19 .join(" "); 20 } 21 22 // Spanish/French translations (assets/json/landing-pages-i18n.json). 23 var I18N = null; 24 25 function getCurrentLanguage() { 26 var params = new URLSearchParams(window.location.search); 27 var lang = 28 params.get("lang") || 29 (document.documentElement.getAttribute("lang") || "en") 30 .slice(0, 2) 31 .toLowerCase(); 32 return ["en", "es", "fr"].indexOf(lang) === -1 ? "en" : lang; 33 } 34 35 function activeI18n() { 36 var lang = getCurrentLanguage(); 37 return lang !== "en" && I18N && I18N[lang] ? I18N[lang] : null; 38 } 39 40 function fill(text, vars) { 41 return String(text).replace(/\{(\w+)\}/g, function (m, key) { 42 return vars[key] != null ? vars[key] : m; 43 }); 44 } 45 46 function localizedPage(page, lang) { 47 var t = I18N && I18N[lang] && I18N[lang].pages[page.slug]; 48 return t ? Object.assign({}, page, t) : page; 49 } 50 51 function localizedUrl(slug, lang) { 52 return ( 53 "https://cabrialeswebstudio.com/" + 54 slug + 55 (lang !== "en" && I18N && I18N[lang] ? I18N[lang].suffix : "") 56 ); 57 } 58 59 function findBySlug(allPages, slug) { 60 return allPages.find(function (item) { 61 return item.slug === slug; 62 }); 63 } 64 65 function getIndustryCityLinks() { 66 // Only link to city hubs that are real pages (the other city slugs redirect). 67 return [ 68 "web-design-san-antonio", 69 "web-design-austin", 70 "web-design-dallas", 71 "web-design-houston", 72 ]; 73 } 74 75 function getPrimaryLinks(page) { 76 if (page.type === "city") { 77 return [ 78 "website-salvage-services", 79 "website-redesign-services", 80 "web-development-services", 81 "graphic-design-services", 82 "web-design-austin", 83 "web-design-dallas", 84 "web-design-houston", 85 ]; 86 } 87 88 if (page.type === "industry") { 89 var cityLinks = getIndustryCityLinks(page); 90 return cityLinks.concat(["how-much-does-web-design-cost"]); 91 } 92 93 if (page.type === "service-local") { 94 return [ 95 "website-salvage-services", 96 "website-redesign-services", 97 "web-development-services", 98 "graphic-design-services", 99 "web-design-austin", 100 "web-design-dallas", 101 "web-design-houston", 102 ]; 103 } 104 105 return [ 106 "web-design-san-antonio", 107 "website-salvage-services", 108 "website-redesign-services", 109 "web-development-services", 110 "graphic-design-services", 111 "web-page-design-company", 112 "web-design-austin", 113 "web-design-dallas", 114 "web-design-houston", 115 ]; 116 } 117 118 function buildIntro(page) { 119 var city = page.city || "major North American cities"; 120 var industry = page.industry || "small and mid-size businesses"; 121 var keyword = page.focusKeyword; 122 var isCanada = page.region === "Canada"; 123 var isMexico = page.region === "Mexico"; 124 125 if (page.type === "city" && isMexico) { 126 return ( 127 "Los negocios en " + 128 city + 129 " compiten en resultados de búsqueda local saturados. Los sitios web genéricos no funcionan para convertir. Construimos sitios web personalizados alrededor de la intención real de compra, para que tus clientes entiendan rápidamente qué haces, por qué deben confiarte, y cómo contactarte. Nos enfocamos en diseño mobile-first, velocidad de carga rápida, mensajerÃa clara de servicios, y estructura de página orientada a conversiones para que tu sitio apoye el crecimiento de ingresos. Si buscas " + 130 keyword + 131 ", esta página te da un camino directo a estrategia, ejecución, y mejoras continuas que se alinean con tu mercado." 132 ); 133 } 134 135 if (page.type === "city") { 136 return ( 137 "Businesses in " + 138 city + 139 " are competing in crowded local search results, and generic templates usually fail to convert. Our team builds custom websites around real buyer intent so customers can quickly understand what you do, why they should trust you, and how to contact you. We focus on mobile-first design, fast load times, clear service messaging, and conversion-focused page structure so your site supports revenue growth instead of acting like a digital brochure. If you are targeting " + 140 keyword + 141 ", this page gives you a direct path to strategy, execution, and ongoing improvements that align with your market across San Antonio, Austin, Dallas, Houston, Chicago, Los Angeles, New York, Toronto, Vancouver, and Montreal." 142 ); 143 } 144 145 if (page.type === "industry") { 146 return ( 147 "Teams in " + 148 industry + 149 " need websites that match how customers buy: fast answers, trust signals, and simple next steps. We design and build sites with industry-specific messaging, service pages, and lead capture flows that improve both visibility and conversion quality. Our process combines strategy, UX, technical SEO foundations, and practical content structure so your website works as a reliable growth channel. Whether you are operating in Texas metros like San Antonio, Austin, Dallas, Fort Worth, Houston, and El Paso or competing in larger North American markets like Chicago, Los Angeles, New York, Toronto, Vancouver, and Montreal, we tailor each build to your offers, sales cycle, and operations. This helps your business stand out in local and regional results while producing better inbound leads." 150 ); 151 } 152
153 return "Your website should do more than look good - it should attract qualified traffic and turn visitors into real conversations. We build conversion-focused websites for businesses across multiple industries, from consulting and logistics to barbershops, nonprofits, construction, loans, and real estate. Every project includes clear messaging architecture, service-focused page layouts, mobile optimization, and strong technical foundations that support search visibility. We also support bilingual delivery in English, Spanish, and French, helping brands connect with broader audiences in major Texas cities and top North American metros. If you are planning a new website, redesign, or growth-focused upgrade, this page is structured to help you move from idea to execution with a practical, results-first approach."; 154 } 155 156 function buildBusinessTypesHtml() { 157 var types = [ 158 "Consulting", 159 "Logistics", 160 "Hairstylists and Barbershops", 161 "Nonprofits", 162 "Construction", 163 "Loans and Finance", 164 "Real Estate", 165 "Healthcare", 166 "Legal", 167 "Restaurants", 168 "Retail", 169 "New Businesses", 170 "Small to Mid-Size Businesses", 171 ]; 172 173 return types 174 .map(function (type) { 175 return ( 176 '<span class="badge rounded-pill text-bg-light border me-2 mb-2">' + 177 escapeHtml(type) + 178 "</span>" 179 ); 180 }) 181 .join(""); 182 } 183 184 function buildPrimaryLinksHtml(page, allPages) { 185 var slugs = getPrimaryLinks(page); 186 return slugs 187 .map(function (slug) { 188 var item = findBySlug(allPages, slug); 189 if (!item) { 190 return ""; 191 } 192 return ( 193 '<li class="mb-2"><a href="/' + 194 item.slug + 195 '" class="link-primary text-decoration-none">' + 196 escapeHtml(item.h1) + 197 "</a></li>" 198 ); 199 }) 200 .join(""); 201 } 202 203 function buildLocalizedFaqs(page, i18n) { 204 var S = i18n.strings; 205 var vars = { 206 keyword: page.focusKeyword, 207 cityIn: page.cityIn, 208 industryFor: page.industryFor, 209 industry: page.industry, 210 }; 211 var faqs = S.faqs.map(function (f) { 212 return { q: fill(f.q, vars), a: fill(f.a, vars) }; 213 }); 214 if (page.type === "guide") { 215 faqs[0] = { q: S.faq_guide0.q, a: S.faq_guide0.a }; 216 faqs[2] = { q: S.faq_guide2.q, a: S.faq_guide2.a }; 217 } 218 if (page.type === "bilingual") { 219 faqs.push({ q: S.faq_bilingual.q, a: S.faq_bilingual.a }); 220 } 221 return faqs; 222 } 223 224 function buildFaqs(page) { 225 var i18n = activeI18n(); 226 if (i18n) { 227 return buildLocalizedFaqs(page, i18n); 228 } 229 var keyword = page.focusKeyword; 230 var city = page.city || "major U.S. cities"; 231 var industry = page.industry || "small businesses"; 232 var isMexico = page.region === "Mexico"; 233 234 if (isMexico) { 235 var baseFaqs = [ 236 { 237 q: "¿Cómo trabajan con proyectos de " + keyword + "?", 238 a: "Empezamos con tus objetivos comerciales, mapeamos el viaje del cliente, y construimos páginas alrededor de la intención de búsqueda y caminos de conversión. Cada proyecto incluye velocidad, UX, y mejores prácticas de SEO.", 239 }, 240 { 241 q: "¿Trabajan con negocios fuera de México?", 242 a: 243 "SÃ. Trabajamos con clientes en " + 244 city + 245 " y otros mercados principales a través de un proceso remoto con comunicación clara y revisiones de hitos.", 246 }, 247 { 248 q: "¿Pueden adaptar esto para " + industry + "?", 249 a: "SÃ. Adaptamos mensajerÃa, estructura, y llamadas a la acción para tu industria para que tu sitio hable directamente a tu audiencia.", 250 }, 251 { 252 q: "¿Trabajan con cualquier tipo de negocio?", 253 a: "SÃ. Apoyamos consultorÃa, logÃstica, peluquerÃas, organizaciones sin fines de lucro, construcción, préstamos, bienes raÃces, y muchos otros negocios pequeños y medianos.", 254 }, 255 { 256 q: "¿Ofrecen soporte en sitios web en español?",
257 a: "SÃ. Hablamos español. Proporcionamos soporte bilÃngüe de sitios web, implementación de contenido en español, y estructuras de página preparadas para traducción.", 258 }, 259 ]; 260 return baseFaqs; 261 } 262 263 var baseFaqs = [ 264 { 265 q: "How do you approach " + keyword + " projects?", 266 a: "We start with your business goals, map the buyer journey, and build pages around search intent and conversion paths. Every project includes speed, UX, and SEO best practices.", 267 }, 268 { 269 q: "Do you work with businesses outside Texas?", 270 a: 271 "Yes. We support clients in " + 272 city + 273 " and other major U.S. markets through a remote-first process with clear communication and milestone reviews.", 274 }, 275 { 276 q: "Can you tailor this for " + industry + "?", 277 a: "Yes. We adapt messaging, structure, and calls-to-action for your industry so your site speaks directly to the audience you serve.", 278 }, 279 { 280 q: "Can you work with any business type?", 281 a: "Yes. We support consulting, logistics, barbershops, nonprofits, construction, loans, real estate, and many other small to mid-size businesses.", 282 }, 283 { 284 q: "Do you offer Spanish-language website support?", 285 a: "Yes. Se habla Espanol. We provide bilingual website support, Spanish content implementation, and translation-ready page structures.", 286 }, 287 ]; 288 289 if (page.type === "guide") { 290 baseFaqs[0] = { 291 q: "Who should read this guide?", 292 a: "This guide is for business owners comparing options before investing in a new website or redesign. It helps you make practical decisions based on goals and budget.", 293 }; 294 baseFaqs[2] = { 295 q: "How can this guide improve lead quality?", 296 a: "It highlights the pages, messaging, and UX elements that improve trust and drive better inquiries from the right customers.", 297 }; 298 } 299 300 if (page.type === "bilingual") { 301 baseFaqs.push({ 302 q: "Tienen soporte en espanol para todo el proceso del sitio web?", 303 a: "Si. Podemos trabajar en espanol durante estrategia, contenido, revision y lanzamiento para que tu equipo tenga claridad en cada etapa.", 304 }); 305 } 306 307 return baseFaqs; 308 } 309 310 function buildSectionContent(page, allPages) { 311 var localized = activeI18n(); 312 if (localized) { 313 return buildLocalizedSectionContent(page, allPages, localized); 314 } 315 var primaryLinksHtml = buildPrimaryLinksHtml(page, allPages); 316 var isMexico = page.region === "Mexico"; 317 318 var faqs = buildFaqs(page); 319 var faqHtml = faqs 320 .map(function (faq, index) { 321 var id = "faq-" + index; 322 return ( 323 '<div class="accordion-item">' + 324 '<h2 class="accordion-header" id="' + 325 id + 326 '-heading">' + 327 '<button class="accordion-button ' + 328 (index === 0 ? "" : "collapsed") + 329 '" type="button" data-bs-toggle="collapse" data-bs-target="#' + 330 id + 331 '-collapse" aria-expanded="' + 332 (index === 0 ? "true" : "false") + 333 '" aria-controls="' + 334 id + 335 '-collapse">' + 336 escapeHtml(faq.q) + 337 "</button>" + 338 "</h2>" + 339 '<div id="' + 340 id + 341 '-collapse" class="accordion-collapse collapse ' + 342 (index === 0 ? "show" : "") + 343 '" aria-labelledby="' + 344 id + 345 '-heading" data-bs-parent="#landing-faq">' + 346 '<div class="accordion-body">' + 347 escapeHtml(faq.a) + 348 "</div>" + 349 "</div>" + 350 "</div>" 351 ); 352 }) 353 .join(""); 354 355 var contextLine = isMexico 356 ? "Esta página está personalizada para negocios en " + page.city + "." 357 : page.type === "city" 358 ? "This page is tailored for businesses in " + page.city + "." 359 : page.type === "industry" 360 ? "This page is tailored for " + 361 page.industry + 362 " in competitive markets." 363 : page.type === "service-local" 364 ? "This page is tailored for businesses in major U.S. cities seeking local design expertise." 365 : "This page is tailored for businesses targeting growth across major U.S. markets."; 366 367 var industryLinkExtras = isMexico 368 ? '<li class="mb-2"><a href="/contact" class="link-primary text-decoration-none">
368Contactar Cabriales Web Studio</a></li>' 369 : page.type === "industry" 370 ? '<li class="mb-2"><a href="/portfolio" class="link-primary text-decoration-none">Portfolio and case examples</a></li>' + 371 '<li class="mb-2"><a href="/contact" class="link-primary text-decoration-none">Contact Cabriales Web Studio</a></li>' 372 : page.type === "city" 373 ? '<li class="mb-2"><a href="/contact" class="link-primary text-decoration-none">Contact Cabriales Web Studio</a></li>' 374 : page.type === "service-local" 375 ? '<li class="mb-2"><a href="/portfolio" class="link-primary text-decoration-none">View portfolio</a></li>' + 376 '<li class="mb-2"><a href="/contact" class="link-primary text-decoration-none">Contact Cabriales Web Studio</a></li>' 377 : '<li class="mb-2"><a href="/portfolio" class="link-primary text-decoration-none">View our portfolio</a></li>'; 378 379 return ( 380 '<section class="py-5">' + 381 '<div class="container px-4 px-lg-5">' + 382 '<div class="row g-4">' + 383 '<div class="col-12">' + 384 '<div class="card border-0 shadow-sm">' + 385 '<div class="card-body p-4">' + 386 '<h2 class="h4 fw-bold mb-3">Overview</h2>' + 387 '<p class="text-muted mb-4">' + 388 escapeHtml(buildIntro(page)) + 389 "</p>" + 390 '<p class="mb-4 text-muted"><strong>Focus:</strong> ' + 391 escapeHtml(contextLine) + 392 "</p>" + 393 '<h3 class="h5 fw-bold mb-3">What\'s Included</h3>' + 394 '<ul class="mb-4">' + 395 "<li><strong>Scope:</strong> strategy, layout architecture, content flow, and conversion mapping</li>" + 396 "<li><strong>Timeline:</strong> milestone-based delivery with structured review checkpoints</li>" + 397 "<li><strong>Deliverables:</strong> responsive pages, SEO-ready structure, and launch support</li>" + 398 "<li><strong>Language:</strong> English and Spanish support for bilingual businesses</li>" + 399 "</ul>" + 400 '<h3 class="h5 fw-bold mb-3">Business Types We Serve</h3>' + 401 '<div class="mb-0">' + 402 buildBusinessTypesHtml() + 403 "</div>" + 404 "</div>" + 405 "</div>" + 406 "</div>" + 407 "</div>" + 408 '<div class="row g-4 mt-2">' + 409 '<div class="col-12">' + 410 '<div class="card border-0 shadow-sm">' + 411 '<div class="card-body p-4">' + 412 '<h3 class="h5 fw-bold mb-3">Related Pages</h3>' + 413 '<ul class="list-unstyled mb-0">' + 414 primaryLinksHtml + 415 industryLinkExtras + 416 "</ul>" + 417 "</div>" + 418 "</div>" + 419 "</div>" + 420 "</div>" + 421 "</div>" + 422 "</section>" + 423 '<section class="py-5">' + 424 '<div class="container px-4 px-lg-5">' + 425 '<div class="card border-0 shadow-sm">' + 426 '<div class="card-body p-4 p-lg-5 text-center">' + 427 (isMexico 428 ? '<h2 class="h3 fw-bold mb-3">¿Por Qué Elegirnos?</h2>' 429 : '<h2 class="h3 fw-bold mb-3">Why Choose Us</h2>') + 430 (isMexico 431 ? '<p class="text-muted mb-3">Hemos trabajado con organizaciones sin fines de lucro, empresas de servicios, marcas locales, y equipos orientados al crecimiento en múltiples industrias. Nuestro proceso se construye alrededor de ejecución práctica, progreso medible, y comunicación clara.</p>' 432 : '<p class="text-muted mb-3">We have worked with nonprofits, service companies, local brands, and growth-focused teams across multiple industries. Our process is built around practical execution, measurable progress, and clear communication.</p>') + 433 '<ul class="mb-4">' + 434 (isMexico 435 ? "<li>Planificación impulsada por portafolio con ejemplos reales y detalle de implementación</li>" 436 : "<li>Portfolio-driven planning with real examples and implementation detail</li>") + 437 (isMexico 438 ? "<li>Decisiones de UX orientadas a conversiones alineadas con objetivos comerciales</li>" 439 : "<li>Conversion-focused UX decisions aligned to business goals</li>") + 440 (isMexico 441 ? "<li>Limpieza técnica y fundamentos SEO incorporados en la entrega</li>" 442 : "<li>Technical cleanup and SEO foundations baked into delivery</li>") + 443 "</ul>" + 444 '<a href="/portfolio" class="btn btn-outline-primary me-2">' + 445 (isMexico ? "Ver Portafolio" : "View Portfolio") + 446 "</a>" + 447 '<a href="/contact" class="btn btn-primary">' + 448 (isMexico ? "Obtener Cotización Personalizada" : "Get a Custom Quote") + 449 "</a>" + 450 "</div>" + 451 "</div>" + 452 "</div>" + 453 "</section>" + 454 '<section class="py-5 bg-light">' + 455 '<div class="container px-4 px-lg-5">' + 456 '<h2 class="h3 fw-bold text-center mb-4">Frequently Asked Questions</h2>' + 457 '<div class="accordion" id="landing-faq">' + 458 faqHtml + 459 "</div>" + 460 "</div>" + 461 "</section>" + 462 '<section class="py-5">' + 463 '<div class="container px-4 px-lg-5 text-center">' + 464 '<h2 class="h3 fw-bold mb-3">' + 465 (isMexico 466 ? "¿Listo para un Sitio Web Mejor?" 467 : "Ready to Build a Better Website?") + 468 "</h2>" + 469 '<p class="text-muted mb-3">' + 470 (isMexico 471 ? "Nos adaptamos a tu presupuesto y cronograma. Obtén una cotización personalizada adaptada a los objetivos comerciales y la estrategia de crecimiento de tu negocio." 472 : "We work with your budget and timeline. Get a custom quote tailored to your business goals and growth strategy.") + 473 "</p>" + 474 '<p class="mb-4"><i class="fas fa-language me-2"></i>' + 475 (isMexico 476 ? "Servicio en español disponible." 477 : "Se habla Espanol - servicios disponibles en espanol.") + 478 "</p>" + 479 '<p class="mb-4"><i class="fas fa-phone me-2"></i><a href="tel:210-201-4206" class="text-decoration-none">210-201-4206</a></p>' + 480 '<a href="/contact" class="btn btn-primary btn-lg px-5">' + 481 (isMexico ? "Obtener Cotización Personalizada" : "Get a Custom Quote") + 482 "</a>" + 483 "</div>" + 484 "</section>" 485 ); 486 } 487 488 // City hubs are hand-written pages (not in the translation JSON), so their link labels live here. 489 var HUB_LABELS = { 490 "web-design-san-antonio": { es: "Diseño Web en San Antonio", fr: "Conception Web à San Antonio" }, 491 "web-design-austin": { es: "Diseño Web en Austin", fr: "Conception Web à Austin" }, 492 "web-design-dallas": { es: "Diseño Web en Dallas", fr: "Conception Web à Dallas" }, 493 "web-design-houston": { es: "Diseño Web en Houston", fr: "Conception Web à Houston" }, 494 }; 495 496 function buildLocalizedSectionContent(page, allPages, i18n) { 497 var S = i18n.strings; 498 var suffix = i18n.suffix; 499 var vars = { industry: page.industry, industryFor: page.industryFor }; 500 501 var links = getPrimaryLinks(page) 502 .map(function (slug) { 503 var t = i18n.pages[slug]; 504 if (!t && HUB_LABELS[slug]) { 505 t = { h1: HUB_LABELS[slug][suffix.replace("-", "")] }; 506 } 507 if (!t) { 508 return ""; 509 } 510 return ( 511 '<li class="mb-2"><a href="/' + 512 slug + 513 suffix + 514 '" class="link-primary text-decoration-none">
514' + 515 escapeHtml(t.h1) + 516 "</a></li>" 517 ); 518 }) 519 .join(""); 520 521 var portfolioHref = "/portfolio" + suffix; 522 var contactHref = "/contact" + suffix; 523 var contact = 524 '<li class="mb-2"><a href="' + 525 contactHref + 526 '" class="link-primary text-decoration-none">' + 527 escapeHtml(S.link_contact) + 528 "</a></li>"; 529 var extras = 530 page.type === "industry" 531 ? '<li class="mb-2"><a href="' + 532 portfolioHref + 533 '" class="link-primary text-decoration-none">' + 534 escapeHtml(S.link_portfolio_industry) + 535 "</a></li>" + 536 contact 537 : '<li class="mb-2"><a href="' + 538 portfolioHref + 539 '" class="link-primary text-decoration-none">' + 540 escapeHtml(S.link_portfolio_default) + 541 "</a></li>"; 542 543 var contextLine = 544 page.type === "industry" ? fill(S.ctx_industry, vars) : S.ctx_default; 545 var intro = 546 page.type === "industry" 547 ? fill(S.intro_industry, vars) 548 : S.intro_default; 549 550 var faqHtml = buildLocalizedFaqs(page, i18n) 551 .map(function (faq, index) { 552 var id = "faq-" + index; 553 return ( 554 '<div class="accordion-item">' + 555 '<h2 class="accordion-header" id="' + 556 id + 557 '-heading">' + 558 '<button class="accordion-button ' + 559 (index === 0 ? "" : "collapsed") + 560 '" type="button" data-bs-toggle="collapse" data-bs-target="#' + 561 id + 562 '-collapse" aria-expanded="' + 563 (index === 0 ? "true" : "false") + 564 '" aria-controls="' + 565 id + 566 '-collapse">' + 567 escapeHtml(faq.q) + 568 "</button>" + 569 "</h2>" + 570 '<div id="' + 571 id + 572 '-collapse" class="accordion-collapse collapse ' + 573 (index === 0 ? "show" : "") + 574 '" aria-labelledby="' + 575 id + 576 '-heading" data-bs-parent="#landing-faq">' + 577 '<div class="accordion-body">' + 578 escapeHtml(faq.a) + 579 "</div>" + 580 "</div>" + 581 "</div>" 582 ); 583 }) 584 .join(""); 585 586 var types = S.types 587 .map(function (type) { 588 return ( 589 '<span class="badge rounded-pill text-bg-light border me-2 mb-2">' + 590 escapeHtml(type) + 591 "</span>" 592 ); 593 }) 594 .join(""); 595 596 var whyList = S.why_li 597 .map(function (li) { 598 return "<li>" + escapeHtml(li) + "</li>"; 599 }) 600 .join(""); 601 602 return ( 603 '<section class="py-5">' + 604 '<div class="container px-4 px-lg-5">' + 605 '<div class="row g-4">' + 606 '<div class="col-12">' + 607 '<div class="card border-0 shadow-sm">' + 608 '<div class="card-body p-4">' + 609 '<h2 class="h4 fw-bold mb-3">' + 610 escapeHtml(S.overview) + 611 "</h2>" + 612 '<p class="text-muted mb-4">' + 613 escapeHtml(intro) + 614 "</p>" + 615 '<p class="mb-4 text-muted"><strong>' + 616 escapeHtml(S.focus) + 617 "</strong> " + 618 escapeHtml(contextLine) + 619 "</p>" + 620 '<h3 class="h5 fw-bold mb-3">' + 621 escapeHtml(S.included) + 622 "</h3>" + 623 '<ul class="mb-4">' + 624 S.inc.join("") + 625 "</ul>" + 626 '<h3 class="h5 fw-bold mb-3">' + 627 escapeHtml(S.types_heading) + 628 "</h3>" + 629 '<div class="mb-0">' + 630 types + 631 "</div>" + 632 "</div>" + 633 "</div>" + 634 "</div>" + 635 "</div>" + 636 '<div class="row g-4 mt-2">' + 637 '<div class="col-12">' + 638 '<div class="card border-0 shadow-sm">' + 639 '<div class="card-body p-4">' + 640 '<h3 class="h5 fw-bold mb-3">' + 641 escapeHtml(S.related) + 642 "</h3>" + 643 '<ul class="list-unstyled mb-0">' + 644 links + 645 extras + 646 "</ul>" + 647 "</div>" + 648 "</div>" + 649 "</div>" + 650 "</div>" + 651 "</div>" + 652 "</section>" + 653 '<section class="py-5">' + 654 '<div class="container px-4 px-lg-5">' + 655 '<div class="card border-0 shadow-sm">' + 656 '<div class="card-body p-4 p-lg-5 text-center">' + 657 '<h2 class="h3 fw-bold mb-3">' + 658 escapeHtml(S.why_h) + 659 "</h2>" + 660 '<p class="text-muted mb-3">' + 661 escapeHtml(S.why_p) + 662 "</p>" + 663 '<ul class="mb-4">' + 664 whyList + 665 "</ul>" + 666 '<a href="' + 667 portfolioHref + 668 '" class="btn btn-outline-primary me-2">' + 669 escapeHtml(S.btn_portfolio) + 670 "</a>" + 671 '<a href="' + 672 contactHref + 673 '" class="btn btn-primary">' + 674 escapeHtml(S.btn_quote) + 675 "</a>" + 676 "</div>" + 677 "</div>" + 678 "</div>" + 679 "</section>" + 680 '<section class="py-5 bg-light">' + 681 '<div class="container px-4 px-lg-5">' + 682 '<h2 class="h3 fw-bold text-center mb-4">' + 683 escapeHtml(S.faq_h) + 684 "</h2>" + 685 '<div class="accordion" id="landing-faq">' + 686 faqHtml + 687 "</div>" + 688 "</div>" + 689 "</section>" + 690 '<section class="py-5">' + 691 '<div class="container px-4 px-lg-5 text-center">' + 692 '<h2 class="h3 fw-bold mb-3">' + 693 escapeHtml(S.cta_h) + 694 "</h2>" + 695 '<p class="text-muted mb-3">' + 696 escapeHtml(S.cta_p) + 697 "</p>" + 698 '<p class="mb-4"><i class="fas fa-language me-2"></i>' + 699 escapeHtml(S.cta_lang) + 700 "</p>" + 701 '<p class="mb-4"><i class="fas fa-phone me-2"></i><a href="tel:210-201-4206" class="text-decoration-none">210-201-4206</a></p>' + 702 '<a href="' + 703 contactHref + 704 '" class="btn btn-primary btn-lg px-5">' + 705 escapeHtml(S.btn_quote) + 706 "</a>" + 707 "</div>" + 708 "</section>" 709 ); 710 } 711 712 function setSchema(page, faqs) { 713 var currentLanguage = getCurrentLanguage(); 714 var canonical = localizedUrl(page.slug, currentLanguage); 715 var i18nActive = activeI18n(); 716 717 var breadcrumb = { 718 "@context": "https://schema.org", 719 "@type": "BreadcrumbList", 720 itemListElement: [ 721 { 722 "@type": "ListItem", 723 position: 1, 724 name: i18nActive ? i18nActive.home : "Home", 725 item: "https://cabrialeswebstudio.com/", 726 }, 727 { 728 "@type": "ListItem", 729 position: 2, 730 name: page.h1, 731 item: canonical, 732 }, 733 ], 734 }; 735 736 var service = { 737 "@context": "https://schema.org", 738 "@type": "Service", 739 name: page.h1, 740 serviceType: page.serviceType, 741 areaServed: 742 page.type === "city" 743 ? [{ "@type": "City", name: page.city }] 744 : [ 745 { "@type": "City", name: "San Antonio" }, 746 { "@type": "City", name: "Austin" }, 747 { "@type": "City", name: "Dallas" }, 748 { "@type": "City", name: "Houston" }, 749 { "@type": "City", name: "Fort Worth" }, 750 { "@type": "City", name: "El Paso" }, 751 { "@type": "City", name: "Chicago" }, 752 { "@type": "City", name: "Los Angeles" }, 753 { "@type": "City", name: "New York" }, 754 { "@type": "City", name: "Miami" }, 755 { "@type": "City", name: "Toronto" }, 756 { "@type": "City", name: "Vancouver" }, 757 { "@type": "City", name: "Montreal" }, 758 { "@type": "City", name: "Calgary" }, 759 { "@type": "City", name: "Mexico City" }, 760 ], 761 description: page.metaDescription, 762 provider: { 763 "@type": "ProfessionalService", 764 name: "Cabriales Web Studio", 765 url: "https://cabrialeswebstudio.com", 766 knowsLanguage: 767 currentLanguage === "fr" ? ["en", "es", "fr"] : ["en", "es"], 768 }, 769 }; 770 771 var faqSchema = { 772 "@context": "https://schema.org", 773 "@type": "FAQPage", 774 mainEntity: faqs.map(function (faq) { 775 return { 776 "@type": "Question", 777 name: faq.q,
778 acceptedAnswer: { 779 "@type": "Answer", 780 text: faq.a, 781 }, 782 }; 783 }), 784 }; 785 786 [breadcrumb, service, faqSchema].forEach(function (obj) { 787 var script = document.createElement("script"); 788 script.type = "application/ld+json"; 789 script.text = JSON.stringify(obj); 790 document.head.appendChild(script); 791 }); 792 } 793 794 function setMeta(page) { 795 function setOrCreate(selector, attr, value) { 796 var el = document.head.querySelector(selector); 797 if (!el) { 798 el = document.createElement("meta"); 799 document.head.appendChild(el); 800 } 801 el.setAttribute(attr, value); 802 return el; 803 } 804 805 function setAlternateLink(hreflang, href) { 806 var selector = 'link[rel="alternate"][hreflang="' + hreflang + '"]'; 807 var el = document.head.querySelector(selector); 808 if (!el) { 809 el = document.createElement("link"); 810 el.rel = "alternate"; 811 el.setAttribute("hreflang", hreflang); 812 document.head.appendChild(el); 813 } 814 el.href = href; 815 } 816 817 var currentLanguage = getCurrentLanguage(); 818 var effectiveLanguage = 819 currentLanguage === "es" || page.region === "Mexico" 820 ? "es" 821 : currentLanguage; 822 var effectiveLocale = 823 effectiveLanguage === "es" 824 ? "es_MX" 825 : effectiveLanguage === "fr" 826 ? "fr_CA" 827 : "en_US"; 828 var canonicalUrl = localizedUrl(page.slug, currentLanguage); 829 830 document.title = page.title + " | Cabriales Web Studio"; 831 document.documentElement.lang = effectiveLanguage; 832 833 setOrCreate('meta[name="description"]', "name", "description").setAttribute( 834 "content", 835 page.metaDescription, 836 ); 837 838 setOrCreate( 839 'meta[property="og:title"]', 840 "property", 841 "og:title", 842 ).setAttribute("content", page.title + " | Cabriales Web Studio"); 843 setOrCreate( 844 'meta[property="og:description"]', 845 "property", 846 "og:description", 847 ).setAttribute("content", page.metaDescription); 848 setOrCreate('meta[property="og:url"]', "property", "og:url").setAttribute( 849 "content", 850 canonicalUrl, 851 ); 852 setOrCreate( 853 'meta[property="og:locale"]', 854 "property", 855 "og:locale", 856 ).setAttribute("content", effectiveLocale); 857 858 setOrCreate( 859 'meta[name="twitter:title"]', 860 "name", 861 "twitter:title", 862 ).setAttribute("content", page.title + " | Cabriales Web Studio"); 863 setOrCreate( 864 'meta[name="twitter:description"]', 865 "name", 866 "twitter:description", 867 ).setAttribute("content", page.metaDescription); 868 869 var canonical = document.head.querySelector('link[rel="canonical"]'); 870 if (!canonical) { 871 canonical = document.createElement("link"); 872 canonical.rel = "canonical"; 873 document.head.appendChild(canonical); 874 } 875 canonical.href = canonicalUrl; 876 877 // Language alternates only exist for pages that have translated versions. 878 if (I18N && I18N.es && I18N.es.pages[page.slug]) { 879 setAlternateLink("en", localizedUrl(page.slug, "en")); 880 setAlternateLink("es", localizedUrl(page.slug, "es")); 881 setAlternateLink("fr", localizedUrl(page.slug, "fr")); 882 setAlternateLink("x-default", "https://cabrialeswebstudio.com/"); 883 } 884 } 885 886 function renderPage(page, allPages) { 887 var heroTitle = document.getElementById("landing-hero-title"); 888 var heroSubtitle = document.getElementById("landing-hero-subtitle"); 889 var content = document.getElementById("landing-content"); 890 891 if (!heroTitle || !heroSubtitle || !content) { 892 return; 893 } 894 895 var lang = getCurrentLanguage(); 896 if (lang !== "en") { 897 page = localizedPage(page, lang); 898 } 899 900 heroTitle.textContent = page.h1; 901 heroSubtitle.textContent = page.subtitle; 902 903 content.innerHTML = buildSectionContent(page, allPages); 904 905 var faqs = buildFaqs(page); 906 setMeta(page); 907 setSchema(page, faqs); 908 } 909 910 document.addEventListener("DOMContentLoaded", function () { 911 var slug = document.body.getAttribute("data-landing-slug"); 912 913 if (!slug) { 914 return; 915 } 916 917 // Pages pre-rendered at build time (npm run prerender) already contain 918 // their content, head tags and structured data. 919 if (document.body.hasAttribute("data-prerendered")) { 920 return; 921 } 922 923 fetch("/assets/json/landing-pages.json") 924 .then(function (response) { 925 if (!response.ok) { 926 throw new Error("Could not load landing page data"); 927 } 928 return response.json(); 929 }) 930 .then(function (pages) { 931 return fetch("/assets/json/landing-pages-i18n.json") 932 .then(function (r) { 933 return r.ok ? r.json() : null; 934 }) 935 .catch(function () { 936 return null; 937 }) 938 .then(function (i18n) { 939 I18N = i18n; 940 return pages; 941 }); 942 }) 943 .then(function (pages) { 944 var page = pages.find(function (item) { 945 return item.slug === slug; 946 }); 947 948 if (!page) { 949 var fallbackTitle = document.getElementById("landing-hero-title"); 950 var fallbackSubtitle = document.getElementById( 951 "landing-hero-subtitle", 952 ); 953 if (fallbackTitle) { 954 fallbackTitle.textContent = slugToTitle(slug); 955 } 956 if (fallbackSubtitle) { 957 fallbackSubtitle.textContent = 958 "Custom website services tailored to your business goals."; 959 } 960 return; 961 } 962 963 renderPage(page, pages); 964 }) 965 .catch(function (err) { 966 console.error(err); 967 }); 968 }); 969 970 // Expose renderPage for universal landing page 971 window.renderLandingPage = renderPage; 972})();
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.