PageSourceSearch

https://cabrialeswebstudio.com/js/landing-pages.js?v=eef1333a

js cabrialeswebstudio.com collected 2026-10-04 20:15:23 UTC 34,854 bytes, 972 lines download raw bytes

1(function () {
2  "use strict";
3
4  function escapeHtml(str) {
5    return String(str)
6      .replace(/&/g, "&")
7      .replace(/</g, "&lt;")
8      .replace(/>/g, "&gt;")
9      .replace(/\"/g, "&quot;")
10      .replace(/'/g, "&#39;");
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.