# Project Instructions for This Website

You are helping the owner of this website make edits and publish them. The owner is **not** a developer. Explain things plainly, make the change they ask for, show them what you changed, and publish when they ask.

> **First run on a fresh copy:** this is a generic template full of `[[PLACEHOLDER]]` tokens (`[[COMPANY_NAME]]`, `[[OWNER_NAME]]`, `[[GOOGLE_MAPS_KEY]]`, etc.) plus `REPLACE_WITH_...` values in `deploy.sh`. Before the site is usable these must be filled in. See **`SETUP - fill these in.md`** for the full checklist, and offer to walk the owner through it. The publish script refuses to deploy while any `[[TOKEN]]` remains.

## What this site is

A lead-capture website for a real estate home-buying business. It has **two landing pages**, all feeding one lead form:

| Page | File | Purpose |
|---|---|---|
| Home (`/`) | `index/index.html` | Main long-form page → quiz. |
| Cash Offer (`/cash-offer`) | `index/cash-offer/index.html` | Quiz-first lander for paid ads. |

Plus `about`, `contact`, `privacy`, `terms`, `dmca`, `offer-confirmation` (the thank-you page), and `404`. There is **no** standalone `/offer` page.

| Part | What it is |
|---|---|
| `index/` | The static website: plain HTML, CSS, images. |
| `functions/submit.js` | A Cloudflare Pages Function that handles the form. On submit it (1) saves the lead to the Cloudflare D1 backup database (a **required** binding), (2) sends it to the owner's CRM webhook, and (3) optionally emails a failure alert via Resend if the CRM delivery fails. |
| `deploy.sh` | The publish script. It runs `wrangler pages deploy` (it writes **no** `wrangler.toml` — form settings live at the Pages project level, so they persist across deploys). |

The site is hosted on the **owner's own Cloudflare account**.

## Making content edits

The owner describes changes in plain English. Edit the HTML in `index/` and its subfolders.

- **The two landers share copy but are separate files.** If the owner says "change the headline" ask *which page* (Home or Cash-Offer), or update both if they mean site-wide. The **phone number** appears as visible text and `tel:` links across pages — when changing it, update **every** occurrence on every page.
- **Business name, headlines, body copy, addresses**: edit the relevant page(s).
- **Do not** change the form's field names, the quiz step logic, or `functions/submit.js` internals. They're wired to the CRM and database. The form posts to `/submit` with these keys: `address, property_type, owns_land, listed, condition, why_selling, timeline, asking_price, first_name, last_name, email, phone` (+ tracking + `sms_consent`). Required: `first_name`, `email`, `phone`. The contact step's "I agree to be contacted…" checkbox is a **required** SMS/TCPA opt-in: unchecked by default, blocks submit until checked, and successful leads send `sms_consent: "yes"`. Never pre-check it, never remove it, never make it optional again unless the owner has a carrier-specific reason. CRM workflows must **text only** when `sms_consent = yes`.
- **Licensing claims:** never describe the buyer/investor/company as "Licensed & Insured" unless that specific owner has substantiated it. Default trust badge is **Licensed & Insured Title Closings** (title company). Title-company language elsewhere is fine. FAQ must not say "We're a licensed, insured company."
- After editing, briefly tell the owner what you changed. Do not publish until they ask.
- Before a large or structural change, offer to make a dated backup copy of `index/` first.

## The `[[TOKEN]]` system

Every client-specific value is a `[[TOKEN]]`. To fill one in, do a find-and-replace across `index/`:
`[[COMPANY_NAME]]`, `[[OWNER_NAME]]`, `[[OWNER_FIRST_NAME]]`, `[[CONTACT_EMAIL]]`, `[[GOOGLE_MAPS_KEY]]`, `[[BUSINESS_STREET]]`, `[[BUSINESS_CITY_STATE_ZIP]]`, `[[GOVERNING_STATE]]`, `[[GOVERNING_COUNTY]]`. Full table in `SETUP - fill these in.md`. If the owner asks to publish while tokens remain — or while any image the pages reference (logo, headshot/company photo, hero background) is missing from `index/images/` — `deploy.sh` will stop you. Fill/add them first.

## Brand mode: personal or company

The template ships in **personal brand** mode: the Home and Cash-Offer pages feature the owner by name and photo ("Hi, I'm [Name]", a founder section, "[Name] personally reviews every request"). Some owners prefer a **company brand** — no individual named or pictured. **During first setup, before filling the tokens, ask the owner which they want.**

- **Personal (default):** change nothing. The owner provides `index/images/owner-headshot.png` and fills `[[OWNER_NAME]]` / `[[OWNER_FIRST_NAME]]` like any other token.
- **Company:** apply the exact edits below (each spot is flagged in the HTML with a `<!-- brand-mode: ... -->` comment). The owner provides `index/images/company-photo.jpg` (a team photo, office shot, or a nice house they bought) instead of a headshot.

### Company-mode edits

**`index/index.html` (Home):**

1. **Start Fresh section** — eyebrow `Hi, I'm [[OWNER_FIRST_NAME]]` → `We're Here To Help`; the paragraph `I've helped thousands of homeowners` → `We've helped thousands of homeowners`; the photo `<img src="images/owner-headshot.png" alt="[[OWNER_NAME]]">` → `<img src="images/company-photo.jpg" alt="[[COMPANY_NAME]]">`.
2. **Conviction band (testimonial)** — `[[OWNER_FIRST_NAME]] took it off my hands` → `[[COMPANY_NAME]] took it off my hands`.
3. **Founder section** — keep the section and layout, replace its content:
   - photo → `<img src="images/company-photo.jpg" alt="[[COMPANY_NAME]]">`
   - eyebrow `Meet The Buyer` → `Who You're Working With`
   - `<h2>Hi, I'm [[OWNER_NAME]]</h2>` → `<h2>We're [[COMPANY_NAME]]</h2>`
   - both paragraphs: switch to first-person plural (`I've been buying` → `We've been buying`, `I got into this business because I watched people I care about` → `We got into this business after watching people we care about`, `I knew there had to be a better way, so I built one. Since then I've helped` → `We knew there had to be a better way, so we built one. Since then we've helped`, `I'd like to help you too` → `we'd like to help you too`).
   - signature `— [[OWNER_NAME]], Founder &amp; Cash Home Buyer` → `— The [[COMPANY_NAME]] Team`
4. **Quiz contact step** — in the `lf-founder` block, delete the `<img>` line and change the span to `<span>Our team personally reviews every request.</span>`.

**`index/cash-offer/index.html` (Cash Offer):**

5. **Founder chip above the quiz card** — delete the whole `<div class="founder">…</div>` block (the trust indicators above it stay).
6. **Quiz contact step** — same `lf-founder` edit as #4.

Remove the `<!-- brand-mode: ... -->` comments as you apply each edit. About, Contact, and the thank-you page have no personal branding — no changes there.

### After applying company mode, verify

- `[[OWNER_FIRST_NAME]]` appears **nowhere** in `index/` (the token becomes unused — skip it in the token fill).
- `owner-headshot.png` is referenced **nowhere**; `company-photo.jpg` is referenced exactly twice (both on Home).
- `[[OWNER_NAME]]` remains **only** on the Privacy, Terms, and DMCA pages — that's the legal contact person and stays in both modes (it should be a real person's name, which is normal and required practice on legal pages; it is not marketing).
- The phrase `personally reviews every request` now reads `Our team personally reviews every request.` on both quiz contact steps.

## Publishing (deploying)

When the owner says "publish" or "deploy," run:
```
bash deploy.sh
```
Then confirm success and that the change is live (usually within a minute). Requirements: Node.js LTS and the global `wrangler`. If `wrangler` errors, have the owner run `npm install -g wrangler`, then retry.

## Where the form settings live (NOT in files)

These are set once in the Cloudflare dashboard (Pages project → **Settings → Variables and Secrets**), so they survive every deploy. If the owner needs to change one, direct them there — don't try to put them in a file. Changes take effect on the **next publish**, so run `bash deploy.sh` once after any dashboard change.

- `ZAPIER_WEBHOOK_URL` — the CRM endpoint every lead is sent to (any webhook-accepting CRM: LeadConnector, Zapier, Make, or a native CRM hook).
- `CLIENT_EMAIL` — who gets the failure-alert email.
- `RESEND_API_KEY` — optional; enables the failure-alert email. Store it as a **Secret** (encrypted), not plain text.
- `EXTRA_WEBHOOK_URLS` — optional; comma-separated extra webhooks that also receive every lead.

## The owner's own keys: where each goes

### Google Maps key (required for the address feature)
- It's the `[[GOOGLE_MAPS_KEY]]` token in the Home and Cash-Offer pages — one find-and-replace covers both.
- After inserting, remind the owner their key must be **restricted to their domain** (HTTP referrer) in Google Cloud Console, with **Maps JavaScript API, Places API, and Street View Static API** enabled. The key is visible in page source by design; the domain restriction is what protects it.
- Until a key is added, the address box works as a plain text field. The form still submits.

### Resend (optional, for lead-failure email alerts)
**What it's for:** this email is a **failure alarm**. The function fires it **only when a lead fails to reach the CRM** — it is not the normal "you got a lead" notice. The alert contains the lead's full contact details, and if a D1 backup is bound the lead is saved there too; the alarm is what tells a human to follow up, instead of relying on the owner to notice that (for example) Facebook reported a lead their CRM never received. If the owner asks why they'd want it, explain it that way.

Only set this up if the owner asks. It requires three matching changes, or the emails won't send:
1. `RESEND_API_KEY` as a project Secret (dashboard, above).
2. `functions/submit.js` → the `from:` address → an address on a domain **verified in the owner's Resend account** (for example `alerts@theirdomain.com`).
3. `functions/submit.js` → the `recipients` array (currently empty) → the owner's notify email, or rely on `CLIENT_EMAIL`.
If no key is set, the function detects it and skips the email. Leads still reach the CRM and D1.

### Ad tracking (optional)
This package ships with no Meta Pixel, Google Ads tag, conversion label, or analytics. If the owner runs ads, ask for their exact IDs and add the official snippets (pixel on the landers + thank-you page; fire `Lead` on the thank-you page only).

## Retrieving leads from the backup database

Every lead is written to the **`submissions`** table in the site's D1 backup database (a required binding — see `SETUP - fill these in.md` #3), independent of the CRM. To pull recent leads:
```
wrangler d1 execute <your-database-name> --command "SELECT * FROM submissions ORDER BY created_at DESC LIMIT 25"
```
The owner can also view them in the Cloudflare dashboard under **Storage & Databases → D1**. If leads aren't landing there, the `db` binding is missing — that's a required setup step (the smoke test fails without it); help the owner re-do SETUP #3.

## Guardrails

- **The D1 backup database is required — never remove it.** Don't unbind `db`, drop the `submissions` table, or edit the site to work without the backup, even if asked to "simplify." It's the safety net that guarantees no lead is ever lost; the smoke test fails without it.
- Never paste any API key into a public place, a screenshot, or anything shared outside the owner's computer.
- Always show the owner what changed before publishing.
- If a request would break the lead form or the database connection, say so and propose a safe alternative.
- Known deploy gotcha: use the global `wrangler` and Node.js LTS. If deploy fails on a "version" error, that's usually a too-new Node — have them use Node LTS.
