Skip to content

Latest commit

 

History

History
1285 lines (729 loc) · 84.9 KB

File metadata and controls

1285 lines (729 loc) · 84.9 KB

Missing Features and Improvements

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.

Accessibility & Image Optimization

Multiple images lacking width/height attributes causing layout shift

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.

Fix: Via admin dashboard at https://beta.ontargetaba.com/admin/head-scripts.html:

  1. Navigate to the 'Head Scripts' page
  2. 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>
  1. Click 'Save & open PR'
  2. 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.

Fix: Via admin dashboard at https://beta.ontargetaba.com/admin/head-scripts.html:

  1. Navigate to the 'Head Scripts' page
  2. Append (or add separately) the following Google Ads tag:
<script async src="https://www.googletagmanager.com/gtag/js?id=AW-354317910"></script> <script> window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', 'AW-354317910'); </script>
  1. Click 'Save & open PR'
  2. Merge to deploy

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.

Fix: Via admin dashboard at https://beta.ontargetaba.com/admin/head-scripts.html:

  1. Navigate to 'Head Scripts'
  2. Append the Facebook Pixel snippet. Replace PIXEL_ID with your actual Pixel ID (check your Facebook Ads Manager or legacy WP PixelYourSite settings):
<script> !function(f,b,e,v,n,t,s) {if(f.fbq)return;n=f.fbq=function(){n.callMethod? n.callMethod.apply(n,arguments):n.queue.push(arguments)}; if(!f._fbq)f._fbq=n;n.push=n;n.loaded=!0;n.version='2.0'; n.queue=[];t=b.createElement(e);t.async=!0; t.src=v;s=b.getElementsByTagName(e)[0]; s.parentNode.insertBefore(t,s)}(window, document,'script', 'https://connect.facebook.net/en_US/fbevents.js'); fbq('init', 'YOUR_PIXEL_ID'); fbq('track', 'PageView'); </script>

  1. Replace YOUR_PIXEL_ID with the actual ID (typically 10-15 digits)
  2. Click 'Save & open PR'
  3. 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.

Fix: Via admin dashboard at https://beta.ontargetaba.com/admin/head-scripts.html:

  1. Navigate to 'Head Scripts'
  2. Append the Hotjar snippet:
<script> (function(h,o,t,j,a,r){ h.hj=h.hj||function(){(h.hj.q=h.hj.q||[]).push(arguments)}; h._hjSettings={hjid:5016360,hjsv:6}; a=o.getElementsByTagName('head')[0]; r=o.createElement('script');r.async=1; r.src=t+h._hjSettings.hjid+j+h._hjSettings.hjsv; a.appendChild(r); })(window,document,'https://static.hotjar.com/c/hotjar-','.js?sv='); </script>
  1. The site ID (5016360) is already embedded in the script
  2. Click 'Save & open PR'
  3. 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.

Fix: Via admin dashboard at https://beta.ontargetaba.com/admin/head-scripts.html:

  1. Navigate to 'Head Scripts'
  2. Log into WhatConverts dashboard (account 159348) and retrieve your tracking snippet
  3. Typically looks like:
<script type="text/javascript" src="https://app.whatconverts.com/static/tracking_code/[YOUR_ACCOUNT_ID]/whatconverts_tracking.js"></script>
  1. Paste into the head-scripts textarea
  2. Click 'Save & open PR'
  3. Merge to deploy
  4. 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:

  1. At line 281, change the finish() function to:

function finish() { if (window.gtag) { gtag('event', 'generate_lead', { 'event_category': 'engagement', 'event_label': state.intent || 'unknown', 'value': 1 }); gtag('event', 'view_form', { 'form_id': (ROUTES[state.intent]?.jot || 'unknown') }); } go(4); }

  1. This fires:

    • 'generate_lead' event when the user completes the triage (maps to GA4 conversion)
    • 'view_form' event with the Jotform ID for funnel analysis
  2. Then in Google Analytics 4, mark 'generate_lead' as a conversion event in Admin > Events > Mark as conversion

  3. 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';

  gtag('event', 'form_submission', {
    'event_category': 'engagement',
    'event_label': formType,
    'form_id': JotForm.formId
  });
}
return true;

}); }

Then in Google Analytics 4:

  1. Admin > Events > Mark 'form_submission' as a conversion
  2. Create an event filter to split by 'event_label' (contact, intake_form, autism_testing)
  3. 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.

Fix: Change og:image meta content to absolute URL: content='https://ontargetaba.com/assets/images/footerImg.png'. Also update twitter:image on line 18 to be absolute.

Blog post JSON-LD schema references broken logo URL

Severity: medium Where: blog/post.html, line 394 (dynamically generated)

The BlogPosting JSON-LD publisher logo URL is hardcoded to 'https://ontargetaba.com/wp-content/uploads/2022/04/footerImg.png', the old WordPress path. This breaks the schema and results in broken logo references in SERP rich results.

Fix: Update the hardcoded logo URL in blog/post.html JavaScript (around line 394) to use the correct migrated asset path: 'https://ontargetaba.com/assets/images/footerImg.png'.

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).

Image Accessibility

Insurance logo images with empty alt attributes

Severity: low Where: C:\Users\nates\Downloads\Claude\Utah Autism Testing\website\index.html, lines 1512-1517

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.

Fix: Update the blog post template to always use absolute image URLs: set ogImg to 'https://ontargetaba.com/assets/og/blog-' + slug + '.svg' and fallbackImg to 'https://ontargetaba.com/assets/images/footerImg.png'.

Internal Linking & Cross-Navigation

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.

Legal & Compliance

Privacy policy contains placeholder email addresses

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

Severity: low Where: C:\Users\nates\Downloads\Claude\Utah Autism Testing\website\index.html, lines 632, 1504-1517

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

Severity: low Where: C:\Users\nates\Downloads\Claude\Utah Autism Testing\website\contact.html (line 490)

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

Severity: low Where: C:\Users\nates\Downloads\Claude\Utah Autism Testing\website\index.html:1504-1515

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.

Fix: Add integrity hashes to CDN script tags: <script src='https://cdn.jsdelivr.net/npm/marked/marked.min.js' integrity='sha384-...'></script> and same for dompurify

Prefers Reduced Motion

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.'

Progressive Web App

Missing Web App Manifest and Favicon

Severity: low Where: Root directory (C:\Users\nates\Downloads\Claude\Utah Autism Testing\website\)

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.

Fix: Replace all instances of 'https://ontargetaba.com/wp-content/uploads/2022/04/footerImg.png' with the correct path to the migrated image asset (likely https://ontargetaba.com/assets/images/footerImg.png or equivalent on the new site). Check all JSON-LD blocks across every HTML file.

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.

Fix: Update all JSON-LD image URLs from 'https://ontargetaba.com/wp-content/uploads/...' to 'https://ontargetaba.com/assets/images/footerImg.png' or '/assets/images/footerImg.png'. This should be done by the inject-seo.py or build script to avoid hardcoding in templates.

SEO & Indexability

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

Severity: low Where: C:\Users\nates\Downloads\Claude\Utah Autism Testing\website\blog\post.html:9-19

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.

Fix: Add CSP header to _headers: Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net; img-src 'self' data: https:; style-src 'self' 'unsafe-inline';

Missing Strict-Transport-Security (HSTS) header

Severity: high Where: _headers configuration file

_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.net https://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.

Fix: Add CSP header to _headers file: Content-Security-Policy: default-src 'self'; script-src 'self' https://form.jotform.com https://cdn.jsdelivr.net; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data: https://fonts.bunny.net;

Missing critical security headers in _headers

Severity: medium

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.net https://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.

Third-Party Integration

Third-party scripts (marked.js, dompurify) lack Subresource Integrity (SRI) checksums

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.

Fix: Add integrity attributes: <script src='https://cdn.jsdelivr.net/npm/marked/marked.min.js' integrity='sha384-...' crossorigin='anonymous'></script> and similar for dompurify

Jotform script embeds lack integrity verification

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

Third-party scripts (marked.js, dompurify) lack Subresource Integrity (SRI) checks

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.

Fix: Add integrity hashes to script tags: <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js" integrity="sha384-..." crossorigin="anonymous"></script> (generate hashes from the CDN)

Title Length & Uniqueness

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

Severity: low Where: C:\Users\nates\Downloads\Claude\Utah Autism Testing\website\contact.html, autism-testing.html, pre-intake-form.html

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.