zsty.us

Before / After · Case Study

Palisades 1st Choice — a fire-rebuild contractor's brochure turned into the system that runs the business

  • Local Pro Services
  • Real Estate

A contractor rebuilding homes after the Palisades fire had a five-page brochure site where every button led to the same generic contact form. The site still looks exactly like the one families already trust — but now every new lead gets a personal follow-up drafted within hours, and every family gets a private portal showing their rebuild's timeline, documents, and budget.

Same navy-and-periwinkle brochure on the outside. A typed Postgres app with an outreach agent and a homeowner portal on the inside.
🔒palisades1stchoice.vercel.app
jackiej.events — modern site

At a glance

What this rebuild covers.

  • Design

    The five-page brochure grew into a full site — a real project gallery, a dedicated free-consultation flow, and separate forms that actually route inquiries instead of dumping everything into one inbox.

  • Theme & color

    The navy-and-periwinkle palette, the aerial drone photography, and the calm, steady voice were kept exactly as they were — that look was already right for a contractor talking to families who lost everything.

  • Security

    Each family's portal is locked to their own sign-in, and sensitive documents like insurance paperwork and permits live in a private vault that only opens through short-lived links.

  • Agents

    Every new lead gets a personalized introduction drafted automatically within four hours and queued for the owner's one-click approval — and a chat assistant on every page answers rebuild questions using the site's 20 articles.

  • Customer notifications

    When a project moves from permitting to construction, or staff posts a photo update, the homeowner gets an email automatically with a link straight to their portal — no more status-update phone calls.

  • Search visibility

    Twenty in-depth rebuild articles and three city-specific landing pages give the site a real footprint in search, where the old brochure had none.

Receipts — measured

Numbers that moved.

5+
Routes
was 5+
0
DB tables
was 0
0
Blog posts
was 0
0
Background agents
was 0
  • 5 pages → 27+ pages
  • 0 → 20 helpful articles
  • 6 background helpers working around the clock
  • Every family gets a private project portal

The pixel diff

Overview, then the walkthrough.

Left: everything the legacy site shipped — frozen in place. Right: an auto-scrolling tour of the rebuild, from hero to booking. No slider to fight with.

Before — the overview
🔒palisades1stchoice.com
Before — the overview

This is everything the legacy site had to offer above the fold. No pricing, no calendar, no booking — the funnel ended at a mailto.

After — scroll through what shipped
🔒palisades1stchoice.vercel.app
After — scroll through what shipped

Auto-scrolls through the modern site so you can see the booking surface, the package grid, the pricing table, and the calendar inline — without leaving this page.

The four beats

Problem · Insight · Build · Outcome.

Scroll past the legacy frame. The four beats land in order. At the end, the modern site fades into the same window.

🔒palisades1stchoice.com
Legacy
01 · Problem

A WordPress brochure was costing them every lead.

Every 'Get Your Free Consultation' button dumped into a single Fluent Forms endpoint. Phone wasn't tel:. Email wasn't mailto:. No blog meant zero SEO surface. No client portal meant every status update was a phone call. Families in trauma needed structure; the site offered a contact form.

02 · Insight

Keep the brand. Replace the plumbing.

The navy-and-periwinkle visual language was already right for a contractor talking to families who lost everything. We kept Poppins, kept the drone heroes, kept the calm corporate voice. What needed to change was every system underneath — forms, content, ops, comms — and a homeowner portal that didn't exist.

03 · Build

Six phases, six weeks.

Phase 1 discovery + scaffold. Phase 2 preview deploy. Phase 2.5 wiring + CRUD. Phase 3 GitHub origin + DB-first projects + DNS/GBP playbooks. Phase 4 real auth + 20 blog posts + outreach agent skeleton. Phase 5 admin CRUD + client portal foundation. Phase 6 the rest: outreach queue, doc vault, progress messages, budget tracking, campaigns, mailers, chatbot, Vercel cron, Twilio webhook.

04 · Outcome

A working operating surface, not a brochure.

27+ routes, 13 DB tables, 6 background agents, 20 SSG insight posts, three city SEO landings, a Twilio number with inbound webhook, a Claude outreach drafter, an admin dashboard that replaces /wp-admin, a homeowner portal that replaces phone calls. Same hero. Different machine underneath.

🔒palisades1stchoice.vercel.app
Modern
For the technically curiousHow it was done
  1. 01

    Where it started

    A five-page WordPress site built with Elementor on Hostinger — one generic Fluent Forms contact form, no blog, no project gallery, no client portal. Every 'Get Your Free Consultation' button dumped to the same form. The phone number wasn't tappable, the email address wasn't clickable, and there was zero search presence.

  2. 02

    What was wrong

    Families in trauma needed structure, and the site offered a contact form. Leads that came in got no follow-up unless someone happened to call back. Every status question — where's my permit, where's my money going — was a phone call, because nothing gave homeowners a window into their own project.

  3. 03

    The rebuild

    Keep the brand, replace the plumbing. The rebuild kept the palette, the typography, the drone heroes, and the hero copy where it worked — and rebuilt everything underneath on our hosting platform: 27+ pages, typed and validated forms, a 13-table managed database, private object storage for documents and drone footage, and an admin dashboard that replaces the old WordPress back office.

  4. 04

    The homeowner portal

    Each family gets a private project page showing an ordered status timeline from first contact through final inspection, a document vault for insurance paperwork and permits served through short-lived private links, a budget tracker answering the number-one question every family asks — received, paid, remaining — plus a message thread with photo updates and a drone footage gallery.

  5. 05

    The agents

    A scheduled agent wakes every four hours, finds leads that haven't been contacted, and drafts a personalized introduction in the company's calm, steady voice — queued for the owner's one-click approval before anything sends by email or text. Replies to the dedicated tracked phone line are matched back to the original message automatically and surface right next to the draft that prompted them. A public chat assistant answers visitor questions grounded in the site's 20 articles.

  6. 06

    What the owner does now

    Reviews drafted outreach and approves with one click. Flips a project's status and the family is emailed automatically. Prints ready-made neighborhood mailer flyers with trackable QR codes straight from the dashboard instead of rebuilding artwork every campaign. The site went from a brochure that collected dust to the operating surface the business actually runs on.

Architecture

Old stack
  • WordPress 6.9.4
  • Elementor Pro
  • Fluent Forms
  • WPR Addons
  • Hostinger Reach
  • Hostinger hosting
New stack
  • modern web framework
  • design system
  • managed Postgres
  • edge network
  • AI models
  • SMS rails
  • transactional email rails
  • edge hosting we operate

What changed, with evidence

Design

Same visual language, much more surface area

We kept the navy-and-periwinkle palette, the Poppins typography, the drone-captured aerial heroes, and the corporate-calm voice — these were already correct for a contractor talking to fire-trauma families. The rebuild adds a typed forms layer, a 6-step '01–06' phase timeline on /services, distinct /free-consultation and /contact forms (the old site dumped both into the same Fluent Forms endpoint), three city-specific SEO landings, and 20 SSG insight posts. Hero copy is preserved verbatim where it worked.

Before
5 pages, one form, no blog, every CTA hit the same generic contact endpoint.
After
27+ routes, typed Zod-validated forms, dedicated free-consultation flow, /projects gallery, /insights with 20 long-tail posts, three city landings (/service-areas/pacific-palisades, /altadena, /malibu).
Agent backbone

Outreach drafter on a 4-hour Vercel Cron

Every 4 hours the cron hits GET /api/agents/outreach (authenticated by an auto-injected bearer secret). It pulls new leads that have no outreach_attempts yet, calls Anthropic claude-sonnet-4-5 with a system prompt that anchors voice ('corporate, calm, competent — never WALK-INS WELCOME energy, never imitate insurance or FEMA'), drafts personalized subject + body per channel (email or SMS), and writes the draft to outreach_attempts with status='queued'. Brendon reviews at /admin/outreach and approves with one click — which dispatches via the verified-identity SMTP path (email) or the Twilio raw API (SMS).

Before
No outreach — only the family who happened to find the contact form ever heard back.
After
Every new lead gets a Claude-drafted intro queued for human approval within 4 hours.
  • Schedule: 0 */4 * * * in vercel.json
  • Model: claude-sonnet-4-5
  • Throttle on send: 250ms between recipients
  • Bounce + complaint path: SES Configuration Set with SNS topics → Stalwart suppression list (Hetzner) — currently transitioning per 2026-05-28 audit
Agent backbone

Anthropic ChatWidget with RAG over the 20 insights posts

Floating widget at bottom-right of every public page (hidden on /admin/* and /client/*). POST /api/chat does keyword-overlap RAG over the posts table — lowercased ≥4-char terms, regex match on title or body_md, top 4 hits with their excerpts get injected into the system prompt. Streams Anthropic Messages API SSE through plain text/plain to the browser. System prompt anchors voice + always closes with 'Want a 30-min call? Book at /free-consultation — no charge, no obligation.'

  • Source: src/app/api/chat/route.ts + src/components/ChatWidget.tsx
  • Stop list: this, that, with, from, what, when, where, which, there, …
  • Render hook: usePathname() hides widget on /admin* and /client*
Retention

Homeowner client portal — timeline · docs · budget · messages

Each family gets /client/[slug] gated to project.ownerUserId. They see: an ordered status timeline (lead → design → permitting → insurance coordination → construction → final inspection → delivered), a document vault (insurance dec pages, permits, contracts — private R2 with presigned-GET, 5-min URLs), a budget tracker (received / paid / remaining with category breakdowns — the #1 question every family asks), a message thread with photo attachments (R2 public bucket), and a drone footage gallery. Internal-only entries flagged via visibility='internal' don't render to clients. 11 new DB tables backed by Drizzle + Neon.

  • 13 tables total in src/db/schema.ts including project_documents, project_messages, project_budget_entries
  • Budget = headline cards (Received / Paid / Remaining + % spent) + entries table with kind badges (RECEIVED green / PAID red / COMMITMENT)
  • Document vault: 100 MB cap, content-type whitelist, scoped key prefix documents/<projectId>/<random>-<safeName>
Real-time push

Twilio inbound-SMS reply matcher on a dedicated tracked phone line

Inbound SMS to the Palisades subaccount number POSTs to /api/webhooks/twilio. We normalize the From phone (strip everything but digits + +), join outreach_attempts ↔ leads on digit-matching phone, mark the most-recent sent SMS attempt as status='replied', persist replyBody + replyReceivedAt, return empty TwiML 200. The admin /outreach review page surfaces replies inline under the original draft.

  • An isolated messaging subaccount keeps this client's traffic separate
  • Emergency address registered — avoids $75/emergency-call fee
  • X-Twilio-Signature validation: TODO marker in the route handler
Design

Mailers + EDDM print pipeline (no Cmd+P required)

/admin/mailers renders three print-ready EDDM flyer variants (Palisades / Altadena / Malibu) via Puppeteer + qrcode-generator. Each variant has a UTM-tagged QR code that opens /free-consultation?utm_source=eddm&utm_campaign={variant}. Click 'Print' → PDF download → ship to USPS EDDM bulk-mail vendor. Saves 10–15 minutes per print run versus rebuilding the Adobe artwork each campaign.

While the owner sleeps.

Autonomous surfaces

The agent backbone keeps the brand earning after hours. The owner approves; the system runs.

  • Outreach drafter

    every 4 hours (Vercel Cron)

    Pulls new leads, drafts personalized intros via Claude, queues for human approval. Brendon reviews + sends from /admin/outreach.

  • Inbound SMS reply matcher

    on each inbound SMS

    Matches replies on the dedicated tracked phone line back to the original outreach_attempt by phone digits. Surfaces in /admin/outreach next to the draft that triggered it.

  • Status-change client notifier

    on each status change

    When Brendon flips a project from permitting → construction (etc.), an email goes to the linked owner_user_id with a templated update + deep link to /client/[slug].

  • New-message client notifier

    on each message post

    When staff posts a photo update + caption to a project, the owner gets an email summarizing the post + link to the timeline.

  • Drone-upload partner pipeline

    on each partner upload

    HMAC-token gated /drone-uploads/[token] route. Partner uploads raw .mp4/.heic to R2 via presigned PUT; DB row written via /api/drone-uploads/complete. Auto-processing webhook to Open Claw team (contract pending).

  • Public chatbot RAG

    on each user message

    Floating widget on every public page streams Claude answers grounded in the 20 SSG insight posts. Anonymous mode; OG Life subscription gate deferred until that ships.

← All rebuilds

Palisades 1st Choice — a fire-rebuild contractor's brochure turned into the system that runs the business — zsty.us