Next.js 16 marketing website for Puente Health & Concierge.
npm install
npm run dev
# → http://localhost:3000npm install -g vercel
vercelOr connect the repo in the Vercel dashboard — zero configuration needed.
Every brand string, color, price range, and stat lives in one file:
src/config/site.ts
Change BRAND.name, BRAND.email, BRAND.phone, tier prices in TIERS, and procedure estimates in PROCEDURES here. Everything on the site picks up the change automatically.
- Testimonials —
src/components/sections/TestimonialsSection.tsx: swap the placeholder cards for real client quotes + photos. - Hero images — Add a
public/images/folder and update the Hero/About sections to usenext/imagewith real photography. - Pricing — Adjust
TIERSinsrc/config/site.tsonce you finalize your fee structure.
The form POSTs to /api/lead (src/app/api/lead/route.ts). It currently logs to the console. To send real notifications:
Option A — Resend (email)
npm install resend// in route.ts, after validation:
import { Resend } from 'resend';
const resend = new Resend(process.env.RESEND_API_KEY);
await resend.emails.send({
from: 'leads@puente.health',
to: 'hello@puente.health',
subject: `New lead: ${body.name}`,
text: JSON.stringify(body, null, 2),
});Option B — HubSpot CRM
await fetch('https://api.hubapi.com/crm/v3/objects/contacts', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${process.env.HUBSPOT_API_KEY}`,
},
body: JSON.stringify({
properties: {
firstname: body.name,
email: body.email,
phone: body.phone,
hs_lead_status: 'NEW',
},
}),
});Option C — Airtable
await fetch(`https://api.airtable.com/v0/${process.env.AIRTABLE_BASE_ID}/Leads`, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.AIRTABLE_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ fields: body }),
});Add secrets to .env.local (never commit this file):
RESEND_API_KEY=re_...
HUBSPOT_API_KEY=pat-...
AIRTABLE_API_KEY=pat...
AIRTABLE_BASE_ID=app...
In Vercel, add the same keys under Project → Settings → Environment Variables.
| Route | File | Notes |
|---|---|---|
/ |
src/app/page.tsx |
Homepage — all conversion sections |
/how-it-works |
src/app/how-it-works/page.tsx |
Detailed 4-step walkthrough + fears addressed |
/services |
src/app/services/page.tsx |
Dental (live) + coming-soon categories |
/pricing |
src/app/pricing/page.tsx |
Savings example + tier cards + comparison table |
/calculator |
src/app/calculator/ |
Interactive savings calculator (client component) |
/about |
src/app/about/page.tsx |
Mission, vetting standard, bilingual advantage |
/contact |
src/app/contact/ |
Lead form → /api/lead |
/api/lead |
src/app/api/lead/route.ts |
Stub POST handler — wire to email/CRM here |
/privacy |
src/app/privacy/page.tsx |
Privacy policy placeholder |
/terms |
src/app/terms/page.tsx |
Terms of service placeholder |
/sitemap.xml |
src/app/sitemap.ts |
Auto-generated |
/robots.txt |
src/app/robots.ts |
Auto-generated |
src/
app/ # Next.js App Router pages + API routes
components/
layout/ # Header, Footer
sections/ # Homepage sections (Hero, Stats, Trust, etc.)
ui/ # Shared primitives (Button, Card, ConsultCTA, etc.)
config/
site.ts # ← All brand content lives here
- Per-page
<title>/<meta description>via Next.jsmetadataexports OrganizationJSON-LD schema on every page (viasrc/components/JsonLd.tsx)sitemap.xmlandrobots.txtauto-generated- Update
metadataBaseinsrc/app/layout.tsxonce you have a real domain
- The disclaimer in
DISCLAIMER(src/config/site.ts) appears in the footer and on Services / Pricing pages. - Privacy Policy and Terms pages are placeholders — have a lawyer review before launch.
- No language implies Puente employs clinicians or guarantees clinical results.