← All projects
Live Updated: Invalid Date

The Website Factory

The Website Factory is an end-to-end system that turns small Romanian businesses with no web presence into clients. It starts from public discovery of businesses in open registries (OpenStreetMap via Overpass) and Google Maps, queried per city and per CAEN activity code, computes a need · reachability score from 0 to 100 based on the digital gap, then generates a per-prospect site mockup hosted on a preview URL. Delivery is fixed: 2,000 RON paid in full at signing, site live in 7 days.

The shipped product is built on a bilingual Astro skeleton (Romanian default plus English at /en/) with real i18n routes, a contact form protected by Cloudflare Turnstile, a Decap CMS for self-editing, and hosting on Cloudflare Pages at 0 EUR per month. The discovery, scoring and mockup-render pipeline is written in Python over a DuckDB database.

Read the full case study →

The problem and the motivation

A large number of small Romanian businesses, barbershops, restaurants, cafes, beauty salons, workshops, exist online only through a Facebook page, a profile on a delivery app, or a Google Maps listing. They have no website of their own, no domain, or one that is dead or parked. For them a website is not a technology problem but a problem of time and trust: nobody has shown them what their own site would concretely look like.

The Website Factory starts exactly from this observation. Instead of selling an abstract promise, the system first builds a tangible demonstration: each prospect's own site, rendered from their real data, on a URL they can open immediately. The pitch becomes: your site already exists, it just needs to be switched on.

The architecture and how it works

The system is a multi-step pipeline, each step a separate Python module writing into a single DuckDB database, website.duckdb. The steps are: discovery, enrichment, scoring, mockup render, then a funnel-style CRM for tracking the sale.

Discovery (maps_prospect_scan.py) is hybrid and runs on free lanes. The backbone is OpenStreetMap via the public Overpass API: for each city and each CAEN activity code a query is run against the city's administrative boundary, filtered on OSM tags. The CAEN-to-OSM tag mapping is explicit: 9602 (hairdressing, barber) to shop=beauty|hairdresser, 5610 (restaurants) to amenity=restaurant|fast_food, 5630 (cafes, bars), 9604 (fitness), 4520 (car repair), 5510 (lodging). A second lane, Google Places, is dormant and only activates when a key is present, storing strictly the place_id and the system's own verdict to respect the terms of use. A third lane, a scraping cascade, cross-checks in the enrichment pass whether the business actually has a website.

Scoring (prospect_score.py) is the part that gives the whole effort direction. The final score, 0 to 100, combines two dimensions. Need (score_need) measures the digital gap: no site or a dead site is 1.0, Facebook-only 0.8, a weak site 0.5, a good site 0.0. Reachability (score_reach) measures how easily the business can be contacted and convinced: presence of a phone, buy-signals such as a rating and review count, and a flag for businesses that are on a delivery app but have no site. The formula is score = 100 · need · (0.4 + 0.6 · reach), so need dominates and reachability scales the result. In practice the top of the list is occupied by businesses that most need a site and are easiest to reach.

Generating the mockups

This is the heart of the system, and it went through two generations. The first, mockup_gen.py, asked a free language model to produce a Romanian one-pager from the prospect's data. That approach turned out to generate thin skeletons with invented detail. The second generation, render_mockup.py, replaced the model's improvisation with a deterministic template renderer.

The deterministic renderer fills a real per-vertical template, restaurant, cafe, or barbershop with its own accent, using only fields that were actually harvested. Anything not harvested, photos, the exact menu, precise hours, is rendered as a clearly labelled demonstrative placeholder, never invented and presented as fact. This decision is not cosmetic: it is an honesty guarantee. Real photos, menu and reviews are injected only in a separate asset-harvest step before the mockup is sent.

On top of the content, the script deterministically injects a protection posture that it never delegates to the model: a noindex meta tag so demos never appear in search engines, plus a visible watermark ribbon at the top that clearly states, in Romanian, that this is a demo proposal, an unofficial site built as a demonstration for the owner, with the business name, the date, and a note that it will be deleted on request. Each mockup gets a slug and a path-based preview URL under mockups.popescubogdan.ro. Nothing is published automatically: any deploy stays a manually confirmed decision.

Key technical decisions

The shipped product, the skeleton in SITES/client-template, is built on Astro with static rendering and i18n. The default language is Romanian, with no URL prefix, and English lives under /en/ as real routes, not a JavaScript toggle. Three vertical homepages, brochure, services-with-booking, and shop are wired as true i18n routes, each with its EN counterpart, so per client the right vertical is kept and the rest removed.

The shared layout, Base.astro, deliberately uses a system-font stack with no render-blocking web fonts, to stay Lighthouse-friendly. Per-vertical accent colours are injected via variables, and language switching is route-based. Vertical components receive per-locale strings as props, so a single component serves two routes with no duplication.

For self-editing, the system uses Decap CMS from public/admin/, which edits src/data/site.json (brand, phone, WhatsApp, email, address, headline and subheadline in RO and EN) plus the blog folder. The homepage reads from this JSON, so editing the file followed by a rebuild updates the site, which was verified across all verticals.

The contact form is a Pages Function (functions/api/contact.ts): it verifies the Cloudflare Turnstile token through the siteverify endpoint, then sends the message via an Email Workers binding to a verified address on the client's zone. The form therefore works with no server of its own and no monthly cost.

Anti-fragility, performance, cost and testing

Several decisions are oriented toward robustness. Discovery respects the public lanes: a polite pause between Overpass queries and sequential querying, so the free service is not overloaded. The noindex plus watermark plus owner-notice posture protects the demos, both legally and ethically. The rule of inventing nothing, and explicitly marking every placeholder, keeps the entire output within what can be defended with real data.

On performance, the client skeleton was measured with Lighthouse: a performance score of 0.99, accessibility 0.95 and SEO 1.0 on the homepage, above the internal threshold of 90. The system-font stack and static rendering are the direct cause of these scores.

On cost, the entire product runs on the Cloudflare free tier: Pages for hosting, DNS, SSL and Email Routing for email, Turnstile for anti-bot, all at 0 EUR per month. The discovery and scoring pipeline likewise runs on free lanes and locally. On testing, the skeleton ships build-output QA scripts, qa_dist.py and qa_leak.py, which check route existence, scan the EN visible text for Romanian leaks, and verify footer link resolution. Discovery has a smoke-test gate: one city with two CAEN codes before scaling up.

The outcome and current status

The system has produced about 100 per-prospect mockups for real businesses in Iasi, Cluj-Napoca, Timisoara and Sector 1, each with its own slug and preview URL. The commercial model is simple and fixed: 2,000 RON paid in full at signing, delivery within 7 days from payment and content, a bilingual site always, editable by the client through the CMS, hosted at 0 EUR per month. The sales process is phone-first, owner-led, with a three-touch cadence: a call, the mockup link the same day, a callback at three days and a nudge at seven. In short, The Website Factory is a production line that takes a business with no web presence from a row in a public registry all the way to a demonstrative site it can see before saying yes.

Timeline

  • 10/06/2026 Product decisions locked (32-question matrix); discovery, scoring and the first-generation mockup module written.
  • 13/06/2026 Lighthouse measured on the client skeleton: performance 0.99, accessibility 0.95, SEO 1.0.
  • 14/06/2026 Bilingual Astro client skeleton built: RO plus EN i18n routes, Decap CMS proven on all verticals, contact Pages Function with Turnstile.