diff --git a/docs/superpowers/plans/2026-07-24-services-content-blocks.md b/docs/superpowers/plans/2026-07-24-services-content-blocks.md
new file mode 100644
index 0000000..a69119c
--- /dev/null
+++ b/docs/superpowers/plans/2026-07-24-services-content-blocks.md
@@ -0,0 +1,614 @@
+# Services Content Blocks Implementation Plan
+
+> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
+
+**Goal:** Build the missing content-area components for the Mapping services page (and any other service sub-page), matching `Mapping-Services.png`. The hero, header, and footer are already in place and must not be touched.
+
+**Architecture:** One new ACF block (`services-list`) that renders a row of cards. Card count is editor-driven (2, 3, or 4); card size and grid layout adapt to the count. The orange horizontal line that bisects the images is rendered by the block itself as a full-bleed SVG background. The closing "At CWC, mapping is about more than data…" block reuses the existing `pull-quote` block — no new build needed. Editor composes the page in Gutenberg by adding `Services List` and `Pull Quote` blocks to any service sub-page; `page.php` already calls `the_content()`, so no template change is required.
+
+**Tech Stack:** WordPress 6.x, PHP 8.x, Tailwind CSS v4, Playwright, axe-core, PHPCS (WordPress standard).
+
+## Global Constraints
+
+- Tabs for PHP indentation (project standard).
+- `static/dist/theme.css` is committed (not gitignored) per project convention.
+- PHPCS uses the WordPress coding standard; `composer lint` must pass for all touched files.
+- All design colors use the project's existing CSS custom properties from `styles/base/colors.css`. No hardcoded hex values for theme colors.
+- Reuse the existing post-title accent pattern (blue heading + orange `::after` underline from `.post-title h1` in `styles/base/misc.css`) for consistency. Do not introduce a new accent style.
+- Reuse the existing `pull-quote` block for the closing statement — no duplicate block.
+- Page template `page.php` is not modified. The page-children block is not used.
+- This work does **not** edit `header.php`, `footer.php`, `views/partials/page-hero.php`, or `views/partials/page-hero-services.php`.
+- Tailwind v4 picks up class names from source files at build time. The dynamic `lg:grid-cols-N` class needs to literally appear in the source PHP for Tailwind to emit the rule.
+- The `static/img/services-list-line.svg` is a fixed asset; do not make it editor-uploadable.
+- All commits follow the project's emoji-prefixed commit-message style (e.g. `✨ feat: …`).
+
+## File Structure
+
+| File | Responsibility | Created/Modified |
+| --- | --- | --- |
+| `views/blocks/services-list/services-list.php` | Block template (PHP) — renders the row of cards. | Create |
+| `views/blocks/services-list/services-list.css` | Block styles (CSS) — layout, headings, bisecting line. | Create |
+| `views/blocks/services-list/block.json` | Block registration metadata (name, category, icon, render template). | Create |
+| `acf/group_services_list.json` | ACF field group for the `acf/services-list` block. | Create |
+| `static/img/services-list-line.svg` | Decorative orange horizontal line SVG that bisects the images. | Create |
+| `styles/blocks/index.css` | Import the new block's CSS. | Modify |
+| `tests/services-page.spec.js` | Playwright tests for the rendered block. | Create |
+| `static/dist/theme.css` | Rebuilt via `npm run build`. | Modified |
+
+---
+
+## Task 1: ACF Field Group
+
+**Files:**
+- Create: `acf/group_services_list.json`
+
+**Interfaces:**
+- Produces: ACF group with `key: "group_services_list"`, location scoped to `block == acf/services-list`, fields: `items` (repeater) → `image`, `heading`, `description`.
+
+- [ ] **Step 1: Create the ACF field group JSON file**
+
+Create the file `acf/group_services_list.json` with the following exact content:
+
+```json
+{
+ "key": "group_services_list",
+ "title": "Services List",
+ "fields": [
+ {
+ "key": "field_services_items",
+ "label": "Items",
+ "name": "items",
+ "aria-label": "",
+ "type": "repeater",
+ "instructions": "Add 2, 3, or 4 cards. The grid layout adapts to the count.",
+ "required": 0,
+ "conditional_logic": 0,
+ "wrapper": {
+ "width": "",
+ "class": "",
+ "id": ""
+ },
+ "layout": "block",
+ "pagination": 0,
+ "min": 0,
+ "max": 0,
+ "collapsed": "",
+ "button_label": "Add Service",
+ "rows_per_page": 20,
+ "sub_fields": [
+ {
+ "key": "field_services_item_image",
+ "label": "Image",
+ "name": "image",
+ "aria-label": "",
+ "type": "image",
+ "instructions": "",
+ "required": 0,
+ "conditional_logic": 0,
+ "wrapper": {
+ "width": "",
+ "class": "",
+ "id": ""
+ },
+ "return_format": "array",
+ "library": "all",
+ "min_width": "",
+ "min_height": "",
+ "min_size": "",
+ "max_width": "",
+ "max_height": "",
+ "max_size": "",
+ "mime_types": "",
+ "allow_in_bindings": 0,
+ "preview_size": "medium"
+ },
+ {
+ "key": "field_services_item_heading",
+ "label": "Heading",
+ "name": "heading",
+ "aria-label": "",
+ "type": "text",
+ "instructions": "",
+ "required": 0,
+ "conditional_logic": 0,
+ "wrapper": {
+ "width": "",
+ "class": "",
+ "id": ""
+ },
+ "default_value": "",
+ "maxlength": "",
+ "allow_in_bindings": 0,
+ "placeholder": "",
+ "prepend": "",
+ "append": ""
+ },
+ {
+ "key": "field_services_item_description",
+ "label": "Description",
+ "name": "description",
+ "aria-label": "",
+ "type": "textarea",
+ "instructions": "",
+ "required": 0,
+ "conditional_logic": 0,
+ "wrapper": {
+ "width": "",
+ "class": "",
+ "id": ""
+ },
+ "default_value": "",
+ "placeholder": "",
+ "maxlength": "",
+ "rows": 4,
+ "new_lines": ""
+ }
+ ]
+ }
+ ],
+ "location": [
+ [
+ {
+ "param": "block",
+ "operator": "==",
+ "value": "acf/services-list"
+ }
+ ]
+ ],
+ "menu_order": 0,
+ "position": "normal",
+ "style": "default",
+ "label_placement": "top",
+ "instruction_placement": "label",
+ "hide_on_screen": "",
+ "active": true,
+ "description": "Service cards row. Card count (2-4) determines the layout.",
+ "show_in_rest": 0,
+ "display_title": "",
+ "allow_ai_access": false,
+ "ai_description": "",
+ "modified": 1753305600
+}
+```
+
+- [ ] **Step 2: Verify the JSON parses**
+
+Run: `node -e "JSON.parse(require('fs').readFileSync('acf/group_services_list.json','utf8')); console.log('OK')"`
+Expected: prints `OK` and exits 0.
+
+- [ ] **Step 3: Commit**
+
+```bash
+cd "C:/Users/ksolo/Herd/community-works-collaborative/wp-content/themes/community-works-collaborative"
+git add acf/group_services_list.json
+git commit -m "✨ feat: Add ACF field group for services-list block"
+```
+
+---
+
+## Task 2: Decorative SVG Asset
+
+**Files:**
+- Create: `static/img/services-list-line.svg`
+
+**Interfaces:**
+- Produces: A 1600×8 viewBox SVG with a 2px-tall orange `#F26B53` rectangle, `preserveAspectRatio="none"`. Reference URL: `/wp-content/themes/community-works-collaborative/static/img/services-list-line.svg`.
+
+- [ ] **Step 1: Create the SVG asset**
+
+Create the file `static/img/services-list-line.svg` with the following exact content:
+
+```svg
+
+```
+
+- [ ] **Step 2: Verify the SVG is well-formed**
+
+Run: `node -e "const fs=require('fs'); const s=fs.readFileSync('static/img/services-list-line.svg','utf8'); if(!s.includes('viewBox=\"0 0 1600 8\"')||!s.includes('fill=\"#F26B53\"')) { process.exit(1); } console.log('OK')"`
+Expected: prints `OK` and exits 0.
+
+- [ ] **Step 3: Commit**
+
+```bash
+cd "C:/Users/ksolo/Herd/community-works-collaborative/wp-content/themes/community-works-collaborative"
+git add static/img/services-list-line.svg
+git commit -m "✨ feat: Add services-list decorative line SVG"
+```
+
+---
+
+## Task 3: Block Registration JSON
+
+**Files:**
+- Create: `views/blocks/services-list/block.json`
+
+**Interfaces:**
+- Produces: `block.json` declaring `acf/services-list` as the block name, `services-list.php` as the render template, `grid-view` icon, `common` category. `regACFBlocks()` in `functions.php` will pick this up automatically — no PHP registration code change required.
+
+- [ ] **Step 1: Create the block.json file**
+
+Create the file `views/blocks/services-list/block.json` with the following exact content:
+
+```json
+{
+ "name": "acf/services-list",
+ "title": "Services List",
+ "description": "A row of service cards. Card count (2, 3, or 4) determines the layout.",
+ "category": "common",
+ "icon": "grid-view",
+ "keywords": ["services", "cards", "mapping"],
+ "acf": {
+ "mode": "preview",
+ "renderTemplate": "services-list.php"
+ },
+ "supports": {
+ "anchor": true,
+ "align": false
+ }
+}
+```
+
+- [ ] **Step 2: Verify the JSON parses**
+
+Run: `node -e "JSON.parse(require('fs').readFileSync('views/blocks/services-list/block.json','utf8')); console.log('OK')"`
+Expected: prints `OK` and exits 0.
+
+- [ ] **Step 3: Commit**
+
+```bash
+cd "C:/Users/ksolo/Herd/community-works-collaborative/wp-content/themes/community-works-collaborative"
+git add views/blocks/services-list/block.json
+git commit -m "✨ feat: Add services-list block.json registration"
+```
+
+---
+
+## Task 4: Block CSS
+
+**Files:**
+- Create: `views/blocks/services-list/services-list.css`
+
+**Interfaces:**
+- Produces: BEM-styled CSS for `.services-list`, `__line`, `__grid`, `__card`, `__media`, `__heading`, `__desc`. Reuses `text-cwc-blue-01` and `var(--color-secondary)` from existing tokens. Renders the bisecting line via `background-image: url('/wp-content/themes/community-works-collaborative/static/img/services-list-line.svg')`.
+
+- [ ] **Step 1: Create the block CSS file**
+
+Create the file `views/blocks/services-list/services-list.css` with the following exact content:
+
+```css
+/* Services List block */
+
+.services-list {
+ --services-line-top: 25%;
+
+ &__line {
+ position: absolute;
+ inset-inline: 0;
+ top: var(--services-line-top);
+ height: 8px;
+ pointer-events: none;
+ z-index: 1;
+ background-image: url('/wp-content/themes/community-works-collaborative/static/img/services-list-line.svg');
+ background-repeat: no-repeat;
+ background-position: center center;
+ background-size: 100% 100%;
+ }
+
+ &__grid {
+ position: relative;
+ z-index: 2;
+ }
+
+ &__card {
+ @apply flex flex-col;
+ }
+
+ &__media {
+ @apply overflow-hidden rounded-md bg-gray-100;
+ }
+
+ &__img {
+ @apply w-full h-full object-cover;
+ }
+
+ &__heading {
+ @apply text-cwc-blue-01 text-25px font-bold leading-none mt-6 mb-4;
+
+ &::after {
+ background: var(--color-secondary);
+ content: "";
+ display: block;
+ height: 4px;
+ margin-top: 0.45rem;
+ width: 3rem;
+ }
+ }
+
+ &__desc {
+ @apply text-gray-600 text-16px font-light leading-snug;
+ }
+}
+
+@media (max-width: 1023px) {
+ .services-list__line {
+ display: none;
+ }
+}
+```
+
+- [ ] **Step 2: Verify the CSS contains the expected classes**
+
+The brief uses CSS-nesting shorthand (`&__line`, `&__grid`, etc.) for 6 of the 7 BEM children, and a literal selector (`.services-list__line`) for the one inside the `@media` block. A naive grep for the literal class names will undercount. Use:
+
+```bash
+grep -cE "&__(line|grid|card|media|img|heading|desc)|services-list__(line|grid|card|media|img|heading|desc)" views/blocks/services-list/services-list.css
+```
+
+Expected: `8` (7 nested `&__` selectors + 1 literal `.services-list__line` inside the `@media` block).
+
+- [ ] **Step 3: Commit**
+
+```bash
+cd "C:/Users/ksolo/Herd/community-works-collaborative/wp-content/themes/community-works-collaborative"
+git add views/blocks/services-list/services-list.css
+git commit -m "✨ feat: Add services-list block CSS"
+```
+
+---
+
+## Task 5: Block Template (PHP)
+
+**Files:**
+- Create: `views/blocks/services-list/services-list.php`
+
+**Interfaces:**
+- Consumes: `get_field( 'items' )` → array of items each with `image` (array: `url`, `alt`), `heading` (string), `description` (string).
+- Produces: A `` element containing the bisecting `__line` div, a `container` with a grid div, and a `` per item. Computes the dynamic `lg:grid-cols-N` class from item count.
+
+- [ ] **Step 1: Create the block PHP template**
+
+Create the file `views/blocks/services-list/services-list.php` with the following exact content (tabs for indentation):
+
+```php
+
+
+>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+- [ ] **Step 2: Lint the PHP file with PHPCS**
+
+Run: `composer exec phpcs -- views/blocks/services-list/services-list.php`
+Expected: exits 0 with no errors. If there are pre-existing project-wide issues unrelated to this file, fix only the ones in this file.
+
+- [ ] **Step 3: Verify the dynamic grid class is in the source**
+
+Run: `grep -E "lg:grid-cols-\$grid_cols|lg:grid-cols-' \. \$grid_cols" views/blocks/services-list/services-list.php`
+Expected: at least one match (Tailwind must see the literal class name in the source to emit the rule).
+
+- [ ] **Step 4: Commit**
+
+```bash
+cd "C:/Users/ksolo/Herd/community-works-collaborative/wp-content/themes/community-works-collaborative"
+git add views/blocks/services-list/services-list.php
+git commit -m "✨ feat: Add services-list block PHP template"
+```
+
+---
+
+## Task 6: CSS Import Wiring
+
+**Files:**
+- Modify: `styles/blocks/index.css`
+
+**Interfaces:**
+- Consumes: `views/blocks/services-list/services-list.css` (from Task 4).
+- Produces: A new `@import` line at the end of `styles/blocks/index.css` so Tailwind's build picks up the block's CSS.
+
+- [ ] **Step 1: Add the import to the block index CSS**
+
+Append the following line at the end of `styles/blocks/index.css`:
+
+```css
+@import '../../views/blocks/services-list/services-list.css';
+```
+
+After the edit, the file should end with:
+
+```
+@import '../../views/blocks/contact-info/contact-info.css';
+@import '../../views/blocks/services-list/services-list.css';
+```
+
+- [ ] **Step 2: Verify the import is present**
+
+Run: `grep -c "services-list/services-list.css" styles/blocks/index.css`
+Expected: `1`.
+
+- [ ] **Step 3: Commit**
+
+```bash
+cd "C:/Users/ksolo/Herd/community-works-collaborative/wp-content/themes/community-works-collaborative"
+git add styles/blocks/index.css
+git commit -m "🔧 chore: Import services-list block CSS into theme"
+```
+
+---
+
+## Task 7: Build the CSS
+
+**Files:**
+- Modify: `static/dist/theme.css`
+
+**Interfaces:**
+- Consumes: The new import in `styles/blocks/index.css` (from Task 6) and the block CSS (from Task 4).
+- Produces: A rebuilt `static/dist/theme.css` that includes the `.services-list*` rules and the dynamic `lg:grid-cols-2`, `lg:grid-cols-3`, `lg:grid-cols-4` utilities.
+
+- [ ] **Step 1: Run the production Tailwind build**
+
+Run: `npm run build`
+Expected: completes with no errors. The output file `static/dist/theme.css` is updated in place.
+
+- [ ] **Step 2: Verify the services-list CSS classes are in the built output**
+
+Run: `grep -c "services-list__heading" static/dist/theme.css`
+Expected: `1` or more (the class should appear in the built CSS).
+
+- [ ] **Step 3: Verify the dynamic grid classes are in the built output**
+
+Run: `grep -E "lg\\\\:grid-cols-(2|3|4) " static/dist/theme.css | wc -l`
+Expected: `3` (all three column counts should be present).
+
+- [ ] **Step 4: Commit the rebuilt dist**
+
+```bash
+cd "C:/Users/ksolo/Herd/community-works-collaborative/wp-content/themes/community-works-collaborative"
+git add -f static/dist/theme.css
+git commit -m "🎨 build: Rebuild dist with services-list block styles"
+```
+
+---
+
+## Task 8: Playwright Tests
+
+**Files:**
+- Create: `tests/services-page.spec.js`
+
+**Interfaces:**
+- Consumes: The rendered `views/blocks/services-list/services-list.php` template (Tasks 4 + 5) on the WordPress page `/services/mapping/`.
+- Produces: Three tests:
+ 1. Card count, image, and heading visibility per block (expects 2 + 4 cards across two block instances).
+ 2. Bisecting `__line` is present and `aria-hidden="true"`.
+ 3. No axe-core a11y violations scoped to `.services-list`.
+
+> **Prerequisite:** the test depends on the mapping page being composed in Gutenberg with two `Services List` blocks (2 items + 4 items) and a `Pull Quote` block. That composition is the editor's responsibility and is not part of this plan. The tests should be authored against the *intended* state, not a placeholder state.
+
+- [ ] **Step 1: Create the test spec file**
+
+Create the file `tests/services-page.spec.js` with the following exact content:
+
+```js
+import { test, expect } from "@playwright/test";
+import AxeBuilder from "@axe-core/playwright";
+
+test.describe("Services List block on /services/mapping/", () => {
+ test("renders the heading and image for each card", async ({ page }) => {
+ await page.goto("/services/mapping/");
+
+ const blocks = page.locator(".services-list");
+ await expect(blocks).toHaveCount(2);
+
+ await expect(blocks.nth(0).locator(".services-list__card")).toHaveCount(2);
+ await expect(blocks.nth(1).locator(".services-list__card")).toHaveCount(4);
+
+ const firstCard = blocks.nth(0).locator(".services-list__card").first();
+ await expect(firstCard.locator(".services-list__media img")).toBeVisible();
+ await expect(firstCard.locator(".services-list__heading")).toBeVisible();
+ });
+
+ test("bisecting line is present and aria-hidden", async ({ page }) => {
+ await page.goto("/services/mapping/");
+ const line = page.locator(".services-list__line").first();
+ await expect(line).toBeAttached();
+ await expect(line).toHaveAttribute("aria-hidden", "true");
+ });
+
+ test("no axe violations on the services-list block", async ({ page }) => {
+ await page.goto("/services/mapping/");
+ const results = await new AxeBuilder({ page })
+ .include(".services-list")
+ .analyze();
+ expect(results.violations).toEqual([]);
+ });
+});
+```
+
+- [ ] **Step 2: Run the tests**
+
+Run: `npx playwright test tests/services-page.spec.js`
+Expected: all 3 tests pass. If tests fail because the mapping page has not yet been composed in the editor, that's an expected prerequisite — note the prerequisite in the final report and skip the test run.
+
+- [ ] **Step 3: Commit**
+
+```bash
+cd "C:/Users/ksolo/Herd/community-works-collaborative/wp-content/themes/community-works-collaborative"
+git add tests/services-page.spec.js
+git commit -m "✅ test: Add Playwright tests for services-list block"
+```
+
+---
+
+## Self-Review Notes
+
+- **Spec coverage:**
+ - One new ACF block (`services-list`) — covered by Tasks 1, 3, 4, 5.
+ - Repeater of `items` with `image` / `heading` / `description` — covered by Task 1.
+ - Card count drives grid columns (2/3/4) — covered by Task 5 (PHP computes the class) and Task 7 (Tailwind emits the rule).
+ - Orange bisecting line, full-bleed, behind the cards, hidden on mobile — covered by Tasks 2 (SVG), 4 (CSS with `inset-inline: 0`, `z-index: 1` on line, `z-index: 2` on grid, media query to hide on `