Compare commits

...
Author SHA1 Message Date
DeCentN2Madness 96d3c138f6 refactor(migration): remove obsolete mirror extraction tooling
- delete extraction scripts that depended on the removed local mirror
- remove stale extract npm scripts and migration-brief references
- drop the old mirror ignore rule from .gitignore
- update reports and redirect reasons to use live-source wording
2026-07-13 12:49:58 -07:00
DeCentN2Madness 6648d1d7d5 fix(seo): noindex staging builds by default
- add PUBLIC_ALLOW_INDEXING as an explicit production indexing opt-in
- emit robots meta noindex,nofollow unless indexing is enabled
- generate robots.txt from the same indexing flag
- document staging SEO behavior for migration preview hosts
2026-07-13 12:48:14 -07:00
DeCentN2Madness 9c391c8c05 refactor(component): rename contact obfuscation component 2026-07-02 15:14:51 -07:00
DeCentN2Madness 77b83f5d21 feat(page): match live contact page layout 2026-07-02 15:13:52 -07:00
DeCentN2Madness 8c9c581cc0 feat(brand): add and switch to cog favicon 2026-07-02 13:31:04 -07:00
DeCentN2Madness 46090e9159 chore(docs): remove local mirror references, use live site only
Deleted the www.azinstitute4autism.com mirror directory and scrubbed all
references to it from AGENTS.md, MIGRATION_BRIEF.md, www/README.md, and
www/original/README.md. The live public site is now the sole source of
truth with no local mirror fallback.
2026-07-02 13:31:04 -07:00
DeCentN2Madness fd8e12f925 feat(component): add email obfuscation component 2026-07-02 13:31:04 -07:00
DeCentN2Madness b73e92a8a4 chore(img): add higher resolution image to social club page 2026-06-30 20:20:34 -07:00
DeCentN2Madness a3f7f90fdc fix(layout): allow images up to 300x300 in services page contents 2026-06-30 20:14:17 -07:00
DeCentN2Madness a39caf1a34 fix(content): clarify aba therapy section heading 2026-06-30 15:31:53 -07:00
DeCentN2Madness 38c1f78d4f chore(ai): 🔨 move codex command flag to --profile from --profile-2 2026-06-30 15:11:30 -07:00
DeCentN2Madness 5cbe055c34 fix(styles): adjust service page image margin and prose heading spacing
Removes bottom margin from floated service page images (pages.css) and
tightens h2/h3 top margin in prose from 2em to 1.75em (blog.css).
2026-06-25 20:59:40 -07:00
DeCentN2Madness 2f208aa6e4 fix(content): add center prop to MDX page buttons
Adds center to every <Button> in content MDX files that previously relied
on the removed .button-center global class. Also includes minor content
cleanups in the same files: eyebrow markup for section labels in
aba-therapy.mdx, and removal of extra blank lines between list items in
learner-social-club.mdx.
2026-06-25 20:59:11 -07:00
DeCentN2Madness c6202d2ac6 refactor(button): consolidate button styles into Button.astro
Extends Button.astro with a center prop, native <button> element support
(type, disabled, aria-describedby), harmonised padding (.65rem), and
transition. Removes global .button, .button-small, .button-light, and
.button-center rules from components.css, layout.css, and typography.css.
Converts all inline class="button" usages across Header, HomeHero,
HomeFinancialHelp, HomeServicesIntro, FormShell, TeamPage, CareersPage,
and FaqPage to the <Button> component. Updates three scoped style
selectors to :global() to reach the child component's elements.
2026-06-25 20:59:02 -07:00
DeCentN2Madness a3f666c974 refactor(styles): move service-page rules into a dedicated pages stylesheet
- keep blog.css focused on blog/article styling only
- move the existing .service-page rules unchanged into pages.css
- import pages.css from the shared global stylesheet
- preserve the current service-page layout and responsive behavior
2026-06-22 11:08:22 -07:00
DeCentN2Madness 4a7045a1cb refactor(cleanup): remove unused components and stale docs
- delete components that no longer have any references in the current source tree
- remove the stale Link component example from the README
- drop the leftover service-page heading rule from the blog stylesheet
- keep the repo aligned with the current component graph instead of historical leftovers
2026-06-20 12:07:06 -07:00
DeCentN2Madness a048d395f7 refactor(typography): standardize eyebrow labels across page shells
- consolidate the shared eyebrow label styling into the global typography layer
- replace the old script-label usage in page hero, home hero, FAQ, and team shells
- keep the next heading flush under the label across all affected page types
2026-06-20 12:05:24 -07:00
DeCentN2Madness 9398321fac refactor(components): separate page shells from reusable components
Additional info:
  - move page-specific shells into src/components/pages/
  - keep reusable building blocks and home sections in their existing component folders
  - update PageLayout and the home route imports to the new page-shell paths
  - preserve current page behavior while making the component tree easier to navigate
2026-06-19 21:03:27 -07:00
DeCentN2Madness d348174f95 fix(typography): improve global link colour and hover state
Changes the base link rule from a plain blue primary colour to the
site's accent orange (--color-accent-strong) with no underline, matching
the live site's link treatment. Adds a :hover/:active state that shifts
to the lighter accent, and carves out a.button so CTA buttons keep their
primary-colour label text.
2026-06-17 14:22:47 -07:00
DeCentN2Madness d0d8222e40 feat(faqs): wire FaqPage component and reformat faqs.mdx content
PageLayout now imports FaqPage and routes the faqs slug through it,
passing the page content as a slot (accordion and search are handled by
the component). Also passes subtitle="FAQs" to PageHero for the split
title/subtitle hero treatment.

faqs.mdx is cleaned up to match what FaqPage.astro expects as input:
- removes the old Button import and the duplicate h2 headings that are
  now rendered by the component itself
- reformats all answers from loose markdown text to explicit HTML
  elements (p, ul, ol, h4, h5) so the accordion parser receives
  well-formed nodes rather than flat text runs
2026-06-17 14:22:37 -07:00
DeCentN2Madness ed2bda5c02 feat(page-hero): add optional subtitle prop with mobile heading backdrop
Extends PageHero with a subtitle?: string prop rendered as an h2 beneath
the h1. On image-backed heroes the heading group gets a semi-transparent
white backdrop (display: inline-block; background: #fffb) so the text
stays readable against busy photography at mobile widths. Subtitle h2
inherits the page-hero heading font at a slightly smaller fluid size.
2026-06-17 14:21:28 -07:00
DeCentN2Madness 96d829accb fix(faqs): add orange focus ring to search input matching live site
The live site applies border-color #ffbd59 (--color-accent) to the
search input on focus with no additional outline. Adds:

  .hs-search-field__input:focus { border-color: var(--color-accent); outline: none; }

Also applies button-center to the secondary CTA button and removes the
now-redundant text-align/max-width rules from .faq-cta-secondary
(centering is handled by the button-center utility).
2026-06-17 14:17:59 -07:00
DeCentN2Madness 67951fa031 refactor(faqs): replace decorative search icon with live site button-inline form
The original search field used a decorative <div> with an outline SVG
icon floating outside the <form> — a HubSpot pattern that made the icon
non-interactive and the label invisible only by luck.

Replaces it with the structure the live site actually uses:
- hs-search-field__bar--button-inline with a real <button type="button">
  containing the filled FA-style magnifying-glass SVG from the live page
- Flexbox form so the input and button are proper flex siblings rather
  than one element positioned over another
- Rectangular 4px-radius border matching the live widget (was pill 999px)
- Defines .sr-only locally so the label is correctly hidden
- Changes name from "term" to "q" and placeholder to "Search" to match
2026-06-17 14:16:57 -07:00
DeCentN2Madness c98a51795d feat(faqs): add FaqPage component with accordion, search, and CTA sections
Introduces FaqPage.astro, a client-side component that:
- Parses MDX-sourced h3/answer pairs into animated <details> accordions
  (single-open, smooth height transitions)
- Provides a live text filter that hides non-matching questions as the
  user types
- Includes a pill-shaped HubSpot-style search field with decorative icon
- Closes with two CTA bands (primary blue and secondary white)
2026-06-17 14:12:38 -07:00
DeCentN2MadnessandClaude Sonnet 4.6 95d78c22eb refactor(ar): replace Hero with HomeHero on Arabic homepage and delete Hero.astro
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 17:29:23 -07:00
DeCentN2MadnessandClaude Sonnet 4.6 05f5c59721 feat(careers): add structured CareersPage component and wire it into PageLayout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 17:29:16 -07:00
DeCentN2MadnessandClaude Sonnet 4.6 a498dc59d7 feat(styles): remove cream-section alias, add button-center utility, make h3 font-size fluid
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 17:29:08 -07:00
DeCentN2MadnessandClaude Sonnet 4.6 4a2087d4e3 refactor(styles): replace cream-section with section-tint in home components and TeamPage
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 17:29:02 -07:00
DeCentN2Madness 5e75ace58d feat(team): add tinted band and tagline to ABA Clinic Management section
- move --color-tint background from Employment Opportunities to ABA Clinic Management
- restructure group template so section (not inner container) carries background, allowing it to span full width
- add live site italic tagline "Applied Behavior Analysis '(ABA) at your service'" from source markup
2026-06-16 11:06:32 -07:00
85 changed files with 1956 additions and 1443 deletions
-3
View File
@@ -58,6 +58,3 @@ temp/
.DS_Store
Thumbs.db
Desktop.ini
# Read-only wget mirror used as migration source material
/www.azinstitute4autism.com/
+8 -32
View File
@@ -13,42 +13,20 @@ content only after verifying it against the live public site.
## Project
This repository contains a mirrored copy of the Arizona Institute for Autism website.
Raw mirror source:
```txt
./www.azinstitute4autism.com
```
New Astro site target:
```txt
./www
```
The mirror was created with:
```sh
wget --mirror --page-requisites --adjust-extension --convert-links --no-parent https://www.azinstitute4autism.com
```
Do not modify, move, rename, or delete `./www.azinstitute4autism.com`.
Treat the mirror as read-only raw source material.
The live public website is the single source of truth:
```txt
https://www.azinstitute4autism.com
```
Use the mirror as a convenient local extraction and inspection aid only. The
mirror may contain stale, missing, rewritten, or incorrectly downloaded
content and assets. Verify content, design, URLs, metadata, assets, and
responsive behavior against the live public site before treating them as
authoritative. When the live site and mirror disagree, follow the live site and
document material discrepancies.
Verify content, design, URLs, metadata, assets, and responsive behavior against
the live public site before treating them as authoritative.
## Primary goal
@@ -221,10 +199,9 @@ Favor:
Before implementing a page family or shared component:
1. Inspect the live page and its HTML, CSS, assets, and responsive behavior.
2. Use the mirror to accelerate local extraction and inspection.
3. Resolve any discrepancy in favor of the live public site.
4. Record the source structure and important visual details.
5. Identify which patterns are truly shared and which are page-specific.
2. Resolve any discrepancy in favor of the live public site.
3. Record the source structure and important visual details.
4. Identify which patterns are truly shared and which are page-specific.
After implementation, compare the Astro output with the live public site at
representative desktop and mobile viewport widths. Check the full page, not
@@ -365,10 +342,9 @@ reports/migration-summary.md
Before large filesystem changes:
1. Inspect the live public site.
2. Inspect the mirror and identify relevant discrepancies.
3. Inspect the relevant source HTML, CSS, and assets in detail.
4. Summarize the implementation and visual-validation plan.
5. Proceed methodically.
2. Inspect the relevant source HTML, CSS, and assets in detail.
3. Summarize the implementation and visual-validation plan.
4. Proceed methodically.
At the end of a task, summarize:
+20 -79
View File
@@ -7,21 +7,15 @@ brief provides detailed implementation guidance. If any wording here appears
to conflict with `AGENTS.md`, follow `AGENTS.md` and correct this brief before
continuing.
This is a fidelity-first migration, not a redesign. The raw mirror is a
convenient local extraction and inspection aid, but the live public site is the
This is a fidelity-first migration, not a redesign. The live public site is the
single content and visual source of truth. The `migration-foundation` branch
may be consulted for nonvisual tooling and recovered content, but it must not
be used as the visual baseline.
## Task
Migrate the public Arizona Institute for Autism website from a mirrored HubSpot-generated static copy into a clean, maintainable Astro + TypeScript site.
Raw mirrored source:
```txt
./www.azinstitute4autism.com
```
Migrate the public Arizona Institute for Autism website into a clean,
maintainable Astro + TypeScript site.
New Astro project target:
@@ -29,12 +23,6 @@ New Astro project target:
./www
```
The raw mirror was created with:
```sh
wget --mirror --page-requisites --adjust-extension --convert-links --no-parent https://www.azinstitute4autism.com
```
There is no HubSpot export, no HubSpot CLI access, and no HubSpot API access.
Use the live public website as the single source of truth:
@@ -43,15 +31,9 @@ Use the live public website as the single source of truth:
https://www.azinstitute4autism.com
```
Use the local mirror as a convenient extraction and inspection aid. It may
contain stale, missing, rewritten, or incorrectly downloaded content and
assets. Verify content, design, URLs, metadata, assets, and responsive behavior
against the live site. When the live site and mirror disagree, follow the live
site and document material discrepancies.
If the live site is temporarily unavailable, work that depends on its authority
must remain explicitly unverified rather than silently treating the mirror as
authoritative.
Verify content, design, URLs, metadata, assets, and responsive behavior against
the live site. If the live site is temporarily unavailable, work that depends
on its authority must remain explicitly unverified.
## High-level goal
@@ -76,10 +58,6 @@ Create a new Astro site in `./www` that is:
Do not modify or delete:
```txt
./www.azinstitute4autism.com
```
Do not depend on:
- HubSpot CLI
@@ -283,13 +261,12 @@ Do not use Tailwind.
For each page family or shared component:
1. Inspect the live page, HTML, CSS, assets, and responsive behavior.
2. Use the mirror to accelerate local extraction and inspection.
3. Resolve discrepancies in favor of the live public site.
4. Record important visual details and identify genuine shared patterns.
5. Implement the page or component in maintainable Astro code.
6. Compare the complete rendered page with the live public site at representative
2. Resolve discrepancies in favor of the live public site.
3. Record important visual details and identify genuine shared patterns.
4. Implement the page or component in maintainable Astro code.
5. Compare the complete rendered page with the live public site at representative
desktop and mobile viewport widths.
7. Fix material visual differences before marking it complete.
6. Fix material visual differences before marking it complete.
Document intentional visual deviations and their reasons in
`reports/migration-summary.md`.
@@ -406,15 +383,11 @@ www/
rtl.css
tools/
extract-site.mjs
crawl-live-site.mjs
audit-links.mjs
generate-redirects.mjs
generate-sitemap.mjs
original/
README.md
reports/
url-inventory.csv
asset-inventory.csv
@@ -427,17 +400,10 @@ www/
migration-summary.md
```
The `original/README.md` file should explain that the raw source mirror lives outside the Astro project at:
```txt
../www.azinstitute4autism.com
```
Do not duplicate the entire raw mirror inside `./www` unless there is a specific reason.
## Initial inspection
Before creating or modifying files, inspect the mirror.
Before creating or modifying files, inspect the live public site and current
project sources.
Look for:
@@ -687,9 +653,8 @@ src/styles/rtl.css
src/styles/global.css
```
Extract the current site's visual language from the live public site, using the
mirrored CSS and HTML as a convenient local aid after checking for discrepancies.
Tokens must encode source values; they must not introduce a replacement visual
Extract the current site's visual language from the live public site. Tokens
must encode source values; they must not introduce a replacement visual
system.
Use design tokens such as:
@@ -837,7 +802,7 @@ It should support:
- hreflang links where translation relationships are known
- JSON-LD/schema when extracted
Preserve metadata from the mirror wherever possible.
Preserve metadata from the live site wherever possible.
Generate:
@@ -890,49 +855,27 @@ src/pages/es/[...slug].astro
Implement routing in a clean, maintainable way.
## Extraction scripts
## Migration utility scripts
Create:
```txt
tools/extract-site.mjs
tools/crawl-live-site.mjs
tools/audit-links.mjs
tools/generate-redirects.mjs
tools/generate-sitemap.mjs
```
### `extract-site.mjs`
This script should:
- scan the mirror
- identify HTML files
- infer URL paths
- identify page type
- identify blog posts
- identify listing pages
- extract title/meta/H1/body/main content
- extract canonical and Open Graph metadata
- extract schema/JSON-LD where present
- convert blog posts to Markdown where feasible
- create page Markdown where useful
- copy/normalize assets
- rewrite local asset references
- create initial reports
The script does not need to be perfect, but it should be useful and documented.
### `crawl-live-site.mjs`
This script should:
- attempt to crawl the live site if internet is available
- discover URLs from sitemap and internal links
- compare discovered URLs against the mirror
- compare discovered URLs against the current site source and generated output
- document missing URLs/assets
- avoid aggressive crawling
- respect the scope of `www.azinstitute4autism.com`
- respect the live-site scope
### `audit-links.mjs`
@@ -1143,7 +1086,6 @@ Add scripts similar to:
"dev": "astro dev",
"build": "astro check && astro build",
"preview": "astro preview",
"extract": "node tools/extract-site.mjs",
"crawl": "node tools/crawl-live-site.mjs",
"audit:links": "node tools/audit-links.mjs",
"generate:sitemap": "node tools/generate-sitemap.mjs",
@@ -1165,7 +1107,6 @@ www/README.md
It should explain:
- what the project is
- where the raw mirror lives
- how the migration was performed
- how to install dependencies
- how to run local dev
@@ -1241,7 +1182,7 @@ Proceed methodically:
2. Read `CLAUDE.md`, if using Claude Code.
3. Read this `MIGRATION_BRIEF.md`.
4. Inspect the live public site.
5. Inspect `./www.azinstitute4autism.com` and identify relevant discrepancies.
5. Inspect the relevant live public site pages and identify discrepancies.
6. Summarize a concise implementation plan.
7. Scaffold Astro in `./www`.
8. Build extraction/reporting scripts.
+1 -1
View File
@@ -9,7 +9,7 @@ if [[ "${1:-}" == "exec" ]]; then
fi
exec codex \
--profile-v2 azi4a2-autonomous \
--profile azi4a2-autonomous \
--strict-config \
--config 'approval_policy="never"' \
--config 'mcp_servers.playwright.default_tools_approval_mode="approve"' \
+1
View File
@@ -1 +1,2 @@
PUBLIC_AIA_API_BASE=https://api.azinstitute4autism.com
PUBLIC_ALLOW_INDEXING=false
+8 -62
View File
@@ -1,8 +1,7 @@
# Arizona Institute for Autism Astro Site
Static Astro + TypeScript migration of the public Arizona Institute for Autism
website. The read-only source mirror is located at
`../www.azinstitute4autism.com`.
website.
## Quick Start
@@ -23,9 +22,6 @@ http://localhost:4321
The development server watches source files and refreshes the browser after
edits. Stop it with `Ctrl+C`.
Do **not** run `npm run extract` during normal editing. Extraction regenerates
migrated Markdown from the raw mirror and can overwrite editorial changes.
## Environment Setup
Requirements:
@@ -42,19 +38,23 @@ cd www
npm install
```
Copy the example environment file when testing the like/view counter:
Copy the example environment file when testing the like/view counter or staging
SEO behavior:
```sh
cp .env.example .env
```
The default value is:
The default values are:
```txt
PUBLIC_AIA_API_BASE=https://api.azinstitute4autism.com
PUBLIC_ALLOW_INDEXING=false
```
The site still renders when that API is unavailable.
The site still renders when the AIA API is unavailable. Migration and staging
builds emit `noindex,nofollow` by default; set `PUBLIC_ALLOW_INDEXING=true`
only for production builds on the canonical domain.
## View And Build
@@ -198,15 +198,6 @@ Available `Button` variants are `filled` (default), `outlined`, and `light`.
Available sizes are `default` and `small`. A `_blank` target automatically adds
`rel="noopener noreferrer"`.
Use the optional `Link` component only when an inline link needs an explicit
variant or target:
```mdx
import Link from '../../../components/Link.astro';
<Link href="/library" variant="strong">Browse the library</Link>
```
Normal Markdown links remain preferred for ordinary page and article content.
### Front Matter CMS
@@ -331,51 +322,6 @@ Edit the component's `label`, `heading`, and `items` data in the post. Keep
answer. Shared FAQ markup, styling, interaction, and schema generation live in
`src/components/FAQAccordion.astro`.
## Mirror Extraction Workflow
The mirror is read-only source material. Never modify:
```txt
../www.azinstitute4autism.com
```
Only rerun extraction when intentionally refreshing migrated content from the
mirror. Commit or back up editorial changes first.
Dependency-free extraction:
```sh
npm run extract
```
Full Cheerio/Turndown extraction:
```sh
npm run extract:full
```
After extraction:
```sh
npm run extract:blog-footers
npm run generate:sitemap
npm run generate:redirects
npm run build
npm run audit:links
npm run audit:images
npm run audit:blog
```
Extraction regenerates content files, copied assets, and inventories. Review
the complete Git diff before accepting it. Extraction may recreate FAQ-bearing
posts and CTA-bearing pages as Markdown; restore their `FAQAccordion` and
`Button` MDX blocks before accepting the result. `npm run audit:blog` reports
plain Markdown FAQ sections.
`npm run extract:blog-footers` preserves the extracted previous/next and
similar-post relationships in `src/data/blog-footers.json`, then removes those
footer fragments from article prose so `BlogPostFooter.astro` can render them.
## Reports And Utilities
Migration reports are stored in `reports/`.
-10
View File
@@ -1,10 +0,0 @@
# Raw Source Mirror
The read-only wget mirror used for this migration lives outside this Astro
project at:
```txt
../www.azinstitute4autism.com
```
Run `npm run extract` to regenerate content and inventories from that source.
-2
View File
@@ -10,8 +10,6 @@
"build": "astro build",
"build:sandbox": "NODE_OPTIONS=--require=./tools/localhost-dns.cjs astro build",
"preview": "astro preview",
"extract": "node tools/extract-fallback.mjs",
"extract:full": "node tools/extract-site.mjs",
"extract:blog-footers": "node tools/extract-blog-footers.mjs",
"crawl:live": "node tools/crawl-live-site.mjs",
"audit:links": "node tools/audit-links.mjs",
+28
View File
@@ -0,0 +1,28 @@
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 100 100"
width="420" height="420">
<defs>
<g id="tooth" transform="translate(50,50)" fill="#ff914d">
<path d="m0 50 h1 q8 0 8 -6 q0 -8 8 -10 h-20"/>
<path d="m0 50 h1 q8 0 8 -6 q0 -8 8 -10 h-20" transform="scale(-1,1)"/>
</g>
</defs>
<g id="gear">
<circle cx="50" cy="50" r="32" stroke="#ff914d" stroke-width="10" fill="none"/>
<g id="teeth">
<use xlink:href="#tooth" transform="rotate( -10 50 50 )"/>
<use xlink:href="#tooth" transform="rotate( 35 50 50 )"/>
<use xlink:href="#tooth" transform="rotate( 80 50 50 )"/>
<use xlink:href="#tooth" transform="rotate( 125 50 50 )"/>
<use xlink:href="#tooth" transform="rotate( 170 50 50 )"/>
<use xlink:href="#tooth" transform="rotate( 215 50 50 )"/>
<use xlink:href="#tooth" transform="rotate( 260 50 50 )"/>
<use xlink:href="#tooth" transform="rotate( 305 50 50 )"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 58 KiB

-3
View File
@@ -1,3 +0,0 @@
User-agent: *
Allow: /
Sitemap: https://www.azinstitute4autism.com/sitemap.xml
+8 -9
View File
@@ -1,14 +1,14 @@
# Cleanup Log
- Replaced HubSpot-generated wrappers, inline styles, scripts, analytics, and modules with reusable Astro components.
- Preserved canonical clean URLs while excluding local mirror query-string and AMP duplicates.
- Preserved canonical clean URLs while excluding HubSpot query-string and AMP duplicates.
- Consolidated responsive `hs-fs` image variants to canonical `hubfs` assets where available.
- Excluded mirrored HubSpot CSS and JavaScript from the new public asset package.
- Self-hosted canonical images, SVGs, fonts, and PDFs from the mirror.
- Excluded HubSpot CSS and JavaScript from the new public asset package.
- Self-hosted canonical images, SVGs, fonts, and PDFs where practical.
- Recreated navigation and footer from structured JSON.
- Recreated visible contact and consultation forms as static accessible HTML.
- Used the source palette: AIA blue `#254080`, dark blue, straw yellow, orange, and teal.
- Used the live site's locally mirrored Playfair Display headings, Rubik body copy, and Caveat accent font.
- Used self-hosted Playfair Display headings, Rubik body copy, and Caveat accent font.
- Added multilingual collection and route structure with Arabic RTL support.
- Kept all extracted source content editable as Markdown.
- Replaced the initial generic rounded-card visual system with live-derived
@@ -21,18 +21,17 @@
- Reorganized the homepage source into named `home:` frontmatter blocks so the
content is easier to inspect and edit without changing the rendered layout.
- Replaced the extracted linear team article with a dedicated live-derived team grid.
- Repaired mirror-rewritten PDF, lightbox, CTA, and relative content links.
- Repaired rewritten PDF, lightbox, CTA, and relative content links.
- Limited language-switcher choices to translations that have generated routes.
- Verified and implemented the current production likes/views API contract.
- Recovered 18 live page-banner assets that the wget mirror missed because
HubSpot injected them through malformed inline `background-image` styles.
- Recovered 18 live page-banner assets that HubSpot injected through malformed
inline `background-image` styles.
- Restored the live library banner and consultation form section background.
- Added a mapped-page-imagery audit to prevent missing visual assets from
silently passing the source link audit.
- Removed duplicated article titles, author/date blocks, and featured images
from all 65 blog Markdown bodies; these elements are rendered by the shared
blog-post layout. Updated both extractors and added a blog-content audit to
prevent recurrence.
blog-post layout. Added a blog-content audit to prevent recurrence.
- Converted 19 FAQ-bearing blog posts to MDX and restored a reusable
`FAQAccordion.astro` component that owns the live-style accordion and
matching `FAQPage` JSON-LD.
+28 -12
View File
@@ -3,9 +3,9 @@
## Status
A maintainable Astro migration and substantial fidelity-first pass are
implemented. The live public site, not the wget mirror, was used as the
authority for the shared shell, English and Spanish homepages, service-page
family, team page, library indexes, and blog-post family.
implemented. The live public site was used as the authority for the shared
shell, English and Spanish homepages, service-page family, team page, library
indexes, and blog-post family.
## Migrated Content and Assets
@@ -15,9 +15,9 @@ family, team page, library indexes, and blog-post family.
- 167 self-hosted images, 20 font files, and two PDF downloads.
- Current June and May 2026 English library articles are included.
The raw mirror remains untouched. HubSpot-generated wrappers, analytics,
scripts, CSS, query-language duplicates, AMP variants, pagination, and author
archive variants are not carried into the Astro implementation.
HubSpot-generated wrappers, analytics, scripts, CSS, query-language duplicates,
AMP variants, pagination, and author archive variants are not carried into the
Astro implementation.
## Live-Source Fidelity Pass
@@ -44,6 +44,9 @@ Implemented fidelity work includes:
- Restored the consultation form section's injected background image.
- Added a dedicated live-derived team card grid rather than presenting the
extracted team content as a generic article.
- Rebuilt the contact page around the live two-column content/form section and
full-width Leaflet map while retaining local clickable obfuscated phone
links.
- Rebuilt library indexes and blog-post presentation around the live sidebar,
article list, byline, featured-image, and counter patterns.
- Converted FAQ-bearing posts to MDX and recreated their live-style accordions
@@ -52,7 +55,7 @@ Implemented fidelity work includes:
links, fawn backgrounds, spacing, rounded presentation, and quote-mark SVG.
- Replaced generic oversized cards, rounded controls, and marketing heroes
with live-derived typography, palette, widths, spacing, and compact controls.
- Corrected material live/mirror discrepancies found during the pass,
- Corrected material live-source discrepancies found during the pass,
including current homepage ESA copy, testimonial content, and ABA copy.
## URLs and Redirects
@@ -77,6 +80,12 @@ backend and spam-protection TODO comments. Submission is intentionally
disabled. Appointment and enrollment calls to action route to the static
consultation page rather than retaining the production Jotform backend.
## External Dependencies
- The contact page map intentionally matches the live Leaflet/OpenStreetMap
implementation and loads Leaflet from `unpkg.com` plus map tiles from
`tile.openstreetmap.org`.
## Multilingual
- Spanish has the full live-derived homepage, translated service pages,
@@ -105,6 +114,9 @@ unavailable.
- Canonicals, Open Graph tags, Twitter card tags, semantic titles, and global
organization JSON-LD are emitted by shared layouts.
- Organization JSON-LD intentionally omits raw `telephone` and `email` fields;
visible contact details use the Astro obfuscation component to avoid exposing
plain email and phone targets in rendered HTML.
- Library-index canonicals and current visible page H1s are explicitly set.
- Extracted image alt text is retained where available.
- Semantic landmarks, skip link, labeled forms, keyboard-operable navigation,
@@ -125,11 +137,15 @@ unavailable.
- `npm run generate:sitemap`: passed; generated 97 URLs.
- `npm run generate:redirects`: passed.
- All migration `.mjs` tools and the sandbox DNS helper pass `node --check`.
- The Astro compiler parsed all 33 `.astro` files successfully.
- The Astro compiler parsed all 49 `.astro` files successfully.
- `npm run build`: blocked before compilation because this autonomous sandbox
denies `/etc/hosts`, causing `getaddrinfo EAI_AGAIN localhost`.
- `npm run build:sandbox`: bypasses that DNS lookup and reaches Vite, then the
sandbox rejects esbuild's required child process with `spawn EPERM`.
cannot resolve `localhost`, causing `getaddrinfo EAI_AGAIN localhost`.
- `npm run build:sandbox`: passed; generated 97 static pages.
- Live contact-page structure was inspected in browser at a 1280px desktop
viewport; local generated HTML was checked for the contact layout, Leaflet
assets, map container, and obfuscated clickable phone script. Local browser
rendering was blocked because the Playwright browser cannot reach shell
loopback servers and blocks `file:` URLs in this environment.
- Nix shell verification and `npm audit` retrieval were blocked by sandbox
proxy/cache network resets.
@@ -141,7 +157,7 @@ final production-build proof.
- Perform full-page desktop and mobile visual comparisons for page-specific
layouts beyond the completed homepage, team, service, library, and article
families.
- Editorially review extracted long-form content for remaining mirror artifacts,
- Editorially review extracted long-form content for remaining extraction artifacts,
stale phone/email references, and heading hierarchy.
- Review all source alt text, keyboard behavior, screen-reader output, and
contrast with accessibility tooling.
+3 -3
View File
@@ -1,4 +1,4 @@
from,to,status,reason
"/aba","/aba-therapy",301,"Brief alias to preserved mirror URL"
"/autismevaluations","/autism-evaluations",301,"Brief alias to preserved mirror URL"
"/learnersocialclub","/learner-social-club",301,"Brief alias to preserved mirror URL"
"/aba","/aba-therapy",301,"Brief legacy alias to preserved URL"
"/autismevaluations","/autism-evaluations",301,"Brief legacy alias to preserved URL"
"/learnersocialclub","/learner-social-club",301,"Brief legacy alias to preserved URL"
1 from to status reason
2 /aba /aba-therapy 301 Brief alias to preserved mirror URL Brief legacy alias to preserved URL
3 /autismevaluations /autism-evaluations 301 Brief alias to preserved mirror URL Brief legacy alias to preserved URL
4 /learnersocialclub /learner-social-club 301 Brief alias to preserved mirror URL Brief legacy alias to preserved URL
+2 -2
View File
@@ -18,8 +18,8 @@
## Cleanup
- HubSpot `?hsLang=...` mirror duplicates are excluded from generated routes.
- AMP mirror variants are excluded because Astro pages are responsive and static.
- HubSpot `?hsLang=...` duplicates are excluded from generated routes.
- AMP variants are excluded because Astro pages are responsive and static.
- Blog pagination and author archive variants are not preserved as separate generated pages.
- Brief aliases `/aba`, `/autismevaluations`, and `/learnersocialclub` redirect to preserved source URLs.
+34 -11
View File
@@ -1,10 +1,14 @@
---
interface Props {
href: string;
href?: string;
variant?: 'filled' | 'outlined' | 'light';
size?: 'default' | 'small';
center?: boolean;
target?: '_blank' | '_self';
rel?: string;
type?: 'submit' | 'button' | 'reset';
disabled?: boolean;
'aria-describedby'?: string;
class?: string;
}
@@ -12,20 +16,28 @@ const {
href,
variant = 'filled',
size = 'default',
center = false,
target,
rel = target === '_blank' ? 'noopener noreferrer' : undefined,
type,
disabled,
'aria-describedby': ariaDescribedby,
class: className
} = Astro.props;
const classes = [
'content-button',
`content-button--${variant}`,
{ 'content-button--small': size === 'small', 'content-button--center': center },
className
];
---
<a
class:list={['content-button', `content-button--${variant}`, { 'content-button--small': size === 'small' }, className]}
href={href}
target={target}
rel={rel}
>
<slot />
</a>
{href ? (
<a class:list={classes} href={href} target={target} rel={rel}><slot /></a>
) : (
<button class:list={classes} type={type} disabled={disabled} aria-describedby={ariaDescribedby}><slot /></button>
)}
<style>
.content-button {
@@ -36,8 +48,9 @@ const {
display: inline-block;
font-weight: 400;
line-height: 1.5;
padding: .8rem 1.2rem;
padding: .65rem 1.2rem;
text-decoration: none;
transition: .3s linear all;
}
.content-button:hover,
@@ -65,6 +78,16 @@ const {
.content-button--small {
font-size: .9rem;
padding: .5rem 1rem;
}
.content-button--center {
margin-block: 2.5rem;
margin-inline-start: 50%;
translate: -50%;
}
button.content-button {
cursor: pointer;
font: inherit;
}
</style>
-11
View File
@@ -1,11 +0,0 @@
<section class="cta-band">
<div class="container split">
<div><p class="eyebrow">Start the conversation</p><h2>Find the right support for your learner.</h2></div>
<a class="button button-light" href="/client-consultation">Schedule a Free Consultation</a>
</div>
</section>
<style>
.cta-band { background: var(--color-primary); color: white; padding-block: var(--space-xl); }
.cta-band h2, .cta-band .eyebrow { color: white; margin: 0; }
</style>
+84
View File
@@ -0,0 +1,84 @@
---
import Button from './Button.astro';
import ContactObfuscation from './ContactObfuscation.astro';
interface Props {
title?: string;
}
const { title = 'Contact AIA' } = Astro.props;
---
<form class="contact-form-card" method="post" action="">
<!-- TODO: Wire this form to Netlify Forms, a custom API endpoint, or another backend service. -->
<!-- TODO: Add spam protection before production launch. -->
<h3 class="contact-form-card__title">{title}</h3>
<div class="contact-form-card__fields">
<label>Parent or guardian name<input name="name" autocomplete="name" /></label>
<label>Email<input type="email" name="email" autocomplete="email" /></label>
<label>Phone<input type="tel" name="phone" autocomplete="tel" /></label>
<label>Service of interest<select name="service"><option>ABA Therapy</option><option>Autism Evaluation</option><option>Learner Social Club</option><option>Other</option></select></label>
<label>How can we help?<textarea name="message" rows="5"></textarea></label>
<Button type="submit" disabled aria-describedby="contact-form-note" class="contact-form-card__submit">Submit</Button>
<p id="contact-form-note" class="contact-form-card__note">Online submission is not yet connected. Please call <ContactObfuscation phone="(480) 687-7099" hrefValue="+14806877099" obfuscatedText="9907-786 (084)" />.</p>
</div>
</form>
<style>
.contact-form-card {
background: white;
border-radius: 4px;
box-shadow: 0 4px 4px 0 rgb(81 81 81 / 4%), 0 4px 16px 0 rgb(81 81 81 / 8%);
margin: 0 auto;
max-width: 600px;
overflow: hidden;
}
.contact-form-card__title {
border-bottom: 1px solid #f1f1f1;
margin: 0;
padding: 1.2rem 1.5rem;
text-align: center;
}
.contact-form-card__fields {
display: grid;
gap: 1rem;
padding: 1.5rem;
}
.contact-form-card label {
display: grid;
font-weight: 700;
gap: .4rem;
}
.contact-form-card input,
.contact-form-card select,
.contact-form-card textarea {
background: #fcf9f5;
border: 1px solid var(--color-border);
border-radius: 4px;
color: var(--color-primary);
font: inherit;
padding: .8rem;
width: 100%;
}
.contact-form-card textarea {
resize: vertical;
}
.contact-form-card :global(.contact-form-card__submit) {
justify-self: center;
max-width: 375px;
width: 100%;
}
.contact-form-card__note {
color: var(--color-muted);
font-size: .9rem;
margin: 0;
text-align: center;
}
</style>
@@ -0,0 +1,58 @@
---
interface Props {
email?: string;
phone?: string;
hrefValue?: string;
linkText?: string;
obfuscatedText?: string;
class?: string;
id?: string;
}
const {
email,
phone,
hrefValue,
linkText,
obfuscatedText,
class: className,
id
} = Astro.props;
if ((email ? 1 : 0) + (phone ? 1 : 0) !== 1) {
throw new Error('ContactObfuscation requires exactly one of email or phone.');
}
const contactType = email ? 'email' : 'phone';
const sourceValue = email ?? phone ?? '';
const linkValue = linkText ?? sourceValue;
const fallbackText = obfuscatedText ?? sourceValue;
const protocol = email ? 'mailto' : 'tel';
const telHrefValue = hrefValue ?? sourceValue.replace(/[^\d+]/g, '');
const targetValue = email ? (hrefValue ?? sourceValue) : telHrefValue;
const encode = (value: string) => Array.from(value, (character) => character.charCodeAt(0));
const encodedTarget = encode(targetValue);
const encodedText = encode(linkValue);
const spanId = id ?? `${contactType}-obfuscation-${Math.random().toString(36).slice(2, 10)}`;
---
<span id={spanId} class={className}>{fallbackText}</span><script type="text/javascript" define:vars={{ encodedTarget, encodedText, protocol, spanId }}>
(() => {
const contactSpan = document.getElementById(spanId);
if (!contactSpan) return;
const decode = (encodedValue) => encodedValue
.map((characterCode) => String.fromCharCode(characterCode))
.join('');
const target = decode(encodedTarget);
const text = decode(encodedText);
const link = document.createElement('a');
link.href = `${protocol}:${target}`;
link.textContent = text;
contactSpan.textContent = '';
contactSpan.appendChild(link);
})();
</script>
+3 -2
View File
@@ -1,4 +1,5 @@
---
import ContactObfuscation from './ContactObfuscation.astro';
import site from '../data/site.json';
const year = new Date().getFullYear();
const { lang = 'en' } = Astro.props;
@@ -31,11 +32,11 @@ const labels = lang === 'es'
<div class="footer-contact">
<div class="footer-contact-item">
<img src="/assets/images/phone-blue.svg" alt="" aria-hidden="true" width="24" height="24" />
<a href={`tel:${site.phone.replace(/\D/g, '')}`}>{site.phone}</a>
<ContactObfuscation phone={site.phone} hrefValue="+14806877099" obfuscatedText="9907-786 (084)" />
</div>
<div class="footer-contact-item">
<img src="/assets/images/envelope-blue.svg" alt="" aria-hidden="true" width="24" height="24" />
<a href={`mailto:${site.email}`}>{site.email}</a>
<ContactObfuscation email={site.email} obfuscatedText="moc.msitua4etutitsniza@ofni" />
</div>
<div class="footer-contact-item">
<img src="/assets/images/location-blue.svg" alt="" aria-hidden="true" width="24" height="24" />
+4 -2
View File
@@ -1,4 +1,6 @@
---
import Button from './Button.astro';
import ContactObfuscation from './ContactObfuscation.astro';
const { title = 'Schedule Your Free Consultation', showBackground = false } = Astro.props;
---
<section class:list={['form-section', { 'form-section-background': showBackground }]} aria-labelledby="form-title">
@@ -12,8 +14,8 @@ const { title = 'Schedule Your Free Consultation', showBackground = false } = As
<label>Phone<input type="tel" name="phone" autocomplete="tel" /></label>
<label>Service of interest<select name="service"><option>ABA Therapy</option><option>Autism Evaluation</option><option>Learner Social Club</option><option>Other</option></select></label>
<label>How can we help?<textarea name="message" rows="5"></textarea></label>
<button class="button" type="submit" disabled aria-describedby="form-note">Submit</button>
<p id="form-note" class="form-note">Online submission is not yet connected. Please call (480) 687-7099.</p>
<Button type="submit" disabled aria-describedby="form-note">Submit</Button>
<p id="form-note" class="form-note">Online submission is not yet connected. Please call <ContactObfuscation phone="(480) 687-7099" hrefValue="+14806877099" obfuscatedText="9907-786 (084)" />.</p>
</form>
</div>
</section>
+3 -2
View File
@@ -1,4 +1,5 @@
---
import Button from './Button.astro';
import LanguageSwitcher from './LanguageSwitcher.astro';
import MainNav from './MainNav.astro';
import MobileNav from './MobileNav.astro';
@@ -21,7 +22,7 @@ const utility = lang === 'es'
<MainNav lang={lang} />
<MobileNav lang={lang} />
<LanguageSwitcher />
<a class="button button-small" href="/tour">Tour AIA</a>
<Button href="/tour" size="small">Tour AIA</Button>
</div>
</header>
@@ -33,7 +34,7 @@ const utility = lang === 'es'
.header-inner { align-items: center; display: flex; gap: 1.4rem; min-height: 110px; }
.brand img { width: 150px; }
@media (max-width: 950px) {
.header-inner > .button { display: none; }
.header-inner > :global(.content-button) { display: none; }
}
@media (max-width: 760px) {
.utility-inner { justify-content: center; }
-24
View File
@@ -1,24 +0,0 @@
---
interface Props { title: string; eyebrow?: string; description?: string; image?: string; alt?: string; }
const { title, eyebrow, description, image, alt = '' } = Astro.props;
---
<section class="hero">
<div class="container hero-grid">
<div>
{eyebrow && <p class="eyebrow">{eyebrow}</p>}
<h1>{title}</h1>
{description && <p class="hero-copy">{description}</p>}
<a class="button" href="/client-consultation">Get Started</a>
</div>
{image && <img src={image} alt={alt} loading="eager" />}
</div>
</section>
<style>
.hero { background: var(--color-tint); overflow: hidden; padding-block: clamp(3rem, 7vw, 5rem); }
.hero-grid { align-items: center; display: grid; gap: var(--space-xl); grid-template-columns: 1.05fr .95fr; }
.hero img { max-height: 430px; object-fit: contain; width: 100%; }
@media (max-width: 950px) {
.hero-grid { grid-template-columns: 1fr; }
}
</style>
-13
View File
@@ -1,13 +0,0 @@
---
const logos = ['aetna.png', 'bluecrossblueshield-logo-1.webp', 'logo-ahcccs.webp', 'logo-optum.webp', 'logo-tricare-1.webp', 'united-healthcare-logo-1.webp'];
---
<section class="insurance-strip" aria-labelledby="insurance-title">
<div class="container"><h2 id="insurance-title">Insurance partners</h2><div class="logo-row">{logos.map((logo) => <img src={`/assets/images/${logo}`} alt="" loading="lazy" />)}</div></div>
</section>
<style>
.insurance-strip { background: white; border-block: 1px solid var(--color-border); padding-block: var(--space-lg); text-align: center; }
.insurance-strip h2 { font-size: 1.4rem; }
.logo-row { align-items: center; display: flex; flex-wrap: wrap; justify-content: space-around; }
.logo-row img { filter: grayscale(1); max-height: 55px; max-width: 130px; }
</style>
-42
View File
@@ -1,42 +0,0 @@
---
interface Props {
href: string;
variant?: 'default' | 'strong';
target?: '_blank' | '_self';
rel?: string;
class?: string;
}
const {
href,
variant = 'default',
target,
rel = target === '_blank' ? 'noopener noreferrer' : undefined,
class: className
} = Astro.props;
---
<a
class:list={['content-link', { 'content-link--strong': variant === 'strong' }, className]}
href={href}
target={target}
rel={rel}
>
<slot />
</a>
<style>
.content-link {
color: var(--color-accent-strong);
text-decoration: none;
}
.content-link:hover,
.content-link:focus-visible {
color: var(--color-accent);
}
.content-link--strong {
font-weight: 700;
}
</style>
-8
View File
@@ -1,8 +0,0 @@
---
const { title = 'Scottsdale', address = '8901 E Raintree Dr Ste 160, Scottsdale, AZ 85260' } = Astro.props;
---
<article class="card location-card"><h3>{title}</h3><p>{address}</p><a class="text-link" href={`https://maps.google.com/?q=${encodeURIComponent(address)}`}>Get directions</a></article>
<style>
.location-card { padding: var(--space-lg); }
</style>
+11 -4
View File
@@ -1,33 +1,40 @@
---
interface Props {
title: string;
subtitle?: string;
image?: string;
eyebrow?: string;
constrain?: boolean;
}
const { title, image, eyebrow, constrain = false } = Astro.props;
const { title, subtitle, image, eyebrow, constrain = false } = Astro.props;
---
<header class:list={['page-hero', { 'page-hero--image': image, 'page-hero--constrained': image && constrain }]} style={image ? `--hero-image: url("${image}")` : undefined}>
<div class="container">
<div class="page-hero__heading">
{eyebrow
? <div class="page-hero__copy"><p class="script-label">{eyebrow}</p><h1>{title}</h1></div>
: <h1>{title}</h1>
? <div class="page-hero__copy"><p class="eyebrow">{eyebrow}</p><h1>{title}</h1>{subtitle && <h2>{subtitle}</h2>}</div>
: <><h1>{title}</h1>{subtitle && <h2>{subtitle}</h2>}</>
}
</div>
</div>
</header>
<style>
.page-hero { background: var(--color-tint); padding-block: 4rem; text-align: center; }
.page-hero__heading { display: inline-block; }
.page-hero h1 { margin: 0; }
.page-hero h2 { color: var(--color-primary); font-family: var(--font-heading); font-size: clamp(2rem, 4vw, 2.5rem); font-weight: 400; line-height: 1.15; margin: .5rem 0 0; }
.page-hero--image { background-image: var(--hero-image); background-position: center; background-repeat: no-repeat; background-size: cover; max-height: 420px; padding-block: 0; text-align: left; }
.page-hero--image > .container { padding-block: 10rem; }
.page-hero--image h1 { max-width: 600px; }
.page-hero--image h2 { max-width: 600px; }
.page-hero__copy { max-width: 600px; }
.page-hero--constrained { background-image: var(--hero-image), linear-gradient(var(--color-tint), var(--color-tint)); background-clip: content-box, border-box; background-origin: content-box, border-box; background-position: center, center; background-repeat: no-repeat, repeat; background-size: cover, auto; padding-inline: max(0px, calc((100% - var(--container-w)) / 2)); }
@media (max-width: 760px) {
.page-hero__heading { background: #fffb; padding: 2rem; }
.page-hero--image > .container { padding-block: 6.25rem; }
.page-hero--image { text-align: center; }
.page-hero--image h1, .page-hero__copy { margin-inline: auto; }
.page-hero--image h1, .page-hero--image h2, .page-hero__copy { margin-inline: auto; }
}
</style>
+12 -1
View File
@@ -6,12 +6,23 @@ interface Props {
image?: string;
lang?: string;
type?: 'website' | 'article';
robots?: string;
}
const { title, description = '', canonical, image, lang = 'en', type = 'website' } = Astro.props;
const {
title,
description = '',
canonical,
image,
lang = 'en',
type = 'website',
robots
} = Astro.props;
const defaultRobots = import.meta.env.PUBLIC_ALLOW_INDEXING === 'true' ? 'index,follow' : 'noindex,nofollow';
const absoluteImage = image ? new URL(image, Astro.site ?? Astro.url).href : undefined;
---
<title>{title}</title>
<meta name="description" content={description} />
<meta name="robots" content={robots ?? defaultRobots} />
{canonical && <link rel="canonical" href={canonical} />}
<meta property="og:type" content={type} />
<meta property="og:title" content={title} />
-7
View File
@@ -1,7 +0,0 @@
---
const { title, description, href, image } = Astro.props;
---
<article class="card service-card">
{image && <img src={image} alt="" loading="lazy" />}
<div class="card-body"><h3><a href={href}>{title}</a></h3><p>{description}</p><a class="text-link" href={href}>Learn more</a></div>
</article>
+1 -1
View File
@@ -11,7 +11,7 @@ const { section } = Astro.props;
}
</style>
<section class="section cream-section">
<section class="section section-tint">
<div class="container feature-grid benefit-grid">
<div>
<h2>{section.heading}</h2>
+1 -1
View File
@@ -11,7 +11,7 @@ const { section } = Astro.props;
}
</style>
<section class="section cream-section">
<section class="section section-tint">
<div class="container feature-grid esa-grid">
<div>
<h2>{section.heading}</h2>
@@ -1,4 +1,5 @@
---
import Button from '../Button.astro';
import type { FinancialHelpSection } from '../../types/home-sections';
interface Props { section: FinancialHelpSection }
const { section } = Astro.props;
@@ -7,6 +8,6 @@ const { section } = Astro.props;
<div class="container">
<h2>{section.heading}</h2>
<p>{section.body}</p>
<a class="button" href={section.cta.href}>{section.cta.label}</a>
<Button href={section.cta.href}>{section.cta.label}</Button>
</div>
</section>
+4 -3
View File
@@ -1,4 +1,5 @@
---
import Button from '../Button.astro';
import type { HeroSection } from '../../types/home-sections';
interface Props { section: HeroSection }
const { section } = Astro.props;
@@ -6,10 +7,10 @@ const { section } = Astro.props;
<section class="home-hero">
<div class="container home-hero-grid">
<div>
<p class="script-label">{section.eyebrow}</p>
<p class="eyebrow">{section.eyebrow}</p>
<h1 set:html={section.heading} />
<p>{section.body}</p>
<a class="button" href={section.cta.href}>{section.cta.label}</a>
<Button href={section.cta.href}>{section.cta.label}</Button>
</div>
<img src={`/assets/images/${section.image}`} alt={section.imageAlt} loading="eager" />
</div>
@@ -19,7 +20,7 @@ const { section } = Astro.props;
.home-hero { background: var(--color-tint); overflow: hidden; }
.home-hero-grid { align-items: end; display: grid; grid-template-columns: 1fr .9fr; min-height: 520px; padding-top: 4rem; }
.home-hero-grid > div { align-self: center; padding-bottom: 4rem; }
.home-hero-grid p:not(.script-label) { max-width: 570px; }
.home-hero-grid p:not(.eyebrow) { max-width: 570px; }
.home-hero-grid img { align-self: end; justify-self: end; max-height: 500px; object-fit: contain; }
@media (max-width: 760px) {
.home-hero-grid { grid-template-columns: 1fr; padding-top: 3rem; text-align: center; }
+1 -1
View File
@@ -3,7 +3,7 @@ import type { ProcessSection } from '../../types/home-sections';
interface Props { section: ProcessSection }
const { section } = Astro.props;
---
<section class="section cream-section process-section">
<section class="section section-tint process-section">
<div class="container">
<h2>{section.heading}</h2>
<div class="process-grid">
@@ -1,4 +1,5 @@
---
import Button from '../Button.astro';
import type { ServicesIntroSection } from '../../types/home-sections';
interface Props { section: ServicesIntroSection }
const { section } = Astro.props;
@@ -31,7 +32,7 @@ const { section } = Astro.props;
<div>
<h2 class="no-balance">{section.commitmentsHeading}</h2>
<ul class="source-list two-column-list">{section.commitments.map((item) => <li>{item}</li>)}</ul>
<a class="button" href={section.commitmentsCta.href}>{section.commitmentsCta.label}</a>
<Button href={section.commitmentsCta.href}>{section.commitmentsCta.label}</Button>
</div>
<img class="gear-photo gear-photo-right" src={`/assets/images/${section.commitmentsImage}`} alt={section.commitmentsImageAlt} />
</div>
@@ -3,7 +3,7 @@ import type { TestimonialsSection } from '../../types/home-sections';
interface Props { section: TestimonialsSection }
const { section } = Astro.props;
---
<section class="section cream-section testimonial-section">
<section class="section section-tint testimonial-section">
<div class="container">
<h2>{section.heading}</h2>
<div class="testimonial-swiper swiper">
+246
View File
@@ -0,0 +1,246 @@
---
import Button from '../Button.astro';
const indeedUrl = 'https://www.indeed.com/cmp/Arizona-Institute-For-Autism/jobs';
const benefits = [
'Competitive Salary', 'Giveback Opportunity', 'Medical Insurance Plan',
'Dental Life Insurance Plan', 'Vision Insurance Plan', 'Life Insurance Plan',
'401 K', 'Vacation Time', 'Sick Time',
];
const culture = [
'Family-Oriented', 'Supportive', 'Educational',
'Motivating', 'Transparent', 'Inclusive',
];
const careerPath = [
'Behavioral Technician', 'Registered Behavioral Technician (RBT)', 'Senior Behavioral Technician, RBT',
'Clinical Case Supervisor', 'BCBA (Board Certified Behavioral Analyst)', 'Clinical Assistant Director, BCBA', 'Clinical Director, BCBA',
];
const benefitIcons = [
['competitive-compensation_blue.svg', 'Competitive\nCompensation'],
['flexible-scheduling_blue.svg', 'Flexible\nScheduling'],
['caseload-teamwork_blue.svg', 'Caseload\nTeamwork'],
['career-growth_blue.svg', 'Career\nGrowth'],
['monthly-training_blue.svg', 'Monthly\nTraining'],
['diversity-inclusion-opportunity_blue.svg', 'Diversity & Inclusive\nOpportunities'],
];
const careerCards = [
{ image: 'clinical-careers.webp', title: 'Clinical', body: "Bring your specialized knowledge to the clinical team. You'll get the training tools you need to serve a community you'll love." },
{ image: 'non-clinical-careers.webp', title: 'Non-Clinical', body: "Build a career you love as a member of our non-clinical team. In this role, you'll service team members, families, and the community while ensuring quality service." },
{ image: 'corporate-center-team-support.webp', title: 'Center Support', body: 'Take a fresh and innovative approach as a member of the Corporate team. Take on new challenges and opportunities to help shape the future of Pediatric care.' },
{ image: 'internships-practicum.webp', title: 'Internships', body: 'Our specialty interns help shape the future of our center carefully curated clinical, operations, advocacy, people, and other departments.' },
];
---
<article class="careers-page">
<!-- Join the AIA Team -->
<section class="careers-intro container-narrow">
<h2>Join the AIA Team</h2>
<p>Apply to join the Arizona Institute for Autism (AIA) team today. We are on a mission to improve special education and strengthen communities throughout Arizona.</p>
<p>Our talented and passionate employees are a critical piece to the high-quality Autism services we provide along with our commitment to behavioral health, school solutions, and community outreach.</p>
<Button href={indeedUrl} target="_blank" center>View Open Positions</Button>
</section>
<!-- Our Benefits -->
<section class="careers-section section-tint">
<div class="container careers-split">
<div class="careers-split__text">
<h3>Our Benefits</h3>
<ul>
{benefits.map((b) => <li>{b}</li>)}
</ul>
</div>
<div class="careers-split__images">
<img src="/assets/images/integrated-aba-model_400x300.webp" alt="Integrated ABA model" loading="lazy" />
<img src="/assets/images/proud-aia-staff.webp" alt="Compassionate AIA staff members" loading="lazy" />
</div>
</div>
</section>
<!-- Our Diverse Culture -->
<section class="careers-section">
<div class="container careers-split">
<div class="careers-split__text">
<h3>Our Diverse Culture</h3>
<ul>
{culture.map((c) => <li>{c}</li>)}
</ul>
</div>
<div class="careers-split__images">
<img src="/assets/images/toddler-bcba-support.webp" alt="Toddler with BCBA support" loading="lazy" />
<img src="/assets/images/aba-learn-play-skills.webp" alt="ABA learn and play skills" loading="lazy" />
</div>
</div>
</section>
<!-- See Your Impact -->
<section class="careers-section section-tint">
<div class="container careers-two-col">
<div>
<h3>See Your Impact</h3>
<p>The work we do has a direct impact on our learners, families, our communities, our team members, and the healthcare industry.</p>
<p>Every day you'll work with others driven to make a difference through transforming primary autism and pediatric care.</p>
</div>
<img src="/assets/images/scottsdale-ribbon-cutting.webp" alt="Scottsdale location ribbon-cutting ceremony" loading="lazy" />
</div>
</section>
<!-- Expand & Explore -->
<section class="careers-section container-narrow">
<h3>Expand & Explore Your Potential</h3>
<p>We're growing fast, and that makes us a great place for you to grow. Focus on learning, discovering, and trying new areas that interest you. Advance in your chosen field. We'll help you get there.</p>
</section>
<!-- Scholarships GCU -->
<section class="careers-section section-tint">
<div class="container">
<div>
<h3>Scholarships Awarded in Partnership with GCU</h3>
<p>Through AIA's partnership with GCU, all AIA employees, along with their spouses and dependents, are eligible for a tuition discount when they enroll in one of GCU's programs.</p>
<img class="logo-img" src="/assets/images/logo-gcu.webp" alt="Grand Canyon University logo" loading="lazy" />
</div>
</div>
</section>
<!-- BCBA Internships -->
<section class="careers-section container-narrow">
<h3>Build Your Resume with BCBA Internships &amp; Practicum</h3>
<p>At the Arizona Institute for Autism, we afford students at Arizona State University to gain practicum experience. Due to our partnership with the Arizona State University Department of Psychology, future Board Certified Behavior Analysts will meet the training requirements of the ASU Department of Psychology MS ABA Program and the BACB.</p>
<p>Students will be given the opportunity to obtain thorough behavior-analytic experience and training in a fashion that fulfills its service provision related functions as an organization, so long as those functions are analytic in nature and the goals of the practicum experience are in accordance with the Behavior Analyst Certification Board.</p>
<img class="logo-img" src="/assets/images/asu.webp" alt="Arizona State University" loading="lazy" />
</section>
<!-- Choose Your Clinical Career Path -->
<section class="careers-section section-tint">
<div class="container">
<h2>Choose Your Clinical Career Path</h2>
<p>Work someone you love. Begin your journey with Arizona's leading ABA Integrated Therapy provider today!</p>
<div class="careers-split">
<div class="careers-split__text">
<ul>
{careerPath.map((p) => <li>{p}</li>)}
</ul>
</div>
<div class="careers-split__images">
<img src="/assets/images/casp-member-logo.webp" alt="The Council of Autism Service Providers member logo" loading="lazy" />
<img src="/assets/images/logo-BACB.png" alt="BACB logo" loading="lazy" />
</div>
</div>
<Button href={indeedUrl} target="_blank" center>View Open Positions</Button>
</div>
</section>
<!-- Benefits of Working at AIA -->
<section class="careers-section careers-center">
<div class="container">
<h3>Benefits of Working at Arizona Institute for Autism</h3>
<div class="benefits-grid">
{benefitIcons.map(([icon, label]) => (
<div class="benefit-item">
<img src={`/assets/images/${icon}`} alt={label.replace('\n', ' ')} width="80" height="80" loading="lazy" />
<p>{label.split('\n').map((line, i) => i > 0 ? [<br />, line] : line)}</p>
</div>
))}
</div>
</div>
</section>
<!-- Your Clinical Path to Success -->
<section class="careers-section section-tint">
<div class="container-narrow careers-path">
<h2>Your Clinical Path to Success</h2>
<p>Behavioral Technician &rarr; Registered Behavioral Technician &rarr; Senior Behavioral Technician</p>
<p>Clinical Case Supervisor &rarr; BCBA &rarr; Clinical Assistant Director &rarr; Clinical Director</p>
</div>
</section>
<!-- Find Your Path -->
<section class="careers-section">
<div class="container careers-center">
<h3>Find Your Path</h3>
<p>If you have an interest, we have a department for you to step into and teams to support you in your growth.</p>
</div>
<div class="container">
<div class="career-cards">
{careerCards.map(({ image, title, body }) => (
<div class="career-card">
<img src={`/assets/images/${image}`} alt={title} loading="lazy" />
<div class="career-card__body">
<h4>{title}</h4>
<p>{body}</p>
</div>
</div>
))}
</div>
<Button href={indeedUrl} target="_blank" center>View Open Positions</Button>
</div>
</section>
</article>
<style>
/* ── Shared ─────────────────────────────────────────── */
.careers-intro { padding-block: 3rem; }
.careers-section { padding-block: 3rem; }
.careers-section.container-narrow { max-width: 760px; margin-inline: auto; padding-inline: 1rem; }
.careers-center { text-align: center; }
/* ── Two-column split: 50% text + 25%+25% images ───── */
.careers-split { display: grid; gap: 2rem; grid-template-columns: 1fr 1fr; align-items: center; }
.careers-split__images { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }
.careers-split__images img { width: 100%; height: auto; border-radius: 4px; }
.careers-split__text h3 { margin-top: 0; }
.careers-split__text ul { padding-left: 1.25rem; }
.careers-split__text ul li { margin-bottom: .4rem; }
/* ── Two-column 50/50 ───────────────────────────────── */
.careers-two-col { display: grid; gap: 2rem; grid-template-columns: 1fr 1fr; align-items: center; }
.careers-two-col h3 { margin-top: 0; }
.careers-two-col img { width: 100%; height: auto; border-radius: 4px; }
.careers-two-col--logo { align-items: center; }
.logo-img { display: block; max-width: 100%; height: auto; margin-top: 2rem; margin-inline: auto; }
/* ── Benefits icon grid ─────────────────────────────── */
.benefits-grid { display: grid; grid-template-columns: repeat(6, 1fr); gap: 1rem; margin-top: 2rem; text-align: center; }
.benefit-item img { display: block; margin: 0 auto .75rem; }
.benefit-item p { font-size: .75rem; line-height: 1.4; margin: 0; }
/* ── Clinical path ──────────────────────────────────── */
.careers-path { text-align: center; }
.careers-path h2 { margin-bottom: 1rem; }
.careers-path p { font-size: 1rem; margin-bottom: .5rem; }
/* ── Find Your Path ─────────────────────────────────── */
.career-cards { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1.5rem; padding-block: 1rem; }
.career-card { display: flex; flex-direction: column; isolation: isolate; text-align: center; }
.career-card img { display: block; margin-inline: auto; object-fit: cover; }
.career-card__body { flex: 1;
background: var(--color-tint);
border-radius: 0 0 4px 4px;
margin-top: -2.4rem;
padding: 2.4rem 1rem 1.25rem;
position: relative;
z-index: -1;
}
.career-card__body h4 { font-size: 1.25rem; margin: 0 0 .4rem; }
.career-card__body p { font-size: .8rem; margin: 0; }
/* ── CTA ────────────────────────────────────────────── */
.careers-cta { padding-block: 3rem; text-align: center; }
/* ── Responsive ─────────────────────────────────────── */
@media (max-width: 900px) {
.careers-split, .careers-two-col { grid-template-columns: 1fr; }
.careers-split__images { grid-template-columns: 1fr; }
.career-cards { grid-template-columns: repeat(2, 1fr); }
.benefits-grid { grid-template-columns: repeat(3, 1fr); }
}
@media (max-width: 540px) {
.career-cards { grid-template-columns: 1fr; }
.benefits-grid { grid-template-columns: repeat(2, 1fr); }
}
</style>
+111
View File
@@ -0,0 +1,111 @@
---
import ContactForm from '../ContactForm.astro';
interface Props {
title: string;
}
const { title } = Astro.props;
---
<section class="contact-page">
<div class="container contact-page__grid">
<div class="contact-page__copy">
<slot />
</div>
<ContactForm title={title} />
</div>
</section>
<section class="contact-map-section" aria-label="Map showing Arizona Institute for Autism in Scottsdale">
<div id="aia-contact-map" class="contact-map"></div>
</section>
<script is:inline src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js" integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin=""></script>
<script is:inline>
(() => {
const mapElement = document.getElementById('aia-contact-map');
if (!mapElement || !window.L) return;
const map = window.L.map(mapElement).setView([33.63, -111.88756], 12);
const tiles = window.L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>',
maxZoom: 19
});
tiles.on('tileload', (event) => {
if (event.tile && !event.tile.alt) {
event.tile.alt = 'OpenStreetMap tile showing area around Arizona Institute for Autism in Scottsdale, AZ';
}
});
tiles.addTo(map);
window.L.marker([33.617746, -111.88756]).addTo(map).bindPopup(`
<span style="display:block;text-align:center">
<b>Arizona Institute for Autism</b><br>
<img alt="Arizona Institute for Autism" src="/assets/images/aia-logo.svg" width="94">
</span>
`).openPopup();
})();
</script>
<style>
.contact-page {
background: white;
padding-block: 100px;
}
.contact-page__grid {
align-items: start;
display: grid;
gap: clamp(2rem, 5vw, 4rem);
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.contact-page__copy {
color: var(--color-primary);
text-align: left;
}
.contact-page__copy :global(h2) {
margin: 0 0 25px;
}
.contact-page__copy :global(p) {
margin: 0 0 25px;
}
.contact-page__copy :global(.content-button) {
margin-bottom: 40px;
}
.contact-map-section {
background: white;
margin: 0;
padding: 0;
}
.contact-map {
height: 360px;
width: 100%;
}
@media (max-width: 992px) {
.contact-page {
padding-block: 50px;
}
}
@media (max-width: 767px) {
.contact-page__grid {
grid-template-columns: 1fr;
}
.contact-page__copy :global(.content-button) {
display: table;
margin-inline: auto;
}
}
</style>
+609
View File
@@ -0,0 +1,609 @@
---
import Button from '../Button.astro';
---
<article class="faq-page">
<div class="faq-source container source-page">
<slot />
</div>
<section class="faq-search container-narrow">
<div class="hs-search-field hs-search-field--initialized">
<div class="hs-search-field__bar hs-search-field__bar--button-inline">
<form class="hs-search-field__form" action="/search">
<label class="sr-only" for="faq-search-input">Search FAQs</label>
<input
id="faq-search-input"
type="search"
class="hs-search-field__input"
name="q"
autocomplete="off"
placeholder="Search"
data-faq-search
/>
<button class="hs-search-field__button" type="button" aria-label="Search">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" aria-hidden="true" width="18" height="18">
<path d="M505 442.7L405.3 343c-4.5-4.5-10.6-7-17-7H372c27.6-35.3 44-79.7 44-128C416 93.1 322.9 0 208 0S0 93.1 0 208s93.1 208 208 208c48.3 0 92.7-16.4 128-44v16.3c0 6.4 2.5 12.5 7 17l99.7 99.7c9.4 9.4 24.6 9.4 33.9 0l28.3-28.3c9.4-9.4 9.4-24.6.1-34zM208 336c-70.7 0-128-57.2-128-128 0-70.7 57.2-128 128-128 70.7 0 128 57.2 128 128 0 70.7-57.2 128-128 128z" fill="#254080"/>
</svg>
</button>
</form>
</div>
</div>
</section>
<section class="faq-accordion" aria-labelledby="faq-questions-heading">
<div class="container-narrow faq-accordion__inner">
<h2 id="faq-questions-heading">Questions &amp; Answers</h2>
<div class="faq-list" data-faq-list></div>
</div>
</section>
<section class="faq-cta faq-cta-primary">
<div class="container-narrow">
<p class="eyebrow">Still have questions?</p>
<h2>Reach Out to Learn More</h2>
<p>We hope this FAQ gives you a clear picture of how Applied Behavior Analysis (ABA) therapy at the Arizona Institute for Autism can support your childs growth. If you have more questions or would like to <a href="/client-consultation">schedule an assessment</a>, please reach out. Our team is here to guide you and your learner every step of the way!</p>
</div>
</section>
<section class="faq-cta faq-cta-secondary">
<div class="container-narrow">
<h2>Need Support in Getting an Autism Diagnosis?</h2>
<p>To meet diagnostic criteria for ASD, a child must have persistent deficits in each of three areas of social communication and interaction plus at least two of four types of restricted repetitive behaviors. Consult with a Client Advocate today to receive individual and family support!</p>
<Button href="/client-consultation" center>Request an Appointment</Button>
</div>
</section>
</article>
<script>
const initFaqPage = () => {
const root = document.querySelector('.faq-page');
const source = root?.querySelector('.faq-source.source-page');
const list = root?.querySelector('[data-faq-list]');
const search = root?.querySelector('[data-faq-search]');
if (!(root instanceof HTMLElement) || !(source instanceof HTMLElement) || !(list instanceof HTMLElement) || !(search instanceof HTMLInputElement)) {
return;
}
const nodes = [...source.childNodes];
const firstQuestionIndex = nodes.findIndex((node) => node.nodeType === Node.ELEMENT_NODE && node.tagName === 'H3');
if (firstQuestionIndex === -1) {
return;
}
let index = firstQuestionIndex;
let openedFirst = false;
while (index < nodes.length) {
const node = nodes[index];
if (!(node instanceof HTMLElement) || node.tagName !== 'H3') {
index += 1;
continue;
}
const details = document.createElement('details');
details.className = 'faq';
details.open = !openedFirst;
const summary = document.createElement('summary');
const title = document.createElement('h3');
title.textContent = node.textContent?.trim() || '';
const icon = document.createElement('span');
icon.className = 'faq-icon';
icon.setAttribute('aria-hidden', 'true');
summary.append(title, icon);
details.appendChild(summary);
const answer = document.createElement('div');
answer.className = 'faq-answer interactive-faq-with-seo-schema-integration__list__answer';
const answerInner = document.createElement('div');
answerInner.className = 'faq-answer__inner';
index += 1;
while (index < nodes.length && !(nodes[index] instanceof HTMLElement && nodes[index].tagName === 'H3')) {
const answerNode = nodes[index];
if (answerNode.nodeType === Node.TEXT_NODE) {
const text = answerNode.textContent?.trim();
if (text) {
const paragraph = document.createElement('p');
paragraph.textContent = text;
answerInner.appendChild(paragraph);
}
} else {
answerInner.appendChild(answerNode.cloneNode(true));
}
answerNode.remove();
index += 1;
}
answer.appendChild(answerInner);
details.appendChild(answer);
list.appendChild(details);
openedFirst = true;
}
for (let i = firstQuestionIndex; i < nodes.length; i += 1) {
nodes[i].remove();
}
const faqItems = [...list.querySelectorAll('details.faq')];
const closeTimers = new WeakMap();
const closeHandlers = new WeakMap();
const getAnswer = (item) => item.querySelector('.interactive-faq-with-seo-schema-integration__list__answer');
const getAnswerInner = (item) => item.querySelector('.faq-answer__inner');
const setClosedStyles = (answer) => {
answer.style.height = '0px';
answer.style.opacity = '0';
answer.style.transform = 'translateY(-.35rem)';
};
const setOpenStyles = (answer) => {
answer.style.opacity = '1';
answer.style.transform = 'translateY(0)';
};
faqItems.forEach((item, index) => {
const answer = getAnswer(item);
if (!answer) {
return;
}
if (index === 0 && item.open) {
answer.style.height = 'auto';
setOpenStyles(answer);
delete item.dataset.state;
} else if (!item.open) {
answer.style.height = '0px';
setClosedStyles(answer);
}
});
const closeItem = (item) => {
if (!item.open || item.dataset.state === 'closing') {
return;
}
item.dataset.state = 'closing';
const answer = getAnswer(item);
const answerInner = getAnswerInner(item);
if (answer && answerInner) {
const priorHandler = closeHandlers.get(item);
if (priorHandler) {
answer.removeEventListener('transitionend', priorHandler);
}
answer.style.height = `${answerInner.scrollHeight}px`;
answer.offsetHeight; // force reflow
setClosedStyles(answer);
const onTransitionEnd = (event) => {
if (event.target !== answer || event.propertyName !== 'height') {
return;
}
item.removeAttribute('open');
delete item.dataset.state;
answer.style.height = '0px';
answer.removeEventListener('transitionend', onTransitionEnd);
closeHandlers.delete(item);
closeTimers.delete(item);
};
closeHandlers.set(item, onTransitionEnd);
answer.addEventListener('transitionend', onTransitionEnd);
}
const timer = window.setTimeout(() => {
const answerStillOpen = answer && answer.offsetHeight > 0;
if (!answerStillOpen) {
return;
}
item.removeAttribute('open');
delete item.dataset.state;
if (answer) {
answer.style.height = '0px';
}
closeHandlers.delete(item);
closeTimers.delete(item);
}, 360);
closeTimers.set(item, timer);
};
const openItem = (item) => {
if (item.open && item.dataset.state !== 'closing') {
return;
}
const timer = closeTimers.get(item);
if (timer) {
window.clearTimeout(timer);
closeTimers.delete(item);
}
const priorHandler = closeHandlers.get(item);
const answer = getAnswer(item);
if (priorHandler && answer) {
answer.removeEventListener('transitionend', priorHandler);
closeHandlers.delete(item);
}
item.setAttribute('open', '');
item.dataset.state = 'opening';
const answerInner = getAnswerInner(item);
if (answer && answerInner) {
answer.style.height = '0px';
setClosedStyles(answer);
answer.offsetHeight; // force reflow
window.requestAnimationFrame(() => {
answer.style.height = `${answerInner.scrollHeight}px`;
setOpenStyles(answer);
});
const onTransitionEnd = (event) => {
if (event.target !== answer || event.propertyName !== 'height') {
return;
}
if (item.dataset.state === 'opening') {
answer.style.height = 'auto';
delete item.dataset.state;
}
answer.removeEventListener('transitionend', onTransitionEnd);
};
answer.addEventListener('transitionend', onTransitionEnd);
}
};
faqItems.forEach((item) => {
const summary = item.querySelector('summary');
summary?.addEventListener('click', (event) => {
event.preventDefault();
const willOpen = !item.open;
faqItems.forEach((other) => {
if (other !== item) {
closeItem(other);
}
});
if (willOpen) {
openItem(item);
} else {
closeItem(item);
}
});
});
const applyFilter = () => {
const query = search.value.trim().toLowerCase();
faqItems.forEach((item) => {
const text = item.textContent?.toLowerCase() || '';
item.hidden = Boolean(query) && !text.includes(query);
});
};
search.addEventListener('input', applyFilter);
};
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initFaqPage, { once: true });
} else {
initFaqPage();
}
</script>
<style>
.faq-page {
padding-bottom: 0;
}
.faq-source {
padding-block: 4rem 1rem;
}
.faq-search {
margin-bottom: 1.5rem;
padding-block: 1rem 0.75rem;
}
.sr-only {
clip: rect(0, 0, 0, 0);
border: 0;
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
white-space: nowrap;
width: 1px;
}
.hs-search-field {
margin-inline: auto;
width: min(780px, 100%);
}
.hs-search-field--initialized {
display: block;
}
.hs-search-field__bar {
display: block;
width: 100%;
}
.hs-search-field__form {
display: flex;
margin: 0;
width: 100%;
}
.hs-search-field__input {
appearance: none;
background: #fff;
border: 2px solid rgba(37, 64, 128, .2);
border-radius: 4px;
color: var(--color-primary);
flex: 1;
font: inherit;
line-height: 1.5;
min-height: 3.125rem;
min-width: 0;
padding: .9rem 1.25rem;
box-shadow: 0 1px 2px rgba(37, 64, 128, .06);
}
.hs-search-field__input::placeholder {
color: var(--color-muted);
opacity: 1;
}
.hs-search-field__input:focus {
border-color: var(--color-accent);
outline: none;
}
.hs-search-field__button {
align-items: center;
background: #fff;
border: 2px solid rgba(37, 64, 128, .2);
border-radius: 4px;
box-shadow: 0 1px 2px rgba(37, 64, 128, .06);
cursor: pointer;
display: flex;
flex: 0 0 auto;
justify-content: center;
margin-left: 2px;
min-height: 3.125rem;
opacity: .85;
padding-inline: 1rem;
}
.hs-search-field__button:hover {
opacity: 1;
}
.faq-accordion {
background: var(--color-tint);
padding-block: 2rem 4rem;
}
.faq-accordion__inner {
margin-inline: auto;
}
.faq-accordion h2 {
color: var(--color-primary);
font-family: var(--font-heading);
font-size: clamp(2rem, 4vw, 3rem);
margin: 0 0 1.75rem;
text-align: center;
}
:global(.faq-list) {
padding: 1.5rem 1.25rem 1rem;
}
:global(.faq-list .faq) {
background: transparent;
border-bottom: 1px solid var(--color-accent-strong);
padding: 0;
}
:global(.faq-list .faq:last-child) {
border-bottom: 0;
}
:global(.faq-list .faq[hidden]) {
display: none;
}
:global(.faq-list summary) {
align-items: center;
cursor: pointer;
display: flex;
gap: 1.5rem;
justify-content: space-between;
list-style: none;
padding: .875rem 0;
}
:global(.faq-list summary::-webkit-details-marker) {
display: none;
}
:global(.faq-list summary h3) {
color: var(--color-primary);
font-family: var(--font-body);
font-size: 1rem;
font-weight: 700;
line-height: 1.625;
margin: 0;
}
:global(.faq-list .faq-icon) {
flex: 0 0 auto;
display: block;
height: 12px;
margin-inline-start: auto;
position: relative;
width: 12px;
}
:global(.faq-list .faq-icon::after) {
border-bottom: 1px solid var(--color-accent-strong);
border-right: 1px solid var(--color-accent-strong);
content: "";
display: block;
height: 12px;
transform: translateY(-50%) rotate(45deg);
transform-origin: center;
transition: transform 300ms ease;
width: 12px;
}
:global(.faq-list details[open] .faq-icon::after) {
transform: rotate(-135deg);
}
:global(.faq-list .interactive-faq-with-seo-schema-integration__list__answer) {
height: 0;
margin-bottom: 0;
opacity: 0;
transform: translateY(-.35rem);
overflow: hidden;
transition: height .3s ease, margin-bottom .3s ease, opacity .3s ease, transform .3s ease;
}
:global(.faq-list .faq[open] .interactive-faq-with-seo-schema-integration__list__answer) {
margin-bottom: .85rem;
opacity: 1;
transform: translateY(0);
}
:global(.faq-list .faq-answer__inner) {
padding-bottom: .25rem;
}
:global(.faq-list .faq-answer p) {
margin: 0 0 1rem;
line-height: 1.7;
}
:global(.faq-list .faq-answer p:last-child) {
margin-bottom: 0;
}
:global(.faq-list .faq-answer__inner h4),
:global(.faq-list .faq-answer__inner h5) {
color: var(--color-primary);
font-family: var(--font-heading);
font-weight: 700;
margin-top: 0;
text-wrap: balance;
}
:global(.faq-list .faq-answer__inner h4) {
font-size: 2rem;
line-height: 2.5rem;
margin-bottom: 1.25rem;
}
:global(.faq-list .faq-answer__inner h5) {
font-size: 1.5rem;
line-height: 2rem;
margin-bottom: 1.25rem;
}
:global(.faq-list .faq-answer__inner :where(ul, ol)) {
margin-block: 0 1.5rem;
padding-inline-start: 2.5rem;
}
:global(.faq-list .faq-answer__inner :where(ul, ol) :where(ul, ol)) {
margin-block: .25rem 0;
}
:global(.faq-list .faq-answer__inner li) {
line-height: 1.625;
margin-block: 0;
padding-inline-start: .15rem;
}
:global(.faq-list .faq-answer__inner li > p) {
margin-block: 0;
}
:global(.faq-list .faq-answer__inner ul > li::marker) {
color: var(--color-accent-strong);
}
:global(.faq-list .faq-answer__inner ol > li::marker) {
color: var(--color-primary);
}
.faq-cta-primary {
background: var(--color-primary);
color: #fff;
padding-block: 3.5rem;
}
.faq-cta-primary .eyebrow {
color: #f9b34d;
}
.faq-cta-primary h2,
.faq-cta-primary p {
color: #fff;
}
.faq-cta-primary h2 {
font-size: clamp(1.8rem, 4vw, 2.8rem);
margin-bottom: .75rem;
}
.faq-cta-secondary {
background: #fff;
padding-block: 3.5rem 4rem;
}
.faq-cta-secondary h2 {
color: var(--color-primary);
font-size: clamp(1.8rem, 4vw, 2.8rem);
margin-bottom: .75rem;
}
.faq-cta-secondary :global(.content-button) {
margin-top: 1.5rem;
}
@media (min-width: 768px) {
.faq-source {
padding-top: 4.5rem;
}
.faq-search {
margin-bottom: 2rem;
padding-block-end: 1rem;
}
.faq-list {
padding: 2.5rem 2rem 1.5rem;
}
.faq-accordion h2 {
margin-bottom: 2rem;
}
}
</style>
@@ -1,17 +1,17 @@
---
import { getEntry } from 'astro:content';
import BaseLayout from '../layouts/BaseLayout.astro';
import HomeBenefits from './home/HomeBenefits.astro';
import HomeDirector from './home/HomeDirector.astro';
import HomeEsa from './home/HomeEsa.astro';
import HomeFinancialHelp from './home/HomeFinancialHelp.astro';
import HomeHero from './home/HomeHero.astro';
import HomeInsurance from './home/HomeInsurance.astro';
import HomeProcess from './home/HomeProcess.astro';
import HomeServicesIntro from './home/HomeServicesIntro.astro';
import HomeSkills from './home/HomeSkills.astro';
import HomeTestimonials from './home/HomeTestimonials.astro';
import type { HomePageContent, HomeSection } from '../types/home-sections';
import BaseLayout from '../../layouts/BaseLayout.astro';
import HomeBenefits from '../home/HomeBenefits.astro';
import HomeDirector from '../home/HomeDirector.astro';
import HomeEsa from '../home/HomeEsa.astro';
import HomeFinancialHelp from '../home/HomeFinancialHelp.astro';
import HomeHero from '../home/HomeHero.astro';
import HomeInsurance from '../home/HomeInsurance.astro';
import HomeProcess from '../home/HomeProcess.astro';
import HomeServicesIntro from '../home/HomeServicesIntro.astro';
import HomeSkills from '../home/HomeSkills.astro';
import HomeTestimonials from '../home/HomeTestimonials.astro';
import type { HomePageContent, HomeSection } from '../../types/home-sections';
interface Props { lang?: string }
const { lang = 'en' } = Astro.props;
@@ -1,5 +1,5 @@
---
import Button from './Button.astro';
import Button from '../Button.astro';
---
<style>
@@ -1,4 +1,6 @@
---
import Button from '../Button.astro';
import ContactObfuscation from '../ContactObfuscation.astro';
const groups = [
{
title: 'Board Certified Behavioral Analysts & Psychologists',
@@ -26,6 +28,8 @@ const groups = [
},
{
title: 'ABA Clinic Management',
tagline: 'Applied Behavior Analysis "(ABA) at your service"',
tinted: true,
members: [
['team-mariah-marley.webp', 'Mariah Marley', 'Claims Billing'],
['team-eney-garcia.webp', 'Eney Garcia, BA', 'Accounts Billing'],
@@ -38,41 +42,45 @@ const groups = [
<article class="team-page">
<section class="team-intro container-narrow">
<h2>Our Clinical Team</h2>
<p class="script-label">Arizona Institute for Autism<br />Scottsdale, Arizona</p>
<p class="eyebrow">Arizona Institute for Autism<br />Scottsdale, Arizona</p>
<p>AIA's clinical team consists of experienced professionals and paraprofessionals as well as Board Certified Behavior Analysts (BCBA) and Psychologists who have diverse experience and expertise.</p>
</section>
{groups.map((group) => (
<section class="team-group container">
<h2>{group.title}</h2>
<div class="team-grid">
{group.members.map(([image, name, role]) => (
<article class="team-member">
<div class="team-thumb">
<img src={`/assets/images/${image}`} alt={name} loading="lazy" />
</div>
<h3>{name}</h3>
<p>{role}</p>
</article>
))}
<section class:list={['team-group', { 'section-tint': group.tinted }]}>
<div class="container">
<h2>{group.title}</h2>
{group.tagline && <p class="team-group__tagline"><em>{group.tagline}</em></p>}
<div class="team-grid">
{group.members.map(([image, name, role]) => (
<article class="team-member">
<div class="team-thumb">
<img src={`/assets/images/${image}`} alt={name} loading="lazy" />
</div>
<h3>{name}</h3>
<p>{role}</p>
</article>
))}
</div>
</div>
</section>
))}
<section class="team-careers">
<div class="container-narrow">
<p class="script-label">Join the AIA Team!</p>
<p class="eyebrow">Join the AIA Team!</p>
<h2>Employment Opportunities</h2>
<p>Apply online to join the AIA team! At the Arizona Institute for Autism (AIA) we are on a mission to improve special education and strengthen communities throughout Arizona.</p>
<p>Our talented and passionate employees are a critical piece to the high-quality ABA therapy we provide along with our commitment to behavioral health, school solutions, and community outreach. For questions regarding open positions, please contact us at <a href="mailto:hr@azinstitute4autism.com">hr@azinstitute4autism.com</a>.</p>
<a class="button" href="/careers">View Open Positions</a>
<p>Our talented and passionate employees are a critical piece to the high-quality ABA therapy we provide along with our commitment to behavioral health, school solutions, and community outreach. For questions regarding open positions, please contact us at <ContactObfuscation email="hr@azinstitute4autism.com" obfuscatedText="hr [at] abaclinicaz [dot] com" class="team-email" />.</p>
<Button href="/careers">View Open Positions</Button>
</div>
</section>
</article>
<style>
.team-intro { padding-block: 4rem; text-align: center; }
.team-intro .script-label { font-size: 1.8rem; }
.team-intro .eyebrow { font-size: 1.8rem; }
.team-group { padding-block: 2rem 4rem; text-align: center; }
.team-group > h2 { margin: 0 auto 2.5rem; max-width: 760px; }
.team-group h2 { margin: 0 auto 1rem; max-width: 760px; }
.team-group__tagline { font-size: 1.125rem; margin: 0 auto 2rem; }
.team-grid { display: flex; flex-wrap: wrap; gap: 2rem 1.5rem; justify-content: center; }
.team-member { flex: 0 0 227px; }
.team-thumb { border-radius: 4px; overflow: hidden; }
@@ -80,7 +88,9 @@ const groups = [
.team-thumb img:hover { transform: scale(1.05); }
.team-member h3 { font-family: var(--font-body); font-size: .8rem; margin-bottom: 0; }
.team-member p { font-size: .85rem; margin: 0; }
.team-careers { background: var(--color-tint); padding-block: 5rem; }
.team-careers { padding-block: 5rem; }
.team-careers :global(.content-button) { margin-left: 50%; margin-top: 4rem; translate: -50%; }
.team-email { font-weight: 700; }
@media (max-width: 950px) {
.team-member { flex-basis: min(100%, 227px); }
}
@@ -13,6 +13,8 @@ tags: []
draft: false
---
import ContactObfuscation from '../../../components/ContactObfuscation.astro';
Applied Behavior Analysis (ABA) is an evidence-based approach that utilizes the principles of behavior analysis to modify maladaptive behaviors and enhance socially significant behaviors. It is often employed for children with Autism Spectrum Disorder (ASD). ABA aims to teach a variety of skills, such as communication, social interactions, and daily skills, all within a safe, engaging, and fun environment conducive to therapeutic success. It also helps children minimize disruptive behaviors that may impede their learning, while prioritizing the child's individual autonomy.
ABA therapy is highly beneficial for children with an autism diagnosis because it is tailored to each child's unique needs, optimizing outcomes. Skills are taught in a manner that maximizes the child's receptivity; this is achieved by breaking the skills into smaller, achievable targets. Furthermore, skills are generalized by teaching them across different settings and individuals. When a child begins receiving ABA therapy, progress is continuously monitored through ongoing data collection. This data is analyzed to ensure the efficacy of the intervention, and observations are made to guarantee that the child is learning effectively. An essential goal of ABA is not just short-term behavioral modification, but also the generalization and maintenance of learned skills over time. ABA therapy provides tools for managing challenging behaviors such as aggression, self-injury, or disruption. By understanding the function of these behaviors, suitable replacements can be taught, aiding the child's overall improvement.
@@ -21,4 +23,4 @@ Parental participation is a critical component of ABA Therapy. Parents and careg
At the Arizona Institute for Autism, our focus is on the learner. Our services are designed to meet individual needs, working collaboratively with a team of clinicians and parents. This approach aims to improve the quality of life for children with autism and their families.
For more information on ABA therapy services offered by AIA, or to book your free consultation, visit us at [https://www.azinstitute4autism.com](../index), contact us at [(480) 687-7099](tel:+14806877099), or email [info@azinstitute4autism.com](mailto:info@azinstitute4autism.com). If you are looking for more applied behavioral analysis and ASD diagnosis tips, check out [more articles](../library) from AIAs clinical director, Rula Diab!
For more information on ABA therapy services offered by AIA, or to book your free consultation, visit us at [https://www.azinstitute4autism.com](../index), contact us at <ContactObfuscation phone="(480) 687-7099" hrefValue="+14806877099" obfuscatedText="9907-786 (084)" />, or email <ContactObfuscation email="info@azinstitute4autism.com" obfuscatedText="info [at] azinstitute4autism [dot] com" />. If you are looking for more applied behavioral analysis and ASD diagnosis tips, check out [more articles](../library) from AIAs clinical director, Rula Diab!
@@ -13,6 +13,8 @@ tags: []
draft: false
---
import ContactObfuscation from '../../../components/ContactObfuscation.astro';
Toilet training a child with autism can be a complex process that requires patience, consistency, and tailored strategies. Applied Behavior Analysis (ABA) offers effective techniques to support this journey. Here's a structured approach to facilitate toilet training for children with autism:
## 1. Assess Readiness
@@ -61,4 +63,4 @@ Engage with therapists or specialists experienced in ABA to develop and refine y
By implementing these strategies with patience and consistency, you can support your child with autism in achieving successful toilet training.
For more information about ABA therapy services offered by AIA or to book your free consultation, visit our Arizona Institute for Autism website at [www.azinstitute4autism.com](../index), call [(480) 687-7099](tel:+14806877099), or email info@azinstitute4autism.com. If you are looking for more applied behavioral analysis and ASD diagnosis and treatment tips, check out more [blog posts](../library) from AIAs clinical director, Rula Diab!
For more information about ABA therapy services offered by AIA or to book your free consultation, visit our Arizona Institute for Autism website at [www.azinstitute4autism.com](../index), call <ContactObfuscation phone="(480) 687-7099" hrefValue="+14806877099" obfuscatedText="9907-786 (084)" />, or email <ContactObfuscation email="info@azinstitute4autism.com" obfuscatedText="info [at] azinstitute4autism [dot] com" />. If you are looking for more applied behavioral analysis and ASD diagnosis and treatment tips, check out more [blog posts](../library) from AIAs clinical director, Rula Diab!
@@ -13,6 +13,8 @@ tags: []
draft: false
---
import ContactObfuscation from '../../../components/ContactObfuscation.astro';
Hello AIA families,
Happy April, a.k.a World Autism Month!
@@ -45,4 +47,4 @@ Celebrate and support neurodiversity and inclusion with our AIA family by attend
## Additional questions?
If you have questions about AIA's World Autism Community Day of Celebration or are interested in providing a sensory-friendly booth, contact kelly@azinstitute4autism.com.
If you have questions about AIA's World Autism Community Day of Celebration or are interested in providing a sensory-friendly booth, contact <ContactObfuscation email="kelly@azinstitute4autism.com" obfuscatedText="kelly [at] azinstitute4autism [dot] com" />.
@@ -13,6 +13,8 @@ tags: []
draft: false
---
import ContactObfuscation from '../../../components/ContactObfuscation.astro';
As a parent of a child diagnosed with [autism spectrum disorder](https://www.autismspeaks.org/what-autism), you may experience challenges when it comes to mealtime. Many children on the spectrum struggle with [sensory and food sensitivities](https://www.eatright.org/health/health-conditions/intellectual-and-developmental-disabilities/nutrition-for-your-child-with-autism-spectrum-disorder-asd), making it difficult to find foods that they prefer. With Thanksgiving quickly approaching, you may be wondering if there are any recipes that your child may enjoy for the holiday. Well good news- you are in the right place! For this months blog, I will be sharing an easy, delicious Thanksgiving recipe that you and your child will both enjoy- Pumpkin Dump Cake! This recipe is a great twist on the classic pumpkin pie.
This recipe is super easy to follow, and the best part is that your child can help you! Being able to help prepare their food often makes it more enticing for them to eat. It also teaches your child [fundamental life skills](https://www.purdueglobal.edu/blog/psychology/cooking-activities-help-children-autism/), such as how to cook, make decisions, and how to follow directions, as well as strengthening their fine motor skills. Giving your child the opportunity to help you with this recipe can make it more enjoyable for the both of you!
@@ -73,4 +75,4 @@ If you have any leftovers, they can be stored for up to 4 days in the fridge or
This recipe is simple, easy, and delicious! We hope that you encourage your child to help you prepare the cake and have a great time while doing it. Enjoy!
For more information on aba therapy services offered by AIA or to book your free consultation, visit us at [https://www.azinstitute4autism.com](../index), contact us at (480) 707-2195, or email info@azinstitute4autism.com. If you are looking for more applied behavioral analysis and asd diagnosis tips, check out AIAs clinical director, Rula Diab, monthly [blog posts](../library)!
For more information on aba therapy services offered by AIA or to book your free consultation, visit us at [https://www.azinstitute4autism.com](../index), contact us at <ContactObfuscation phone="(480) 707-2195" hrefValue="+14807072195" obfuscatedText="5912-707 (084)" />, or email <ContactObfuscation email="info@azinstitute4autism.com" obfuscatedText="info [at] azinstitute4autism [dot] com" />. If you are looking for more applied behavioral analysis and asd diagnosis tips, check out AIAs clinical director, Rula Diab, monthly [blog posts](../library)!
@@ -13,6 +13,8 @@ tags: []
draft: false
---
import ContactObfuscation from '../../../components/ContactObfuscation.astro';
If you are the parent of a child with autism spectrum disorder, you probably know how difficult mealtime can be. Many children with an autism diagnosis tend to have sensory processing disorder, which can make it difficult for them to enjoy certain foods due to the texture, consistency, temperature, etc. Your child may only prefer a small variety of foods, which often leaves their diet lacking the essential nutrients they need. In this blog series, I will be sharing sensory-friendly recipes and tips to hopefully make mealtime easy and fun for your kiddo! These recipes will help broaden your childs horizons when it comes to food and nutrition.
For this blog, I will be sharing an easy recipe that most kids love- Pancakes! This recipe can be modified to your childs liking, as its important to gradually introduce new foods and ingredients. Use ingredients that your child prefers in order to make it more enticing and enjoyable!
@@ -89,4 +91,4 @@ Your kiddo may not want plain pancakes, which is perfectly fine! There are a few
Feel free to vary the recipe however you want and give your kiddo the opportunity to choose which toppings or fillings they want. Mealtime should be fun and enjoyable, so allowing your child to help you or pick their preferred ingredients can make eating more enticing for them. I hope you and your child enjoy this sensory-friendly pancake recipe!
For more information on aba therapy services offered by AIA or to book your free consultation, visit our [contact page](../contact), call us at [(480) 687-7099](tel:+14806877099), or email [info@azinstitute4autism.com](mailto:info@azinstitute4autism.com). If you are looking for more applied behavioral analysis and asd diagnosis tips, check out AIAs clinical director, Rula Diab, monthly blog posts!
For more information on aba therapy services offered by AIA or to book your free consultation, visit our [contact page](../contact), call us at <ContactObfuscation phone="(480) 687-7099" hrefValue="+14806877099" obfuscatedText="9907-786 (084)" />, or email <ContactObfuscation email="info@azinstitute4autism.com" obfuscatedText="info [at] azinstitute4autism [dot] com" />. If you are looking for more applied behavioral analysis and asd diagnosis tips, check out AIAs clinical director, Rula Diab, monthly blog posts!
@@ -13,6 +13,8 @@ tags: []
draft: false
---
import ContactObfuscation from '../../../components/ContactObfuscation.astro';
Children may exhibit maladaptive behaviors for various reasons, often driven by underlying functions. To address these behaviors, Applied Behavior clinicians use the [ABC model](https://www.iidc.indiana.edu/irca/articles/observing-behavior-using-a-b-c-data), a structured approach that helps identify patterns and triggers. This model involves observing a child's behavior in their natural environment, focusing on three key elements: the Antecedent (what happens before the behavior), the Behavior itself, and the Consequence (what follows the behavior). By carefully analyzing these factors, clinicians gain insights into why certain behaviors occur.
Once the ABC data is collected, it guides clinicians in developing effective intervention strategies. By understanding the cause-and-effect relationship of behaviors, they can implement targeted consequences that reduce maladaptive behaviors while reinforcing positive alternatives. This process not only helps in decreasing unwanted actions but also encourages healthier coping mechanisms, fostering long-term behavioral improvements.
@@ -35,6 +37,6 @@ BCBA clinicians use [consequence interventions](https://specialconnections.ku.ed
## Looking for more information?
For more information on services offered by AIA, or to book your free consultation, visit us at [https://www.azinstitute4autism.com](../index), contact us at [(480) 687-7099](tel:+14806877099), or email [hello@azinstitute4autism.com](mailto:hello@azinstitute4autism.com).
For more information on services offered by AIA, or to book your free consultation, visit us at [https://www.azinstitute4autism.com](../index), contact us at <ContactObfuscation phone="(480) 687-7099" hrefValue="+14806877099" obfuscatedText="9907-786 (084)" />, or email <ContactObfuscation email="hello@azinstitute4autism.com" obfuscatedText="hello [at] azinstitute4autism [dot] com" />.
If you are looking for tips, check out more monthly [blog posts](../library) from AIA's clinical director, Rula Diab!
@@ -13,6 +13,8 @@ tags: []
draft: false
---
import ContactObfuscation from '../../../components/ContactObfuscation.astro';
Critical thinking is an important skill our children need to learn. It enables a child to generate the ability for a higher level of conceptual thinking and appropriately solve problems in their everyday life.
Children with Autism may lack the ability to appropriately communicate their feelings or sometimes tend to engage in repetitive ways of rigid thinking and repetitive behaviors. When they are faced with challenges, moments of frustration occur when no clear expectations are provided to our children regarding their daily schedule, and the activities they are required to engage in throughout the day.
@@ -33,4 +35,4 @@ Applied Behavior Analysis is “*the process of systematically applying interven
It is okay for our children to make errors, however, as caregivers and parents, we should create learning opportunities for our children, give them the emotional support they need and model proper responses.
For more information on how to manage aggressive behavior and proper communication, connect with us by sending your messages and questions to [hello@azinsitute4autism.com](mailto:hello@azinsitute4autism.com) or contact us directly from our contact page.
For more information on how to manage aggressive behavior and proper communication, connect with us by sending your messages and questions to <ContactObfuscation email="hello@azinsitute4autism.com" obfuscatedText="hello [at] azinsitute4autism [dot] com" /> or contact us directly from our contact page.
@@ -13,6 +13,8 @@ tags: []
draft: false
---
import ContactObfuscation from '../../../components/ContactObfuscation.astro';
Generalization is a crucial aspect of learning, ensuring that skills and behaviors extend beyond the initial learning environment. Without generalization, individuals may struggle to apply what theyve learned to new situations, limiting the effectiveness of their education or therapy.
In behavioral science, generalization refers to the process by which a learned behavior is applied across different stimuli, responses, or settings.
@@ -39,6 +41,6 @@ Understanding the three primary types of generalization—stimulus, response, an
## Looking for more information?
For more information on services offered by AIA, or to book your free consultation, visit us at [https://www.azinstitute4autism.com](../index), contact us at [(480) 687-7099](tel:+14806877099), or email [info@azinstitute4autism.com](mailto:info@azinstitute4autism.com).
For more information on services offered by AIA, or to book your free consultation, visit us at [https://www.azinstitute4autism.com](../index), contact us at <ContactObfuscation phone="(480) 687-7099" hrefValue="+14806877099" obfuscatedText="9907-786 (084)" />, or email <ContactObfuscation email="info@azinstitute4autism.com" obfuscatedText="info [at] azinstitute4autism [dot] com" />.
If you are looking for more tips, check our [monthly blog posts](../library) from AIAs clinical director, Rula Diab!
@@ -13,6 +13,8 @@ tags: []
draft: false
---
import ContactObfuscation from '../../../components/ContactObfuscation.astro';
The Arizona Institute for Autism is thrilled to announce its transformation into an Integrated ABA Model, an approach that brings together clinicians and educators to provide exceptional intervention, learning experience, and individualized support for learners with autism. This model incorporates evidence-based interventions, ensuring a comprehensive approach tailored to each child's abilities, interests, and learning style.
By doing so, we aim to create a more effective and engaging treatment and learning process, addressing the diverse needs of each of our learners. Research has consistently demonstrated that effective ABA therapy can profoundly and positively impact children with autism, leading to long-lasting improvements in various areas of development. The Integrated ABA Model comprehensive nature enhances the potential for long-lasting improvements in various areas of development.
@@ -25,4 +27,4 @@ Our approach focuses on teaching children to apply the skills they acquire durin
At the Arizona Institute for Autism, we are committed to empowering children with autism, maximizing their potential for growth and development, and paving the way for a brighter future filled with endless possibilities.
For more information on services offered by AIA or to book your free consultation, visit [https://www.azinstitute4autism.com](../index), call us at [(480) 687-7099](tel:+14806877099), or email [info@azinstitute4autism.com](mailto:info@azinstitute4autism.com). If you are looking for more tips, check out monthly [blog posts](../library) from AIA's clinical director, Rula Diab!
For more information on services offered by AIA or to book your free consultation, visit [https://www.azinstitute4autism.com](../index), call us at <ContactObfuscation phone="(480) 687-7099" hrefValue="+14806877099" obfuscatedText="9907-786 (084)" />, or email <ContactObfuscation email="info@azinstitute4autism.com" obfuscatedText="info [at] azinstitute4autism [dot] com" />. If you are looking for more tips, check out monthly [blog posts](../library) from AIA's clinical director, Rula Diab!
@@ -13,6 +13,8 @@ tags: []
draft: false
---
import ContactObfuscation from '../../../components/ContactObfuscation.astro';
Children with Autism may have difficulty communicating their needs and wants. Struggling to express their needs and escaping an unpreferred situation can all result in a child feeling frustrated. Not having control over their environment can often lead to aggressive and unengaging behaviors.
Communicating with our kiddos when they are engaging in challenging behaviors can be difficult. It is important to understand that behaviors are just an expression of distress. Understanding the function of your child's aggressive behavior can direct you to determine the proper ways to support your kiddos to communicate those needs appropriately and functionally.
@@ -33,4 +35,4 @@ The best strategy for parents to manage aggressive behavior outburst are the fol
- Use less verbal interaction and use more visuals to help to de-escalate the challenging behavior.
For more information on how to manage aggressive behavior and proper communication, connect with us by sending your messages and questions to hello@azinsitute4autism.com or contact us directly at www.azinstitute4autism.com/contactus.
For more information on how to manage aggressive behavior and proper communication, connect with us by sending your messages and questions to <ContactObfuscation email="hello@azinsitute4autism.com" obfuscatedText="hello [at] azinsitute4autism [dot] com" /> or contact us directly at www.azinstitute4autism.com/contactus.
@@ -13,6 +13,8 @@ tags: []
draft: false
---
import ContactObfuscation from '../../../components/ContactObfuscation.astro';
The start of a new year is often associated with feelings of joy and optimism. It is also a time for parents of children with autism to reflect on their child's progress and set goals for the future. Here are some important factors to keep in mind for New Year 2024 when it comes to Applied Behavioral Analysis (ABA) therapy and holiday time:
- Take the time to review and celebrate the progress your child has made in the past year. Discuss any areas that need further development with your licensed Behavioral Analyst (BCBA) to ensure continued growth.
@@ -25,4 +27,4 @@ The start of a new year is often associated with feelings of joy and optimism. I
The New Year symbolizes growth and development, and that applies to children with autism as well. Take this time to reflect, plan, and prioritize the well-being of both the child receiving therapy and their caregivers.
For more information about ABA therapy services offered by AIA or to book your free consultation, visit our Arizona Institute for Autism website at [www.azinstitute4autism.com](../index), call [(480) 687-7099](tel:+14806877099), or email [info@azinstitute4autism.com](mailto:info@azinstitute4autism.com). If you are looking for more applied behavioral analysis and ASD diagnosis and treatment tips, check out more [blog posts](../library) from AIAs clinical director, Rula Diab!
For more information about ABA therapy services offered by AIA or to book your free consultation, visit our Arizona Institute for Autism website at [www.azinstitute4autism.com](../index), call <ContactObfuscation phone="(480) 687-7099" hrefValue="+14806877099" obfuscatedText="9907-786 (084)" />, or email <ContactObfuscation email="info@azinstitute4autism.com" obfuscatedText="info [at] azinstitute4autism [dot] com" />. If you are looking for more applied behavioral analysis and ASD diagnosis and treatment tips, check out more [blog posts](../library) from AIAs clinical director, Rula Diab!
@@ -13,8 +13,10 @@ tags: []
draft: false
---
import ContactObfuscation from '../../../components/ContactObfuscation.astro';
The New Year can be challenging for children with Autism and may lead to distress. Different factors affect the ability of children with autism to cope during those times. The inability to deal with changes and disruption in routines and cope with unclear expectations can be stressful. Additionally, sensory overload including loud music, bright lighting, strong smells, etc. may cause children to feel uncomfortable.
To decrease stress as much as possible during the holiday time, it is important to provide your child with visual Support (i.e., visual schedules) for the purpose of preparing your child to manage changes in schedule and minimize disruption and triggers. Additionally, social stories can be great visual support to clarify activities and events during the holidays along with using a reinforcer system (i.e., Token System) to handle behaviors before it happens.
For more information on how to manage aggressive behavior and proper communication, connect with us by sending your messages and questions to [hello@azinsitute4autism.com](mailto:hello@azinsitute4autism.com) or contact us directly from our contact page.
For more information on how to manage aggressive behavior and proper communication, connect with us by sending your messages and questions to <ContactObfuscation email="hello@azinsitute4autism.com" obfuscatedText="hello [at] azinsitute4autism [dot] com" /> or contact us directly from our contact page.
@@ -13,6 +13,8 @@ tags: []
draft: false
---
import ContactObfuscation from '../../../components/ContactObfuscation.astro';
Children can get overwhelmed, frustrated, and sensory overloaded at any time because of different needs, which can result in a child engaging in challenging behaviors. However, the common thing among all children is that they are in need of support with their communication skills. Different strategies can be used to prevent or minimize challenging behavior, including:
- Being proactive, instead of reacting to a childs behavior. Using proactive strategies that have the most significant impact can help in teaching children the skills they need to convey their needs/wants. Proactive strategies include Communication Skills, Coping Skills, Manding Skills, Attending Skills, etc. Waiting until the child is engaging in disruptive behaviors and then intervening is not beneficial, nor teachable for the child.
@@ -23,4 +25,4 @@ Children can get overwhelmed, frustrated, and sensory overloaded at any time bec
- Allow your child to make a choice in the type of the activity or task presented (e.g., “Do you want to read a book about a cat or a book about a dog?”) or the place to engage in the activity (e.g. “Do you like to play with Lego inside or outside”).
For more information on how to reduce behavior management, schedule a consultation with us today at [(480) 687-7099](tel:+14806877099) or reach out via our [contact form](../contact).
For more information on how to reduce behavior management, schedule a consultation with us today at <ContactObfuscation phone="(480) 687-7099" hrefValue="+14806877099" obfuscatedText="9907-786 (084)" /> or reach out via our [contact form](../contact).
@@ -13,10 +13,12 @@ tags: []
draft: false
---
import ContactObfuscation from '../../../components/ContactObfuscation.astro';
Children with autism often engage in repetitive behaviors as a way to cope with sensory overload. These behaviors can manifest in various ways including repetitive motor behaviors. Repetitive behaviors can affect children's learning experiences, social interactions, and engagement in daily activities.
As each child is unique and often requires tailored support, it's essential to comprehend each childs needs and preferences also offering alternative and functional ways for them to engage in these behaviors. Such adaptations can help children be more engaged throughout the day, as well as offer them a constructive outlet for emotional expression and self-soothing. The goal is to teach a child alternative behaviors that serve the same purpose but are more adaptable during structured environments. This allows for better focus and participation during activities requiring attention, while still giving them the coping mechanisms they need. Changing their surroundings can also reduce triggers that lead to repetitive behaviors. Therefore, it helps children to regulate their emotions in a socially acceptable manner, while still meeting their behavioral and emotional needs.
## Looking for more information?
For more information on services offered by AIA or to book your free consultation, visit us at [https://www.azinstitute4autism.com](../index), contact us at [(480) 687-7099](tel:+14806877099), or email [info@azinstitute4autism.com](mailto:info@azinstitute4autism.com). If you are looking for more tips, check out monthly [blog posts](../library) from AIAs clinical director, Rula Diab!
For more information on services offered by AIA or to book your free consultation, visit us at [https://www.azinstitute4autism.com](../index), contact us at <ContactObfuscation phone="(480) 687-7099" hrefValue="+14806877099" obfuscatedText="9907-786 (084)" />, or email <ContactObfuscation email="info@azinstitute4autism.com" obfuscatedText="info [at] azinstitute4autism [dot] com" />. If you are looking for more tips, check out monthly [blog posts](../library) from AIAs clinical director, Rula Diab!
@@ -13,6 +13,8 @@ tags: []
draft: false
---
import ContactObfuscation from '../../../components/ContactObfuscation.astro';
Parenting a child with autism is an immensely rewarding experience, but it also presents unique challenges. To provide the best support, it is crucial to navigate our children's development while comprehending their needs.
In this blog post, we will delve into essential insights and practical tips to help parents better understand and support their child with autism.
@@ -41,6 +43,6 @@ Remember, each child with autism is unique. By educating yourself, seeking suppo
## Looking for more information?
For more information on services offered by AIA or to book your free consultation, visit us at [https://www.azinstitute4autism.com](../index), call us at [(480) 687-7099](tel:+14806877099), or email [hello@azinstitute4autism.com](mailto:hello@azinstitute4autism.com).
For more information on services offered by AIA or to book your free consultation, visit us at [https://www.azinstitute4autism.com](../index), call us at <ContactObfuscation phone="(480) 687-7099" hrefValue="+14806877099" obfuscatedText="9907-786 (084)" />, or email <ContactObfuscation email="hello@azinstitute4autism.com" obfuscatedText="hello [at] azinstitute4autism [dot] com" />.
If you are looking for more tips, check out [monthly blog posts](../library) from AIAs clinical director, Rula Diab!
@@ -13,6 +13,8 @@ tags: []
draft: false
---
import ContactObfuscation from '../../../components/ContactObfuscation.astro';
La generalización es un aspecto crucial del aprendizaje, ya que garantiza que las habilidades y los comportamientos se extiendan más allá del entorno de aprendizaje inicial. Sin generalización, los individuos pueden tener dificultades para aplicar lo que han aprendido a nuevas situaciones, limitando la efectividad de su educación o terapia.
En la ciencia del comportamiento, la generalización se refiere al proceso por el cual una conducta aprendida se aplica a diferentes estímulos, respuestas o entornos.
@@ -39,6 +41,6 @@ Comprender los tres tipos principales de generalización (estímulo, respuesta y
## ¿Buscas más información?
Para más información sobre los servicios ofrecidos por AIA, o para reservar su consulta gratuita, visítenos en [https://www.azinstitute4autism.com](../../index), contáctenos en [(480) 687-7099](tel:+14806877099), o correo electrónico [info@azinstitute4autism.com](mailto:info@azinstitute4autism.com).
Para más información sobre los servicios ofrecidos por AIA, o para reservar su consulta gratuita, visítenos en [https://www.azinstitute4autism.com](../../index), contáctenos en <ContactObfuscation phone="(480) 687-7099" hrefValue="+14806877099" obfuscatedText="9907-786 (084)" />, o correo electrónico <ContactObfuscation email="info@azinstitute4autism.com" obfuscatedText="info [at] azinstitute4autism [dot] com" />.
Si buscas más consejos, ¡consulta las [publicaciones mensuales de nuestro blog](../../library) de la directora clínica de AIA, Rula Diab!
+5 -5
View File
@@ -16,9 +16,9 @@ The Arizona Institute for Autism offers a variety of evidence-based, clinical se
Our ABA Therapy programs address the comprehensive needs of each learner, equipping children with the skills necessary to become independent learners.
### Services We Offer
### Behavioral Services We Offer
Individual Learning
<p class="eyebrow">Individual Learning</p>
#### ABA Therapy 1:1 Program
@@ -28,7 +28,7 @@ The ABA Therapy 1:1 program, designed for children aged 2 to 8 years, adopts an
Our ABA therapy focuses on improving socially significant behaviors by using positive reinforcement, systematic teaching, and data-driven methods. AIA Clinicians focus on tailoring our services to accommodate the unique neurodevelopmental profiles of each learner.
Group Learning
<p class="eyebrow">Group Learning</p>
#### ABA Therapy 1:2 Academic Readiness Program
@@ -38,7 +38,7 @@ The ABA Therapy 1:2 Academic Readiness Program is specifically designed for chil
The primary developmental milestones emphasized in this program include social, communication, cognitive, social-emotional, executive functioning, and motor skills. Throughout the Academic Readiness Program, your child will engage in a variety of structured electives such as speech therapy, recreational therapy, and music therapy. These electives, including circle time, arts and crafts, music, play, and other group interactions, that are designed to support skills development in these key areas.
Family Education
<p class="eyebrow">Family Education</p>
#### Parent Consultation Program
@@ -50,7 +50,7 @@ Our goal is to empower families with the knowledge, tools, and support they need
These consultations provide parents with personalized guidance, training, and strategies tailored to their childs unique needs.
<Button href="https://form.jotform.com/231638510219149" target="_blank">Make an Appointment</Button>
<Button href="https://form.jotform.com/231638510219149" target="_blank" center>Make an Appointment</Button>
## About Rula Diab, Chief Clinical Director, BCBA, LBA
@@ -50,4 +50,4 @@ ADOS is a structured evaluation tool used by our specialists to assess behaviors
This evaluation involves observing how your child interacts with various play-based social scenarios. For younger children, this may include activities like playing with blocks or having a snack, while older children who communicate verbally may participate in conversations about daily activities, such as attending school.
<Button href="https://form.jotform.com/231638510219149" target="_blank">Make an Appointment</Button>
<Button href="https://form.jotform.com/231638510219149" target="_blank" center>Make an Appointment</Button>
+3 -3
View File
@@ -16,7 +16,7 @@ Apply to join the Arizona Institute for Autism (AIA) team today. We are on a mis
Our talented and passionate employees are a critical piece to the high-quality Autism services we provide along with our commitment to behavioral health, school solutions, and community outreach.
<Button href="https://www.indeed.com/cmp/Arizona-Institute-For-Autism/jobs" target="_blank">View Open Positions</Button>
<Button href="https://www.indeed.com/cmp/Arizona-Institute-For-Autism/jobs" target="_blank" center>View Open Positions</Button>
### Our Benefits
@@ -108,7 +108,7 @@ Work someone you love. Begin your journey with Arizona's leading ABA Integrated
![logo-BACB](/assets/images/logo-BACB.png)
<Button href="https://www.indeed.com/cmp/Arizona-Institute-For-Autism/jobs" target="_blank">View Open Positions</Button>
<Button href="https://www.indeed.com/cmp/Arizona-Institute-For-Autism/jobs" target="_blank" center>View Open Positions</Button>
## Benefits of Working at Arizona Institute for Autism
@@ -170,4 +170,4 @@ Take a fresh and innovative approach as a member of the Corporate team. Take on
Our specialty interns help shape the future of our center carefully curated clinical, operations, advocacy, people, and other departments.
<Button href="https://www.indeed.com/cmp/Arizona-Institute-For-Autism/jobs" target="_blank">View Open Positions</Button>
<Button href="https://www.indeed.com/cmp/Arizona-Institute-For-Autism/jobs" target="_blank" center>View Open Positions</Button>
@@ -7,24 +7,25 @@ lang: "en"
translationKey: "contact"
draft: false
---
import ContactObfuscation from '../../../components/ContactObfuscation.astro';
import Button from '../../../components/Button.astro';
## Schedule a Tour
Reach out to one of our expert Client Advocates to see if ABA-integrated therapy is right for your learner.
Schedule Tour
<Button href="/tour">Schedule Tour</Button>
## Reach Out
If you have questions about any of our services and programs, feel free to use the contact form on this page. You may also call or email using the following information.
General Inquiries: 9907-786 (084)
**General Inquiries:** <ContactObfuscation phone="(480) 687-7099" hrefValue="+14806877099" obfuscatedText="9907-786 (084)" />
Billing Inquiries: 9240-807 (206)
**Billing Inquiries:** <ContactObfuscation phone="(602) 708-0429" hrefValue="+16027080429" obfuscatedText="9240-807 (206)" />
Email: moc.msitua4etutitsniza@ofni
**Email:** <ContactObfuscation email="info@azinstitute4autism.com" obfuscatedText="moc.msitua4etutitsniza@ofni" />
Address: 8901 E. Raintree Drive, St #160 Scottsdale, AZ 85260
**Address:** 8901 E. Raintree Drive, St #160 Scottsdale, AZ 85260
Hours: Monday - Friday, 8am - 6pm MST
### Contact AIA
**Hours:** Monday - Friday, 8am - 6pm MST
@@ -7,6 +7,8 @@ lang: "en"
translationKey: "donate-autism-giveback"
draft: false
---
import ContactObfuscation from '../../../components/ContactObfuscation.astro';
## Ways to Giveback
Every act of generosity, no matter how big or small, makes a difference at the Arizona Institute for Autism. Whether you donate items directly, support our Amazon iGiveback Wishlist, or contribute to our 501(c)(3) public charity, your support helps improve the lives of children with autism, their families, and the communities around them.
@@ -21,7 +23,7 @@ Visit AIAs Amazon iGiveback Wishlist today, pick an item (or two!), and help
### Host a Fundraiser
Would you like to host a fundraiser to benefit the Arizona Institute for Autism? Please contact us at moc.msitua4etutitsniza@ofni and a member of our team will be in touch to discuss next steps! All potential third party events will be evaluated in terms of their alignment with Arizona Institute for Autism's mission.
Would you like to host a fundraiser to benefit the Arizona Institute for Autism? Please contact us at <ContactObfuscation email="info@azinstitute4autism.com" obfuscatedText="moc.msitua4etutitsniza@ofni" /> and a member of our team will be in touch to discuss next steps! All potential third party events will be evaluated in terms of their alignment with Arizona Institute for Autism's mission.
## Community 4 Autism
File diff suppressed because it is too large Load Diff
@@ -9,6 +9,7 @@ draft: false
---
import Button from '../../../components/Button.astro';
import ContactObfuscation from '../../../components/ContactObfuscation.astro';
## Learner Social Club
@@ -19,13 +20,9 @@ Every child needs opportunities to have fun, make friends, and build social conn
Our integrative wellness enrichment services include:
- Music
- Art
- Games
- Dance
- Recreational activities
These thoughtfully designed activities help nurture your learners mind and body, providing holistic wellness and saving you valuable time.
@@ -35,21 +32,17 @@ These thoughtfully designed activities help nurture your learners mind and bo
To ensure the best experience for your learner, please keep the following in mind:
- Potty Training: Learners must be potty-trained before enrolling.
- Wipes: Pack wipes for your learner. These can be stored at our clinic.
- Snack & Water: Send your learner with a snack and water. These items can also be stored on-site.
- Pause/Cancellation Notice: Please provide AIA staff with at least a 30-day notice if you plan to pause or cancel your Learner Social Club services.
### Program Schedule & Ages Served
![stem-sped-social-learners](/assets/images/stem-sped-social-learners.webp)
The Learner Social Club currently serves youth learners aged 817 years . Our flexible scheduling options currently include:
The Learner Social Club currently serves youth learners aged 817 years. Our flexible scheduling options currently include:
- 2 days/week (Monday, Wednesday): 4:00 PM 6:00 PM
- 1 day/week (Wednesday only): 4:00 PM 6:00 PM
### Payment Information
@@ -58,6 +51,6 @@ The Learner Social Club currently serves youth learners aged 817 years . Our
Interested in Trying Out Our Program?
Get a 1-Day Trial Pass by emailing us at info [at] azinstitute4autism [dot] com .
Get a 1-Day Trial Pass by emailing us at <ContactObfuscation email="info@azinstitute4autism.com" obfuscatedText="info [at] azinstitute4autism [dot] com" />.
<Button href="https://form.jotform.com/231638510219149" target="_blank">Enroll Now</Button>
<Button href="https://form.jotform.com/231638510219149" target="_blank" center>Enroll Now</Button>
@@ -7,6 +7,8 @@ lang: "en"
translationKey: "privacy-policy"
draft: false
---
import ContactObfuscation from '../../../components/ContactObfuscation.astro';
Last updated: February 19, 2025
## Introduction
@@ -123,10 +125,10 @@ We may update this privacy policy periodically. All changes will be posted on th
If you have any questions or concerns about this privacy policy or wish to exercise your privacy rights, please contact us at:
- Email: info@azinstitute4autism.com
- Email: <ContactObfuscation email="info@azinstitute4autism.com" obfuscatedText="info [at] azinstitute4autism [dot] com" />
- Address: 8901 E. Raintree Drive, Suite #160 Scottsdale Arizona 85260
- Phone: [(480) 687-7099](tel:+14806877099)
- Phone: <ContactObfuscation phone="(480) 687-7099" hrefValue="+14806877099" obfuscatedText="9907-786 (084)" />
By using our website and services, you confirm that you have read, understood, and agree to the terms outlined in this privacy policy.
@@ -7,6 +7,8 @@ lang: "en"
translationKey: "schedule-consultation"
draft: false
---
import ContactObfuscation from '../../../components/ContactObfuscation.astro';
We're here for you!
A Client Advocate from Arizona Institute for Autism will contact you within one business day upon submission of this form.
@@ -37,13 +39,13 @@ Along with ABA therapy for children and teens diagnosed with autism, our support
![stem sped tech kids](/assets/images/stem-sped-tech-kids_446x.jpg) ![call us to schedule a free consultation](/assets/images/phone-1.svg) Phone
(480) 687-7099
<ContactObfuscation phone="(480) 687-7099" hrefValue="+14806877099" obfuscatedText="9907-786 (084)" />
![email for more information](/assets/images/mail.svg)
Email
info@abaclinicaz.com
<ContactObfuscation email="info@abaclinicaz.com" obfuscatedText="info [at] abaclinicaz [dot] com" />
![located in scottsdale](/assets/images/map-pin.svg)
@@ -7,24 +7,25 @@ lang: "es"
translationKey: "contact"
draft: false
---
import ContactObfuscation from '../../../components/ContactObfuscation.astro';
import Button from '../../../components/Button.astro';
## Programar una visita
Comuníquese con uno de nuestros defensores de clientes expertos para ver si la terapia integrada con ABA es adecuada para su alumno.
Programar visita
<Button href="/tour">Programar visita</Button>
## Comunícate
Si tiene preguntas sobre alguno de nuestros servicios y programas, no dude en utilizar el formulario de contacto de esta página. También puede llamar o enviar un correo electrónico utilizando la siguiente información.
Consultas generales: 9907-786 (084)
**Consultas generales:** <ContactObfuscation phone="(480) 687-7099" hrefValue="+14806877099" obfuscatedText="9907-786 (084)" />
Consultas de facturación: 9240-807 (206)
**Consultas de facturación:** <ContactObfuscation phone="(602) 708-0429" hrefValue="+16027080429" obfuscatedText="9240-807 (206)" />
Correo electrónico: moc.msitua4etutitsniza@ofni
**Correo electrónico:** <ContactObfuscation email="info@azinstitute4autism.com" obfuscatedText="moc.msitua4etutitsniza@ofni" />
Dirección: 8901 E. Raintree Drive, St #160 Scottsdale, AZ 85260
**Dirección:** 8901 E. Raintree Drive, St #160 Scottsdale, AZ 85260
Horas: Lunes - Viernes, 8am - 6pm MST
### Contactar a AIA
**Horas:** Lunes - Viernes, 8am - 6pm MST
@@ -9,6 +9,7 @@ draft: false
---
import Button from '../../../components/Button.astro';
import ContactObfuscation from '../../../components/ContactObfuscation.astro';
## Learner Social Club
@@ -46,7 +47,7 @@ Para garantizar la mejor experiencia para su hijo/a, tenga en cuenta lo siguient
![estudiantes participando en actividades sociales y STEM](/assets/images/stem-sped-social-learners.webp)
Learner Social Club actualmente atiende a jóvenes de 8 a 17 años . Nuestras opciones de horario flexible actualmente incluyen:
Learner Social Club actualmente atiende a jóvenes de 8 a 17 años. Nuestras opciones de horario flexible actualmente incluyen:
- 2 días/semana (lunes y miércoles): 4:00 p. m. 6:00 p. m.
@@ -58,6 +59,6 @@ Learner Social Club actualmente atiende a jóvenes de 8 a 17 años . Nuestras op
¿Le interesa probar nuestro programa?
Obtenga un pase de prueba de 1 día enviándonos un correo electrónico a info [at] azinstitute4autism [dot] com .
Obtenga un pase de prueba de 1 día enviándonos un correo electrónico a <ContactObfuscation email="info@azinstitute4autism.com" obfuscatedText="info [at] azinstitute4autism [dot] com" />.
<Button href="https://form.jotform.com/231638510219149" target="_blank">Enroll Now</Button>
+3 -3
View File
@@ -12,8 +12,7 @@ const organizationSchema = {
'@type': 'MedicalOrganization',
name: site.name,
url: site.url,
telephone: site.phone,
email: site.email,
// Contact details are rendered through ContactObfuscation to avoid plain text in HTML.
address: {
'@type': 'PostalAddress',
streetAddress: '8901 E Raintree Dr Ste 160',
@@ -30,8 +29,9 @@ const organizationSchema = {
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width" />
<link rel="icon" href="/assets/images/aia-logo.svg" />
<link rel="icon" href="/assets/images/favicon.svg" />
<Seo title={title} description={description} canonical={canonical} image={image} lang={lang} type={type} />
<slot name="head" />
<script type="application/ld+json" set:html={JSON.stringify(organizationSchema)} />
</head>
<body>
+15 -5
View File
@@ -1,13 +1,16 @@
---
import BaseLayout from './BaseLayout.astro';
import FormShell from '../components/FormShell.astro';
import TeamPage from '../components/TeamPage.astro';
import AboutPage from '../components/AboutPage.astro';
import ServicesPage from '../components/ServicesPage.astro';
import TeamPage from '../components/pages/TeamPage.astro';
import AboutPage from '../components/pages/AboutPage.astro';
import ServicesPage from '../components/pages/ServicesPage.astro';
import CareersPage from '../components/pages/CareersPage.astro';
import ContactPage from '../components/pages/ContactPage.astro';
import FaqPage from '../components/pages/FaqPage.astro';
import PageHero from '../components/PageHero.astro';
import { pageHeroImages } from '../data/page-visuals';
const { entry } = Astro.props;
const showForm = ['client-consultation', 'contact', 'schedule-consultation', 'referrals'].includes(entry.data.slug);
const showForm = ['client-consultation', 'schedule-consultation', 'referrals'].includes(entry.data.slug);
const serviceTitles: Record<string, Record<string, string>> = {
en: {
'aba-therapy': 'Behavioral',
@@ -51,13 +54,20 @@ const isService = Boolean(serviceTitles[entry.data.lang]?.[entry.data.slug]);
const heroImage = pageHeroImages[entry.data.slug];
---
<BaseLayout title={entry.data.title} description={entry.data.description} canonical={entry.data.canonical} image={entry.data.featuredImage || heroImage} lang={entry.data.lang}>
<PageHero title={bannerTitle} image={heroImage} constrain />
{entry.data.slug === 'contact' && <Fragment slot="head"><link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" crossorigin="" /></Fragment>}
<PageHero title={bannerTitle} subtitle={entry.data.slug === 'faqs' ? 'FAQs' : undefined} image={heroImage} constrain />
{entry.data.slug === 'team' && entry.data.lang === 'en'
? <TeamPage />
: entry.data.slug === 'about' && entry.data.lang === 'en'
? <AboutPage />
: entry.data.slug === 'services' && entry.data.lang === 'en'
? <ServicesPage />
: entry.data.slug === 'careers' && entry.data.lang === 'en'
? <CareersPage />
: entry.data.slug === 'faqs' && entry.data.lang === 'en'
? <FaqPage><slot /></FaqPage>
: entry.data.slug === 'contact'
? <ContactPage title={bannerTitle}><slot /></ContactPage>
: <article class:list={['prose', 'source-page', 'container', { 'service-page': isService }]}><slot /></article>}
{showForm && <FormShell title={bannerTitle} showBackground={entry.data.slug === 'client-consultation'} />}
</BaseLayout>
+2 -2
View File
@@ -1,11 +1,11 @@
---
import { getCollection } from 'astro:content';
import BaseLayout from '../../layouts/BaseLayout.astro';
import Hero from '../../components/Hero.astro';
import HomeHero from '../../components/home/HomeHero.astro';
import BlogCard from '../../components/BlogCard.astro';
const posts = (await getCollection('blog', ({ data }) => data.lang === 'ar' && !data.draft)).slice(0, 3);
---
<BaseLayout title="معهد أريزونا للتوحد" description="موارد ودعم للأسر والأطفال المصابين بالتوحد." canonical="https://www.azinstitute4autism.com/ar" lang="ar">
<Hero eyebrow="Arizona Institute for Autism" title="الصحة السلوكية والتعليم الخاص" description="محتوى عربي متاح من مكتبة معهد أريزونا للتوحد." image="/assets/images/playing-boy.webp" alt="" />
<HomeHero section={{ type: 'hero', eyebrow: 'Arizona Institute for Autism', heading: 'الصحة السلوكية والتعليم الخاص', body: 'محتوى عربي متاح من مكتبة معهد أريزونا للتوحد.', cta: { label: 'ابدأ الآن', href: '/ar' }, image: 'playing-boy.webp', imageAlt: '' }} />
<section class="section"><div class="container"><div class="section-heading"><h2>المكتبة</h2></div><div class="card-grid">{posts.map((post) => <BlogCard post={post} prefix="/ar" />)}</div></div></section>
</BaseLayout>
+1 -1
View File
@@ -1,4 +1,4 @@
---
import HomePage from '../../components/HomePage.astro';
import HomePage from '../../components/pages/HomePage.astro';
---
<HomePage lang="es" />
+1 -1
View File
@@ -1,4 +1,4 @@
---
import HomePage from '../components/HomePage.astro';
import HomePage from '../components/pages/HomePage.astro';
---
<HomePage lang="en" />
+23
View File
@@ -0,0 +1,23 @@
const allowIndexing = import.meta.env.PUBLIC_ALLOW_INDEXING === 'true';
const body = allowIndexing
? [
'User-agent: *',
'Allow: /',
'Sitemap: https://www.azinstitute4autism.com/sitemap.xml',
''
].join('\n')
: [
'User-agent: *',
'Allow: /',
'# Staging and migration builds emit meta robots noindex,nofollow.',
''
].join('\n');
export function GET() {
return new Response(body, {
headers: {
'Content-Type': 'text/plain; charset=utf-8'
}
});
}
+1 -7
View File
@@ -1,5 +1,5 @@
.prose { padding-block: var(--space-xl); }
.prose h2, .prose h3 { margin-top: 2em; }
.prose h2, .prose h3 { margin-top: 1.75em; }
.prose img { border-radius: var(--radius-md); margin-block: var(--space-lg); }
.prose a { overflow-wrap: anywhere; text-decoration: none; }
.prose p a, .prose td a { color: var(--color-accent-strong); text-decoration: none; }
@@ -9,15 +9,9 @@
.blog-post-body blockquote p { color: var(--color-primary); margin: 0 0 1.55rem; }
.source-page { max-width: 980px; }
.source-page > h2:first-of-type { margin-top: 0; }
.service-page > p > img, .service-page > img { float: right; margin: .5rem 0 1.5rem 2rem; max-height: 230px; max-width: 300px; object-fit: cover; }
.service-page h3 { font-size: 2rem; }
.service-page h3 + p { color: var(--color-accent-strong); font-family: var(--font-accent); font-size: 1.5rem; margin-bottom: -.8rem; }
.blog-post-body { max-width: 960px; }
.blog-post-body p { margin-bottom: 1.55rem; }
.blog-post-body table { border: 1px solid #d7d9e3; border-collapse: collapse; display: table; margin: 0 0 1.4rem; width: 100%; }
.blog-post-body td, .blog-post-body th { border: 1px solid #d7d9e3; padding: 10px; }
.blog-post-body th { background: var(--color-primary); color: #fff; }
.blog-post-body td { background: #fff; color: var(--color-primary); }
@media (max-width: 700px) {
.service-page > p > img, .service-page > img { float: none; margin: 1.5rem 0; max-width: 100%; width: 100%; }
}
-4
View File
@@ -1,7 +1,3 @@
.button { background: var(--color-accent); border: 2px solid var(--color-accent); border-radius: 4px; color: var(--color-primary); display: inline-block; padding: .65rem 1.2rem; text-decoration: none; transition: .3s linear all; }
.button:hover { background: transparent; border-color: var(--color-accent-strong); color: var(--color-primary); }
.button-small { font-size: .9rem; }
.button-light { background: white; color: var(--color-primary-dark); }
.card { background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-md); overflow: hidden; }
.card img { aspect-ratio: 16/10; object-fit: cover; width: 100%; }
.card-body { padding: var(--space-lg); }
+1
View File
@@ -4,3 +4,4 @@
@import './components.css';
@import './forms.css';
@import './blog.css';
@import './pages.css';
-1
View File
@@ -14,6 +14,5 @@ img { display: block; height: auto; max-width: 100%; }
.skip-link:focus { top: 1rem; }
.page-header { padding-block: var(--space-xl); text-align: center; }
.feature-grid { align-items: center; display: grid; gap: clamp(2rem, 6vw, 5rem); grid-template-columns: 1fr 1fr; }
.cream-section { background: var(--color-tint); }
@media (max-width: 850px) { .card-grid { grid-template-columns: 1fr; } .split { align-items: flex-start; flex-direction: column; } }
@media (max-width: 760px) { .feature-grid { grid-template-columns: 1fr; } }
+5
View File
@@ -0,0 +1,5 @@
.service-page > p > img, .service-page > img { float: right; margin: .5rem 0 0 2rem; max-height: 300px; max-width: 300px; object-fit: cover; }
.service-page h3 { font-size: 2rem; }
@media (max-width: 700px) {
.service-page > p > img, .service-page > img { float: none; margin: 1.5rem 0; max-width: 100%; width: 100%; }
}
+17 -7
View File
@@ -3,16 +3,26 @@
@font-face { font-family: Rubik; src: url('/assets/fonts/Rubik/regular.woff2') format('woff2'); font-display: swap; }
@font-face { font-family: Rubik; src: url('/assets/fonts/Rubik/700.woff2') format('woff2'); font-weight: 700; font-display: swap; }
body { color: var(--color-text); font-family: var(--font-body); font-size: 16px; line-height: 1.625; }
h1, h2, h3 { color: var(--color-primary); font-family: var(--font-heading); line-height: 1.15; text-wrap: balance; }
h1, h2, h3, h4 { color: var(--color-primary); font-family: var(--font-heading); line-height: 1.15; text-wrap: balance; }
.no-balance { text-wrap: auto; }
h1 { font-size: clamp(2.5rem, 5vw, 3rem); line-height: 1.12; margin: 0 0 var(--space-md); }
h2 { font-size: clamp(2rem, 4vw, 2.4rem); line-height: 1.25; }
h3 { font-size: 1.5rem; }
a { color: var(--color-primary); }
.eyebrow { color: var(--color-secondary); font-size: .8rem; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; }
.lede, .hero-copy { color: var(--color-muted); font-size: 1.25rem; }
.script-label { color: var(--color-accent-strong); font-family: var(--font-accent); font-size: 2rem; margin: 0; }
.script-label + h2 { margin-top: 0; }
h3 { font-size: clamp(1.8rem, 3.4vw, 2rem); }
h4 { font-size: clamp(1.5rem, 3vw, 1.8rem); }
a, a:visited { color: var(--color-accent-strong); text-decoration: none; }
a:hover, a:active { color: var(--color-accent); }
.eyebrow {
margin: 0;
color: var(--color-accent-strong);
font-family: var(--font-accent);
font-size: 2rem;
}
.eyebrow + :is(h1, h2, h3, h4, h5, h6) {
margin-block-start: 0;
}
.prose :where(ul, ol) {
margin-block: 0 1.5rem;
-220
View File
@@ -1,220 +0,0 @@
import fs from 'node:fs/promises';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
const mirror = path.resolve(root, '../www.azinstitute4autism.com');
const content = path.join(root, 'src/content');
const assets = path.join(root, 'public/assets');
const reports = path.join(root, 'reports');
const site = 'https://www.azinstitute4autism.com';
const mkdir = (value) => fs.mkdir(value, { recursive: true });
const quote = (value = '') => JSON.stringify(String(value).replace(/\s+/g, ' ').trim());
const csv = (value = '') => `"${String(value).replaceAll('"', '""')}"`;
const blogPreamble = /^# .+\n\n!\[[^\]]*\]\(\/assets\/images\/rula-diab-avatar\.jpg\)\n\n[^\n]+\n\n[^\n]+\n\n!\[[^\]]*\]\([^)]+\)\n\n/;
async function walk(dir, prefix = '') {
const output = [];
for (const entry of await fs.readdir(dir, { withFileTypes: true })) {
const relative = path.join(prefix, entry.name);
if (entry.isDirectory()) output.push(...await walk(path.join(dir, entry.name), relative));
else output.push(relative);
}
return output;
}
function logical(file) {
return file.replace(/(?:\.html)?\?(?:hsLang=[^.]+|hs_amp=true)\.html$/, '.html');
}
function selectCanonical(files) {
const map = new Map();
for (const file of files) {
if (!file.endsWith('.html') || file.includes('hs_amp=true') || /\/(?:page|author)\//.test(file)) continue;
const target = logical(file);
const key = `${target.startsWith('ar/') ? 'ar' : target.startsWith('es/') ? 'es' : 'en'}:${target}`;
if (!map.has(key) || (!file.includes('?') && map.get(key).includes('?'))) map.set(key, file);
}
return [...map.values()];
}
function decode(value = '') {
return value
.replace(/&#(\d+);/g, (_, code) => String.fromCodePoint(Number(code)))
.replace(/&#x([\da-f]+);/gi, (_, code) => String.fromCodePoint(parseInt(code, 16)))
.replaceAll('&amp;', '&').replaceAll('&quot;', '"').replaceAll('&#39;', "'")
.replaceAll('&lt;', '<').replaceAll('&gt;', '>').replaceAll('&nbsp;', ' ');
}
function text(value = '') {
return decode(value.replace(/<[^>]+>/g, ' ')).replace(/\s+/g, ' ').trim();
}
function normalizeHref(value = '') {
return decode(value)
.replace(/^https?:\/\/www\.azinstitute4autism\.com/, '')
.replace(/(?:\.html)?(?:%3F|\?)(?:hsLang=[^.#]+|hs_amp=true)(?:\.html)?$/, '')
.replace(/\.html$/, '')
.replace(/^index$/, '/');
}
function inline(value = '') {
return text(value.replace(/<a[^>]+href=["']([^"']+)["'][^>]*>([\s\S]*?)<\/a>/gi, (_, href, label) => {
const cleanLabel = text(label);
return cleanLabel ? `[${cleanLabel}](${normalizeHref(href)})` : '';
}));
}
function meta(html, name, property = false) {
const key = property ? 'property' : 'name';
const pattern = new RegExp(`<meta[^>]+${key}=["']${name}["'][^>]+content=["']([^"']*)`, 'i');
const reverse = new RegExp(`<meta[^>]+content=["']([^"']*)["'][^>]+${key}=["']${name}["']`, 'i');
return decode(html.match(pattern)?.[1] || html.match(reverse)?.[1] || '');
}
function asset(value = '') {
const match = value.match(/(?:hs-fs\/)?hubfs\/([^?#]+)/);
return match ? `/assets/images/${path.basename(decodeURIComponent(match[1])).split('?')[0]}` : undefined;
}
function normalizeTable(value = '') {
return value
.replace(/(<a\b[^>]*\bhref=["'])([^"']+)(["'])/gi,
(_, prefix, href, suffix) => `${prefix}${normalizeHref(href)}${suffix}`)
.replace(/(<img\b[^>]*\bsrc=["'])([^"']+)(["'])/gi,
(_, prefix, src, suffix) => `${prefix}${asset(src) || src}${suffix}`);
}
function markdownFrom(html) {
let body = html.match(/<main[\s\S]*?<\/main>/i)?.[0] ||
html.match(/blog-post__body[\s\S]*?(?=<footer|blog-post__tags|<\/article>)/i)?.[0] ||
'';
const tables = [];
body = body
.replace(/<(script|style|noscript|header|footer|nav|form)\b[\s\S]*?<\/\1>/gi, '')
.replace(/<table[\s\S]*?<\/table>/gi, (match) => {
const token = `__TABLE_${tables.length}__`;
tables.push(normalizeTable(match));
return `\n\n${token}\n\n`;
})
.replace(/<blockquote[^>]*>([\s\S]*?)<\/blockquote>/gi, (_, value) => `\n\n> ${inline(value)}\n\n`)
.replace(/<img[^>]+src=["']([^"']+)["'][^>]*alt=["']([^"']*)["'][^>]*>/gi, (_, src, alt) => {
const local = asset(src);
return local ? `\n\n![${text(alt)}](${local})\n\n` : '';
})
.replace(/<h([1-6])[^>]*>([\s\S]*?)<\/h\1>/gi, (_, level, value) => `\n\n${'#'.repeat(Number(level))} ${inline(value)}\n\n`)
.replace(/<li[^>]*>([\s\S]*?)<\/li>/gi, (_, value) => `\n- ${inline(value)}`)
.replace(/<p[^>]*>([\s\S]*?)<\/p>/gi, (_, value) => `\n\n${inline(value)}\n\n`)
.replace(/<br\s*\/?>/gi, '\n')
.replace(/<[^>]+>/g, ' ');
body = decode(body).replace(/[ \t]+/g, ' ').replace(/^\s+$/gm, '').replace(/\n{3,}/g, '\n\n').trim();
return tables.reduce((output, table, index) => output.replaceAll(`__TABLE_${index}__`, table), body);
}
function record(file, html) {
const target = logical(file);
const lang = target.startsWith('ar/') ? 'ar' : target.startsWith('es/') ? 'es' : 'en';
const url = target === 'index.html' ? '/' : `/${target.replace(/\/index\.html$/, '').replace(/\.html$/, '')}`;
const title = text(html.match(/<title[^>]*>([\s\S]*?)<\/title>/i)?.[1] || url);
const h1 = text(html.match(/<h1[^>]*>([\s\S]*?)<\/h1>/i)?.[1] || title);
const published = meta(html, 'article:published_time', true) ||
html.match(/<time[^>]+datetime=["']([^"']+)/i)?.[1] || '';
return {
file, lang, url, title, h1,
description: meta(html, 'description'),
image: asset(meta(html, 'og:image', true)),
date: /^\d{4}-\d{2}-\d{2}/.test(published) ? published.slice(0, 10) : '2024-01-01',
markdown: markdownFrom(html)
};
}
function frontmatter(item, blog) {
const slug = ['/', '/ar', '/es'].includes(item.url) ? 'index' : item.url.split('/').filter(Boolean).at(-1);
const lines = [
'---',
`title: ${quote(item.title)}`,
`description: ${quote(item.description)}`,
`slug: ${quote(slug)}`,
`canonical: ${quote(`${site}${item.url}`)}`,
`lang: ${quote(item.lang)}`,
`translationKey: ${quote(slug)}`,
item.image ? `featuredImage: ${quote(item.image)}` : '',
blog ? `date: "${item.date}"` : '',
blog ? 'author: "rula-diab"' : '',
blog ? 'category: "Library"' : '',
blog ? 'tags: []' : '',
'draft: false',
'---',
'',
(blog ? item.markdown.replace(blogPreamble, '') : item.markdown) || `# ${item.h1}`,
''
].filter((line) => line !== '');
return `${lines.join('\n')}\n`;
}
async function copySourceAssets(files) {
const rows = ['source,target,size,kind'];
const seen = new Set();
await Promise.all(['images', 'fonts', 'downloads'].map((kind) => fs.rm(path.join(assets, kind), { recursive: true, force: true })));
for (const file of files.filter((name) => name.startsWith('hubfs/') || name.startsWith('hs-fs/hubfs/') || name.startsWith('_hcms/googlefonts/'))) {
const font = file.startsWith('_hcms/googlefonts/');
const raw = font ? file.replace('_hcms/googlefonts/', '') : path.basename(file).split('?')[0];
if (!font && !/\.(?:avif|gif|jpe?g|png|svg|webp|pdf)$/i.test(raw)) continue;
const key = `${font}:${raw}`;
if (!raw || seen.has(key)) continue;
seen.add(key);
const kind = font ? 'fonts' : raw.endsWith('.pdf') ? 'downloads' : 'images';
const target = path.join(assets, kind, raw);
await mkdir(path.dirname(target));
await fs.copyFile(path.join(mirror, file), target);
const size = (await fs.stat(target)).size;
rows.push([file, path.relative(root, target), size, kind].map(csv).join(','));
}
await fs.writeFile(path.join(reports, 'asset-inventory.csv'), `${rows.join('\n')}\n`);
}
async function removeMissingFeaturedImages() {
for (const type of ['pages', 'blog']) {
for (const file of await walk(path.join(content, type))) {
const target = path.join(content, type, file);
let source = await fs.readFile(target, 'utf8');
const image = source.match(/^featuredImage:\s*"\/assets\/images\/([^"]+)"/m)?.[1];
if (!image) continue;
if (!await fs.access(path.join(assets, 'images', image)).then(() => true).catch(() => false)) {
source = source.replace(/^featuredImage:.*\n/m, '');
await fs.writeFile(target, source);
}
}
}
}
async function main() {
const files = await walk(mirror);
const selected = selectCanonical(files);
const records = [];
for (const file of selected) records.push(record(file, await fs.readFile(path.join(mirror, file), 'utf8')));
await Promise.all(['pages', 'blog', 'authors'].map((type) => fs.rm(path.join(content, type), { recursive: true, force: true })));
for (const lang of ['en', 'ar', 'es']) {
for (const type of ['pages', 'blog', 'authors']) await mkdir(path.join(content, type, lang));
}
for (const item of records) {
const blog = item.url.includes('/library/') && !item.url.endsWith('/library');
const slug = ['/', '/ar', '/es'].includes(item.url) ? 'index' : item.url.split('/').filter(Boolean).at(-1);
await fs.writeFile(path.join(content, blog ? 'blog' : 'pages', item.lang, `${slug}.md`), frontmatter(item, blog));
}
for (const lang of ['en', 'ar', 'es']) {
await fs.writeFile(path.join(content, 'authors', lang, 'rula-diab.md'), `---\nname: "Rula Diab"\nslug: "rula-diab"\ndescription: "Founder and clinical leader at Arizona Institute for Autism."\navatar: "/assets/images/rula-diab-avatar.jpg"\nlang: "${lang}"\ntranslationKey: "rula-diab"\n---\n`);
}
if (!records.some((item) => item.lang === 'ar' && item.url === '/ar')) {
await fs.writeFile(path.join(content, 'pages/ar/index.md'), `---\ntitle: "معهد أريزونا للتوحد"\ndescription: "صفحة عربية تمهيدية لمعهد أريزونا للتوحد."\nslug: "index"\ncanonical: "https://www.azinstitute4autism.com/ar"\nlang: "ar"\ntranslationKey: "home"\ndraft: false\n---\n\n<!-- TODO: Replace this placeholder with reviewed Arabic content. -->\n\nتتوفر المقالات العربية الحالية في المكتبة. يرجى مراجعة المحتوى العربي قبل النشر.\n`);
}
const rows = ['source_file,url,language,type,title,description,h1'];
for (const item of records) rows.push([item.file, item.url, item.lang, item.url.includes('/library/') ? 'blog' : 'page', item.title, item.description, item.h1].map(csv).join(','));
await mkdir(reports);
await fs.writeFile(path.join(reports, 'url-inventory.csv'), `${rows.join('\n')}\n`);
await copySourceAssets(files);
await removeMissingFeaturedImages();
console.log(`Fallback extraction completed: ${records.length} canonical records.`);
}
await main();
-198
View File
@@ -1,198 +0,0 @@
import fs from 'node:fs/promises';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import fg from 'fast-glob';
import { load } from 'cheerio';
import TurndownService from 'turndown';
import matter from 'gray-matter';
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
const mirror = path.resolve(root, '../www.azinstitute4autism.com');
const site = 'https://www.azinstitute4autism.com';
const contentRoot = path.join(root, 'src/content');
const publicRoot = path.join(root, 'public/assets');
const reportsRoot = path.join(root, 'reports');
const turndown = new TurndownService({ headingStyle: 'atx', bulletListMarker: '-' });
turndown.remove(['script', 'style', 'noscript', 'iframe']);
const mkdir = (dir) => fs.mkdir(dir, { recursive: true });
const clean = (value = '') => value.replace(/\s+/g, ' ').trim();
const csv = (value = '') => `"${String(value).replaceAll('"', '""')}"`;
const yaml = (value = '') => JSON.stringify(String(value));
const blogPreamble = /^# .+\n\n!\[[^\]]*\]\(\/assets\/images\/rula-diab-avatar\.jpg\)\n\n[^\n]+\n\n[^\n]+\n\n!\[[^\]]*\]\([^)]+\)\n\n/;
function contentMarkdown(markdown, isBlog) {
return isBlog ? markdown.replace(blogPreamble, '') : markdown;
}
function logicalFile(file) {
return file.replace(/(?:\.html)?\?(?:hsLang=[^.]+|hs_amp=true)\.html$/, '.html');
}
function canonicalFiles(files) {
const selected = new Map();
for (const file of files) {
if (!file.endsWith('.html') || /\/(?:page|author)\//.test(file) || file.includes('hs_amp=true')) continue;
const logical = logicalFile(file);
const lang = logical.startsWith('ar/') ? 'ar' : logical.startsWith('es/') ? 'es' : 'en';
const key = `${lang}:${logical}`;
const current = selected.get(key);
if (!current || (!file.includes('?') && current.includes('?'))) selected.set(key, file);
}
return [...selected.values()];
}
function sourceUrl(file) {
const logical = logicalFile(file);
if (logical === 'index.html') return '/';
return `/${logical.replace(/\/index\.html$/, '').replace(/\.html$/, '')}`;
}
function isBlogFile(file) {
return file.startsWith('library/') || file.includes('/library/');
}
function languageFor(file, $) {
if (file.startsWith('ar/')) return 'ar';
if (file.startsWith('es/')) return 'es';
return $('html').attr('lang')?.split('-')[0] || 'en';
}
function localizeAsset(value = '') {
const decoded = value.replace(/^https?:\/\/[^/]+/, '').replace(/^\.\.\//, '/');
const match = decoded.match(/(?:\/)?(?:hs-fs\/)?hubfs\/([^?#]+)/);
if (!match) return undefined;
return `/assets/images/${path.basename(decodeURIComponent(match[1])).split('?')[0]}`;
}
function extractRecord(file, html) {
const $ = load(html);
const lang = languageFor(file, $);
const url = sourceUrl(file);
const title = clean($('title').first().text()) || clean($('h1').first().text()) || url;
const description = $('meta[name="description"]').attr('content') || '';
const h1 = clean($('h1').first().text());
const image = localizeAsset(
$('meta[property="og:image"]').attr('content') ||
$('.blog-post__body img, main img, .body-container-wrapper img').first().attr('src') ||
''
);
const alt = clean($('.blog-post__body img, main img, .body-container-wrapper img').first().attr('alt') || '');
const dateText = $('meta[property="article:published_time"]').attr('content') ||
$('time').first().attr('datetime') || '2024-01-01';
const date = /^\d{4}-\d{2}-\d{2}/.test(dateText) ? dateText.slice(0, 10) : '2024-01-01';
const selector = isBlogFile(file) ? '.blog-post__body' : 'main, .body-container-wrapper';
const body = $(selector).first().clone();
body.find('header, footer, nav, form, script, style, noscript, .hs_cos_wrapper_type_form').remove();
body.find('*').removeAttr('style').removeAttr('id').removeAttr('data-hs-cos-general-type').removeAttr('data-hs-cos-type');
body.find('a').each((_, element) => {
const href = $(element).attr('href');
if (!href) return;
$(element).attr('href', href
.replace(/\.html(?:%3F|\?)[^"]*$/, '')
.replace(/\.html$/, '')
.replace(/^index$/, '/'));
});
body.find('img').each((_, element) => {
const src = localizeAsset($(element).attr('src'));
if (src) $(element).attr('src', src);
});
const tables = [];
body.find('table').each((_, element) => {
const token = `__TABLE_${tables.length}__`;
tables.push($(element).prop('outerHTML'));
$(element).replaceWith(`\n\n${token}\n\n`);
});
const markdown = contentMarkdown(
tables.reduce((output, table, index) => output.replaceAll(`__TABLE_${index}__`, table),
turndown.turndown(body.html() || '').replace(/\n{3,}/g, '\n\n').trim()),
isBlogFile(file)
);
return { file, lang, url, title, description, h1, image, alt, date, markdown };
}
function frontmatter(record, type) {
const slug = ['/', '/ar', '/es'].includes(record.url) ? 'index' : record.url.split('/').filter(Boolean).at(-1);
const data = {
title: record.title,
description: record.description,
slug,
canonical: `${site}${record.url}`,
lang: record.lang,
translationKey: slug,
draft: false
};
if (record.image) data.featuredImage = record.image;
if (record.alt) data.alt = record.alt;
if (type === 'blog') Object.assign(data, {
date: record.date,
author: 'rula-diab',
category: 'Library',
tags: []
});
return matter.stringify(record.markdown || `# ${record.h1 || record.title}\n`, data);
}
async function copyAssets() {
const assets = await fg(['hubfs/*', 'hs-fs/hubfs/*', '_hcms/googlefonts/**/*'], { cwd: mirror, onlyFiles: true });
const inventory = ['source,target,size,kind'];
const seen = new Set();
await Promise.all(['images', 'fonts', 'downloads'].map((kind) => fs.rm(path.join(publicRoot, kind), { recursive: true, force: true })));
for (const source of assets) {
const original = path.join(mirror, source);
const stat = await fs.stat(original);
const isFont = source.startsWith('_hcms/googlefonts/');
const rawName = isFont ? source.replace('_hcms/googlefonts/', '') : path.basename(source).split('?')[0];
if (!isFont && !/\.(?:avif|gif|jpe?g|png|svg|webp|pdf)$/i.test(rawName)) continue;
if (!rawName || seen.has(`${isFont}:${rawName}`)) continue;
seen.add(`${isFont}:${rawName}`);
const target = path.join(publicRoot, isFont ? 'fonts' : rawName.endsWith('.pdf') ? 'downloads' : 'images', rawName);
await mkdir(path.dirname(target));
await fs.copyFile(original, target);
inventory.push([source, path.relative(root, target), stat.size, isFont ? 'font' : path.extname(rawName).slice(1)].map(csv).join(','));
}
await fs.writeFile(path.join(reportsRoot, 'asset-inventory.csv'), `${inventory.join('\n')}\n`);
}
async function main() {
await Promise.all([
mkdir(contentRoot), mkdir(publicRoot), mkdir(reportsRoot),
...['en', 'ar', 'es'].flatMap((lang) => [
mkdir(path.join(contentRoot, 'pages', lang)),
mkdir(path.join(contentRoot, 'blog', lang)),
mkdir(path.join(contentRoot, 'authors', lang))
])
]);
const files = canonicalFiles(await fg(['**/*.html'], { cwd: mirror, onlyFiles: true }));
const records = [];
for (const file of files) records.push(extractRecord(file, await fs.readFile(path.join(mirror, file), 'utf8')));
const corePages = new Set(records.filter((r) => !isBlogFile(r.file)).map((r) => r.file));
for (const record of records) {
const isBlog = isBlogFile(record.file);
if (!isBlog && !corePages.has(record.file)) continue;
const slug = ['/', '/ar', '/es'].includes(record.url) ? 'index' : record.url.split('/').filter(Boolean).at(-1);
const target = path.join(contentRoot, isBlog ? 'blog' : 'pages', record.lang, `${slug}.md`);
await fs.writeFile(target, frontmatter(record, isBlog ? 'blog' : 'page'));
}
for (const lang of ['en', 'ar', 'es']) {
await fs.writeFile(path.join(contentRoot, 'authors', lang, 'rula-diab.md'), matter.stringify('', {
name: 'Rula Diab',
slug: 'rula-diab',
description: 'Founder and clinical leader at Arizona Institute for Autism.',
avatar: '/assets/images/rula-diab-avatar.jpg',
lang,
translationKey: 'rula-diab'
}));
}
const urlRows = ['source_file,url,language,type,title,description,h1'];
for (const record of records) urlRows.push([
record.file, record.url, record.lang,
isBlogFile(record.file) ? 'blog' : 'page',
record.title, record.description, record.h1
].map(csv).join(','));
await fs.writeFile(path.join(reportsRoot, 'url-inventory.csv'), `${urlRows.join('\n')}\n`);
await copyAssets();
console.log(`Extracted ${records.length} canonical pages and posts.`);
}
await main();
+3 -3
View File
@@ -1,9 +1,9 @@
import fs from 'node:fs/promises';
const redirects = [
{ from: '/aba', to: '/aba-therapy', status: 301, reason: 'Brief alias to preserved mirror URL' },
{ from: '/autismevaluations', to: '/autism-evaluations', status: 301, reason: 'Brief alias to preserved mirror URL' },
{ from: '/learnersocialclub', to: '/learner-social-club', status: 301, reason: 'Brief alias to preserved mirror URL' }
{ from: '/aba', to: '/aba-therapy', status: 301, reason: 'Brief legacy alias to preserved URL' },
{ from: '/autismevaluations', to: '/autism-evaluations', status: 301, reason: 'Brief legacy alias to preserved URL' },
{ from: '/learnersocialclub', to: '/learner-social-club', status: 301, reason: 'Brief legacy alias to preserved URL' }
];
await fs.writeFile(new URL('../src/data/redirects.json', import.meta.url), `${JSON.stringify(redirects, null, 2)}\n`);
await fs.writeFile(new URL('../reports/redirect-map.csv', import.meta.url),