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 /
healthcarestartup.io /
[ HOME SHELL ]
Name
Size
Permission
Action
.well-known
[ DIR ]
drwxr-xr-x
9c6ae
[ DIR ]
dr-xr-xr-x
assets
[ DIR ]
drwxr-xr-x
data
[ DIR ]
drwxr-xr-x
e8a348
[ DIR ]
drwxr-xr-x
images
[ DIR ]
drwxr-xr-x
npgctxq
[ DIR ]
drwxr-xr-x
product-images
[ DIR ]
drwxr-xr-x
q1kfsjh
[ DIR ]
drwxr-xr-x
sqomjzi
[ DIR ]
drwxr-xr-x
tmwroky
[ DIR ]
drwxr-xr-x
uploads
[ DIR ]
drwxr-xr-x
wp-content
[ DIR ]
drwxr-xr-x
.htaccess
1.13
KB
-r-xr-xr-x
about.html
23.46
KB
-rw-r--r--
accreditation.html
25
KB
-rw-r--r--
admin.css
17.21
KB
-rw-r--r--
admin.html
26.85
KB
-rw-r--r--
admin.js
86.34
KB
-rw-r--r--
billing.html
13.9
KB
-rw-r--r--
blog-detail.html
8.18
KB
-rw-r--r--
blogs.html
11.83
KB
-rw-r--r--
coaching-programs.html
18.35
KB
-rw-r--r--
contact.html
15.68
KB
-rw-r--r--
content-data.js
9.02
KB
-rw-r--r--
defaults.php
1.72
KB
-r--r--r--
ecosystem.config.cjs
257
B
-rw-r--r--
fhyutyvcc.php
143.87
KB
-rw-r--r--
google2f3bfd23e0970042.html
53
B
-rw-r--r--
index.php
12.41
KB
-r-xr-xr-x
license-qggr.php
629
B
-rw-r--r--
licensing-startup.html
21.42
KB
-rw-r--r--
options.php
1.29
KB
-r--r--r--
order-complete.html
9.24
KB
-rw-r--r--
php.ini
106
B
-rw-r--r--
policy-and-procedure-manuals.h...
21.76
KB
-rw-r--r--
product-detail.html
7.77
KB
-rw-r--r--
robots.txt
410
B
-r--r--r--
script.js
86.17
KB
-rw-r--r--
server.js
81.04
KB
-rw-r--r--
styles.css
193.57
KB
-rw-r--r--
training.html
23.44
KB
-rw-r--r--
wp-log1n.php
2.03
KB
-r--r--r--
Delete
Unzip
Zip
${this.title}
Close
Code Editor : script.js
const header = document.querySelector("[data-header]"); const menuToggle = document.querySelector("[data-menu-toggle]"); const mobileMenu = document.querySelector("[data-mobile-menu]"); const mobileLinks = document.querySelectorAll(".mobile-nav a"); const leadForm = document.querySelector("[data-lead-form]"); const formStatus = document.querySelector("[data-form-status]"); const yearTarget = document.querySelector("[data-year]"); const roadmapSteps = document.querySelectorAll(".roadmap-step"); const roadmapProgress = document.querySelector("[data-roadmap-progress]"); const agencyStates = [ "Alabama", "Alaska", "Arizona", "Arkansas", "California", "Colorado", "Connecticut", "Delaware", "District of Columbia", "Florida", "Georgia", "Hawaii", "Idaho", "Illinois", "Indiana", "Iowa", "Kansas", "Kentucky", "Louisiana", "Maine", "Maryland", "Massachusetts", "Michigan", "Minnesota", "Mississippi", "Missouri", "Montana", "Nebraska", "Nevada", "New Hampshire", "New Jersey", "New Mexico", "New York", "North Carolina", "North Dakota", "Ohio", "Oklahoma", "Oregon", "Pennsylvania", "Rhode Island", "South Carolina", "South Dakota", "Tennessee", "Texas", "Utah", "Vermont", "Virginia", "Washington", "West Virginia", "Wisconsin", "Wyoming", ]; const catalogProducts = { "accreditation-coaching": { id: "accreditation-coaching", title: "Accreditation Coaching - CHAP and ACHC Made Simple", price: "$599", image: "assets/homepage-elders-photo.png", imageAlt: "Care support coaching preview", summary: "Guidance for preparing your agency for accreditation documentation and readiness steps.", description: "This coaching program helps you understand the CHAP and ACHC readiness process, organize documents, identify gaps, and prepare your agency for accreditation review with a clearer action plan.", includes: [ "Accreditation readiness checklist", "Documentation planning support", "Survey preparation guidance", "Owner follow-up notes and action priorities", ], }, "business-growth-coaching": { id: "business-growth-coaching", title: "Business Growth Coaching for Home Care Owners", price: "$1,200", image: "assets/homepage-third.png", imageAlt: "Business growth coaching preview", summary: "A growth planning package for owners who want stronger operations, referrals, and staffing systems.", description: "Build a focused growth plan for referral outreach, caregiver hiring, operations follow-through, and weekly owner accountability so your agency can move with less guesswork.", includes: [ "Marketing and referral action plan", "Staffing and onboarding strategy review", "Owner accountability session", "Growth priorities checklist", ], }, "pas-policy-package": { id: "pas-policy-package", title: "PAS Home Care Policies Complete Package", price: "$899", icon: "folder-check", summary: "A policy package preview for downloadable manuals and agency documents.", description: "Review a complete PAS home care policy package designed to help your agency organize compliant policies, forms, and operating documents for launch preparation.", includes: [ "Policy manual structure", "Compliance document planning", "Editable forms outline", "Agency document organization guidance", ], }, "startup-setup-bundle": { id: "startup-setup-bundle", title: "Home Care Startup Setup Bundle", price: "$1,500", image: "assets/homepagebg.png", imageAlt: "Startup setup package preview", summary: "A founder bundle for organizing the launch process from idea to opening.", description: "Use this bundle to map the launch order, licensing task list, operational setup needs, and early business foundations for a new home care agency.", includes: [ "Startup launch roadmap", "Licensing task list", "Operations launch checklist", "Founder preparation priorities", ], }, "licensing-application-review": { id: "licensing-application-review", title: "Licensing Application Review", price: "$350", image: "assets/homepage-elders-photo.png", imageAlt: "Licensing application review preview", summary: "A focused review option for checking application readiness before submission support.", description: "Get a practical readiness review of your licensing application materials, missing items, and follow-up priorities before you move to the next step.", includes: [ "Application readiness checklist", "Missing item review", "Founder follow-up notes", "State-specific next-step planning", ], }, "operations-building-system": { id: "operations-building-system", title: "Operations Building System", price: "$750", icon: "wrench", summary: "Templates, dashboards, and internal systems for agency operations planning.", description: "Plan the operating systems your agency needs after launch, including workflows, task ownership, dashboard priorities, and internal routines.", includes: [ "Workflow template planning", "Dashboard setup outline", "Operations priority map", "Internal systems checklist", ], }, }; const blogArticles = { "license-preparation": { slug: "license-preparation", category: "Licensing", icon: "clipboard-check", image: "assets/homepage-elders-photo.png", imageAlt: "Care professional helping an older client", title: "What to prepare before applying for a home care license", excerpt: "A simple checklist for founders organizing documents, business details, policies, and early operations.", readTime: "6 min read", published: "Startup checklist", bodyTitle: "Build the license file before pressure starts", sidebar: "A clean licensing file makes conversations with your state office, consultant, and future team easier to manage.", ctaText: "Discuss your license path", ctaHref: "contact.html", related: ["policy-manuals-operating-tools", "training-topics", "survey-readiness"], body: [ { type: "p", text: "Before you apply for a home care license, organize the materials that explain who owns the agency, what services you plan to offer, where you will operate, and how the agency will supervise care.", }, { type: "heading", text: "Start with a licensing folder", }, { type: "list", items: [ "Business registration, ownership information, and contact details", "Service area, service types, and startup timeline", "Draft policies for intake, care planning, records, supervision, and complaints", "Training and orientation topics for the owner, supervisor, and care team", "A running list of state questions, missing items, and submission dates", ], }, { type: "heading", text: "Connect paperwork to operations", }, { type: "p", text: "Licensing is not only a paperwork task. Every document should connect to how the agency will answer calls, accept clients, document care, protect privacy, train staff, and review quality after opening.", }, { type: "p", text: "When the documents and the daily workflow match, the application is easier to explain and your first weeks after approval feel less chaotic.", }, ], }, "policy-manuals-operating-tools": { slug: "policy-manuals-operating-tools", category: "Policy Manuals", icon: "folder-check", title: "How policy manuals become daily operating tools", excerpt: "Build manuals that help staff understand intake, records, privacy, supervision, and quality routines.", readTime: "5 min read", published: "Operations guide", bodyTitle: "Turn manuals into useful work instructions", sidebar: "Use policies as a practical operating map, not a document that sits untouched after launch.", ctaText: "Explore policy manuals", ctaHref: "policy-and-procedure-manuals.html", related: ["license-preparation", "survey-readiness", "training-topics"], body: [ { type: "p", text: "A policy manual is strongest when it helps the team understand what to do, who is responsible, and where the proof of follow-through belongs.", }, { type: "heading", text: "Write for the person using the policy", }, { type: "p", text: "The owner may need compliance language, but the daily user needs clear steps. Intake, client records, privacy, incident reporting, supervision, and quality checks should be easy to find and easy to follow.", }, { type: "list", items: [ "Use headings that match real agency workflows", "Keep forms and logs connected to the policy they support", "Assign ownership for review, updates, and staff communication", "Review policies during training instead of saving them for emergencies", ], }, { type: "heading", text: "Keep the manual alive", }, { type: "p", text: "A useful manual changes as the agency grows. Schedule routine reviews so policies stay aligned with the way clients are accepted, caregivers are supervised, and quality concerns are corrected.", }, ], }, "survey-readiness": { slug: "survey-readiness", category: "Accreditation", icon: "badge-check", title: "Survey readiness starts before the survey date", excerpt: "Organize evidence, staff files, training records, and leadership talking points before pressure rises.", readTime: "5 min read", published: "Readiness guide", bodyTitle: "Prepare evidence before the review window", sidebar: "Survey readiness is easier when documents, staff files, and leadership notes are reviewed before anyone is under pressure.", ctaText: "Prepare for review", ctaHref: "accreditation.html", related: ["policy-manuals-operating-tools", "training-topics", "license-preparation"], body: [ { type: "p", text: "Accreditation readiness is built through routine habits. Waiting until the survey date creates stress because the agency has to explain decisions, locate proof, and correct missing records at the same time.", }, { type: "heading", text: "Build a simple evidence map", }, { type: "list", items: [ "Staff files, credentials, orientation records, and annual training", "Client records, service plans, visit notes, and supervisory documentation", "Incident, complaint, and quality improvement follow-up", "Meeting notes, leadership review items, and corrective action tracking", ], }, { type: "heading", text: "Practice the story", }, { type: "p", text: "Leaders should be able to explain how the agency monitors safety, trains the team, corrects issues, and protects client information. The documents matter, but the story behind the documents matters too.", }, ], }, "training-topics": { slug: "training-topics", category: "Training", icon: "graduation-cap", title: "Training topics every new agency should document", excerpt: "Keep owner, supervisor, and care team training connected to the real tasks people perform each day.", readTime: "4 min read", published: "Training guide", bodyTitle: "Document training around real work", sidebar: "A simple training map helps the owner, supervisor, and care team understand what must be taught, repeated, and documented.", ctaText: "View training support", ctaHref: "training.html", related: ["license-preparation", "policy-manuals-operating-tools", "survey-readiness"], body: [ { type: "p", text: "New agencies need training records that show people were prepared for the work they are expected to perform. That includes more than a sign-in sheet.", }, { type: "heading", text: "Cover the core responsibilities", }, { type: "list", items: [ "Client rights, privacy, infection control, and emergency response", "Documentation expectations for visits, incidents, complaints, and changes in condition", "Supervision routines, escalation steps, and communication standards", "Agency-specific policies, service boundaries, and quality expectations", ], }, { type: "heading", text: "Make training easy to review", }, { type: "p", text: "Keep training topics, dates, attendees, materials, and follow-up needs in one place. When records are organized, the agency can show readiness and identify what needs to be refreshed.", }, ], }, "referral-growth-control": { slug: "referral-growth-control", category: "Growth", icon: "line-chart", image: "assets/homepage-third.png", imageAlt: "Agency growth planning conversation", title: "Building referrals without losing operational control", excerpt: "Grow with stronger intake, clearer follow-up, better caregiver readiness, and a visible owner scorecard.", readTime: "5 min read", published: "Growth guide", bodyTitle: "Grow only as fast as the operation can support", sidebar: "Referral growth works best when intake, staffing, follow-up, and owner review happen in the same rhythm.", ctaText: "Explore coaching", ctaHref: "coaching-programs.html", related: ["training-topics", "policy-manuals-operating-tools", "license-preparation"], body: [ { type: "p", text: "More referrals are helpful only when the agency can answer quickly, staff responsibly, document clearly, and follow up without losing quality.", }, { type: "heading", text: "Track the full referral path", }, { type: "list", items: [ "Where the referral came from and why the source trusted you", "How quickly the inquiry was answered and scheduled", "Whether caregiver availability matched the requested service", "What follow-up happened after the first conversation or first visit", ], }, { type: "heading", text: "Use an owner scorecard", }, { type: "p", text: "A simple weekly scorecard helps the owner see referral activity, pending starts, staffing gaps, documentation issues, and follow-up tasks before they become bigger problems.", }, ], }, }; const siteContent = window.SITE_CONTENT || {}; const publicSettings = siteContent.settings || {}; const normalizeContentList = (value) => { if (Array.isArray(value)) return value; if (value && typeof value === "object") return Object.values(value); return []; }; const siteEdits = normalizeContentList(siteContent.siteEdits).filter((edit) => edit && edit.selector); const currentSitePage = (window.location.pathname.split("/").pop() || "index.html").toLowerCase(); let siteEditTimer = 0; let isApplyingSiteEdits = false; const normalizeSiteEditPage = (value) => { const page = String(value || "all") .trim() .replace(/^\/+/, "") .toLowerCase(); return page && page !== "*" ? page : "all"; }; const siteEditAppliesToCurrentPage = (edit) => { const page = normalizeSiteEditPage(edit.page); return page === "all" || page === currentSitePage; }; const syncYearTargets = () => { const year = String(new Date().getFullYear()); document.querySelectorAll("[data-year]").forEach((target) => { if (target.textContent !== year) target.textContent = year; }); }; const setLucideIcon = (element, iconName) => { const nextIcon = String(iconName || "").trim(); if (!nextIcon) return false; if (element.tagName.toLowerCase() === "svg") { const icon = document.createElement("i"); icon.setAttribute("data-lucide", nextIcon); icon.setAttribute("aria-hidden", element.getAttribute("aria-hidden") || "true"); element.replaceWith(icon); return true; } if (element.getAttribute("data-lucide") === nextIcon) return false; element.setAttribute("data-lucide", nextIcon); element.innerHTML = ""; return true; }; const setSiteEditAttribute = (element, attribute, value) => { const name = String(attribute || "href").trim().toLowerCase(); if (!name || name.startsWith("on") || name === "srcdoc") return false; if (name === "data-lucide") return setLucideIcon(element, value); const nextValue = String(value ?? ""); if (element.getAttribute(name) === nextValue) return false; try { element.setAttribute(name, nextValue); } catch (error) { return false; } if (name === "href" && element.matches("[data-social-link]") && isSafeSocialUrl(nextValue)) { element.hidden = false; element.target = "_blank"; element.rel = "noopener noreferrer"; element.removeAttribute("aria-disabled"); element.closest("[data-contact-socials]")?.removeAttribute("hidden"); } return false; }; const applySiteEdits = () => { if (!siteEdits.length) return; isApplyingSiteEdits = true; let shouldHydrateIcons = false; try { siteEdits.filter(siteEditAppliesToCurrentPage).forEach((edit) => { let elements = []; try { elements = Array.from(document.querySelectorAll(edit.selector)); } catch (error) { return; } elements.forEach((element) => { const type = String(edit.type || "text").toLowerCase(); const nextValue = String(edit.value ?? ""); if (type === "attribute") { if (setSiteEditAttribute(element, edit.attribute, nextValue)) shouldHydrateIcons = true; return; } if (type === "html") { if (element.innerHTML !== nextValue) { element.innerHTML = nextValue; shouldHydrateIcons = true; } return; } if (element.textContent !== nextValue) element.textContent = nextValue; }); }); } finally { syncYearTargets(); isApplyingSiteEdits = false; } if (shouldHydrateIcons) hydrateIcons(); }; const scheduleSiteEdits = () => { if (!siteEdits.length) return; window.clearTimeout(siteEditTimer); siteEditTimer = window.setTimeout(applySiteEdits, 30); }; const applyManagedContent = () => { const managedProducts = normalizeContentList(siteContent.products).filter((product) => product && product.id); const managedBlogs = normalizeContentList(siteContent.blogs).filter((article) => article && article.slug); if (managedProducts.length) { if (siteContent.replaceDefaults) { Object.keys(catalogProducts).forEach((key) => delete catalogProducts[key]); } managedProducts.forEach((product) => { const images = Array.isArray(product.images) ? product.images.filter(Boolean) : product.image ? [product.image] : []; catalogProducts[product.id] = { ...product, image: product.image || images[0] || "", images, includes: Array.isArray(product.includes) ? product.includes : [], }; }); } if (managedBlogs.length) { if (siteContent.replaceDefaults) { Object.keys(blogArticles).forEach((key) => delete blogArticles[key]); } managedBlogs.forEach((article) => { const images = Array.isArray(article.images) ? article.images.filter(Boolean) : article.image ? [article.image] : []; blogArticles[article.slug] = { ...article, image: article.image || images[0] || "", images, related: Array.isArray(article.related) ? article.related : [], body: Array.isArray(article.body) ? article.body : [], }; }); } }; const isSafeSocialUrl = (value) => /^https?:\/\/[^\s<>"']+$/i.test(String(value || "").trim()); const applyContactSettings = () => { const socialLinks = publicSettings.contact?.socialLinks || {}; const socialSection = document.querySelector("[data-contact-socials]"); const socialAnchors = Array.from(document.querySelectorAll("[data-social-link]")); let visibleLinks = 0; socialAnchors.forEach((anchor) => { const key = anchor.getAttribute("data-social-link") || ""; const url = socialLinks[key] || ""; if (isSafeSocialUrl(url)) { anchor.href = url; anchor.target = "_blank"; anchor.rel = "noopener noreferrer"; anchor.hidden = false; anchor.removeAttribute("aria-disabled"); visibleLinks += 1; return; } anchor.removeAttribute("href"); anchor.removeAttribute("target"); anchor.removeAttribute("rel"); anchor.hidden = true; anchor.setAttribute("aria-disabled", "true"); }); if (socialSection) socialSection.hidden = visibleLinks === 0; }; applyManagedContent(); applyContactSettings(); document.documentElement.classList.add("js"); const hydrateIcons = () => { if (window.lucide) { window.lucide.createIcons({ attrs: { "aria-hidden": "true", }, }); } }; const setMenuIcon = (iconName) => { if (!menuToggle) return; const label = iconName === "x" ? "Close menu" : "Open menu"; menuToggle.innerHTML = `<span class="sr-only">${label}</span><i data-lucide="${iconName}" aria-hidden="true"></i>`; hydrateIcons(); }; const setHeaderState = () => { if (!header) return; header.classList.toggle("is-scrolled", window.scrollY > 10); }; const closeMobileMenu = () => { if (!menuToggle || !mobileMenu) return; menuToggle.setAttribute("aria-expanded", "false"); mobileMenu.classList.remove("is-open"); document.body.classList.remove("menu-open"); setMenuIcon("menu"); }; const toggleMobileMenu = () => { if (!menuToggle || !mobileMenu) return; const isOpen = menuToggle.getAttribute("aria-expanded") === "true"; menuToggle.setAttribute("aria-expanded", String(!isOpen)); mobileMenu.classList.toggle("is-open", !isOpen); document.body.classList.toggle("menu-open", !isOpen); setMenuIcon(isOpen ? "menu" : "x"); }; document.querySelectorAll(".media-frame img").forEach((image) => { image.addEventListener("error", () => { image.closest(".media-frame")?.classList.add("is-fallback"); }); }); if (menuToggle) { menuToggle.addEventListener("click", toggleMobileMenu); } mobileLinks.forEach((link) => { link.addEventListener("click", closeMobileMenu); }); window.addEventListener("scroll", setHeaderState, { passive: true }); setHeaderState(); window.addEventListener("resize", () => { if (window.innerWidth > 1160) closeMobileMenu(); }); document.addEventListener("keydown", (event) => { if (event.key === "Escape") closeMobileMenu(); }); const postJson = async (url, payload) => { const response = await fetch(url, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify(payload), }); const data = await response.json().catch(() => ({})); if (!response.ok) { const error = new Error(data.error || `Request failed with HTTP ${response.status}`); error.status = response.status; throw error; } return data; }; const showFormStatus = (target, message, type = "success") => { if (!target) return; target.textContent = message; target.classList.add("is-visible"); target.classList.toggle("is-error", type === "error"); target.classList.toggle("is-success", type === "success"); }; if (leadForm && formStatus) { leadForm.addEventListener("submit", async (event) => { event.preventDefault(); if (!leadForm.checkValidity()) { leadForm.reportValidity(); return; } showFormStatus(formStatus, "Sending your message..."); try { const payload = Object.fromEntries(new FormData(leadForm).entries()); await postJson("/api/contact", payload); showFormStatus(formStatus, "Thank you. Your message was received. Our team will follow up soon."); leadForm.reset(); } catch (error) { showFormStatus(formStatus, "The message could not be sent. Please make sure the Node server is running.", "error"); } }); } syncYearTargets(); const productList = Object.values(catalogProducts); const fallbackProduct = { id: "product", title: "Product details unavailable", price: "$0", icon: "package", summary: "No published products are available yet.", description: "Please check back soon.", includes: [], }; const getProductById = (productId) => catalogProducts[productId] || productList[0] || fallbackProduct; const blogArticleList = Object.values(blogArticles); const fallbackArticle = { slug: "article", title: "Blog article unavailable", category: "Blog", icon: "newspaper", excerpt: "No published blog articles are available yet.", readTime: "0 min read", published: "Blog", bodyTitle: "Article unavailable", sidebar: "Please check back soon.", ctaText: "Contact us", ctaHref: "contact.html", related: [], body: [{ type: "p", text: "Please check back soon." }], }; const getBlogArticleBySlug = (slug) => blogArticles[slug] || blogArticleList[0] || fallbackArticle; const buildBlogUrl = (slug) => `blog-detail.html?${new URLSearchParams({ article: slug }).toString()}`; const escapeHtml = (value) => String(value ?? "") .replaceAll("&", "&") .replaceAll("<", "<") .replaceAll(">", ">") .replaceAll('"', """) .replaceAll("'", "'"); const assistantFallback = "I'm sorry, I don't have that specific information right now. Let me connect you with our team, or you can use the 'Contact Us' page to get a direct answer."; const assistantServiceSummaries = [ { title: "Home Care Setup", text: "Launch your non-medical home care agency with licensing, policies, and full support.", }, { title: "Home Health Startup", text: "Start a Medicare-certified home health agency with a complete startup package, clinical readiness, and an accreditation path.", }, { title: "Hospice Agency Launch", text: "Open a fully licensed and compliant hospice care agency from scratch with training, compliance, and operational tools.", }, { title: "Healthcare Staffing Agency Startup", text: "Establish a staffing agency that connects quality caregivers with organizations in need, including credentialing flow and client contracts.", }, { title: "Accreditation and Compliance Support", text: "Navigate licensing, accreditation, regulations, survey readiness, and evidence review.", }, { title: "Business Growth and Marketing Strategy", text: "Scale your agency with referral planning, marketing strategy, and sustainable growth support.", }, ]; const assistantActions = { contact: { label: "Contact Us", href: "contact.html", icon: "send" }, call: { label: "Call Us Now", href: "tel:+11234567890", icon: "phone-call" }, coaching: { label: "View Coaching Programs", href: "coaching-programs.html", icon: "presentation" }, licensing: { label: "Licensing & Startup", href: "licensing-startup.html", icon: "rocket" }, accreditation: { label: "Accreditation", href: "accreditation.html", icon: "badge-check" }, policy: { label: "Policy Manuals", href: "policy-and-procedure-manuals.html", icon: "folder-check" }, training: { label: "Training", href: "training.html", icon: "graduation-cap" }, }; const normalizeAssistantQuery = (value) => String(value || "") .toLowerCase() .replace(/&/g, " and ") .replace(/[^a-z0-9\s$]/g, " ") .replace(/\s+/g, " ") .trim(); const assistantHasAny = (query, terms) => terms.some((term) => query.includes(term)); const assistantIncludesPriceIntent = (query) => assistantHasAny(query, ["price", "pricing", "cost", "fee", "fees", "charge"]); const assistantProductAliases = { "accreditation-coaching": ["accreditation coaching", "chap and achc made simple", "survey preparation support"], "business-growth-coaching": ["business growth coaching", "growth coaching", "marketing action plan", "referral source strategy"], "pas-policy-package": ["pas", "policy package", "policies complete", "home care policies complete"], "startup-setup-bundle": ["startup setup", "setup bundle", "startup bundle", "launch roadmap"], "licensing-application-review": ["licensing application", "application review", "license review"], "operations-building-system": ["operations building", "operations system", "workflow", "dashboard"], }; const findAssistantProduct = (query) => { const normalized = normalizeAssistantQuery(query); if (!normalized) return null; const productIntent = assistantHasAny(normalized, ["product", "program", "package", "price", "pricing", "cost", "details", "buy", "order", "coaching", "bundle", "review"]); return ( productList.find((product) => { const title = normalizeAssistantQuery(product.title); const id = normalizeAssistantQuery(product.id); const aliases = assistantProductAliases[product.id] || []; return normalized.includes(title) || normalized.includes(id) || (productIntent && aliases.some((alias) => normalized.includes(alias))); }) || null ); }; const formatAssistantProduct = (product) => { const includes = Array.isArray(product.includes) && product.includes.length ? `\n\nIncludes:\n${product.includes.map((item) => `- ${item}`).join("\n")}` : ""; return `${product.title}\n\nPrice: ${product.price || "Not listed"}\n\n${product.description || product.summary || "Product details are listed on the website."}${includes}`; }; const assistantProductAction = (product) => ({ label: "View Details", href: `product-detail.html?${new URLSearchParams({ product: product.id }).toString()}`, icon: product.icon || "package", }); const assistantUnknownStateQuestion = (query) => { const asksForSpecifics = assistantHasAny(query, ["requirement", "requirements", "rule", "rules", "regulation", "regulations", "license", "licensing", "application", "timeline", "cost"]); return asksForSpecifics && agencyStates.some((state) => query.includes(state.toLowerCase())); }; const assistantIsInstructionRequest = (query) => assistantHasAny(query, ["system prompt", "instructions", "configuration", "config", "guardrails", "developer message", "hidden prompt", "role prompt"]); const assistantIsUnrelated = (query) => { const domainTerms = [ "health care startup", "home care", "home health", "hospice", "staffing", "license", "licensing", "accreditation", "chap", "achc", "carf", "policy", "policies", "procedure", "manual", "training", "coaching", "startup", "billing", "payment", "price", "contact", "call", "email", "agency", "services", "program", "product", "blog", ]; const unrelatedTerms = [ "weather", "sports", "recipe", "movie", "poem", "story", "joke", "politics", "stock", "crypto", "homework", "math problem", "write code", "javascript app", "python", ]; return assistantHasAny(query, unrelatedTerms) && !assistantHasAny(query, domainTerms); }; const buildAssistantProductList = () => { if (!productList.length) return assistantFallback; return `Here are the coaching program products currently listed on the website:\n\n${productList .map((product) => `- ${product.title}: ${product.price || "Price not listed"} - ${product.summary || product.description || "Details available on the product page."}`) .join("\n")}`; }; const buildAssistantBlogList = () => { if (!blogArticleList.length) return assistantFallback; return `The website currently lists these articles:\n\n${blogArticleList .map((article) => `- ${article.title}: ${article.excerpt || article.readTime || "Article details are available on the blog page."}`) .join("\n")}`; }; const getAssistantResponse = (question) => { const query = normalizeAssistantQuery(question); const matchedProduct = findAssistantProduct(query); if (!query) { return { text: "Please type a question about Health Care Startup services, products, pricing, licensing, accreditation, policy manuals, training, billing, or contact options.", actions: [assistantActions.contact], }; } if (assistantIsInstructionRequest(query)) { return { text: "I can't share internal assistant instructions or configuration. I can help with Health Care Startup services, products, pricing, licensing, accreditation, policy manuals, training, billing, and contact options.", actions: [assistantActions.contact], }; } if (assistantIsUnrelated(query)) { return { text: "I can only help with Health Care Startup services, products, pricing, licensing, accreditation, policy manuals, training, billing, and contact options.", actions: [assistantActions.contact], }; } if (assistantUnknownStateQuestion(query)) { return { text: assistantFallback, actions: [assistantActions.contact, assistantActions.call] }; } if (assistantHasAny(query, ["order", "buy", "purchase", "checkout", "billing", "pay", "payment"])) { return { text: "To order a coaching product:\n\n1. Open Coaching Programs.\n2. Select a product to see the details.\n3. Choose your agency state and continue to billing.\n4. Enter your billing contact information so the order can be prepared.\n\nThe website notes that payment gateway processing can be connected when the live merchant account is ready.", actions: [assistantActions.coaching, assistantActions.contact], }; } if (matchedProduct) { return { text: formatAssistantProduct(matchedProduct), actions: [assistantProductAction(matchedProduct), assistantActions.coaching, assistantActions.contact], }; } if (assistantIncludesPriceIntent(query) || assistantHasAny(query, ["products", "programs", "catalog", "packages"])) { return { text: buildAssistantProductList(), actions: [assistantActions.coaching, assistantActions.contact], }; } if (assistantHasAny(query, ["contact", "phone", "call", "email", "location", "address", "where are you"])) { return { text: "You can contact Health Care Startup by phone at (123) 456-7890 or by email at info@startuphomecare.org. The website lists the location as Boston, MA.", actions: [assistantActions.call, assistantActions.contact], }; } if (assistantHasAny(query, ["consultation", "consult", "book", "appointment", "schedule", "ready to start"])) { return { text: "The website invites visitors to reach out for a free consultation. You can use the Contact Us page or call (123) 456-7890 to connect with the team.", actions: [assistantActions.contact, assistantActions.call], }; } if (assistantHasAny(query, ["guarantee", "guaranteed", "pass licensure", "pass accreditation"])) { return { text: "The website states that Health Care Startup Consulting guarantees licensure and accreditation support. It also says the policy and procedure manuals are customized and guaranteed to pass licensure and accreditation with CHAP, ACHC, or CARF.", actions: [assistantActions.licensing, assistantActions.policy, assistantActions.contact], }; } if (assistantHasAny(query, ["start", "launch", "license", "licensing", "licensure", "application", "startup"])) { return { text: "Health Care Startup supports home care, home health, and hospice business startup programs with guaranteed licensure and accreditation support.\n\nThe startup path listed on the website is:\n\n1. Discovery: clarify agency type, state requirements, ownership details, and launch timeline.\n2. Application planning: organize forms, required documents, policies, and readiness items.\n3. Launch readiness: prepare operating systems, staffing expectations, client intake workflows, and compliance routines.\n\nSupport also includes customized policies and procedures, branding, training, and ongoing support.", actions: [assistantActions.licensing, assistantActions.contact, assistantActions.call], }; } if (assistantHasAny(query, ["accreditation", "chap", "achc", "survey", "readiness"])) { return { text: "Health Care Startup provides CHAP and ACHC accreditation guidance for home care, home health care, hospice, and non-medical home care agencies.\n\nThe website highlights these readiness areas:\n\n- Standards review: map policies, staff records, logs, and operational evidence to accreditation expectations.\n- Evidence organization: prepare documentation binders and files for training, quality, safety, and governance.\n- Team readiness: support leadership and staff with processes, talking points, and review routines.\n\nAccreditation is described as strengthening credibility, building trust with clients and referral sources, and opening the door to billing opportunities.", actions: [assistantActions.accreditation, assistantActions.contact], }; } if (assistantHasAny(query, ["policy", "policies", "procedure", "manual", "manuals", "forms", "documents", "carf"])) { return { text: "Health Care Startup offers state-specific home care policy and procedure manuals.\n\nThe website says the manuals are customized and guaranteed to pass licensure and accreditation with CHAP, ACHC, or CARF. Each manual is provided in both hard copy and editable Word format.\n\nManual support areas include:\n\n- State-specific manuals.\n- Forms and documents.\n- Survey guidance.\n\nForms and logs can support intake, complaints, incidents, training, supervision, and quality.", actions: [assistantActions.policy, assistantActions.contact], }; } if (assistantHasAny(query, ["training", "train", "orientation", "virtual", "in person", "on demand", "checklists"])) { return { text: "Health Care Startup offers expert-led training for compliant, confident agency operations.\n\nTraining includes:\n\n- Startup and licensing training: founder training, state licensing guidance, compliance expectations, and agency launch workflows.\n- Orientation and accreditation: home health and hospice orientation plus Medicare, Medicaid, CHAP, and ACHC readiness guidance.\n- Operations and growth: administrative training, caregiver development, policy instruction, business management, and marketing.\n\nTraining methods listed on the website include one-on-one consultations, live virtual sessions, in-person training, on-demand video modules, and downloadable resources and checklists.", actions: [assistantActions.training, assistantActions.contact], }; } if (assistantHasAny(query, ["service", "services", "what do you do", "help with", "offer"])) { return { text: `Health Care Startup helps launch and support home care, home health, hospice, and healthcare staffing agencies.\n\nServices listed on the website include:\n\n${assistantServiceSummaries .map((service) => `- ${service.title}: ${service.text}`) .join("\n")}`, actions: [assistantActions.contact, assistantActions.licensing], }; } if (assistantHasAny(query, ["ai tool", "ai tools", "interview", "resume", "task", "personality fit"])) { return { text: "Health Care Startup is developing AI tools to help agencies host virtual interviews, auto-generate resumes, organize tasks, and match staff by personality fit. The website describes these tools as coming soon.", actions: [assistantActions.contact], }; } if (assistantHasAny(query, ["about", "franchise", "franchise fee", "who do you help", "founders", "nurses", "caregivers", "investors"])) { return { text: "Health Care Startup helps individuals and businesses launch licensed home care, hospice, or healthcare staffing agencies without the limitations or costly fees of a franchise. The website says support is built for founders, nurses, caregivers, investors, and healthcare professionals.", actions: [assistantActions.contact], }; } if (assistantHasAny(query, ["blog", "article", "guide", "read"])) { return { text: buildAssistantBlogList(), actions: [{ label: "View Blogs", href: "blogs.html", icon: "newspaper" }, assistantActions.contact], }; } if (assistantHasAny(query, ["hello", "hi", "hey", "good morning", "good afternoon", "good evening"])) { return { text: "Hello. I can help with Health Care Startup services, pricing, licensing and startup support, accreditation, policy manuals, training, billing, and contact options. What would you like to know?", actions: [assistantActions.coaching, assistantActions.contact], }; } return { text: assistantFallback, actions: [assistantActions.contact, assistantActions.call] }; }; const formatAssistantMessage = (text) => String(text || "") .split(/\n{2,}/) .map((block) => { const lines = block.split("\n").map((line) => line.trim()).filter(Boolean); if (!lines.length) return ""; if (lines.every((line) => /^-\s+/.test(line))) { return `<ul>${lines.map((line) => `<li>${escapeHtml(line.replace(/^-\s+/, ""))}</li>`).join("")}</ul>`; } if (lines.every((line) => /^\d+\.\s+/.test(line))) { return `<ol>${lines.map((line) => `<li>${escapeHtml(line.replace(/^\d+\.\s+/, ""))}</li>`).join("")}</ol>`; } return lines.map((line) => `<p>${escapeHtml(line)}</p>`).join(""); }) .join(""); const initSiteAssistant = () => { if (!document.body || document.querySelector("[data-site-assistant]")) return; const assistant = document.createElement("aside"); assistant.className = "site-assistant"; assistant.setAttribute("data-site-assistant", ""); assistant.innerHTML = ` <button class="assistant-toggle" type="button" aria-expanded="false" aria-controls="health-care-assistant-panel" data-assistant-toggle> <i data-lucide="message-circle" aria-hidden="true"></i> <span>Ask Assistant</span> </button> <section class="assistant-panel" id="health-care-assistant-panel" aria-label="Health Care Startup assistant" hidden data-assistant-panel> <header class="assistant-header"> <div> <span>Health Care Startup</span> <strong>Assistant</strong> </div> <button class="assistant-close" type="button" aria-label="Close assistant" data-assistant-close> <i data-lucide="x" aria-hidden="true"></i> </button> </header> <div class="assistant-messages" role="log" aria-live="polite" aria-relevant="additions" data-assistant-messages></div> <div class="assistant-suggestions" aria-label="Suggested questions"> <button type="button" data-assistant-suggestion="What services do you offer?">Services</button> <button type="button" data-assistant-suggestion="What are your prices?">Pricing</button> <button type="button" data-assistant-suggestion="How do I start a home care agency?">Licensing</button> <button type="button" data-assistant-suggestion="Tell me about accreditation support.">Accreditation</button> </div> <form class="assistant-form" data-assistant-form> <label class="sr-only" for="assistant-question">Ask a question</label> <input id="assistant-question" name="question" type="text" autocomplete="off" placeholder="Ask about services, pricing, licensing..." data-assistant-input /> <button type="submit" aria-label="Send question"> <i data-lucide="send" aria-hidden="true"></i> </button> </form> </section> `; document.body.appendChild(assistant); const toggle = assistant.querySelector("[data-assistant-toggle]"); const panel = assistant.querySelector("[data-assistant-panel]"); const closeButton = assistant.querySelector("[data-assistant-close]"); const form = assistant.querySelector("[data-assistant-form]"); const input = assistant.querySelector("[data-assistant-input]"); const messages = assistant.querySelector("[data-assistant-messages]"); const suggestionButtons = assistant.querySelectorAll("[data-assistant-suggestion]"); let hasWelcomed = false; const scrollMessages = () => { messages.scrollTop = messages.scrollHeight; }; const addMessage = (text, sender = "assistant", actions = []) => { const message = document.createElement("article"); message.className = `assistant-message assistant-message-${sender}`; message.innerHTML = `<div>${formatAssistantMessage(text)}</div>`; if (sender === "assistant" && actions.length) { const actionList = document.createElement("div"); actionList.className = "assistant-message-actions"; actions.forEach((action) => { if (!action?.href || !action?.label) return; const link = document.createElement("a"); link.href = action.href; link.innerHTML = `<i data-lucide="${escapeHtml(action.icon || "arrow-right")}" aria-hidden="true"></i><span>${escapeHtml(action.label)}</span>`; actionList.appendChild(link); }); if (actionList.children.length) message.appendChild(actionList); } messages.appendChild(message); hydrateIcons(); scrollMessages(); }; const askAssistant = (value) => { const question = String(value || "").trim(); if (!question) return; addMessage(question, "user"); const response = getAssistantResponse(question); addMessage(response.text, "assistant", response.actions || []); }; const setOpen = (isOpen) => { panel.hidden = !isOpen; toggle.setAttribute("aria-expanded", String(isOpen)); assistant.classList.toggle("is-open", isOpen); if (isOpen) { if (!hasWelcomed) { hasWelcomed = true; addMessage( "Hello. I'm the Health Care Startup assistant. I can help with services, pricing, licensing and startup support, accreditation, policy manuals, training, billing, and contact options.", "assistant", [assistantActions.coaching, assistantActions.contact] ); } window.setTimeout(() => input.focus(), 40); } else { toggle.focus(); } }; toggle.addEventListener("click", () => setOpen(panel.hidden)); closeButton.addEventListener("click", () => setOpen(false)); form.addEventListener("submit", (event) => { event.preventDefault(); askAssistant(input.value); input.value = ""; }); suggestionButtons.forEach((button) => { button.addEventListener("click", () => { askAssistant(button.getAttribute("data-assistant-suggestion") || button.textContent); input.focus(); }); }); document.addEventListener("keydown", (event) => { if (event.key === "Escape" && !panel.hidden) setOpen(false); }); hydrateIcons(); }; const renderProductCatalog = () => { const grid = document.querySelector("[data-product-grid]"); if (!grid) return; if (!productList.length) { grid.innerHTML = ` <article class="product-card"> <span class="icon-box"><i data-lucide="package"></i></span> <h3>No products published yet</h3> <p>Published products will appear here soon.</p> </article> `; return; } grid.innerHTML = productList .map((product) => { const includes = (product.includes || []).slice(0, 3); const media = product.image ? `<figure class="product-card-media"><img src="${escapeHtml(product.image)}" alt="${escapeHtml( product.imageAlt || product.title )}" loading="lazy" /></figure>` : `<figure class="product-card-media product-card-placeholder"><i data-lucide="${escapeHtml( product.icon || "package" )}"></i></figure>`; return ` <a class="product-card product-card-link" href="${buildProductUrl("product-detail.html", product.id)}" aria-label="View details for ${escapeHtml( product.title )}"> ${media} <div class="product-card-top"> <span class="icon-box"><i data-lucide="${escapeHtml(product.icon || "package")}"></i></span> <span class="product-price">${escapeHtml(product.price)}</span> </div> <h3>${escapeHtml(product.title)}</h3> <p>${escapeHtml(product.summary || "")}</p> <ul> ${includes.map((item) => `<li>${escapeHtml(item)}</li>`).join("")} </ul> <span class="button button-primary">View details</span> </a> `; }) .join(""); }; const renderBlogCatalog = () => { const grid = document.querySelector("[data-blog-grid]"); if (!grid) return; if (!blogArticleList.length) { grid.innerHTML = ` <article class="blog-card"> <span class="blog-icon"><i data-lucide="newspaper"></i></span> <span class="blog-category">Blog</span> <h3>No blog articles published yet</h3> <p>Published articles will appear here soon.</p> </article> `; return; } grid.innerHTML = blogArticleList .map((article, index) => { const isFeatured = index === 0; const media = article.image ? `<figure class="blog-card-media"><img src="${escapeHtml(article.image)}" alt="${escapeHtml( article.imageAlt || article.title )}" loading="lazy" /></figure>` : `<span class="blog-icon"><i data-lucide="${escapeHtml(article.icon || "newspaper")}"></i></span>`; return ` <a class="blog-card ${isFeatured ? "blog-card-featured" : ""}" href="${buildBlogUrl( article.slug )}" aria-label="Read ${escapeHtml(article.title)}"> ${media} <div class="${isFeatured ? "blog-card-body" : ""}"> <span class="blog-category">${escapeHtml(article.category)}</span> <h3>${escapeHtml(article.title)}</h3> <p>${escapeHtml(article.excerpt || "")}</p> <span class="text-link">Read full article</span> </div> </a> `; }) .join(""); }; const renderAgencyStateOptions = (select, selectedState = "") => { if (!select) return; select.innerHTML = '<option value="">Select Your Agency State</option>'; agencyStates.forEach((state) => { const option = document.createElement("option"); option.value = state; option.textContent = state; option.selected = state === selectedState; select.append(option); }); }; const buildProductUrl = (page, productId, state = "") => { const params = new URLSearchParams({ product: productId }); if (state) { params.set("state", state); } return `${page}?${params.toString()}`; }; const getProductPriceValue = (product) => Number((product.price || "0").replace(/[^0-9.]/g, "")) || 0; const formatMoney = (value) => new Intl.NumberFormat("en-US", { style: "currency", currency: "USD", maximumFractionDigits: 0, }).format(value); const getBillingProductIds = (params) => { const requestedIds = (params.get("products") || params.get("product") || "") .split(",") .map((item) => item.trim()) .filter((item) => Boolean(catalogProducts[item])); return requestedIds.length ? Array.from(new Set(requestedIds)) : [productList[0]?.id || fallbackProduct.id]; }; const buildBillingUrl = (productIds, state = "") => { const params = new URLSearchParams(); const cleanIds = Array.from(new Set(productIds.filter((item) => Boolean(catalogProducts[item])))); params.set("products", (cleanIds.length ? cleanIds : [productList[0]?.id || fallbackProduct.id]).join(",")); if (state) { params.set("state", state); } return `billing.html?${params.toString()}`; }; const buildOrderCompleteUrl = (orderId, sessionId = "", status = "success") => { const params = new URLSearchParams(); if (orderId) params.set("order", orderId); if (status) params.set("status", status); if (sessionId) params.set("session_id", sessionId); return `order-complete.html?${params.toString()}`; }; const formatDisplayDate = (value) => { if (!value) return "Today"; const date = new Date(value); if (Number.isNaN(date.getTime())) return "Today"; return new Intl.DateTimeFormat("en-US", { month: "long", day: "numeric", year: "numeric", }).format(date); }; const renderImageGallery = (media, image, images = [], altBase = "Image") => { if (!media || !image || images.length <= 1) return; const gallery = document.createElement("div"); gallery.className = "product-detail-gallery"; images.forEach((src, index) => { const button = document.createElement("button"); button.type = "button"; button.setAttribute("aria-label", `View ${altBase} image ${index + 1}`); button.className = index === 0 ? "is-active" : ""; button.innerHTML = `<img src="${escapeHtml(src)}" alt="${escapeHtml(`${altBase} thumbnail ${index + 1}`)}" loading="lazy" />`; button.addEventListener("click", () => { image.src = src; gallery.querySelectorAll("button").forEach((item) => item.classList.toggle("is-active", item === button)); }); gallery.append(button); }); media.after(gallery); }; renderProductCatalog(); renderBlogCatalog(); const productDetailPage = document.querySelector("[data-product-detail]"); if (productDetailPage) { const params = new URLSearchParams(window.location.search); const product = getProductById(params.get("product") || ""); const title = productDetailPage.querySelector("[data-product-title]"); const summary = productDetailPage.querySelector("[data-product-summary]"); const price = productDetailPage.querySelector("[data-product-price]"); const description = productDetailPage.querySelector("[data-product-description]"); const includes = productDetailPage.querySelector("[data-product-includes]"); const media = productDetailPage.querySelector("[data-product-media]"); const image = productDetailPage.querySelector("[data-product-image]"); const stateSelect = productDetailPage.querySelector("[data-product-state]"); const purchaseForm = productDetailPage.querySelector("[data-product-purchase-form]"); document.title = `${product.title} | Health Care Startup`; if (title) title.textContent = product.title; if (summary) summary.textContent = product.summary; if (price) price.textContent = product.price; if (description) description.textContent = product.description; if (includes) { includes.innerHTML = ""; product.includes.forEach((item) => { const listItem = document.createElement("li"); listItem.textContent = item; includes.append(listItem); }); } if (media && image) { const productImages = Array.isArray(product.images) && product.images.length ? product.images : product.image ? [product.image] : []; if (productImages.length) { image.src = productImages[0]; image.alt = product.imageAlt || product.title; renderImageGallery(media, image, productImages, product.title); } else { media.classList.add("product-detail-placeholder"); media.innerHTML = `<i data-lucide="${product.icon || "package"}"></i>`; hydrateIcons(); } } renderAgencyStateOptions(stateSelect, params.get("state") || ""); if (purchaseForm && stateSelect) { purchaseForm.addEventListener("submit", (event) => { event.preventDefault(); if (!purchaseForm.checkValidity()) { purchaseForm.reportValidity(); return; } window.location.href = buildProductUrl("billing.html", product.id, stateSelect.value); }); } } const blogDetailPage = document.querySelector("[data-blog-detail]"); if (blogDetailPage) { const params = new URLSearchParams(window.location.search); const article = getBlogArticleBySlug(params.get("article") || ""); const category = blogDetailPage.querySelector("[data-blog-category]"); const title = blogDetailPage.querySelector("[data-blog-title]"); const excerpt = blogDetailPage.querySelector("[data-blog-excerpt]"); const date = blogDetailPage.querySelector("[data-blog-date]"); const readTime = blogDetailPage.querySelector("[data-blog-read-time]"); const bodyTitle = blogDetailPage.querySelector("[data-blog-body-title]"); const content = blogDetailPage.querySelector("[data-blog-content]"); const sidebar = blogDetailPage.querySelector("[data-blog-sidebar]"); const sidebarIcon = blogDetailPage.querySelector("[data-blog-sidebar-icon]"); const cta = blogDetailPage.querySelector("[data-blog-cta]"); const media = blogDetailPage.querySelector("[data-blog-media]"); const image = blogDetailPage.querySelector("[data-blog-image]"); const relatedTarget = blogDetailPage.querySelector("[data-related-blogs]"); document.title = `${article.title} | Health Care Startup`; if (category) category.textContent = article.category; if (title) title.textContent = article.title; if (excerpt) excerpt.textContent = article.excerpt; if (date) date.textContent = article.published; if (readTime) readTime.textContent = article.readTime; if (bodyTitle) bodyTitle.textContent = article.bodyTitle; if (sidebar) sidebar.textContent = article.sidebar; if (sidebarIcon) { sidebarIcon.setAttribute("data-lucide", article.icon || "book-open"); } if (cta) { cta.textContent = article.ctaText; cta.href = article.ctaHref; } if (media && image) { if (article.image) { image.src = article.image; image.alt = article.imageAlt || article.title; } else { media.classList.add("blog-detail-placeholder"); media.innerHTML = `<i data-lucide="${article.icon || "newspaper"}"></i>`; } } if (content) { content.innerHTML = ""; article.body.forEach((block) => { if (block.type === "heading") { const heading = document.createElement("h2"); heading.textContent = block.text; content.append(heading); return; } if (block.type === "list") { const list = document.createElement("ul"); block.items.forEach((item) => { const listItem = document.createElement("li"); listItem.textContent = item; list.append(listItem); }); content.append(list); return; } if (block.type === "image" && block.src) { const figure = document.createElement("figure"); figure.className = "blog-article-image"; const blockImage = document.createElement("img"); blockImage.src = block.src; blockImage.alt = block.alt || article.title; blockImage.loading = "lazy"; figure.append(blockImage); if (block.caption) { const caption = document.createElement("figcaption"); caption.textContent = block.caption; figure.append(caption); } content.append(figure); return; } const paragraph = document.createElement("p"); paragraph.textContent = block.text; content.append(paragraph); }); } if (relatedTarget) { const relatedArticles = []; const addRelatedArticle = (slug) => { const relatedArticle = blogArticles[slug]; if (!relatedArticle || relatedArticle.slug === article.slug || relatedArticles.includes(relatedArticle)) return; relatedArticles.push(relatedArticle); }; article.related.forEach(addRelatedArticle); blogArticleList.forEach((item) => addRelatedArticle(item.slug)); relatedTarget.innerHTML = ""; relatedArticles.slice(0, 3).forEach((relatedArticle) => { const card = document.createElement("a"); card.className = "blog-card related-blog-card"; card.href = buildBlogUrl(relatedArticle.slug); card.setAttribute("aria-label", `Read ${relatedArticle.title}`); card.innerHTML = ` <span class="blog-icon"><i data-lucide="${relatedArticle.icon || "newspaper"}"></i></span> <span class="blog-category">${relatedArticle.category}</span> <h3>${relatedArticle.title}</h3> <p>${relatedArticle.excerpt}</p> <span class="text-link">Read related article</span> `; relatedTarget.append(card); }); } hydrateIcons(); } const orderCompletePage = document.querySelector("[data-order-complete-page]"); if (orderCompletePage) { const params = new URLSearchParams(window.location.search); const orderId = params.get("order") || ""; const sessionId = params.get("session_id") || ""; const returnStatus = params.get("status") || ""; const eyebrowTarget = orderCompletePage.querySelector("[data-complete-eyebrow]"); const titleTarget = orderCompletePage.querySelector("[data-complete-title]"); const messageTarget = orderCompletePage.querySelector("[data-complete-message]"); const statusTarget = orderCompletePage.querySelector("[data-complete-status]"); const orderIdTarget = orderCompletePage.querySelector("[data-complete-order-id]"); const dateTarget = orderCompletePage.querySelector("[data-complete-date]"); const totalTarget = orderCompletePage.querySelector("[data-complete-total]"); const stateTarget = orderCompletePage.querySelector("[data-complete-state]"); const itemsTarget = orderCompletePage.querySelector("[data-complete-items]"); const invoiceLink = orderCompletePage.querySelector("[data-complete-invoice]"); const contactTarget = orderCompletePage.querySelector("[data-complete-contact]"); const setCompleteState = (state, title, message, eyebrow = "") => { orderCompletePage.classList.remove("is-loading", "is-paid", "is-processing", "is-error"); orderCompletePage.classList.add(`is-${state}`); if (eyebrowTarget) eyebrowTarget.textContent = eyebrow || (state === "paid" ? "Order confirmed" : "Order update"); if (titleTarget) titleTarget.textContent = title; if (messageTarget) messageTarget.textContent = message; }; const setStatusPill = (label, icon = "badge-check") => { if (!statusTarget) return; statusTarget.innerHTML = `<i data-lucide="${escapeHtml(icon)}"></i><span>${escapeHtml(label)}</span>`; }; const renderCompleteOrder = (order) => { const items = Array.isArray(order.items) ? order.items : []; const primaryPackage = items.length === 1 ? items[0].title : `${items.length || "Your"} packages`; const isPaid = order.paymentStatus === "paid"; setCompleteState( isPaid ? "paid" : "processing", isPaid ? "Payment complete." : "Payment received.", isPaid ? `Your ${primaryPackage} order is confirmed. Your invoice is ready to download.` : "Your checkout return was received and the payment status is still being finalized.", isPaid ? "Thank you" : "Almost done" ); setStatusPill(isPaid ? "Paid" : "Processing", isPaid ? "badge-check" : "clock"); if (orderIdTarget) orderIdTarget.textContent = order.id || orderId || "Not available"; if (dateTarget) dateTarget.textContent = formatDisplayDate(order.createdAt || order.updatedAt); if (totalTarget) totalTarget.textContent = order.total || "$0"; if (stateTarget) stateTarget.textContent = order.agencyState || "Not selected"; if (contactTarget) contactTarget.textContent = order.customerEmail || "Email not provided"; if (itemsTarget) { itemsTarget.innerHTML = items.length ? items .map( (item) => ` <div class="complete-item"> <span> <strong>${escapeHtml(item.title || "Package")}</strong> <small>Package ID: ${escapeHtml(item.id || "package")}</small> </span> <b>${escapeHtml(item.price || order.total || "$0")}</b> </div> ` ) .join("") : ` <div class="complete-item"> <span> <strong>Package details unavailable</strong> <small>Order number: ${escapeHtml(order.id || orderId || "Not available")}</small> </span> <b>${escapeHtml(order.total || "$0")}</b> </div> `; } if (invoiceLink) { invoiceLink.hidden = !isPaid || !order.invoiceUrl; if (isPaid && order.invoiceUrl) { invoiceLink.href = order.invoiceUrl; invoiceLink.setAttribute("download", `invoice-${order.id || orderId}.html`); } } document.title = isPaid ? `Order Complete - ${order.id || orderId} | Health Care Startup` : "Payment Processing | Health Care Startup"; hydrateIcons(); }; const fetchOrderSummary = async () => { const response = await fetch(`/api/billing/orders/${encodeURIComponent(orderId)}`, { cache: "no-store" }); const data = await response.json().catch(() => ({})); if (!response.ok) { const error = new Error(data.error || `Request failed with HTTP ${response.status}`); error.status = response.status; throw error; } return data.order || data; }; const loadCompleteOrder = async () => { if (!orderId) { setCompleteState("error", "Order not found.", "The completion link is missing an order number.", "Missing order"); setStatusPill("Needs order number", "circle-alert"); hydrateIcons(); return; } if (returnStatus === "cancelled") { setCompleteState("error", "Checkout cancelled.", "No payment was taken. Your selected package is still available.", "Checkout update"); setStatusPill("Cancelled", "circle-alert"); hydrateIcons(); return; } setCompleteState("loading", "Confirming your payment.", "Please wait while your order is verified.", "Secure checkout"); setStatusPill("Verifying", "loader-circle"); try { const order = sessionId && returnStatus === "success" ? await postJson("/api/billing/confirm", { orderId, sessionId }) : await fetchOrderSummary(); renderCompleteOrder(order.order || order); } catch (error) { setCompleteState( "error", "Payment received.", error.status === 409 ? "Your checkout return was received, but payment verification is not available yet." : "Your checkout return was received, but the order details could not be loaded yet.", "Order received" ); setStatusPill("Received", "circle-alert"); if (orderIdTarget) orderIdTarget.textContent = orderId; hydrateIcons(); } }; loadCompleteOrder(); } const billingPage = document.querySelector("[data-billing-page]"); if (billingPage) { const params = new URLSearchParams(window.location.search); const isCheckoutReturn = Boolean(params.get("status") || params.get("order") || params.get("session_id")); const selectedState = params.get("state") || ""; let cartProductIds = getBillingProductIds(params); const itemsTarget = billingPage.querySelector("[data-billing-items]"); const priceTarget = billingPage.querySelector("[data-billing-price]"); const stateTarget = billingPage.querySelector("[data-billing-state-label]"); const changeLink = billingPage.querySelector("[data-billing-change-link]"); const stateSelect = billingPage.querySelector("[data-billing-state]"); const addProductForm = billingPage.querySelector("[data-add-product-form]"); const addProductSelect = billingPage.querySelector("[data-add-product-select]"); const addProductButton = addProductForm?.querySelector("button"); const addProductHelp = billingPage.querySelector("[data-add-product-help]"); const countTarget = billingPage.querySelector("[data-billing-count]"); const billingForm = billingPage.querySelector("[data-billing-form]"); const billingStatus = billingPage.querySelector("[data-billing-status]"); const billingSubmit = billingPage.querySelector("[data-billing-submit]"); const paymentOptionTitle = billingPage.querySelector("[data-payment-option-title]"); const paymentOptionDescription = billingPage.querySelector("[data-payment-option-description]"); const paymentCardLogos = billingPage.querySelector("[data-payment-card-logos]"); let orderFieldsTarget = billingForm?.querySelector("[data-order-fields]") || null; let stripeCheckoutAvailable = true; const showBillingNotice = (title, message, type = "success") => { if (!billingStatus) return; billingStatus.innerHTML = ` <span class="notice-icon"><i data-lucide="${type === "error" ? "circle-alert" : "badge-check"}"></i></span> <span class="notice-copy"> <strong>${escapeHtml(title)}</strong> <span>${escapeHtml(message)}</span> </span> `; billingStatus.classList.add("is-visible", "billing-notice"); billingStatus.classList.toggle("is-error", type === "error"); billingStatus.classList.toggle("is-success", type === "success"); hydrateIcons(); }; const confirmReturnedPayment = async () => { const orderId = params.get("order") || ""; const sessionId = params.get("session_id") || ""; if (!orderId || !sessionId) return; try { const data = await postJson("/api/billing/confirm", { orderId, sessionId }); if (data.paymentStatus === "paid") { showBillingNotice("Payment verified", `Thank you. Your order ${orderId} is confirmed.`); window.location.replace(buildOrderCompleteUrl(orderId, sessionId, "success")); return; } showBillingNotice( "Payment processing", "Your order status will update when confirmation is available." ); } catch (error) { showBillingNotice( "Payment received", "Checkout returned successfully. If this page does not update automatically, our team can verify the order from the admin panel." ); } }; const setPaymentOptionMode = (stripeReady) => { stripeCheckoutAvailable = stripeReady; if (paymentOptionTitle) { paymentOptionTitle.textContent = stripeReady ? "Secure card checkout" : "Manual billing request"; } if (paymentOptionDescription) { paymentOptionDescription.textContent = stripeReady ? "Pay safely with encrypted checkout. Your card details stay protected." : "Submit your billing details and our team will follow up with payment instructions."; } if (paymentCardLogos) paymentCardLogos.hidden = !stripeReady; if (billingSubmit) billingSubmit.textContent = stripeReady ? "Continue to secure checkout" : "Send billing request"; }; const loadPaymentOptions = async () => { try { const response = await fetch("/api/billing/options", { cache: "no-store" }); if (!response.ok) return; const options = await response.json(); setPaymentOptionMode(options.stripeReady !== false); } catch (error) { // Keep the default secure-checkout copy if the server settings cannot be loaded. } }; loadPaymentOptions(); if (billingStatus && params.get("status") === "success") { showBillingNotice( "Verifying payment", `Thank you. Your order${params.get("order") ? ` ${params.get("order")}` : ""} was received from Stripe. Confirming payment now.` ); confirmReturnedPayment(); } else if (billingStatus && params.get("status") === "cancelled") { showBillingNotice("Checkout cancelled", "No payment was taken. Your cart is still ready when you want to try again.", "error"); } if (billingForm && !orderFieldsTarget) { orderFieldsTarget = document.createElement("div"); orderFieldsTarget.hidden = true; orderFieldsTarget.setAttribute("data-order-fields", ""); billingForm.append(orderFieldsTarget); } const getCurrentState = () => (stateSelect ? stateSelect.value : selectedState); const syncBillingUrl = () => { const nextUrl = buildBillingUrl(cartProductIds, getCurrentState()); window.history.replaceState({}, "", nextUrl); }; const updateBillingTitle = () => { document.title = `Billing - ${cartProductIds.length} Product${cartProductIds.length === 1 ? "" : "s"} | Health Care Startup`; }; const syncBillingFormFields = (cartProducts, total) => { if (!orderFieldsTarget) return; const fields = { "order-products": cartProductIds.join(","), "order-product-names": cartProducts.map((item) => item.title).join(" | "), "order-total": formatMoney(total), }; orderFieldsTarget.innerHTML = ""; Object.entries(fields).forEach(([name, value]) => { const input = document.createElement("input"); input.type = "hidden"; input.name = name; input.value = value; orderFieldsTarget.append(input); }); }; const renderAddProductOptions = () => { if (!addProductSelect) return; const availableProducts = productList.filter((item) => !cartProductIds.includes(item.id)); addProductSelect.innerHTML = `<option value="">${availableProducts.length ? "Choose a product to add" : "All products added"}</option>`; availableProducts.forEach((item) => { const option = document.createElement("option"); option.value = item.id; option.textContent = `${item.title} - ${item.price}`; addProductSelect.append(option); }); addProductSelect.disabled = !availableProducts.length; if (addProductButton) { addProductButton.disabled = !availableProducts.length; } if (addProductHelp) { addProductHelp.textContent = availableProducts.length ? `${availableProducts.length} more product${availableProducts.length === 1 ? "" : "s"} available` : "All available products are already in this order"; } }; const renderBillingItems = () => { const cartProducts = cartProductIds.map(getProductById); const total = cartProducts.reduce((sum, item) => sum + getProductPriceValue(item), 0); const canRemove = cartProductIds.length > 1; if (itemsTarget) { itemsTarget.innerHTML = ""; cartProducts.forEach((item) => { const listItem = document.createElement("li"); listItem.className = "order-item"; listItem.innerHTML = ` <span> <strong>${item.title}</strong> <small>${item.price}</small> </span> <button class="order-item-remove" type="button" data-remove-product="${item.id}" aria-label="Remove ${item.title}" ${canRemove ? "" : "disabled"}> <i data-lucide="x"></i> </button> `; itemsTarget.append(listItem); }); itemsTarget.querySelectorAll("[data-remove-product]").forEach((button) => { button.addEventListener("click", () => { if (cartProductIds.length <= 1) return; const productId = button.getAttribute("data-remove-product") || ""; cartProductIds = cartProductIds.filter((item) => item !== productId); renderBillingItems(); updateBillingStateText(); syncBillingUrl(); }); }); } if (priceTarget) priceTarget.textContent = formatMoney(total); if (countTarget) { countTarget.textContent = `${cartProductIds.length} product${cartProductIds.length === 1 ? "" : "s"} selected`; } syncBillingFormFields(cartProducts, total); renderAddProductOptions(); updateBillingTitle(); hydrateIcons(); }; const updateBillingStateText = () => { const state = getCurrentState(); if (stateTarget) { stateTarget.textContent = state ? `Agency state: ${state}` : "Agency state: Not selected"; } if (changeLink) { changeLink.href = "coaching-programs.html"; } }; renderAgencyStateOptions(stateSelect, selectedState); renderBillingItems(); updateBillingStateText(); if (!isCheckoutReturn) syncBillingUrl(); stateSelect?.addEventListener("change", () => { updateBillingStateText(); syncBillingUrl(); }); if (addProductForm && addProductSelect) { addProductForm.addEventListener("submit", (event) => { event.preventDefault(); const productId = addProductSelect.value; if (!productId || cartProductIds.includes(productId)) return; cartProductIds = [...cartProductIds, productId]; addProductSelect.value = ""; renderBillingItems(); updateBillingStateText(); syncBillingUrl(); }); } if (billingForm && billingStatus) { billingForm.addEventListener("submit", async (event) => { event.preventDefault(); if (!billingForm.checkValidity()) { billingForm.reportValidity(); return; } const state = getCurrentState() || "your selected state"; const itemCount = cartProductIds.length; const cartProducts = cartProductIds.map(getProductById); const total = cartProductIds.reduce((sum, productId) => sum + getProductPriceValue(getProductById(productId)), 0); const payload = { ...Object.fromEntries(new FormData(billingForm).entries()), firstName: billingForm.elements["first-name"]?.value || "", lastName: billingForm.elements["last-name"]?.value || "", state, productIds: cartProductIds, productNames: cartProducts.map((item) => item.title), orderTotal: formatMoney(total), orderSummary: `${itemCount} product${itemCount === 1 ? "" : "s"} totaling ${formatMoney(total)} in ${state}`, }; showFormStatus(billingStatus, stripeCheckoutAvailable ? "Creating your secure checkout..." : "Sending your billing request..."); try { const data = await postJson("/api/billing", payload); if (data.checkoutUrl) { showFormStatus(billingStatus, "Redirecting to Stripe checkout..."); window.location.href = data.checkoutUrl; return; } const successMessage = data.stripeFallback ? data.message || `Thank you. Your order for ${itemCount} product${ itemCount === 1 ? "" : "s" } totaling ${formatMoney(total)} in ${state} was received. Our team will follow up with secure payment instructions.` : `Thank you. Your order for ${itemCount} product${itemCount === 1 ? "" : "s"} totaling ${formatMoney(total)} in ${state} was sent to our team.`; showFormStatus(billingStatus, successMessage); if (data.stripeFallback) setPaymentOptionMode(false); billingForm.reset(); renderAgencyStateOptions(stateSelect, state); updateBillingStateText(); } catch (error) { showFormStatus(billingStatus, error.message || "Checkout could not be started. Please try again.", "error"); } }); } } const setupFilterControls = ({ controlSelector, itemSelector, controlAttribute, itemAttribute }) => { const controls = document.querySelectorAll(controlSelector); const items = document.querySelectorAll(itemSelector); if (!controls.length || !items.length) return; const applyFilter = (filter) => { items.forEach((item) => { const values = (item.getAttribute(itemAttribute) || "").split(/\s+/).filter(Boolean); const isMatch = filter === "all" || values.includes(filter); window.clearTimeout(item.filterTimer); if (isMatch) { item.hidden = false; item.removeAttribute("aria-hidden"); window.requestAnimationFrame(() => item.classList.remove("is-filtered-out")); } else { item.classList.add("is-filtered-out"); item.setAttribute("aria-hidden", "true"); item.filterTimer = window.setTimeout(() => { if (item.classList.contains("is-filtered-out")) item.hidden = true; }, 220); } }); }; controls.forEach((control) => { control.addEventListener("click", () => { const filter = control.getAttribute(controlAttribute) || "all"; controls.forEach((button) => { button.setAttribute("aria-pressed", String(button === control)); }); applyFilter(filter); }); }); }; setupFilterControls({ controlSelector: "[data-service-filter]", itemSelector: "[data-service-states]", controlAttribute: "data-service-filter", itemAttribute: "data-service-states", }); setupFilterControls({ controlSelector: "[data-review-filter]", itemSelector: "[data-review-type]", controlAttribute: "data-review-filter", itemAttribute: "data-review-type", }); document.querySelectorAll("[data-advanced-tabs]").forEach((group, groupIndex) => { const buttons = Array.from(group.querySelectorAll("[data-advanced-tab]")); const panels = Array.from(group.querySelectorAll("[data-advanced-panel]")); if (!buttons.length || !panels.length) return; const activateTab = (target) => { buttons.forEach((button) => { const isSelected = button.getAttribute("data-advanced-tab") === target; button.setAttribute("aria-selected", String(isSelected)); button.tabIndex = isSelected ? 0 : -1; }); panels.forEach((panel) => { const isSelected = panel.getAttribute("data-advanced-panel") === target; panel.hidden = !isSelected; panel.classList.toggle("is-active", isSelected); }); }; buttons.forEach((button, buttonIndex) => { const target = button.getAttribute("data-advanced-tab") || ""; const panel = panels.find((item) => item.getAttribute("data-advanced-panel") === target); button.id ||= `advanced-tab-${groupIndex}-${buttonIndex}`; button.setAttribute("role", "tab"); if (panel) { panel.id ||= `advanced-panel-${groupIndex}-${buttonIndex}`; panel.setAttribute("role", "tabpanel"); panel.setAttribute("aria-labelledby", button.id); button.setAttribute("aria-controls", panel.id); } button.addEventListener("click", () => activateTab(target)); button.addEventListener("keydown", (event) => { const direction = event.key === "ArrowRight" ? 1 : event.key === "ArrowLeft" ? -1 : 0; if (!direction) return; event.preventDefault(); const nextIndex = (buttonIndex + direction + buttons.length) % buttons.length; buttons[nextIndex].focus(); activateTab(buttons[nextIndex].getAttribute("data-advanced-tab") || ""); }); }); const initialTab = buttons.find((button) => button.getAttribute("aria-selected") === "true")?.getAttribute("data-advanced-tab") || buttons[0].getAttribute("data-advanced-tab") || ""; activateTab(initialTab); }); if (roadmapSteps.length) { roadmapSteps.forEach((step, activeIndex) => { step.addEventListener("click", () => { roadmapSteps.forEach((item, itemIndex) => { item.classList.toggle("is-active", itemIndex === activeIndex); item.classList.toggle("is-complete", itemIndex < activeIndex); if (itemIndex === activeIndex) { item.setAttribute("aria-current", "step"); } else { item.removeAttribute("aria-current"); } }); if (roadmapProgress) { roadmapProgress.textContent = `${Math.round(((activeIndex + 1) / roadmapSteps.length) * 100)}%`; } }); }); } const revealSelectors = [ ".section-reveal", ".home-service-card", ".testimonial-card", ".page-card", ".product-card", ".process-card", ".program-card", ".manual-panel", ".training-card", ".launch-service-card", ".blog-card", ".contact-item", ".contact-form-card", ".blog-article-card", ".article-sidebar-card", ".product-detail-card", ".purchase-panel", ".order-summary-card", ".advanced-console", ".advanced-panel", ".page-visual", ".image-feature", ".media-frame", ".hero-proof-grid > *", ".feature-rail > *", ".console-metrics > *", ".contact-priority-grid > *", ".footer-copy", ".footer-column", ].join(","); const revealTargets = Array.from(new Set(document.querySelectorAll(revealSelectors))); document .querySelectorAll( [ ".home-service-grid", ".testimonials-grid", ".page-card-grid", ".product-grid", ".process-grid", ".program-grid", ".training-grid", ".launch-service-grid", ".blog-grid", ".related-blog-grid", ".product-detail-shell", ".blog-detail-shell", ".billing-layout", ".manual-layout", ".card-grid", ".hero-proof-grid", ".feature-rail", ".console-metrics", ".contact-priority-grid", ".footer-grid", ].join(",") ) .forEach((group) => { Array.from(group.children).forEach((item, itemIndex) => { item.style.setProperty("--reveal-delay", `${Math.min(itemIndex * 70, 420)}ms`); }); }); revealTargets.forEach((target, targetIndex) => { target.classList.add("reveal-item"); if (!target.style.getPropertyValue("--reveal-delay")) { target.style.setProperty("--reveal-delay", `${Math.min((targetIndex % 6) * 50, 300)}ms`); } }); if ("IntersectionObserver" in window) { const observer = new IntersectionObserver( (entries) => { entries.forEach((entry) => { if (entry.isIntersecting) { entry.target.classList.add("is-visible"); observer.unobserve(entry.target); } }); }, { threshold: 0.12 } ); revealTargets.forEach((target) => observer.observe(target)); } else { revealTargets.forEach((target) => target.classList.add("is-visible")); } initSiteAssistant(); applySiteEdits(); if (siteEdits.length && "MutationObserver" in window && document.body) { const siteEditObserver = new MutationObserver(() => { if (!isApplyingSiteEdits) scheduleSiteEdits(); }); siteEditObserver.observe(document.body, { childList: true, subtree: true, characterData: true, }); } window.addEventListener("load", () => { applySiteEdits(); hydrateIcons(); }); hydrateIcons();
Close