...`).
3. **Remove the 15-word excerpt** (`
...`).
4. **Keep the byline** (`
...`).
5. **Tighten the card's details padding** — the current `px-4 py-8` leaves a lot of empty space when there's no excerpt. Change to `px-4 py-6` (or whatever the design lands on after visual review; spec this as `px-4 py-6`).
6. **Empty state** — when no posts are found, the "Nothing here yet..." heading still renders. This is the existing behavior; no change. (The `else` branch is unchanged.)
7. **`customExcerpt()` helper** is now unused in `index.php`. The helper itself (in `lib/extras.php` or `lib/helpers.php`) is left as-is — other templates may use it. Just don't call it from `index.php`.
## post-list.css Wiring
The file is currently empty boilerplate. Add:
```css
/* Blog/post index listing styles */
.post-list__h1 {
color: var(--color-cwc-blue-01);
font-family: var(--font-quincy, 'Quincy', serif);
font-size: var(--h1);
font-weight: 700;
line-height: 1.2;
margin: 0 0 2rem;
text-align: left;
}
```
The `color: var(--color-cwc-blue-01)` matches the contact-info heading and the inner-page hero heading for visual consistency. The other values are the project's existing H1 typography tokens (Quincy serif, fluid `--h1` size, default 700 weight).
**No other changes** to `post-list.css`. The card styling (border, shadow, padding) is currently inline via Tailwind utility classes in `index.php`; the spec leaves that as-is.
## Data Flow
- **Editor flow:** the blog index uses standard WordPress post data. Editors write posts in the WP admin, set a featured image, assign categories, and publish. The blog renders whatever is published.
- **Render flow:** WordPress routes `/blog/`, `/category/...`, `/tag/...`, `/author/...`, and `/?s=...` (search) to `index.php`. The template loops over the matching posts, renders each as a card, and renders pagination at the bottom. The sidebar is rendered when `hasSidebar()` returns true.
- **Per-page variation:** none — the design is fixed.
## Error Handling and Edge Cases
- **No posts found** → the "Nothing here yet..." empty state renders.
- **Pagination** → preserved; renders at the bottom of the post list.
- **Featured image missing** → the existing `picsum.photos` fallback is preserved. (The current code has `get_the_post_thumbnail_url() ? $featImg : 'https://picsum.photos/600/400?random=' . get_the_ID();`. This is a hack — a real fallback would be a project default image. Out of scope for this task.)
- **Page title when there are no posts** → still renders "Blog" (the title is unconditional). This is fine — a user navigating to an empty category should still know what page they're on.
- **Search results** → the "Blog" title is misleading for search. A future task can add a per-view title (`is_search() ? 'Search Results' : 'Blog'`). Out of scope here.
- **Hero** → the `header.php` change from the previous turn (`$showHero = isServicesDescendant() && get_field( 'hero_style' ) === 'default'`) already ensures the blog renders without the inner-page hero. This task doesn't touch `header.php`.
## Out of Scope
- New ACF fields for posts.
- New archive templates (`archive.php`, `category.php`, `tag.php`, `author.php`).
- Single-post view (`single.php`) changes.
- A proper fallback image (the `picsum.photos` URL is a known wart but not a blocker).
- Pagination restyling (the existing `the_posts_pagination()` is fine).
- Changing `front-page.php` (the home page).
- Sidebar widget changes (the existing `sidebar.php` works as-is).
- A per-view page title (category-specific, tag-specific, etc.). The current spec hard-codes "Blog" everywhere.
## Verification
- Add `tests/blog-page.spec.js` covering:
- Desktop (1280px): the blog page renders with the "Blog" H1, the post grid, no inner-page hero, the sidebar (when `hasSidebar()` is true).
- Mobile (402px): the post grid stacks to a single column, no horizontal overflow, the page title is visible.
- 320px: nothing clips.
- Axe scan: no violations.
- No console errors.
- Each post card has: image, title link, byline (author + date). Does NOT have a categories row or an excerpt.
- Run `npm run build` to regenerate `static/dist/`.
- Run the full Playwright suite (`npx playwright test`) including `tests/mobile-homepage.spec.js`, `tests/site-a11y.spec.js`, `tests/inner-page.spec.js`, `tests/contact-page.spec.js`, `tests/services-hero-scope.spec.js`, and the new `tests/blog-page.spec.js`.
- Run `composer lint` to catch PHPCS issues.
- Capture a 1280px full-page screenshot of `/blog/` for visual comparison with `blog-mockup.png`.
## Acceptance Criteria
1. `/blog/` renders a "Blog" page title (Quincy serif, CWC Blue 01, the project's standard H1 style).
2. Each post card on `/blog/` shows: featured image, title, byline (author + date). No categories row. No excerpt.
3. The existing sidebar logic is preserved: when `hasSidebar()` is true, the post grid is in the main column and the sidebar is in the 4th column of a 4-col grid.
4. Category, tag, author, and search views (which all use `index.php`) render with the same design — page title + post grid + sidebar.
5. No inner-page hero is rendered on the blog (this was the previous turn's change; preserved here).
6. No console errors, axe violations, or PHPCS errors.
7. The new blog test passes; existing tests continue to pass with no regressions.