[Contractor Name] · HVAC site
A trades marketing site. Built to convert homeowners with a leak, not to win a design award. Phone above the fold, service area visible, real photos, named technicians.
Source of truth
Vercel deploys from main. Content (services, service areas, gallery photos) lives in MDX in the repo for v0. Move to Sanity when the client wants to edit themselves.
Tech stack
Next.js 15 (App Router) + React + Tailwind v4. CallRail for the tracking phone number on the site. Resend API for the apply/quote form (emails + SMS to the owner). Cloudinary for real-photo galleries. Sanity (later) for content. Vercel hosting.
Deploy
git push origin main. Vercel auto-deploys. Phone number from CallRail is environment variable; swap when CallRail changes.
File map
app/page.tsxhome: hero with phone, services snapshot, service-area map, recent jobs, reviewsapp/services/[service]/page.tsxper-service pages (/services/ac-repair,/services/heat-pump-install)app/service-area/[city]/page.tsxper-city landing pages for local SEOapp/quote/page.tsxlead formapp/api/quote/route.tsPOST -> Resend email + Twilio SMS to ownercontent/services.tsservices list (price ranges, descriptions, FAQ)content/service-area.tscities served (zip codes, drive times)content/gallery.tsreal job photos (before/after pairs)components/PhoneCTA.tsxsticky phone CTA, mobile-prominent
.env keys
NEXT_PUBLIC_PHONECallRail tracking number (formatted(555) 555-1234)NEXT_PUBLIC_PHONE_TELraw E.164 (+15555551234) fortel:linksRESEND_API_KEYRESEND_NOTIFY_EMAILowner's inboxTWILIO_ACCOUNT_SID,TWILIO_AUTH_TOKEN,TWILIO_FROM_NUMBER,TWILIO_NOTIFY_NUMBER(SMS alerts)CALLRAIL_FORM_TRACKING_ID(binds web leads to CallRail)GOOGLE_REVIEWS_PLACE_IDfor the reviews embed
Hard rules
- Phone number is above the fold on every page. Click-to-call on mobile is the primary CTA.
- License + insurance numbers visible in the footer. Trade audiences look for these.
- Photos are REAL (the team, the trucks, actual jobs). No stock photos of "diverse team in hard hats" -- audience instantly distrusts.
- No em-dashes anywhere (Pangram flags them; owner WILL notice if his copy has them).
- Service-area pages are real local SEO pages: city name in H1, neighborhoods listed, distinct testimonial per city. Not duplicate content.
- Lead form notifies via BOTH email AND SMS. Owner is on a roof, not at a desk.
- Submit must be smoke-tested with real Resend + Twilio before launch. Lost leads = lost revenue.
Recent significant changes
- 2026-05-08: Scaffolded. Locked: phone-first design (homeowner mid-leak is not browsing), per-city pages for local SEO, real photos only, dual email + SMS alerts.
Next session: start here
- Buy CallRail tracking number, set in
.env. - Verify Resend domain. Verify Twilio messaging service.
- Populate
content/services.tsandcontent/service-area.tswith the client's actual scope. - Get 30-50 real job photos from the client. Compress + upload to Cloudinary.
- Submit a real test lead. Confirm email AND SMS arrive within 30 seconds.
- Run apex-seo before DNS attach.