BlueBee
Full agency website on Payload CMS plus a Gemini sales-offer generator
/ Overview
The whole agency website — design, build, content model, PL+EN i18n — plus a custom Gemini sales-offer generator and a self-hosted Twenty CRM on the same Mikrus VPS. One stack. One bill. Editable by the agency, not by engineering.
See it live/ Project Details
Category: Agency website + own AI platform
Client: BlueBee Marketing — built by CraftWeb
Role: Full-stack · AI · CMS architecture
Timeline: 2026
Live: bluebee.marketing
/ Problem
BlueBee needed a website first. Then they needed to sell faster than they could write. Every offer is bespoke. Writing one from scratch takes 45–90 minutes. The naive "just ask Gemini" answer rewrites sacred prices, switches Polish to English, drops half the bullets and 504s after 80+ s.
/ TL;DR
10 / 6
Services across 6 industries — the full marketing surface of the agency.
PL + EN
Field-level i18n with translated paths and hreflang.
15–25 s
Internal AI offer generator — down from 80+ s synchronous baseline. 5× speedup.
4 layers
Prompt guards (sacred numbers / language lock / completeness / user-edit).



/ Vision
Site is the brand, before it's a funnel. Editable by the agency, not engineering. The offer-generator never lies about prices. One VPS, no SaaS bills.
/ Principles
Site is the brand
Bold typography on neon yellow, no stock photos, a magenta hero character memorable enough to come up in onboarding calls.
Editable by the agency
Every service, industry, case study, testimonial, blog post lives in Payload CMS as a first-class collection.
Never lies about prices
SACRED NUMBERS extracted from brief, regex-validated against generated HTML, drift fails the response and triggers stricter retry.
One VPS, no SaaS bills
Site, Payload admin, Gemini generator, Puppeteer PDF and self-hosted Twenty CRM share one Mikrus VPS.
/ Engineering challenges
Async Gemini that doesn't 504. Hallucinated prices caught by regex. Puppeteer PDF that matches the iframe pixel-for-pixel. WYSIWYG edits locked against AI refine. Six challenges, six named commits.
/ 01 — 06
01 — Async Gemini → 504 timeout
80 s synchronous Gemini calls → nginx 504. Fix: in-memory job queue (f7ad1df, 78476c4). POST returns jobId, client polls every 1.5 s with Cancel + shimmer + bytes.
02 — Hallucinated prices + silent PL→EN drift
4 prompt-guardrail layers in src/lib/gemini.ts (f7ad1df, 5deaaab) — SACRED NUMBERS regex, language hard lock, completeness check, data-user-edited hard lock.
03 — Puppeteer PDF matching the iframe exactly
Hardcoded 1280 px viewport ≠ iframe's real width → pagebreaks mid-table. Fix (cc19b83, 6edb917): measure actual .page width, set viewport dynamically, @page { margin: 0 } + pageRanges: '1'.
04 — WYSIWYG inline edit + lock against refine
inline-edit.ts (~250 lines, 5deaaab) — toggle contenteditable on leaves; diff stamps data-user-edited="1"; refine prompt prepends rule #0 "HARD LOCK".
05 — i18n middleware with translated EN paths
PL bez prefiksu (/uslugi), EN with prefix + translated segments (/en/services). Custom middleware in src/middleware.ts — Accept-Language detection, static-map path rewriting, hreflang.
06 — AOS scroll vs SSR opacity trap
AOS CSS sets opacity: 0 until scroll fires — never fires on SSR. Fix: override [data-aos] { opacity: 1 !important; transform: none !important; }. Instant content beats clever entrance.

/ Stack
Next.js 15 + Payload CMS v3 + Postgres + Gemini Flash + Puppeteer + self-hosted Twenty CRM. One PM2 fork, one nginx, one Mikrus VPS.
/ Tech
Next.js 15 + React 19
App Router. Server Actions cut API ceremony. Route groups split (frontend) and (payload).
Payload CMS v3 (headless)
Code-as-config, TS-native, Postgres adapter. Local API → custom routes share admin auth.
PostgreSQL via Payload adapter
One Postgres for collections + Offers + form submissions + audit log.
Google Gemini Flash
Streaming via generateContentStream. Speed for offer generation.
Puppeteer v24 (dynamic viewport)
Renders actual iframe HTML at iframe's real width — pixel-for-pixel.
Self-hosted Twenty CRM
Docker Compose. Own data, custom fields aligned with agency pipeline, zero per-seat cost.
PM2 + nginx + Certbot
Single PM2 fork enough. In-memory job queue lives in that one process.
/ Results
5× speedup on offer generation. Zero hallucinations across 50+ shipped offers. 100% PL+EN coverage for 10 services + 6 industries + case studies + blog.
/ Numbers
15–25 s per offer
Vs 80+ s baseline — 5× speedup.
0 hallucinations
Across 50+ shipped offers — SACRED NUMBERS regex catches every drift.
100% PL+EN
Locale coverage for 10 services + 6 industries + case studies + blog.
0 timeouts
Async job queue + GC after 15 min — salesperson never sees a 504.










