KJP Website Updates — Project Hub
Consolidated from Notion 2026-04-23. Notion is deprecated; this file is canonical.
Client: Kurt Johnson Photography Site: kurtjohnsonphotography.com G&M Contact: Eric Downs KJP Contacts: Casey Kurz (project lead, MailChimp, wrote original descriptions), Tori Gerkin / Victoria Gerkin (Art Director, email contact), Kurt & Carolyn Johnson (photographer + business partner), Jerred (Casey's partner) Client Timeline: No hard deadline ("it took us a year to make this list") — proceed at our pace
Project Documents
| Document | What it is | For |
|---|---|---|
| phase-2/KJP-AI-Feature-Brief.md | What the client asked for, what we're building, plain-English process overview | Client-facing |
| phase-2/KJP-AI-Tagging-PRD.md | Full technical spec — architecture, data model, API routes, WP plugin, build order | Internal / Dev |
| phase-2/KJP-AI-Cost-Estimate.md | Per-image token costs, model comparison, OpenRouter strategy, client invoice numbers | Internal / Dev |
| phase-2/KJP-AI-LLM-Strategy.md | Three tiered AI options (Small/Medium/Large) + LLM discoverability strategy | Discovery framing |
| phase-1/PHASE-1-SCOPE-AND-OUTLINE.md | Signed Phase 1 SOW scope with dev outline | Active build |
| phase-1/KJP-Website-Updates-Working-Doc.md | Detailed per-item specs, code snippets, hours | Active build |
Timeline Summary
- Feb 9 2026 — Client request email with PDF of asks
- Feb 11 — Q&A sent
- Mar 2 — All answers received
- Mar 10 — Original SOW sent ($5,625 / 25 hrs)
- Mar 10 — Casey asked about descriptions scope
- Mar 11 — Response drafted recommending keep descriptions
- Apr 1 — Client team questions answered (AI examples, provider, lightbox perf, newsletter strategy)
- Apr 14 — Tori asked for AI-only bid
- Apr 17 — Tori reversed, wants split into Phase 1 (non-AI) + Phase 2 (AI deferred)
- Apr 17 — Upload sheet column swap broke the site; Eric fixed, added guardrails, sent Loom
- Apr 21 — Phase 1 SOW SIGNED ($1,800 / 8 hrs / 14-day production). AI deferred to fall/winter.
- Apr 22 — Tori asked to update spreadsheet Type dropdown + alphabetize site By Type filter
- Apr 23 — By Type filter alphabetized (done). Spreadsheet dropdown: reverted my changes after Eric flagged overreach; replied teaching Tori how to edit it herself.
- Apr 29 → May 8 — Phase 1 build, staging deploy, soft-launch review with Tori
- May 8 — Tori signed off (Johnsons + team reviewed, no further changes)
- May 9 — 🚀 Phase 1 LAUNCHED to production (3 days early). All 5 line items live + reCAPTCHA v2 added immediately after. Final invoice ($900) due.
Signed Phase 1 SOW (✅ Launched 2026-05-09)
Amount: $1,800 / 8 hrs / 14-day production PDF: phase-1/2026-018_SOW_Phase2-SIGNED.pdf Loom walkthrough (original): https://www.loom.com/share/956083d2d25f422db797958547ee80bc
Line items: Gallery Lightbox (Photography + Installations), Installation Slider dynamic sidebar, Newsletter MailChimp + Name Fields, Exclude Team from Analytics, Search > 200.
Full dev outline: phase-1/PHASE-1-SCOPE-AND-OUTLINE.md.
Site Stats
| Metric | Count | Notes |
|---|---|---|
| Photography posts | 8,372 | Tags/type 99%+ covered |
| Installations | 486 | 10-30 photos each in slider |
| Missing descriptions | 6,124 (73%) | Primary AI tool target |
| Missing color | 3,416 (41%) | Client interested in AI tagging this |
| Missing region | 4,630 (55%) | Client will handle manually (overlapping regions) |
Environment & Access
| Item | Detail |
|---|---|
| Host | Kinsta |
| SSH | ssh kurtjohnsonphotographycom@34.162.230.19 -p 61436 |
| WP path (prod) | /www/kurtjohnsonphotographycom_376/public |
| Theme | gmlaunch |
| Local dev | DevKinsta (Docker, not Local by Flywheel) |
| Local URL | http://kurtjohnsonphotographycom.local:50381 |
| GA4 Property | G-RF1XX48Q59 |
| Image storage | Flat files at /wp-content/uploads/wpallimport/files/ — NOT in Media Library |
| Import Google Sheet | https://docs.google.com/spreadsheets/d/1YYkMxk3xBrMHBywyEqWFbvo75qTyyU2G3s2hsb9NKso |
Feature Requests — Status & Decisions
1+5. Gallery Lightbox & Photo Navigation (Phase 1 — Active)
Status: Scoped — ready to build Client asks: - Hover over image in archive grid → image pops up slightly larger showing full crop + KJP 4-digit code - Click image → opens full-size lightbox with all metadata + prev/next arrows to browse all images on that page - Works on both Photography AND Installation archive pages
Solution: PhotoSwipe 5 (MIT, zero dependencies, ~5kb, built for photography)
Client Decisions:
| Question | Answer |
|---|---|
| Click or hover? | Both. Hover = slightly larger preview + code. Click = full lightbox with arrows. |
| Arrow browsing scope | All images on the current page |
| Info in lightbox | Hover: just KJP 4-digit code. Click/lightbox: all info + arrows |
| Which archives? | Both Photography and Installations |
Dev note: Two-part interaction — hover preview (CSS/JS tooltip) + click-to-open lightbox (PhotoSwipe). Hover preview is separate from the lightbox and needs custom implementation. Files: loop-photo.php, functions.php, slick-init.js, FacetWP facetwp-loaded rebinding.
Scope question (Apr 23): Signed SOW only mentions click lightbox. Hover preview was in the original quote. At trimmed 8 hrs, need Eric's call on whether hover stays in Phase 1.
2. Installation Slider — Dynamic Sidebar (Phase 1 — Active)
Status: Scoped — ready to build (KJP handles data entry)
What they want: When clicking through installation photos, the sidebar info should change per slide — everything except title and institution.
Solution: Extend ACF installations_photos_2 repeater with per-photo subfields + JS sidebar swap on Slick afterChange.
Client Decisions:
| Question | Answer |
|---|---|
| What changes per photo? | Everything EXCEPT title and institution. Description, type, tags all change. |
| Institution stays same? | Yes — project name stays, browse photos within that one installation |
| Who enters the data? | KJP handles data entry. They have most of it already. |
| Simpler version? | No — they want full data per photo, not just captions |
Dev note: We build the fields and the JS swap logic. KJP populates 486 installations worth of per-photo data. They said "we will rewrite descriptions to better match the photos." Files: single-installations.php, slick-init.js, ACF field group for repeater subfields.
3. Newsletter — MailChimp + Name Fields (Phase 1 — Active)
Status: Scoped — ready to build (need MailChimp API key from Casey)
What they want: Newsletter form → MailChimp integration, add first/last name fields, sitewide.
Solution: Gravity Forms MailChimp Add-On + reCAPTCHA v3 (98% spam rate found in Q1 maintenance).
Client Decisions:
| Question | Answer |
|---|---|
| MailChimp account? | Yes. Casey is lead and can provide API key. |
| Keep email to Tori? | Yes, keep as backup |
| Sitewide or homepage? | Sitewide (footer form on all pages + homepage module) |
| Name fields required? | Yes, required |
Blocker: MailChimp API key from Casey.
4. Exclude Team from Analytics (Phase 1 — Active)
Status: Scoped — ready to configure (no code changes)
What they want: Remove KJP team from GA4 tracking.
Solution: GA4 internal traffic filters (IP-based for office/studio) + Google Analytics Opt-out browser extension for home.
Client Info: - 4 team members: Casey & Jerred (live together), Kurt & Carolyn (live together) - All work from home AND a studio - Need: 3 IP addresses (2 homes + 1 studio) + browser extensions for all 4
Blocker: Need the 3 IP addresses — Casey/Jerred home, Kurt/Carolyn home, studio. Each person Googles "what is my IP" from each location.
6. Search Results Limit (Phase 1 — Active)
Status: Ready to deploy — one-line fix
What they want: Search caps at 200, show all results.
Solution: Change cap in FacetWP/SearchWP integration from 200 → 2,000. Use SearchWP per_page filter (fires after FacetWP's override). Do NOT edit FacetWP plugin file directly (overwritten on update).
No client input needed. Ready to ship today.
7. AI-Generated Tags & Descriptions (Phase 2 — Deferred)
Status: DEFERRED to fall/winter 2026 per Tori (4/17). Original SOW sent 3/10 ($5,625 / 25 hrs) superseded by Phase 1 signed 4/21.
Original plan: Custom AI tool to backfill 6,124 missing descriptions with a feedback/learning loop.
Scope evolution (Casey's input 3/10):
Key insight from Casey: "More than having descriptions for each image, we think it would be more beneficial to have AI generate the searchable tags for each image." Tags are their priority — designers search by mood (uplifting, calm, soothing), flower types, etc. Descriptions are secondary and only worth it if they significantly help SEO.
What Casey wants (priority order): 1. AI-generated tags — mood (uplifting, calm, soothing), specific flower/plant species, visual descriptors. #1 ask. 2. AI color classification — Casey interested in AI tagging dominant color(s). Asked: "Does AI have capability of picking the most prominent color(s)?" 3. AI descriptions — open to it IF reasonably priced and good for SEO, but not the priority. 4. Region — NO. They overlap regions and handle manually.
Voice/tone guidance (for descriptions if we do them): - Match Casey's existing description style - Healthcare-focused client base — avoid negative, dark, or threatening words - Never use the word "magical" — Kurt hates it
Review process: KJP reviews everything before publish. No auto-publish. Ongoing: Yes, auto-tag new uploads.
Full spec: phase-2/KJP-AI-Tagging-PRD.md · Costs: phase-2/KJP-AI-Cost-Estimate.md · Tiers: phase-2/KJP-AI-LLM-Strategy.md
Action Items — G&M (as of hub consolidation)
Phase 1 (Active): - [ ] Request MailChimp API key from Casey - [ ] Request 3 IPs (Casey/Jerred home, Kurt/Carolyn home, studio) - [ ] Deploy search limit fix (no blockers, do it) - [ ] Resolve hover-preview scope question with Eric - [ ] Build calendar + kickoff email to Tori
Phase 2 (AI — Deferred): - [ ] Follow up with Casey re: AI tags vs descriptions — scope tagging system (mood, species, visual descriptors) - [ ] Answer Casey's multi-color question — yes, AI can tag multiple or pick dominant - [ ] Assess SEO value of descriptions — give Casey a straight answer - [ ] Taxonomy cleanup before any AI work (Pruple, Yelllow, Gold, Grasses, Gray, Black + zero-post region/type terms)
Outstanding Questions (for when Phase 2 activates)
- MailChimp API key — Casey said she'd provide it (still open for Phase 1 too)
- IP addresses for analytics filtering — still open for Phase 1
- AI scope clarification — mood tag categories, supplement vs replace, descriptions in or out
- Multi-color tagging — all prominent vs dominant
- Budget/timeline expectations for Phase 2
Completed Work
2026-04-23 — Alphabetized website By Type filter (FacetWP type facet orderby flipped from count to display_value via direct SQL on facetwp_settings). Verified in Chrome. No charge, Phase 1 goodwill.
2026-04-17 — Fixed the Number/Featured Image column swap that broke 69 photography posts. Patched production DB, corrected sheet rows, added hard data validation. Full post-mortem in theme docs.
Q1 2026 Maintenance (Feb) — DevKinsta setup (MariaDB auth, port conflicts, SSH key, rsync sync, 52k URL replacements). Bug fixes: PHP warnings on Photography archive (loop-photography.php line 65 $have_posts → $wp_query), ACF translation loading notice (wrapped acf_add_options_page() in init hook), deactivated Bulk Delete plugin. Spam finding: 98% of form entries were spam bots.
Project Quote Reference
Approach: No formal design pass — we build to match the existing site's look and feel, then show a working mockup for client review before finalizing.
Original estimate (pre-Phase 1 signing):
| # | Item | Hours | Status |
|---|---|---|---|
| 1+5 | Gallery Lightbox + Hover Preview | 5 | Signed Phase 1 (trimmed to fit 8-hr budget) |
| 2 | Installation Slider — Dynamic Sidebar | 2 | Signed Phase 1 |
| 3 | Newsletter — MailChimp + Name Fields | 0.5 | Signed Phase 1 (waiting on API key) |
| 4 | Exclude Team from Analytics | 0.5 | Signed Phase 1 (waiting on IPs) |
| 6 | Search Results Limit Fix | 0.5 | Signed Phase 1 (ready to deploy) |
| — | QA, Testing & Launch | 1 | Signed Phase 1 |
| 7 | AI & LLM Strategy — Discovery | 3–5 | Deferred to Phase 2 |
| 7 | AI & LLM Strategy — Build | TBD (S/M/L tiers) | Deferred to Phase 2 |
Signed Phase 1 SOW total: 8 hrs / $1,800 (well below the 23.5–36 hr original range). Eric's call: assume intentional, work to 8 hrs.
Technical Reference
Taxonomy Data (from production DB)
Tags: 2,778 terms (396 zero-post), 59,446 assignments across 8,372 posts. Free-form hierarchical. Significant duplicates (flower/flowers, landscape/landscapes, tree/trees) and typos (testure, tesures, palicans, slouds, photgraphy).
Top 15 tags by usage: flower (2,425), flowers (2,290), landscape (1,944), green (1,678), trees (1,642), grasses (1,590), landscapes (1,448), leaves (1,349), tree (1,309), water (1,261), waterscape (1,211), midwest (1,096), texture (1,075), leaf (1,041), waterscapes (990).
What's missing (Casey's ask): Mood/emotional tags (uplifting, calm, soothing, serene, energetic). Specific plant species (lily exists; most flowers aren't identified by species).
Color: 16 terms (6 zero-post). Active: Green (1,501), Yellow (681), Pink (644), Orange (527), White (519), Blue (501), Purple (495), Red (416), Brown (273), Black/White (82). Junk: Pruple, Black, Gold, Gray, Yelllow, Grasses. Total coverage: 5,639 of 8,372 posts (67%).
Type: 14 active (as of 2026-04-23): Flowers (2,067), Landscapes (1,551), Waterscapes (1,108), Leaves (990), Trees (954), Grasses (781), Animals (615), Panoramas (603), Textures (456), Vectors (273), Impressionism (190), Slices (126), Layers (75), Whimsy (44). Typos to clean (all 0): Wasterscapes, Gresses, Landscaspes, Watescapes, Ainmals, Aninmals, Aniamls, Graasses, Grassses, Panoramics, Flower, Vector, Texture, Landscape, Interpretations, Filters.
Region: 11 terms (4 zero-post). Active: Midwest (1,575), Southeast (814), Southwest (475), East (347), Rocky Mountains (335), Pacific (217), Noncontiguous (8). Unused: Great Lakes, Northeast, Mid-Atlantic, East Coast.
Key File Paths
| Purpose | File |
|---|---|
| Photography CPT + taxonomies | functions/post-type-photography.php |
| Installations CPT + taxonomies | functions/post-type-installations.php |
| Photography archive | archive-photography.php → inc/cpt-loops/loop-photography.php |
| Photo grid item | inc/loops/loop-photo.php |
| Installation single + slider | single-installations.php |
| Slick slider config | js/slick-init.js |
| Footer (newsletter form) | footer.php (Gravity Form ID 2) |
| Newsletter CTA module | inc/page-modules/2_column_email_signup_cta_module.php |
| FacetWP rebinding + scroll | header.php (facetwp-loaded event) |
| Search 200 limit (source) | plugins/facetwp/includes/integrations/searchwp/searchwp.php line 187 |
| ACF: Installation photos repeater | acf-json/group_62741bba884ac.json |
| ACF: Page modules | acf-json/group_5afb1724c1090.json |
| FacetWP settings | functions/wp-facet-settings.php (theme filter hooks) — main settings live in facetwp_settings WP option |
Architecture Notes
- Images stored as flat files via WP All Import at
/wp-content/uploads/wpallimport/files/— NOT in WP Media Library. ACF text fields store filenames. - Slick carousel for all sliders. Installation slider class has a typo:
.instalation-slider(one L). Do not "fix" — many styles depend on it. - 49 posts per page on photography archive, ordered by menu_order ASC.
- FacetWP handles all filtering — facets for type, region, color, search (SearchWP), plus pagination.
- No build tools — all JS/CSS loaded directly or via CDN. jQuery is present.
- Grid pattern: Alternating groups of 3 and 4 images (3-4-3-4 pattern).
- Gravity Forms Form 2 = newsletter. Custom submit button with Font Awesome arrow icon.
- FacetWP reindexing quirk: do NOT run
wp facetwp indexdirectly —prefix_category_query()infunctions/wp-pre-get-posts-blog.phpcaps it to 14 posts. Use the eval-file workaround documented in theme'sdocs/STACK.md.