KJP Phase 1 — Scope & Dev Outline
Single source of truth for the currently-active SOW. Start here before touching code.
- Job #: 2026-018
- Signed: 2026-04-21 (Kurt Johnson)
- Signed SOW: 2026-018_SOW_Phase2-SIGNED.pdf
- Budget: 8 hrs / $1,800 fixed bid
- Production window: 14 days from kickoff
- Payment: $900 at signing, $900 on completion
- Project Hub (canonical client-decisions doc): ../KJP-Project-Hub.md
- Detailed working doc (per-item specs, hours, code snippets): KJP-Website-Updates-Working-Doc.md (same folder)
Note on hours: Original quote was 23.5–36 hrs for the same line items. Signed SOW is 8 hrs. Eric's call (2026-04-23): assume intentional, work to the 8-hr number, flag overages as they surface.
Environment
| Item | Detail |
|---|---|
| Host | Kinsta (not Flywheel) |
| SSH | ssh kurtjohnsonphotographycom@34.162.230.19 -p 61436 |
| WP path (prod) | /www/kurtjohnsonphotographycom_376/public |
| Local dev | DevKinsta (Docker) |
| Docker WP-CLI | docker exec devkinsta_fpm bash -c "cd /www/kinsta/public/kurtjohnsonphotographycom && wp ..." |
| Theme | gmlaunch at /Users/edowns/DevKinsta/public/kurtjohnsonphotographycom/wp-content/themes/gmlaunch/ |
| GA4 property | G-RF1XX48Q59 |
| Image storage | Flat files at /wp-content/uploads/wpallimport/files/ — NOT WP Media Library. ACF text fields store filenames. |
| Import sheet | https://docs.google.com/spreadsheets/d/1YYkMxk3xBrMHBywyEqWFbvo75qTyyU2G3s2hsb9NKso |
Scope — 5 line items from the signed SOW
1. Gallery Lightbox & Photo Navigation (Photography + Installations archives)
SOW text: "Gallery Lightbox & Photo Navigation to allow users to enlarge photos on click."
Hover-preview decision (2026-04-29): click-only — signed SOW scope, no hover preview. Re-quotable later.
Client decisions (from KJP-Project-Hub.md): - Click → full PhotoSwipe lightbox with all info + prev/next arrows - Arrows cycle through all images on the current page (49 posts/page on Photography) - Applies to both Photography and Installations archives
Library: PhotoSwipe 5 (MIT, ~5kb, no deps, photography-native)
Files to edit:
- inc/loops/loop-photo.php — wrap images, add lightbox data attributes
- inc/cpt-loops/loop-photography.php — archive template
- inc/cpt-loops/loop-installations.php — installation archive template
- functions.php — enqueue PhotoSwipe
- header.php — already has the facetwp-loaded event; rebind PhotoSwipe after FacetWP AJAX reloads
- New init JS file
2. Installation Slider — Dynamic Sidebar
SOW text: "When clicking through installation photos, the sidebar description should change per slide."
Client decisions (from KJP-Project-Hub.md): - Sidebar changes on each slide: description, type, tags (everything except title and institution) - Title + institution stay fixed (it's still one installation project) - KJP handles all data entry for 486 installations × 10–30 photos each - They plan to rewrite descriptions to better match individual photos
Architecture change: Extend ACF installations_photos_2 repeater with per-photo subfields (description, type, tags), then JS swaps sidebar HTML on Slick's afterChange event.
Files to edit:
- single-installations.php — output per-slide data as data-* attributes; sidebar becomes JS-swappable container
- js/slick-init.js — hook afterChange to update sidebar
- acf-json/group_62741bba884ac.json — add repeater subfields
Gotchas:
- Slick class has a typo: .instalation-slider (one L). Don't "fix" it.
- Fallback gracefully to installation-level data if a slide has no per-photo metadata (data entry isn't done yet for 486 posts).
3. Newsletter — MailChimp + First/Last Name Fields
SOW text: "Add first and last name fields to the newsletter form. Pass entries through to Mailchimp."
Client decisions (from KJP-Project-Hub.md): - Sitewide (footer form on all pages + homepage CTA module) - Name fields required - MailChimp account exists; Casey has API key - Keep Tori email notification as backup (still want her to see signups) - Q1 maintenance found 98% spam (7,473 of 7,613 entries) — reCAPTCHA v3 is needed
Approach: Gravity Forms MailChimp Add-On (included with their Elite license) + reCAPTCHA v3.
Files to edit:
- Gravity Forms admin → Form 2: add First Name + Last Name fields
- Gravity Forms admin → MailChimp feed: Email→email, First Name→FNAME, Last Name→LNAME
- footer.php — layout adjustment, current form is single-line (email + arrow). Options: stacked vertical, or two-row (names row 1, email+submit row 2), or compact with placeholder labels. No formal design.
- inc/page-modules/2_column_email_signup_cta_module.php — homepage module variant
- functions.php — if needed for submit button filter
Blocker: MailChimp API key from Casey.
4. Exclude Team from Analytics (GA4)
SOW text: "GA4 internal traffic filters (IP-based for office/studio) + Google Analytics Opt-out browser extension for home."
No code changes — all GA4 admin configuration.
Team: 4 people across 2 homes + 1 studio. - Casey + Jerred (one home) - Kurt + Carolyn (one home) - All four work at a shared studio too
What to do: 1. GA4 Admin → Data Streams → Web → Configure Tag Settings → Define Internal Traffic → add 3 IPs 2. GA4 Admin → Data Settings → Data Filters → activate Internal Traffic filter 3. Email team with link to https://tools.google.com/dlpage/gaoptout (handles non-office browsing) 4. Verify in GA4 real-time reports
Blocker: 3 IP addresses. Easiest: ask each person to Google "what is my IP" from office/studio + both homes.
5. Search Results Limit > 200
SOW text: "Allow for the search results to be greater than 200."
Root cause: FacetWP caps SearchWP queries at 200 in plugins/facetwp/includes/integrations/searchwp/searchwp.php line 187 (hardcoded performance guard).
Client decision (from KJP-Project-Hub.md): Raise to 2,000.
Fix: Use SearchWP's per_page filter to override FacetWP's cap. This fires after FacetWP's override, so it wins. Snippet is in the working doc.
Perf impact: 0.5–2s slower on broad terms ("flower" matches thousands). Narrow terms see zero diff. Pagination unchanged.
Files to edit: functions.php (filter) OR a small mu-plugin. Do NOT edit the FacetWP plugin file directly (overwritten on update).
No blockers. Can ship today.
Admin / Project Management (1 hr in SOW)
- Build project calendar, assign milestones across the 14-day window
- Kickoff email to Tori with prereq asks (MailChimp key, 3 IPs)
- QA pass, final delivery email + invoice trigger at end
Kickoff Blockers (need before work starts)
- MailChimp API key — from Casey. Blocks item 3 (wiring).
- 3 IP addresses — Casey/Jerred home, Kurt/Carolyn home, studio. Blocks item 4.
Item 5 (search fix) has zero blockers and can ship on day 1 as a quick win.
QA Checklist (✅ Launched 2026-05-09)
- [x] Lightbox opens on Photography archive with prev/next arrows through current page
- [x] Lightbox opens on Installations archive (note: Installations archive cards click-through to single, not lightbox — by design after restoring 4/30 regression)
- [x] Lightbox rebinds correctly after a FacetWP filter change (capture-phase document click listener as belt-and-suspenders)
- [x] Installation slider sidebar swaps on slide change (verified on
/installations/demo-childrens-nebraska/) - [x] Installation slider falls back gracefully on posts without per-photo data
- [x] Newsletter footer form accepts first + last name + email
- [x] Newsletter homepage module accepts all three
- [x] Newsletter submission lands in MailChimp audience with correct field mapping (gform_after_submission_2 hook registered, mc4wp.api_key present, list
b0a726b389) - [x] Tori still gets the notification email (no admin notification changes made)
- [x] reCAPTCHA v2 active (spec said v3; we shipped v2 — v2 was already configured globally with site keys, just added the field to Form 2)
- [x] GA4 — chose opt-out browser extension path instead of IP filter (client decision 4/30); team installing the extension on each browser
- [x] Site search returns more than 200 results on broad terms (SearchWP cap raised to 2,000)
- [ ] Real-browser cross-device smoke test — DEFERRED: chrome-devtools MCP wedged on launch day. Eric to eyeball the live site directly.
What's NOT in Phase 1
- AI / LLM work (tagging, descriptions, taxonomy cleanup, LLM optimization) — deferred to Phase 2. See ../phase-2/KJP-AI-Tagging-PRD.md, ../phase-2/KJP-AI-Cost-Estimate.md, ../phase-2/KJP-AI-LLM-Strategy.md, and
../phase-2/claude-bundle-full/for the parked specs. - Newsletter placement strategy / popup / design — Tori asked about this 4/1; we punted it as new scope.
- Hover preview — out of scope (decision 2026-04-29, click-only build). Re-quotable later if Tori asks.
Related Files
| File | What it is |
|---|---|
| 2026-018_SOW_Phase2-SIGNED.pdf | Binding contract (same folder) |
| KJP-Website-Updates-Working-Doc.md | Full dev specs, code snippets, hour breakdowns per item (same folder) |
| ../reference/KJP-Client-Questions.md | Original discovery Q&A |
| ../README.md | Top-level project hub (timeline, status log, contacts) |
| ../KJP-Project-Hub.md | Canonical client-decisions hub (ex-Notion) |