You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Generated by a multi-agent audit (8 dimensional finders + 3 loop rounds + adversarial verification). 111 findings confirmed real: 35 high / 52 medium / 24 low.
The agents compared the new static-CF-Pages site at beta.ontargetaba.com against the legacy WordPress site at ontargetaba.com and against best practices for a healthcare lead-gen site.
Categories: ARIA & Semantics, About / Mission Content, Accessibility, Accessibility & Image Optimization, Accessibility & Semantics, Accessibility - Keyboard Navigation, Admin / Setup, Analytics / Tracking, Blog Indexing, Blog SEO, Case Studies / Success Stories, Confirmation Page Messaging, Contact Information, Cookie Consent & Tracking, Domain & Redirects, Domain Hardcoding, Error Handling & User Experience, Form Accessibility, Form Accessibility & Functionality, Form Functionality, Form Functionality & Submission, Form Functionality & UX, HTTP Headers & Performance, Heading Hierarchy, Healthcare Data & HIPAA, Image Accessibility, Image Optimization & Performance, Internal Linking, Internal Linking & Cross-Navigation, Jotform Iframe Security, Keyboard Navigation, Legal & Compliance, Link Targeting & SEO, Location-Specific Content, Meta Tags & Descriptions, Mobile Navigation, Mobile UX - Form Padding on Narrow Screens, Mobile UX - Safe Area Insets, Mobile UX - Touch Target Sizing, Performance, Performance & Optimization, Performance & Third-Party Integration, Prefers Reduced Motion, Pricing & Insurance Transparency, Privacy & Compliance, Privacy & Data Disclosure, Progressive Web App, SEO & Domain References, SEO & Indexability, SEO & Meta Tags, SEO & Metadata, Security, Security & Browser Integration, Security & Cross-Origin, Security & Cross-Window Vulnerability, Security Headers, Sitemap & Crawlability, Special Programs / Initiatives, State Privacy Laws, Sticky CTA & Mobile Engagement, Structured Data & SEO, Team & Staff Bios, Text Alternatives, Third-Party Data Processors, Third-Party Integration, Title Length & Uniqueness, Trust Signals - Credentials Display, Video Content, Web Standards, Web Standards & Browser Integration, Web Standards & Progressive Enhancement.
ARIA & Semantics
FAQ accordion buttons use aria-expanded but lack aria-controls linking to expanded content
Severity: low
FAQ items (app.js lines 77-84, faqs.html line 537+) toggle aria-expanded on .faq-q buttons, but there is no aria-controls attribute connecting the button to the collapsible .faq-a div. This makes it harder for screen reader users to understand which content is controlled by which button.
Fix: Add unique IDs to each .faq-a div (e.g., 'faq-answer-1') and add aria-controls="faq-answer-1" to the corresponding .faq-q button. Alternatively, wrap the Q and A in a
/ element (semantic collapse).
Marquee review carousel uses aria-label but hides duplicate cards with aria-hidden without proper structure
Severity: low
The review marquee (index.html lines 878-1431) includes two passes of review cards for seamless looping. The second pass is marked aria-hidden="true" (line 1035), but the markup is duplicated rather than referenced. Screen readers will announce the aria-label="Parent reviews" on the container, but the structure is confusing. No alternative is provided for users who cannot see the animation.
Fix: Keep the aria-hidden structure for decorative duplicates, but add aria-live="polite" to the marquee container or consider using a static list as a screen-reader-only alternative (display: none for sighted users) that lists all review cards in a logical order.
About / Mission Content
Missing Ilana Gross clinical philosophy and detailed background
Severity: low
Where:/about.html has basic bio in schema and inline text; limited narrative depth.
The about.html page mentions Ilana's credentials and education briefly but lacks her personal story, clinical philosophy, why she started the company, or what sets her approach apart. Biographical depth builds trust and differentiation.
Fix: Expand Ilana's bio with: personal story of why she started On Target ABA, her clinical philosophy ('I believe...'), specific case or insight that shaped her approach, what makes her methodology unique, and how she mentors her team. Can be a short article or video.
Accessibility
Job application form also lacks label associations
Severity: high
Where:C:/Users/nates/Downloads/Claude/Utah Autism Testing/website/job-application.html
Similar to employment-application.html, the job-application.html form has the same label/input accessibility issue where form fields lack proper id-for associations.
Fix: Apply the same fix as employment application: add unique id attributes to all form inputs and corresponding 'for' attributes to labels.
Severity: medium
Where:C:\Users\nates\Downloads\Claude\Utah Autism Testing\website\index.html lines 632, 1504-1517 and similar across other pages
Images throughout the site (particularly the hero image jessica-rockowitz and insurance logos) lack explicit width and height attributes. This causes Cumulative Layout Shift (CLS) as the browser must download the image to determine its dimensions, leading to delayed paint and layout recalculation. The hero image (366 KB) is particularly critical.
Fix: Add width and height attributes to all img elements matching their natural dimensions or aspect-ratio containers. Example: or use CSS aspect-ratio with inline container sizing.
Accessibility & Semantics
Leadbot widget uses role='button' with tabindex='0' instead of native button elements
Severity: medium
Where:C:\Users\nates\Downloads\Claude\Utah Autism Testing\website\assets\js\leadbot.js:49-51
The leadbot widget (leadbot.js lines 49, 51) creates div elements with role='button' and tabindex='0' for the bubble and close button. While there is also a proper button element (line 53), the div-based buttons lack keyboard event handling and proper semantics, creating inconsistent keyboard navigation.
Fix: Replace div role='button' elements with proper elements: ...
Accessibility - Keyboard Navigation
FAQ accordion buttons lack aria-controls linking to expanded content
Severity: medium
FAQ accordion buttons (faq-q) have aria-expanded="false" but no aria-controls attribute linking to the associated content panel. WCAG best practice requires aria-controls to create explicit semantic relationship between button and content. This affects assistive technology navigation.
Fix: Add aria-controls="faq-answer-1" (or similar) to each button, and matching id="faq-answer-1" to the corresponding .faq-a div.
Admin / Setup
Head-scripts admin page is ready but unconfigured
Severity: high
Where:admin/head-scripts.html lines 93-104 (quick reference section)
The admin infrastructure exists (admin/head-scripts.html and /api/head-scripts) and includes helpful documentation about the legacy tracking, but assets/data/head-scripts.json is completely empty. The page is production-ready; it just needs tracking snippets pasted in.
Fix: Go to https://beta.ontargetaba.com/admin/head-scripts.html and follow the steps above to paste in GA4, Ads, Facebook, Hotjar, and WhatConverts snippets. The admin page already lists the legacy IDs with clear guidance. It's the quickest path to parity.
Analytics / Tracking
Google Analytics 4 not installed
Severity: high
Where:assets/data/head-scripts.json (empty); no gtag.js snippet loaded on any page
The legacy WordPress site tracks with two GA4 properties (G-PHLC29WGVN and G-D3RBZZ5WPL), but the new site has zero GA4 implementation. This is a critical gap for user behavior analytics, traffic sources, conversion tracking, and goal measurement.
Paste the following into the HTML content textarea:
<script async src="https://www.googletagmanager.com/gtag/js?id=G-PHLC29WGVN"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-PHLC29WGVN');
gtag('config', 'G-D3RBZZ5WPL');
</script>
Click 'Save & open PR'
Merge the PR to deploy
Note: Both GA4 measurement IDs are configured in a single gtag initialization (second config call adds the second property). This script will be injected into the of every public page by the build process (optimize-pages.py).
Google Ads conversion tracking not installed
Severity: high
Where:assets/data/head-scripts.json (empty)
The legacy site has Google Ads conversion tag AW-354317910 for tracking lead conversions and ad ROI. The new site is missing this entirely, meaning paid ad performance cannot be measured.
Alternatively, if you want a single gtag.js snippet handling both GA4 and Ads, add both config calls in the second <script> block:
gtag('config', 'G-PHLC29WGVN');
gtag('config', 'G-D3RBZZ5WPL');
gtag('config', 'AW-354317910');
Then configure conversion goals (e.g., 'Contact form submitted', 'Schedule consultn') in Google Ads Manager.
Facebook Pixel not installed
Severity: high
Where:assets/data/head-scripts.json (empty)
The legacy WordPress site uses PixelYourSite plugin with a Facebook Pixel for retargeting, lookalike audiences, and conversion tracking. The new site has no Facebook Pixel implementation, losing audience data and ad attribution.
Replace YOUR_PIXEL_ID with the actual ID (typically 10-15 digits)
Click 'Save & open PR'
Verify in Facebook Pixel Helper browser extension after merge
Note: The legacy site may have exposed the Pixel ID in the _fbp cookie or PixelYourSite WP admin settings.
Hotjar analytics not installed
Severity: medium
Where:assets/data/head-scripts.json (empty)
The legacy site uses Hotjar (site ID 5016360) for session recording, heatmaps, and form analytics. The new site is missing Hotjar, eliminating visibility into user behavior patterns, drop-off points, and form field friction.
The site ID (5016360) is already embedded in the script
Click 'Save & open PR'
Merge and verify in Hotjar dashboard
Hotjar will then begin recording sessions and generating heatmaps on all public pages.
WhatConverts call tracking not installed
Severity: medium
Where:assets/data/head-scripts.json (empty)
The legacy site uses WhatConverts (account 159348) for inbound call tracking, phone number attribution, and call recording. The new site has no call-tracking implementation, losing attribution data for phone leads and making it impossible to correlate ad spend to actual calls.
Verify in WhatConverts dashboard that calls are being logged
Note: You may also need to replace the display phone number on the site with a WhatConverts-generated tracking number if you want call attribution. Check WhatConverts settings for the recommended phone number to display.
Leadbot triage conversions not tracked in GA4
Severity: medium
Where:assets/js/leadbot.js (lines 286-283, finish() function); no gtag() event calls
The in-repo leadbot.js widget (4-step triage) successfully routes leads to Jotforms, but form completion events are not being fired to Google Analytics. This means GA4 cannot track conversion funnels (intent → location → age → contact form).
Fix: Modify assets/js/leadbot.js to fire gtag conversion events:
'generate_lead' event when the user completes the triage (maps to GA4 conversion)
'view_form' event with the Jotform ID for funnel analysis
Then in Google Analytics 4, mark 'generate_lead' as a conversion event in Admin > Events > Mark as conversion
Leadbot will now report its own conversion rate separately from Jotform submissions.
Alternatively, wait for Jotform-level form submission tracking (see separate finding below).
Jotform submission tracking not wired to GA4
Severity: medium
Where:All form pages: contact.html, pre-intake-form.html, autism-testing.html; assets/js/app.js has no form tracking hooks
Three Jotforms are embedded (autism testing, pre-intake, contact) but there is no JavaScript event listener to fire GA4 conversion events when forms submit. Form completions go unmeasured in analytics.
Fix: Add Jotform submission tracking to assets/js/app.js (after line 274 where the main IIFE closes):
// ---------- Jotform conversion tracking ----------
if (window.JotForm) {
JotForm.onSubmit(function(errors) {
if (errors.length > 0) return false;
if (window.gtag) {
// Determine form type from the page
const path = window.location.pathname;
let formType = 'contact';
if (path.includes('autism-testing')) formType = 'autism_testing';
else if (path.includes('pre-intake')) formType = 'intake_form';
Admin > Events > Mark 'form_submission' as a conversion
Create an event filter to split by 'event_label' (contact, intake_form, autism_testing)
Form submissions will now appear as conversions and funnels
Note: Jotform also has native GA4 integration. Check Jotform Form Settings > Integrations > Google Analytics to see if you can enable it there instead.
No UTM parameter tracking or source attribution in leadbot
Severity: low
Where:assets/js/leadbot.js, line 247-254 (params object in finish() function)
When leadbot routes leads to Jotforms, it includes 'source=leadbot' but does not capture or pass UTM parameters from the current URL. If visitors arrive via campaigns (utm_source, utm_medium, utm_campaign), that attribution is lost.
Fix: Enhance the params object in leadbot.js to capture UTM parameters:
const params = {
name: state.name,
phone: state.phone,
email: state.email,
age: state.age,
region: state.region,
source: 'leadbot',
utm_source: (new URL(location).searchParams.get('utm_source') || ''),
utm_medium: (new URL(location).searchParams.get('utm_medium') || ''),
utm_campaign: (new URL(location).searchParams.get('utm_campaign') || ''),
utm_content: (new URL(location).searchParams.get('utm_content') || ''),
};
Then in Jotform form settings, map these fields to custom form fields (utm_source, utm_medium, etc.) so they are captured alongside the lead data. This enables attribution in your CRM or Jotform reports.
Blog Indexing
No hreflang tags for alternate language or regional variants
Severity: low
The site serves only English content (en-US) for two geographic regions (Utah and Ohio). There are no hreflang links pointing to alternate language or region-specific versions. While this is not a critical issue for a single-language site, if the business plans to expand to Spanish or other languages, the lack of hreflang structure will make future localization more difficult.
Fix: If no multilingual expansion is planned, this is not urgent. If future expansion is planned, add hreflang tags now: etc. For now, add hreflang='en-us' self-reference on each page for clarity.
Blog SEO
Blog post dynamic SEO fails static crawler analysis
Severity: high
The blog/post.html canonical link is initially set to 'https://ontargetaba.com/blogs/' (line 9) with ID 'seo-canonical', then updated at runtime (line 378) to meta.source_url. Search crawlers that do not execute JavaScript will see a generic /blogs/ canonical instead of the specific post URL, causing crawlers to treat individual posts as duplicates of the blog index. This is particularly problematic for SEO since each blog post should have a unique canonical pointing to itself.
Fix: Pre-render the canonical and core SEO meta tags for each blog post at build time. Either: (1) Generate a unique post.html per slug (static build), or (2) Use a Cloudflare Worker to inject the correct canonical/OG tags server-side before returning HTML to crawlers.
Blog post template lacks proper structured JSON-LD for article schema
Severity: medium
The blog/post.html dynamically injects BlogPosting JSON-LD at runtime via JavaScript (lines 382-399). However, this schema is injected AFTER page load via JavaScript into a script tag—search engine crawlers may not execute JavaScript before deciding to index. The BlogPosting schema includes hardcoded image fallback '/assets/images/footerImg.png' (relative path) instead of full absolute URL 'https://ontargetaba.com/assets/images/footerImg.png'. Additionally, datePublished and dateModified are both set to meta.date (line 387-388), providing no indication of actual update frequency.
Fix: Move the BlogPosting JSON-LD generation to a server-side rendering step or pre-render it as static text in the HTML head before closing tag (not via JavaScript). Use absolute image URLs. Ensure dateModified is set to the actual last-modified date from the blog post metadata, not the original publish date.
Blog post og:image uses relative URL instead of absolute
Severity: medium
Where:blog/post.html, line 14
The og:image meta tag contains '/assets/images/footerImg.png' (relative path). Social platforms require absolute URLs with domain for proper preview rendering. The fallback image is also relative, breaking Open Graph previews when blog posts are shared.
Blog post template missing article-specific OpenGraph and Twitter Card tags
Severity: medium
Where:blog/post.html lines 10-18
Blog post template (blog/post.html) lacks article:published_time, article:modified_time, article:author, and article:section OpenGraph tags, and missing proper Twitter Card image dimensions.
Fix: Add meta tags to blog/post.html: , , , ,
Case Studies / Success Stories
No published case studies or family transformation stories
Severity: medium
Where:New site has brief testimonials on home and service pages; no case study or success story pages found.
The site contains only testimonial quotes (5-star reviews) but no detailed case studies showing before/after improvements, specific behavioral goals met, or family journeys. Similar services use case studies to build confidence and demonstrate outcomes beyond generic testimonials.
Fix: Create a case-study or success-stories page (anonymized or with consent) showing: child's starting point → specific goals → measurable progress → parent impact. Example: 'From Non-Verbal to Sentence Construction' or 'Building Independence: 6-Month Milestone.' Can be simple cards or narrative format.
Confirmation Page Messaging
Minimal next-steps guidance on thank-you confirmation pages
Severity: low
Where:/thank-you.html and /thank-you-confirmation.html have minimal post-submission guidance.
Both /thank-you.html and /thank-you-confirmation.html offer basic 'we'll be in touch' copy plus links to pre-intake form and ABA therapy guide. Missing: (1) clear timeline for when they'll hear back, (2) what to prepare for first call, (3) insurance questions to have ready, (4) location-specific next steps.
Fix: Expand thank-you pages with: 'We'll call you within 24 business hours' + 'Have your insurance info handy' + 'Questions to ask during your first call' + location-specific info (which clinic will contact them). Also consider a location-detection feature to show region-specific messaging.
Contact Information
Privacy contact email is placeholder [email protected]
Severity: low
The privacy policy and cookie consent page list [email protected] as the contact for privacy/data subject requests. This is likely a placeholder and should be a real, monitored email address with clear response SLA (GDPR requires 30 days; CCPA requires 45 days).
Fix: Replace [email protected] with a real, monitored privacy@ontargetaba.com or legal@ontargetaba.com address. Include a statement that data subject requests will be responded to within 30-45 days (depending on jurisdiction) and that requestors may be asked to verify identity. Document internally who monitors this email and the process for handling requests.
Cookie Consent & Tracking
No active cookie consent banner or consent manager on public pages
Severity: high
The privacy policy and cookie-consent.html reference a 'cookie consent banner' and 'Manage preferences' buttons, but there is no functional cookie banner or consent management system deployed on the public site. The app.js contains no cookie consent/banner logic. While head-scripts.json is currently empty, when analytics/tracking scripts are added (Google Analytics IDs are documented as G-PHLC29WGVN and G-D3RBZZ5WPL, Google Ads AW-354317910, Hotjar 5016360, WhatConverts 159348, Facebook Pixel), they would fire immediately without user consent, violating GDPR, CCPA, and state privacy law requirements.
Fix: Implement a consent management platform (CMP) such as Osano, OneTrust, or TrustBox that blocks tracking scripts until consent is given. The banner must load before any non-essential scripts. Ensure consent persists and that users can withdraw consent at any time. Make the 'Manage preferences' button on cookie-consent.html functional.
Domain & Redirects
robots.txt still points to old WordPress domain
Severity: high
The robots.txt file contains a hardcoded reference to https://ontargetaba.com/sitemap.xml. During the production cut-over from beta.ontargetaba.com to ontargetaba.com, this needs verification, but more critically: there is NO redirect mapping from old WordPress URLs (e.g., /aba-therapy-murray-utah/, /on-target-aba-autism-testing-autism-evaluations/) to the new clean URLs. The _redirects file only handles blog post routing, not the legacy WordPress to new URL migration.
Fix: Create a comprehensive _redirects file entry mapping all old WordPress URL patterns to new canonical URLs. At minimum: /aba-therapy-murray-utah/ → /murray-utah/, /on-target-aba-autism-testing-autism-evaluations/ → /autism-testing/, and similar for all major service/location pages. Document the full redirect map and validate 301 status codes post-deployment.
Domain Hardcoding
inject-seo.py hardcodes production domain in SEO script generation
Severity: high
Line 18 of inject-seo.py defines SITE = 'https://ontargetaba.com'. This variable is used throughout the script to inject canonical links, OG URLs, and all JSON-LD schema URLs into generated HTML files (run at build time). If the build is run on the beta.ontargetaba.com branch before the production cutover, all URLs will be hard-coded to the production domain, but the site is still on beta. This creates a mismatch: canonical tags point to ontargetaba.com, but the actual site is at beta.ontargetaba.com, causing duplicate content issues.
Fix: Make SITE domain configurable: read from environment variable (e.g., SITE_DOMAIN=https://beta.ontargetaba.com python inject-seo.py) or from a config file that changes per environment. Alternatively, use protocol-relative URLs (//ontargetaba.com) with dynamic scheme injection, or generate canonical/og:url tags that respect the request host header at serve time via a Cloudflare Worker.
Error Handling & User Experience
Missing 404 error page
Severity: medium
There is no 404.html file in the website directory. When users visit a non-existent page (e.g., /missing-page), they see a generic Cloudflare Pages default error page instead of a branded, helpful error page with navigation back to key pages.
Fix: Create 404.html in the website root with a branded error page that includes links to home, blog, contact, and other key pages. Optionally configure error page routing in Cloudflare Pages (if the Pages build system supports custom error pages).
Form Accessibility
Jotform embedded forms inherit no built-in form label association or validation announcements
Severity: medium
The contact page (contact.html line 491) embeds Jotform via external script. Jotform is a third-party SaaS widget outside your control. While Jotform does include WCAG support, the context around the form lacks descriptive text explaining what the form does, required fields, or expected input formats. No aria-label or role is applied to the wrapper.
Fix: Wrap the Jotform with a
element that includes: (1) A descriptive heading explaining the form's purpose, (2) Instructions for required fields, (3) An aria-label or aria-describedby on the wrapper div, (4) A note about any prefilled fields from leadbot routing.
Form Accessibility & Functionality
Employment and job application forms use onclick alert instead of proper submission
Severity: medium
Where:employment-application.html line 472, job-application.html line 474
Both employment-application.html and job-application.html use onsubmit with event.preventDefault() and alert() instead of proper form submission handlers with validation and server-side processing.
Fix: Replace alert-based submission with proper form submission to a backend endpoint or Jotform embed, with validation before submission.
Form Functionality
Insurance verification form has no action endpoint
Severity: high
Where:insurance.html line 661
The insurance verification form uses action="#" with no actual submission endpoint, preventing form data from being processed or sent anywhere.
Fix: Replace action="#" with a valid endpoint that processes the form data, e.g., action="/api/insurance" or point to a Jotform embed like other forms on the site.
Employment and job application forms use onclick alert instead of proper submission
Severity: medium
Where:C:/Users/nates/Downloads/Claude/Utah Autism Testing/website/employment-application.html, job-application.html
Both employment-application.html and job-application.html have forms with onsubmit='event.preventDefault(); alert(...); this.reset();' which is not a real form submission. This creates a poor user experience (alert boxes are jarring) and provides no backend processing or email confirmation.
Fix: Replace inline onsubmit handlers with either: (1) real form submission to a backend endpoint, (2) Jotform embed like contact.html, or (3) JavaScript form handler that shows a proper success message via modal or toast notification instead of alert().
Form Functionality & Submission
Insurance verification form has non-functional action and target attributes
Severity: high
The insurance verification form on insurance.html has action="#" with target="_blank", which prevents form submission. The form has a visible submit button labeled 'Verify my insurance' with an arrow icon, creating user expectation of functional form submission, but the form will not submit anywhere when clicked.
Fix: Either: (1) Remove target="_blank" and replace action="#" with a real endpoint (e.g., action="/api/insurance"), (2) Connect form submission to a Jotform embed like the other forms, or (3) Add form validation and preventDefault with clear UI feedback if intentionally placeholder. Currently it violates user expectations.
Form Functionality & UX
Insurance verification form has action='#' target='_blank' with no submission handler
Severity: medium
Where:C:\Users\nates\Downloads\Claude\Utah Autism Testing\website\insurance.html:661-710
The insurance verification form at insurance.html:661 has action='#' with target='_blank' but no onsubmit handler or submission endpoint. When users click 'Verify my insurance', the form attempts to submit to '#' in a new blank tab, creating a broken UX.
Fix: Either: (A) add proper form handler with preventDefault and API submission, (B) change to a link/button that navigates to a form page, or (C) integrate with Jotform like other pages
HTTP Headers & Performance
Missing Link headers for resource hints in _headers
Severity: low
Instead of adding preconnect links to every HTML page, Cloudflare Pages _headers can declare Link headers once for all pages. This reduces HTML bloat and centralizes third-party optimization. Currently using HTML-level preload only for fonts but missing HTTP-level preconnect directives.
Fix: Add Link header directives to _headers for critical domains: /* → Link: https://form.jotform.com; rel=preconnect; crossorigin, https://fonts.bunny.net; rel=preconnect (if applicable). This applies to all pages without modifying HTML.
Heading Hierarchy
Potential skipped heading levels in service pathway cards
Severity: low
In index.html lines 705-746, the service cards use h3 for card titles after an h2 'Our services' headline, which is correct. However, some pages may have inconsistent hierarchy. This is mostly fine, but audit pages like faqs.html where sections use h2 but subsections use h3 without clear structural relationship.
Fix: Verify heading hierarchy is logical throughout. Ensure h2 > h3 > h4 flows naturally and no levels are skipped (e.g., h2 > h4). Use the WAVE browser extension or Axe DevTools to flag hierarchy issues.
Healthcare Data & HIPAA
No HIPAA Business Associate Agreements (BAA) listed for third-party processors handling patient data
Severity: high
On Target ABA is a healthcare provider (autism testing + ABA therapy) handling protected health information (PHI). The privacy policy lists 'Jotform' as a form processor and mentions form submissions with patient intake data, but does not disclose whether Jotform or other third parties (e.g., Cloudflare, email providers) have signed BAAs. HIPAA requires written BAAs with all entities that may touch PHI. Additionally, there is no mention of HIPAA, HIPAA compliance, or security safeguards specific to healthcare data in the privacy policy or terms of service.
Fix: 1) Obtain and execute BAAs from Jotform, Cloudflare (if they handle data), email service providers, and any form processors. 2) Add a dedicated section to the privacy policy stating compliance with HIPAA, the Privacy Rule, Security Rule, and Breach Notification Rule. 3) Disclose which processors have BAAs and their data handling practices. 4) Ensure end-to-end encryption for forms collecting PHI (child name, age, diagnosis info, parent contact details).
Six insurance company logo images on index.html (lines 1512-1517) have empty alt attributes (alt=""). While these are decorative logos, screen readers still announce them as images. Decorative images should use alt="" combined with aria-hidden="true" or be marked as presentation-only.
Fix: Add aria-hidden='true' to decorative logo images or restructure so the img is not announced: or wrap in figure with aria-hidden.
Image Optimization & Performance
Images lack width and height attributes, causing layout shift
Severity: medium
All elements throughout the site are missing width and height attributes. While CSS classes define responsive sizing (w-full, h-auto), explicit width/height prevents Cumulative Layout Shift (CLS) during image load. This affects Core Web Vitals and user experience.
Fix: Add width and height attributes to all tags (e.g., ). Even with responsive CSS, these tell the browser the aspect ratio to reserve space before the image loads.
Internal Linking
Blog posts use relative OG image URLs instead of absolute
Severity: medium
In blog/post.html line 360, the OG image is set as '/assets/og/blog-{slug}.svg' (relative path). OpenGraph meta tags must use absolute URLs per specification. Social platforms (Facebook, Twitter, LinkedIn) need fully qualified URLs like 'https://ontargetaba.com/assets/og/blog-{slug}.svg' to properly fetch and cache images.
Limited internal cross-linking between related service pages
Severity: low
Where:Service pages lack 'Related' or 'Learn More' sections linking to FAQs, blog posts, or complementary services.
Service pages (center-based-aba-therapy.html, in-home-aba-therapy.html, etc.) have CTAs to contact but minimal 'related services' or 'you may also want to read' links. No cross-linking to related blog posts or guides that answer common follow-up questions.
Fix: Add 'Related Resources' or 'Common Questions' sections at bottom of each service page linking to: (1) relevant FAQ items, (2) related blog posts, (3) complementary services (e.g., center-based page links to in-home options), (4) location pages for that service.
Jotform Iframe Security
Jotform iframes lack sandbox attribute and integrity checks
Severity: medium
The autism-testing.html and other form pages load Jotform iframes via <script type="text/javascript" src="https://form.jotform.com/jsform/260534406459156"></script> without a sandbox attribute. This means the iframe has unrestricted access to the parent window (though Jotform is external, lack of explicit restrictions increases risk). There are no subresource integrity (SRI) checks on external scripts, so CDN compromise or man-in-the-middle attacks could inject malicious code. For forms collecting PHI (child diagnosis, parent contact), this is a compliance risk.
Fix: 1) If using Jotform iframes, add sandbox attributes: sandbox="allow-forms allow-scripts allow-same-origin" (restrict based on what Jotform actually needs). 2) Monitor Jotform's security practices; ensure they publish a SOC 2 report or similar. 3) For any external script src attributes, add integrity="sha384-..." hashes to detect tampering. 4) Consider self-hosting or using a HIPAA-compliant form builder (Typeform, Formstack) if PHI collection is frequent.
Keyboard Navigation
Leadbot widget bubble uses role='button' with tabindex='0' instead of native button elements
Severity: medium
In leadbot.js (lines 49-51), the greeting bubble and close button use '
' instead of native elements. While tabindex is present, role=button divs don't receive keyboard event handling automatically and lack the semantic expectation of Space/Enter keypresses. The close button (span.lb-close with role=button) is particularly problematic.
Fix: Replace the greeting bubble div with '
' and replace the close span with ''. Native buttons provide built-in keyboard support (Space/Enter) and better screen reader semantics.
Severity: high
Where:C:\Users\nates\Downloads\Claude\Utah Autism Testing\website\privacy-policy.html:661,667
The privacy-policy.html file contains placeholder email '[email protected]' in two locations (lines 661 and 667), which should be replaced with the actual privacy contact email address from the business. This prevents users from being able to contact the company regarding privacy concerns as required by GDPR and other privacy laws.
Fix: Replace '[email protected]' with the actual privacy contact email address (e.g., [email protected] or the appropriate contact)
Link Targeting & SEO
Inconsistent use of onerror handler on images instead of proper fallback
13 images across index.html use inline onerror="this.style.display='none'" handlers (lines 632, 1504-1517). This is a legacy pattern that hides broken images silently. Modern best practice is to use CSS error states or elements with fallback sources.
Fix: Replace onerror handlers with CSS-based error handling or use picture elements:
Location-Specific Content
Limited location-differentiated messaging and local service details
Severity: medium
Where:/murray-utah.html, /mayfield-ohio.html, /gahanna-ohio.html, /worthington-ohio.html. JSON shows hours as generic 8am-5pm.
While location pages exist (murray-utah.html, mayfield-ohio.html, gahanna-ohio.html, worthington-ohio.html), each lacks service availability details. No content explains which programs are available at which location (e.g., 'Early Intervention is available in Utah only' or 'In-home ABA available in all Ohio locations'). No location-specific insurance networks, waitlists, or opening hours variation.
Fix: For each location page: (1) specify which programs/services are available, (2) list insurance carriers accepted at that location (if different), (3) show accurate local hours if flexible, (4) mention director or lead BCBA at that location, (5) add local therapist/RBT availability or waitlist status if relevant.
Meta Tags & Descriptions
landing.html lacks canonical, OpenGraph, and JSON-LD tags
Severity: medium
landing.html (the paid traffic landing page) has no canonical link, no og:url/og:title/og:description, and no JSON-LD schema. While it correctly includes 'noindex, nofollow' robots meta tag, the missing OG tags mean social sharing will use generic fallbacks. If this page is ever removed from noindex, it will have no structured data.
Fix: Even for noindex pages intended for paid traffic, add minimal SEO metadata: canonical href (should be 'noindex' with self-referential canonical), og:title/og:description for social preview, and Organization JSON-LD. This ensures if the page becomes indexable later, crawlers have complete information.
test.html is a development/test page left in production
Severity: medium
test.html exists in the root and contains a basic meta description ('This is a test page'), full SEO tags, and is not marked noindex. This appears to be a development/QA artifact that should not be publicly visible. If crawlers discover it, it wastes crawl budget and appears as low-quality/thin content.
Fix: Either delete test.html from production, or mark it 'noindex, nofollow'. If kept as a QA page, ensure it is excluded from sitemap.xml and robots.txt.
Mobile Navigation
Mobile nav menu keyboard trap risk: no explicit focus management when opening/closing
Severity: medium
The mobile menu (header.js lines 54-74) toggles visibility and sets aria-expanded but does not manage focus. When the menu opens, focus should move to the first menu item or the close button. When the menu closes, focus should return to the toggle button. Currently, focus remains wherever it was, which can confuse keyboard users and may create a focus trap if the menu opens and closes rapidly.
Fix: Add focus management: (1) When menu opens, call firstMenuItem.focus() or closeButton.focus(), (2) When menu closes, call toggle.focus(). (3) Trap focus within the menu using Shift+Tab/Tab logic, or rely on visually hidden focus order. (4) Test with Tab/Shift+Tab to ensure keyboard navigation is fluid.
Mobile UX - Form Padding on Narrow Screens
Contact form padding may be insufficient on screens <360px wide
The form wrapper uses p-4 sm:p-6 on mobile (16px padding on <640px), which consumes 32px of a 320px viewport, leaving 288px. While technically workable, the px-5 pattern on the outer .max-w-7xl applies throughout the site (5 * 4 = 20px margin per side), further constraining layouts. No explicit handling exists for ultra-narrow devices or landscape mode on small phones.
Fix: Add a media query for @media (max-width: 360px) { [data-jotform-wrap] { padding: 1rem .75rem !important; } } or use px-4 (16px) as the baseline instead of px-5. Test rendering on iPhone SE and landscape orientations.
Mobile UX - Safe Area Insets
No safe-area-inset handling for notched devices (iPhone X+, Android)
Severity: high
Where:C:\Users\nates\Downloads\Claude\Utah Autism Testing\website\assets\js\header.js (line 173, renderMobileCta function)
The sticky mobile CTA bar uses fixed bottom-0 left-0 right-0 without padding-right: env(safe-area-inset-right) or padding-bottom: env(safe-area-inset-bottom). On devices with notches or home indicators (iPhone 12+, Galaxy Z Fold, etc.), the bar will be cut off or overlap system UI. The viewport-fit=cover meta tag is also absent, failing to enable the safe-area environment variables.
Fix: Add viewport-fit=cover to the viewport meta tag in all HTML files. Update the mobile CTA bar div to include style='padding-bottom: max(env(safe-area-inset-bottom), .625rem); padding-right: env(safe-area-inset-right); padding-left: env(safe-area-inset-left);' or equivalent Tailwind safelist definitions. Similarly protect the fixed mobile nav toggle button.
Mobile UX - Touch Target Sizing
Mobile menu button and navigation targets do not meet WCAG AAA 44px minimum
Severity: medium
Where:C:\Users\nates\Downloads\Claude\Utah Autism Testing\website\assets\templates\sections\header.html (mobile nav links) and C:\Users\nates\Downloads\Claude\Utah Autism Testing\website\assets\js\header.js (mobile CTA buttons at lines 175-179)
The mobile navigation button is w-11 h-11 (44px square in pixels), meeting the absolute minimum. However, multiple touch targets in the mobile navigation panel (py-2.5 nav links = ~32px effective height) and mobile sticky CTA bar (py-2.5 buttons) fall below the recommended 48px WCAG AAA standard and may be difficult to tap accurately, especially for children with motor control challenges (the core audience).
Fix: Increase mobile nav link padding to py-3 (12px = 44px total height) and mobile CTA button padding to py-3 minimum. The .btn base class uses padding: .85rem 1.4rem but mobile overrides with py-2.5 which is insufficient for accessibility.
Performance
Tailwind JIT at runtime (398 KB uncompressed) instead of static precompilation
Severity: high
The site loads Tailwind via /assets/vendor/tailwind.js (398 KB), which performs JIT compilation in the browser. This is render-blocking, adds ~400 KB to every page, and delays First Contentful Paint. The optimize-pages.py script explicitly swaps the CDN version for this local vendor copy, but there is no build step to precompile Tailwind to static CSS based on the actual HTML classes used across all pages.
Fix: Run npx tailwindcss -i assets/css/tailwind-input.css -o assets/css/tailwind-compiled.css --minify (or equivalent) during build.sh, then reference the precompiled CSS instead of the JIT vendor script. This will reduce the CSS payload from 398 KB JIT + 24 KB app.css to ~15-20 KB of actual used classes.
No defer or async on critical JS; render-blocking script execution at end of document
Severity: high
Scripts are loaded at the end of the body without async/defer attributes: <script src="/assets/js/header.js"></script>, <script src="/assets/js/footer.js"></script>, <script src="assets/js/app.js"></script>. While placed at the end of the body to minimize blocking, these should use defer so parsing is not halted and multiple scripts can execute in parallel.
Fix: Add defer attribute to all three script tags: <script src="/assets/js/header.js" defer></script>. This allows HTML parsing to continue while scripts download in the background.
Hero image (jessica-rockowitz) at 366 KB with no lazy-loading or modern format fallback
Severity: high
The main LCP candidate image /assets/images/jessica-rockowitz-jbiInQGY8og-unsplash.png is 366 KB and loaded without lazy-loading, size hints, or WebP/AVIF fallback. It is a PNG in a high-resolution frame and blocks paint.
Fix: Convert to WebP (likely ~80-120 KB) and AVIF (~50-80 KB), use <picture> with source elements. Add loading="eager" for hero images, and use sizes attribute for responsive images. Example: <picture><source srcset="...hero.avif" type="image/avif"><source srcset="...hero.webp" type="image/webp"><img src="...hero.png" alt="..." width="600" height="750" decoding="async" /></picture>
Large images throughout site without modern formats (NewAbout 241 KB, multiple 200+ KB PNGs)
Severity: high
Many images in /assets/images/ are unoptimized PNGs: NewAbout.png (241 KB), several 200+ KB PNG images, no AVIF/WebP variants. The entire /assets/ directory is 26 MB.
Fix: Bulk-convert all large PNG/JPG images to AVIF (smallest) with WebP fallback and original format as final fallback. Use a tool like ImageMagick, cwebp, or avifenc in the build pipeline. Prioritize images over 100 KB for conversion first.
Tailwind config inlined in every page (28 lines of configuration JS)
Severity: medium
Every page includes a <script> block with tailwind.config = { theme: { extend: { ... } } } (lines 9-30 in index.html). This is necessary for JIT but becomes redundant once Tailwind is precompiled.
Fix: Remove the inline tailwind.config block once static CSS compilation is implemented. If you must keep JIT for admin pages, move this to a separate script that only admin pages load.
Only one font weight (600) preloaded; other weights downloaded on-demand
Severity: medium
Only /assets/fonts/plus-jakarta-sans-600.woff2 is preloaded. The site uses fonts at 400, 500, 700, 800 weights (per @font-face definitions), which are fetched on first use, causing layout shift and text rendering delay.
Fix: Preload the 400 weight (body text) in addition to 600: add <link rel="preload" as="font" type="font/woff2" crossorigin href="assets/fonts/plus-jakarta-sans-400.woff2">. Consider font-display: fallback (0.1s) instead of swap (3s) for critical display font (Fraunces).
No explicit LCP element identification or optimization
Severity: medium
The hero section includes a large image (366 KB), decorative blobs, and text inside a grid. There is no explicit LCP candidate optimization or largest-contentful-paint meta directive. The image is likely the LCP but may not be identified correctly by the browser.
Fix: Add fetchPriority="high" to the hero image <img> tag. Consider critical CSS inlining for above-the-fold styles to reduce render-blocking CSS (currently 24 KB app.css + 398 KB Tailwind).
View-transition meta tag present but no CSS transitions defined for major layout changes
Severity: low
The site includes <meta name="view-transition" content="same-origin"> (line 550) for cross-fade navigation, but no explicit view-transition CSS rules are defined in app.css to control the animation (duration, easing). Will use browser defaults.
Fix: Add view-transition rules in app.css to explicitly define animations for better control and faster perceived performance: ::view-transition { animation-duration: 250ms; }
Performance & Optimization
Multiple branded logo images lack width/height attributes
Insurance partner logos (lines 1504-1515 in index.html) lack width and height attributes, which prevents layout calculations and can contribute to CLS. These images should have explicit dimensions defined.
Fix: Add width="40" height="40" (or appropriate dimensions) to all logo tags: <img ... width="40" height="40" class="h-10 ...">
Performance & Third-Party Integration
marked.js and DOMPurify loaded without Subresource Integrity (SRI)
Severity: medium
Where:blog/post.html lines 45-46
Blog post template loads marked.min.js and dompurify from CDN without integrity attributes, making them vulnerable to supply chain attacks if the CDN is compromised.
prefers-reduced-motion is respected in CSS but animations in leadbot widget and app.js still execute without explicit opt-in
Severity: medium
app.css (lines 227-231) includes media query for prefers-reduced-motion, and leadbot.js (line 15) checks matchMedia for reduced motion. However, the counter animation (app.js lines 104-124) and the reveal animation for scroll triggers do not explicitly check prefers-reduced-motion in JavaScript. Users with motion sensitivity may experience issues.
Fix: Add explicit prefers-reduced-motion checks in app.js for the counter animation and scroll reveal. Example: if (!reducedMotion) { /* run animation */ } wrapping the requestAnimationFrame calls.
Pricing & Insurance Transparency
No published fee schedule or out-of-pocket cost ranges
Severity: medium
Where:/insurance.html mentions '$0 typical out-of-pocket' on home page but no detailed breakdown. No pricing page.
The insurance.html page explains coverage and acceptance but provides no fee schedule, hourly rates, typical session costs, or out-of-pocket ranges for uninsured/underinsured families. Legacy site likely had clearer pricing transparency. Many parents want to know ballpark costs before calling.
Fix: Create /pricing.html or add pricing section to /insurance.html showing: typical session costs, rates per hour by service type (center-based vs. in-home), self-pay rates, autism scholarship info, payment plans. Clearly state 'For exact pricing, call' to avoid stale info, but give ballpark ranges.
Privacy & Compliance
Leadbot localStorage usage not disclosed in Privacy Policy
Severity: medium
Where:Privacy Policy (html) does not mention localStorage; leadbot.js:86-91 use localStorage
The leadbot widget (leadbot.js lines 86, 91) persists user state in localStorage (STORAGE_KEY = 'ota_leadbot_v1'), but the Privacy Policy does not disclose this local storage usage or describe what data is persisted.
Fix: Update Privacy Policy to include: 'We use browser localStorage to persist your responses in our intake widget (ota_leadbot_v1 key) to avoid re-asking the same questions. This data is stored locally on your device and is not transmitted to our servers unless you submit a form.'
Privacy & Data Disclosure
localStorage used by Leadbot widget not disclosed in Privacy Policy
Severity: medium
Where:C:\Users\nates\Downloads\Claude\Utah Autism Testing\website\assets\js\leadbot.js (line 83) vs. C:\Users\nates\Downloads\Claude\Utah Autism Testing\website\privacy-policy.html
The leadbot.js widget persists state to localStorage using the key 'ota_leadbot_v1' (line 83), storing user intent, region, age, name, phone, and email. However, the Privacy Policy does not disclose this use of localStorage for data persistence. The Privacy Policy should explicitly mention localStorage usage, its purpose (session state persistence), and that users can clear it by clearing browser storage.
Fix: Add a section to the Privacy Policy explaining localStorage usage: 'We use browser localStorage to save your interaction state with our intake assistant widget. This data is stored only on your device and is not transmitted to our servers. You can clear this data by clearing your browser's cache or localStorage.'
The site lacks a manifest.json (or .webmanifest) file and favicon references in HTML. While not critical for core functionality, this impacts progressive web app capabilities, mobile home screen installation, and browser tab/address bar icons.
Fix: Create a manifest.json file in the root directory with app metadata (name, icons, theme colors, display mode). Add link references in HTML head: ''. Also add favicon references: ''. Consider generating multiple favicon formats (ico, png) for broad browser support.
SEO & Domain References
JSON-LD logo URL points to old WordPress domain
Severity: high
Where:All pages (index.html, about.html, etc.) in JSON-LD schema @graph Organization section
The logo URLs in the Organization and LocalBusiness schema blocks still reference https://ontargetaba.com/wp-content/uploads/2022/04/footerImg.png, which is the old WordPress domain path. Since the image was not migrated to the new static site, this URL returns 404, breaking schema validation and resulting in broken Open Graph previews.
Logo image still references old WordPress domain in JSON-LD
Severity: medium
Where:C:/Users/nates/Downloads/Claude/Utah Autism Testing/website (every .html file, schema.org sections)
Throughout all pages, the JSON-LD schema uses 'https://ontargetaba.com/wp-content/uploads/2022/04/footerImg.png' for logo and clinic images. While the new footerImg.png exists at /assets/images/, the old WordPress domain URL in schema may cause search engines to crawl the old site for image metadata. The image is hosted locally but schema points to the old domain.
Test page (test.html) indexed in sitemap and accessible from navigation
Severity: high
Where:C:/Users/nates/Downloads/Claude/Utah Autism Testing/website/sitemap.xml:172; test.html; sitemap.xml
test.html is a development/test page that is included in the sitemap.xml (line 172) and has full SEO tags. This page should never be exposed to public search engines or web crawlers. The page has a title 'Test - On Target ABA' and is listed in search results.
Fix: Remove test.html from sitemap.xml, add it to robots.txt disallow list, or add 'noindex, nofollow' robots meta tag to test.html itself. Consider moving test pages to /admin/ directory with authentication.
test.html is production-accessible and lacks noindex/nofollow protection
Severity: high
Where:test.html
test.html is a development/test page that is still publicly accessible, included in sitemap.xml (line 172), and lacks 'noindex, nofollow' meta tags. This can inflate crawl budget and confuse search engines with duplicate/test content.
Fix: Either remove test.html entirely from production, or add to its . Remove the entry from sitemap.xml.
test.html lacks noindex protection in production
Severity: high
Where:test.html - entire head section
test.html does not include a robots meta tag with noindex/nofollow, meaning this development/test page is crawlable and indexable by search engines and appears in sitemap.
Fix: Add to test.html head and remove test.html from sitemap.xml generation
SEO & Meta Tags
landing.html lacks canonical, OpenGraph, and JSON-LD tags
Severity: high
Where:landing.html, lines 1-60
The landing page (marked as paid-traffic-only with 'noindex' on line 10) has no canonical link, no og:url, og:image, or schema.org markup. While the noindex directive is intentional, the missing canonical and OG tags mean the page cannot be properly previewed on social platforms if shared, and lacks proper SEO foundation.
Fix: Add canonical link: . Add og:image, og:url, og:type, and og:description meta tags. Consider adding a basic WebPage schema.org JSON-LD block even for noindex pages to ensure structured data is complete.
landing.html missing canonical, og:, and twitter: meta tags
Severity: high
Where:landing.html (head section)
The landing page (landing.html) is marked as noindex in robots meta tag but lacks standard OpenGraph, Twitter Card, and canonical tags which are critical for social sharing and search visibility despite noindex status.
Fix: Add meta tags: , , , , ,
landing.html lacks OpenGraph image metadata
Severity: medium
landing.html is missing og:image, og:image:width, og:image:height, twitter:image meta tags. While the page has robots=noindex (so it won't appear in organic search), if shared on social media or messaging apps, it will not have a preview image, reducing engagement and click-through rates.
Fix: Add og:image, og:image:width, og:image:height to landing.html. Generate a custom OG image (or reuse one from another landing-type page) and reference it in the meta tags. The gen-og-images.mjs script should be updated if applicable to generate images for landing pages.
SEO & Metadata
Blog post template lacks article-specific OpenGraph and article meta tags
The blog/post.html template dynamically generates og:type="article" but lacks article-specific OpenGraph meta tags such as og:article:published_time, og:article:modified_time, og:article:author, which are important for social media sharing and SEO crawling of article content.
Fix: Add article-specific meta tags to the template: and similar for modified_time and author
Security
Insurance form target='_blank' lacks rel attribute
Severity: medium
Where:C:/Users/nates/Downloads/Claude/Utah Autism Testing/website/insurance.html (form element)
The insurance.html form has target='_blank' without rel='noopener noreferrer', creating a window.opener security vulnerability that allows the external site to access the parent window object and potentially perform actions on behalf of users.
Fix: Add rel='noopener noreferrer' to the form element: <form action='#' target='_blank' rel='noopener noreferrer' ...>
Jotform iframe embeds lack sandbox attribute
Severity: medium
Where:C:/Users/nates/Downloads/Claude/Utah Autism Testing/website/contact.html, autism-testing.html (script tag type='text/javascript' src='https://form.jotform.com/jsform/...')
Jotform is embedded via script tags that inject iframes without sandbox attributes. This allows the iframe to execute scripts, submit forms, open popups, and access the parent window context without restriction, increasing attack surface.
Fix: While jotform.com-injected iframes cannot be directly controlled, implement Content-Security-Policy to restrict what Jotform can access. Alternatively, switch to using Jotform's iframe embed mode (if available) which allows sandbox attributes. Document this limitation in security notes.
Security & Browser Integration
Missing Content-Security-Policy (CSP) header
Severity: high
Where:_headers configuration file
_headers file lacks a Content-Security-Policy header, providing no protection against XSS attacks, unsafe script execution, or malicious resource loading.
_headers file lacks an HSTS header, failing to enforce HTTPS for all connections and making the site vulnerable to downgrade attacks.
Fix: Add to _headers: Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
Missing X-XSS-Protection header
Severity: medium
Where:_headers configuration file
_headers file lacks X-XSS-Protection header to provide legacy XSS attack protection for older browsers.
Fix: Add to _headers: X-XSS-Protection: 1; mode=block
Security & Cross-Origin
Insurance form with target='_blank' lacks rel='noopener' attribute
Severity: high
Where:C:\Users\nates\Downloads\Claude\Utah Autism Testing\website\insurance.html:661
The insurance verification form at insurance.html line 661 uses target='_blank' to open the form in a new window/tab without the rel='noopener' security attribute. This creates a window opener vulnerability where the new window can access window.opener and redirect the original page.
Fix: Add rel='noopener noreferrer' to the form element: <form action='#' target='_blank' rel='noopener noreferrer' ...>
Security & Cross-Window Vulnerability
Form with target='_blank' missing rel='noopener'
Severity: high
Where:C:\Users\nates\Downloads\Claude\Utah Autism Testing\website\insurance.html, line 661: <form action="#" target="_blank" class="...">
The insurance verification form on insurance.html uses target='_blank' to open in a new window but lacks the rel='noopener' attribute. This exposes the form to a reverse tabnabbing attack where the new window can access the opener's window.location, potentially allowing malicious content to redirect users to phishing pages.
Fix: Add rel='noopener' to the form element:
Security Headers
Missing Content-Security-Policy (CSP) Header
Severity: high
Where:C:\Users\nates\Downloads\Claude\Utah Autism Testing\website\_headers
The _headers file lacks a Content-Security-Policy directive, which is critical for mitigating XSS (cross-site scripting) and injection attacks. This is especially important given the site embeds Jotform, leadbot widget, and other third-party scripts.
Fix: Add a Content-Security-Policy header in the _headers file. Example: 'Content-Security-Policy: default-src "self"; script-src "self" https://cdn.jsdelivr.nethttps://form.jotform.com; style-src "self" "unsafe-inline"; img-src "self" data: https:; font-src "self"; connect-src "self" https://form.jotform.com; frame-src https://form.jotform.com;' (adjust based on actual third-party requirements)
Public pages lack X-Frame-Options header (clickjacking vulnerability)
Severity: high
The _headers file only applies X-Frame-Options: DENY to /admin/* routes. Public pages (/*) do not inherit this header, leaving them vulnerable to clickjacking attacks where malicious actors could frame the site and trick users into clicking hidden buttons.
Fix: Add X-Frame-Options: SAMEORIGIN (or DENY if framing is not needed) to the wildcard /* section of _headers to protect all public pages from clickjacking.
Missing X-Frame-Options header for public pages
Severity: high
Where:_headers file, public content section (lines 23-27)
The wildcard rule for all public pages (/) includes X-Content-Type-Options and Referrer-Policy, but omits X-Frame-Options. Without this header, the site is vulnerable to clickjacking attacks. Only the /admin/ path has explicit X-Frame-Options: DENY protection.
Fix: Add X-Frame-Options: SAMEORIGIN (or DENY if Jotform iframes are handled differently) to the wildcard rule under /* in _headers.
Public pages missing X-Frame-Options header (clickjacking vulnerability)
Severity: high
_headers file sets X-Frame-Options: DENY only for /admin/* paths. Public pages (/*) lack X-Frame-Options, allowing the site to be embedded in frames on other domains. While the site doesn't appear to be a sensitive financial app, this is a standard security best practice for all pages.
Fix: Add X-Frame-Options: SAMEORIGIN to the /* rule in _headers to allow framing only from the same origin, or DENY to prevent all framing.
Missing Content-Security-Policy (CSP) header on all public pages
Severity: high
Where:C:\Users\nates\Downloads\Claude\Utah Autism Testing\website\_headers
The _headers file does not include a Content-Security-Policy header. This prevents defense against XSS attacks and constrains resource loading (scripts, styles, images) to trusted sources. Currently, the site loads Jotform, marked.js, dompurify, and Google fonts without CSP constraints.
The _headers file lacks several important security headers: (1) Strict-Transport-Security (HSTS) to enforce HTTPS; (2) Content-Security-Policy (CSP) to prevent XSS and data exfiltration; (3) X-XSS-Protection for legacy browser support; (4) X-Permitted-Cross-Domain-Policies to restrict cross-domain requests. The admin section has X-Frame-Options: DENY (good), but the global rule does not. For a healthcare provider handling PHI, these headers are critical to prevent eavesdropping and injection attacks.
Fix: Add the following headers to the _headers file under the global rule (/*): Strict-Transport-Security: max-age=31536000; includeSubDomains; preload; X-XSS-Protection: 1; mode=block; Content-Security-Policy (implement a strict policy blocking inline scripts and restricting external sources); X-Permitted-Cross-Domain-Policies: none. Test CSP in report-only mode before deploying.
Missing Strict-Transport-Security (HSTS) Header
Severity: medium
Where:C:\Users\nates\Downloads\Claude\Utah Autism Testing\website\_headers
The _headers file lacks the Strict-Transport-Security (HSTS) header, which is crucial for preventing man-in-the-middle attacks by forcing all future connections to use HTTPS. This is particularly important for a healthcare/medical business site handling sensitive family information.
Fix: Add Strict-Transport-Security header to _headers file: 'Strict-Transport-Security: max-age=31536000; includeSubDomains; preload' (or similar configuration based on your HTTPS deployment strategy)
Missing critical security headers in _headers configuration
Severity: medium
Where:C:/Users/nates/Downloads/Claude/Utah Autism Testing/website/_headers
The _headers file is missing several important security headers: Strict-Transport-Security (HSTS), Content-Security-Policy, X-XSS-Protection, and Expect-CT. These headers protect against man-in-the-middle attacks, XSS, and other common vulnerabilities.
Fix: Add the following headers to _headers: Strict-Transport-Security: max-age=31536000; includeSubDomains, Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.nethttps://form.jotform.com; img-src 'self' data: https:; font-src 'self', X-XSS-Protection: 1; mode=block, Expect-CT: max-age=86400
Sitemap & Crawlability
No 404 error page or error handling for missing blog posts
Severity: medium
The blog/post.html template shows a 'Post not found' message for invalid slugs (line 137-141), but this is a 200 status response with error content, not a true 404. Search engines will see a successful HTTP 200 and index the generic error message as content. Additionally, there is no custom 404.html for other missing pages.
Fix: Implement proper 404 handling: (1) Configure Cloudflare Pages to return actual 404 status codes for missing files. (2) Create a custom 404.html with proper SEO (title, meta description, internal linking suggestions). (3) For blog posts, return 404 status when slug is not found instead of 200 + 'not found' message.
Special Programs / Initiatives
Limited visibility of autism scholarship/funding programs
Severity: low
Where:/insurance.html has a single card; no dedicated page for funding/scholarship options.
The insurance.html page mentions 'Autism Scholarship Providers' as a card but provides no details on which programs On Target ABA partners with, eligibility, or how to apply. Blog has an article on Ohio's program, but it's not discoverable from main navigation.
Fix: Create /scholarships.html or expand /insurance.html section with: (1) list of autism scholarship programs by location (Utah, Ohio), (2) eligibility overview, (3) how to apply, (4) link to blog article, (5) CTA to contact for help navigating programs.
State Privacy Laws
Missing disclosures for Utah and Ohio privacy laws (UTDAL, OPPA)
Severity: medium
On Target ABA operates in Utah (where the Utah Data Attribute Law / UTDAL is relevant) and Ohio (where the Ohio Personal Privacy Act / OPPA, effective 2027, applies). The privacy policy does not mention either law, does not provide state-specific rights (right to delete, right to opt-out of targeted ads, right to correct, etc.), and does not explain how to submit requests. The policy also does not differentiate between Utah and Ohio resident rights or provide jurisdiction-specific contact instructions.
Fix: Add a section to the privacy policy explaining UTDAL (Utah) and OPPA (Ohio) rights for residents in those states. Include state-specific opt-out mechanisms, instructions for submitting data deletion/correction requests, response timelines, and any exemptions (e.g., HIPAA data). Provide separate contact info or a process for state-specific requests. Consider a banner or clear disclosure on the site indicating which laws apply.
Sticky CTA & Mobile Engagement
No sticky floating CTA on mobile for key conversion pages
Severity: medium
Where:C:\Users\nates\Downloads\Claude\Utah Autism Testing\website\contact.html, pre-intake-form.html, autism-testing.html
Contact, pre-intake-form, and autism-testing pages lack a sticky bottom mobile CTA (floating 'Get Started' button or phone number) that stays visible as users scroll. Mobile users often scroll to read but don't scroll back to top. Critical for service-based businesses.
Fix: Add fixed footer bar on mobile (<768px breakpoint) with either 'Schedule Now' button + phone link, or 'Call (888) 989-5011' with tap-to-call affordance. Keep it 50-60px tall. This can increase mobile conversions 15-25%.
Structured Data & SEO
Missing Service Schema on autism-testing.html
Severity: medium
Where:C:\Users\nates\Downloads\Claude\Utah Autism Testing\website\autism-testing.html (lines 57-300+)
The autism-testing.html page lacks Service schema markup, while other service pages (center-based-aba-therapy.html, in-home-aba-therapy.html, early-intervention-autism-program.html, potty-training-program.html) include detailed Service @type schemas. This creates inconsistent semantic markup across similar high-priority pages and may impact search visibility for service-specific queries.
Fix: Add a Service schema object to the JSON-LD in the autism-testing.html head section matching the structure used in other service pages. The schema should include: '@type': 'Service', 'name': 'Autism Testing', 'provider': reference to the organization, 'areaServed': service areas, 'availableChannel': online/in-person, and other relevant service properties.
Team & Staff Bios
Missing therapist and staff team bios
Severity: medium
Where:New site lacks: team page, staff directory, individual BCBA/RBT bios. Legacy WP site likely had team section in Elementor.
The new site only features the founder Ilana Gross but has no pages showcasing other BCBAs, RBTs, or clinical staff member profiles. The About page does not include team member photos, credentials, specialties, or years of experience. This is standard trust-building content for behavioral health providers.
Fix: Create a dedicated team or meet-the-team page with bios (photos, credentials, specialties, years of experience) for at least BCBAs and key clinical staff at each location. Include in /about.html or as /team.html.
Text Alternatives
Decorative SVG icons lack aria-hidden=true, creating noise for screen readers
Severity: low
Many SVG icons throughout the site (header, buttons, testimonials) do not have aria-hidden="true". While some have stroke-only designs (like phone icons), decorative icons are being announced to screen readers. Example: line 1037 in index.html review marquee SVG stars have no aria-hidden.
Fix: Add aria-hidden="true" to all purely decorative SVGs. For meaningful SVGs (e.g., status indicators), add role="img" and aria-label="description". Audit all tags and add attributes consistently across the site.
Third-Party Data Processors
Privacy policy lacks specific processor names and roles (controller vs. processor)
Severity: medium
The privacy policy uses generic 'categories of third parties' (e.g., 'Customer management systems', 'Data analytics providers', 'Email service providers') but does not name specific processors or their roles. Under GDPR/CCPA/state law, data subjects should know: (1) exact names of processors handling their data; (2) what data they receive; (3) their location and security practices; (4) whether they are a controller or processor. For example, 'Jotform', 'Cloudflare', 'Google Analytics', 'your email provider' should be named. Processors handling PHI must be identified and have BAAs.
Fix: Add a detailed 'Data Processors' table or section listing: company name, role (processor/controller), data categories, location, and purpose. Link to each processor's privacy policy and data handling addendum. Include forms of their security certifications (SOC 2, ISO 27001, HIPAA compliance). Ensure Jotform's privacy policy is reviewed for HIPAA compliance and that a BAA is in place.
Severity: high
Where:C:\Users\nates\Downloads\Claude\Utah Autism Testing\website\blog\post.html:45-46 and admin\post-editor.html:33-34
External scripts from CDN (marked.js, dompurify) are loaded without integrity checksums. These scripts handle dynamic content processing and should have cryptographic verification to prevent tampering.
Severity: high
Where:Multiple pages: autism-testing.html:1059, contact.html:492, landing.html:281, pre-intake-form.html:505
Jotform scripts loaded across multiple pages (autism-testing.html, contact.html, landing.html, pre-intake-form.html) are loaded without integrity checksums or sandbox attributes on iframes. This creates a dependency on external party without cryptographic verification.
Fix: Add integrity attribute to Jotform script tags if provider supports it; alternatively, implement Content-Security-Policy to restrict script execution origin
Severity: medium
Where:C:\Users\nates\Downloads\Claude\Utah Autism Testing\website\blog\post.html:45-46
The blog/post.html file loads marked.js and dompurify from CDN (cdn.jsdelivr.net) without integrity attributes. This exposes the site to potential compromise if the CDN is compromised. The script tags should include integrity hashes to verify the downloaded content hasn't been tampered with.
Some page titles exceed recommended 60-character length
Severity: low
Several page titles are 70-92 characters, which exceeds Google's typical display limit of ~60 characters on desktop and ~40 on mobile. Long titles risk truncation in search results. Examples: 'On Target ABA — ABA Therapy for Kids in Cleveland, Columbus & Salt Lake City' (86 chars after entity decoding), 'Our Services — ABA Therapy in Cleveland, Columbus & Salt Lake City | On Target ABA' (92 chars). While not wrong, this wastes visible space and may hurt CTR.
Fix: Trim titles to 50-60 characters, prioritizing the most important keywords first. Example: 'ABA Therapy in Cleveland, Columbus & Salt Lake City' (52 chars) instead of the longer version. Review SEO_PAGES dictionary in inject-seo.py and shorten titles without losing meaning.
Trust Signals - Credentials Display
Clinical credentials missing from key conversion pages above-fold
About.html exists with Ilana Gross BCBA details and schema markup, but contact.html, autism-testing.html, and pre-intake-form.html do not surface founder/clinical credentials above-fold or near forms. High-anxiety parents need credibility immediately.
Fix: On contact.html, add trust strip: 'Led by Ilana Gross, BCBA | 10+ years autism ABA | Licensed clinicians only.' On autism-testing.html hero, add badge: 'Evaluations by Licensed Clinicians (BCBA-supervised).' Reduces anxiety and builds credibility without extra content.
Video Content
No embedded video content or testimonial videos
Severity: low
Where:No video embeds across the new site.
Modern healthcare sites include video testimonials, service walkthroughs, founder message, or day-in-the-life content. No video assets found on new site (though legacy site likely had similar limitation with Elementor embeds).
Fix: Consider adding (low priority): 30-second testimonial videos, 2-min 'what is ABA' explainer, 'day in the life at our center' video, founder/clinical director message. Even simple static images with overlays can help—but full video is ideal for trust-building.
Web Standards
Missing favicon and apple-touch-icon declarations
Severity: medium
Where:All HTML pages lack: <link rel='icon' href='/favicon.ico'>, <link rel='apple-touch-icon' href='/apple-touch-icon.png'>, <link rel='manifest' href='/manifest.json'>
No favicon.ico, manifest.json, or apple-touch-icon defined. Users visiting on mobile and desktop see broken icon in browser tab/home screen, and Progressive Web App installation is not discoverable.
Fix: Create favicon files and manifest.json; add to base HTML template:
Web Standards & Browser Integration
No favicon or apple-touch-icon defined
Severity: low
Where:All pages; no favicon.ico exists; no <link rel="icon"> or <link rel="apple-touch-icon"> tags
Pages lack favicon and apple-touch-icon links. While not critical for SEO, this results in 404 errors for favicon.ico requests, wastes bandwidth, and makes the site appear less polished in browser tabs and home screens.
Fix: Create or migrate favicon files (favicon.ico, favicon-16x16.png, favicon-32x32.png, apple-touch-icon.png). Add and to the of all pages.
Web Standards & Progressive Enhancement
No Web App Manifest (manifest.json) defined
Severity: medium
Where:Entire project; no manifest.json exists; no link rel="manifest" in <head>
A manifest.json file is missing, and there is no tag in page heads. This prevents the site from being installable as a PWA and makes it impossible to customize the app's appearance when added to home screens.
Fix: Create assets/manifest.json with name, short_name, description, icons, start_url, display, theme_color, and background_color. Reference it in all pages with .
Missing favicon and apple-touch-icon links
Severity: medium
Where:All HTML pages - missing from head section
No or tags present in any page head, leading to missing browser tab icons and iOS home screen app icons.
Fix: Add to all page heads: ,
Missing web app manifest (manifest.json)
Severity: low
The site has no manifest.json file. While not critical for core functionality, a manifest enables PWA features like add-to-home-screen on mobile, custom theme colors, and splash screens. Users cannot install the site as a progressive web app.
Fix: Create assets/manifest.json with name, short_name, description, icons (multiple sizes), theme_color (#00B7EA), background_color (#FAF5E6), display mode, and scope. Link it in all HTML pages with .
How to use this document
High severity: address before scaling paid traffic.
Medium: schedule into the next sprint.
Low: backlog.
Findings already addressed since the audit was run should be removed. To regenerate, re-run the find-site-improvements workflow.