Claude Code Prompt
Generate a comprehensive build prompt from your brief.
Balanced, production-ready prompt
Rationale
dPC's homepage is organised around a single design tension: professional authority × neighbourhood warmth. Every decision resolves this tension rather than collapsing it. The navy/white palette and Montserrat 900 display weight carry the authority signal; Source Sans 3 Light body copy, amber accents, and specific human copy ('We show up on time, clean up after ourselves, and answer the phone when you call') carry the warmth. The Porsche reference contributes extreme copy restraint and the amber-as-single-accent discipline — applied to a local service context rather than automotive luxury. The Compass Pools reference contributes the feature-forward hero structure that leads with product benefits before gallery. The signature map animation is the emotional centrepiece: it makes the company feel geographically planted, not a faceless national franchise. All other motion is functional and restrained. The typography system uses weight contrast (900 display vs 300 body) as the primary hierarchy signal — this is the typographyPov requirement executed as a system, not a font choice. Every personality moment targets the seams where generic sites go silent: the 404, the form confirmation, the map marker tooltip, the footer tagline. These are the moments that convert a visitor's subconscious trust into a phone call.
Quality Score
type system
easter eggs
real imagery
speed design
one animation
specific copy
chassis surface
motion restraint
personality seams
every page homepage
Auto-revised areas
p2_one_animation: The spec uses several different motion languages at once: staggered fade-up, button scale, clip-path menu reveal, and image load zoom. That dilutes the sense of one coherent signature animation.
p6_easter_eggs: There is no intentional surprise, hidden delight, or brand-specific easter egg. The spec is highly functional but emotionally flat.
p8_every_page_homepage: The spec strongly defines the homepage shell, but it does not require the same editorial structure and trust-building treatment on inner pages like Services, Service Area, Reviews, and Contact.
Build Checklist
- 1. Build Navigation section
- 2. Build Hero section
- 3. Build Trust Bar section
- 4. Build Services section
- 5. Build Service Area Map section
- 6. Build Reviews section
- 7. Build Credentials & Trust section
- 8. Build Contact / Lead Form section
- 9. Build Footer section
Prompt Output
Copy this into Claude Code or your preferred LLM.
Build a production-ready Next.js 14 website for New website for dPC. Every design decision below is a creative requirement, not a suggestion.
---
## DESIGN TOKENS
### Palette
```css
:root {
--color-navy: #17324D; /* primary */
--color-white: #FFFFFF; /* surface */
--color-service-blue: #2A7FFF; /* accent */
--color-warm-gray: #E9ECEF; /* border */
--color-ink: #0D1F2D; /* ink */
--color-amber: #F2C14E; /* highlight */
}
```
### Tailwind Config
```js
// tailwind.config.ts → theme.extend
colors: {
'navy': '#17324D',
'white': '#FFFFFF',
'service-blue': '#2A7FFF',
'warm-gray': '#E9ECEF',
'ink': '#0D1F2D',
'amber': '#F2C14E',
},
fontFamily: { display: ['Montserrat', 'sans-serif'], body: ['Source Sans 3', 'sans-serif'] }
```
### Google Fonts
```ts
import { Montserrat, Source_Sans_3 } from 'next/font/google'; const montserrat = Montserrat({ subsets: ['latin'], weight: ['700', '800', '900'], variable: '--font-display' }); const sourceSans = Source_Sans_3({ subsets: ['latin'], weight: ['300', '400', '600'], variable: '--font-body' });
```
### Spacing Scale (8px base)
```css
:root {
--space-unit: 8px;
--space-1: 8px;
--space-2: 16px;
--space-3: 24px;
--space-4: 32px;
--space-5: 48px;
--space-6: 64px;
}
```
- **Max-width container**: 1280px
- **Grid**: 12-column grid, gap: space-3
---
## TYPOGRAPHY SYSTEM
- **Display**: Montserrat 900, 60px desktop / 38px mobile, line-height 1.05, letter-spacing -0.03em. Hero headline only — one per page
- **Heading-1**: Montserrat 800, 40px desktop / 28px mobile, line-height 1.1, letter-spacing -0.02em. Section titles
- **Heading-2**: Montserrat 700, 26px desktop / 20px mobile, line-height 1.2, letter-spacing -0.01em. Card titles, sub-section labels
- **Body**: Source Sans 3 400, 17px desktop / 16px mobile, line-height 1.65, letter-spacing 0em. All paragraph text, descriptions
- **Body-Light**: Source Sans 3 300, 17px desktop / 16px mobile, line-height 1.65, letter-spacing 0em. Supporting copy beneath bold headlines — creates weight contrast
- **UI-Label**: Montserrat 700, 12px desktop / 11px mobile, line-height 1.0, letter-spacing 0.12em. Eyebrow labels, button text, nav items — always uppercase
- **Accent-Stat**: Montserrat 900, 48px desktop / 36px mobile, line-height 1.0, letter-spacing -0.03em. Trust stats — number only, paired with Body-Light descriptor
> Never use a font weight outside this system — no 500 or 600 for Montserrat
> Display and Heading-1 always set in navy (#17324D) on white surfaces, or white on navy surfaces
> Body text minimum 17px — never smaller on desktop
> UI-Label always uppercase, always letter-spaced at 0.12em
> Amber (#F2C14E) used only as a typographic accent on numerals or single-word highlights — never as a background fill
> No centred body text blocks longer than two lines
> Heading weight contrast (900 display vs 300 body) is the primary hierarchy signal — do not flatten it
---
## PAGE ARCHITECTURE
### 1. NAVIGATION
**Layout**: Fixed top bar, full-width, 72px height. Logo left-aligned (wordmark only, navy). Nav links centre-right: Services, Service Area, Reviews, Contact. Single CTA button far right: 'Call Now' with phone number visible at desktop. Mobile: hamburger collapses to full-screen overlay with large tap targets (min 48px). Background: white with 1px warm-gray border-bottom. No blur, no transparency — solid and legible.
**Copy**: Logo: dPC. Nav links: Services | Service Area | Reviews | Contact. CTA: Call (555) 000-0000
**Styling**: Background: #FFFFFF. Border-bottom: 1px solid #E9ECEF. Logo: Montserrat 800, #17324D, 22px. Nav links: UI-Label style, #17324D, hover: #2A7FFF with 0.2s ease transition on color only. CTA button: background #2A7FFF, white text, Montserrat 700, 13px, letter-spacing 0.08em, border-radius 4px, padding space-2 space-3. No box-shadow on nav bar.
**Imagery**: None — typography-only nav
**Interaction**: Framer Motion, nav CTA button scale 1.0 to 1.03 on hover, 0.15s, ease-out. Mobile menu: clip-path reveal from top, 0.35s, cubic-bezier(0.16,1,0.3,1)
**Hard Constraints**:
- Phone number must be visible in nav at desktop breakpoint — this is a lead-gen site
- No sticky nav blur or frosted glass — solid white only
- Mobile menu must have minimum 48px tap targets
### 2. HERO
**Layout**: Full-bleed, 100vh on desktop, 85vh on mobile. Two-column split at desktop: left 55% carries headline, subhead, two CTAs, and a single trust badge row. Right 45% is a full-height editorial photograph — crew on a job site, real faces, real neighbourhood context. No centred layout. Left column content vertically centred, padding-left space-6. Right image bleeds to viewport edge with no border-radius. A thin amber (#F2C14E) vertical rule (2px wide, 80px tall) sits between the eyebrow label and the Display headline as a typographic accent — borrowed from Porsche's red accent discipline applied to the amber highlight token.
**Copy**: Eyebrow (UI-Label): SERVING [CITY NAME] SINCE 2009. Display headline: Work done right. Done local. Subhead (Body-Light): We show up on time, clean up after ourselves, and answer the phone when you call. That's it. Primary CTA: Get a Free Quote. Secondary CTA: Call (555) 000-0000. Trust badges: Licensed & Insured | 4.9★ on Google (312 reviews) | Same-day availability
**Styling**: Background: #17324D (navy) on left column. Right column: full-bleed photograph, no overlay. Left column text: white Display headline, white Body-Light subhead. Amber vertical rule: #F2C14E, 2px × 80px, positioned left of headline with space-3 gap. Primary CTA: #2A7FFF background, white Montserrat 700, border-radius 4px, padding space-2 space-4. Secondary CTA: transparent background, white border 1.5px, white text, same sizing. Trust badge row: UI-Label, #E9ECEF color, space-3 gap between items, a 1px warm-gray vertical separator between each. No gradient overlays on the navy panel.
**Imagery**: Editorial crew photograph — real team members on a completed job site, natural light, neighbourhood visible in background. Minimum 1400px wide source. Served via next/image with priority flag. No stock abstract imagery, no gradient mesh.
**Interaction**: Framer Motion, left column content staggered fade-up: eyebrow 0ms delay, headline 80ms delay, subhead 160ms delay, CTAs 240ms delay, trust badges 320ms delay. Each element: translateY 20px to 0, opacity 0 to 1, duration 0.7s, cubic-bezier(0.16,1,0.3,1). Right image: scale 1.04 to 1.0 on load, duration 1.2s, ease-out. Max 3 elements animating simultaneously — stagger enforces this.
**Hard Constraints**:
- Phone number must appear as a tappable CTA above the fold on mobile
- No centred hero layout — left-aligned editorial split only
- No gradient backgrounds on the hero — navy panel is flat colour
- Amber rule is a typographic accent only — never a background fill
- Real crew photography mandatory — no stock abstract or gradient blob imagery
### 3. TRUST BAR
**Layout**: Full-width horizontal band, 96px tall on desktop, auto-height on mobile. Single row of 4 trust signals with icon + label pairs, evenly distributed across 12-column grid. No carousel — all four visible simultaneously on desktop. On mobile: 2×2 grid, space-4 gap.
**Copy**: 1: Licensed & Insured in [State]. 2: 312 Five-Star Google Reviews. 3: Same-Day Service Available. 4: Family-Owned Since 2009.
**Styling**: Background: #E9ECEF (warm gray). Each item: small filled icon in #2A7FFF (16px), UI-Label text in #17324D, space-2 gap between icon and text. No card borders, no shadows — items float on the gray band. Vertical 1px #FFFFFF separators between items on desktop.
**Imagery**: SVG icons only — checkmark shield, star, clock, house. No stock icon libraries — custom SVG at 16×16px.
**Interaction**: static by design
**Hard Constraints**:
- All four trust signals visible simultaneously on desktop — no carousel
- No vague benefit copy — every signal must be specific and verifiable
### 4. SERVICES
**Layout**: Section padding: space-6 top and bottom. Eyebrow label + Heading-1 left-aligned, max-width 560px. Below: 3-column card grid on desktop, single column on mobile. Each card: 1px warm-gray border, white background, space-4 internal padding. Card structure: service name (Heading-2), one-sentence specific description (Body), and a text link 'Learn more →' in service-blue. No line icons — each card uses a small editorial photograph (4:3 ratio, 240px tall) of the actual service being performed.
**Copy**: Eyebrow: WHAT WE DO. Heading: Services built for [City] homes. Card 1 — [Service Name]: [Specific one-sentence description of what is done, e.g. 'We resurface driveways up to 2,000 sq ft in a single day — no subcontractors, no surprises.']. Card 2 — [Service Name]: [Specific description]. Card 3 — [Service Name]: [Specific description]. Each card CTA: Get a quote for this →
**Styling**: Section background: #FFFFFF. Card border: 1px solid #E9ECEF. Card hover state: translateY -4px, box-shadow 0 8px 24px rgba(23,50,77,0.10), transition 0.25s cubic-bezier(0.16,1,0.3,1) — the warm shadow references the navy palette, not a generic black shadow. Card image: border-radius 0 (editorial, not rounded). Service name: Heading-2, #17324D. Description: Body-Light, #17324D at 80% opacity. Link: UI-Label, #2A7FFF, no underline, arrow animates right 4px on hover.
**Imagery**: Real photographs of each service in progress — crew hands, materials, finished results. 4:3 ratio, 240px tall in card. No gradient overlays, no abstract imagery.
**Interaction**: Framer Motion ScrollTrigger equivalent (useInView), cards stagger fade-up: 0ms, 100ms, 200ms delay respectively. Each: translateY 16px to 0, opacity 0 to 1, duration 0.6s, power2.out. Card hover lift is CSS transition only — not Framer Motion.
**Hard Constraints**:
- No line icons — real service photography only
- No vague benefit copy — every description must name a specific outcome or constraint
- Hover shadow must use navy-tinted rgba, not generic black
### 5. SERVICE AREA MAP
**Layout**: Full-width section, space-6 padding top and bottom. Two-column layout on desktop: left 40% carries heading, body copy, and a list of served towns/neighbourhoods. Right 60% is the signature map animation. On mobile: stacked, map above text. Map container: 480px tall on desktop, 320px on mobile. Background of section: #17324D (navy).
**Copy**: Eyebrow (UI-Label, amber): OUR BACKYARD. Heading-1 (white): We know these streets. Body (Body-Light, white 80% opacity): From [Neighbourhood A] to [Neighbourhood B], we've worked on homes across [City] for 15 years. If you can see [Local Landmark] from your front porch, we're your crew. Town list: [Town 1] · [Town 2] · [Town 3] · [Town 4] · [Town 5] · [Town 6] — rendered in UI-Label, amber, space-2 gap.
**Styling**: Section background: #17324D. Left column text: white and amber as specified. Map container: white background, border-radius 4px, overflow hidden. Map uses Mapbox GL JS or Leaflet with a custom monochrome tile style (navy-tinted, not default Google Maps). Service area polygon fills in #2A7FFF at 15% opacity. Business address pin: custom SVG marker in #F2C14E. No default red Google Maps pins.
**Imagery**: Custom-styled map tile — monochrome navy/white. No screenshots of Google Maps.
**Interaction**: GSAP ScrollTrigger: on section enter (80% viewport), Mapbox/Leaflet flyTo animates from zoom level 8 to zoom level 11 centred on business address over 1.4s ease-in-out, then service area polygon draws in via GeoJSON layer fade opacity 0 to 0.15 over 0.8s with 0.3s delay. Business address marker drops in with translateY -20px to 0, opacity 0 to 1, 0.4s, power2.out, at 1.8s total. This is the page's ONE signature animation.
**Hard Constraints**:
- This is the signature animation — no other section on this page may have a complex scroll animation
- Map must use custom tile style — no default Google Maps or OpenStreetMap default tiles
- Business address must be the animation origin point
- Town list must name real, specific neighbourhoods — no placeholder 'Area 1' copy
### 6. REVIEWS
**Layout**: Section padding: space-6 top and bottom. Eyebrow + Heading-1 left-aligned. Below: horizontal scroll row of review cards on mobile, 3-column grid on desktop. Each card: reviewer name, star rating (5 amber stars rendered as SVG, not emoji), review text (max 3 sentences — truncated with expand), Google review badge. Below the grid: aggregate stat row — '4.9 average across 312 Google reviews' with a link to the Google Business profile.
**Copy**: Eyebrow: WHAT NEIGHBOURS SAY. Heading-1: 312 reviews. One opinion. Each review card must use verbatim real review text — no fabricated testimonials. Aggregate row: 4.9 ★ average · 312 reviews on Google · [View all reviews →]
**Styling**: Section background: #FFFFFF. Card: 1px #E9ECEF border, white background, space-4 padding. Reviewer name: Heading-2 weight (Montserrat 700) but at 16px, #17324D. Stars: #F2C14E SVG, 16px. Review text: Body, #17324D at 85% opacity. Google badge: small Google 'G' SVG + 'Google Review' in UI-Label, #17324D at 50% opacity. Aggregate row: Accent-Stat for '4.9', amber colour, paired with Body-Light descriptor.
**Imagery**: Google reviewer avatars if available via API. No stock headshot photography.
**Interaction**: Framer Motion useInView, cards fade-up stagger: 0ms, 80ms, 160ms. translateY 12px to 0, opacity 0 to 1, 0.5s, ease-out. Mobile horizontal scroll: native CSS scroll-snap, no JS carousel library.
**Hard Constraints**:
- No fabricated testimonials — real Google review text only
- Star rating must be SVG, not emoji or icon font
- Aggregate stat must link to live Google Business profile
### 7. CREDENTIALS & TRUST
**Layout**: Full-width band, space-5 padding top and bottom. Single row of credential logos/badges on desktop: licence number, insurance certificate badge, industry association logos, BBB rating. On mobile: 2-column grid. Background: #E9ECEF.
**Copy**: Eyebrow (centred, UI-Label): LICENSED, INSURED & ACCOUNTABLE. Licence: [State] Contractor Licence #XXXXXX. Insurance: General Liability + Workers' Comp. Association: [Industry Body] Member. BBB: A+ Rated.
**Styling**: Background: #E9ECEF. All logos/badges: grayscale filter, opacity 0.7, hover: full colour, opacity 1.0, transition 0.2s ease. Licence number: Body, #17324D, monospace rendering for the number itself. No decorative borders or card wrappers — items float on the gray band.
**Imagery**: Official badge SVGs from licensing bodies. No placeholder shield icons.
**Interaction**: static by design
**Hard Constraints**:
- Licence number must be real and specific — no placeholder
- No decorative card wrappers — credentials speak for themselves
### 8. CONTACT / LEAD FORM
**Layout**: Section padding: space-6 top and bottom. Two-column on desktop: left 45% is contact information and a reassurance block. Right 55% is the lead form. Form fields: Name, Phone, Email, Service needed (select), Message (optional), Submit. No CAPTCHA visible — use honeypot. Background: #17324D (navy).
**Copy**: Eyebrow (amber, UI-Label): GET IN TOUCH. Heading-1 (white): We'll call you back within 2 hours. Left column body (Body-Light, white 80%): No forms lost in a queue. When you submit this, it goes directly to [Owner Name]'s phone. Phone: (555) 000-0000. Email: [email protected]. Hours: Mon–Sat 7am–6pm. Form submit button: Send My Request. Form confirmation (inline, replaces button): Got it — [Owner Name] will call you before [time + 2 hours]. Form field placeholder text: Name field: 'Your name', Phone: 'Best number to reach you', Service: 'What can we help with?'
**Styling**: Section background: #17324D. Form fields: white background, 1px #E9ECEF border, border-radius 4px, padding space-2 space-3. Focus state: border-color transitions to #2A7FFF, box-shadow 0 0 0 3px rgba(42,127,255,0.15), transition 0.2s ease — smooth and deliberate, not jarring. Field labels: UI-Label, white, space-1 margin-bottom. Submit button: #2A7FFF background, white Montserrat 700, full-width on mobile, auto-width on desktop. Error states: amber (#F2C14E) border + small error text in amber below field. Success state: inline replacement, no page reload.
**Imagery**: None — typography and form only. Optional: small crew photograph at 200px wide in left column, editorial style.
**Interaction**: CSS transitions only: field focus border 0.2s ease, submit button hover scale 1.0 to 1.02 0.15s ease-out. Form success state: Framer Motion AnimatePresence, button fades out 0.2s, confirmation text fades in 0.3s with translateY 8px to 0.
**Hard Constraints**:
- Form confirmation must be specific — include owner name and a calculated callback time
- No CAPTCHA visible to user
- Focus states must be smooth — no default browser outline
- Error states use amber, not red — stays within palette
### 9. FOOTER
**Layout**: Full-width, space-5 padding top, space-4 padding bottom. Three columns on desktop: left — logo + tagline + social links. Centre — quick nav links. Right — address, phone, hours. Below a 1px warm-gray divider: a single-line copyright row with a personality moment on the right side. Background: #0D1F2D (ink — slightly darker than navy for depth).
**Copy**: Logo: dPC. Tagline (Body-Light, white 60%): Doing it properly. Since 2009. Nav links: Services | Service Area | Reviews | Contact | Privacy. Address: [Full address]. Phone: (555) 000-0000. Hours: Mon–Sat 7am–6pm. Copyright: © 2025 dPC. All rights reserved. Personality line (right-aligned, UI-Label, white 40%): Built in [City]. Answering calls since 2009.
**Styling**: Background: #0D1F2D. All text: white at varying opacities (logo 100%, tagline 60%, nav 70%, address 80%, copyright 40%). Nav links hover: #2A7FFF, 0.2s ease. Social icons: white 50% opacity, hover white 100%, 0.2s ease. Divider: 1px solid rgba(255,255,255,0.1). No gradient, no decorative elements.
**Imagery**: None
**Interaction**: static by design
**Hard Constraints**:
- Footer personality line must reference the actual city — not a placeholder
- No legal dump — privacy policy is a single link, not a paragraph
- Footer background must be ink (#0D1F2D), not navy — creates depth separation from the contact section
---
## MOTION PLAN
**Signature Animation** (service-area-map): On scroll entry, the map flies in from regional zoom (level 8) to neighbourhood zoom (level 11) centred on the business address, then the service area polygon fades in at 15% opacity, then the business address marker drops in. The visitor experiences the company planting itself in their neighbourhood — motion serves the narrative of local rootedness.
- Library: GSAP ScrollTrigger + Mapbox GL JS flyTo API
- Implementation: ScrollTrigger.create({ trigger: '#service-area-map', start: 'top 80%', onEnter: () => { map.flyTo({ center: [lng, lat], zoom: 11, duration: 1400, easing: t => t < 0.5 ? 2*t*t : -1+(4-2*t)*t }); setTimeout(() => { map.setPaintProperty('service-area-fill', 'fill-opacity', 0.15); }, 1400); setTimeout(() => { gsap.fromTo('#address-marker', { y: -20, opacity: 0 }, { y: 0, opacity: 1, duration: 0.4, ease: 'power2.out' }); }, 1800); } })
**Per-Section Motion**:
- nav: CTA button scale 1.0→1.03 on hover, 0.15s ease-out. Mobile menu clip-path reveal, 0.35s cubic-bezier(0.16,1,0.3,1)
- hero: Staggered fade-up: 5 elements, 80ms stagger, translateY 20px→0, opacity 0→1, 0.7s cubic-bezier(0.16,1,0.3,1). Right image scale 1.04→1.0 on load, 1.2s ease-out
- trust-bar: static by design
- services: useInView stagger fade-up: 3 cards, 100ms stagger, translateY 16px→0, opacity 0→1, 0.6s power2.out. Card hover lift: CSS only, translateY -4px, shadow, 0.25s cubic-bezier(0.16,1,0.3,1)
- service-area-map: SIGNATURE ANIMATION — see motionPlan.signatureAnimation. No additional motion in this section.
- reviews: useInView stagger fade-up: 3 cards, 80ms stagger, translateY 12px→0, opacity 0→1, 0.5s ease-out
- credentials: static by design
- contact: CSS transitions: field focus border 0.2s ease. Submit hover scale 1.02, 0.15s ease-out. Form success: AnimatePresence fade-out button 0.2s, fade-in confirmation translateY 8px→0, 0.3s
- footer: static by design
**Global Motion Rules**:
- Maximum 3 simultaneously animated elements per viewport at any time
- Stagger delays enforce the 3-element simultaneous cap — never trigger all elements at once
- The service area map animation is the ONE signature animation for this page — no other section may use a complex multi-step scroll animation
- All hover transitions are CSS only — Framer Motion reserved for scroll-triggered entrance animations and the form success state
- No looping animations anywhere on the page
- prefers-reduced-motion: all animations reduce to opacity fade only, 0.3s, no translate
---
## BANNED DEFAULTS
The following are explicitly banned. If the build includes any of these, it has failed:
- ❌ No Inter as sole font — Source Sans 3 is the body font, Montserrat is display
- ❌ No centred hero with gradient subtitle — left-aligned editorial split only
- ❌ No abstract geometric logomark placeholder
- ❌ No product UI mockup floating on a gradient panel
- ❌ No 'Trusted by' logo bar without context
- ❌ No three-column feature grid with line icons and vague benefit copy — real photography in service cards
- ❌ No default shadcn/Tailwind card components without customisation
- ❌ No gradient mesh or blob backgrounds — navy and warm-gray flat surfaces only
- ❌ No soft pastel gradients anywhere on the page
- ❌ No filler marketing copy — every line must name a specific outcome, number, or constraint
- ❌ No default Google Maps tile style — custom monochrome map required
- ❌ No generic black box-shadows — all shadows use navy-tinted rgba
- ❌ No rounded card borders on service cards — editorial, not bubbly
- ❌ No emoji in UI — SVG only for stars and icons
---
## PERSONALITY MOMENTS
**404 page**: Headline: 'This page doesn't exist. Unlike our call-back guarantee.' Subhead in Body-Light: 'We couldn't find that page, but we can definitely find your address. Head home or give us a call.' Two CTAs: 'Back to Home' and 'Call (555) 000-0000'. Background: navy. Small illustration: a hand-drawn-style map pin with a question mark, SVG, amber colour. No stock 404 imagery.
**Form confirmation state**: Inline replacement of submit button: 'Got it — [Owner Name] will call you before [calculated time]. If your phone rings from a [City] number, that's us.' Rendered in Body, white, with the time in Montserrat 700 amber. No modal, no page reload, no generic 'Thank you for your submission.'
**Service card hover state**: On hover, the 'Get a quote for this →' link arrow animates 4px to the right with a 0.2s ease-out transition. The card lifts 4px with a navy-tinted warm shadow. The effect feels responsive and alive — like the card is leaning toward you.
**Footer tagline**: 'Doing it properly. Since 2009.' — set in Body-Light at 60% white opacity beneath the logo. The word 'properly' is the brand's quiet confidence. Right-aligned copyright companion: 'Built in [City]. Answering calls since 2009.' — UI-Label, 40% white opacity.
**Map marker tooltip**: When the address marker completes its drop-in animation on the service area map, a small tooltip appears: '[Business Name] HQ' with a tiny amber dot. On hover/tap: 'This is where we start every morning.' — Body, white, navy background tooltip, no default Mapbox popup styling.
**Loading state (initial page load)**: If LCP image is not yet loaded, a minimal placeholder: navy background with the dPC wordmark in white, pulsing opacity 0.4→1.0→0.4 at 1.5s interval. No spinner, no progress bar. Disappears instantly when hero image loads — no fade-out delay.
**Empty state (form fields on focus)**: Phone field: on focus, placeholder text transitions from 'Best number to reach you' to 'We'll only call — no spam texts.' in 0.3s opacity crossfade. Reinforces the trust message at the exact moment of commitment anxiety.
---
## PERFORMANCE BUDGET
- **LCP target**: < 2.5s
- **CLS target**: < 0.1
- **Image strategy**: All images served via next/image with automatic WebP/AVIF conversion. Hero photograph: priority={true}, sizes='(max-width: 768px) 100vw, 55vw', preloaded in <head>. Service card images: lazy loaded, sizes='(max-width: 768px) 100vw, 33vw'. Map tiles: loaded asynchronously after hero LCP completes — map section uses IntersectionObserver to defer Mapbox GL JS bundle until section is 200px from viewport.
- **Font strategy**: Montserrat and Source Sans 3 loaded via next/font/google with display: swap. Only weights used in the typography system are loaded: Montserrat 700, 800, 900 and Source Sans 3 300, 400, 600. No full font family download. Preconnect to fonts.gstatic.com in <head>.
- **Bundle notes**: Mapbox GL JS (~250kb gzipped) loaded dynamically via next/dynamic with ssr: false — only when service area section enters viewport. GSAP core + ScrollTrigger loaded only on client. Framer Motion tree-shaken to motion and AnimatePresence only. No full component library imported — all UI components hand-built to spec. Target JS bundle: < 180kb gzipped for initial page load.
Export Brief + Prompt
Download the full brief, direction, and prompt.