diff --git a/skills/ve-html-template-generator/README.md b/skills/ve-html-template-generator/README.md
new file mode 100644
index 000000000..fa823a185
--- /dev/null
+++ b/skills/ve-html-template-generator/README.md
@@ -0,0 +1,192 @@
+# VE HTML Template Generator Skill
+
+Quick guide for using `ve-html-template-generator`.
+
+## What to provide in the prompt
+
+Minimum required inputs:
+
+- `client`: client slug (example: `yeti`)
+- `html`: local HTML path (preferred) or URL
+
+Minimal prompt:
+
+```text
+Use $ve-html-template-generator.
+client: yeti
+html: /Users/you/Downloads/yeti-location-source.html
+```
+
+## Optional inputs
+
+- `customRoot`: defaults to `packages/visual-editor/src/components/custom/`
+- `starterRoot`: defaults to `starter/src/templates/`
+- `scope`: what to include/exclude from source page
+- `notes`: constraints for slot structure, component behavior, etc.
+
+Example with optional inputs:
+
+```text
+Use $ve-html-template-generator.
+client: acme
+html: /tmp/acme-homepage.html
+scope: Exclude legal policy bands and cookie modal markup.
+notes:
+- Split every heading/body/cta into focused nested slots.
+- Lock generated slot surfaces for editing-only use (`allow={[]}` on all section and nested layout slot renders).
+- Keep hours entity-defaulted.
+- If source has multiple hours bands (for example store + drive-thru), generate separate entity-bound hours slots for each band.
+- Do not use any sibling client templates under `starter/src/templates/*` or sibling custom client folders under `packages/visual-editor/src/components/custom/*` as reference; derive structure from source HTML + shared VE components only.
+- Nearby Stores/Locations should follow shared NearbyLocations-style dynamic behavior, not hardcoded static store links.
+- Build client-local slots/atoms by adapting the closest shared generic baseline first, then layer client-specific changes.
+- For FAQ/Q&A bands, adapt shared FAQ behavior patterns first (question/answer modeling + interaction), then restyle to match source.
+- For footer social links, baseline against shared `FooterSocialLinksSlot` behavior (platform-specific fields + URL validation + icon rendering), not text-link lists.
+- Decompose location summary content into nested focused slots (back-link, heading/meta, status, CTA), not one omnibus summary slot.
+- For user-visible copy props, use `translatableString` (or entity-backed string fields) so embedded fields work; reserve plain `text` fields for URLs/class names/ids.
+- Ignore likely hidden utility labels from HTML in nav/footer (for example `Top Links`, `Actions`, `Menu`) unless there is source evidence they are visibly rendered.
+- Any CTA-like label must have an actionable link/href (or CTA entity). Never render CTA labels as plain non-interactive text.
+- Compose complex layouts grid-first using shared Core Information/slot baselines, then wrap into client-bespoke section/slot names.
+- Use shared map/image patterns (no map iframes, no backgroundImageUrl text fields).
+- Keep functionality first: preserve entity wiring (especially hours) and real map behavior before visual refinements.
+- Match header/footer chrome to the source site (dark or light) and preserve readable contrast.
+- Include source-theme header/footer background options (for example source brand blue), not only white/neutral choices.
+- Use full-bleed hero/promo shells only when source sections are edge-to-edge (`px-0 md:px-0` + `max-w-none`).
+- Render rich text slots via `resolveComponentData` output (React element or string), not html-only parsing.
+- If source has media + list sections (for example amenities with a lead image), keep a dedicated media slot plus list/content slots.
+- When merging similar sections, keep optional slots (like CTA slots) and add top-level show/hide toggles instead of removing those slots.
+- For sections with multiple slots, expose top-level show/hide toggles for most slots (`slot count - 1` coverage target).
+- When store-info includes hours/location/map/parking, preserve stacked order (`Hours -> Location -> Map -> Parking`) unless source evidence clearly differs.
+```
+
+## What the skill generates
+
+Under `packages/visual-editor/src/components/custom//`:
+
+- `atoms/*.tsx`
+- `components/*Section.tsx`
+- `components/*Slot.tsx`
+- `index.ts`
+- `ve.ts`
+
+Under `packages/visual-editor/src/components/categories/`:
+
+- `SectionsCategory.tsx`
+- `SlotsCategory.tsx`
+
+Shared package registration updates:
+
+- `packages/visual-editor/src/components/configs/mainConfig.tsx` (visible `Sections` + hidden `Slots` category wiring)
+- `packages/visual-editor/src/components/categories/index.ts` (category exports)
+- `packages/visual-editor/src/components/index.ts` (generated client exports)
+
+Under `starter/src/templates//`:
+
+- `-config.tsx`
+- `-template.tsx`
+
+Starter is a consumer-only wrapper. It should not be the source of truth for generated sections/slots/atoms.
+
+## Validate output
+
+Run validator:
+
+```bash
+python3 skills/ve-html-template-generator/scripts/validate_client_template.py --client-path starter/src/templates/
+```
+
+Optional screenshot smoke test scaffold:
+
+```bash
+python3 skills/ve-html-template-generator/scripts/scaffold_client_template_smoke_test.py \
+ --client-slug \
+ --template-path starter/src/templates//-template.tsx \
+ --config-path starter/src/templates//-config.tsx
+```
+
+Run generated smoke test:
+
+```bash
+pnpm -C packages/visual-editor exec vitest run \
+ src/components/generated/Template.smoke.test.tsx
+```
+
+On first run for a newly generated test, seed screenshot baselines:
+
+```bash
+pnpm -C packages/visual-editor exec vitest run --update \
+ src/components/generated/Template.smoke.test.tsx
+```
+
+Smoke screenshots should capture full page height so lower-page sections (for example footer/legal) are validated.
+
+Default source-vs-generated section parity scaffold (captures source HTML and generated section screenshots):
+
+```bash
+python3 skills/ve-html-template-generator/scripts/scaffold_client_template_section_parity_test.py \
+ --client-slug \
+ --html-path /path/to/source.html \
+ --config-path starter/src/templates//-config.tsx \
+ --overwrite
+```
+
+Run section parity test:
+
+```bash
+pnpm -C packages/visual-editor exec vitest run \
+ src/components/generated/Template.section-parity.test.tsx
+```
+
+For first-time baseline capture of a new generated parity test, run once with `--update`.
+
+This section parity run is now the default post-generation step for the skill.
+After the initial parity run, apply one focused correction pass immediately, then rerun smoke + section parity.
+After those reruns, run final build checks before considering the task done:
+
+```bash
+pnpm -C packages/visual-editor build
+pnpm -C starter build
+```
+
+Default behavior for this skill is now: `generate -> parity -> correction pass -> rerun -> build gate`.
+
+Optional section parity controls:
+
+```bash
+CLIENT_TEMPLATE_SECTION_PARITY_LIMIT=10 \
+CLIENT_TEMPLATE_SECTION_PARITY_ALL_VIEWPORTS=1 \
+pnpm -C packages/visual-editor exec vitest run \
+ src/components/generated/Template.section-parity.test.tsx
+```
+
+Optional full-page parity scaffold:
+
+```bash
+python3 skills/ve-html-template-generator/scripts/scaffold_client_template_visual_parity_test.py \
+ --client-slug \
+ --html-path /path/to/source.html \
+ --config-path starter/src/templates//-config.tsx \
+ --overwrite
+```
+
+Run parity test:
+
+```bash
+pnpm -C packages/visual-editor exec vitest run \
+ src/components/generated/Template.visual-parity.test.tsx
+```
+
+For first-time baseline capture of a new generated parity test, run once with `--update`.
+
+Screenshot parity should stay advisory-first: use it to find layout/media/styling gaps, but keep shared baseline functionality and slot wiring intact.
+By default, parity should drive one focused correction pass, not open-ended restyling loops.
+
+## From-scratch verification
+
+Use this before evaluating a new generation:
+
+1. Clear or avoid old saved layout data for the target entity (`document.__.layout`).
+2. Open editor and drag the new client sections from left nav.
+3. Verify header/footer nested slots are clickable/editable (brand/nav/utility, signup/links/legal).
+4. Verify default values are visible in the props panel before edits.
+
+Old saved layouts from earlier generations can hide or distort current slot behavior.
diff --git a/skills/ve-html-template-generator/SKILL.md b/skills/ve-html-template-generator/SKILL.md
new file mode 100644
index 000000000..469b01db7
--- /dev/null
+++ b/skills/ve-html-template-generator/SKILL.md
@@ -0,0 +1,420 @@
+---
+name: ve-html-template-generator
+description: Generate isolated client-specific Yext Visual Editor templates from existing website HTML by decomposing a page into bespoke atoms, slot components, and section components, then producing package-scoped custom component files under packages/visual-editor/src/components/custom/client-slug plus client template/config files under starter/src/templates/client-slug. Use when asked to create or update a client template from downloaded HTML, view-source markup, or exported page snapshots (Step 1 of the client-template epic).
+---
+
+# VE HTML Template Generator
+
+## Enforce Client Isolation
+
+Treat every client template as fully isolated from shared/generic VE components.
+
+Required isolation rules:
+
+- Generate client-specific atoms, sections, slot components, and category/config files.
+- Generate new client-specific header and footer sections even when shared versions appear identical.
+- Do not import or register shared categories/components/atoms in client generated files.
+- Do not read, copy, or adapt any sibling client templates under `starter/src/templates/*` or sibling custom client folders under `packages/visual-editor/src/components/custom/*` (except the target client being generated/updated).
+- Never use another client template as a starting scaffold. Baselines come from shared/generic VE components in `packages/visual-editor/src/components/...` plus the target source HTML only.
+- Keep custom atoms/slots/sections under `packages/visual-editor/src/components/custom//...`.
+- Keep starter files minimal: only `starter/src/templates//-config.tsx` and `starter/src/templates//-template.tsx`.
+
+## Default Assumptions
+
+Assume these defaults unless the user explicitly overrides them:
+
+- Generate client-only atoms, sections, categories, config, and template files.
+- Recreate header/footer as client-owned sections.
+- Use slot-first section architecture (`sections compose slot children`).
+- Default hours content to entity mode (`field: "hours"`, not hard-coded weekly constants).
+- Default non-hours content to constant mode unless user explicitly requests KG-first defaults.
+- Do not expose atoms in the left nav; expose sections only.
+- Create exactly one visible client-specific section category in the left nav.
+- Create one hidden client-specific slot category in config.
+- Use custom implementation root `packages/visual-editor/src/components/custom/`.
+- Do not modify shared/generic VE section/slot implementations, but do update shared package registries for the new client category wiring.
+- Do not add or depend on `registerMainConfigExtension`/runtime mainConfig mutation from `starter`.
+- Run section-level screenshot parity by default after initial generation (report-first, no default diff gate).
+- Prioritize functionality/usability over visual similarity (entity wiring, slot editability, map/hours behavior) when tradeoffs are required.
+
+## Minimal Prompt Contract
+
+Treat these as the only required user inputs:
+
+- `client`: client slug (example: `yeti`)
+- `html`: local HTML file path or URL
+
+If the user provides only `client` and `html`, proceed with all defaults in this skill and do not ask for additional setup questions.
+
+Example minimal invocation:
+
+```text
+Use $ve-html-template-generator.
+client: yeti
+html: /path/to/yeti-homepage.html
+```
+
+## Collect Inputs
+
+Gather these inputs before coding:
+
+- Client slug (example: `yeti`)
+- Source HTML file path (preferred) or page URL
+- Custom implementation root (`packages/visual-editor/src/components/custom/`) unless overridden
+- Starter wrapper root (`starter/src/templates/`) unless overridden
+- Scope boundaries (for example: "exclude only legal/policy fragments")
+
+If only a URL is provided, fetch once and save a local snapshot:
+
+```bash
+curl -L "" -o /tmp/-page.html
+```
+
+## Build Section Plan
+
+Create a first-pass section breakdown from the HTML:
+
+```bash
+python3 scripts/extract_page_sections.py \
+ --input /tmp/-page.html \
+ --output /tmp/-sections.json
+```
+
+Review the JSON and adjust obvious bad splits before generating components.
+
+Favor one horizontal band per component. Keep sections small and cohesive.
+Derive the section list from source evidence, not prior client templates.
+
+Before coding, produce a quick source-evidence map:
+
+- planned section -> source selector/snippet (heading/copy/image/link evidence)
+- include only sections that have source evidence (or explicit user request)
+- infer page archetype from HTML (`location-detail`, `marketing`, etc.) and only include archetype-appropriate sections (do not auto-insert FAQ/store-info bands unless source supports them)
+
+Before coding sections, run normalization guidance from
+`references/section-normalization.md` to:
+
+- split over-aggregated slots into focused concerns
+- merge structurally duplicate sections into one reusable section with toggles/presets
+
+## Generate Template Files
+
+Create exactly this shape for each client:
+
+- `packages/visual-editor/src/components/custom//atoms/*.tsx`
+- `packages/visual-editor/src/components/custom//components/Section.tsx`
+- `packages/visual-editor/src/components/custom//components/Slot.tsx`
+- `packages/visual-editor/src/components/custom//index.ts`
+- `packages/visual-editor/src/components/custom//ve.ts`
+- `packages/visual-editor/src/components/categories/SectionsCategory.tsx`
+- `packages/visual-editor/src/components/categories/SlotsCategory.tsx`
+- `starter/src/templates//-config.tsx`
+- `starter/src/templates//-template.tsx`
+
+Follow `references/template-contract.md` for naming, registration, and assembly.
+
+## Generate Section Components
+
+Follow `references/ve-field-patterns.md` for all prop and field conventions.
+Follow `references/slot-paradigm.md` for section and slot composition.
+Follow `references/section-normalization.md` for slot decomposition and duplicate-section merge decisions.
+Follow `references/layout-containment.md` for section shell and slot containment rules.
+Follow `references/map-image-parity.md` for map/image parity with existing shared VE components.
+Follow `references/baseline-reuse-map.md` to select shared baselines for client-local atoms/slots/sections.
+
+When generating inside this repository, mirror these implementation patterns:
+
+- `packages/visual-editor/src/components/pageSections/VideoSection.tsx` (slot-based section shell)
+- `packages/visual-editor/src/components/pageSections/CoreInfoSection.tsx` (multi-slot section and default slot props)
+- `packages/visual-editor/src/components/pageSections/AboutSection/AboutSection.tsx` (multi-column slot layout and slot rendering behavior)
+- `packages/visual-editor/src/components/contentBlocks/HoursTable.tsx` (hours entity field defaults)
+
+Required conventions:
+
+- Define section props with `styles` and hidden `slots`.
+- Define slot fields with `{ type: "slot" }` and `visible: false`.
+- Render content through slot children (`Slot allow={[]} />`) instead of hard-coded inline structures.
+- Render slot children with `style={{ height: "auto" }}` unless there is a specific, justified alternative.
+- For slot components that compose child slots (for example header/footer layout slots), apply the same lock-down rule on every nested slot render: `allow={[]}` plus `style={{ height: "auto" }}`.
+- Generated slots are for structured prop editing, not free-form nesting; do not leave any generated slot render permissive.
+- Keep slots concern-specific; avoid combined slot names such as `HoursLocationSlot`.
+- Decompose textual concerns aggressively: heading/subheading/body/legal text/each CTA should be separate slots by default.
+- Decompose CTA concerns aggressively: each CTA (primary vs secondary) should be its own slot rather than embedded in text slots.
+- For sections with 2+ slots, expose top-level `show...` controls for most slots (target coverage: slot count minus one, or higher).
+- For user-visible non-entity copy inputs, use `translatableString` fields (embedded-field capable), not plain `text`/`textarea` inputs.
+- Keep plain `text` inputs for infrastructure-only values (for example URLs, class names, ids, API keys, numeric query params), not rendered copy.
+- Header/footer must be nested slot trees, not terminal omnibus slots:
+ - section shell slot(s) -> layout slot -> focused child slots (logo, nav links, utility links, CTA groups, legal groups)
+ - avoid one-slot header/footer implementations that inline all text/links/buttons
+- When source HTML includes likely hidden utility labels in nav/footer (for example `Top Links`, `Actions`, `Menu` headings), do not surface those as visible heading copy unless source evidence shows they are actually visible.
+- Header/footer layout slots must be directly editable:
+ - every layout slot `slots.` key must have populated `defaultProps.slots.` entries
+ - do not leave any child slot empty when section defaults are instantiated
+ - do not rely on runtime layout normalization/migrations to make header/footer slots editable
+- If a section needs grouping, use a layout slot that composes child slots instead of one slot with many unrelated text/CTA fields.
+- Ensure every slot `type` referenced in section `defaultProps.slots` is registered in client slot category components and included in `-config.tsx` components.
+- Do not use placeholder slot defaults (`props: {}`); populate full slot prop objects in section `defaultProps.slots`.
+- For array fields in slot components, always provide `defaultItemProps` so newly added items are well-formed.
+- Use `YextField(..., { type: "entityField", filter: ... })` for editable content props inside slot components.
+- Use `entityField` only for supported/intentional types (for example `type.string`, `type.rich_text_v2`, `type.image`, `type.phone`, `type.hours`).
+- Avoid `entityField` for `type.url`; use explicit URL fields (`text` or `translatableString`) unless a dedicated supported pattern is required.
+- Represent content props as `YextEntityField` (`field`, `constantValue`, `constantValueEnabled`).
+- For any `YextEntityField` default, use localized constant objects (for example `{ en: "Heading", hasLocalizedValue: "true" }`) instead of plain string constants so right-panel defaults hydrate correctly.
+- For any `YextEntityField` default, use localized constant objects that include `hasLocalizedValue: "true"` at the top level.
+- Apply localized constant object rules consistently in `defaultProps`, array `defaultItemProps`, and any section-specific slot-prop overrides created via spread.
+- For location-stream templates, default location-specific content (name/address/phone/hours) to entity mode or derive from entity fields; avoid hardcoded city/address/phone constants with empty field bindings.
+- For location-stream templates, avoid hardcoded map/directions URLs in defaults; derive from address data at render-time or expose entity-backed fields.
+- Map implementations should mirror shared VE behavior:
+ - prefer `MapboxStaticMapComponent` with coordinate entity field defaults (`field: "yextDisplayCoordinate"`)
+ - or use `getDirections`-derived links when embed providers are unavailable
+ - render an actual map surface (static map image or shared map component), not a decorative placeholder panel
+ - avoid `iframe` map embeds with hardcoded provider URLs
+ - avoid `mapImage` fallback fields in map slots unless explicitly requested by the user
+ - do not seed map slots with hero/promo marketing images as map defaults
+- Background images should mirror shared hero/promo behavior:
+ - use `data.backgroundImage` as `YextEntityField<...>` with `filter.types: ["type.image"]`
+ - resolve at render-time via shared image resolution patterns (`resolveYextEntityField` + `getImageUrl`)
+ - avoid `backgroundImageUrl` text fields in `styles` or `data`
+- Image slots should mirror shared `ImageWrapper` behavior:
+ - `data.image` entity field using `type.image`
+ - style controls for width/aspect ratio/constrain behavior
+ - render-safe empty state when image data is missing
+- Resolve content through `resolveComponentData(..., locale, streamDocument)`.
+- Use `EntityField` wrappers around rendered values that map to KG/static content.
+- Include practical style props for text size, color, alignment, and spacing/background.
+- Provide render-safe `defaultProps` that approximate source content.
+- For hours-related slot components, default to entity hours (`field: "hours"`, `constantValueEnabled: false`).
+- When source has multiple distinct hours bands (for example store hours + drive-thru hours), generate separate hours slots/components for each band (for example `StoreHoursSlot`, `DriveThruHoursSlot`), and keep each band entity-bound.
+- Use defensive rendering in list/slot components (guard missing item structures instead of directly dereferencing nested `.field` paths).
+- Merge duplicate sections with the same slot/style structure into a single reusable section and expose behavior differences as toggles (for example `showCTA`).
+- When merging duplicate sections, keep the superset of optional slots (especially CTA/disclaimer/body variants) and control each optional slot with top-level show/hide toggles instead of removing slots.
+- Any CTA-like label must map to an actionable CTA control (CTA/link/button with href/link action). Never render CTA labels as plain non-interactive text.
+- Use baseline-first generation: copy/adapt proven shared section shell patterns into client-local files before inventing new section shells.
+- Use baseline-first slot/atom generation too:
+ - choose the closest shared slot/atom behavior by concern
+ - copy/adapt into client-local files
+ - only invent new slot/atom APIs when source fidelity requires it
+- Prefer grid-first composition when source layout is complex: assemble with shared grid/core-info atoms/slots first, then wrap that composition into client-bespoke named sections with focused slot labels.
+- For FAQ/Q&A bands, baseline against shared FAQ implementations first (`FAQsSection` + FAQ card patterns), then adapt styling/layout to match source visuals.
+- Nearby stores/location clusters should follow shared NearbyLocations behavior (dynamic lookup from document coordinate/radius/limit) rather than static hardcoded link lists.
+- Footer social link groups should follow shared `FooterSocialLinksSlot` behavior (platform-specific fields + URL validation + icon rendering), not generic text-link lists.
+- Do not keep location summary or hero summary content in one omnibus slot; use nested layout slots with focused child slots (back-link, heading/address/meta, status, CTA group).
+- In multi-column sections, apply containment-safe wrappers (`overflow-hidden` at section content level, `min-w-0` on columns, no absolute positioning for core content blocks).
+- If a section exposes `styles.textColor`, avoid hardcoded slot text color classes (for example `text-white`) that neutralize that control; use inherited color or slot-level style props.
+- For header/footer slots, avoid hardcoded white/light text and border utility classes (`text-white`, `text-white/..`, `text-neutral-100`, `border-white/...`) that can conflict with section backgrounds; use inherited color or explicit style props.
+- Header/footer section defaults should match source-site chrome (dark or light) and preserve readable contrast in the default state.
+- Header/footer background controls should include source-theme options (for example source brand blue for Yeti-like dark chrome), not only neutral/white options.
+- When source has stacked store-info content (hours/location/map/parking), preserve source order in section structure. Default to `Hours -> Location -> Map -> Parking` when all four are present.
+- Hero/promo bands should use full-bleed shell wrappers (`className="px-0 md:px-0 py-0 md:py-0"` + `contentClassName="max-w-none"`) only when the source band is edge-to-edge.
+- Hero/promo media slots should not apply rounded corner classes unless the source design explicitly uses rounded media.
+- Hero/promo image elements should render as block-level (`className` includes `block h-full w-full object-cover`) to avoid baseline whitespace gaps.
+- For rich text slot rendering, prefer `resolveComponentData(...)` render output (`ReactElement | string`) over manual `html` extraction only; this prevents hidden copy when rich text value shapes vary (html/json/localized object forms).
+- For source bands that combine media + list content (for example amenities/gallery + feature list), include an explicit media slot alongside list/content slots; do not collapse to list-only when source contains meaningful imagery.
+
+## Generate Client Template Root
+
+In `-template.tsx`:
+
+- Import client section components from `@yext/visual-editor` (package exports), not from local starter `components/`.
+- Use full `@yext/pages` template structure as the client entrypoint.
+- Compose page output in source order.
+- In default layout content entries, set section `props` to section `defaultProps` (or a deliberate deep override of `defaultProps`) instead of `props: {}`.
+- Pass stream document metadata into Puck render (`metadata={{ streamDocument: document }}`) for field resolution consistency.
+- Keep the implementation client-scoped. Do not modify generic/shared templates unless requested.
+
+In `-config.tsx`:
+
+- Register only client-owned section and slot category components from `@yext/visual-editor` (no shared page-section categories).
+- Define one visible client-specific sidebar category for generated sections in the left nav.
+- Define one hidden client-specific category for slot components.
+- Ensure the visible category contains sections only (no atoms, no slot components).
+- Ensure every slot component used by section defaults is present in the slot category component map and therefore available in registry lookups during drag/drop.
+- Include slot component registrations in `config.components` (for example: spread `SlotsCategoryComponents`).
+
+Follow `references/client-config-category-pattern.md` for a concrete config/category shape.
+
+If `starter/src/ve.config.tsx` exists, integrate generated client sections into starter editor nav:
+
+- Keep starter as a consumer of package config. Do not make `mainConfig` depend on starter imports.
+- Prefer `...mainConfig.components`/`...mainConfig.categories` for client section visibility after package registration.
+- Add `-location` mapped to `Config` in `componentRegistry`.
+- Only add explicit client section/slot maps to starter `devConfig` if package `mainConfig` has not been updated yet.
+- Extend starter `DevProps` only when explicit starter-local client maps are added.
+
+For package-default availability (non-starter), update shared package config too:
+
+- Add a package-native `SectionsCategory.tsx` file under `packages/visual-editor/src/components/categories`.
+- Add a package-native `SlotsCategory.tsx` file under `packages/visual-editor/src/components/categories`.
+- Register both `...CategoryComponents` maps in `packages/visual-editor/src/components/configs/mainConfig.tsx`.
+- Add a visible `Sections` category and hidden `Slots` category in `mainConfig.categories`.
+- Keep `directoryConfig` and `locatorConfig` unchanged unless explicitly requested.
+
+## Validate
+
+Run repository validation commands and fix errors before finishing.
+Use `references/quality-checklist.md` as a mandatory gate before finalizing.
+Use `references/test-and-screenshot-workflow.md` as a required post-generation step for every new generation unless the user explicitly opts out.
+Treat screenshot parity as an advisory signal for spacing/hierarchy/media gaps; do not regress shared baseline functionality (slots, field wiring, editability, data behavior) to chase pixel-perfect matches.
+Treat section parity pairing as heuristic by source order and generated section order; use diff output to guide review, not as absolute truth.
+Apply at most one parity-driven correction pass by default, focused on high-impact issues (missing media/maps, incorrect hierarchy/layout, severe contrast/readability, major spacing breaks).
+For major structure/layout mismatches (slot order, missing key bands, wrong visual hierarchy), increase parity weighting enough to correct those in the single default correction pass.
+Do not make parity-driven refactors that reduce slot decomposition, field consistency, or editor stability.
+Always run final build checks after the correction-pass reruns, and keep fixing until build commands pass.
+
+Run skill validation:
+
+```bash
+python3 scripts/validate_client_template.py --client-path starter/src/templates/
+```
+
+Confirm:
+
+- Directory and file names match the required contract exactly.
+- No shared/generic VE sections/components/atoms are imported into client config.
+- Sections are slot-based and slot fields are hidden.
+- Slot types referenced by sections are registered in client slot category and config component registry.
+- Section default slot props are populated (no empty `{}` slot prop placeholders).
+- Slot array fields define `defaultItemProps`.
+- Unsupported `entityField` filters like `type.url` are not used.
+- `YextEntityField` defaults do not use plain-string `constantValue`; they use localized objects with `hasLocalizedValue: "true"`.
+- `YextEntityField` and `YextEntityField` defaults include localized objects with `hasLocalizedValue: "true"` when locale keys are used.
+- Sections avoid composite slot concerns (for example `HoursLocationSlot`).
+- All slot render calls include `allow={[]}` and `style={{ height: "auto" }}` by default in both section files and nested slot/layout files.
+- Non-list slot components avoid over-aggregated text/CTA fields.
+- Sections with 2+ slots expose top-level show/hide controls for most slots (target coverage: slot count minus one).
+- Multi-slot section shells include containment cues (`overflow-hidden`, column wrappers with `min-w-0`) to prevent visual spillover.
+- Location-stream templates avoid hardcoded location constants and map/directions URLs in defaults.
+- Map blocks follow shared VE map patterns (no hardcoded iframe map embeds).
+- Map slots do not use fallback `mapImage` fields/defaults unless explicitly requested.
+- Map defaults do not use hero/promo marketing image URLs.
+- Background image and image slot data follow shared VE image entity patterns (no plain URL text controls for core image sources).
+- Nearby-locations behavior is not hardcoded to a static link list; it follows shared NearbyLocations-style dynamic data lookup.
+- FAQ/Q&A sections preserve shared FAQ behavior patterns (question/answer data shape + interaction model) while matching source styling.
+- Slot/atom implementations are adapted from the closest shared baseline unless source constraints require a deliberate divergence.
+- Header/footer implementations are nested slot trees with focused child slots.
+- Header/footer slot styles maintain text/background contrast without relying on hardcoded white/light utility classes.
+- Header/footer defaults match source chrome style (dark/light) while preserving readable contrast.
+- Hero/promo sections only use full-bleed wrappers when source parity requires edge-to-edge media.
+- No sibling client template paths/symbols are referenced from generated files.
+- No sibling custom client paths/symbols are referenced from `packages/visual-editor/src/components/custom/*`.
+- Validator does not flag suspicious structural similarity with older sibling client templates.
+- Store-info sections that include `HoursSlot`, `LocationInfoSlot`, `MapSlot`, and `ParkingSlot` preserve source-first order (`Hours -> Location -> Map -> Parking`) unless source evidence clearly differs.
+- Rich text slot copy renders when edited/defaulted (no html-only parsing assumptions).
+- Merged promo-like sections preserve optional CTA slots and expose top-level CTA show/hide toggles.
+- Section `textColor` style controls are not undermined by hardcoded slot text color classes.
+- No structurally duplicate sections exist when a toggle/variant-based merge is feasible.
+- Hours defaults use entity binding, not static weekly strings.
+- Multi-band hours sections (for example store + drive-thru) are represented as multiple entity-bound hours slots, not one slot with a secondary heading and no second hours data source.
+- Default layout section entries do not use empty `props: {}`.
+- Starter editor left nav includes the generated client section category when starter config exists.
+- Starter config consumes package `mainConfig` for client sections/slots after package registration (no starter-owned mainConfig mutation).
+- Fresh drag/drop from left nav works for header/footer sections and their nested child slots (brand/nav/utility, signup/links/legal) without blank/non-clickable slot regions.
+- Footer social areas render platform-aware social link icons/links (not a plain social text list).
+- User-facing copy fields are translatable/embedded-field capable (`translatableString` or entity-backed string fields), not plain text inputs.
+- Header/nav utility groups do not include noisy hidden headings (for example `Top Links` / `Actions`) unless source shows those labels on-screen.
+- CTA labels are always tied to actionable href/link controls (no plain-text CTA labels).
+- No Storm-side integration logic is introduced.
+- The generated page structure preserves source hierarchy (hero -> body bands -> CTA/footer).
+
+## From-Scratch Test Protocol
+
+Before calling generation "good", validate in a clean run:
+
+1. Remove/ignore old generated client layout data for the target entity (or use a new entity with no saved `__layout`).
+2. Start editor, drag fresh generated sections from left nav.
+3. Confirm nested header/footer slots are selectable and editable in canvas and right props panel.
+4. Confirm defaults are visible in props panel before any edits.
+5. Run validator script and address all failures.
+
+## Required Screenshot Parity Step
+
+After initial generation, always scaffold and run section parity:
+
+```bash
+python3 scripts/scaffold_client_template_section_parity_test.py \
+ --client-slug \
+ --html-path /path/to/source.html \
+ --config-path starter/src/templates//-config.tsx \
+ --overwrite
+```
+
+```bash
+pnpm -C packages/visual-editor exec vitest run \
+ src/components/generated/Template.section-parity.test.tsx
+```
+
+After this first parity run, perform one focused correction pass in the same turn by default:
+
+- fix the highest-impact issues surfaced by parity + smoke (missing/incorrect bands, major spacing/layout drift, contrast/accessibility failures)
+- rerun smoke and section parity
+- run final build checks:
+ - `pnpm -C packages/visual-editor build`
+ - `pnpm -C starter build`
+- report what changed and what still differs
+
+Do not stop at parity report output unless the user explicitly asks to skip correction.
+Do not stop at rerun screenshots either if build checks fail; fix build errors before finishing.
+
+For a brand-new generated parity test, run once with `--update` to seed screenshot baselines.
+
+Use parity output to apply one focused correction pass. If browser tests cannot run in the current environment, report that parity execution was skipped and why.
+
+Optional section parity controls:
+
+```bash
+CLIENT_TEMPLATE_SECTION_PARITY_LIMIT=10 \
+CLIENT_TEMPLATE_SECTION_PARITY_ALL_VIEWPORTS=1 \
+pnpm -C packages/visual-editor exec vitest run \
+ src/components/generated/Template.section-parity.test.tsx
+```
+
+Optional section parity gate (keep disabled by default; enable only when needed):
+
+```bash
+CLIENT_TEMPLATE_SECTION_PARITY_MAX_DIFF= \
+pnpm -C packages/visual-editor exec vitest run \
+ src/components/generated/Template.section-parity.test.tsx
+```
+
+## Optional Additional QA
+
+When rapid QA is requested, scaffold a smoke screenshot test:
+
+```bash
+python3 scripts/scaffold_client_template_smoke_test.py \
+ --client-slug \
+ --template-path starter/src/templates//-template.tsx \
+ --config-path starter/src/templates//-config.tsx
+```
+
+Then run the generated test with the repository Vitest browser workflow to produce screenshot artifacts.
+For a brand-new generated smoke test, run once with `--update` to seed screenshot baselines.
+
+Smoke screenshots should capture full rendered page height (not just viewport fold) so footer/late-page regressions are visible.
+
+Optional full-page source-vs-generated parity scaffold:
+
+```bash
+python3 scripts/scaffold_client_template_visual_parity_test.py \
+ --client-slug \
+ --html-path /path/to/source.html \
+ --config-path starter/src/templates//-config.tsx \
+ --overwrite
+```
+
+Run parity test:
+
+```bash
+pnpm -C packages/visual-editor exec vitest run \
+ src/components/generated/Template.visual-parity.test.tsx
+```
+
+For a brand-new generated visual parity test, run once with `--update` to seed screenshot baselines.
+
+Optional parity gate (fail if diff is above threshold):
+
+```bash
+CLIENT_TEMPLATE_PARITY_MAX_DIFF= \
+pnpm -C packages/visual-editor exec vitest run \
+ src/components/generated/Template.visual-parity.test.tsx
+```
diff --git a/skills/ve-html-template-generator/agents/openai.yaml b/skills/ve-html-template-generator/agents/openai.yaml
new file mode 100644
index 000000000..2df0746ef
--- /dev/null
+++ b/skills/ve-html-template-generator/agents/openai.yaml
@@ -0,0 +1,4 @@
+interface:
+ display_name: "VE HTML Template Generator"
+ short_description: "Generate slot-based client templates"
+ default_prompt: "Use $ve-html-template-generator with `client: ` and `html: ` to generate isolated, slot-based client component files under `packages/visual-editor/src/components/custom/` plus starter wrapper config/template files under `starter/src/templates/`, then run section screenshot parity as the default post-generation QA step."
diff --git a/skills/ve-html-template-generator/references/baseline-reuse-map.md b/skills/ve-html-template-generator/references/baseline-reuse-map.md
new file mode 100644
index 000000000..f3e0d2f53
--- /dev/null
+++ b/skills/ve-html-template-generator/references/baseline-reuse-map.md
@@ -0,0 +1,59 @@
+# Baseline Reuse Map
+
+Use this map before writing client-local atoms/slots/sections. Pick the closest shared baseline, then copy/adapt it into `packages/visual-editor/src/components/custom//...`.
+
+## Why
+
+- Client output should stay isolated from shared runtime registrations.
+- But implementation quality should still match proven shared patterns.
+- "New client component" should usually mean "client-local copy of a proven baseline plus client-specific changes", not an entirely novel shell.
+
+## Baseline Selection
+
+- Nearby locations bands:
+ - Section baseline: `packages/visual-editor/src/components/pageSections/NearbyLocations/NearbyLocations.tsx`
+ - Slot baseline: `packages/visual-editor/src/components/pageSections/NearbyLocations/NearbyLocationsCardsWrapper.tsx`
+ - Expect dynamic radius/limit + coordinate-driven lookup, not hardcoded nearby-store links.
+- FAQ / Q&A bands:
+ - Section baseline: `packages/visual-editor/src/components/pageSections/FAQsSection/FAQsSection.tsx`
+ - Card baseline: `packages/visual-editor/src/components/pageSections/FAQsSection/FAQCard.tsx`
+ - Reuse shared FAQ data/functionality patterns first (question/answer rich text behavior, card/list structure, interaction model), then style to source visuals.
+- Hours blocks:
+ - Baseline: `packages/visual-editor/src/components/contentBlocks/HoursTable.tsx`
+ - Expect `field: "hours"` entity defaults.
+ - For multiple hours bands (store/drive-thru/lobby), create separate entity-bound hours slots per band.
+- Maps/directions:
+ - Baseline families: map patterns already used in shared VE (`MapboxStaticMapComponent`, `getDirections`).
+ - Avoid static iframe embeds and hardcoded one-off map URLs.
+- Media/image slots:
+ - Baseline: shared image resolution + wrapper patterns (`resolveYextEntityField`, `getImageUrl`, `ImageWrapper`-style behavior).
+- Multi-slot info bands:
+ - Baselines: `CoreInfoSection`, `AboutSection`, `VideoSection`.
+ - For complex/irregular bands, start with shared grid/core-info composition patterns, then wrap into client-bespoke section names and focused slots.
+- CTA/link slots:
+ - Baseline: shared CTA slot ergonomics (show/hide toggles at section level for optional actions).
+ - Baseline implementation: `packages/visual-editor/src/components/contentBlocks/CtaWrapper.tsx`
+ - Every CTA label should map to actionable link/href/cta data; do not render CTA labels as plain text spans.
+- Footer social links:
+ - Baseline: `packages/visual-editor/src/components/footer/FooterSocialLinksSlot.tsx`
+ - Expect platform-specific social fields, URL validation, and icon-based rendering (not plain text social link lists).
+
+## Slot/Atom Reuse Rules
+
+- Prefer adapting existing shared slot APIs first; only diverge when source fidelity requires it.
+- Keep slot composition nested and concern-focused.
+- For optional content in merged sections, keep the slot and gate with show/hide toggles.
+- Keep generated slots non-nestable (`allow={[]}`) because they are editor surfaces, not arbitrary content containers.
+- For FAQ/Q&A sections, prioritize shared FAQ interaction and data-shape behavior over bespoke one-off card models.
+- Suppress likely hidden utility nav headings (for example `Top Links`, `Actions`, `Menu`) unless source evidence confirms they are visibly rendered.
+
+## Nearby Stores Rule
+
+When source includes a "Nearby Stores/Locations" area on a location template:
+
+- Do not seed static links as the primary behavior.
+- Model after shared NearbyLocations behavior:
+ - radius + limit controls
+ - coordinate/document-driven lookup
+ - dynamic card/list rendering
+- If source has a short static fallback list, keep it optional and clearly secondary to dynamic lookup.
diff --git a/skills/ve-html-template-generator/references/client-config-category-pattern.md b/skills/ve-html-template-generator/references/client-config-category-pattern.md
new file mode 100644
index 000000000..11ce6a400
--- /dev/null
+++ b/skills/ve-html-template-generator/references/client-config-category-pattern.md
@@ -0,0 +1,115 @@
+# Client Config and Category Pattern
+
+Use this pattern to keep client templates isolated, slot-based, and visible in both package `mainConfig` and starter `/edit`.
+
+## Goal
+
+- Keep custom section/slot/atom implementation in package custom paths.
+- Expose client sections in one visible left-nav category.
+- Register client slot components in one hidden category.
+- Keep starter as a consumer of package config, not the source of truth.
+- Ensure every `type: ""` referenced in section defaults is present in slot category component maps.
+
+## Package Category Files
+
+Add:
+
+- `packages/visual-editor/src/components/categories/SectionsCategory.tsx`
+- `packages/visual-editor/src/components/categories/SlotsCategory.tsx`
+
+Pattern:
+
+```tsx
+import {
+ YetiHeaderSection,
+ YetiHeaderSectionProps,
+} from "../custom/yeti/components/YetiHeaderSection.tsx";
+
+export interface YetiSectionsCategoryProps {
+ YetiHeaderSection: YetiHeaderSectionProps;
+}
+
+export const YetiSectionsCategoryComponents = {
+ YetiHeaderSection,
+};
+
+export const YetiSectionsCategory = Object.keys(
+ YetiSectionsCategoryComponents,
+) as (keyof YetiSectionsCategoryProps)[];
+```
+
+Apply the same shape for slots category files.
+
+Slot registration rule:
+
+- If a section default uses `type: "YetiHoursSlot"`, then `YetiHoursSlot` must be included in `YetiSlotsCategoryComponents`.
+
+## Starter Client Config Example
+
+`starter/src/templates//-config.tsx` should import category/component maps from `@yext/visual-editor`:
+
+```tsx
+import { Config, DropZone } from "@puckeditor/core";
+import {
+ YetiSectionsCategory,
+ YetiSectionsCategoryComponents,
+ YetiSectionsCategoryProps,
+ YetiSlotsCategory,
+ YetiSlotsCategoryComponents,
+ YetiSlotsCategoryProps,
+} from "@yext/visual-editor";
+
+export interface YetiTemplateProps
+ extends YetiSectionsCategoryProps,
+ YetiSlotsCategoryProps {}
+
+export const yetiConfig: Config = {
+ components: {
+ ...YetiSectionsCategoryComponents,
+ ...YetiSlotsCategoryComponents,
+ },
+ categories: {
+ yetiSections: {
+ title: "Yeti Sections",
+ components: YetiSectionsCategory,
+ },
+ yetiSlots: {
+ components: YetiSlotsCategory,
+ visible: false,
+ },
+ },
+ root: {
+ render: () => (
+
+ ),
+ },
+};
+```
+
+## Package MainConfig Integration
+
+To make generated sections available in default `main` template flow:
+
+1. Export new category files from `packages/visual-editor/src/components/categories/index.ts`.
+2. Register `SectionsCategoryComponents` and `SlotsCategoryComponents` in `packages/visual-editor/src/components/configs/mainConfig.tsx`.
+3. Add one visible `Sections` and one hidden `Slots` category in `mainConfig.categories`.
+4. Keep `directoryConfig` and `locatorConfig` unchanged unless explicitly requested.
+
+## Starter Editor Integration
+
+When `starter/src/ve.config.tsx` exists:
+
+1. Keep `devConfig` based on `...mainConfig.components` and `...mainConfig.categories`.
+2. Add `-location` -> `Config` in `componentRegistry`.
+3. Avoid starter-local ownership of client categories/components when package `mainConfig` already includes them.
+
+Do not add slot categories to visible starter nav categories.
+
+## Notes
+
+- Keep category IDs deterministic (`Sections`, `Slots`).
+- Keep section category titles human-readable (`Yeti Sections`).
+- Include header and footer sections in the visible section category.
diff --git a/skills/ve-html-template-generator/references/layout-containment.md b/skills/ve-html-template-generator/references/layout-containment.md
new file mode 100644
index 000000000..106414b9e
--- /dev/null
+++ b/skills/ve-html-template-generator/references/layout-containment.md
@@ -0,0 +1,69 @@
+# Layout Containment
+
+Use these rules to prevent slot content from spilling into neighboring sections in editor or live rendering.
+
+## Shell Rules
+
+- Keep section content in normal document flow; avoid absolute/fixed positioning for core content.
+- Add `overflow-hidden` to section content wrappers when multiple slots are stacked or arranged in columns.
+- In grid/flex column layouts, add `min-w-0` to each column wrapper.
+- Prefer intrinsic height (`h-auto`/content-driven) over fixed heights for text-heavy columns.
+
+## Slot Render Rules
+
+- Render slot children with `style={{ height: "auto" }}` by default.
+- Keep `allow={[]}` unless the section intentionally supports free slot replacement.
+- Use explicit wrappers per slot row/column to preserve boundaries.
+
+## Two-Column Details Pattern
+
+```tsx
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+## Pre-Ship Checks
+
+- Resize content-heavy slots (long address, long parking copy, many hour rows) and verify no overlap with next section.
+- Drag/reorder section in editor and confirm drop-zone overlays stay inside section bounds.
+
+## Full-Bleed Media Pattern
+
+For hero/promo sections that should match full-width source media:
+
+- Use section shell with zero horizontal padding and unconstrained content width.
+- Ensure breakpoint padding is also removed (`md:px-0`) so desktop does not retain side gaps.
+- Keep overlay copy padded internally while media itself spans edge-to-edge.
+- Avoid rounded-corner wrappers on full-bleed media slots unless source design is rounded.
+
+Example pattern:
+
+```tsx
+
+
+
+
+
+
+
+
+
+
+```
diff --git a/skills/ve-html-template-generator/references/map-image-parity.md b/skills/ve-html-template-generator/references/map-image-parity.md
new file mode 100644
index 000000000..63e192980
--- /dev/null
+++ b/skills/ve-html-template-generator/references/map-image-parity.md
@@ -0,0 +1,52 @@
+# Map and Image Parity
+
+Use this reference to keep generated client templates consistent with existing shared VE component behavior.
+
+## Map Parity Baseline
+
+Prefer these shared patterns:
+
+- `packages/visual-editor/src/components/pageSections/StaticMapSection.tsx`
+- `packages/visual-editor/src/components/contentBlocks/MapboxStaticMap.tsx`
+- `packages/visual-editor/src/components/contentBlocks/Address.tsx` (`getDirections` usage)
+
+Required map rules:
+
+- For location templates, prefer `MapboxStaticMapComponent` with coordinate entity defaults:
+ - `coordinate.field: "yextDisplayCoordinate"`
+- If a static map embed is not available, use `getDirections(...)` links derived from address/entity data.
+- Render a real map surface (shared map component/static map image) for map bands; do not use faux coordinate placeholder cards.
+- Do not hardcode map provider URLs in defaults.
+- Do not use raw `iframe` map embeds for core map blocks.
+- Do not model map preview as a generic `mapImage` content field by default.
+- Do not seed map defaults with hero/promo marketing imagery.
+
+## Image Parity Baseline
+
+Prefer these shared patterns:
+
+- `packages/visual-editor/src/components/pageSections/HeroSection.tsx`
+- `packages/visual-editor/src/components/pageSections/PromoSection/PromoSection.tsx`
+- `packages/visual-editor/src/components/contentBlocks/image/Image.tsx` (`ImageWrapper`)
+- `packages/visual-editor/src/components/pageSections/heroVariants/SpotlightHero.tsx`
+
+Required image rules:
+
+- Background images should be modeled as `data.backgroundImage` entity fields:
+ - `type: "entityField"`
+ - `filter.types: ["type.image"]`
+- Resolve background images at render-time via shared image resolver patterns:
+ - `resolveYextEntityField(...)`
+ - `getImageUrl(...)`
+- Do not use plain URL text controls for core background image sources:
+ - avoid `backgroundImageUrl` fields in `styles`/`data`
+- Image slots should follow `ImageWrapper`-style data shapes:
+ - `data.image` is an image entity field
+ - width/aspect-ratio style controls
+ - safe empty-state behavior if image data is missing
+
+## Default Data Guidance
+
+- For image defaults, include URL + dimensions so previews render immediately.
+- Keep image defaults entity-compatible (constant by default, entity toggle available).
+- For location templates, avoid store-specific hardcoded map URLs in defaults.
diff --git a/skills/ve-html-template-generator/references/quality-checklist.md b/skills/ve-html-template-generator/references/quality-checklist.md
new file mode 100644
index 000000000..6e4fb272f
--- /dev/null
+++ b/skills/ve-html-template-generator/references/quality-checklist.md
@@ -0,0 +1,106 @@
+# Quality Checklist
+
+Run this checklist before finishing generation.
+
+## Architecture
+
+- Every section component has `slots` in props and fields.
+- Slot fields are hidden (`visible: false`).
+- Section render output composes slot children (``).
+- Nested slot/layout components that render child slots also use ``.
+- Slot default items use client-owned slot component types.
+- Every slot type referenced by section defaults is registered in client slot category components.
+- Section slot defaults are populated with real props (no `props: {}` placeholders).
+- Slots represent focused concerns, not combined concerns (for example avoid `HoursLocationSlot`).
+- Hours/location/parking concerns are represented by separate slots when present.
+- Sections with 2+ slots expose top-level show/hide controls for most slots (target coverage: slot count minus one).
+- Multi-band hours content (store/drive-thru/etc.) is represented as separate entity-bound hours slots.
+- Text concerns are decomposed into focused slots (heading/body/disclaimer instead of one omnibus text slot).
+- CTA concerns are decomposed into dedicated slots (primary/secondary CTA slots when both are present).
+- Header/footer structures use nested slots (layout slot + focused child slots), not one terminal omnibus slot.
+- Header/footer layout slots have fully populated child slot defaults (no empty child slot arrays).
+- Footer social links use dedicated social-links slot behavior (platform-aware link fields + icon rendering), not generic text-link list slots.
+- Header/footer section defaults match source chrome style (dark or light) while preserving readable contrast.
+- Header/footer background style controls include source-theme options (for example brand-blue/dark chrome choices when source uses dark header/footer).
+- Client-local sections/slots/atoms are adapted from the closest shared baseline unless a source-driven divergence is required.
+
+## Data Defaults
+
+- Most editable text/image content defaults to constant mode.
+- Hours defaults to mapped entity mode (`field: "hours"` and `constantValueEnabled: false`).
+- No hard-coded weekly hours rows unless explicitly requested by user.
+- User-facing copy inputs use embedded-field-capable controls (`translatableString` or entity-backed string fields), not plain `text` inputs.
+- Array fields include `defaultItemProps` so adding rows in editor cannot create malformed items.
+- Avoid `entityField` filters with `type.url`; use text/translatable URL fields instead.
+- `YextEntityField` defaults use localized objects (`{ en: "...", hasLocalizedValue: "true" }`), not plain string constants.
+- `YextEntityField` defaults include localized objects with `hasLocalizedValue: "true"` when locale keys are used.
+- Location-stream templates avoid hardcoded location constants (city/address/phone) with empty `field` bindings.
+- Location-stream templates avoid hardcoded map/directions URLs tied to one address.
+- Map implementations follow shared VE patterns (`MapboxStaticMapComponent` or `getDirections`), not hardcoded iframe map embeds.
+- Map sections render real map surfaces (shared map component/static map image), not decorative placeholder cards.
+- Map slots do not default to generic `mapImage` content fields unless explicitly requested.
+- Map defaults are not reused from hero/promo image assets.
+- Nearby stores/locations blocks are dynamic (shared NearbyLocations-style lookup), not static hardcoded store-link lists.
+- FAQ/Q&A sections preserve shared FAQ behavior patterns (question/answer modeling + interaction behavior), with client/source-specific styling layered on top.
+- Background images are entity-driven (`type.image`) instead of plain URL text fields like `backgroundImageUrl`.
+- Image slots use shared-style image data patterns (`data.image` entity field + width/aspect controls + empty-state-safe behavior).
+
+## Editor Nav and Config
+
+- Client sections are visible in one client section category.
+- Client slot components are in one hidden client slot category.
+- Client slot components are included in `config.components` registration (for example via `SlotsCategoryComponents` spread).
+- Atoms are not nav-selectable.
+- Starter config is updated so client sections show in starter `/edit` left nav when applicable.
+- Starter remains a consumer of package `mainConfig` (`...mainConfig.components`, `...mainConfig.categories`) instead of owning generated client section/slot maps.
+- Starter `componentRegistry` includes `-location` mapped to `Config`.
+- Starter `DevProps` is only extended with client section/slot props when explicit starter-local client component maps are intentionally added.
+- Package `mainConfig` registers client-specific `SectionsCategoryComponents` and `SlotsCategoryComponents`.
+- Package `mainConfig.categories` includes visible `Sections` + hidden `Slots` categories.
+- Selecting a slot-backed section shows populated slot props in the right-hand props panel.
+- All slot render calls include `allow={[]}` and `style={{ height: "auto" }}` in both sections and nested slot/layout components.
+- Freshly dragged header/footer sections are editable in-canvas (no blank/non-clickable nested slot regions).
+
+## Runtime and UX
+
+- Use render-safe defaults so sections display immediately.
+- Prefer theme-backed controls for color selectors over raw text color fields.
+- Add `liveVisibility` toggles for section-level show/hide behavior.
+- Use section-level error boundaries/wrappers where appropriate.
+- Ensure `` receives `metadata={{ streamDocument: document }}` in client template entrypoints.
+- Render code defensively handles partially defined array rows (no unsafe nested `.field` reads).
+- Similar sections that differ only by copy/CTA visibility are merged into one component with toggles.
+- Merged sections preserve optional slots from all source variants (for example CTA slots) and expose explicit show/hide toggles for those slots.
+- CTA labels are always actionable (paired with href/link/CTA entity data), not rendered as plain text.
+- Multi-slot section shells include containment cues (`overflow-hidden`, column wrappers with `min-w-0`).
+- Store-info sections that include hours/location/map/parking preserve source-first slot order (default `Hours -> Location -> Map -> Parking`).
+- Core section/slot wrappers avoid absolute/fixed positioning for main content.
+- Section-level `textColor` controls remain effective (slot content does not force hardcoded conflicting text color classes).
+- Header/footer editability is preserved through nested child slots for logo/nav/CTA/legal concerns.
+- Header/footer text and border styles maintain contrast and do not rely on fixed white/light utility classes.
+- Header/nav utility defaults avoid noisy hidden headings (for example `Top Links`, `Actions`, `Menu`) unless source explicitly shows them.
+- Hero/promo sections that are media-led use full-bleed section shell wrappers (`px-0 md:px-0 py-0 md:py-0` + `max-w-none`) when source parity requires edge-to-edge imagery.
+- Hero/promo media slots avoid rounded corners by default unless source design explicitly calls for rounded media.
+- Hero/promo image elements render as block-level (`block h-full w-full object-cover`) to avoid baseline whitespace seams.
+- Rich text slot rendering uses `resolveComponentData` render output (React element or string) rather than html-only extraction, so copy remains visible across value shapes.
+- Source bands that pair media + list/content keep a dedicated media slot instead of dropping imagery during decomposition.
+- Generated files do not reference sibling client template paths/symbols under `starter/src/templates/` or `packages/visual-editor/src/components/custom/`.
+
+## Final Validation
+
+- Run `python3 scripts/validate_client_template.py --client-path starter/src/templates/`.
+- Type check passes for generated client template files.
+- Sections render in expected order in default layout.
+- Default layout section entries use populated props (default props or explicit overrides), not `props: {}` placeholders.
+- Client category appears in left nav in starter editor.
+- Validate with a clean/new layout state (no stale `document.__.layout` from old generations).
+- Hours slot resolves live entity hours without manual constant content.
+- Hours blocks keep explicit entity-backed wiring (`field: "hours"`) and expose entity/constant state correctly in the editor.
+- Summary/hero info areas avoid monolithic slot props and are decomposed into nested focused child slots.
+- Validator does not report suspicious structural similarity to older sibling client templates.
+- Section parity test is scaffolded and run for the generation (or explicitly reported as skipped with reason if browser screenshot tests are unavailable).
+- Parity-driven updates are limited to high-impact visual corrections and do not degrade slot architecture or field/editability behavior.
+- For major hierarchy/order mismatches, parity should drive one corrective pass (source order/stacking/theme contrast) before finalizing.
+- Optional smoke screenshot tests are scaffolded when requested and run (or explicitly reported as not run if environment lacks browser test support).
+- After correction-pass reruns, run `pnpm -C packages/visual-editor build` and ensure it exits successfully.
+- After correction-pass reruns, run `pnpm -C starter build` and ensure it exits successfully.
diff --git a/skills/ve-html-template-generator/references/section-normalization.md b/skills/ve-html-template-generator/references/section-normalization.md
new file mode 100644
index 000000000..026d78ff6
--- /dev/null
+++ b/skills/ve-html-template-generator/references/section-normalization.md
@@ -0,0 +1,131 @@
+# Section Normalization
+
+Use this pass before writing final section files.
+
+## Goal
+
+- Split over-aggregated slots into focused slot components.
+- Merge structurally duplicate sections into one reusable section.
+
+## Slot Decomposition Rules
+
+Each slot should map to one clear concern. Avoid omnibus slots.
+
+Good:
+
+- `HoursSlot`
+- `LocationInfoSlot`
+- `ParkingSlot`
+
+Bad:
+
+- `HoursLocationSlot`
+- `InfoAndHoursSlot`
+
+When a section contains hours, location info, and parking, default to three slots:
+
+- `HoursSlot`
+- `LocationInfoSlot`
+- `ParkingSlot`
+
+If source contains multiple hours bands (for example store hours and drive-thru hours), do not collapse them into one hours slot.
+Generate one dedicated entity-bound hours slot per band.
+
+Text and CTA decomposition defaults:
+
+- `HeadingSlot` for primary heading text.
+- `BodySlot` for descriptive copy.
+- `PrimaryCtaSlot` and `SecondaryCtaSlot` for distinct CTAs.
+- `DisclaimerSlot` for legal/supporting copy.
+- Use embedded-field-capable copy inputs (`translatableString` or entity-backed string fields) for user-visible non-entity text.
+- Every CTA label slot/field should pair with actionable href/link/CTA data; do not keep CTA labels as plain text-only output.
+
+For non-list slots, treat these as over-aggregated and split them:
+
+- 3+ translatable text entity fields in one slot
+- Any CTA label/link fields combined with multiple unrelated text fields
+
+If visual grouping is needed, create a layout slot that composes focused child slots.
+
+Nested-slot bias:
+
+- Prefer nested layout slots over packing multiple text/CTA fields into one terminal slot.
+- Keep parent section as a shell, with layout slots composing focused content slots.
+- Keep nested slot renders locked for editing-only usage (`allow={[]}` + `style={{ height: "auto" }}`).
+
+Header/footer decomposition:
+
+- Treat header/footer as high-priority slotification targets.
+- Split monolithic header/footer slots into layout + focused child slots.
+- Do not leave header/footer copy/links/buttons in one terminal slot component.
+- Do not model footer social as a generic text-link list slot. Use a dedicated social-links slot pattern with platform-aware link fields and icon rendering.
+- Suppress noisy hidden utility heading labels in nav/footer defaults (`Top Links`, `Actions`, `Menu`) unless source evidence confirms visible rendering.
+
+Location summary decomposition:
+
+- Treat hero/location summary content as a layout slot composed of focused child slots.
+- Split into child concerns such as:
+ - back link
+ - location heading/address/meta
+ - status row
+ - CTA row
+- Avoid monolithic summary slots with many unrelated text/link fields.
+
+## Duplicate Section Merge Rules
+
+If two sections share the same layout shell, slot schema, and style controls, merge them into one section component.
+
+Use configurable toggles/variants instead of duplicate files.
+
+When merging, preserve the superset of optional content slots from all variants (do not drop "sometimes present" slots).
+
+Example:
+
+- If one variant has CTA and another does not, keep CTA slot(s) in merged component.
+- Add explicit top-level show/hide toggles for those optional slots (`showPrimaryCta`, `showSecondaryCta`, etc.).
+- Default toggles per layout instance to match source parity (disabled where not used).
+
+Example merge:
+
+- Replace `CustomizeSection` + `ReserveSection` with `PromoBannerSection`
+- Add style/data toggles:
+ - `showCTA: boolean`
+ - `overlayClass`
+ - `backgroundImageUrl`
+- Keep alternate defaults as preset prop objects.
+
+## Decision Heuristic
+
+Treat sections as duplicates when all are true:
+
+- Same slot keys
+- Same style field keys
+- Same dominant layout pattern (same wrapper structure)
+
+When duplicates are detected, generate one component plus multiple default preset objects.
+
+## Baseline-First Rules
+
+Use existing shared VE sections as implementation baselines before inventing novel structures.
+Do not use sibling client templates as baselines.
+
+Preferred baseline order:
+
+1. `CoreInfoSection` for multi-slot information grids
+2. `AboutSection` for mixed column content and sidebar patterns
+3. `VideoSection` for simple heading+content shells
+
+For complex bespoke pages, assemble with shared grid/core-info atom patterns first, then wrap that composition in client-bespoke section names and focused slot labels.
+
+Then adapt/copy those patterns into client-local files (do not import shared sections directly).
+
+For location pages with nearby stores/locations content, baseline against NearbyLocations patterns rather than creating static nearby-link lists.
+
+## Containment Rules
+
+Before finalizing, verify section shell containment:
+
+- Multi-slot section container uses `overflow-hidden`.
+- Grid/flex column wrappers include `min-w-0`.
+- Slot calls use `style={{ height: "auto" }}` unless intentionally overridden.
+- Core content in section/slot wrappers avoids absolute/fixed positioning.
diff --git a/skills/ve-html-template-generator/references/slot-paradigm.md b/skills/ve-html-template-generator/references/slot-paradigm.md
new file mode 100644
index 000000000..9b277c374
--- /dev/null
+++ b/skills/ve-html-template-generator/references/slot-paradigm.md
@@ -0,0 +1,218 @@
+# Slot Paradigm
+
+Use this for all generated client sections.
+
+## Section Shape
+
+Client sections should be structural wrappers that render slot content.
+
+```tsx
+import { ComponentConfig, Fields, PuckComponent, Slot } from "@puckeditor/core";
+import { YextField } from "@yext/visual-editor";
+
+type ClientHeroSectionProps = {
+ styles: {
+ backgroundColor?: string;
+ };
+ slots: {
+ HeadingSlot: Slot;
+ BodySlot: Slot;
+ PrimaryCTASlot: Slot;
+ };
+ liveVisibility: boolean;
+};
+
+const fields: Fields = {
+ styles: YextField("Styles", {
+ type: "object",
+ objectFields: {
+ backgroundColor: YextField("Background Color", {
+ type: "select",
+ options: "BACKGROUND_COLOR",
+ }),
+ },
+ }),
+ slots: {
+ type: "object",
+ objectFields: {
+ HeadingSlot: { type: "slot" },
+ BodySlot: { type: "slot" },
+ PrimaryCTASlot: { type: "slot" },
+ },
+ visible: false,
+ },
+ liveVisibility: YextField("Visible on Live Page", {
+ type: "radio",
+ options: [
+ { label: "Show", value: true },
+ { label: "Hide", value: false },
+ ],
+ }),
+};
+
+const HeroView: PuckComponent = ({ slots }) => {
+ return (
+
+
+
+
+
+ );
+};
+```
+
+## Default Slot Props
+
+Each section must define `defaultProps.slots` with client-owned slot types.
+
+```tsx
+defaultProps: {
+ slots: {
+ HeadingSlot: [{ type: "YetiHeadingSlot", props: { ... } }],
+ BodySlot: [{ type: "YetiBodySlot", props: { ... } }],
+ PrimaryCTASlot: [{ type: "YetiCTASlot", props: { ... } }],
+ },
+}
+```
+
+Never use placeholder slot defaults like `props: {}`.
+
+## Slot Drop Lock
+
+Generated slots are for editing pre-scaffolded fields/props, not for arbitrary nesting.
+
+- Always render generated slot calls with `allow={[]}`.
+- Apply this to section-level slot calls and nested layout-slot calls.
+- Keep `style={{ height: "auto" }}` on those slot calls unless intentionally overridden.
+
+Prefer exporting `defaultProps` from each slot component and reusing those objects in section slot defaults.
+
+## Top-Level Slot Visibility Controls
+
+For sections with 2+ slots, expose top-level `show...` toggles in `styles` for most slots.
+
+- Target coverage: `slot count - 1` or higher.
+- Keep always-on slots only when they are structurally required.
+- Wire each toggle directly in section render logic, not only inside nested child slots.
+
+Example:
+
+```tsx
+styles: {
+ showHeading: boolean;
+ showBody: boolean;
+ showPrimaryCta: boolean;
+}
+```
+
+Example:
+
+```tsx
+import { defaultYetiFaqListSlotProps } from "./YetiFaqListSlot";
+
+slots: {
+ FaqListSlot: [{ type: "YetiFaqListSlot", props: defaultYetiFaqListSlotProps }],
+}
+```
+
+## Hours Slot Rule
+
+When a section includes hours, route through a client slot component and default to entity hours:
+
+```tsx
+HoursSlot: [
+ {
+ type: "YetiHoursSlot",
+ props: {
+ data: {
+ hours: {
+ field: "hours",
+ constantValue: {},
+ constantValueEnabled: false,
+ },
+ },
+ },
+ },
+];
+```
+
+Avoid hard-coding weekly hours strings in section defaults.
+
+If source has multiple hours bands, generate multiple hours slots (for example `StoreHoursSlot`, `DriveThruHoursSlot`) and keep each one entity-bound.
+
+## Decomposition Heuristic
+
+Prefer multiple focused slots over one combined slot.
+
+When a section visually contains distinct concerns (hours table, location details, parking notes), represent each concern as its own slot rather than a merged slot.
+
+Apply this rule to copy/CTA decomposition as well:
+
+- Heading text should be its own slot.
+- Body/description text should be its own slot.
+- Each CTA should be its own slot (primary CTA, secondary CTA).
+- Legal/disclaimer text should be its own slot.
+- For user-visible non-entity copy fields, use embedded-field-capable inputs (`translatableString`) rather than plain `text` inputs.
+- CTA labels must map to actionable href/link/CTA data; do not render CTA labels as plain text spans.
+
+Avoid non-list slots with many text fields plus CTA fields. If grouping is needed, use a layout slot that renders child slots.
+
+Favor nested slot composition when in doubt:
+
+- Section shell slot -> layout slot -> focused text/CTA slots
+- Avoid terminal slots that try to own all copy and actions for an entire band
+
+Header/footer specific rule:
+
+- Header/footer should always be multi-layer slot compositions, not single terminal content slots.
+- Use focused child slots for:
+ - logo/brand
+ - navigation groups
+ - CTA groups
+ - utility/legal groups
+- Keep header/footer layout orchestration in layout slots and keep content concerns in child slots.
+- Ensure layout slots have populated default child slots so they are editable immediately after drag/drop.
+- Avoid introducing layout-slot shapes that require runtime backfill/normalization to become editable.
+- Footer social link concerns should use dedicated social slot patterns (platform-aware fields + icon rendering), not generic text-link list slots.
+- Avoid noisy nav utility heading defaults (`Top Links`, `Actions`, `Menu`) unless source evidence confirms they are visibly rendered.
+
+Location summary specific rule:
+
+- Avoid one monolithic summary slot with many unrelated fields.
+- Use nested summary layout slots with focused child slots (back-link, heading/meta, status, CTA).
+
+Recommended pattern:
+
+```tsx
+slots: {
+ HoursSlot: Slot;
+ LocationInfoSlot: Slot;
+ MapSlot: Slot;
+ ParkingSlot: Slot;
+}
+```
+
+When all four concerns are present, preserve source-first stacked order by default:
+
+`Hours -> Location -> Map -> Parking`
+
+## Containment Pattern for Multi-Column Sections
+
+Use containment-safe wrappers to avoid slot overflow into adjacent sections:
+
+```tsx
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+For core content, avoid absolute/fixed positioning in slot wrappers unless strictly decorative and fully bounded by a relative container.
diff --git a/skills/ve-html-template-generator/references/template-contract.md b/skills/ve-html-template-generator/references/template-contract.md
new file mode 100644
index 000000000..8b5f2cc2c
--- /dev/null
+++ b/skills/ve-html-template-generator/references/template-contract.md
@@ -0,0 +1,168 @@
+# Template Contract
+
+Use this contract for every generated client template.
+
+## Required Directory Layout
+
+```text
+packages/visual-editor/src/components/custom//
+ atoms/
+ .tsx
+ ...
+ components/
+ Section.tsx
+ Slot.tsx
+ ...
+ index.ts
+ ve.ts
+
+packages/visual-editor/src/components/categories/
+ SectionsCategory.tsx
+ SlotsCategory.tsx
+
+starter/src/templates//
+ -config.tsx
+ -template.tsx
+```
+
+## Naming Rules
+
+- Use lowercase client slug for generated folder and starter template filenames.
+- Use `PascalCase` for section/slot component filenames and exports.
+- Suffix generated section components with `Section`.
+- Suffix slot components with `Slot`.
+- Keep component names stable after first generation; update implementation instead of renaming unless required.
+- Prefix client-branded header/footer names with client context (`YetiHeaderSection`, `YetiFooterSection`).
+
+## Isolation Rules
+
+- Do not import shared/generic VE atoms, sections, categories, or config maps into client generated section/slot/atom implementations.
+- Do not import, reference, or adapt sibling client templates under `starter/src/templates/`.
+- Do not import, reference, or adapt sibling custom clients under `packages/visual-editor/src/components/custom/`.
+- Always generate new client-specific header/footer sections.
+- Always generate client-specific atoms even when equivalent shared atoms exist.
+- Keep generated section/slot/atom implementation in `packages/visual-editor/src/components/custom/`.
+- Use shared generic components only as behavior baselines to copy/adapt into client-local files.
+
+## Template Responsibilities
+
+In `starter/src/templates//-template.tsx`:
+
+- Use a full `@yext/pages` template entrypoint structure.
+- Import client section components from `@yext/visual-editor`.
+- Import client config from `./-config`.
+- Preserve source page order when composing the initial layout and defaults.
+- In default layout `content`, use section `defaultProps` (or explicit deep overrides) instead of `props: {}` placeholders.
+- Export the client template entrypoint used by the site template system.
+
+In `starter/src/templates//-config.tsx`:
+
+- Register client-owned section and slot category component maps imported from `@yext/visual-editor`.
+- Define one visible client-specific category for sections in the left nav.
+- Define one hidden client-specific category for slot components.
+- Expose only section components in the visible category.
+- Do not register atoms as nav-selectable components.
+- Register all slot component types referenced by section `defaultProps.slots`.
+
+In `packages/visual-editor/src/components/categories/SectionsCategory.tsx` and `SlotsCategory.tsx`:
+
+- Import section/slot components from `../generated//components/...`.
+- Export props interfaces and `...CategoryComponents` maps.
+- Export category arrays from `Object.keys(...CategoryComponents)`.
+
+In `packages/visual-editor/src/components/configs/mainConfig.tsx`:
+
+- Register `SectionsCategoryComponents` and `SlotsCategoryComponents` in `components`.
+- Add one visible `Sections` category and one hidden `Slots` category in `mainConfig.categories`.
+- Leave `directoryConfig` and `locatorConfig` unchanged unless explicitly requested.
+
+When `starter/src/ve.config.tsx` is present:
+
+- Keep starter as a consumer of package `mainConfig` (`...mainConfig.components`, `...mainConfig.categories`).
+- Add `-location` -> `Config` to `componentRegistry`.
+- Avoid starter-local mutation of `mainConfig` or starter-sourced category ownership.
+
+## Section Component Responsibilities
+
+In each `packages/visual-editor/src/components/custom//components/Section.tsx`:
+
+- Define explicit props with `data`, `styles`, and `slots`.
+- Drive structure/content composition through slots, not inline hard-coded markup.
+- Use intuitive, concern-specific slots (for example `HoursSlot`, `LocationInfoSlot`, `ParkingSlot`).
+- When source has multiple hours bands, represent each band with its own entity-bound hours slot.
+- Decompose text and CTA concerns into dedicated slots by default (for example `HeadingSlot`, `BodySlot`, `PrimaryCtaSlot`).
+- Use embedded-field-capable controls for user-visible copy (`translatableString` or entity-backed string fields), not plain `text` inputs.
+- Keep sections focused on one horizontal band from the source page.
+- Keep defaults close to source copy and imagery for fast first review.
+- Avoid introducing cross-client abstractions in first-pass output.
+- Nearby stores/location sections should follow shared NearbyLocations-style dynamic behavior, not static hardcoded store-link lists.
+- FAQ/Q&A sections should follow shared FAQ data/interaction patterns first, then adapt visuals.
+- When source includes media + list compositions (for example amenities image + amenities list), preserve a dedicated media slot in the section shell.
+- When combining two similar sections, preserve the superset of optional slots (for example CTA slots) and gate optional ones with top-level show/hide style toggles.
+- Use containment-safe section shells (`overflow-hidden`, `min-w-0` column wrappers, slot render calls with `style={{ height: "auto" }}`).
+- Header/footer sections must use nested slot composition (layout slot + focused child slots), not single omnibus slots.
+- Header/footer layout slots must include populated default child-slot entries so nested slots are immediately editable after drag/drop.
+- Header/footer slot content should not depend on fixed white/light text classes for readability; preserve contrast across background options.
+- Header/footer section defaults should match source chrome style (dark or light) while preserving readable contrast.
+- Avoid noisy utility heading defaults in header/nav groups (for example `Top Links`, `Actions`, `Menu`) unless source evidence shows those headings are visibly rendered.
+- Footer social links should follow the shared social-links baseline (platform-specific fields, URL validation, icon rendering), not plain text social link lists.
+- Location summary-style content should use nested layout + focused child slots rather than one large summary slot.
+- Hero/promo sections should use full-bleed shell wrappers (`px-0 md:px-0` outer padding, `max-w-none` content wrapper) when source parity expects media to reach section edges.
+
+## Slot Component Responsibilities
+
+In each `packages/visual-editor/src/components/custom//components/Slot.tsx`:
+
+- Implement client-owned content blocks used by section slots.
+- Keep slot component APIs narrow and reusable across client sections.
+- Treat generated slots as non-nestable editing surfaces: any `` render inside slot/layout components must use `allow={[]}` and `style={{ height: "auto" }}`.
+- For hours slots, default to mapped entity hours (`field: "hours"`) instead of hard-coded constant hours.
+- Export slot components through client slot category maps so Puck can resolve them during drag/drop.
+- For array controls, provide `defaultItemProps` to prevent malformed new rows.
+- Do not rely on `type.url` entity selectors for editable URL values.
+- Map slots should follow shared VE map patterns (`MapboxStaticMapComponent` or `getDirections`), not hardcoded iframe URLs.
+- Image/background slots should follow shared VE image entity patterns (`type.image` selectors and render-time image resolution).
+- Media-led hero/promo slots should avoid rounded-corner wrappers unless source design explicitly requires rounded media.
+- Rich text slots should render `resolveComponentData` output directly (React element or string) instead of assuming `value.html` is always present.
+- CTA labels should always pair with actionable href/link/CTA data and render as CTA controls, not plain text spans.
+
+## Atom Responsibilities
+
+In each `packages/visual-editor/src/components/custom//atoms/.tsx`:
+
+- Keep atom API minimal and predictable.
+- Include only behavior needed by client-owned sections.
+- Avoid importing shared atoms from outside the client generated folder.
+- Do not depend on runtime layout-shape normalization to make generated slots editable; generate correct slot defaults directly.
+
+## Minimum Completion Checklist
+
+- Every major visual band has exactly one section component.
+- Header and footer are client-specific sections.
+- Atoms used by sections are client-specific atoms.
+- Sections use hidden slot fields and render through slot children.
+- All slot render calls are locked (`allow={[]}`) in both sections and nested slot/layout components.
+- Section slot defaults are fully populated (no `props: {}` placeholders).
+- Header/footer default section backgrounds/styles match source chrome style.
+- Starter client config/template import client category components/sections from `@yext/visual-editor`.
+- Package `mainConfig` and categories include the new client section/slot category wiring.
+- No sibling client symbols/paths are referenced in generated code.
+- The client config includes visible `clientSections` and hidden `clientSlots` categories.
+- Slot components with array fields include `defaultItemProps`.
+- `YextEntityField` defaults use localized constant objects (`{ en, hasLocalizedValue: "true" }`) so right-panel defaults are visible on first load.
+- `YextEntityField` defaults include `hasLocalizedValue: "true"` when using locale-key constants.
+- User-facing copy fields are embedded-field capable (`translatableString` or entity-backed string fields), not plain text controls.
+- Hours/location/parking are not collapsed into a single omnibus slot.
+- Similar shell sections are merged into one configurable section where feasible.
+- Location-stream templates avoid hardcoded city/address/phone/map defaults tied to a single location.
+- Map blocks avoid hardcoded iframe provider embeds and rely on shared map patterns.
+- Map slots do not default to generic `mapImage` fields populated with marketing imagery.
+- Background images and image slots are entity-driven (`type.image`) rather than plain URL text fields.
+- Section-level text color controls are not neutralized by hardcoded slot text color classes.
+- Header/nav defaults do not include hidden utility heading noise (`Top Links`, `Actions`, `Menu`) unless source visibly renders those headings.
+- CTA labels are actionable (link/href/cta), not plain text.
+- Hero/promo media reaches section edges for full-width source bands (no accidental inset wrappers or rounded-card framing), including desktop breakpoints.
+- The template root imports and uses the client config.
+- The template default layout does not use empty section `props: {}` placeholders.
+- The generated files compile with no type errors.
+- The output is easy to iterate component-by-component in follow-up prompts.
diff --git a/skills/ve-html-template-generator/references/test-and-screenshot-workflow.md b/skills/ve-html-template-generator/references/test-and-screenshot-workflow.md
new file mode 100644
index 000000000..53f6e1e6a
--- /dev/null
+++ b/skills/ve-html-template-generator/references/test-and-screenshot-workflow.md
@@ -0,0 +1,173 @@
+# Test and Screenshot Workflow
+
+Use this on every generation as the default post-generation parity workflow.
+
+## Goal
+
+- Create a smoke test that renders the generated client template/config.
+- Capture screenshots for quick visual review.
+- Reuse the existing Vitest browser + screenshot matcher workflow.
+- Prefer section-level source-vs-generated comparison before whole-page comparison.
+- Always capture section-level source-vs-generated parity in one pass (report-first defaults).
+
+## Scaffold Test
+
+Run:
+
+```bash
+python3 scripts/scaffold_client_template_smoke_test.py \
+ --client-slug \
+ --template-path starter/src/templates//-template.tsx \
+ --config-path starter/src/templates//-config.tsx
+```
+
+This creates:
+
+- `packages/visual-editor/src/components/generated/Template.smoke.test.tsx`
+
+## Required Section Parity (Default)
+
+Run:
+
+```bash
+python3 scripts/scaffold_client_template_section_parity_test.py \
+ --client-slug \
+ --html-path /path/to/source.html \
+ --config-path starter/src/templates//-config.tsx \
+ --overwrite
+```
+
+This creates:
+
+- `packages/visual-editor/src/components/generated/Template.section-parity.test.tsx`
+- `packages/visual-editor/src/components/generated/Template.section-parity.source.html`
+
+Run section parity test:
+
+```bash
+pnpm -C packages/visual-editor exec vitest run \
+ src/components/generated/Template.section-parity.test.tsx
+```
+
+For a brand-new generated parity test, run once with `--update` to seed screenshot baselines:
+
+```bash
+pnpm -C packages/visual-editor exec vitest run --update \
+ src/components/generated/Template.section-parity.test.tsx
+```
+
+After the initial parity run, apply one focused correction pass immediately, then rerun smoke + section parity.
+After those reruns, run final build checks:
+
+```bash
+pnpm -C packages/visual-editor build
+pnpm -C starter build
+```
+
+Default workflow is now: `generate -> parity -> correction pass -> rerun -> build gate`.
+
+Optional section parity controls:
+
+```bash
+CLIENT_TEMPLATE_SECTION_PARITY_LIMIT=10 \
+CLIENT_TEMPLATE_SECTION_PARITY_ALL_VIEWPORTS=1 \
+pnpm -C packages/visual-editor exec vitest run \
+ src/components/generated/Template.section-parity.test.tsx
+```
+
+Optional section parity gate:
+
+```bash
+CLIENT_TEMPLATE_SECTION_PARITY_MAX_DIFF= \
+pnpm -C packages/visual-editor exec vitest run \
+ src/components/generated/Template.section-parity.test.tsx
+```
+
+## Optional Full-Page Parity
+
+Optional full-page parity scaffold:
+
+```bash
+python3 scripts/scaffold_client_template_visual_parity_test.py \
+ --client-slug \
+ --html-path /path/to/source.html \
+ --config-path starter/src/templates//-config.tsx \
+ --overwrite
+```
+
+This creates:
+
+- `packages/visual-editor/src/components/generated/Template.visual-parity.test.tsx`
+- `packages/visual-editor/src/components/generated/Template.source.html`
+
+## Run Test
+
+Run only the generated test:
+
+```bash
+pnpm -C packages/visual-editor exec vitest run \
+ src/components/generated/Template.smoke.test.tsx
+```
+
+For a brand-new generated smoke test, run once with `--update` to seed screenshot baselines:
+
+```bash
+pnpm -C packages/visual-editor exec vitest run --update \
+ src/components/generated/Template.smoke.test.tsx
+```
+
+Smoke screenshots should capture full page height so lower-page sections (for example footer/legal) are included in review.
+
+Run visual parity test:
+
+```bash
+pnpm -C packages/visual-editor exec vitest run \
+ src/components/generated/Template.visual-parity.test.tsx
+```
+
+For a brand-new generated visual parity test, run once with `--update` to seed screenshot baselines:
+
+```bash
+pnpm -C packages/visual-editor exec vitest run --update \
+ src/components/generated/Template.visual-parity.test.tsx
+```
+
+Optional parity gate:
+
+```bash
+CLIENT_TEMPLATE_PARITY_MAX_DIFF= \
+pnpm -C packages/visual-editor exec vitest run \
+ src/components/generated/Template.visual-parity.test.tsx
+```
+
+## Test Expectations
+
+Generated smoke tests should:
+
+- render a minimal layout using the generated client config
+- verify no fatal render errors
+- run across desktop/tablet/mobile viewports
+- capture full rendered page height screenshots for baseline visual review
+
+Generated visual parity tests should:
+
+- capture section-level source screenshot baselines and generated section screenshots
+- log diff pixel counts per section so large regressions are easy to triage
+- default to report-only behavior unless explicit parity thresholds are provided
+- treat section pairing as a heuristic by source order and generated section registration order (manual review is still required)
+- capture source HTML screenshot baselines per viewport
+- capture generated template screenshots per viewport
+- log source-vs-generated diff pixel counts
+- optionally fail when diff exceeds `CLIENT_TEMPLATE_PARITY_MAX_DIFF`
+
+## Notes
+
+- Keep smoke tests lightweight; they are intended for rapid iteration feedback.
+- Keep screenshot parity advisory-first: fix missing media/structure/wiring issues first, then tune styling.
+- Do not break baseline slot behavior or field editability for screenshot parity wins.
+- Apply a single parity-driven correction pass by default; only run extra passes for severe mismatches or explicit user request.
+- Prioritize high-impact parity fixes: missing imagery/maps, wrong section hierarchy, readability/contrast failures, and major spacing/layout breaks.
+- Treat hierarchy/order mismatches as mandatory correction targets in the default pass (for example stacked store info ordering, header/footer theme contrast).
+- For map sections that require API keys, prefer deterministic fallbacks and modest screenshot thresholds.
+- If the local environment cannot run browser screenshot tests, still generate the test file and report that execution was skipped.
+- Use parity screenshots as an iterative loop: fix largest visual gaps first (missing media, spacing, hierarchy, contrast), rerun parity test, repeat.
diff --git a/skills/ve-html-template-generator/references/ve-field-patterns.md b/skills/ve-html-template-generator/references/ve-field-patterns.md
new file mode 100644
index 000000000..4887a72d1
--- /dev/null
+++ b/skills/ve-html-template-generator/references/ve-field-patterns.md
@@ -0,0 +1,329 @@
+# VE Field Patterns
+
+Use these patterns for generated components so they work with Visual Editor controls.
+
+## Slot-First Rule
+
+Section components should be layout shells that render slot children.
+
+Required shape in section props:
+
+```ts
+slots: {
+ : Slot;
+ ...
+}
+```
+
+Required shape in section fields:
+
+```ts
+slots: {
+ type: "object",
+ objectFields: {
+ : { type: "slot" },
+ },
+ visible: false,
+}
+```
+
+Use slot defaults in `defaultProps.slots` with client-owned slot component types.
+
+## Local Atom Rule
+
+When a section needs primitives (heading, body text, image wrapper, button), implement/import them from `packages/visual-editor/src/components/custom//atoms/*`.
+
+Do not import shared atoms from generic VE directories.
+
+## Core Shape: Entity vs Constant
+
+Use this data shape for content-bearing props:
+
+```ts
+type YextEntityField = {
+ field: string;
+ constantValue: T;
+ constantValueEnabled?: boolean;
+};
+```
+
+Initialize most fields with meaningful constants and `constantValueEnabled: true`.
+
+Exception:
+
+- Hours fields should default to mapped entity values (`field: "hours"`) and entity mode.
+
+Supported entity selector filters for this skill:
+
+- `type.string`
+- `type.rich_text_v2`
+- `type.image`
+- `type.phone`
+- `type.hours`
+
+Avoid `entityField` with `type.url` in generated client templates.
+
+## Text Field Pattern
+
+Use an entity field selector filtered to string values:
+
+```ts
+text: YextField("Text", {
+ type: "entityField",
+ filter: { types: ["type.string"] },
+});
+```
+
+Default value pattern:
+
+```ts
+text: {
+ field: "",
+ constantValue: { en: "Section heading", hasLocalizedValue: "true" },
+ constantValueEnabled: true,
+}
+```
+
+Do not use a plain string for `constantValue` on `YextEntityField` defaults.
+
+Bad:
+
+```ts
+constantValue: "Section heading";
+```
+
+This can render in preview, but the right-side prop editor often initializes as empty because it reads localized values from `constantValue[locale]`.
+
+## Rich Text Pattern
+
+Use rich text selector for marketing copy blocks:
+
+```ts
+description: YextField("Description", {
+ type: "entityField",
+ filter: { types: ["type.rich_text_v2"] },
+});
+```
+
+Default constant value should be locale-aware and render-safe.
+
+For `YextEntityField` defaults, include `hasLocalizedValue: "true"` when using locale keys.
+
+Good:
+
+```ts
+description: {
+ field: "",
+ constantValue: {
+ en: { html: "
Example
", json: "{...}" },
+ hasLocalizedValue: "true",
+ },
+ constantValueEnabled: true,
+}
+```
+
+Avoid locale-key objects without `hasLocalizedValue`, which can break prop-panel hydration and runtime rendering.
+
+Render pattern for rich text slots:
+
+- Resolve with `resolveComponentData(...)` and render the result directly.
+- Handle both `ReactElement` and `string` outputs.
+- Do not rely exclusively on manual `value.html` parsing, because editor/KG data may arrive as different rich-text object shapes.
+
+## Hours Pattern
+
+Use hours selectors for hours-based slot components:
+
+```ts
+hours: YextField("Hours", {
+ type: "entityField",
+ filter: { types: ["type.hours"] },
+});
+```
+
+Default hours binding pattern:
+
+```ts
+hours: {
+ field: "hours",
+ constantValue: {},
+ constantValueEnabled: false,
+}
+```
+
+## URL Field Pattern
+
+Use URL fields directly for links/background URLs:
+
+```ts
+ctaUrl: YextField("CTA URL", { type: "text" });
+```
+
+If localization is required, use a translatable-string field strategy rather than `entityField` with `type.url`.
+
+Do not use URL text fields for core map/image sources (background images, map embeds, directions source URLs) when entity-backed/shared patterns are available.
+
+## Location Stream Dynamic Data Pattern
+
+For templates with `entityTypes: ["location"]`, avoid hardcoded location defaults that lock output to one site/store.
+
+Preferred defaults:
+
+- Store name/hero title: bind to `field: "name"` when the source represents location identity.
+- Address text: bind to address fields (`address.line1`, derived city/state/postal, or `address` object patterns).
+- Phone text: bind to `mainPhone` (or equivalent phone entity field) instead of raw text fields.
+- Hours: bind to `hours` with `constantValueEnabled: false`.
+
+Avoid in location-stream defaults:
+
+- hardcoded city/address strings with empty `field`
+- hardcoded map/directions URLs tied to one address
+- plain `text` field controls for location primitives (`mapUrl`, `directionsUrl`, `phoneNumber`) unless the user explicitly requests static-only behavior
+
+## Image Asset Pattern
+
+Use image selector for visual media blocks:
+
+```ts
+image: YextField(
+ "Image",
+ {
+ type: "entityField",
+ filter: { types: ["type.image"] },
+ },
+);
+```
+
+Default value should include URL and dimensions to avoid empty render states.
+
+For background imagery, prefer shared hero/promo shape:
+
+```ts
+backgroundImage: YextField<
+ any,
+ ImageType | AssetImageType | TranslatableAssetImage
+>("Image", {
+ type: "entityField",
+ filter: { types: ["type.image"] },
+});
+```
+
+Avoid `backgroundImageUrl` text fields for primary background image control.
+
+Use shared render-time resolution patterns (`resolveYextEntityField`, `getImageUrl`) for localized image values.
+
+For image slots, mirror `ImageWrapper` conventions:
+
+- `data.image` entity field with `type.image`
+- style controls like width/aspect ratio
+- empty-state-safe render behavior for missing image data
+
+## Map Pattern
+
+For location templates, align map behavior with shared VE map patterns:
+
+- Preferred: `MapboxStaticMapComponent` + coordinate entity field (`field: "yextDisplayCoordinate"`).
+- Acceptable fallback: `getDirections(...)` links derived from address/entity data.
+
+Avoid:
+
+- hardcoded map provider URLs in defaults
+- raw `iframe` embeds as the primary map behavior
+
+## Resolve Pattern in Render
+
+Resolve field data through locale + stream document:
+
+```ts
+const streamDocument = useDocument();
+const { i18n } = useTranslation();
+
+const headline = resolveComponentData(
+ data.headline,
+ i18n.language,
+ streamDocument,
+);
+```
+
+Wrap resolved field output in `EntityField` where applicable.
+
+## Section Runtime Pattern
+
+Use `VisibilityWrapper` and `ComponentErrorBoundary` for top-level section renders when applicable.
+
+Prefer theme-backed selectors over raw text controls for color choices.
+
+## Array Robustness Pattern
+
+For every array field, include `defaultItemProps`:
+
+```ts
+items: YextField("Items", {
+ type: "array",
+ arrayFields: {
+ question: YextField("Question", {
+ type: "entityField",
+ filter: { types: ["type.string"] },
+ }),
+ },
+ defaultItemProps: {
+ question: {
+ field: "",
+ constantValue: { en: "Question", hasLocalizedValue: "true" },
+ constantValueEnabled: true,
+ },
+ },
+});
+```
+
+Keep the same localized-object shape when overriding translatable fields through spread defaults:
+
+```ts
+const override = {
+ ...base,
+ data: {
+ ...base.data,
+ heading: {
+ field: "",
+ constantValue: { en: "Reserve Now", hasLocalizedValue: "true" },
+ constantValueEnabled: true,
+ },
+ },
+};
+```
+
+In render code, guard array entries before reading nested values to avoid crashes from malformed editor rows.
+
+Safe field-id pattern for list rows:
+
+```tsx
+const questionField = item?.question;
+
+ {resolveComponentData(
+ questionField ?? {
+ field: "",
+ constantValue: { en: "Question", hasLocalizedValue: "true" },
+ constantValueEnabled: true,
+ },
+ locale,
+ streamDocument,
+ )}
+;
+```
+
+## Common Style Controls
+
+Include simple, reusable style controls by default:
+
+- Text alignment (`left`, `center`, `right`)
+- Color token selector (`SITE_COLOR` or `BACKGROUND_COLOR`)
+- Heading level (if section has heading)
+- Section spacing/padding controls
+
+Keep style props shallow and avoid nested style systems for first-pass generated sections.
+
+If a section exposes `styles.textColor`, slot components rendered inside it should not hardcode conflicting color classes (for example `text-white`). Let color inherit or use slot style props wired to editor fields.
diff --git a/skills/ve-html-template-generator/scripts/extract_page_sections.py b/skills/ve-html-template-generator/scripts/extract_page_sections.py
new file mode 100755
index 000000000..b957dda60
--- /dev/null
+++ b/skills/ve-html-template-generator/scripts/extract_page_sections.py
@@ -0,0 +1,202 @@
+#!/usr/bin/env python3
+"""Extract candidate page sections from an HTML file.
+
+This is a best-effort helper for planning generated template components.
+It favors semantic tags (, , , ,