---
type: plan
title: "Content and migration"
status: draft
updated: 2026-09-02
---

# Content and migration

How the existing content of equinoxbusinesslaw.com gets into the rebuilt site on staging
(https://equinoxbusinesslaw.demoing.info), what is migrated as-is, what is rebuilt, who decides
the things we cannot decide, and how we prove the result is complete. Read `brief.md` and
`.logs/planning/scope.md` first; the page-by-page disposition and the redirect map live in
`.logs/planning/site-architecture.md`; phases, gates, and the database snapshot procedure live in
`.logs/planning/build-plan.md`.

Facts here come from the live-site inspection of 2026-09-02 (authoritative for the live site and
the staging state), the accepted proposal (sent 2026-07-30, accepted 2026-08-13) for what was
sold, and the client thread (2026-07-09 to 2026-08-27) for client decisions. Where this plan says
"to be confirmed", it names who confirms; do not guess.

Naming: the lead-capture tool is "the Business Legal Health Assessment (name to be confirmed)"
on first mention and "the assessment" after. States: Washington first, Oregon secondary, Colorado
retained; DC and VA removed (client decision 2026-08-27). Posts: 513 published as of 2026-09-02.
Team: 9 bios (8 live plus Nicole Farahdel).

Server rules are not restated here. Before touching the instance (`wp-content/`), read
`/srv/projects/standards/README.md` (dispatches to `coding.md`, `wordpress.md`, `frontend.md`).
Every WordPress write goes through `srv-gw wp --project equinoxbusinesslaw -- <wp-cli>`; every
database read through `srv-gw db-query --project equinoxbusinesslaw`. The live site is never
written to (proposal: "the live site stays untouched until you approve").

## What migrates and what is rebuilt

| Content | Live state (2026-09-02) | Disposition | Notes |
|---|---|---|---|
| Posts | 513 published (REST `X-WP-Total`). Stored as clean classic HTML, not Beaver Builder, not Gutenberg. | MIGRATE INTACT with categories, tags, publish dates, featured images, and Yoast title/description meta. Slugs unchanged. No pruning, no rewriting, no merging. | Content pruning is AISV's later phase and is not in scope. The single post template must render classic HTML on-brand with no manual formatting (proposal, "Post Structure and Blog Migration"). |
| Pages | 32 URLs in the Yoast page sitemap; every one is a Beaver Builder (`bb-plugin` + UABB) layout stored in builder meta. | REBUILD AS BLOCKS. Nothing is migrated mechanically. The visible text of each live page is copied off the live page as DRAFT input for the rebuilt page. | Page-by-page disposition (rebuild / migrate text / retire with redirect) is in `.logs/planning/site-architecture.md`. The home page is built to the brand story copy, not the live copy (see "Home page copy map"). |
| Team bios | 8 live bios (one at root: `/danica-paddock/`); Mandy Bowen already gone from the sitemap. | REBUILD on one bio template; 9 bios (add Nicole Farahdel, Corporate Paralegal, placeholder headshot and bio until supplied; no stock photo of a person). | Roster with titles in `.logs/planning/site-architecture.md`. Nicole's details: to be confirmed by Alicia Wimmer. |
| Policy and legal pages | 5 in the sitemap: `/terms-of-service/`, `/privacy-policy/`, `/cookie-policy/`, `/disclaimer/`, `/eula/`. The proposal counted 4. | MIGRATE TEXT AS-IS into plain paragraph/heading blocks. No design attention, no copy changes. | Working default (`.logs/planning/build-plan.md`): migrate all five. `/eula/` is a Decide-with-client row in `.logs/planning/site-architecture.md` (recommendation: retire + redirect to `/terms-of-service/`). Rian confirms with the client which four the proposal meant. Whether `/privacy-policy/` and `/cookie-policy/` are static text or Termageddon-generated embeds (the live site runs Usercentrics via Termageddon): to be confirmed from the live page source before copying; an embed is re-attached, not transcribed. |
| Media library | Uploads on the live host; size not measured. Staging `wp-content/uploads/` holds only template media. | MIGRATE. Keep the `/wp-content/uploads/YYYY/MM/` paths identical so in-post image URLs survive the domain swap. Image optimization across migrated media is in scope (proposal, "Performance"). | Uploads size and the exact count of attachments: to be confirmed by Rian from the export. The 20th-anniversary logo in the live library is never reused (client decision 2026-08-27); the Brand Kit logo replaces it. |
| Users | July audit: 20 accounts, 14 with published posts. REST on 2026-09-02 exposes 9 users with public posts; the sitemap has 7 author archives. | AUDIT, then the client decides per person (see "Authors"). Every migrated account gets a password reset; former-staff accounts are retired or reduced per the client's matrix. | Roster is confirmed from the exported database. A least-privilege user for AISV's OpenForge publishing (Author or Editor, with an Application Password, never pasted in chat) is created at launch, not on staging. |
| Gravity Forms | Forms 1, 2, 3 seen on the live site (newsletter and contact). | Forms: MIGRATE the definitions, then rebuild the contact form with light qualification and a source field per `.logs/planning/tracking-conversion.md`. Entries: migrate only under Route A (they ride in the database); under Route B they stay in the live-site backup. | Whether historic entries must be on the new site: to be confirmed by Alicia Wimmer. Licence status and transfer: to be confirmed by Rian in week one. |
| The assessment | A Typeform embed on `/our-approach/legal-health-check/` (`data-tf-widget="QIIINVqG"`, iframe title "BHA Survey") inside `id="bha-form"`. | EMBED ONLY. Re-embed the same widget id on the rebuilt assessment page. Nothing to migrate; no logic is rebuilt (AISV's Creative Concept 03 is not in scope). | Typeform account ownership and what it routes to: to be confirmed by Rian with AISV. The page's licensing disclaimer ("Washington, Idaho, and Oregon") is NOT carried over until the client resolves the Idaho/Colorado discrepancy. |
| Menus, widgets, footer | Beaver Builder / theme-specific. | REBUILD. Footer "Community Involvement" list migrates minus EO Portland and Women in Finance; Twitter and Facebook links are dropped everywhere (client decisions 2026-08-27). | See `.logs/planning/site-architecture.md` for the target nav. |
| Custom taxonomy `article-type` | Empty (no posts) but still emits a Yoast sitemap with 4 term URLs. | RETIRE. Do not register it on the new site; its sitemap disappears. | Add its 4 term URLs to the redirect map (target: `/blog/`). |
| Tracking tags | GTM, Google tag, GA4, Hotjar, RB2B, Meta pixel, Usercentrics. | PRESERVE AND VERIFY, not migrate. | Owned by `.logs/planning/tracking-conversion.md`. |
| Schema | Yoast default graph with two firm names. | NOT MIGRATED. Rebuilt via `bw-ai-schema-pro` configuration. | Owned by `.logs/planning/schema-entity-plan.md`. |

## Migration route

Two routes are possible. **Route A is recommended.** The decision is Rian's and is made before
Phase 3 of `.logs/planning/build-plan.md`; it depends on what access the client's current host
and vendor actually hand over (hosting-account and registrar access asked 2026-08-13; Alicia
Wimmer "working on providing" it on 2026-08-18; no confirmation since).

### Route A (recommended): restore the live site into staging, then swap to Kadence and strip Beaver Builder

1. Take a full backup of the live site: database plus `wp-content/uploads/` (themes and plugins
   are optional; we do not want them). Source, in order: (1) a host-level export from the
   client's current hosting control panel (database dump plus a `wp-content/uploads/` archive),
   read-only, nothing installed on live; (2) only if the host offers no export, and only with
   Rian's written go-ahead recorded in `.logs/handoff.md`, a one-time UpdraftPlus install on
   live to take the backup, removed from live immediately after the archive is downloaded.
   Restoring into staging uses UpdraftPlus (already installed on staging) for an UpdraftPlus
   set; how a raw host dump is imported on staging (the snapshot procedure in
   `.logs/planning/build-plan.md` names the sanctioned path) is to be confirmed by Rian.
2. Snapshot staging first (see "Database snapshot"). Staging is an untouched template clone (site
   title "Demoing", 1 post, 2 pages), so there is nothing to lose yet, but the habit starts here.
3. Restore the database and uploads into staging with UpdraftPlus (restore "Database" and
   "Uploads" only; do not restore "Themes" or "Plugins"). Let UpdraftPlus's migrator do the
   search-replace from `equinoxbusinesslaw.com` to `equinoxbusinesslaw.demoing.info`; verify with
   `srv-gw wp --project equinoxbusinesslaw -- search-replace --dry-run` afterwards. Whether
   staging's UpdraftPlus is the build that includes the Migrator (cross-URL search-replace on
   restore) is to be confirmed by Rian; if not, restore without URL rewriting and run
   `srv-gw wp --project equinoxbusinesslaw -- search-replace equinoxbusinesslaw.com
   equinoxbusinesslaw.demoing.info --all-tables --dry-run` first, then without `--dry-run`.
3b. Before anything else, reset every restored account's password without printing or emailing
   it. The restored database carries every live login with its password hash (hosting-vendor
   and former-staff administrators included), and until this runs anyone holding a live-site
   login can sign into staging behind the shared gate. List the IDs with
   `srv-gw wp --project equinoxbusinesslaw -- user list --field=ID`, drop `support`'s ID from
   the list, then run `srv-gw wp --project equinoxbusinesslaw -- user reset-password <ids...>
   --skip-email` (wp-cli generates random passwords and shows none). Rian sets `support`'s own
   password in wp-admin. Demote any restored account with the administrator role that is not on
   the roster to subscriber until the client's per-person matrix is applied.
4. Re-assert the staging stack, because the restored `active_plugins` option and `template` /
   `stylesheet` options point at the live site's theme and plugins that are not on disk:
   `srv-gw wp --project equinoxbusinesslaw -- theme activate kadence-child`, then activate the
   template plugin set (Advanced Custom Fields Pro, BW Dev, Gravity Forms, Kadence Blocks, Kadence
   Blocks Pro, Kadence Pro, UpdraftPlus) and install the missing ones: Yoast SEO, Search & Filter
   Pro (client licence), `bw-ai-schema-pro` (from plugins.bowden.works), and Redirection (or the
   equivalent Rian picks). Clear the orphaned plugin references from `active_plugins`.
5. Strip Beaver Builder residue (see "Beaver Builder strip").
6. Set the site title to the canonical firm name once the client confirms it (working
   assumption: Equinox Business Law Group PLLC).

Keeps: post IDs, all post meta (Yoast titles and descriptions, featured-image links), users and
their post attributions, media attachment records and paths, categories and tags with their
descriptions and slugs, Gravity Forms definitions and entries, Search & Filter Pro settings, Yoast
site settings and redirects if any, menus (rebuilt anyway), comments.

Loses / brings along to be removed: the `html5blank` theme record, Beaver Builder and UABB page
meta on every page, Rocket Lazy Load settings, any host-specific mu-plugins or drop-ins in the
backup (the gateway's `srv-gw fix-wpe-mu` handles the WP Engine family; anything else is removed
by hand), a table prefix that may differ from staging's (to be confirmed by Rian from the backup;
UpdraftPlus handles a prefix change on restore).

Prerequisites for Route A, each to be confirmed before it is chosen:

- Live WordPress administrator access (named in the proposal under "Hosting and access"; not
  yet requested in the thread. The 2026-08-13 request covered the hosting account and domain
  registrar, which Alicia Wimmer was "working on providing" on 2026-08-18; the 2026-08-27 email
  reports Google and Cloudflare access only. Rian asks for it): to be confirmed by Alicia Wimmer.
- A backup of the live site, sourced in the order given in step 1 (host-level export first).
  Whether UpdraftPlus is installed on the live site is unknown (a backup plugin leaves no
  front-end trace; the 2026-09-02 inspection did not check plugins from the admin side): to be
  confirmed by Rian once WordPress admin access exists. Installing it on the live site is a
  plugin change and the standing rule is that the live site is never touched, hence the written
  go-ahead in step 1.
- The staging plugin set is reinstalled after the restore (step 4). Search & Filter Pro and
  Termageddon licences are the client's: to be confirmed by Rian in week one.
- A staging snapshot before the restore, and another after step 4 succeeds.

### Route B: WXR import into the template clone

1. From the live WordPress admin, export a WXR file of **Posts only** (Tools > Export, "Posts")
   plus a separate export of **Media**. Do not export Pages: that is where the Beaver Builder meta
   lives, and pages are rebuilt anyway.
2. Import on staging with the WordPress Importer through
   `srv-gw wp --project equinoxbusinesslaw -- import <file> --authors=<mapping.csv>` with
   attachment fetching on. The author mapping file is the outcome of the "Authors" matrix.
3. Export Gravity Forms definitions as JSON from the live admin and import them on staging (GF's
   own import/export). Entries stay in the live-site backup unless the client wants them.
4. Rebuild Search & Filter Pro configuration, Yoast site settings, and menus from scratch.

Keeps: post content, slugs, dates, categories, tags, postmeta (Yoast per-post title/description
travel in the WXR), featured-image relationships (when attachments import cleanly), authors as
mapped.

Loses: post IDs (new ones are assigned), user accounts other than those the mapping creates,
Gravity Forms entries, Search & Filter Pro and Yoast site-level configuration, comments unless
exported with posts, any media the importer fails to fetch over HTTP (Cloudflare or bot rules on
the live host can block the fetch; count attachments before and after).

Route B is the fallback when live administrator access arrives but host-level access never does,
or when the restore in Route A proves dirtier than expected. It starts cleaner and demands more
mapping work.

### Common to both routes

- Category and tag slugs are not changed during import. Consolidation is a separate, sign-off
  gated step (see "Categories and tags").
- Post slugs are not changed. The redirect map in `.logs/planning/site-architecture.md` covers
  pages, author archives, retired categories, and the `article-type` term URLs, not posts.
- The WordPress REST API must remain open and unauthenticated for reads on the launched site;
  AISV's OpenForge publishes through it. No hardening plugin or snippet that closes `/wp-json`.
- Every new file written into `wp-content/` follows the permission rules in
  `/srv/projects/standards/wordpress.md` (`chmod 664` files, `775` dirs, or
  `srv-gw fix-permissions --project equinoxbusinesslaw`).

## Beaver Builder strip

Pages are rebuilt as Kadence blocks, so whatever Beaver Builder and UABB stored for them is
discarded deliberately. Nothing from the builder is rendered on the new site.

Under Route A, after the restore:

1. Confirm `bb-plugin` and any UABB / PowerPack plugin are not on disk and not in
   `active_plugins`. (The July audit named BB PowerPack; the 2026-09-02 inspection saw UABB.
   Which addon set was really installed is to be confirmed by Rian from the backup.)
2. Delete the builder meta from every page and post. The keys are the `_fl_builder_*` family
   (`_fl_builder_data`, `_fl_builder_draft`, `_fl_builder_enabled`, and siblings). Inventory
   first with `srv-gw db-query` (`SELECT meta_key, COUNT(*) FROM wp_postmeta WHERE meta_key LIKE
   '\_fl\_builder%' GROUP BY meta_key`), then delete with
   `srv-gw wp --project equinoxbusinesslaw -- post meta delete` per key, or a single reviewed
   `DELETE` that Rian runs. Take the snapshot first.
3. Check **posts** for residue. Posts are classic HTML, but a handful may have been opened in the
   builder at some point or may carry shortcodes from plugins that no longer exist. Query
   `post_content` on `post_type = 'post'` for `[fl_builder`, `[uabb`, `[pp_`, and generally for
   any `[` shortcode token, and for `class="fl-` markup. List every hit in `.logs/handoff.md`
   with the post URL; fix each by editing the content to plain HTML.
4. Remove `html5blank` and its child from the themes list if they came along.

Under Route B there is nothing to strip (pages are never exported); step 3 still runs on the
imported posts.

Verification is text-in equals text-out: for the sample defined under "Verification", the
visible text of the post on the live site equals the visible text of the post on staging.

## Authors

### Audit inputs (2026-09-02)

- REST `/wp/v2/users` on the live site exposes 9 users with public content. The bracketed
  number beside each name in the ground truth's listing is the WordPress user ID (`id` field
  of `/wp/v2/users`), not a post count: verified 2026-09-02 by reading the endpoint directly
  (`id` 43 is `aliciam`, `id` 20 is `michelle`). Where a sibling plan or register reads those
  nine numbers as post counts (and sums them to 312), it is wrong; the same-day published-post
  totals per author were read from `/wp/v2/posts?author=<id>` (`X-WP-Total`) and are in the
  table. They sum to exactly 513, so every published post belongs to one of these nine accounts.
  The authoritative per-author counts still come from the exported database at migration; the
  REST totals are the same-day indicative read.

| User (display name) | WP user ID (REST, 2026-09-02) | Author slug (REST) | Published posts (REST `posts?author=<id>`, 2026-09-02) | In the Yoast author sitemap | Current team member (2026-09-02 roster) |
|---|---|---|---|---|---|
| Michelle Bomberger | 20 | `michelle` | 456 | yes | Yes: CEO & Managing Attorney |
| Andrew Shaw | 29 | `andrew-shaw` (display name "Andrew Shaw, Intern") | 5 | yes | No |
| John Butler | 31 | `johnequinoxbusinesslaw-com` | 2 | yes | No |
| Katie Robinson | 35 | `katie-robinson` | 9 | yes | No |
| Danica Paddock | 36 | `danica-paddock` | 2 | yes | Yes: Corporate Counsel |
| Jessica McGarvie | 37 | `jessica-mcgarvie` | 1 | yes | No |
| Pat Martinez | 39 | `patm` | 0 | no | No |
| Ryan van Buuren | 42 | `ryan` | 0 | no | No |
| Alicia Wimmer | 43 | `aliciam` | 38 | yes | Yes: Sales & Marketing Operations Coordinator |

- 7 author archives are in the Yoast author sitemap (the seven slugs with a non-zero count
  above). Pat Martinez and Ryan van Buuren have 0 published posts, which is why Yoast omits
  their archives; REST lists them because they authored some other public content (presumably
  pages). What they authored, and whether their accounts still hold a role, is confirmed by Rian
  from the exported database.
- 456 of 513 posts (89%) sit under Michelle Bomberger. The "guest credited in the body, Michelle
  in the data" pattern under "The cap" is therefore expected to be common, not rare.
- The July audit (proposal, 2026-07-30) counted 14 authors with published posts and 20 user
  accounts in total. Neither the 7, the 9, nor the 14 is the migration roster; the exact roster
  (every account, role, post count, current or former) is produced from the exported database.
- The current team is 9 people (see `.logs/planning/site-architecture.md`); only Michelle
  Bomberger, Danica Paddock, and Alicia Wimmer of those appear as post authors. The other six
  (Laura Doehle, Dominique Zett, Devon Maxwell, Leaf E'kaika, Pamela Franklin, Nicole Farahdel)
  get Person entities from the team post type regardless, and an author profile only if they
  will write.
- Alicia Wimmer has two identities on the live site: bio at
  `/about-us/legal-team/alicia-megrath/` (the Yoast page sitemap lists
  `/about-us/legal-team/alicia-wimmer/`) and author
  slug `aliciam`. The new site uses one canonical slug (default `alicia-wimmer`, redirect from
  `alicia-megrath`; her preference to be confirmed by Alicia Wimmer).
- Author archives on the live site are at `/blog/author/<slug>/` (strategy notes, 2026-07-27).
  Every retired or renamed archive gets a redirect-map entry.

### Per-person decision matrix (the client fills this in)

We bring the client the list; they decide, per person, whether to keep the byline, reassign the
posts, or retire the account (proposal, "Author Structure"). Pre-filled from the 2026-09-02 REST
list; rows are added for every further account the exported database reveals. Send the completed
list to Alicia Wimmer (with Michelle Bomberger for the attorneys) and record the answers in
`.logs/handoff.md` and, once final, in `.memory/`.

| Person | WP user ID (REST, 2026-09-02) | Posts (REST, 2026-09-02) | Posts (from export) | Keep byline | Reassign to | Retire account | Decision |
|---|---|---|---|---|---|---|---|
| Michelle Bomberger | 20 | 456 | from export (Rian) | expected | - | - | client decides |
| Danica Paddock | 36 | 2 | from export (Rian) | expected | - | - | client decides |
| Alicia Wimmer | 43 | 38 | from export (Rian) | expected | - | - | client decides |
| Andrew Shaw (Intern) | 29 | 5 | from export (Rian) | - | - | - | client decides |
| John Butler | 31 | 2 | from export (Rian) | - | - | - | client decides |
| Katie Robinson | 35 | 9 | from export (Rian) | - | - | - | client decides |
| Jessica McGarvie | 37 | 1 | from export (Rian) | - | - | - | client decides |
| Pat Martinez | 39 | 0 | from export (Rian) | - | - | - | client decides |
| Ryan van Buuren | 42 | 0 | from export (Rian) | - | - | - | client decides |
| (further accounts from the export, incl. Mandy Bowen if present) | from export (Rian) | - | from export (Rian) | - | - | - | client decides |

Blank decision cells are filled by the client; the "Posts (from export)" column is filled by
Rian from the exported database (the REST column is the 2026-09-02 indicative read). The IDs in
the `--post_author=<id>` and `--reassign=<id>` commands below are re-read from the exported
database before use, never copied from this table.

What each option means mechanically:

- **Keep byline**: the account stays (password reset, role reduced to Author or lower if the
  person has left), the archive stays and is bound to a Person record; for a former employee the
  Person record is a contributor record, not a team-page member.
- **Reassign to**: posts are moved to the named account (`srv-gw wp --project
  equinoxbusinesslaw -- post update <ids> --post_author=<id>`), the old archive URL is redirected
  to the new author's archive or bio, and the old account is deleted with its posts reassigned
  (`wp user delete <id> --reassign=<id>`). Reassigning a guest's post to Michelle is the exact
  error already on the live site; do not choose it for guest-written posts.
- **Retire account**: same as reassign for the posts (the client names the target, usually the
  firm's principal or a generic firm account), archive redirected, account deleted.

### Guest and contributor bylines

The proposal sells "a working guest and contributor byline mechanism, so outside contributors
are credited correctly in both the article and the structured data." The mechanism is
`bw-ai-schema-pro`'s author override and author box, not a hand-rolled co-author plugin:

- The plugin overrides a post's byline (display name and link) with a configured team-member
  author record from its team post type, ships an author box, redirects `/author/<slug>/` to the
  bound team page, and warns in admin when a team page used as a schema author has no linked
  WordPress user. Author and Person schema on the post follow the override.
- For an outside contributor (a person who is not on the firm's team page), use the plugin's
  External Authors feature (Author Profiles admin page; stored in the
  `bw_schema_external_authors` option), which exists specifically for guest contributors without
  a team profile and is reusable across posts. The post's author override points at the External
  Author record, not at a team post. To be confirmed on staging before the author phase: that the
  External Author renders in the byline and author box and is emitted as the Article author
  Person in the schema. If either fails, log the gap in `.logs/v8-feedback.md` and raise it with
  Rian; do not work around it in theme code (no hand-written JSON-LD).
- Configuration detail (team post type mapping, Person fields, schema output) lives in
  `.logs/planning/schema-entity-plan.md`.

### The cap

The deliverable is an audit plus a defined set of attribution corrections, not a 513-post
attribution review (strategy notes, 2026-07-27: "audit and correct author structure + a defined
number of corrections, not fix every post"; proposal: "Correction of author attribution where the
article text and underlying data currently disagree"). Practically:

- Fix attribution on every post found during migration QA (the 20-post sample under
  "Verification", plus any post the residue query or the client flags).
- Hand AISV the remaining suspects as a list for their pruning phase.
- Known example: `/blog/know-what-you-dont-know/` credits David Heyting of Hersman Serles Almond
  in the body copy while the author data credits Michelle Bomberger (July audit, one random
  sample). With 513 posts this is assumed systemic. Query for the pattern (`post_content` containing
  "guest", "contributed by", "written by", or a firm name other than Equinox in the first or last
  paragraph) to size it before promising a number.

## Categories and tags

### The category list

On 2026-09-02, 68 categories were registered (REST recount), 51 of them with a non-zero public
post count (the same 51 that appear in the Yoast category sitemap) and 17 empty. (Earlier drafts
of the ground truth said 52; 51 is correct.) Review all 68 at migration.

Categories with posts (slug and public post count, 2026-09-02):

| Slug | Posts | Slug | Posts | Slug | Posts |
|---|---|---|---|---|---|
| business-planning | 282 | case-studies | 7 | ai | 2 |
| entrepreneurship | 281 | employment-law | 7 | family-business | 2 |
| leadership | 212 | business | 6 | immigration | 2 |
| employment | 174 | community-service | 6 | oregon-law | 2 |
| corporate-governance | 75 | company-culture | 6 | remote-work | 2 |
| general-counsel | 38 | lease | 6 | website | 2 |
| contracts | 28 | real-estate | 6 | workplace-safety | 2 |
| tax | 19 | data-security | 5 | acquisition-2 | 1 |
| business-sale | 17 | cybersecurity | 4 | business-transactions | 1 |
| intellectual-property | 16 | diversity-equity-and-inclusion | 4 | cash-flow | 1 |
| covid | 14 | facilities | 4 | ceo | 1 |
| data-privacy | 14 | risk-management | 4 | estate-planning | 1 |
| human-resources | 13 | finance | 3 | hiring | 1 |
| washington-law | 12 | insurance | 3 | innovation | 1 |
| legal-strategy | 11 | | | interstate-commerce-2 | 1 |
| employer | 9 | | | operations | 1 |
| | | | | policies-procedures | 1 |
| | | | | sale-of-business | 1 |
| | | | | sales-business | 1 |
| | | | | training | 1 |
| | | | | uncategorized | 1 |

Empty categories (17): ada-compliance, banking, charity-donations, compensation-2,
exit-planning, financing, franchise, fundraiser, joint-employment, manufacturing, marketing,
negotiation, patent-intellectual-property, seattle-city-ordinance, succession-planning,
trademark-intellectual-property, will.

Posts are multi-categorised (the counts sum far past 513) and four catch-alls
(business-planning, entrepreneurship, leadership, employment) carry most of the archive, so
category archives are near-duplicates of each other and of the blog index.

### Recommendations for AISV (not actions)

We do not merge, rename, or delete a category without AISV's written sign-off, because every
merge changes a URL that may hold rankings or inbound links, and content taxonomy is AISV's lane.
Send this list to AISV; their answer becomes the consolidation map, and every retired slug gets a
redirect-map entry in `.logs/planning/site-architecture.md`.

| Flag | Slugs | Recommendation |
|---|---|---|
| Near-duplicate set | `employment` (174) / `employment-law` (7) / `employer` (9) | Merge into `employment`. |
| Near-duplicate set | `business-sale` (17) / `sale-of-business` (1) | Merge into `business-sale`. |
| Near-duplicate set | `data-privacy` (14) / `data-security` (5) / `cybersecurity` (4) | AISV chooses one or two. |
| Near-duplicate set (empty) | `patent-intellectual-property` / `trademark-intellectual-property` vs `intellectual-property` (16) | Delete the two empty ones. |
| Deleted-duplicate suffix | `acquisition-2` (1), `interstate-commerce-2` (1), `compensation-2` (empty) | Rename to the un-suffixed slug if it is free; otherwise merge into the nearest topic. |
| Stale | `covid` (14) | Keep the posts; AISV decides whether the category stays or folds into `business-planning` / `employment`. |
| Default | `uncategorized` (1) | Re-categorise the one post; hide the category from filters. |
| Empty | the 17 empty categories | Delete on import unless AISV wants any kept for planned content. |
| Encoding defect | "Charity &amp;Amp; Donations", "Policies &amp;Amp; Procedures" (names double-encoded in the database) | Fix the names on import; slugs unchanged. |

Category archive URL shape on the live site (whether `/category/<slug>/` or `/blog/category/<slug>/`)
is to be confirmed from the export so the redirect map is exact.

### The `article-type` taxonomy

Retired. It is empty, it still emits a sitemap with 4 term URLs, and the proposal sells a clean
XML sitemap with it gone. Do not register it on the new site; redirect its 4 term URLs to
`/blog/`.

### Tags

1,004 tags are registered (REST recount, 2026-09-02). Tags are carried over unchanged, attached
to their posts. No tag archive is linked from the templates unless AISV asks; tag archives are
noindexed as thin pages (proposal, "Block thin pages from search engines").

## Blog index and filtering

- The live index is 57 pages of pagination with no search, no filter, and no author on cards
  (July audit). The proposal sells search and category filtering on `/blog/` using the client's
  existing Search & Filter Pro licence (installed on the live site, 57 asset references on the
  home page, not doing this job).
- Build: one Search & Filter Pro search form (keyword plus category dropdown or checklist) on
  `/blog/`, limited to the categories that survive AISV's consolidation map; results render into
  the same post-card pattern as the default index so filtered and unfiltered pages look identical.
- Cards show featured image, title, date, author name (linked to the bound bio or contributor
  record), and the primary category.
- Pagination is retained (numbered, not infinite scroll) so deep archive pages stay crawlable.
- Every single post ends with the paired CTA block (Book a Consultation; Take the assessment) as
  defined in `.logs/planning/tracking-conversion.md`, followed by the author box.
- Search & Filter Pro assets load only on `/blog/` and category archives (conditional loading is
  in scope under "Performance").
- Licence: the client's; whether it is active and can be re-registered to the new install is to
  be confirmed by Rian in week one. If it has lapsed, the replacement decision is Rian's before
  the index is built.

## Placeholder content rules

We draft placeholder copy; Equinox finalizes it with AISV's guidance (proposal, "Content").
The rules that make that safe:

1. **Every drafted block is labelled DRAFT in the editor.** Use one visible pattern everywhere: a
   notice block at the top of the page or section reading "DRAFT - copy to be finalized by
   Equinox/AISV", or the block note facility in `bw-dev` (admin notes), so a reviewer in the
   editor and a reviewer on the staging front end both see the label. The label is removed only
   when AISV or the client marks the copy final in writing (email or comment), and the removal is
   logged in `.logs/diary.md`.
2. **Sources, in this order:** the brand story (`notes/source/`, complete home-page copy plus
   elevator pitches, headlines, and taglines), the AISV strategy deck (`notes/source/`), the live
   page's own copy. Nothing is invented; when none of the three has the sentence, write the
   simplest factual version and mark it DRAFT.
3. **Never Lorem Ipsum**, never "text goes here", never an empty section. The client never
   reviews filler (proposal).
4. **Never presented as final.** The mockup and every staging review call out which copy is
   draft. `.logs/handoff.md` carries the running list of draft sections.
5. **No pricing of any kind in drafted copy.** If the Fractional General Counsel landing page
   add-on is accepted, the tier structure is drafted with the tier names only; the client
   supplies the numbers.
6. **No licensing sentence** until the client resolves the Idaho/Colorado discrepancy; no
   mention of DC or VA; no Twitter or Facebook mentions.
7. **The home page follows the brand story's section map exactly** (twelve sections plus the
   footer line; 13 rows below). Sections are
   not dropped, reordered, or merged in the draft; the client's "elevated, simplified" ask is met
   by design density, and any cut is AISV's call at review.
8. Drafted page copy for the core pages is built from the live page's text (copied as DRAFT
   input) and the brand story's four service groupings where the live copy is thin.

## Home page copy map

Headings are verbatim from the brand story. The full text of every section is in the brand story
under `notes/source/`; copy it from there, not from memory. Every CTA fires its own GTM event
(direct or transitional) per `.logs/planning/tracking-conversion.md`.

| # | Section | Heading (verbatim) | CTA(s) |
|---|---|---|---|
| 1 | Hero | Grow Your Business Without Growing Your Legal Anxiety | Book a Consultation (primary); Take the Business Legal Health Assessment (secondary) |
| 2 | The Problem | Your Business Has Grown. Has Your Legal Protection Grown With It? | Find Out Where Your Business Is Exposed (links to the assessment) |
| 3 | The Stakes | Legal Problems Are Easier to Prevent Than Repair | none |
| 4 | The Guide | You Do Not Need to Navigate This Alone | none |
| 5 | Value Proposition | Experienced Legal Counsel Without the Full-Time Legal Department | none (four groupings: Contracts and Commercial Agreements; Employment and HR Legal Support; Business Risk and Compliance; Ongoing Strategic Legal Guidance) |
| 6 | The Plan | Protecting Your Business Starts With Three Simple Steps | Book a Consultation (primary); Take the Business Legal Health Assessment (secondary). Steps: Assess / Prioritize / Protect and Grow |
| 7 | The Transformation | Imagine Feeling Confident About the Legal Side of Your Business | none |
| 8 | Before and After | Move From Legal Uncertainty to Legal Confidence | none (columns: Without Proactive Legal Support / With Equinox Business Law) |
| 9 | Who It Is For | Built for Entrepreneurs Leading Growing Companies | none (eight-item fit list; the revenue threshold in the brand story is not reproduced in project files; the audience question is open with AISV) |
| 10 | Objection Handling | no section heading in the copy; four question headings: "We Only Call a Lawyer When Something Comes Up."; "We Cannot Justify Hiring In-House Legal Counsel."; "We Are Worried About Unpredictable Legal Bills."; "Nothing Is Wrong Right Now." | none. Built with the `bw-ai-schema-pro` FAQ block (FAQPage markup). Section heading to be confirmed with AISV. |
| 11 | Assessment | How Legally Healthy Is Your Business? | Take the Business Legal Health Assessment |
| 12 | Final CTA | Protect What You Have Built and Prepare for What Comes Next | Book a Consultation (primary); Take the Business Legal Health Assessment (secondary) |
| 13 | Footer line | Proactive Protection. Predictable Costs. Greater Peace of Mind. / Experienced legal counsel for growing businesses. | none |

The CTA label "Take the Business Legal Health Assessment" is the brand story's wording; it changes
if AISV and the client pick a different name for the assessment (three are in circulation:
"Legal Health Check" on the live site, "Business Health Assessment" / "BHA" in the deck,
"Business Legal Health Assessment" in the brand story). No team photo on the home page; stock
imagery allowed but no stock photos of people (client decision 2026-08-27).

Note on FAQ (section 10): Google stopped showing FAQ rich results in Search on 2026-05-07. The
FAQ block is built for AI answer engines and for buyers, not for rich snippets.

## Content freeze

AISV publishes on the live site during the build: the count moved from 510 (July) to 513
(2026-09-02). The proposal's content-freeze term assumes no pre-launch sync unless raised during
staging, and puts post-launch syncing outside the migration scope, so the protocol below is arranged
directly with AISV (not relayed through the client) and recorded in `.logs/handoff.md`.

1. **At export**: record in `.logs/handoff.md` the published post count and the latest
   `post_date` (`srv-gw db-query --project equinoxbusinesslaw "SELECT COUNT(*), MAX(post_date)
   FROM wp_posts WHERE post_type='post' AND post_status='publish'"` on staging after import, and
   the same numbers read from the live REST API at export time). Also record the highest post ID.
2. **During the build**: AISV keeps publishing on live. No manual copying of new posts into
   staging.
3. **Before launch (delta sync)**: re-export from live only the posts with `post_date` after the
   recorded export date (Route A: a fresh WXR of posts filtered by date from the live admin;
   Route B: the same). Import into staging with the same author mapping; fetch their media;
   run the residue query on the new posts; re-run the counts. Also re-check for edits to old
   posts (`post_modified` after the export date) and re-import those by hand.
4. **Cutover window**: confirm with AISV a publishing pause from the delta export until DNS has
   switched and the new site is confirmed live (length of the pause to be agreed with AISV; see
   below). Anything published during the pause window on the old host is lost unless
   re-imported; say so in writing to AISV.
5. **After launch**: AISV publishes into the new site; the live-site backup is retained for
   reference.

The freeze window and the AISV contact who owns it are to be confirmed by Rian with AISV.

## Verification

Run after the main import, again after the delta sync, and once more on the launched site.
Record results in `.logs/diary.md`.

| Check | Method | Pass |
|---|---|---|
| Post count | Staging count (query above) vs live REST `X-WP-Total` at export | Equal (513 at 2026-09-02; re-read at export) |
| Media count | Attachment count on staging vs the export (Route A: `wp_posts` attachments; Route B: importer report) | Equal; zero fetch failures |
| Categories | Count and slugs on staging vs the consolidation map | Every surviving slug present; every retired slug in the redirect map |
| Tags | Count on staging vs 1,004 (2026-09-02; re-read at export) | Equal |
| Users | Accounts on staging vs the completed decision matrix | Every row resolved; no account with a default password |
| Sample of 20 posts | Pick 20 post URLs spread across years and categories; open each on live and on staging; compare visible text (strip tags, normalise whitespace) | Identical text; same featured image; same date; same author name after the matrix is applied |
| Category and author archives | Every category and author archive URL from the live sitemaps resolves on staging (200) or is in the redirect map (301 to the mapped target) | No 404 |
| Yoast meta | Sample the same 20 posts: `_yoast_wpseo_title` and `_yoast_wpseo_metadesc` present on staging where present on live | Present and identical |
| Redirect map | Every entry in `.logs/planning/site-architecture.md` tested on staging with `curl -I` | 301 to the intended target; no chains |
| Beaver Builder residue | Residue query (`_fl_builder%` meta; `[fl_builder`, `[uabb`, `[pp_` in post content) | Zero rows |
| Shortcode residue | `post_content LIKE '%[%]%'` on posts, reviewed by hand | Every hit either renders (a known plugin) or is fixed |
| REST API | On staging, from an owner shell on the server, `curl -s -o /dev/null -w '%{http_code}' -H 'Host: equinoxbusinesslaw.demoing.info' http://172.17.0.1:3139/wp-json/wp/v2/posts?per_page=1` (port 3139 is the container's Caddy-proxied port, so this reaches WordPress behind the id-auth gate; alternatively an authenticated browser session on the gated URL). Never ungate staging for a check. On the launched site, `curl -s https://equinoxbusinesslaw.com/wp-json/wp/v2/posts?per_page=1` | 200 with a post |
| Draft labels | Grep staging content for the DRAFT notice text | Each hit is on the handoff's draft list |

Author-attribution corrections found during the sample are fixed and logged; the sample is the
defined correction set under "The cap".

## Database snapshot

The snapshot procedure (what to export, where it is stored, how it is named, how to restore) is
defined once in `.logs/planning/build-plan.md`; follow it, do not improvise a variant here.

Rule for this plan: take a snapshot **before every destructive pass** listed above: the restore
(Route A), the Beaver Builder meta deletion, every author reassign or account deletion, every
category merge or deletion, the delta sync, and the launch cutover. Name each snapshot for the
pass it precedes and note it in `.logs/diary.md`. The staging `wp_equinoxbusinesslaw` database is
the only copy of the migrated content until launch; the live site is never written to, so the
live site is the fallback only until the client's old host is decommissioned.
