# Setup — Values To Fill In

This website is a **generic template**. Every `[[PLACEHOLDER]]` below must be replaced with your real information before the site goes live. The publish script will **refuse to deploy** while any `[[...]]` token is still in the files, so nothing half-finished can go out by accident.

**Easiest way:** open this folder in VS Code, start Claude, and say
> *"Read the setup file and help me fill everything in."*

Claude asks you for each value and puts it in the right place. You never edit code by hand.

---

## What you're setting up (the 2 pages)

Your site ships with two landing pages, all feeding the same lead form:

| Page | URL | Use it for |
|---|---|---|
| **Home** | `/` | Your main long-form page — the full story, then the quiz. |
| **Cash Offer** | `/cash-offer` | A quiz-first lander built for **paid ads** (jumps to the questions fast). |

Plus the standard pages: About, Contact, Privacy, Terms, DMCA, a thank-you page (`/offer-confirmation`), and a 404. There is **no** standalone `/offer` page.

---

## 1. Publishing settings — `deploy.sh` (required)

Open `deploy.sh` and set three values:

| Placeholder | Replace with |
|---|---|
| `REPLACE_WITH_PAGES_PROJECT_NAME` | Your Cloudflare Pages project name (lowercase, e.g. `go-yourdomain`) |
| `REPLACE_WITH_CLOUDFLARE_ACCOUNT_ID` | Your Cloudflare Account ID (dashboard right sidebar) |
| `REPLACE_WITH_CLOUDFLARE_API_TOKEN` | Your Cloudflare API token (My Profile → API Tokens → Create; the only permission it needs is **Cloudflare Pages: Edit** on your account) |

That's all `deploy.sh` needs. **Your form settings do NOT go here** — they live in the Cloudflare dashboard (next step), so they stay put every time you publish.

> ⚠️ Once the token is in it, treat this folder like a password: never email it, upload it, or share it. Anyone holding `deploy.sh` can publish to your site.

**If the Pages project doesn't exist yet** (fresh Cloudflare account): run `bash deploy.sh` once first — wrangler offers to create the project on the first deploy (say yes). Then add the settings in #2/#3 and publish once more so they take effect. Don't send traffic until the smoke test in #9 passes.

## 2. Form settings — in the Cloudflare dashboard (NOT a file)

In your Pages project → **Settings → Variables and Secrets** (scope: Production), add:

| Variable | Value |
|---|---|
| `ZAPIER_WEBHOOK_URL` | Your CRM's inbound webhook (Zapier, GoHighLevel, Make — anything that accepts a webhook). Every lead is POSTed here. |
| `CLIENT_EMAIL` | The email address that should receive lead-failure alerts. |
| `RESEND_API_KEY` | Optional — only if you want failure-alert emails (see #7). Add it as a **Secret** (encrypted), not plain text. Leave unset otherwise. |
| `EXTRA_WEBHOOK_URLS` | Optional — additional webhook(s) that should ALSO receive every lead (comma-separated), e.g. a second CRM or a Google Sheets hook. Leave unset otherwise. |

> These take effect on your **next publish** — after adding or changing any of them, run `bash deploy.sh` once.

## 3. Lead backup database — Cloudflare D1 (REQUIRED)

A backup database that saves **every lead**, even if your CRM ever misses one. This is a **required, non-negotiable** part of the setup — it's the safety net that means a lead can never be lost, and the smoke test in #9 **fails until it's in place**. Do not skip it or remove it later.

Create a D1 database (dashboard → **Storage & Databases → D1**), bind it to your Pages project as `db` (Pages project → **Settings → Bindings → Add → D1 database**), then run this once in the D1 console:

```sql
CREATE TABLE IF NOT EXISTS submissions (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  created_at TEXT DEFAULT (datetime('now')),
  address TEXT, property_type TEXT, owns_land TEXT, listed TEXT,
  condition TEXT, why_selling TEXT, timeline TEXT, asking_price TEXT,
  first_name TEXT, last_name TEXT, email TEXT, phone TEXT,
  utm_source TEXT, utm_medium TEXT, utm_campaign TEXT, utm_content TEXT,
  utm_term TEXT, fbclid TEXT, gclid TEXT, page_url TEXT, referrer TEXT,
  sms_consent TEXT
);
```

Like the settings in #2, the binding takes effect on your next publish — run `bash deploy.sh` once after binding. If the site ever runs without this binding, it is misconfigured: no backup copy is stored, the smoke test fails, and (if Resend is on) you get an alert email on every lead until it's fixed.

## 4. Your business info — find-and-replace the `[[TOKENS]]` (required)

**First, choose your brand style** (tell Claude which one before filling anything in):

- **Personal brand** (how the template ships): the Home and Cash-Offer pages feature YOU — "Hi, I'm [Name]", your photo, "[Name] personally reviews every request." Strongest for trust; needs your headshot.
- **Company brand**: no individual named or pictured — the pages speak as "we" / your company name. Say *"set this site up as a company brand"* and Claude applies the swap (the exact edits live in `CLAUDE.md` under **Brand mode**). You provide a team/office/house photo instead of a headshot, and you can skip `[[OWNER_FIRST_NAME]]` below. Your name still appears once on the Privacy/Terms/DMCA pages as the legal contact — that's normal and stays in both modes.

Search the whole `index/` folder for each token and replace every occurrence (in VS Code: **Edit → Replace in Files**, or just let Claude do it):

| Token | Replace with | Example |
|---|---|---|
| `[[COMPANY_NAME]]` | Your business name as displayed | We Buy Houses Fast |
| `[[OWNER_NAME]]` | The owner's full name (in company mode: legal pages only) | John Smith |
| `[[OWNER_FIRST_NAME]]` | The owner's first name (unused in company mode) | John |
| `[[CONTACT_EMAIL]]` | Your public contact email | offers@yourdomain.com |
| `[[GOOGLE_MAPS_KEY]]` | Your Google Maps API key (see #6) | AIzaSy... |
| `[[BUSINESS_STREET]]` | Your mailing street address (privacy/terms/dmca) | 123 Main St #100 |
| `[[BUSINESS_CITY_STATE_ZIP]]` | City, state ZIP (privacy/terms/dmca) | Charlotte, NC 28217 |
| `[[GOVERNING_STATE]]` | The state whose law governs your Terms | North Carolina |
| `[[GOVERNING_COUNTY]]` | The county for the Terms legal-venue clause | Mecklenburg |

Then read the **Home** and **Cash Offer** pages top-to-bottom once: testimonials, claims ("nationwide", "1,000s of homes bought", "5-Star Rated"), and any region wording. Keep only what is **true** for you, and localize the copy if you buy in one area ("We Buy Houses in {Region}...").

**Do not claim you are a licensed real estate agent** unless you are. The template default trust badge is **Licensed & Insured Title Closings** (the title company that handles closing), not that *you* are licensed and insured. Title-company language elsewhere is correct to keep.

**Vacant land:** by default the quiz politely declines vacant-land properties (selecting "Vacant Land" ends the quiz with a "we're not able to make offers on vacant land" message, on both landers). If you DO buy vacant land, ask Claude to remove the vacant-land disqualification from both landers.

## 5. Images — `index/images/`

You provide two images; the rest are included.

| File | What it is |
|---|---|
| `logo.png` | Your logo (header; ~500px wide works) — **you add this** |
| `owner-headshot.png` | A square headshot (used in the "You'll work with…" blocks) — **you add this** (personal brand mode) |
| `company-photo.jpg` | Only if you chose **company brand** mode in #4: a team photo, office shot, or a nice house you bought — replaces the headshot slots |
| `hero-bg.jpg` | The faint photo behind the Home page hero (any wide house/neighborhood photo works) — **you add this** |
| `favicon.svg`, `step-1-form.png`, `step-3-offer.png`, `step-6-cash.png` | Included — replace the step images if you have your own |

The publish script checks that every image the pages reference actually exists, so a missing file stops the deploy instead of going live broken.

The **About** page ships without a family photo. If you'd like one, add `index/images/owner-family.jpg` and ask Claude to place it on the About page. Also rewrite the About bio in your own words.

## 6. Google Maps key — required for the address box

- Token: `[[GOOGLE_MAPS_KEY]]` (appears in the Home and Cash-Offer pages — one find-and-replace covers both).
- In **Google Cloud Console**, enable **Maps JavaScript API**, **Places API**, and **Street View Static API**, then create an API key.
- **Billing must be enabled** on the Google Cloud project (Billing → link a card) or the key returns errors. Normal form traffic stays well inside Google's free monthly credit, so this typically costs nothing — but the card has to be on file.
- **Restrict the key to your domain**: edit the key → Application restrictions → **HTTP referrers** → add `https://yourdomain.com/*` and `https://www.yourdomain.com/*` (include the `https://`). Then under **API restrictions**, limit the key to just those three APIs. The key is visible in page source by design, so these restrictions are what protect it from being used on someone else's site and billed to you.
- Until a key is added, the address box still works as a plain text field and the form still submits — it just has no autocomplete or house-photo preview.

## 7. Resend — optional failure-alert email

Only if you want an email when a lead fails to reach your CRM. The alert contains the lead's full contact details, and the lead is also saved in your backup database (#3) — the email is what tells you to go follow up instead of waiting to notice the CRM gap. Requires all three, or the emails won't send:

1. `RESEND_API_KEY` as a project environment variable (#2).
2. `functions/submit.js` → `from: 'alerts@yourdomain.com'` → change to an address on a domain **verified in your Resend account**.
3. `functions/submit.js` → `recipients` (currently empty) → add your notify email, or rely on `CLIENT_EMAIL` from #2.

If you skip Resend, nothing breaks — the failure email is simply never sent.

## 7b. SMS texting & A2P registration (already built in — don't remove)

If you register your business phone number for texting (called A2P 10DLC — your CRM, e.g. Follow Up Boss, walks you through it), the carriers will review this website. The template already includes everything they check for:

- A **required** **"I agree to be contacted…" checkbox** on the contact step of both landers. It is **unchecked by default**. The user must check it to submit; unchecked submit is blocked with an error. Every successful lead sends `sms_consent: "yes"`.
- **TCPA/SMS sections** on the Privacy and Terms pages (including the "mobile information is never shared with third parties" statement carriers look for).

Do not remove the checkbox or these sections, and do not pre-check the box. Do not make the checkbox optional again unless a carrier specifically rejects a required opt-in for your registration (rare — ask before changing). When registering, give the reviewer your homepage URL.

**In your CRM:** every lead from the form arrives with `sms_consent: "yes"`. Still configure CRM automations to **text only when `sms_consent` is `yes`** (never text on a `no` or missing value from other sources). Calling and emailing about the property inquiry is a separate business policy. Keep consent evidence (timestamp, page) in case a carrier asks.

**D1 backup:** the `submissions` table must include a `sms_consent` column. New setups already do. If you are rebuilding an older site onto this template and D1 returns errors like `no column named sms_consent`, run once: `ALTER TABLE submissions ADD COLUMN sms_consent TEXT;`

## 8. Ad tracking — Facebook Pixel, Google Ads, Microsoft Clarity (optional)

This package ships with **no** pixel, ads tag, conversion label, or analytics snippet. If you run ads or want session recordings, give Claude your own IDs and ask it to add the official snippets. (Standard placement: pixel on Home / Cash-Offer + the thank-you page, with the `Lead` event firing on the thank-you page only so junk leads don't count.)

---

## 9. Preview, publish, and verify

**Preview locally first** (serves the site the way Cloudflare does):
```bash
wrangler pages dev index
```
Open the printed `http://127.0.0.1:8788`, and check both landers (Home + Cash-Offer), the logo/favicon, the quiz, and that the consent box blocks submit when unchecked. (Local previews don't load your live form settings, so a local submit looks successful without actually delivering — use it for looks/flow only.)

**Publish:**
```bash
bash deploy.sh
```
(It refuses to run while any `[[TOKEN]]` remains — that's your safety net.)

**Verify after publishing** (replace the domain):
1. Home and `/cash-offer` all load on desktop AND a phone (hard-refresh). Unchecked consent blocks submit; checked consent submits.
2. The address box shows Google suggestions (if not: check the Maps key + its domain restriction).
3. `curl -X POST -H "Content-Type: application/json" -d '{}' https://yourdomain.com/submit` returns a 400 "Missing required fields" (the form's validation is alive).
4. Run the smoke test: `bash test-form.sh yourdomain.com` — it sends ONE real TEST lead and prints **PASS or FAIL for each delivery leg** (CRM webhook + backup database). Don't send traffic until it passes. Clean up that test lead in your CRM (and the D1 row) afterward.

---

## Find anything still unfilled

Search the whole folder for `[[` — if any result shows up, it still needs a value. When `[[` returns nothing and `deploy.sh` has no `REPLACE_WITH_` left, your template is fully filled in.

See **START HERE - Setup and Workflow.md** for installing your tools and the everyday edit/publish routine.
