diff --git a/skills/handlebars-v4-migration/SKILL.md b/skills/handlebars-v4-migration/SKILL.md
new file mode 100644
index 00000000..f6353d8b
--- /dev/null
+++ b/skills/handlebars-v4-migration/SKILL.md
@@ -0,0 +1,259 @@
+---
+name: Handlebars v3 to v4 Migration
+description: >-
+ Use this skill when migrating a BigCommerce Stencil theme from Handlebars v3 to v4,
+ upgrading the template engine, fixing context depth (../) issues in templates.
+ Also triggers when the user asks about "handlebars upgrade",
+ "template engine v4", "stencil handlebars migration", "../ not resolving correctly",
+ "handlebars_v4 config.json", or "handlebars breaking changes".
+ Also triggers when updating a CHANGELOG after a Handlebars migration.
+ Always use this skill when the user mentions upgrading or migrating their theme's template engine.
+---
+
+# Handlebars v3 to v4 Migration
+
+This skill guides you through migrating a BigCommerce Stencil theme from Handlebars v3 to v4.
+There are four required steps — work through them in order.
+
+---
+
+## Step 1: Update config.json
+
+Every Stencil theme has a `config.json` at the repo root. Find the `"version"` key and add the
+engine flag immediately after it:
+
+```json
+{
+ "version": "...",
+ "template_engine": "handlebars_v4",
+ ...
+}
+```
+
+To revert, remove that line or set it to `"handlebars_v3"`.
+
+---
+
+## Step 2: Fix context depth (`../`) in templates
+
+### What changed
+
+**General rule:** in v4, a block helper creates a context frame **only if it actually changes the context**. In v3 this was inconsistent — conditional helpers also created frames even though they don't change context, so templates used extra `../` to compensate. In v4 that was fixed: conditional helpers (`{{#if}}`, `{{#unless}}`, and platform-level helpers like `{{#or}}`, `{{#and}}`, `{{#compare}}`, `{{#inArray}}`, `{{#any}}`) no longer create frames. This means existing `../` inside conditional blocks may now point one level too high.
+
+| Helper | v3 | v4 |
+| ------------------------------------------------------------------------------------------ | ------------- | ------------------------------------ |
+| `{{#if}}` / `{{#unless}}` | created frame | **no frame** — `../` must be removed |
+| Built-in or platform **conditional** helpers (`{{#or}}`, `{{#and}}`, `{{#compare}}`, etc.) | created frame | **no frame** — same rule as `#if` |
+| `{{#each}}` / `{{#with}}` | creates frame | creates frame — no change needed |
+| Platform **iterator** helpers (`{{#for}}`, `{{#enumerate}}`) | creates frame | creates frame — no change needed |
+
+### Find all affected templates
+
+Search the theme for every `../` occurrence:
+
+```bash
+grep -rn "\.\.\/" templates/ --include="*.html"
+grep -rn "@\.\.\/" templates/ --include="*.html"
+```
+
+The second grep finds `@../` — Handlebars data variables from a parent context (e.g. `@../index`, `@../key`, `@../first`, `@../last`). These are special variables set automatically by `{{#each}}`. They follow the same rule as `../`: if `@../index` appears inside a conditional block (`{{#if}}`, `{{#unless}}`, etc.) that is nested inside `{{#each}}`, remove the `../` → `@index`. If it appears directly in the `{{#each}}` body with no conditional wrapper, leave it unchanged.
+
+Review **every hit** in context of the helper it sits inside — some will need fixes, some won't.
+
+### Pattern — `../` inside conditional block helpers (remove the `../`)
+
+Because conditional block helpers (`#if`, `#unless`, `#or`, `#and`, etc.) no longer create a frame, `../` is now one level too high.
+
+**Before (v3)**
+
+```handlebars
+{{#each items}}
+ {{#if isActive}}
+ {{../title}}
+ {{/if}}
+{{/each}}
+```
+
+**After (v4)**
+
+```handlebars
+{{#each items}}
+ {{#if isActive}}
+ {{title}}
+ {{/if}}
+{{/each}}
+```
+
+---
+
+> _The required fix for Step 2 is complete. Optionally, continue below to refactor the same locations using block params._
+
+### Optional: Refactor to block params
+
+> **Sources:**
+>
+> - Handlebars v4.0.0 release notes — context depth change: [handlebars-lang/handlebars.js — release-notes.md](https://github.com/handlebars-lang/handlebars.js/blob/master/release-notes.md)
+> - Block params syntax reference: [handlebarsjs.com — Block Helpers: Block Parameters](https://handlebarsjs.com/guide/block-helpers.html#block-parameters)
+
+After removing `../`, the same locations can be further refactored to name the loop item explicitly with block params. This is optional — removing `../` is fully correct on its own. Block params make the intent clearer when a loop body is complex.
+
+**This applies only when** the removed `../` referred to a **property of the loop item itself** (not the parent/root context).
+
+**Before (after mandatory fix):**
+
+```handlebars
+{{#each products}}
+ {{#if isActive}}
+
{{title}}
+ {{/if}}
+{{/each}}
+```
+
+**After (block params refactor):**
+
+```handlebars
+{{#each products as |product|}}
+ {{#if product.isActive}}
+ {{product.title}}
+ {{/if}}
+{{/each}}
+```
+
+**Block params do NOT apply** when the original `../` was a deliberate exit to parent/root context — those were left unchanged in the mandatory fix and remain correct as-is.
+
+#### Identify candidates for block params
+
+Use the same files identified during the mandatory fix. For each location where `../` was removed, apply the two-check decision:
+
+**Check 1 — confirm the removal happened inside a conditional block body.**
+Only locations where `../` appeared inside a conditional helper (`{{#if}}`, `{{#unless}}`, `{{#or}}`, `{{#and}}`, `{{#compare}}`, etc.) that is itself inside a `{{#each}}` are candidates. A `../` removed directly from the `{{#each}}` body with no conditional wrapper is not a candidate.
+
+```handlebars
+{{#each items}}
+ {{foo}} ← was ../foo directly in #each body — skip, not a candidate
+
+ {{#if condition}}
+ {{bar}} ← was ../bar inside conditional block body — candidate, proceed to Check 2
+ {{/if}}
+{{/each}}
+```
+
+**Check 2 — apply the decision rule** to each location identified in Check 1:
+
+| Question | Answer → Action |
+| -------------------------------------------------------------------------- | ------------------------------------- |
+| Did the removed `../prop` refer to a field on the **loop item**? | Yes → apply block params |
+| Did the removed `../prop` refer to a field on the **parent/root context**? | Yes → skip, block params do not apply |
+
+> **Universal rule — ask one question:** > **"Does every element in this array have this property?"**
+>
+> - **Yes** → it belongs to the loop item → block params apply.
+> _(Examples: a product's `title`, an order's `status`, a variant's `price` — any field that varies per item)_
+> - **No** → it lives outside the array, in a wider context → skip.
+> _(Examples: global config, store settings, page-level data, parameters passed to a partial — anything that is the same regardless of which item you are on)_
+>
+> The property name is not a reliable signal. The question is purely structural:
+> open the page's data object, find the array being iterated, and ask whether
+> the property in question lives inside each element or outside the array entirely.
+
+#### Offer block params to the user
+
+After identifying candidates, present them to the user and ask:
+
+> "Found N places where block params could be applied. This makes templates more explicit and avoids relying on context depth counting inside conditional blocks.
+> Official reference: [handlebarsjs.com/guide/block-helpers.html#block-parameters](https://handlebarsjs.com/guide/block-helpers.html#block-parameters)
+>
+> Apply block params to these locations? (yes / no — the mandatory fix already applied is sufficient either way)"
+
+If the user says **yes**, refactor each `{{#each items}}` that has the pattern to `{{#each items as |item|}}` and replace bare `prop` references with `item.prop` inside conditional blocks.
+If the user says **no**, no further changes needed.
+
+---
+
+## Step 3: Regression testing
+
+### Capture baseline snapshots (before the upgrade)
+
+```bash
+curl -L "https://store.example.com/" > baseline-home.html
+curl -L "https://store.example.com/category/widgets/" > baseline-category.html
+curl -L "https://store.example.com/widgets/widget-1/" > baseline-product.html
+curl -L "https://store.example.com/cart" > baseline-cart.html
+```
+
+Repeat for all pages at risk:
+
+- Home page
+- Category page (with pagination)
+- Product page (with options/modifiers)
+- Cart / Checkout
+- Search results
+- Account: login + orders
+- Any custom templates (blog, brand, custom pages)
+
+### Capture candidate snapshots (after the upgrade)
+
+Apply the changes from Steps 1–2, install the candidate theme, then re-run the same curls,
+saving to `candidate-*.html`.
+
+### Diff and triage
+
+```bash
+diff -u baseline-home.html candidate-home.html | less
+```
+
+Some differences are expected and safe to ignore:
+
+- Script timestamps / "moment" variables
+- CDN cache-busting query parameters (`?t=...`)
+- Auto-generated IDs
+- Injected bootstrap content
+
+Focus on differences in rendered text, link/image URLs, and template-driven content — these are the areas most affected by v4 changes.
+
+### Visual regression
+
+HTML diffs alone won't catch layout regressions. Take screenshots of the same page set
+(baseline vs candidate) at desktop and mobile viewport sizes and compare side-by-side.
+Pay particular attention to areas driven by `../` depth resolution: navigation menus, product grids, category trees.
+
+---
+
+## Step 4: Update the changelog (if present)
+
+Check for a changelog file at the repo root:
+
+```bash
+ls CHANGELOG* CHANGES* HISTORY* 2>/dev/null
+```
+
+Common filenames: `CHANGELOG.md`, `CHANGELOG`, `CHANGES.md`, `HISTORY.md`.
+
+If one exists, add an entry describing the migration. Match the existing format:
+
+- **Keep a Changelog** (uses `## [Unreleased]` / `## [x.y.z]` headings): add under `### Changed` in the `[Unreleased]` section.
+- **Date-based**: add a new entry at the top with today's date.
+- **Freeform**: follow the existing style.
+
+Example entry:
+
+```markdown
+### Changed
+
+- Migrated template engine from Handlebars v3 to v4 (`"template_engine": "handlebars_v4"` in `config.json`)
+- Removed unnecessary `../` references inside conditional block helpers
+```
+
+If no changelog file is found, skip this step.
+
+---
+
+## Quick checklist
+
+- [ ] `config.json` updated: `"template_engine": "handlebars_v4"`
+- [ ] All `../` and `@../` occurrences reviewed
+- [ ] Unnecessary `../` inside conditional helpers (`#if`, `#unless`, `#or`, `#and`, `#compare`, `#inArray`, `#any`) removed
+- [ ] Baseline HTML snapshots captured
+- [ ] Candidate HTML snapshots captured and diffed
+- [ ] Visual regression pass completed on key pages
+- [ ] Changelog updated (if a changelog file exists)