WordPress · Construction
FoxBud
A lead-generation site for a fit-out company, readable for search engines and AI
FoxBud fits out and finishes offices in Warsaw, Wrocław and Kraków. The new site generates leads from two audiences, property managers and office managers, on a custom WordPress theme and plugin with complete structured data for search engines and language models.
- Role
- Sole developer: 124 of 125 commits, 28 pull requests
- Period
- Jul - Sep 2026
- Stack
- WordPress
- PHP
- Custom theme and plugin
- ACF
- schema.org
- llms.txt
- Cloudflare


- custom post types
- 4
- schema.org types in the graph
- 18
- AI bots with an explicit policy
- 17
- months until leads / consents are deleted
- 24 / 48
Context
The site was rebuilt from scratch on a custom theme, with two service paths written in different tones: fit-out for property managers and office finishing for office managers, plus a free cost-estimate audit.
Old service addresses redirect permanently to the new service pages.
What I did
- Classic PHP theme and a custom plugin, no page builder and no build step
- Four custom post types: services, projects, case studies and a knowledge base, with ACF fields registered in code
- Own form handling: leads saved to a database table, with the consent record stored next to each lead
- schema.org graph extended from ACF data: services with offers and prices, FAQ, articles, breadcrumbs, places and audiences
- llms.txt generated from the database on every request
- An AI bot policy: 8 search bots and 8 training bots allowed by the client's decision, one bot blocked
- Daily retention job: leads and their attachments deleted after 24 months, consent records after 48 months
How it fits together
Content
- Services
- Projects
- Case studies
- Knowledge base
Output
- Pages
- schema.org graph
- llms.txt
Readers
- Visitors
- Search engines
- AI assistants
Leads and consents are stored together and deleted on schedule.
Hard parts and how I solved them
Problem
After each deploy visitors saw stale pages, because the server cache and Cloudflare each kept files for 7 days.
Solution
The cache is purged automatically when the theme version changes, and asset URLs include the file's modification time.
Problem
On iOS Safari some words ran together. Text pasted from Figma carried an invisible U+2028 line separator.
Solution
Cleaned 27 fields in one batch and strip the character on every save, so it cannot come back.
Problem
Content edits made by the client kept coming back after deploys, because content migrations re-ran every time.
Solution
Each content batch now runs once per install and records that it ran.
Results
- Content model for 26 projects, 23 knowledge-base articles, 3 case studies and 3 service pages
- 16 content batches delivered as versioned migrations that run once per install
- Structured data with 18 schema.org types, built from the same fields editors fill in
The site is waiting for launch, so there is no live link yet.
Screens
Swipe or use the buttons to browse the screens.
Want this kind of work on your team?
I'm happy to walk you through any of these projects in more detail.
Get in touch



