fix: restore missing live-site page imagery

- recover and self-host HubSpot-injected page hero assets
- map source heroes to page and library layouts
- restore the consultation form background
- add page imagery auditing
- update asset inventory and migration documentation
This commit is contained in:
2026-06-08 13:17:27 -07:00
parent 96edc6e538
commit bfed3bee15
31 changed files with 119 additions and 9 deletions
+3
View File
@@ -118,6 +118,7 @@ Astro prints the preview URL, normally `http://localhost:4321`.
```sh ```sh
npm run build npm run build
npm run audit:links npm run audit:links
npm run audit:images
``` ```
The link audit reads `dist/` after a successful build. If rendered output is The link audit reads `dist/` after a successful build. If rendered output is
@@ -281,6 +282,7 @@ npm run generate:sitemap
npm run generate:redirects npm run generate:redirects
npm run build npm run build
npm run audit:links npm run audit:links
npm run audit:images
``` ```
Extraction regenerates content files, copied assets, and inventories. Review Extraction regenerates content files, copied assets, and inventories. Review
@@ -332,6 +334,7 @@ Build first:
```sh ```sh
npm run build npm run build
npm run audit:links npm run audit:links
npm run audit:images
``` ```
### A content page is missing ### A content page is missing
+1
View File
@@ -14,6 +14,7 @@
"extract:full": "node tools/extract-site.mjs", "extract:full": "node tools/extract-site.mjs",
"crawl:live": "node tools/crawl-live-site.mjs", "crawl:live": "node tools/crawl-live-site.mjs",
"audit:links": "node tools/audit-links.mjs", "audit:links": "node tools/audit-links.mjs",
"audit:images": "node tools/audit-page-imagery.mjs",
"generate:sitemap": "node tools/generate-sitemap.mjs", "generate:sitemap": "node tools/generate-sitemap.mjs",
"generate:redirects": "node tools/generate-redirects.mjs" "generate:redirects": "node tools/generate-redirects.mjs"
}, },
Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

+18
View File
@@ -170,3 +170,21 @@ source,target,size,kind
"hubfs/team-timirah-clay.webp","public/assets/images/team-timirah-clay.webp","16496","images" "hubfs/team-timirah-clay.webp","public/assets/images/team-timirah-clay.webp","16496","images"
"hubfs/toddler-bcba-support.webp","public/assets/images/toddler-bcba-support.webp","11182","images" "hubfs/toddler-bcba-support.webp","public/assets/images/toddler-bcba-support.webp","11182","images"
"hubfs/united-healthcare-logo-1.webp","public/assets/images/united-healthcare-logo-1.webp","4368","images" "hubfs/united-healthcare-logo-1.webp","public/assets/images/united-healthcare-logo-1.webp","4368","images"
"live:https://www.azinstitute4autism.com/hubfs/hero-aba-therapy-intake-process.webp","public/assets/images/hero-aba-therapy-intake-process.webp","14392","images"
"live:https://www.azinstitute4autism.com/hubfs/hero-services-behavioral-aba.webp","public/assets/images/hero-services-behavioral-aba.webp","15330","images"
"live:https://www.azinstitute4autism.com/hubfs/hero-about.webp","public/assets/images/hero-about.webp","24764","images"
"live:https://www.azinstitute4autism.com/hubfs/hero-services-psychological.webp","public/assets/images/hero-services-psychological.webp","15490","images"
"live:https://www.azinstitute4autism.com/hubfs/hero-careers.webp","public/assets/images/hero-careers.webp","24728","images"
"live:https://www.azinstitute4autism.com/hubfs/hero-bg-blank.png","public/assets/images/hero-bg-blank.png","41772","images"
"live:https://www.azinstitute4autism.com/hubfs/hero-client-forms.webp","public/assets/images/hero-client-forms.webp","13878","images"
"live:https://www.azinstitute4autism.com/hubfs/hero-contact-aia.webp","public/assets/images/hero-contact-aia.webp","20230","images"
"live:https://www.azinstitute4autism.com/hubfs/hero-ways-to-giveback.webp","public/assets/images/hero-ways-to-giveback.webp","11654","images"
"live:https://www.azinstitute4autism.com/hubfs/hero-aba-employee-portal.webp","public/assets/images/hero-aba-employee-portal.webp","19798","images"
"live:https://www.azinstitute4autism.com/hubfs/hero-faq.webp","public/assets/images/hero-faq.webp","14624","images"
"live:https://www.azinstitute4autism.com/hubfs/hero-insurance-coverage-guide.webp","public/assets/images/hero-insurance-coverage-guide.webp","21624","images"
"live:https://www.azinstitute4autism.com/hubfs/hero-services-sociological.webp","public/assets/images/hero-services-sociological.webp","16046","images"
"live:https://www.azinstitute4autism.com/hubfs/hero-referrals.webp","public/assets/images/hero-referrals.webp","16646","images"
"live:https://www.azinstitute4autism.com/hubfs/hero-pediatric-services.webp","public/assets/images/hero-pediatric-services.webp","18120","images"
"live:https://www.azinstitute4autism.com/hubfs/hero-meet-the-aia-team.webp","public/assets/images/hero-meet-the-aia-team.webp","15310","images"
"live:https://www.azinstitute4autism.com/hubfs/hero-schedule-aia-tour.webp","public/assets/images/hero-schedule-aia-tour.webp","13290","images"
"live:https://www.azinstitute4autism.com/hubfs/hero-library-index.webp","public/assets/images/hero-library-index.webp","14392","images"
1 source target size kind
170 hubfs/team-timirah-clay.webp public/assets/images/team-timirah-clay.webp 16496 images
171 hubfs/toddler-bcba-support.webp public/assets/images/toddler-bcba-support.webp 11182 images
172 hubfs/united-healthcare-logo-1.webp public/assets/images/united-healthcare-logo-1.webp 4368 images
173 live:https://www.azinstitute4autism.com/hubfs/hero-aba-therapy-intake-process.webp public/assets/images/hero-aba-therapy-intake-process.webp 14392 images
174 live:https://www.azinstitute4autism.com/hubfs/hero-services-behavioral-aba.webp public/assets/images/hero-services-behavioral-aba.webp 15330 images
175 live:https://www.azinstitute4autism.com/hubfs/hero-about.webp public/assets/images/hero-about.webp 24764 images
176 live:https://www.azinstitute4autism.com/hubfs/hero-services-psychological.webp public/assets/images/hero-services-psychological.webp 15490 images
177 live:https://www.azinstitute4autism.com/hubfs/hero-careers.webp public/assets/images/hero-careers.webp 24728 images
178 live:https://www.azinstitute4autism.com/hubfs/hero-bg-blank.png public/assets/images/hero-bg-blank.png 41772 images
179 live:https://www.azinstitute4autism.com/hubfs/hero-client-forms.webp public/assets/images/hero-client-forms.webp 13878 images
180 live:https://www.azinstitute4autism.com/hubfs/hero-contact-aia.webp public/assets/images/hero-contact-aia.webp 20230 images
181 live:https://www.azinstitute4autism.com/hubfs/hero-ways-to-giveback.webp public/assets/images/hero-ways-to-giveback.webp 11654 images
182 live:https://www.azinstitute4autism.com/hubfs/hero-aba-employee-portal.webp public/assets/images/hero-aba-employee-portal.webp 19798 images
183 live:https://www.azinstitute4autism.com/hubfs/hero-faq.webp public/assets/images/hero-faq.webp 14624 images
184 live:https://www.azinstitute4autism.com/hubfs/hero-insurance-coverage-guide.webp public/assets/images/hero-insurance-coverage-guide.webp 21624 images
185 live:https://www.azinstitute4autism.com/hubfs/hero-services-sociological.webp public/assets/images/hero-services-sociological.webp 16046 images
186 live:https://www.azinstitute4autism.com/hubfs/hero-referrals.webp public/assets/images/hero-referrals.webp 16646 images
187 live:https://www.azinstitute4autism.com/hubfs/hero-pediatric-services.webp public/assets/images/hero-pediatric-services.webp 18120 images
188 live:https://www.azinstitute4autism.com/hubfs/hero-meet-the-aia-team.webp public/assets/images/hero-meet-the-aia-team.webp 15310 images
189 live:https://www.azinstitute4autism.com/hubfs/hero-schedule-aia-tour.webp public/assets/images/hero-schedule-aia-tour.webp 13290 images
190 live:https://www.azinstitute4autism.com/hubfs/hero-library-index.webp public/assets/images/hero-library-index.webp 14392 images
+5
View File
@@ -22,5 +22,10 @@
- Repaired mirror-rewritten PDF, lightbox, CTA, and relative content links. - Repaired mirror-rewritten PDF, lightbox, CTA, and relative content links.
- Limited language-switcher choices to translations that have generated routes. - Limited language-switcher choices to translations that have generated routes.
- Verified and implemented the current production likes/views API contract. - 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.
- 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.
- Added language-qualified content collection IDs to prevent English, Spanish, - Added language-qualified content collection IDs to prevent English, Spanish,
and Arabic entries with the same slug from overwriting each other. and Arabic entries with the same slug from overwriting each other.
+6 -1
View File
@@ -12,7 +12,7 @@ family, team page, library indexes, and blog-post family.
- 46 English, 12 Spanish, and 7 Arabic library posts. - 46 English, 12 Spanish, and 7 Arabic library posts.
- 21 English, 9 Spanish, and 2 Arabic page records. - 21 English, 9 Spanish, and 2 Arabic page records.
- One author record per language. - One author record per language.
- 149 self-hosted images, 20 font files, and two PDF downloads. - 167 self-hosted images, 20 font files, and two PDF downloads.
- Current June and May 2026 English library articles are included. - Current June and May 2026 English library articles are included.
The raw mirror remains untouched. HubSpot-generated wrappers, analytics, The raw mirror remains untouched. HubSpot-generated wrappers, analytics,
@@ -37,6 +37,9 @@ Implemented fidelity work includes:
order using one reusable Astro component and self-hosted source assets. order using one reusable Astro component and self-hosted source assets.
- Rebuilt service pages around the live compact title banner and editorial - Rebuilt service pages around the live compact title banner and editorial
presentation, with current visible headings and working calls to action. presentation, with current visible headings and working calls to action.
- Recovered and restored the live decorative hero imagery for all mapped
English and Spanish page routes plus every language's library index.
- Restored the consultation form section's injected background image.
- Added a dedicated live-derived team card grid rather than presenting the - Added a dedicated live-derived team card grid rather than presenting the
extracted team content as a generic article. extracted team content as a generic article.
- Rebuilt library indexes and blog-post presentation around the live sidebar, - Rebuilt library indexes and blog-post presentation around the live sidebar,
@@ -109,6 +112,8 @@ unavailable.
- `npm run audit:links`: passed with zero broken internal source links. The - `npm run audit:links`: passed with zero broken internal source links. The
audit now checks generated routes, root-relative references, relative audit now checks generated routes, root-relative references, relative
Markdown links, and public assets when rendered output is unavailable. Markdown links, and public assets when rendered output is unavailable.
- `npm run audit:images`: passed with no missing mapped page banners, section
backgrounds, or blog featured images.
- `npm run generate:sitemap`: passed; generated 97 URLs. - `npm run generate:sitemap`: passed; generated 97 URLs.
- `npm run generate:redirects`: passed. - `npm run generate:redirects`: passed.
- All migration `.mjs` tools and the sandbox DNS helper pass `node --check`. - All migration `.mjs` tools and the sandbox DNS helper pass `node --check`.
+5
View File
@@ -0,0 +1,5 @@
# Page Imagery Audit
Checked 84 page-banner, section-background, and blog-featured-image references.
No missing mapped page imagery was detected.
+2 -2
View File
@@ -1,7 +1,7 @@
--- ---
const { title = 'Schedule Your Free Consultation' } = Astro.props; const { title = 'Schedule Your Free Consultation', showBackground = false } = Astro.props;
--- ---
<section class="form-section" aria-labelledby="form-title"> <section class:list={['form-section', { 'form-section-background': showBackground }]} aria-labelledby="form-title">
<div class="container form-grid"> <div class="container form-grid">
<div><p class="eyebrow">Get started</p><h2 id="form-title">{title}</h2><p>Tell our client advocates how we can help. Submission is disabled until a production form backend is selected.</p></div> <div><p class="eyebrow">Get started</p><h2 id="form-title">{title}</h2><p>Tell our client advocates how we can help. Submission is disabled until a production form backend is selected.</p></div>
<form class="form" method="post" action=""> <form class="form" method="post" action="">
+20
View File
@@ -0,0 +1,20 @@
export const pageHeroImages: Record<string, string> = {
'aba-therapy-intake-process': '/assets/images/hero-aba-therapy-intake-process.webp',
'aba-therapy': '/assets/images/hero-services-behavioral-aba.webp',
about: '/assets/images/hero-about.webp',
'autism-evaluations': '/assets/images/hero-services-psychological.webp',
careers: '/assets/images/hero-careers.webp',
'client-consultation': '/assets/images/hero-bg-blank.png',
'client-forms': '/assets/images/hero-client-forms.webp',
contact: '/assets/images/hero-contact-aia.webp',
'donate-autism-giveback': '/assets/images/hero-ways-to-giveback.webp',
'employee-portal': '/assets/images/hero-aba-employee-portal.webp',
faqs: '/assets/images/hero-faq.webp',
insurance: '/assets/images/hero-insurance-coverage-guide.webp',
'learner-social-club': '/assets/images/hero-services-sociological.webp',
'privacy-policy': '/assets/images/hero-bg-blank.png',
referrals: '/assets/images/hero-referrals.webp',
services: '/assets/images/hero-pediatric-services.webp',
team: '/assets/images/hero-meet-the-aia-team.webp',
tour: '/assets/images/hero-schedule-aia-tour.webp'
};
+1 -1
View File
@@ -5,7 +5,7 @@ const languagePrefix = lang === 'en' ? '' : `/${lang}`;
const canonical = `https://www.azinstitute4autism.com${languagePrefix}/library`; const canonical = `https://www.azinstitute4autism.com${languagePrefix}/library`;
--- ---
<BaseLayout title={title} description={description} canonical={canonical} lang={lang}> <BaseLayout title={title} description={description} canonical={canonical} lang={lang}>
<header class="library-banner"><div class="container"><p class="script-label">Welcome to</p><h1>{title}</h1></div></header> <header class="library-banner"><div class="container"><div class="library-banner-copy"><p class="script-label">Welcome to</p><h1>{title}</h1></div></div></header>
<section class="library-intro"><div class="container"><h2>Library</h2><p>{description}</p></div></section> <section class="library-intro"><div class="container"><h2>Library</h2><p>{description}</p></div></section>
<div class="container section library-layout"><slot /></div> <div class="container section library-layout"><slot /></div>
</BaseLayout> </BaseLayout>
+5 -3
View File
@@ -2,6 +2,7 @@
import BaseLayout from './BaseLayout.astro'; import BaseLayout from './BaseLayout.astro';
import FormShell from '../components/FormShell.astro'; import FormShell from '../components/FormShell.astro';
import TeamPage from '../components/TeamPage.astro'; import TeamPage from '../components/TeamPage.astro';
import { pageHeroImages } from '../data/page-visuals';
const { entry } = Astro.props; const { entry } = Astro.props;
const showForm = ['client-consultation', 'contact', 'schedule-consultation', 'referrals'].includes(entry.data.slug); const showForm = ['client-consultation', 'contact', 'schedule-consultation', 'referrals'].includes(entry.data.slug);
const serviceTitles: Record<string, Record<string, string>> = { const serviceTitles: Record<string, Record<string, string>> = {
@@ -44,13 +45,14 @@ const pageTitles: Record<string, Record<string, string>> = {
}; };
const bannerTitle = serviceTitles[entry.data.lang]?.[entry.data.slug] || pageTitles[entry.data.lang]?.[entry.data.slug] || entry.data.title.split('|')[0].trim(); const bannerTitle = serviceTitles[entry.data.lang]?.[entry.data.slug] || pageTitles[entry.data.lang]?.[entry.data.slug] || entry.data.title.split('|')[0].trim();
const isService = Boolean(serviceTitles[entry.data.lang]?.[entry.data.slug]); 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} lang={entry.data.lang}> <BaseLayout title={entry.data.title} description={entry.data.description} canonical={entry.data.canonical} image={entry.data.featuredImage || heroImage} lang={entry.data.lang}>
<header class:list={['source-page-banner', { 'service-banner': isService }]}> <header class:list={['source-page-banner', { 'has-source-image': heroImage, 'service-banner': isService }]} style={heroImage ? `background-image: url("${heroImage}")` : undefined}>
<div class="container"><h1>{bannerTitle}</h1></div> <div class="container"><h1>{bannerTitle}</h1></div>
</header> </header>
{entry.data.slug === 'team' && entry.data.lang === 'en' {entry.data.slug === 'team' && entry.data.lang === 'en'
? <TeamPage /> ? <TeamPage />
: <article class:list={['prose', 'source-page', 'container', { 'service-page': isService }]}><slot /></article>} : <article class:list={['prose', 'source-page', 'container', { 'service-page': isService }]}><slot /></article>}
{showForm && <FormShell title={bannerTitle} />} {showForm && <FormShell title={bannerTitle} showBackground={entry.data.slug === 'client-consultation'} />}
</BaseLayout> </BaseLayout>
+6 -2
View File
@@ -54,8 +54,10 @@
.faq summary { cursor: pointer; font-weight: 700; } .faq summary { cursor: pointer; font-weight: 700; }
.source-page-banner { background: var(--color-tint); padding-block: 4rem; text-align: center; } .source-page-banner { background: var(--color-tint); padding-block: 4rem; text-align: center; }
.source-page-banner h1 { margin: 0; } .source-page-banner h1 { margin: 0; }
.service-banner { background-color: #e8f1ee; background-image: radial-gradient(circle at 14% 45%, rgb(37 64 128 / 9%) 0 54px, transparent 55px), radial-gradient(circle at 28% 25%, rgb(37 64 128 / 10%) 0 22px, transparent 23px), radial-gradient(circle at 74% 50%, rgb(37 64 128 / 8%) 0 42px, transparent 43px); padding-block: 7rem; } .source-page-banner.has-source-image { background-color: var(--color-tint); background-position: center; background-repeat: no-repeat; background-size: cover; padding-block: 10rem; text-align: left; }
.library-banner { background: var(--color-tint); padding-block: 5rem; text-align: center; } .source-page-banner.has-source-image h1 { max-width: 600px; }
.library-banner { background-color: var(--color-tint); background-image: url('/assets/images/hero-library-index.webp'); background-position: center; background-repeat: no-repeat; background-size: cover; padding-block: 10rem; text-align: left; }
.library-banner-copy { max-width: 600px; }
.library-banner h1 { margin: 0; } .library-banner h1 { margin: 0; }
.library-intro { background: var(--color-primary); color: white; padding-block: 3rem; } .library-intro { background: var(--color-primary); color: white; padding-block: 3rem; }
.library-intro h2 { color: white; } .library-intro h2 { color: white; }
@@ -140,5 +142,7 @@
.blog-list .blog-card { grid-template-columns: 1fr; } .blog-list .blog-card { grid-template-columns: 1fr; }
.article-featured { height: auto; } .article-featured { height: auto; }
.team-grid { grid-template-columns: 1fr; } .team-grid { grid-template-columns: 1fr; }
.source-page-banner.has-source-image, .library-banner { padding-block: 6.25rem; text-align: center; }
.source-page-banner.has-source-image h1, .library-banner-copy { margin-inline: auto; }
} }
@media (min-width: 761px) and (max-width: 1050px) { .team-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } } @media (min-width: 761px) and (max-width: 1050px) { .team-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
+1
View File
@@ -1,4 +1,5 @@
.form-section { background: var(--color-tint); padding-block: var(--space-xl); } .form-section { background: var(--color-tint); padding-block: var(--space-xl); }
.form-section.form-section-background { background-image: url('/assets/images/rbt-toddler-play.webp'); background-position: center bottom; background-repeat: no-repeat; background-size: contain; }
.form-grid { display: grid; gap: var(--space-xl); grid-template-columns: .8fr 1.2fr; } .form-grid { display: grid; gap: var(--space-xl); grid-template-columns: .8fr 1.2fr; }
.form { background: white; border-radius: var(--radius-md); box-shadow: var(--shadow-sm); display: grid; gap: var(--space-md); padding: var(--space-lg); } .form { background: white; border-radius: var(--radius-md); box-shadow: var(--shadow-sm); display: grid; gap: var(--space-md); padding: var(--space-lg); }
.form label { display: grid; font-weight: 700; gap: var(--space-xs); } .form label { display: grid; font-weight: 700; gap: var(--space-xs); }
+46
View File
@@ -0,0 +1,46 @@
import fs from 'node:fs/promises';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import fg from 'fast-glob';
import matter from 'gray-matter';
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
const publicRoot = path.join(root, 'public');
const failures = [];
const checked = [];
async function checkAsset(reference, owner) {
if (!reference?.startsWith('/assets/')) return;
const target = path.join(publicRoot, reference);
const exists = await fs.access(target).then(() => true).catch(() => false);
checked.push({ owner, reference, exists });
if (!exists) failures.push(`${owner}: ${reference}`);
}
const visualSource = await fs.readFile(path.join(root, 'src/data/page-visuals.ts'), 'utf8');
for (const match of visualSource.matchAll(/['"](?<reference>\/assets\/images\/[^'"]+)['"]/g)) {
await checkAsset(match.groups.reference, 'page hero mapping');
}
await checkAsset('/assets/images/hero-library-index.webp', 'library banner');
await checkAsset('/assets/images/rbt-toddler-play.webp', 'consultation form background');
for (const file of await fg('src/content/blog/**/*.{md,mdx}', { cwd: root, absolute: true })) {
const { data } = matter(await fs.readFile(file, 'utf8'));
if (!data.draft) await checkAsset(data.featuredImage, path.relative(root, file));
}
const uniqueChecked = new Map(checked.map((item) => [`${item.owner}:${item.reference}`, item]));
const report = [
'# Page Imagery Audit',
'',
`Checked ${uniqueChecked.size} page-banner, section-background, and blog-featured-image references.`,
'',
failures.length
? `## Missing Assets\n\n${failures.map((item) => `- ${item}`).join('\n')}`
: 'No missing mapped page imagery was detected.',
''
].join('\n');
await fs.writeFile(path.join(root, 'reports/page-imagery-audit.md'), report);
console.log(`${failures.length} missing mapped page images.`);
if (failures.length) process.exitCode = 1;