# Small Business Website Spec

> Planning reference for rebuilding a small business website: page structure, copy voice, estimator widget, SEO requirements, form handling, DNS migration, and the no-code vs static-framework decision (Base44 vs Astro/Vercel vs WordPress). NOT a deploy tool for no-code platforms — no-code builders have no agent-accessible CLI. Includes the honest reasons we pivoted from no-code to a static framework.

- Skill: `hewi333/small-business-website-spec` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add hewi333/small-business-website-spec`
- Raw SKILL.md: https://api.skillmd.com/api/skills/hewi333/small-business-website-spec/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Marketing & Growth
- License: MIT
- Author: hewi333 (https://skillmd.com/u/hewi333)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/hewi333/small-business-website-spec

---


# Small Business Website Spec

> ⚠️ **This is a planning/specification reference, not a deployable skill for
> no-code platforms.** Base44 and similar builders have no CLI the agent can
> call — building there means dragging pages in a web editor, done by a human.
> A static framework (Astro/Hugo/Eleventy) on Vercel/Netlify/Cloudflare Pages
> *is* agent-workable: the agent can clone the repo, edit, run checks, and push.
> Either way, this skill is the spec: page structure, copy voice, estimator
> widget, SEO checklist, form handling, and the DNS cutover runbook.

## When to Use
- Planning a website rebuild for a small service business
- Choosing between no-code (Base44, Wix Studio, Squarespace) and a static framework (Astro on Vercel)
- Page structure, copy voice, estimator widget, SEO requirements
- DNS migration notes (registrar → new host)
- Wiring contact-form submissions into the agent (email → IMAP polling → CRM → Telegram)
- NOT for automated no-code deployment — no agent-accessible CLI exists there

## Platform Decision: No-Code vs Static Framework

Both paths are valid. Pick by workload, not by hype:

| Factor | No-code (Base44 etc.) | Static framework (Astro/Vercel etc.) |
|---|---|---|
| Who builds it | Human, in a web editor (drag-and-drop) | Human or AI coding agent, in a repo |
| Time to first draft | Hours | Days |
| Monthly cost | ~$50/mo for domain connection + GitHub sync on Builder-tier plans | $0 on Vercel/Netlify free tiers (or an existing plan) |
| SEO | Prerendering exists but is often a black box you can't control | Static HTML — fully crawlable, you own everything |
| Export/exit | Frontend only; backend (auth, DB, entities) stays on the platform SDK — you can't self-host by exporting | It's your repo. Portable by definition. |
| Site types that fit | Interactive apps, member areas, anything needing a real backend | Marketing sites: pages + one estimator widget + one contact form |
| Mutability | A living app on their platform — platform updates can change rendering without you touching anything | An immutable build artifact on CDN edge — it can't break while untouched |
| Agent's role | Paying for the subscription (via stripe-link-cli) and writing the spec — not the build | Full repo work: edit, test, verify, push, deploy |

**How to choose:** if the site needs user accounts, database writes, or complex
app behavior, no-code (or a real framework with a backend) earns its subscription.
If it's a marketing site — pages, photos, an estimator widget, a contact form —
a static build is cheaper, faster to load, better for SEO, and fully agent-workable.

We started on Base44 (for a hackathon "the agent bought the subscription" beat,
which is what `stripe-link-cli` is for), then pivoted to Astro-on-Vercel for the
production site because a static marketing site had zero backend needs. The
pivot itself was cheap **because the spec (this document) was written before
either build** — page inventory, copy voice, estimator logic, and SEO checklist
all carried over unchanged.

## Prerequisites
- Domain at a registrar (GoDaddy or similar) — keep DNS there, repoint records
- Static path: git repo, framework (Astro recommended), hosting account (Vercel/Netlify)
- No-code path: builder account, manual signup + build in the web interface
- Form delivery: an email API (Resend/Postmark/SES) or SMTP — forms should email the owner's inbox
- Payment for no-code subscriptions via the `stripe-link-cli` skill (agent handles the spend, human handles the build)

## Website Architecture

### Pages needed (marketing site for a service business)
1. **Home** — hero image, value proposition, primary CTA ("Get Free Estimate"), estimator above the fold
2. **Service pages** — one per service line (built from a template route; each names the reader's exact situation in the first two sentences)
3. **Service area / location pages** — one per market city (template route, real local copy, not doorway spam)
4. **Realtors & property managers** — the B2B repeat-revenue page, if referrals drive the business
5. **About / Why Us** — owner story, process steps, trust signals, real photos
6. **Gallery** — real job photos, captioned
7. **FAQ** — include a "what makes you different from everyone else?" answer
8. **Contact** — form with estimator pre-fill flow
9. **Reviews** — embedded review widget (or hold back until the widget is wired)
10. **Thank you** — `noindex`, conversion event fires here
11. **Terms** — warranty/terms as a proper page (not a PDF scan)
12. **404**, plus `robots.txt`, `llms.txt` (for AI/agentic search), `sitemap-index.xml`

### Estimator tool integration
The estimator is the conversion hook AND the differentiator. Pricing logic
lives in ONE place (see `estimator-engine` skill; a `rate-card.json` both the
website widget and the agent read is the cleanest split).
- Input: square footage, region tier, property type (residential/commercial/apartment), add-ons
- Output: a **±15% range, never a single number**, never internal line-item math
- Flow: result → one tap → contact form pre-filled with all inputs + quoted range
- Mobile-first: thumb-reachable controls, ≤60 seconds, ≤8 taps
- **The website widget, the agent's chat estimator, and the rate-card file
  must all read from the same rate card.** If a price changes, change the rate
  card first; both consumers pick it up. Canonical source: `estimator-engine`.

### Contact form handler
- Serverless function (or the host's form feature) receives the POST
- Email API (Resend etc.) delivers to the **owner's inbox** — reply-to set to
  the submitter so the owner can hit Reply
- Honeypot spam field (hidden "company" input — if filled, reject silently)
- Validate: required core fields, plus "at least one of phone or email"
- Carrying estimator data into the form? Keep it as structured fields in the
  submission email — the agent's inbox monitor parses them (see below)
- Optional webhook to the agent — but see "Lead intake" before exposing one

### Lead intake (agent integration)
Two ways to get form submissions to the agent:

**A. IMAP polling (recommended default).** The form emails the owner's inbox
with a stable subject prefix (`New website lead: ...`) and a structured
plain-text body. A `no_agent=True` cron script polls the inbox every 3 min,
parses the fields, logs to CRM, and texts the owner's Telegram. Latency ~3 min
— irrelevant for a few leads a week. No public endpoint, nothing to attack.
Full pattern: `grasshopper-voicemail-monitor` skill and its notification-email
reference.

**B. Webhook (faster, more surface).** The form handler POSTs straight to an
agent endpoint. You get instant delivery but you must expose, secure, rate-
limit, and monitor a public URL on the agent box. Only worth it at volume.

Choose A unless leads/minute is a real metric for you. The webhook code can
stay in the form handler, dormant, if you want the option open.

## Content guide

### Copy voice
- **Premium-because-[your real differentiator]** — never "affordable,"
  "budget-friendly," "cheap" (they attract price-shoppers, not your buyers)
- Lead with the outcome (it works, it's guaranteed, it's permanent) — not the chemistry
- Name the reader's exact situation in the first 2 sentences of every service page
- Specificity = trust: real cities, real timeframes, real guarantee terms
- ~7th-grade reading level, short sentences

### Banned-word check
Add a verify-script check for cheap-sounding words: *affordable,
budget-friendly, cheap, competitive pricing, "we work with many budgets,"
best-in-class, world-class, "look no further."* Ban list lives in the repo and
runs in CI — banned words slip back in with every copy edit otherwise.

### Guarantee
If you advertise a guarantee, state its exceptions wherever the guarantee
appears — exceptions buried on a distant page erode trust and invite disputes.

## Common Pitfalls

- **Contact-form cutover risk:** the OLD site's form probably works and goes
  to the owner's inbox. Do NOT repoint DNS until the new form is **verified
  delivering to that same inbox** on a staging domain. Breaking a working
  lead path is worse than keeping the old site live another week.
- **DNS propagation:** 24-48h. Save old DNS values for instant rollback.
- **Email routing:** changing A/CNAME records is fine, but **MX records stay
  pointed at the email host** (e.g. Workspace: `imap.secureserver.net` /
  `smtpout.secureserver.net`). Repointing MX silently breaks the business's email.
- **Mobile-first:** most service-business leads come from phones. Test at
  320px, 375px, 414px widths.
- **301 redirects:** map old (especially WordPress) URLs → new pages before
  cutover, or years of accumulated SEO equity 404s overnight.
- **Banned words:** enforce via the verify script, not vigilance.
- **Terms & conditions:** a scanned-image PDF has no extractable text —
  transcribe it into a real page.
- **Stock photos:** verify licensing before a live launch; "we scraped them
  from our old site" is not a license.
- **Browserless agent box:** no-code checkout and Lighthouse audits need a
  browser. The agent can't drive a web UI without Chromium
  (`npx playwright install --with-deps chromium`); run audits via web tools.
- **Region classification sync:** if you update region tiers in
  `estimator-engine`, update the estimator widget options AND the rate-card
  file. All three must match. Canonical source: `estimator-engine`.
- **No-code export is a one-way door:** the frontend exports; the backend
  doesn't. If you start on no-code, treat it as a prototype — don't wire
  business logic (auth, DB, entities) into it that you'll have to rebuild.
- **Agent pushes to `main`:** for a 1-2 person shop, direct-push with a
  quality gate (tests + verify script) is faster than PR review. Whatever
  you choose, make it a written convention — agents follow conventions,
  they don't infer them.
- **GitHub PATs expire:** if the clone URL embeds a PAT, pushes fail
  mysteriously ~30 days later. Check the token's expiry when setting up,
  and note the `git remote set-url` fix before it bites.

## DNS Configuration (registrar → Vercel example)

```
Type: CNAME
Name: www
Value: cname.vercel-dns.com

Type: A
Name: @
Value: 76.76.21.21 (Vercel default)
```
(No-code hosts give you their own CNAME/A targets — same idea.)

**Do NOT touch MX records** — email stays with the email host.

Cutover order:
1. New contact form verified delivering to the owner's inbox (staging domain)
2. Owner sign-off on content + design
3. THEN repoint DNS
4. Old DNS values saved for instant rollback

## Verification Checklist

### Quality gate (automated — run before every push)
- [ ] Every page renders static HTML with copy in the raw HTML (no client-only content)
- [ ] Unique title (≤60 chars) + meta description (≤155 chars) per indexable page
- [ ] Exactly one H1 + substantial body copy per page
- [ ] Zero banned words (enforced by script)
- [ ] Estimator output matches the agent's chat estimator (same rate card, test N quotes)
- [ ] Guarantee caveats reachable from every page mentioning "guarantee"
- [ ] Structured data (LocalBusiness, Service, FAQPage) + tap-to-call on every page
- [ ] robots.txt, llms.txt, sitemap-index.xml present; /thank-you is noindex
- [ ] Unit tests pass (pricing engine + form validation)

### Pre-cutover (manual)
- [ ] Owner sign-off on the site
- [ ] Form submissions verified delivering to the owner's inbox on the real domain
- [ ] Schema.org validates (Google Rich Results Test)
- [ ] Lighthouse mobile: Performance ≥90, SEO ≥95, Accessibility ≥90
- [ ] Real photos on the site (machines/equipment/jobs, not stock)

### Post-cutover
- [ ] DNS propagated (`dig yourdomain.com`) + HTTPS working
- [ ] 301 redirects from old URLs live
- [ ] Email MX records preserved (don't break email)
- [ ] Search Console + Bing Webmaster verified; sitemap submitted
- [ ] Analytics events firing (estimator_start, estimator_complete, form_submit, call_click)
- [ ] Mobile responsive verified on real devices
- [ ] Lead-intake monitor (if used) picking up form submissions → CRM → Telegram
