Read this page in five minutes. It tells you what we build, what you must decide, and what a human must do by hand.
We replace the fixed header menu with a menu that editors manage in WordPress. Stage 1 already ships the agreed design as fixed HTML behind a switch. Stage 2 renders the same HTML from one WordPress menu, item by item. A checker compares both outputs link by link, so Google sees no change. When the missing pages ship, editors reveal the full design by un-hiding menu items.
Static header and phone sheet in the theme, behind the ctw_nav_v2 switch. Live links only. Not part of this PRD.
The same design, rendered from one editable menu. Same links, same order, same landmarks as stage 1.
Featured cards and the refer-and-earn plaque return when their pages exist. Editors un-hide items. No new code unless the design changes.
Hover and default link states. The link-state specification (nav-link-states.md) fixes colours and hover behaviour for every width. You asked for the same behaviour at every width, so the build checks it with a script, not by eye.
Twenty-one stories. One commit each. otto runs them; a person does NAV-001 and the release steps. NAV-020 runs after the production switch, in a second conversion.
| ID | Title | Who | Depends on | Done when |
|---|---|---|---|---|
| NAV-001 | Human prerequisites: record the input-limit fix, Safe SVG plugin, Convert pause, hub row id | HUMAN | none | Site Health shows 3000 input variables on both servers; the other items have screenshots. |
| NAV-002 | Admin prerequisites and the test runner | CODEX | none | The Menus screen shows custom fields and the Description box; the lint harness passes on PHP 7.2 and 8.3. |
| NAV-003 | Tree builder, roles, badges, mobile order, test scaffold | CODEX | 002 | The tree test passes on three PHP versions with zero deprecations. |
| NAV-004 | Link, tile, card and plaque markup; icon resolution; SVG sanitiser | CODEX | 003 | Every repo icon renders like the bundle; hostile SVG input comes back clean. |
| NAV-005 | Per-item fields on the Menus screen with a guarded save | OPUS | 003 | A saved item shows the right meta rows; an AJAX-added item shows none. |
| NAV-006 | Media picker for icons and card images | OPUS | 005, 010, 017 | Swapping an icon in wp-admin changes the rendered tile; clearing it removes the tile. |
| NAV-007 | Desktop panel markup (1200 px and wider) | CODEX | 004, 014 | The parity checker reports zero differences against the stage-1 bundle. |
| NAV-008 | Medium panel markup (600 to 1199 px) | CODEX | 004, 014 | Each tab's medium block equals the bundle's after normalisation. |
| NAV-009 | Phone sheet markup with phone-only ordering | CODEX | 004, 014 | The sheet equals the bundle; a phone position moves one group without changing desktop. |
| NAV-010 | Render integration on stage 1: fragment filter, composition, test parameters | CODEX | 007, 008, 009, 014, 016 | Switch off: header byte-identical to before. Switch on: the page passes the parity checker. |
| NAV-011 | Cache: build once, flush on every change, previews never cached, link parity asserted | CODEX | 010 | Flush then load gives miss then hit; a page or image change empties the cache. |
| NAV-012 | Section map: nav targets feed the active-tab body class | CODEX | 011 | Thirteen test URLs get the expected body class; the phone sheet opens on the matching tab. |
| NAV-013 | Cloudflare purge on every menu change | CODEX | 011 | One purge per request in the unit test; the production probe is a release step. |
| NAV-014 | Harness and baselines: fixtures, parity checker copy, states check, visual and Lighthouse tools | CODEX | 002 | Baselines exist for stage 1 and the frozen archive; the checker self-tests pass. |
| NAV-015 | Verification of the rendered menu: screenshots, link states, behaviour, Lighthouse | OPUS | 011, 012, 014, 017 | Every screenshot pair differs by 1 % or less; 32 of 32 state probes pass. |
| NAV-016 | Seed the menu tree from a Tools page | CODEX | 003, 014 | 73 items, 21 hidden; a second run adds nothing; the seed matches the bundle's content. |
| NAV-017 | Icon import and the launch / full-design presets | CODEX | 016, 004 | 15 icons uploaded once and assigned; the preset un-hides only pages that answer 200. |
| NAV-018 | Second critical-CSS set for the new header | CODEX | 015, 014 | Seven new files with no old menu rules; first paint is styled with the switch on and off. |
| NAV-019 | Launch gate: URL probe, parity, visual, Lighthouse, staging PHP 8.3 log | CODEX | 006, 013, 015, 017, 018, 021 | Local and staging report FAIL 0; stage 1 versus stage 2 reports zero differences. |
| NAV-020 | Post-switch cleanup: remove the old menu code, export and retire old menus | LATER | 019 | No old menu rules remain; the export is verified before any deletion. |
| NAV-021 | Full-design stylesheet (cards, plaque, footer strip) | OPUS | 010, 014, 017 | The full-design preset matches the frozen archive within 1 %; the launch view does not change. |
industries in the page builder. Recommendation: add the row id before launch, then restore the fragment link./product and to PayPlexo. The new menu does not. Nothing else links to them from every page.max_input_vars is 3000 on production and staging through a .user.ini file in each WordPress root. Verified on 24 September. Revert: delete the file./home/phil/bin/wp. Run it with --path=/var/www/www --skip-themes --skip-plugins, because the theme fails under the command line. Staging has no WP-CLI; the Tools page seeds every environment.main and freeze its bundle. Stage 2 starts from that commit.industries, if you choose that in decision 3./home/phil/bin/wp --path=/var/www/www --skip-themes --skip-plugins db export.?ctw_nav=2 as an administrator.wp option update ctw_nav_v2 1. Purge Cloudflare. Run the URL probe.| Risk | Mitigation |
|---|---|
| The rendered menu drifts from the stage-1 bundle, so Google sees different links. | Every markup story must pass the parity checker against the bundle, link by link and in order. |
| Editors' changes stay invisible for hours because Cloudflare caches whole pages. | Every menu change flushes the cache and sends one Cloudflare purge; a production probe proves it. |
| Code that runs on PHP 7.4 locally fails on PHP 7.2 in production or warns on PHP 8.3 on staging. | Every PHP story runs its tests inside PHP 7.2 and PHP 8.3 containers with exit codes checked. |
| A link to a page that is private, missing or redirecting ships in the menu. | Unpublished targets never render, corridor links have no trailing slash, and the launch gate probes every URL. |
| The first paint flashes an unstyled header on the seven busiest templates. | A second critical-CSS set is generated for the new header and selected only when the switch is on. |
Six reviews ran: four rounds of the otto checker (gpt-6-astra), the SEO crawlability audit, and one independent gpt-6-astra review. The table shows what changed.
| Pass | Found | Changed |
|---|---|---|
| otto round 1 | 1 blocker, 35 major. Cited screenshots did not exist; tooling sat outside the git repository; saves could damage the old menu; dependencies were missing. | Real frames cited; tooling moved into the theme repo; Dante's save handler unhooked; dependencies fixed; stories split where two concerns shared one file. |
| SEO audit | Stage 2 "pass with fixes". Button tabs would break parity with stage 1; hidden items must be absent from the HTML; group titles must not be heading tags. | Tabs stay links; hidden items are omitted; titles use role="heading"; glyphs are hidden from screen readers; the checker script is the acceptance test. |
| otto round 2 | 2 blockers, 37 major. The stage-1 code had landed in the theme; the PRD duplicated it. | The PRD was rebased on stage 1: it reuses the switch, the header override, the assets and the section class, and adds one filter line. |
| otto round 3 | 1 blocker, 32 major. Line numbers had shifted; the bundle's stylesheet lacks the full-design rules; the bundle's icons and the media files differ in size units. | Symbol anchors; new story NAV-021 for the full-design styles; the normaliser ignores icon internals and screenshots prove the look. |
| otto round 4 | 1 blocker, 31 major. The stage-1 stylesheet underlines only the open tab, so a "current section underline" was not real; the local base URL redirects; the bundle shows shorter overview copy on phones. | The section class now preselects the phone tab only; one base URL for every check; a "Subline on phones" field; cache writes guarded against stale builds; unassign, do not delete, the old menus. |
| Independent gpt-6-astra review (round 5) | Running at the time of writing. | See the PRD's review log, section 9. |
Declined, four rounds running: further splitting of seven stories. The task sizer splits stories at conversion time, and each story is one file or one tool set. The finding count stopped falling after round 2, which is the sign to stop iterating and convert.
Prepared 24 September 2026 from docs/nav-audit-2026-09-22/prd-nav-2026.md. Unlisted page: do not index.