For Audrey · Designer · Handoff Playbook

Bright Side PlumbingLocation Pages + Programmatic Figma Playbook

Everything you need to design the BSP location-page template: strategy, competitor intel, 10x local-SEO moves, 14-section architecture, 8-layer schema stack, per-city differentiation pattern, 15-city tier order, brand tokens, Figma specs, asset manifest. Mobile-first. One template multiplies to 45 indexable pages (15 cities × 3 services). Inherits 100% from the Homepage Redesign Playbook design system.

📅 April 17, 2026 · Robert Dove · Dove Web Consulting · For Audrey Laube · Target: /plumber-in-[city]/ template · Parent playbook: BSP_Homepage_Redesign_Playbook.html

💖Start Here — Audrey 5-Step

Skip the strategic / API sections for Phase 1 design. Here is the shortest path from zero to a production-ready Figma frame.

  1. Read The North Star (1 paragraph). It is the only design decision that matters at this stage.
  2. Read the Mobile-First Figma Guide — breakpoints, type scale, 8-px spacing, accessibility minimums.
  3. Open the Component Library — 15 reusable CL components with polished demos. Every section below pulls from these.
  4. Design Section 01 (Sticky Emergency Bar) at Mobile 390 first. Use Overland Park as the reference city. Once approved, duplicate the approach for Sections 02–15.
  5. Skip: Live Fleet strategy, Google API stack, Schema Layers, Tier Order tables. Those are Robert’s build inputs, not design blockers.

If you see [City] in any spec, it is a placeholder substituted from the city data object at build time. Use Overland Park for every Phase 1 Figma frame.

🧪

Build Path — Hybrid (Bricks AI Studio + Claude Design)

Every section below is built via the Hybrid path — Bricks AI Studio generates canonical Bricks JSON from pre-written prompts, Claude validates against this spec, Audrey spot-checks against Figma. The old fully-manual build process has been retired as of Apr 20.

📈 Build Path Locked — Hybrid Only

As of Apr 21, 2026: Every location page ships via the Hybrid build path. Bricks AI Studio license is active on bricks.callbrightside.com. Pre-written prompts for each section of this playbook are generated on-demand by Claude (matching the spec row exactly) and pasted into Bricks AI Studio in the Bricks editor, which emits canonical Bricks JSON. Audrey approves the rendered output against her Figma frame. No manual JSON authoring, no Path A fallback, no exceptions.

Audrey designs the 20% that carries the brand (hero, Why BSP). Bricks AI generates the 80% (trust bar, map, service grid, reviews grid, FAQ, CTA). Naming convention for Figma frames: NN_type_descriptor aligned with the § number in each section spec (e.g. 01_section_hero, 04_section_services_grid).

Header + Footer are off-limits for Audrey. Global Bricks components already ship on every page. She does not design them in Figma. Numbering starts at the first content section.

Prompt modes: Per-Section (one frame at a time) or Whole-Page Prompt Pack (all sections bundled, Figma-flagged frames marked “drop your Figma export, do not regenerate”).

→ Open the full 3-tool workflow diagram + 5 wiring steps

📐 Manual (retired) · Manual Build (Approved)

Audrey hands Figma to Robert. Robert manually assembles the location-page template in Bricks, wires city-data JSON loop, Google Maps iframe, live fleet badge. Same process as the existing /sewer-camera-inspection/ page.

First template build3 to 4 hours
Each additional city15 to 20 min copy via city-data JSON
Tool cost$0
ApprovalAlready approved (default)
RiskKnown. Same pattern as existing sewer-camera-inspection build.
Robert availabilityBlocks other work for the sprint week

🤖 Hybrid Build — Bricks AI Studio + Claude Design

Audrey designs the hero + unique 20% in Figma. Robert prompts Bricks AI Studio for the repeating 80% (trust bar, map, service grid, reviews grid, FAQ, CTA). Claude Design handles the Stephanie weekly deck. City-data JSON loop unchanged.

First template build~1.5 hours
Each additional city5 min copy via city-data JSON
Tool cost$40 / month (cancel-safe monthly)
ApprovalPending — Stephanie yes/no
RiskBricks AI Studio is live but third-party. Revert to A if output fails Audrey review on Tuesday pilot.
Robert availabilityKeeps ~22 hrs this sprint for Smart Bidding, Daniel fixes, ST attribution

🔁 Switch Points — Flip Safely Mid-Sprint

SwitchTriggerWhat happens to already-shipped city pages
Stay A all weekNo approval receivedEverything manual. Sprint may miss end-of-next-week goal. Default path, no action needed.
All B from TuesdayStephanie greenlights Monday night or Tuesday AMCities shipped Monday stay as A. Everything Tuesday onward flips to B.
A to B mid-weekApproval arrives mid-weekKeep cities shipped on A. Remaining cities flip to B. No rebuild of shipped work.
B to A mid-weekBricks AI output fails Audrey quality gate Tuesday EODCancel $40 sub. Revert all subsequent cities to manual. Shipped hybrid cities stay live.
📝 Note on per-section prompts: Rather than bloat this playbook with 14 Bricks AI prompts upfront, prompts are generated on-demand when a section is about to be built under the Hybrid path. Ask for the prompt for any section number and it gets produced within minutes, tuned to the exact spec row below.
🚨 Sewer camera lesson applies to both paths: whichever path we use, every section must extract directly from Audrey’s Figma frame (file key GViYd2jKWUEpLbz1lWghby, mobile 722:55, desktop 707:14). Don’t patch a warped tree. If a Bricks template drifts from the Figma spec, rebuild that section from Figma, do not keep patching. This is why the sewer camera page hit 5 rebuild cycles before v6 matched 1:1.
🎯

The North Star

One line that drives every design decision

Make a homeowner in Overland Park searching "plumber near me" at 11 PM feel like Bright Side is the ONLY local family plumber who can actually save them tonight. Every section, schema field, neighborhood name, and review pulled must reinforce that this page is built for THEIR street, not a national template dressed up with a city name.

Location pages are not homepage clones with the city swapped. They are local-SEO weapons. Google rewards pages that prove the business serves a specific place: city in H1, neighborhoods in body, landmarks in copy, reviews filtered by city, schema with areaServed + GeoCircle, and differentiation that reads like a human who lives there wrote it. Phase 1 ships 4 T1 cities with 1 service each (4 pages). Phase 2 multiplies to 45 (15 cities × 3 services).

📐

Mobile-First Figma Guide

The Core Rule

Location-page traffic is 70%+ mobile (Emergency Eric searches on a phone, standing over a leak). Every section is designed at 375 px first. If it does not work one-handed in a dark basement at 11 PM, it does not ship.

Design order: 📱 Mobile 375 → 📲 Tablet 768 → 💻 Desktop 1440. Every component has all three states defined in Figma variants.
Inherits 100% from the Homepage Playbook. Same breakpoints, type scale, spacing scale, component library, brand tokens. This playbook only adds location-specific specs — it does not redefine the base system.

Breakpoints (same as homepage)

NameRangeFigma frameLocation-page notes
📱 Mobile S320–477 px375 × 812Primary design target. Emergency stack (sticky bar, hero CTA, trust bar, availability) must fit in 2 viewports.
📲 Mobile L478–767 px414 × 896Identical to S, wave divider on.
📐 Tablet768–991 px768 × 10242-col neighborhoods grid, still hamburger.
💻 Desktop992+ px1440 × 8500Embedded map + neighborhoods side-by-side, 3-col before/after.

Type scale, spacing, components

See Homepage Playbook § Figma Guide for full specs — H1 32/48 · H2 26/36 · H3 20/24 · Body 16/18 · 8-px spacing base · the same 8 Figma symbols (Primary CTA, Secondary CTA, Phone CTA, Service Card, Review Card, Trust Chip, FAQ Row, Wave Divider). Location pages add 2 new components:

  • City Chip — clickable chip for neighborhood or nearby-city grid. Same base style as trust chip but pill-shaped, 32h, variant: default / hover / visited. Links to /plumber-in-{slug}/ on nearby-city grid, or to FAQ anchor on neighborhood grid.
  • Availability Chip — "Techs available in [city] today · 45-min avg." Green dot + city name + response time + "Call now" CTA. Mobile: full-width card 80h. Desktop: pinned right in hero column 300×120.

Accessibility + Touch (inherited)

  • ✅ Touch targets ≥ 48 × 48 px with ≥ 8 px spacing
  • ✅ Body ≥ 16 px on mobile, no exceptions
  • ✅ Contrast AA minimum: text on #1D1760 = #FFF or #30C5FF · text on #F8FAFC = #1D1760
  • ✅ Focus ring 2 px #FFEA00 with 2 px offset on every interactive element
  • ✅ Every image alt text includes city context ("Kalen Barker servicing sewer line in Olathe")
  • ✅ One H1, sequential H2s, no skipping to H4
🧩

Component Library — Reusable Figma Components

How to read this catalog

Fourteen reusable components that power every section of the Location Pages template. Each spec is self-contained: purpose, anatomy, variants, responsive sizes, tokens, motion, accessibility, exact Figma component name Audrey will publish, which sections consume it, the build-side HTML pattern Robert ships, and the CSS custom-property values. If a section needs something, it pulls from this catalog. No one-off styling. Phase 1 = must-ship on the first 4 T1 pages. Phase 2 = polish or data-driven layer.

Golden rule: If it is used more than once, it lives here. If Audrey ships a new visual treatment that is not in this library, it does not go to production until it is added to this library.
14
Components
11
Phase 1 (ship first)
3
Phase 2 (data-driven)
8‑px
Spacing base
§ CL-01 📍 Primary CTA Button Phase 1
PurposeThe main "Book Now" / "Schedule Service" conversion action. Highest visual weight on the page after the phone CTA.
AnatomyLabel text (16w700) · optional leading icon 18×18 · optional trailing arrow → 16×16 · 48h mobile / 52h desktop container
Variantsdefault · hover · pressed · disabled · focus · loading (spinner replaces label)
Mobile (375)Full-width (100% with 16 side margin), 48h, padding 12/20, radius 8, label 16w700, min touch 48×48
Tablet (768)Auto-width or 50% in 2-col CTA row, 50h, padding 14/24, label 16w700
Desktop (1440)Auto-width, 52h, padding 14/28, label 16w700, inline with secondary CTA
Tokensbg --blue #30C5FF, color --navy #1D1760, shadow cta, radius 8, hover bg darkens to #27AEDE
MotionHover translateY(-2px) + shadow grow 150ms ease-out · pressed scale(0.98) 80ms · focus ring 2px #FFEA00 + 2px offset
AccessibilityTouch target ≥ 48×48. Contrast navy-on-blue 7.1:1 AAA. role="button" implicit on <button>. Keyboard Enter/Space. prefers-reduced-motion: no transform, opacity only.
Figma nameButton/Primary/Default, Button/Primary/Hover, Button/Primary/Pressed, Button/Primary/Disabled, Button/Primary/Focus, Button/Primary/Loading
Used in section(s)§ 02 Hero (Book CTA) · § 14 Final CTA · Floating CTA in § 11 Financing card · Homepage hero + multiple sections
Build-side HTML pattern<a class="btn btn--primary" href="/book/?city=overland-park">Book Now →</a>
CSS tokens--btn-primary-bg: #30C5FF; --btn-primary-color: #1D1760; --btn-primary-radius: 8px; --btn-primary-shadow: 0 4px 4px rgba(0,0,0,0.15); --btn-primary-pad: 12px 20px;
DemoBook Now →
§ CL-02 💬 Secondary CTA Button Phase 1
PurposeSupporting action that sits next to the primary CTA. "Chat with Daniel," "Get a Free Estimate," "Learn More." Never competes visually with the primary.
AnatomyLabel text (15w600) · optional leading icon 16×16 · 48h mobile / 52h desktop · border 1.5px, transparent bg
Variantsdefault · hover · pressed · disabled · focus · inverted (on dark backgrounds)
Mobile (375)Full-width 100%, 48h, padding 12/20, radius 8, label 15w600
Tablet (768)Auto-width, 50h, padding 14/24
Desktop (1440)Auto-width, 52h, padding 14/28
Tokensbg transparent, color --navy, border 1.5px #1D1760; inverted bg transparent, color --white, border 1.5px #FFFFFF
MotionHover fills bg to --navy + color flip to white, 150ms ease-out · focus ring 2px #FFEA00 + 2px offset
AccessibilityContrast navy-on-white 16:1 AAA. Same touch 48×48. Keyboard Enter/Space.
Figma nameButton/Secondary/Default, Button/Secondary/Hover, Button/Secondary/Inverted, Button/Secondary/Focus
Used in section(s)§ 02 Hero (Chat with Daniel) · § 06 Service Card (Learn More) · § 14 Final CTA (Free Estimate)
Build-side HTML pattern<a class="btn btn--secondary" href="#daniel">Chat with Daniel</a>
CSS tokens--btn-secondary-border: 1.5px solid #1D1760; --btn-secondary-color: #1D1760; --btn-secondary-hover-bg: #1D1760; --btn-secondary-hover-color: #FFFFFF;
DemoChat with Daniel
§ CL-03 📞 Phone CTA Button (Emergency Yellow) Phase 1
PurposeThe single most-tapped element on location pages. Emergency Eric must be able to dial in one thumb tap. Yellow signals urgency; no other button uses this color.
Anatomy📞 phone icon 20×20 · number "(913) 963-1029" 17w800 monospace-leaning · optional "CALL NOW" overline 10w700 · 56h container mobile
Variantsdefault · hover · pressed · focus · compact (for sticky bar at 40h)
Mobile (375)Full-width 100%, 56h, padding 14/22, radius 8, yellow bg, navy text, label 17w800
Tablet (768)Auto or 50%, 54h, padding 14/24
Desktop (1440)Auto, 52h, padding 14/28, visible in header too
Tokensbg --yellow #FFEA00, color --navy #1D1760, shadow cta, radius 8, hover darkens to #E6D300
MotionSubtle pulse 1.6s ease-in-out on hero CTA only (box-shadow 0 0 0 0 → 0 0 0 8px rgba(255,234,0,0.3)) · hover translateY(-2px) 150ms · focus ring 2px #1D1760
AccessibilityContrast navy-on-yellow 12.8:1 AAA. <a href="tel:..."> for dial on mobile. aria-label "Call Bright Side Plumbing at 9 1 3, 9 6 3, 1 0 2 9." Keyboard Enter.
Figma nameButton/Phone/Default, Button/Phone/Hover, Button/Phone/Compact, Button/Phone/Pulse
Used in section(s)Sticky Emergency Bar (top) · § 02 Hero · § 05 Availability card · § 14 Final CTA · Mobile Sticky Call Bar (bottom) · Header (desktop)
Build-side HTML pattern<a class="btn btn--phone" href="tel:+19139631029" data-gtm="phone-cta-hero">📞 (913) 963-1029</a>
CSS tokens--btn-phone-bg: #FFEA00; --btn-phone-color: #1D1760; --btn-phone-radius: 8px; --btn-phone-font: 17px/1 'Inter', sans-serif; --btn-phone-weight: 800;
Demo📞 (913) 963-1029
§ CL-04 🔧 Service Card Phase 1
PurposeShows a single service offering (Sewer Repair, Drain Cleaning, Water Heater, etc.) with icon or photo, title, 2-line description, and "Learn More" link. The unit of the services grid in § 06.
AnatomyIcon 40 mobile / 64 desktop OR photo 100% aspect 3:2 · H3 title (20w700) · 2-line body (15w400) · optional badge top-right (e.g. "Most Requested") · Learn More link
Variantswith-icon (default) · with-photo (Phase 2) · with-badge (Most Requested / New / 24/7) · featured (2x size, yellow accent bar) · hover · focus
Mobile (375)Full-width, icon-left layout, 16 gap, padding 16, radius 12, ivory bg, shadow card-rest
Tablet (768)2-col grid, icon-top layout, padding 20, gap 16
Desktop (1440)3-col grid (2-col on location pages), icon-top layout, padding 24, gap 20, hover-lift translateY(-4px)
Tokensbg --ivory #F8FAFC, border 1px #E5E7EB, radius 12, shadow-rest 0 2px 8px rgba(0,0,0,0.04), shadow-hover 0 8px 24px rgba(29,23,96,0.12)
MotionHover translateY(-4px) + shadow grow 150ms ease-out · icon bounce +5% scale 150ms on hover
AccessibilityEntire card clickable but Learn More link as primary focus target. alt text on photo variant includes city + service. Keyboard: Tab to card, Enter opens detail.
Figma nameCard/Service/Icon, Card/Service/Photo, Card/Service/Badge, Card/Service/Featured, Card/Service/Hover
Used in section(s)§ 06 Services in [City] · Homepage services grid · Related Services on service detail pages
Build-side HTML pattern<article class="service-card service-card--featured"><span class="service-card__badge">Most Requested</span><img class="service-card__icon" .../><h3>Sewer Repair</h3><p>...</p><a>Learn More →</a></article>
CSS tokens--card-bg: #F8FAFC; --card-border: 1px solid #E5E7EB; --card-radius: 12px; --card-pad: 20px; --card-shadow-rest: 0 2px 8px rgba(0,0,0,0.04); --card-shadow-hover: 0 8px 24px rgba(29,23,96,0.12);
Demo
🚿

Drain Cleaning

Clears clogs fast with HD camera inspection and honest written estimate.

Learn More →
MOST REQUESTED
🔧

Sewer Repair

Camera inspection first, trenchless when we can, always a written quote.

Learn More →
♨️

Water Heater

Repair or replacement, tank or tankless, with same-day service in KC metro.

Learn More →
Hover the first card to see translateY(-4px) + shadow-hover motion. Middle card shows "Most Requested" badge variant with yellow accent top-border. No gradients — solid navy per Kalen.
§ CL-05 ⭐ Review Card Phase 1
PurposeSingle customer review with star row, body, author, platform badge, and city chip (new). Used in § 07 city-filtered reviews and global review rails.
AnatomyPlatform badge top-right (Google / Yelp / Angi) · 5-star row 100×24 mobile / 120×30 desktop · body text (15w400, 4-line clamp mobile / 5-line desktop) · author (14w700) + neighborhood/city chip · date (12w400 muted)
VariantsGoogle (blue G badge) · Yelp (red Yelp badge) · Angi (green Angi badge) · BBB (A+ blue badge) · Featured (yellow accent top-border, 2x size)
Mobile (375)100% w, 16 pad, 12 radius, ivory bg, shadow-rest, stars 100×24, body clamp 4 lines
Tablet (768)2-col grid, 18 pad
Desktop (1440)2 or 3-col grid, 422h fixed, 20 pad, stars 120×30, body clamp 5 lines
Tokensbg --ivory, border 1px #E5E7EB, radius 12, stars color #FFB800, quote mark opacity 0.08 navy
MotionCarousel scroll-snap on mobile · hover translateY(-2px) on desktop 150ms
Accessibilityaria-label "5 out of 5 stars" on star row. Link wraps platform badge out to live review. Keyboard Tab reaches review link.
Figma nameCard/Review/Google, Card/Review/Yelp, Card/Review/Angi, Card/Review/BBB, Card/Review/Featured
Used in section(s)§ 07 City-Filtered Reviews · Homepage reviews rail · Service page testimonials
Build-side HTML pattern<article class="review-card review-card--google"><span class="review-card__badge">Google</span><div class="review-card__stars">★★★★★</div><p>...</p><footer><b>Sarah M.</b> · <span class="chip chip--city">Overland Park</span></footer></article>
CSS tokens--review-bg: #F8FAFC; --review-radius: 12px; --review-star-color: #FFB800; --review-badge-google: #4285F4; --review-badge-yelp: #D32323; --review-badge-angi: #A3D65C;
Demo
G · GOOGLE
★★★★★

Juan and Matthew from Bright Side Plumbing did an awesome job repairing my collapsed sewer line and repaired the concrete over it too. Professional, on time, and honest pricing.

Rickey Farmer 📍 Overland Park
Apr 18, 2026
G · GOOGLE
★★★★★

Not enough kind words for these gentlemen who worked on our new sewer line. Chris, Bradley, and Trevor were exceptional. Would hire again in a heartbeat.

Caroline Owens 📍 Leawood
Apr 14, 2026 · Featured
Default Google variant (left) + Featured variant with yellow accent top-border (right). City chip reused from CL-08. Stars color #FFB800.
§ CL-06 🎯 Reveal Card (icon-left / text-right) Phase 1
PurposeMobile-optimal pain-point flag. Icon-left, text-right. Used for guarantees, why-choose-us bullets, and "We fix this in [City]" mini-cards. Reads fast on a phone, no vertical stacking overhead.
AnatomyIcon 32 circle tile with solid navy #1D1760 (no gradient per Kalen Apr 21 meeting) · text column: H4 title (15w700) + 2-line body (13w400 muted) · 12 gap between icon and text
Variantsdefault · hover · accent-blue (blue icon bg) · accent-yellow (yellow icon bg for emergency) · accent-green (guarantee / success)
Mobile (375)100% w, 12 pad, 8 radius, ivory bg, icon 32 circle, gap 12, text stacked right
Tablet (768)2-col grid, 14 pad, icon 36
Desktop (1440)3-col or 4-col grid, 16 pad, icon 40
Tokensbg --ivory, border 1px rgba(29,23,96,0.08), radius 8, icon bg solid #1D1760 (default) / #30C5FF (accent-blue) / #FFEA00 (accent-yellow) / #22C55E (accent-green). No gradients — Kalen preference lock Apr 21.
MotionHover translateY(-1px) 150ms · icon rotate 6deg on hover · reduced-motion: no transform
AccessibilityIcon is decorative, aria-hidden="true". Title and body text are the semantic content. Keyboard-navigable only if card is a link.
Figma nameCard/Reveal/Default, Card/Reveal/Hover, Card/Reveal/Blue, Card/Reveal/Yellow, Card/Reveal/Green
Used in section(s)§ 05 Availability (variants) · § 11 Guarantees row · § 13 FAQ intro bullets · Homepage "Why BSP" row
Build-side HTML pattern<div class="reveal reveal--blue"><span class="reveal__icon">🛡️</span><div class="reveal__text"><h4>Licensed & Insured</h4><p>...</p></div></div>
CSS tokens--reveal-bg: #F8FAFC; --reveal-radius: 8px; --reveal-pad: 12px; --reveal-icon-size: 32px; --reveal-icon-bg: #1D1760; --reveal-icon-bg-blue: #30C5FF; --reveal-icon-bg-yellow: #FFEA00; --reveal-icon-bg-green: #22C55E; --reveal-gap: 12px;
Demo

Licensed & Insured

KCMO + Johnson County plumbing licenses, full liability coverage.

Same-Day Service

Book before 2 PM, we are there today. Overland Park, Olathe, Lenexa, Shawnee.

24/7 Emergency

Burst pipe at 2 AM? We dispatch in 5 minutes, on-site in 60.

$50 On-Time Guarantee

We are late, you are paid. Plus $200 clean-tech guarantee every visit.

4 variants in 2×2 grid: default (navy), accent-blue, accent-yellow (emergency), accent-green (guarantee). Icons aria-hidden — title + body are semantic content. Hover first card for translateY(-1px) motion. Solid fills only — no gradients.
§ CL-07 🛡️ Trust Chip Phase 1
PurposeSmall pill with icon + short label. Used in the trust bar (§ 04) to stack credibility signals: star rating, BBB, generations, same-day, licensed, free estimates.
AnatomyLeading icon 16×16 or emoji · label 13w600 · rounded-rect shape (radius 4 NOT 999) · 28h pill · 6 gap between icon and text
Variantsdefault (on light bg) · inverted (on navy bg) · accent-yellow (emergency signal) · accent-green (active guarantee)
Mobile (375)28h, pad 4/10, font 13w600, radius 4, icon 14, gap 6
Tablet (768)30h, pad 5/12, font 13w600
Desktop (1440)32h, pad 6/14, font 14w600, icon 16
Tokensdefault bg rgba(48,197,255,0.12), color --navy, border 1px #30C5FF; inverted bg rgba(255,255,255,0.12), color --white, border 1px rgba(255,255,255,0.4)
MotionStatic (no hover). If linked, hover translateY(-1px) 150ms.
AccessibilityIcon aria-hidden. Label is the semantic content. If wrapping a trust signal as a link (e.g. to a reviews page), use proper <a> with aria-label.
Figma nameChip/Trust/Default, Chip/Trust/Inverted, Chip/Trust/Yellow, Chip/Trust/Green
Used in section(s)§ 04 Trust Bar (6 chips) · § 02 Hero (review chip) · Header credibility row (desktop)
Build-side HTML pattern<span class="chip chip--trust">⭐ 4.9 Google (384+)</span>
CSS tokens--chip-trust-bg: rgba(48,197,255,0.12); --chip-trust-color: #1D1760; --chip-trust-border: 1px solid #30C5FF; --chip-trust-radius: 4px; --chip-trust-pad: 4px 10px;
Demo⭐ 4.9 Google (384+)
§ CL-08 📍 City Chip (geo-token pill) — NEW Phase 1
PurposeGeo-token pill that links to a city's location page. Used in neighborhoods grid (§ 09 — non-clickable neighborhood names), nearby cities grid (§ 13 — clickable internal links to sibling location pages), and homepage service areas. Differentiates from Trust Chip by shape (fully round 999 radius reads as place-marker, not generic tag).
Anatomy📍 12×12 pin icon (optional) · city name 13w500 mobile / 14w500 desktop · optional trailing live-availability dot · pill shape radius 999 · 28h mobile / 32h desktop
Variantsdefault · hover · current (active page) · available (P2 with live-dot + ETA) · visited · focus
Mobile (375)28h, pad 4/12, font 13w500, radius 999, icon 12, gap 6
Tablet (768)30h, pad 5/13, font 13.5w500
Desktop (1440)32h, pad 6/14, font 14w500, icon 14
Tokens (default)bg rgba(48,197,255,0.12), border 1px solid #30C5FF, color --navy #1D1760, radius 999px
Tokens (hover)bg #30C5FF, color #FFFFFF, translateY(-1px), transition 150ms ease-out
Tokens (current)bg #1D1760, color #FFFFFF, border 1px solid #1D1760 (you-are-here signal)
Tokens (available P2)Appends 8×8 green dot #22C55E with 1.2s pulse, plus "45m" 11w700 text right of dot
MotionHover translateY(-1px) + bg fill 150ms ease-out · available-dot pulse 1.2s ease-in-out infinite · focus ring 2px #FFEA00 + 2px offset
AccessibilityPin icon aria-hidden. Touch 28h violates 48×48 baseline — compensate with 8‑px outer margin so hit area ≥ 44×44, and enforce gap between chips so thumbs do not mis-tap. aria-current="page" on current variant.
Figma nameChip/City/Default, Chip/City/Hover, Chip/City/Current, Chip/City/Available, Chip/City/Visited, Chip/City/Focus
Used in section(s)§ 09 Neighborhoods Served (neighborhood names, non-clickable P1) · § 13 Nearby Cities (clickable internal links) · Homepage § 09 Service Areas · FAQ "Do you serve my area?" answer · Review Card city badge
10x revealEach chip can show live availability from the live-availability module (§ 05). No KC competitor does this. Flip from static pill to "📍 Overland Park · 🟢 45m" when a crew is available in that city.
Build-side HTML pattern<a class="chip chip--city" href="/plumber/overland-park/" data-city="overland-park">📍 Overland Park</a>
CSS tokens--chip-city-bg: rgba(48,197,255,0.12); --chip-city-border: 1px solid #30C5FF; --chip-city-color: #1D1760; --chip-city-radius: 999px; --chip-city-pad: 4px 12px; --chip-city-hover-bg: #30C5FF; --chip-city-hover-color: #FFFFFF; --chip-city-current-bg: #1D1760; --chip-city-current-color: #FFFFFF; --chip-city-available-dot: #22C55E;
Demo📍 Overland Park📍 Olathe (current)📍 Leawood · 🟢 45m
§ CL-09 ❓ FAQ Accordion Row Phase 1
PurposeExpandable question + answer pair. Drives FAQPage schema rich results. Used in § 13 City FAQ (6 Q&A minimum) and homepage FAQ block.
AnatomyQuestion row: H4 question (16w700) · + / − toggle icon 20 right-aligned · divider 1px · Answer row: body text (15w400, 1.6 line-height) · optional Learn More link
Variantscollapsed (default) · expanded · hover (question bg tint) · focus
Mobile (375)100% w, question pad 16, answer pad 0/16/16, min touch 48h question row
Tablet (768)Same but 18 pad
Desktop (1440)Max-width 820, centered, 20 pad
Tokensbg --white, border-bottom 1px #E5E7EB, question color --navy, answer color #475569, icon color --blue
MotionExpand max-height 0 → auto 220ms ease-in-out · chevron rotate 0 → 180deg 220ms · reduced-motion: instant open no transform
Accessibility<details>/<summary> native or button with aria-expanded + aria-controls. Keyboard Enter/Space toggles. FAQPage schema mandatory.
Figma nameFAQ/Row/Collapsed, FAQ/Row/Expanded, FAQ/Row/Hover, FAQ/Row/Focus
Used in section(s)§ 13 City FAQ (6 rows min) · Homepage FAQ (§ 11) · /faqs/ main page
Build-side HTML pattern<details class="faq-row"><summary>How fast can you get to Overland Park?</summary><div class="faq-row__body">Same-day service, averaging 45 minutes...</div></details>
CSS tokens--faq-bg: #FFFFFF; --faq-border: 1px solid #E5E7EB; --faq-q-color: #1D1760; --faq-a-color: #475569; --faq-icon-color: #30C5FF; --faq-pad: 16px; --faq-transition: 220ms ease-in-out;
Demo
How fast can you get to Overland Park? +
Same-day service in Overland Park, averaging 45-60 minutes from call to truck-on-site during business hours. After-hours emergency dispatch averages 60-75 minutes. Book online for a guaranteed same-day window if you call before 2 PM.
Do you charge extra for weekend emergency service? +
No nights/weekends/holidays surcharge. Dispatch fee is flat $89 anytime and waived entirely if you proceed with the repair. The price we quote is the price you pay.
What areas do you serve beyond Overland Park? +
The full KC metro — Olathe, Lenexa, Shawnee, Leawood, Prairie Village, Mission, Merriam, Roeland Park, Fairway, Lee's Summit, and Kansas City MO. Click a nearby city to see their own landing page.
Native <details>/<summary> — no JS needed. Middle row shown in expanded state (+ rotated to ×). Add FAQPage JSON-LD for rich-result eligibility.
§ CL-10 🌊 Wave Divider (inline SVG) Phase 1
PurposeSignature BSP wave transition between sections (water brand nod). Inline SVG for infinite scale + theming. Breaks the endless-scroll feel, introduces rhythm.
AnatomyInline SVG viewBox 0 0 1440 80 · path is a single sine-ish curve · fill = incoming section bg · sits above or below section boundary
Variantstop (convex up) · bottom (convex down) · full (both edges) · navy · ivory · blue-soft · inverted
Mobile (375)40h, preserveAspectRatio="none", fills width
Tablet (768)60h
Desktop (1440)80h · natural SVG size
Tokensfill = whichever section bg is being transitioned INTO. No own color.
MotionStatic. Phase 2 option: subtle horizontal drift 20s linear infinite on desktop only.
AccessibilityPurely decorative. aria-hidden="true". No semantic value. Do not use as a heading separator.
Figma nameDivider/Wave/Top, Divider/Wave/Bottom, Divider/Wave/Full, Divider/Wave/Navy, Divider/Wave/Ivory
Used in section(s)Every section boundary (optional). Mandatory between Hero→Trust, Services→Reviews, Financing→FAQ, Final CTA→Footer.
Build-side HTML pattern<svg class="wave wave--bottom" viewBox="0 0 1440 80" preserveAspectRatio="none" aria-hidden="true"><path d="M0,40 C360,80 1080,0 1440,40 L1440,80 L0,80 Z" fill="#F8FAFC"/></svg>
CSS tokens--wave-h-mobile: 40px; --wave-h-tablet: 60px; --wave-h-desktop: 80px; --wave-fill: inherit;
Demo
Section A — navy bg
Section B — ivory bg (wave transitions INTO this bg color)
Section C — blue bg (flipped wave via transform:scaleY(-1))
Inline SVG (not PNG per Kalen Apr 21) — infinite scale. preserveAspectRatio="none" lets it stretch. aria-hidden="true" because it's pure decoration. Flip direction with transform:scaleY(-1) for convex-up variant.
§ CL-11 🚪 Step Tile (numbered process) Phase 1
PurposeOne step in the "How It Works" sequence. Numbered circle + title + body. Drives the 3-step scroll in § 08.
AnatomyNumber-circle 48 (navy bg, yellow number, 22w800) · H4 title (17w700) · 2-line body (15w400, muted) · optional connecting line to next tile on desktop
Variantsdefault · active (current step) · complete (checkmark replaces number) · with-image (Phase 2 live-crew ETA map)
Mobile (375)100% w, 16 pad, number-circle top-center, text centered, 16 gap between tiles
Tablet (768)3-col row, number top-left of each tile, text below, connector lines between
Desktop (1440)3-col row, 20 pad, connector line 2px dashed navy between tiles at number-circle height
Tokensnumber-circle bg --navy, number color --yellow, title color --navy, body color #475569, connector 2px dashed #1D1760
MotionActive state pulse on number-circle 1.5s · scroll-reveal fade-up 300ms stagger 80ms per tile
AccessibilityUse an ordered list <ol> for native step semantics. Number is aria-hidden if it is in the list marker. aria-current="step" on active.
Figma nameTile/Step/Default, Tile/Step/Active, Tile/Step/Complete, Tile/Step/WithImage
Used in section(s)§ 08 How It Works (3 tiles) · Homepage § 07 process · Booking flow confirmation
Build-side HTML pattern<li class="step-tile step-tile--active"><span class="step-tile__num">1</span><h4>Call or book online</h4><p>...</p></li>
CSS tokens--step-circle-size: 48px; --step-circle-bg: #1D1760; --step-circle-color: #FFEA00; --step-title-color: #1D1760; --step-body-color: #475569; --step-gap: 16px;
Demo
  1. Call or book online

    Daniel or our office answers in 15 seconds. Book a slot same-day.

  2. We dispatch a crew

    Licensed tech on the road in 5 minutes. You see live ETA on the map.

  3. We fix it, you approve

    Written estimate before we touch anything. No surprises. No upsell.

3 states in 1 view: default (left) / active with blue-ring pulse (middle, aria-current="step") / complete with green check (right). Ordered <ol> for semantic step order. Respects prefers-reduced-motion.
§ CL-12 📱 Sticky Mobile Call Bar (floating bottom) Phase 1
PurposeAlways-visible floating bottom bar on mobile only. Two taps max away from a call or a booking regardless of scroll depth. Addresses site-wide known issue #8 "No sticky mobile CTA."
AnatomyLeft 50%: Phone CTA (yellow bg, navy text, phone icon + "Call Now") · Right 50%: Book CTA (blue bg, navy text, calendar icon + "Book") · 64h bar with 16 safe-area bottom inset
Variantsdefault (both CTAs) · emergency (after-hours: full-width yellow phone CTA, Book hidden) · hidden (scrolls off briefly when user scrolls up, reappears at rest)
Mobile (375)Fixed bottom, 64h + safe-area, 100% w, navy bg, 2 CTAs 48h each with 4 gap, z-index 100, box-shadow 0 -4px 12px rgba(0,0,0,0.12)
Tablet (768)Hidden — use desktop header CTAs instead
Desktop (1440)Hidden — header phone CTA + hero book CTA cover this role
Tokensbar bg --navy, phone bg --yellow, book bg --blue, both text color --navy, radius 0 (full-bleed bar)
MotionSlide up from bottom 240ms ease-out on mount · hide on scroll-down (transform translateY 100%) 180ms · show on scroll-up or scroll-rest 200ms
AccessibilityRespects env(safe-area-inset-bottom) for iPhone home bar. Each CTA aria-labeled. Hidden from screen readers when off-screen via aria-hidden toggle. Keyboard Tab reaches both CTAs.
Figma nameBar/Sticky/Mobile/Default, Bar/Sticky/Mobile/Emergency, Bar/Sticky/Mobile/Hidden
Used in section(s)Every location page (mobile only) · Homepage (mobile only) · Every service page (mobile only)
Build-side HTML pattern<aside class="sticky-bar"><a class="sticky-bar__phone" href="tel:...">📞 Call</a><a class="sticky-bar__book" href="/book/">📅 Book</a></aside>
CSS tokens--sticky-bar-bg: #1D1760; --sticky-bar-height: 64px; --sticky-bar-safe: env(safe-area-inset-bottom); --sticky-bar-z: 100; --sticky-bar-shadow: 0 -4px 12px rgba(0,0,0,0.12);
Demo
📱 iPhone 375 viewport preview (scroll inside bar at bottom)

Sample location page content would scroll here. Sticky bar below stays fixed regardless of scroll depth.

Burst pipe symptoms, trust signals, service grid, reviews, FAQ — all scroll under the bar.

The bar stays accessible on every tap, solving the #8 site-wide conversion blocker.

64h with 16px safe-area-inset-bottom for iPhone home-bar compensation.

Auto-hides on scroll-down, returns on scroll-up or rest.

Mobile-only — tablet and desktop hide this in favor of header CTAs. env(safe-area-inset-bottom) keeps clear of iPhone home bar. Both CTAs 48h for comfortable thumb targets.
§ CL-13 💬 Daniel AI Chat Widget Phase 2
PurposeFloating circular bubble bottom-right that opens a chat panel. Daniel is BSP's 24/7 plumbing assistant. Qualifies leads, books jobs, answers common questions, hands off to dispatch.
Anatomy (trigger)64×64 circular bubble · blue bg #30C5FF · white droplet icon 💧 32×32 centered · subtle pulse ring every 8s · optional red notification badge top-right
Anatomy (panel)380w × 560h · navy header (48h: avatar + "Daniel · BSP Assistant" + close ×) · ivory message area (scrollable, bubbles left-AI / right-user) · input bar bottom (48h text input + blue send button)
Variantsclosed (bubble only) · open (panel visible) · minimized (bubble with unread count) · typing (3-dot loader) · offline (fallback "Leave a message" form)
Mobile (375)Bubble 56×56 bottom-right (16 inset, above sticky-bar) · panel becomes full-screen overlay on open
Tablet (768)Bubble 60×60 · panel 380×520 anchored bottom-right
Desktop (1440)Bubble 64×64 · panel 380×560 anchored bottom-right, 24 inset
Tokensbubble bg --blue, icon --white, pulse ring rgba(48,197,255,0.4), panel header --navy, message area --ivory, user bubble --blue, AI bubble --white w/ border
MotionBubble pulse ring scale 1 → 2 + fade every 8s · open panel scale 0.9 → 1 + fade 220ms ease-out · reduced-motion: fade only
AccessibilityBubble aria-label "Chat with Daniel, Bright Side's plumbing assistant." Panel has aria-live="polite" on message area. Keyboard Tab reaches all interactive elements. Focus trap inside panel when open, Esc closes.
Opening copy"Hi, I'm Daniel — Bright Side's 24/7 plumbing assistant. What's going on?"
Backend integrationVapi assistant id e2920d04 · phone line (913) 963-9817 · webhook /api/daniel/book on VM · storm-alerts Slack DM to Ashton (10s SLA)
Figma nameWidget/Daniel/Bubble, Widget/Daniel/Panel/Open, Widget/Daniel/Panel/Typing, Widget/Daniel/Panel/Offline, Widget/Daniel/Mobile/Fullscreen
Used in section(s)Every page (floating) · deep-link from § 02 Hero "Chat with Daniel" secondary CTA
Build-side HTML pattern<div id="daniel-widget" data-vapi-assistant="e2920d04" data-city="overland-park"></div> (hydrated by JS)
CSS tokens--daniel-bubble-bg: #30C5FF; --daniel-bubble-size: 64px; --daniel-panel-w: 380px; --daniel-panel-h: 560px; --daniel-header-bg: #1D1760; --daniel-msg-bg: #F8FAFC; --daniel-pulse: rgba(48,197,255,0.4);
Demo
Closed (bubble)
Open (panel)
💧
Daniel
BSP Assistant · 24/7
Hi, I'm Daniel — Bright Side's 24/7 plumbing assistant. What's going on?
Sewer backing up in the basement, Overland Park
Got it. I'm dispatching a camera-capable crew — avg ETA 45 min from Overland Park. What's the best callback number?
Bubble shows pulse-ring every 2s (not 8s — sped up for demo). Panel states: open with message history + typing indicator (3 animated dots). Vapi assistant id e2920d04 hydrated on mount. aria-live="polite" on message area for screen readers. Respects prefers-reduced-motion.
§ CL-14 📝 Multi-Step Form Field Phase 1
PurposeSingle form field with 3 step states. Multi-step forms lift conversion 86% over single-step (known issue #9). Used in the § 14 Final CTA multi-step form and Daniel's panel.
AnatomyLabel (13w700 uppercase) · input 48h · placeholder (15w400 muted) · optional helper text below · optional leading icon inside input · right-side validation icon (✓ success / ⚠ error)
Step states(1) idle: gray border, placeholder visible · (2) active: blue border + focus ring, label shrinks above · (3) completed: green border + checkmark, label above, value locked but editable
Variantstext · email · phone · select (with chevron) · textarea · radio-group · checkbox · error
Mobile (375)100% w, 48h input, 16 pad horizontal, 12 vertical margin, label 13w700 above, body 15w400 inside
Tablet (768)Same with 18 vertical spacing
Desktop (1440)Max-width 420 for single fields, 100% in 2-col form grid
Tokensidle border 1px #CBD5E1, active border 1.5px #30C5FF, completed border 1.5px #22C55E, error border 1.5px #EF4444, radius 8, bg --white, text --navy
MotionLabel float up 180ms ease-out on focus · border color transition 150ms · validation icon pop scale 0 → 1 180ms ease-out · step progress bar fills 300ms ease-out
AccessibilityNative <label for> binding. aria-invalid and aria-describedby for errors. autocomplete tokens set (tel, email, postal-code). Keyboard Tab order logical. 48h input honors touch target.
Figma nameForm/Field/Idle, Form/Field/Active, Form/Field/Completed, Form/Field/Error, Form/Field/Select, Form/Field/Textarea
Used in section(s)§ 14 Final CTA multi-step form (3 steps: What / Where / Who) · Daniel AI Chat input · Booking confirmation · /contact-us/ form
Build-side HTML pattern<div class="field field--active"><label for="phone">Phone</label><input id="phone" type="tel" autocomplete="tel" placeholder="(913) 555-0123"><span class="field__valid">✓</span></div>
CSS tokens--field-h: 48px; --field-radius: 8px; --field-bg: #FFFFFF; --field-border-idle: 1px solid #CBD5E1; --field-border-active: 1.5px solid #30C5FF; --field-border-done: 1.5px solid #22C55E; --field-border-err: 1.5px solid #EF4444; --field-label-color: #1D1760;
Demo
STEP 2 OF 3 · WHERE
Completed
Active — tab to next field
Idle
Error — please enter a valid email
4 states in one form: completed (green ✓) · active (blue outline + focus ring) · idle (gray border) · error (red border + ⚠ + aria-invalid + aria-describedby). Progress bar top shows Step 2 of 3. Native autocomplete tokens tel/email/postal-code.

Cross-reference matrix

Which component shows up in which location-page section. Audrey uses this to confirm every section has its components inventoried; Robert uses this to confirm every build task pulls from the library (not from scratch).

SectionPrimary componentsSupporting
§ 01 Sticky Emergency BarPhone CTA (compact)Trust Chip (city name)
§ 02 City HeroPrimary CTA · Phone CTA · Secondary CTATrust Chip · Reveal Card (availability)
§ 03 Embedded MapWave Divider
§ 04 Trust BarTrust Chip (×6)
§ 05 Live AvailabilityReveal Card (green accent)Phone CTA · City Chip (available variant)
§ 06 Services in [City]Service Card (featured + 5 default)Wave Divider
§ 07 City-Filtered ReviewsReview Card (Google · Yelp · Angi)City Chip (in review footer)
§ 08 How It WorksStep Tile (×3)
§ 09 Neighborhoods ServedCity Chip (neighborhood variant, 8–12)
§ 10 Local LandmarksReveal Card (optional)
§ 11 Before/After Gallery (P2)Service Card (photo variant)City Chip (street/city tag)
§ 12 Financing + GuaranteesReveal Card (guarantees ×3)Primary CTA (Apply)
§ 13 City FAQFAQ Accordion Row (×6)
§ 14 Nearby CitiesCity Chip (×5-6, clickable)
§ 15 Final CTA + FormMulti-Step Form Field · Primary CTA · Phone CTAWave Divider
Floating (all pages)Sticky Mobile Call Bar · Daniel AI Chat Widget

Component build order — Phase 1 ship priority

  1. Phone CTA (CL-03) — single most tapped element, blocks everything
  2. Primary + Secondary CTA (CL-01, CL-02) — fundamental button system
  3. Trust Chip (CL-07) — used 6× in trust bar, simplest chip
  4. City Chip (CL-08) — drives the entire internal-link moat, unique to location pages
  5. Service Card (CL-04) — § 06 unit, reused from homepage
  6. Review Card (CL-05) — § 07 moat unit
  7. Reveal Card (CL-06) — powers availability + guarantees
  8. Step Tile (CL-11) — § 08 process
  9. FAQ Accordion (CL-09) — § 13 FAQPage schema driver
  10. Wave Divider (CL-10) — section rhythm
  11. Multi-Step Form Field (CL-14) — § 15 conversion
  12. Sticky Mobile Call Bar (CL-12) — floating global
  13. Daniel AI Chat Widget (CL-13) — Phase 2 polish, wire Vapi integration
🎨

Brand Tokens (inherited from Homepage)

Color palette — BSP Navy / Bright Blue / Yellow

--navy
#1D1760
--navy-deep
#100B3E
--blue
#30C5FF
--blue-soft
#BEE6F5
--yellow
#FFEA00
--ivory
#F8FAFC
--white
#FFFFFF
--success
#22C55E
--danger
#EF4444

Usage rules

  • Navy #1D1760 — primary text, H1/H2, hero/CTA/footer backgrounds
  • Blue #30C5FF — primary CTA, link color, trust-chip border, section dividers
  • Yellow #FFEA00 — emergency accent ONLY: phone CTA, sticky emergency bar text, "Same-Day in [City]" chip, focus ring. Never body text.
  • Ivory #F8FAFC — card bg, review card bg, neighborhoods grid bg
  • Success #22C55E — live-availability dot, guarantee badges
  • Danger #EF4444 — sticky emergency bar bg on mobile, FAQ warning callouts

Motion (inherited)

  • Hover lift: translateY(-2px) 150ms ease-out on service / review / city chip
  • FAQ expand: max-height 0 → auto 220ms ease-in-out
  • Availability pulse: 1.2s ease-in-out on green dot, 50% opacity swing
  • Scroll reveal: sections fade in 300ms when 15% visible (Phase 2 polish)
  • Reduced motion: respect prefers-reduced-motion — disable transform, keep opacity only
🏗️

The 14-Section Architecture — scroll order

⚠ Numbering note: This architecture diagram numbers the 14 content sections (01 Hero through 14 Final CTA). The Section-by-Section Figma Specs below include the Sticky Emergency Bar as § 01 and shift every downstream section by 1. If you match specs to architecture, add 1 to architecture numbers (Arch 01 = Spec § 02).

Mobile scroll flow. Every section does ONE job and serves at least one of the 10 ranking signals. Visitor lands, sees emergency proof, scrolls sales conversation, closes. Each section is labeled with which persona it targets primarily.

┌───────────────────────────────────────┐ │ 🚨 STICKY EMERGENCY BAR (fixed top) │ Eric persona │ "📞 (913) 963-1029 · same-day [City]" │ Red bg, yellow text ├───────────────────────────────────────┘ ↓ ┌───────────────────────────────────────┐ │ 01 · 📍 HERO — city-specific H1 │ Eric persona · P1 │ H1: "[Service] in [City]" │ ⭐4.9 + 384+ reviews │ 📞 Call CTA 📅 Book CTA 💬 Chat │ Trust chips └───────────────────────────────────────┘ ↓ ┌───────────────────────────────────────┐ │ 02 · 📍 EMBEDDED GOOGLE MAP │ All personas · P1 │ Centered on city lat/lng, BSP pin │ Builds proximity trust └───────────────────────────────────────┘ ↓ ┌───────────────────────────────────────┐ │ 03 · 🛡️ TRUST BAR (reused) │ Rachel · P1 │ 6 chips: ⭐4.9 · BBB · 5-Gen │ Matches homepage │ · Same-Day · Licensed · Free Est. │ └───────────────────────────────────────┘ ↓ ┌───────────────────────────────────────┐ │ 04 · 🌁️ LIVE AVAILABILITY (10x) │ Eric · P1 (static) / P2 (live) │ "Techs available in [City] today" │ Green dot pulses │ "45-min avg response time" │ Phase 2 pulls ST dispatch └───────────────────────────────────────┘ ↓ ┌───────────────────────────────────────┐ │ 05 · 🔧 SERVICES IN [CITY] (H2) │ All personas · P1 │ Sewer FIRST, then 5 more │ Opener paragraph │ City-flavored intro "In [City],..." │ references landmark + soil └───────────────────────────────────────┘ ↓ ┌───────────────────────────────────────┐ │ 06 · 💬 CITY-FILTERED REVIEWS (MOAT) │ All · P1 │ Live Google API · regex-filter │ keywords: [city] or │ body mentioning city/neighborhood │ neighborhood names └───────────────────────────────────────┘ ↓ ┌───────────────────────────────────────┐ │ 07 · 🚪 HOW IT WORKS (3 steps) │ Eric + Rachel · P1 │ 1 Call / 2 We arrive / 3 Fix │ Same as homepage │ │ P2: live crew ETA map └───────────────────────────────────────┘ ↓ ┌───────────────────────────────────────┐ │ 08 · 🏠 NEIGHBORHOODS SERVED (H2) │ Mike + Rachel · P1 │ 8–12 real neighborhood names │ Pill chip grid │ "We fix pipes from [A] to [Z]" │ per-city data object └───────────────────────────────────────┘ ↓ ┌───────────────────────────────────────┐ │ 09 · 🏛️ LOCAL LANDMARKS (copy block) │ Rachel · P1 │ Opening paragraph references 3–5 │ "From Oak Park Mall to │ landmarks naturally in body copy │ the homes off 151st..." └───────────────────────────────────────┘ ↓ ┌───────────────────────────────────────┐ │ 10 · 📷 BEFORE/AFTER GALLERY (P2 10x) │ Rachel + Eric · P2 │ 3–4 jobs near [City] │ Street-name tagged │ Placeholder dashed-border in P1 │ └───────────────────────────────────────┘ ↓ ┌───────────────────────────────────────┐ │ 11 · 💳 FINANCING + GUARANTEES │ Mike + Rachel · P1 │ Financing card + 3 guarantee tiles │ Reused homepage specs └───────────────────────────────────────┘ ↓ ┌───────────────────────────────────────┐ │ 12 · ❓ CITY FAQ (6 Q&A minimum) │ All · P1 · FAQPage schema │ 1 pricing · 1 response · 1 area │ Rich-result eligible │ 1 emergency · 1 financing · 1 trust │ 6 minimum └───────────────────────────────────────┘ ↓ ┌───────────────────────────────────────┐ │ 13 · 📍 NEARBY CITIES (internal link grid) │ Mike · P1 │ 5–6 neighboring /plumber-in-*/ │ Internal link equity │ chips clickable │ Per-city data object └───────────────────────────────────────┘ ↓ ┌───────────────────────────────────────┐ │ 14 · 📞 FINAL CTA + MULTI-STEP FORM │ All · P1 │ "Ready to fix it right in [City]?" │ Form submits w/ city │ Wave-to-footer transition │ pre-selected └───────────────────────────────────────┘ ↓ ┌───────────────────────────────────────┐ │ FOOTER (global) + 8-LAYER SCHEMA STACK │ All pages └───────────────────────────────────────┘ + 📾 Floating (ALL pages, inherited): 💬 Daniel AI chat widget (bottom-right) 📞 Mobile sticky call bar (bottom)
💼

Location Library — One Data Object Per City

Every location page is assembled from a data object. Add a new city = add one JSON entry. Audrey never hand-wires city copy.

Where the library lives

File: /opt/nexus/nexus/scripts/output/location_library.json (single source of truth). Consumed by the build pipeline at Zone A (prep) to populate city-specific slots in the cloned template.

How to add a city (5 minutes)

  1. Copy an existing entry. Start with overland-park (the gold standard). Every field is required; do not leave any blank.
  2. Fill the data object below. Use real neighborhood + landmark names — Google checks these.
  3. Commit via Claude. Hand Robert the JSON block. Claude validates schema, appends to location_library.json, and runs the schema-lint before commit.
  4. Build the page. Pipeline runs with tuple (target_post_id, city_slug). 30–60 min end-to-end.

Data object schema (copy this template)

{
  "slug": "overland-park",
  "name": "Overland Park",
  "state": "KS",
  "lat": 38.9822,
  "lng": -94.6708,
  "zip_primary": "66213",
  "zip_range": ["66204", "66206", "66207", "66210", "66211", "66212", "66213", "66214", "66221", "66223", "66224", "66251"],
  "tier": 1,
  "neighborhoods": [
    "Blue Valley", "Corporate Woods", "Deer Creek", "Indian Hills",
    "Leabrooke", "Nottingham", "Oak Park", "Sycamore Hills",
    "The Village", "Wilshire Farms"
  ],
  "landmarks": [
    "Oak Park Mall",
    "Deanna Rose Children's Farmstead",
    "Overland Park Arboretum",
    "Corporate Woods",
    "Town Center Plaza"
  ],
  "housing_stock_note": "Homes along 151st and Metcalf are often built on Kansas clay soil that shifts enough to crack old cast iron sewer lines.",
  "common_issue": "Slow-draining sewers from root intrusion in older cast iron.",
  "hero_image_asset_id": 1234,
  "nearby_cities": ["leawood", "prairie-village", "lenexa", "olathe"],
  "review_filter_patterns": [
    "overland park", "op", "blue valley", "corporate woods", "deer creek",
    "indian hills", "leabrooke", "oak park", "town center", "arboretum"
  ],
  "meta_title": "Plumber in Overland Park KS | Sewer Repair | Bright Side Plumbing",
  "meta_desc": "Sewer repair, drain cleaning, water heater service in Overland Park KS. 5th-generation family plumber. Same-day. Call (913) 963-1029.",
  "phase": 1
}

Field rules: neighborhoods must be 8–12 real entries (Google checks). landmarks 3–5 recognizable places. review_filter_patterns case-insensitive regex patterns that catch real review mentions. hero_image_asset_id is the WP media ID after Audrey's hero photo is uploaded (Figma export → WP /wp/v2/media).

Tier priority (which city next)

  1. Tier 1 (Phase 1 build — 4 cities): Overland Park · Olathe · Lenexa · Shawnee. BSP HQ adjacency + highest search volume. Ship first.
  2. Tier 2 (Phase 2 build — 5 cities): Leawood · Prairie Village · Kansas City KS · Gardner · Spring Hill. High-value residential + growing exurbs.
  3. Tier 3 (Phase 3 build — 6 cities): Merriam · Mission · Roeland Park · De Soto · Bonner Springs · Edwardsville. Metro-edge fill + internal-link cluster.
  4. Matches the authoritative "Tier Order — 15 KC Metro Cities" table below. Do not diverge.

Every spec below pulls from this data object. When Audrey sees [City] in a spec, it's a placeholder substituted from data.name. When she sees [Landmark X], it's data.landmarks[N].

📋

Section-by-Section Figma Specs (14)

Per-section brief. Audrey reads this and designs knowing purpose, KPI, copy, mobile layout, desktop layout, components used, phase, inheritance. Cross-references the Homepage Playbook for shared components.

§ 01 🚨 Sticky Emergency Bar (floating top) Phase 1 · NEW
PurposeEmergency Eric lands. Pipe is leaking. Phone must be tappable within 1 second, city must read back so he knows he's in the right place.
KPIMobile tap-through rate on 📞 bar ≥ 8% · time-to-tap < 5s from landing
Copy🚨 Emergency? Call (913) 963-1029 · Same-day service in [City]
Mobile layoutFixed top, 48h, red bg (#EF4444), yellow text (#FFEA00) for phone, white text for rest. Phone underlined. 100% width. Above nav.
Desktop layoutFixed top, 40h, red bg, centered text, phone inline.
ComponentsNew: Emergency Bar (variants: mobile / desktop)
Dismissable?No. Persists all pages. Scrolls with body (position:fixed so always visible).
👀 Polished Demo
🚨 Emergency? (913) 963-1029 · Same-day service in Overland Park LIVE
Rendered mini-preview — use for composition reference, not pixel-final measurements.
§ 02 📍 City Hero — Above the Fold Phase 1
PurposeConfirm location. Win in 5 seconds. Give Eric a phone and Rachel a reason to keep scrolling.
KPIHero CTA CTR ≥ 20% mobile · bounce < 40%
H1 formula[Service] in [City] — [Credential]. Example: "Sewer Repair in Overland Park — 5th-Generation Master Plumbers"
Subtitle"Same-day service across [City] and nearby. Licensed, insured, family-owned since the 1800s."
Meta title50–60 chars: "Plumber in [City] KS | Sewer Repair | Bright Side Plumbing"
Meta desc150–160 chars: "Sewer repair, drain cleaning, water heater service in [City] KS. 5th-generation family plumber. Same-day. Call (913) 963-1029."
Mobile layoutHero photo top 220h (branded truck parked on city street) · H1 32 · subtitle 18 · ⭐ review chip + 384+ reviews · Phone CTA 📞 yellow + Book CTA 📅 blue, stacked 100% w
Desktop layoutFull-bleed bg 600h · left 600w text column · H1 48 · subtitle 28 · CTA row inline · Availability Card pinned right 300×120 over hero
ComponentsHero Image · H1 + Subtitle · Review chip · Phone CTA · Book CTA · Availability Card (new)
Assets needed📷 Branded truck on a real street in [City] per T1 city (4 shots Phase 1). Fallback: generic KC-metro shot with alt text including city.
Inherits fromHomepage Playbook § 01 hero pattern + sewer-camera page herosub1
👀 Polished Demo
🚗 BSP truck · 151st & Metcalf, Overland Park

Sewer Repair in Overland Park — 5th-Gen Master Plumbers

Same-day service across Overland Park and nearby. Licensed, insured, family-owned since the 1800s.

⭐ 4.9 · 384+ reviews
Techs available today
45-min avg
response across Overland Park metro
🚗 Hero image: branded truck in Overland Park

Sewer Repair in Overland Park — 5th-Gen Master Plumbers

Same-day service across Overland Park and nearby. Licensed, insured, family-owned since the 1800s.

🟢 Techs available in Overland Park today
45-min average response
§ 03 📍 Embedded Google Map Phase 1
PurposeProximity signal. Visitor sees a map centered on their city with BSP pin. Reinforces the schema GeoCircle.
CopyCaption under map: "Serving all of [City] and surrounding neighborhoods." No headline.
MobileFull-width 240h, rounded 12, lazy-load iframe, city lat/lng from data object, zoom 11
DesktopFull-width 320h, or 50% side-by-side with Trust Bar on large screens
ComponentsEmbedded Map (new — Google Maps iframe wrapper)
PerformanceIframe loading="lazy" · placeholder gray rect until visible · do not block LCP
👀 Polished Demo
BSP · Overland Park Map © Google

Serving all of Overland Park and surrounding neighborhoods.

📍 (Google Map — centered on Overland Park, zoom 11)
BSP pin visible, city boundaries highlighted
Serving all of Overland Park and surrounding neighborhoods.
§ 04 🛡️ Trust Bar (reused from homepage) Phase 1 · INHERITED
PurposeDestroy doubt in 2 seconds. Same 6 chips as the homepage.
Copy (chips)⭐ 4.9 Google (384+) · 🏅 BBB A+ · 👨‍👨‍👦‍👦 5 Generations · 🚨 Same-Day · 🔒 Licensed & Insured · 💲 Free Estimates
Mobile4-chip column, 16 gap. No change from homepage.
Desktop6-chip horizontal row, on navy band. No change from homepage.
Inherits fromHomepage Playbook § 02 exactly — do not redesign.
👀 Polished Demo
4.9 Google (384+)🏅BBB A+ Accredited👨‍👨‍👦5 Generations🚨Same-Day Service🔒Licensed & Insured💲Free Estimates
⭐ 4.9 Google (384+)🏅 BBB A+👨‍👨‍👦 5 Generations🚨 Same-Day🔒 Licensed & Insured💲 Free Estimates
§ 05 🌁️ Live Availability + Response Time (10x) Phase 1 · 10x #3
PurposeNo KC competitor shows live availability per city. Eric sees "Techs available in Olathe today · 45-min avg" and converts.
Copy (P1 static)🟢 "Techs available in [City] today" · ⏱️ "45-min average response time" · 📞 [Call now] CTA
Copy (P2 live)Pulled from ST dispatch API. Falls back to static string if API down.
MobileFull-width card, 80h, ivory bg, green dot pulses, text stacked, CTA right-aligned
DesktopPinned right inside hero column 300×120 (referenced in § 02). On smaller screens, separate section below trust bar.
ComponentsAvailability Card (new)
P2 hookWire ST dispatch /api/technicians/available?city=[city] — Robert builds. Placeholder data attribute data-availability-city="..."
👀 Polished Demo
Techs available in Overland Park today
⏱️ 45-min average response time
Techs available in Overland Park today
⏱️ 45-min average response time
§ 06 🔧 Services in [City] Phase 1
PurposePosition BSP as sewer-first in every city. Opener paragraph differentiates (housing stock + soil + common issue).
H2"Plumbing Services in [City]"
Opener paragraph120–160 word city-specific opener from data object. Example (Overland Park): "Overland Park homes, especially along 151st and Metcalf, are often built on Kansas clay soil that shifts enough to crack old cast iron sewer lines. We've run trenchless camera inspections from the neighborhoods near Oak Park Mall to the newer builds south of 179th. If you've got a slow drain that keeps coming back, odds are your line is the reason."
6 cards (order)1. Sewer Repair & Replacement (FEATURED — always first) · 2. Sewer Camera Inspection · 3. Drain Cleaning · 4. Water Heaters · 5. Leak Repair · 6. Sump Pumps
Card anatomySame as homepage Service Card (icon 40 mobile / 64 desktop · H3 · 2-line body · Learn More →)
Mobile1-col stack, 16 gap, icon-left layout
Desktop2-col grid, icon-top layout, underline-doodle behind H2
Inherits fromHomepage Playbook § 04 Service Card component exactly
Phase 2 expansionService × City: service card on /plumber-in-overland-park/ links to /sewer-repair-overland-park/ (dedicated service-in-city page)
👀 Polished Demo

Plumbing Services in Overland Park

Overland Park homes along 151st and Metcalf sit on Kansas clay that shifts enough to crack old cast iron. We’ve run trenchless inspections from Oak Park Mall to the newer builds south of 179th…

FEATURED
🛠️
Sewer Repair
Trenchless + traditional.
Learn more →
👁️
Camera Inspection
See inside your pipes.
Learn more →
💧
Drain Cleaning
Same-day clog clearing.
Learn more →
🔥
Water Heaters
Repair + replacement.
Learn more →
🛠️
Leak Repair
Find & fix the source.
Learn more →
💧
Sump Pumps
Install, maintain, fix.
Learn more →

Plumbing Services in Overland Park

Overland Park homes, especially along 151st and Metcalf, are often built on clay soil that shifts and cracks old cast iron lines...

🛠️
Sewer Repair (FEATURED)
Trenchless + traditional
👁️
Camera Inspection
See inside your pipes
💧
Drain Cleaning
Same-day clog clearing
🔥
Water Heaters
Repair + replacement
§ 07 💬 City-Filtered Reviews (MOAT) Phase 1 · MOAT
PurposeEvery competitor shows the same 3 reviews on every city page. Ours shows reviews whose body text mentions this city or its neighborhoods.
H2"What [City] Homeowners Say"
Filter ruleRegex match on review body: [city name] OR any of the 8–12 neighborhood names OR any of the 3–5 landmark names from the data object. Case-insensitive.
Mobile2 review cards stacked, 100% w, ivory bg, shadow-card, 5-star 100×24 row, 4-line clamp, author 14w700 + neighborhood if parsed
Desktop2-col grid, 422h fixed, 5-star 120×30, 5-line clamp
FallbackIf < 2 reviews match the city filter: show "near [City]" reviews from adjacent neighborhoods, with a caption "Reviews from the KC metro nearby."
ComponentsReview Card (inherited from homepage, plus new city-badge slot in corner)
Data sourceservice_page_reviews_apply.py extended to accept --filter-city=[city] arg
👀 Polished Demo

What Overland Park Homeowners Say

★★★★★Google
“Fixed my sewer line the same day I called. Nick knew every street in Blue Valley.”
Caroline O.Blue Valley
★★★★★Google
“Juan and Matthew walked me through every option. Honest, fast, clean.”
Rickey F.Corporate Woods

What Overland Park Homeowners Say

⭐⭐⭐⭐⭐
“Fixed my sewer line the same day I called. Nick knew every street in Blue Valley.”
Caroline O.Blue Valley
⭐⭐⭐⭐⭐
“Juan and Matthew walked me through every option. Honest, fast, clean.”
Rickey F.Corporate Woods
§ 08 🚪 How It Works — 3 Steps Phase 1 · INHERITED
PurposeReduce friction. Same pattern as homepage § 07.
Steps1 Call or book online · 2 We arrive same-day & inspect · 3 We fix it right — licensed, warrantied, cleaned up
LayoutIdentical to homepage § 07 — do not redesign.
Inherits fromHomepage Playbook § 07 Step Tile component
P2 hookStep 2 replaces with live crew-ETA map (10x #2). Placeholder 340×240 mobile / full-width 480h desktop.
👀 Polished Demo
1
📞
Call or book online
24/7 dispatcher · 15-sec pickup
2
🚗
We arrive same-day
60-min avg across OP metro
3
We fix it right
Licensed, warrantied, cleaned up
1
Call or book online
24/7 dispatcher, 15-second pickup
2
We arrive same-day
60 min avg across OP metro
3
We fix it right
Licensed, warrantied, cleaned up
§ 09 🏠 Neighborhoods Served Phase 1 · NEW
PurposeDifferentiation. Real neighborhood names prove we're local. Helps Rachel recognize her area and Mike find his.
H2"Neighborhoods We Serve in [City]"
CopyIntro line: "From [neighborhood A] to [neighborhood Z], we've fixed pipes on every side of [City]."
Chips (8–12 per city)Populated from data object. Example (Overland Park): Blue Valley · Corporate Woods · Deer Creek · Indian Hills · Leabrooke · Nottingham · Oak Park · Sycamore Hills · The Village · Wilshire Farms
MobileChip grid flex-wrap, 8 gap, 4/12 chip pad, fs 14, bg rgba(48,197,255,0.12), border 1px #30C5FF
DesktopSame chips wider grid, 3-col. Optional ivory bg panel behind.
ComponentsCity Chip (new — neighborhood variant, non-clickable by default; clickable on Phase 2 when hyper-local pages exist)
👀 Polished Demo

Neighborhoods We Serve in Overland Park

From Blue Valley to Wilshire Farms, we’ve fixed pipes on every side of Overland Park.

📍Blue Valley📍Corporate Woods📍Deer Creek📍Indian Hills📍Leabrooke📍Nottingham📍Oak Park📍Sycamore Hills📍The Village📍Wilshire Farms

Neighborhoods We Serve in Overland Park

From Blue Valley to Wilshire Farms, we’ve fixed pipes on every side of Overland Park.

Blue ValleyCorporate WoodsDeer CreekIndian HillsLeabrookeNottinghamOak ParkSycamore HillsThe VillageWilshire Farms
§ 10 🏛️ Local Landmarks — Copy Block Phase 1 · NEW
PurposeLandmark anchors (3–5 per city) proof Google + Gemini + Perplexity that this page was written by someone who knows [City].
H2(Optional) "Where We Work in [City]" — or fold landmarks into the § 06 opener paragraph and skip the dedicated section on shorter pages.
Copy pattern"Whether you're near [Landmark A], around [Landmark B], or out by [Landmark C], our crew knows the streets. We've pulled roots out of sewer lines from the older trees around [Landmark D] and swapped water heaters in the new construction off [Landmark E]."
MobileParagraph block with ivory bg card, 16 pad, italic subhead
Desktop2-col: left text block, right image of a real KC landmark (optional, Phase 2)
RuleNever force all 5 landmarks if they make the paragraph sound spammy. 3 naturally placed beats 5 awkwardly placed.
👀 Polished Demo
🏛️Where We Work in Overland Park

Whether you’re near Oak Park Mall, around Deanna Rose Children’s Farmstead, or out by the Overland Park Arboretum, our crew knows the streets. We’ve pulled roots out of sewer lines from the older trees around Corporate Woods and swapped water heaters in the new construction off 179th.

Whether you’re near Oak Park Mall, around Deanna Rose Children’s Farmstead, or out by the Overland Park Arboretum, our crew knows the streets. We’ve pulled roots out of sewer lines from the older trees around Corporate Woods and swapped water heaters in the new construction off 179th.
§ 11 📷 Before / After Gallery (geotagged) Phase 2 · 10x #9
PurposeProof we've worked this street. Nobody else does this.
Phase 1 placeholderReserve slot. Dashed-border card: "P2 · Before/After Gallery — 340×240 per item, 3-item carousel mobile / 3-item row desktop"
Phase 2 buildPulls 3–4 jobs per city from HCP photos or ST jobs feed. Each: before img + after img + street name tag + service type + date.
Privacy ruleStreet only, no house number. "Repair on W 151st St · Overland Park · March 2026"
👀 Polished Demo

Recent jobs in Overland Park

BEFORE
AFTER
Cast iron sewer replacement
📍Blue Valley · March 14
BEFORE
AFTER
Trenchless liner install
📍Corporate Woods · March 22
BEFORE
AFTER
Cast iron sewer replacement
📍 Blue Valley, 2026-03-14
BEFORE
AFTER
Trenchless liner install
📍 Corporate Woods, 2026-03-22
§ 12 💳 Financing + Guarantees Phase 1 · INHERITED
PurposeRemove financial + risk objections. Same spec as homepage § 05 + § 11 condensed into one stacked section.
CopyFinancing card: "Affordable plumbing starts at $0 down. Apply in 60 seconds." · Guarantee tiles: 💯 100% Satisfaction · 🔨 Workmanship Warranty · 💲 Upfront Flat-Rate Pricing
Inherits fromHomepage Playbook § 05 (Financing card) + § 11 (Guarantee tiles)
OmissionNo Plumbinati card on location pages — redundant with homepage. Location page stays tight.
👀 Polished Demo

Overland Park Plumbing FAQ

Do you serve my neighborhood?
How fast can you get here?+
Are you licensed in Johnson County?+
How much does a service call cost?+
Yes — Overland Park and every ZIP from 66204 to 66251. Blue Valley, Corporate Woods, Deer Creek, Indian Hills, and 20+ more. Same-day across all of them.

Overland Park Plumbing FAQ

Do you serve my neighborhood?+
How fast can you get here?+
Are you licensed in Johnson County?+
How much does a service call cost?+
§ 13 ❓ City FAQ (6 minimum, FAQPage schema) Phase 1
PurposeObjection crusher + rich-result eligibility. 6 Q&A minimum per FAQPage schema guidance.
Required question types (6)1. Pricing ("How much does sewer repair cost in [City]?") · 2. Response time ("How fast can you come out to [City]?") · 3. Area specificity ("Do you service all of [City] including [Neighborhood]?") · 4. Emergency ("Is [City] emergency plumbing available 24/7?") · 5. Financing ("Do you offer financing for [City] homeowners?") · 6. Trust ("How long have you served [City]?")
Answer ruleEvery answer mentions [City] at least once. Use neighborhood or landmark names when natural. 40–80 words per answer.
MobileSame accordion spec as homepage § 12 (48h tap, caret 20 right, 16 vert pad)
DesktopMax 800 w centered
ComponentsFAQ Accordion Row (inherited from homepage / sewer-camera page 601ec0 pattern)
SchemaFAQPage JSON-LD auto-generated from data object. See § Schema layer 6.
👀 Polished Demo

Overland Park Plumbing FAQ

How much does sewer repair cost in Overland Park?
Typically $3,000–$15,000 depending on line length, depth, and method (traditional dig vs. trenchless). Older neighborhoods north of College Blvd see higher costs due to mature trees and cast iron age. Free on-site estimate.
How fast can you come out to Overland Park?+
Do you service all of Overland Park including Blue Valley?+
Is Overland Park emergency plumbing available 24/7?+
Do you offer financing for Overland Park homeowners?+
How long have you served Overland Park?+
Rendered mini-preview — use for composition reference, not pixel-final measurements.
§ 14 📍 Nearby Cities (internal link grid) Phase 1 · NEW
PurposeInternal link equity between city pages. Mike discovers neighboring cities and books routine work there too.
CopyIntro: "Also serving [City]'s neighbors:"
Chips (5–6)Populated from data object's nearby_cities[]. Example (Overland Park): Olathe · Lenexa · Leawood · Shawnee · Prairie Village · Mission
Anchor textEach chip reads "Plumber in [City]" — exact-match anchor text for the /plumber-in-[slug]/ URL
MobileChip grid flex-wrap, 8 gap, clickable links
DesktopSame chips horizontal row, centered, 32 pad
ComponentsCity Chip (new — nearby variant, always clickable)
👀 Polished Demo

Also serving Overland Park’s neighbors:

Each chip links to its own /plumber-in-[slug]/ URL — exact-match anchor text for internal link equity.

Rendered mini-preview — use for composition reference, not pixel-final measurements.
§ 15 📞 Final CTA + Multi-Step Form + Footer Phase 1
PurposeClose. Multi-step form converts 86% better than single-step.
Headline"Ready to fix it right in [City]?"
Sub"Call Kansas City's 5th-generation plumbing family."
Multi-step formStep 1: What service? (Sewer / Drain / Water Heater / Other) · Step 2: When? (Emergency now / Today / This week / Flexible) · Step 3: Name, Phone, Address, ZIP — city pre-selected from URL slug
MobileCTA button trio top (📞 Call · 📅 Book · 💬 Chat) · multi-step form below, 1 field per step, progress dots
DesktopCentered card 640w, wave-bg navy, CTA trio top, form below, [Continue →]
Form submissionPOSTs to ServiceTitan with city pre-filled, GCLID captured, source tagged "/plumber-in-[slug]/"
FooterGlobal footer — already shipped Apr 17 (see homepage playbook § 14)
Inherits fromHomepage Playbook § 13 Multi-Step Form + wave-to-footer
🔗

Design Inheritance — Two-Way

Location pages inherit from two sources: the Homepage Playbook (design system) and the live Sewer-Camera Inspection page (component patterns). This playbook does NOT redefine shared tokens. It adds only location-specific additions.

Direct inheritance from Homepage Playbook

Component / PatternSourceUse here
Trust Bar (6 chips)Homepage Playbook § 02§ 04 verbatim
Service Card (icon + H3 + body + link)Homepage Playbook § 04§ 06 verbatim
Financing CardHomepage Playbook § 05§ 12 verbatim
Review Card + live Google Reviews APIHomepage Playbook § 10 + sewer page§ 07 plus city regex filter
Step Tile 3-step "How It Works"Homepage Playbook § 07§ 08 verbatim
Guarantee Tile (3-up)Homepage Playbook § 11§ 12 verbatim
FAQ Accordion RowHomepage Playbook § 12 / sewer page 601ec0§ 13 with city-specific questions
Multi-Step Form + wave-to-footerHomepage Playbook § 13§ 15 with city pre-filled
Footer (global, shipped Apr 17)Homepage Playbook § 14Auto-inherits, no work
Daniel AI Chat Widget (floating)Homepage Playbook floatingSame
Mobile Sticky Call BarHomepage Playbook floatingSame
Type scale, spacing scale, color tokens, motionHomepage Playbook § Figma Guide + § TokensIdentical

Direct inheritance from Sewer-Camera Inspection page (live)

ElementBricks IDUse here
Hero subtitle band + underline doodle schedulingherosub1, f97bb8, 6365c4, 37fbb3, 221286§ 02 H1 underline behind "Plumber in [City]"
Process-steps wave-bg block#brxe-5a5ec7§ 08 How It Works background
Review card anatomy (F8FAFC / 422h / 5-star 120×30)#brxe-172d71, #brxe-5202df§ 07 Review grid
FAQ accordion pattern#brxe-601ec0§ 13 FAQ
Final-CTA wave-to-footer transitionsewer page § 09§ 15 wave

Location-specific additions (NEW components Audrey designs)

  • Emergency Bar — fixed top, red bg, yellow phone link. Mobile + desktop variants.
  • Embedded Map wrapper — iframe + lazy placeholder + caption.
  • Availability Chip — green dot pulse, city name, response time, call CTA.
  • City Chip — neighborhood variant (non-clickable Phase 1) + nearby-city variant (always clickable).
  • Landmark Copy Block — optional ivory-bg card for § 10 when landmarks get their own visual row.
📦

Asset Manifest — per-city shoot list

Per-city photography (priority for Phase 1 T1)

Each T1 city gets a dedicated shoot slot. Fallbacks use generic KC-metro imagery with alt text specifying the city context until real shoots happen.

#Shot per cityFormatWhere it goes
1Branded BSP truck on a real street in the city2560×1440 + 768×1024 crop§ 02 hero bg
2Landmark shot (Oak Park Mall for OP, Olathe Community Center for Olathe, etc.)1600×1200§ 10 landmark block (optional)
3Tech performing sewer camera inspection in a recognizable city home1600×1200§ 06 services card · § 11 gallery
4Before/after pair from a real job in the city (×3 per city minimum)1200×1200 each§ 11 gallery (Phase 2)
5Neighborhood street identifier (Blue Valley sign, etc.)1200×800§ 09 neighborhoods grid (optional)

Reused assets (inherited)

  • 🎨 Wave dividers, underline doodles, service icons — all reused from sewer-camera page
  • 🏥 BSP vertical logo (footer, shipped Apr 17) + horizontal logo (header)
  • 🥇 BBB A+ badge, Google review star, Angi / Yelp / Chamber badges (footer trust row)
  • 🚨 Red emergency icon for sticky bar — new SVG (Audrey designs)
  • 🟢 Green "live" dot for Availability Chip — new CSS pulse

Fallback plan

  • If no city-specific hero shot exists on Phase 1 ship day, use a generic KC-metro shot with descriptive alt text: alt="Bright Side Plumbing branded truck serving Overland Park, Kansas".
  • Landmark shot is optional. If we don't have it, fold the landmark reference into the opener paragraph copy only.
  • Before/after gallery is Phase 2 gated — placeholder only on Phase 1.
👤

Who We Are Designing For (4 Personas)

Same 4 personas from the Homepage Playbook (derived from 301+ reviews across 6 platforms). Location pages skew even harder toward Emergency Eric because that traffic is keyword-triggered by panic ("emergency plumber Olathe," "sewer repair Overland Park now") and almost always mobile. Rachel and Bob land here too via research queries. Mike lands here when Google serves the "nearest plumber" answer for his neighborhood.

PersonaLocation-Page ShareState of MindDesign Implication
🚨 Emergency Eric/Erica60%+ of location-page trafficPanic, same-day, mobile, 11 PM searches, pipe is leaking right nowSticky emergency bar at top · phone number above fold · "techs available in [city] today" live module · 45-min response time prominent
🏗️ Renovation Rachel/Ryan20% of trafficResearches contractors in their specific neighborhood, wants local proofCity-filtered reviews · before/after gallery geotagged · neighborhood list visible · FAQ with city-specific pricing
🏢 Business Owner Bob/Barbara5–8% of trafficCommercial property in specific city, zero disruptionCommercial badge · "licensed in [city]" permit proof · SLA language in FAQ
🔁 Maintenance Mike/Maria10–12% of trafficDiscovers us via "plumber in [neighborhood]" then books routine workNearby-city links at bottom for return visits · financing card · neighborhood list triggers recognition
💰

Why This Matters

45
Indexable Pages (Phase 2)
15
KC Metro Cities
3
Flagship Services
≥40%
Unique Copy Per Page
Programmatic multiplication, not copy-paste. One template, a city-data object per location, and a per-city differentiation rule set that keeps Google happy. Done right, this becomes 45 separate high-intent landing pages capturing "plumber in [city]" search demand across the entire KC metro. Done wrong, it's doorway-page spam that gets the site penalized.

Current gap vs competitors

ProblemImpactSeverity
📍 No city-specific landing pages exist todayAnthony / Roto / BenFranklin capture 100% of "plumber in [city]" traffic🔴 Critical
👤 Service Areas section on homepage = chip grid with no landing targetsKills the Phase 2 play before it starts🔴 Critical
🧮 No Plumber schema with areaServed + GeoCircleGoogle cannot confirm geographic service radius per city🟡 High
🏛️ No sameAs Wikidata linking to OP / Olathe / Lenexa entitiesMissing entity-based AI-search signal🟡 High
📷 No city-filtered reviewsGeneric review pool ignores local specificity Google rewards🟡 High
📋 No FAQ per cityNo rich-result FAQPage real estate per location🟡 High
The revenue math: 45 pages × even 2 qualified sewer clicks per page per month × 30% sewer close rate × $5K average ticket = $135K / month ceiling from programmatic local capture alone. The template is the leverage.
🥊

KC Competitor Location-Page Landscape

Same 5 competitors from the Homepage Playbook — here's how each handles LOCATION strategy

Analyzed April 17, 2026. Focus is the location-page layer specifically: do they have per-city pages, do they use Plumber schema with areaServed, do they filter reviews by city, do they use landmark-anchored copy. Anything I can't verify with source I mark as unknown rather than invent.

CompetitorHas City Pages?SchemaLandmark Copy?Weakness we exploit
Anthony PHC✅ /service-areas/* (one per KC metro city)LocalBusiness generic, no areaServed GeoCircle verified❌ Generic corporate copy swapped per cityTemplate-feel, no family faces, no landmark anchors
Benjamin Franklin KC✅ Franchise city list, thin pagesStandard franchise LocalBusiness schema❌ None — boilerplate national copyFranchise feel, cookie-cutter, no real neighborhood names
Roto-Rooter KC✅ Massive location directoryLocalBusiness + coverage list (scale)❌ No — faceless national stringsZero local story, no crew photos, generic reviews
Kevin Ginnings❌ Homepage only, no city pages confirmedUnknown / weakNo structural presence — free real estate for us
Inception Plumbing⚠️ Partial — a few service-area URLs, unclear depthUnknown — not verified this session❌ Not observed"Truly local" brand voice without the schema or per-city depth to back it up

Location-signal gap matrix — Phase 1 closes every 🔴

SignalAnthonyBenFrankRotoGinningsInceptionBSP target
Per-city landing page⚠️P1 ship
Plumber schema + areaServed + GeoCircle⚠️⚠️⚠︐MOAT — 8-layer stack
sameAs Wikidata city linking10x MOAT
knowsAbout specialty signals10x MOAT
City-filtered reviewsMOAT
Landmark-anchored opener copyMOAT
Live "techs available in [city]" module10x MOAT
Before/after gallery with street nameP2 10x
Neighborhood grid (8–12 names per page)⚠️ generic⚠︐ genericP1 ship
City-specific FAQPage schemaP1 ship
Note on unknowns: Inception's schema depth and Ginnings' structured-data state were not verified in this playbook session. If those change before Phase 1 ship, update this matrix. No claim gets made here that I could not observe or that Robert can't verify on request.
📡

Top 10 Local Ranking Signals We Codify

These are the signals the template must hardcode. Every section the designer ships maps to at least one of these signals. If a section doesn't serve a signal, it doesn't exist.

#SignalWhere the template proves it
1📍 GBP claimed, categorized, activeOff-page — Robert maintains GBP. Page links to GBP profile in footer and embed.
2📎 NAP consistency (Name / Address / Phone)Header + footer + schema all reference 12022 Blue Valley Pkwy, Overland Park, KS 66213 / (913) 963-1029. No variants.
3⭐ Reviews containing city + keyword§ 05 pulls reviews where body contains [city] OR [service keyword], regex filter via live Google Reviews API.
4💬 Reviews with owner responsesReview card includes owner-response field when present. Signal that BSP actually engages.
5📄 On-page keyword + city in title, meta, H1, H2, bodyTitle "Plumber in [City] KS | Sewer Repair | Bright Side Plumbing" · H1 "[Service] in [City]" · H2s repeat city naturally.
6🧮 Schema (LocalBusiness/Plumber + Service + FAQ + Breadcrumb)8-layer stack in § Schema section of this playbook.
7📱 Mobile-first + Core Web Vitals LCP < 2.5sAll imagery lazy, hero as <img> with srcset, no CSS-bg hero, CLS < 0.1.
8🔗 Domain authority + inbound linksOff-page — but "Linktations" (civic sponsorship) is the Phase 2 play. Template provides stable URLs worth linking to.
9📡 Local inbound links with city+keyword anchor textChamber, BBB, partnerships, civic sponsorships — each can point anchor "plumber in Overland Park" to /plumber-in-overland-park/.
10📍 Proximity (GBP + NAP reinforcement)Schema's GeoCircle geoRadius matches GBP service area. Consistency tells Google the business is real here.
🚀

10x Moves No KC Competitor Has

Design hooks Audrey leaves in the template even for Phase 2 features

#10x PlayWhy it beats every KC competitorBSP Asset
1🔗 Wikidata sameAs entity linking per cityEvery city has a Wikidata ID (Overland Park = Q2030450, Olathe = Q1131380). Google connects our page to the city entity. Nobody else does this.Schema layer — zero cost, 100% lift.
2🧠 knowsAbout specialty declarationsDeclares Plumber knowsAbout Trenchless, Hydro-Jetting, Backflow. AI search (Gemini, Perplexity) reads this directly.Schema layer — future-proof for LLM search.
3🌁️ Live availability module"Techs available in Olathe today · 45-min avg response." Pulls from ServiceTitan dispatch. No competitor shows this.ST dispatch API feasible — placeholder in template for Phase 2.
4🎥 Reviews filtered by city + keywordCompetitors show the same 3 reviews on every city page. Ours pulls reviews whose body contains the city or neighborhood name.service_page_reviews_apply.py already wired.
5🏛️ Landmark-anchored copyOpening paragraph references real places (e.g., "from the shops at Oak Park Mall to the homes along 151st Street"). No competitor does this at scale.Per-city data object — 3–5 landmarks each.
6📥 Service × city matrix (45-page build)Phase 2 multiplies to 15 cities × 3 services. Anthony has service-area pages but NOT service × city permutations.Template driven by city-data + service-data objects.
7🪙 "Linktations" via civic sponsorshipChamber memberships, Little League sponsorship, civic donations each yield an inbound link with city+keyword anchor.Stephanie's relationships — Phase 2 outreach.
8🚨 Emergency conversion stack in first 2 viewportsSticky red bar + availability + response time + financing + guarantee visible before any scroll. Competitors bury these.Figma design spec in Section 01–05.
9📷 Before/after gallery with street namesEach photo tagged with neighborhood. Proof the crew has literally been on this street. Nobody does this.HCP / ServiceTitan photo pull — Phase 2.
10📍 GBP per-city areaServed + GeoCircle alignmentPage schema GeoCircle matches GBP service area polygon. Tight consistency = strong proximity signal.Robert wires Schema — zero design work.
Design rule: Any 10x feature that's Phase 2 gets a Figma placeholder component sized correctly with a dashed border labeled "P2 · {feature}". Phase 1 build must not lock us out of Phase 2 layouts.
📋 Live Fleet Availability — Robert build input. Audrey may skip this section for Phase 1 design work.
🚒

Live Fleet Availability · the Badge That Converts

Why this exists

A page that says "24/7 service" is dead signal. A page that says "a crew is 22 min from you in Olathe right now" converts panic callers at 11 PM. No KC competitor can match this because they don't have the data pipeline, the privacy design, or the intelligence stack. BSP does.

Every city page carries one small chip near the hero: green dot, city name, approximate ETA, crew count. That chip is the conversion hinge for Emergency Eric. This section shows the data sources, APIs, privacy rules, 3-rung ladder, component spec, cost ceiling, rollout phases, KPIs, and risk mitigation that make it real. Treat it as a standalone strategic brief inside the playbook.

UX precision required, by signal

Each signal a user sees on a city page needs the right grain. Too coarse and the badge feels fake. Too precise and we expose technician location, which breaks privacy and invites stalking risk.

Signal shown on city pagePrecision requiredPrivacy risk
"Is a crew working in this city today?"Binary yes/noNone
"Approx ETA to this city from current fleet"±10–15 min bucketsNone if grain-blurred
"Crews active in your territory"Rough count (1, 2, 3+)None
"Live map pin of a truck"True GPS coordinatesHIGH, never render on public page

The 3-rung ladder

Each rung unlocks more precision. Each one also costs more to operate. Ship Rung 1 immediately; earn the right to Rung 2 by proving conversion lift; earn Rung 3 only if dispatch desk demands a real-time operator view.

RUNG 1 · ServiceTitan schedule signal [SHIP NOW] │ │ Source: ServiceTitan jobs API (booked-today, crew assigned, arrival window) │ Cost: $0 extra (we already pay for ST) │ Build: 3–5 days (Robert, one cron + one endpoint) │ Unlocks: "A crew is booked in {city} today" · rough ETA from schedule │ Honest: We do not know where the truck actually is right now │ ▼ EARNS THE RIGHT TO │ RUNG 2 · Tech phone PWA + Google Distance Matrix [MONTH 2–3] │ │ Source: Tech installs BSP dispatch PWA, opts in to foreground GPS │ + Google Distance Matrix API (drive time to each city centroid) │ Cost: ~$40–$80/month API (heavy caching), zero hardware │ Build: 2–3 weeks (PWA + /api/fleet/availability rewrite + cache layer) │ Unlocks: True "tech is 22 min from Overland Park" ETA · Daniel AI context │ Honest: iOS background geolocation is flaky, need foreground-only model │ ▼ EARNS THE RIGHT TO │ RUNG 3 · Sinotrack or Traccar GPS hardware per truck [MONTH 6+] │ Source: Hardware GPS pucks on each vehicle, streaming 24/7 Cost: $15–$30/unit + $8–$15/mo per vehicle, plus Kalen approval Build: 4–6 weeks (hardware install + Traccar server + ingest pipeline) Unlocks: Dispatch console real-time map, zero opt-in gaps, after-hours coverage Honest: Gated on Kalen buy-in + proof Rung 2 already moved the number

Badge-render privacy spec

NEVER RENDER on a public page: technician name, GPS coordinates, specific intersections, truck photo with plate, real-time moving pin, vehicle make/model, or anything that lets a bad actor geofence a tech.
SAFE TO RENDER on a public page: city name, green/yellow/red dot, "~NN min" bucket, crew count in the metro (1, 2, 3+), and a generic badge icon. That is the full public surface.
Internal dispatch console (Ashton only, auth-walled): can render true pin, tech name, ETA to next job, and route. Never exposed on any /plumber-in-{city}/ URL.

System architecture

Three data sources feed one cron, which writes one JSON, which fans out to four consumers. Everything is cached aggressively so Google Maps API spend is bounded.

┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ DATA SOURCES │ └────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ [ ServiceTitan jobs API ] ──┐ [ Tech phone PWA GPS (R2) ] ──┤ [ Sinotrack / Traccar GPS (R3)] ──┘ │ ▼ ┌──────────────────────────────────────────────────────────────┐ │ /opt/nexus/titan/fleet_availability.py (5-min cron) │ └──────────────────────────────────────────────────────────────┘ │ ├─── Google Distance Matrix API (cached 30d per origin–dest pair) ├─── Google Geocoding API (cached 30d per address) ├─── titan.technician_gps (latest position per tech, write-only) └─── writes ► /opt/nexus/nexus/scripts/output/fleet_availability.json │ ▼ ┌──────────────────────────────────────────────────────────────┐ │ PUBLIC API │ │ /api/fleet/availability/{city-slug} ► public chip │ │ /api/fleet/status ► summary │ └──────────────────────────────────────────────────────────────┘ │ ├─── Location page "Live Availability Chip" (public, § 05) ├─── Daniel AI prompt context ("a tech will be there in ~22 min") ├─── Dispatch console (Ashton internal, full detail) └─── Google Ads dynamic copy injection (city-targeted RSAs)
§ CL-15 🚒 Live Availability Chip Phase 1
PurposeSmall chip near the city hero. Shows binary availability, approximate ETA bucket, and crew count. Converts Emergency Eric at 11 PM by answering "is anyone actually available?" before he has to call a competitor.
AnatomyLeft: colored dot 8×8 (green/yellow/red) with subtle pulse · city name 13w700 · middle divider dot · ETA string ("~45 min") 13w600 muted · optional right-side crew count ("2 crews") 11w700 in pill
Variantsavailable (green dot, ETA shown) · limited (yellow, "~90 min") · booked-out (red, "Next avail 8 AM tomorrow") · stale (gray, "Available today", no ETA, fallback when feed >2h old) · after-hours (navy bg + yellow dot, "On-call crew 24/7")
Mobile (375)Full-width chip 40h, 12 pad horizontal, sits below hero H1 above primary CTA
Tablet (768)Auto-width pill 36h, inline with trust-chip row
Desktop (1440)Auto-width pill 36h, anchored top-right of hero or first item in trust bar
Tokensbg rgba(255,255,255,0.06), border 1px rgba(48,197,255,0.25), radius 999, text --white, eta text --blue-soft, dot-green #22C55E, dot-amber #F59E0B, dot-red #EF4444
MotionGreen dot subtle 2.4s ease-in-out pulse (scale 1 → 1.15, opacity 1 → 0.6), outer ring expand every 8s, reduced-motion: static
Accessibilityaria-label reads the full string ("Available now, approximately 45 minutes, 2 crews in Overland Park"). Dot color NOT the only signal, text always present. Tab-focusable, links to /book/.
Figma nameChip/City/Available, Chip/City/Limited, Chip/City/BookedOut, Chip/City/Stale, Chip/City/AfterHours
Used in section(s)§ 02 City Hero (primary) · § 05 Live Availability (expanded card variant) · Nearby Cities chips (compact variant)
Build-side HTML pattern<a class="chip chip--city chip--available" href="/plumber/overland-park/"><span class="chip-dot"></span>Overland Park<span class="chip-eta">~45 min</span></a>
CSS tokens--chip-bg: rgba(255,255,255,0.06); --chip-border: 1px solid rgba(48,197,255,0.25); --chip-radius: 999px; --chip-dot-green: #22C55E; --chip-dot-amber: #F59E0B; --chip-dot-red: #EF4444; --chip-pulse-ms: 2400ms;
Data wiringHydrates from /api/fleet/availability/{city-slug}. 60s browser cache. If response stale (>2h) or error, falls back to "stale" variant automatically.

Cost table, Year 1

RungOne-time buildMonthly run-rateYear 1 totalIncludes
Rung 1 ST schedule~8 hrs Robert$0$0 new cashCron + endpoint + chip hydration, reuses existing ST API access
Rung 2 Tech PWA + Maps~40 hrs Robert$40–$80 API + $8 PWA hosting~$600–$1,050PWA build, Distance Matrix, Geocoding, cache layer, fallback logic
Rung 3 Hardware GPS~60 hrs + $15–$30/truck$8–$15/vehicle + $20 server~$1,800–$3,000 (8 trucks)Sinotrack or Traccar units, install, Traccar server, dispatch console UI
3-year ceiling (all rungs active, 8 trucks)~$200/mo~$2,400/yrHard cap on ongoing Google Maps spend from cache model
Rung 1 costs zero new cash and ships this week. Anything that promises more than Rung 1 costs money, which means it needs proof from Rung 1 first.

Phased rollout

PHASE 2 · WEEK 1 [SHIP] ┌───────────────────────────────────────────────────────────────────────────────────────────┐ │ SHIP: Rung 1 (ST schedule feed) + CL-15 chip on 4 T1 cities │ │ COST: $0 · 3–5 days Robert │ │ ADVANCE: Achieve >15% click-to-call lift on T1 cities by day 30 │ └───────────────────────────────────────────────────────────────────────────────────────────┘ │ ▼ PHASE 3 · MONTH 2–3 [IF EARNED] ┌───────────────────────────────────────────────────────────────────────────────────────────┐ │ SHIP: Rung 2 (tech PWA + Distance Matrix) + ETA precision upgrade │ │ COST: ~$50/mo + 2–3 weeks Robert │ │ ADVANCE: ETA accuracy ≥85% on weekly audit + conversion still lifting │ └───────────────────────────────────────────────────────────────────────────────────────────┘ │ ▼ PHASE 4 · MONTH 6+ [ROI-GATED ONLY] ┌───────────────────────────────────────────────────────────────────────────────────────────┐ │ SHIP: Rung 3 (hardware GPS) + dispatch console for Ashton │ │ COST: ~$120–$200/mo + Kalen approval for hardware buy │ │ ADVANCE: Only if dispatch desk is visibly operating blind at scale │ └───────────────────────────────────────────────────────────────────────────────────────────┘

KPI and measurement framework

MetricBaselinePhase 2 targetHow measured
City-page click-to-call rate~6–10% industry avg+15–25% liftGA4 phone_click event per city slug
Form submit from city pagesbaseline TBD, first 14 days+20% liftWP form submit + GCLID capture per city
"Available now" badge exposure0100% of city page visits/api/fleet/availability served count per slug
Badge accuracyn/a≥85% true positivesAshton weekly audit of 20 random claims vs ST jobs
Stale-fallback raten/a<5% of servesCount of stale-variant serves / total serves

Risk matrix

RiskSeverityMitigation
Badge says "available," no one showsHIGH trustStale-data detection: if feed >2h old, auto-fall-back to "Available today" generic variant (no ETA shown)
Tech privacy concern (GPS exposure)MEDCity-grain only on public surface. Coordinates never rendered. Internal tech PWA requires explicit opt-in per shift.
Google Maps API cost spikeLOW30-day origin–destination cache + $148/mo hard cap. If cap hit, script silently falls back to ST-schedule-only mode.
iOS background geolocation flakyMEDHybrid model: PWA foreground-only for Rung 2, ST schedule fallback always. Never depend on iOS background.
Kalen rejects GPS hardwareLOWRung 1 works without any hardware. Kalen does not need to be involved until Rung 3 is proposed.
Competitor clones the badgeLOWMoat is the full pipeline (ST + Maps + cache + dispatch), not the UI. Copying the chip without the backend shows a fake green dot, which users catch fast.

What this unlocks beyond the badge

The badge is the smallest consumer of this pipeline. Once the feed exists, every downstream surface gets smarter.
  • Daniel AI context: "Our tech will be at your house in 22 minutes" instead of generic "someone will be in touch soon."
  • Outbound SMS on dispatch: "Juan is 15 min away, blue Ford F-150, ETA 2:47 PM." Drives 5-star reviews.
  • Internal dispatch console for Ashton: real-time fleet map, auth-walled, zero public exposure.
  • Google Business Profile special-hours auto-sync: if no crew is on-shift, GBP hours reflect "closed," protecting our LSA score.
  • Google Ads dynamic ad copy: "Plumber 10 min from you in Overland Park, Call Now." RSAs update from the same feed.
  • Emergency Eric conversion at 11 PM: the single moment this entire stack exists to own. Panic caller sees a green dot, calls BSP instead of Roto-Rooter.

Bonus free-API layers (stack on the same feed)

Each of these APIs is free or near-free and enriches the city page without any additional paid infrastructure. They plug into the same availability surface.

APIWhat it givesBSP use on location pages
NWS (weather.gov)Active alerts + forecasts per ZIP, free, no key"Freeze warning tonight in Olathe, book a pipe-inspection now" banner · Weather bidding ties back
Google Business Profile APILive reviews, star avg, photos, special hoursCity-filtered review pull for § 07 · auto-match GBP service area to GeoCircle schema
OSM OverpassLandmarks, schools, parks, shopping centers by bboxAuto-populate "from the shops at Oak Park Mall to…" landmark copy per city
USGS Water ServicesReal-time stream gauges + flood alerts"Brush Creek gauge at 5.2 ft, call about basement backup risk" dynamic alert
Census ACSHousing age, median home value per tract"Median home in Olathe is 1998, pipes from that era often…" targeted content

Ship Rung 1 this week · actual work order

No Kalen approval needed for Rung 1. Zero hardware, zero new spend, existing APIs only. Robert owns all three tasks.
  1. Build /opt/nexus/titan/fleet_availability.py cron. Reads ServiceTitan jobs API (today's bookings, crew assignments, arrival windows). Buckets per city slug. Writes /opt/nexus/nexus/scripts/output/fleet_availability.json. 5-min cron. Stale-flag if >2h old.
  2. Wire /api/fleet/availability/{city-slug} and /api/fleet/status endpoints on the existing port 8765 nexus API. Response includes: state (available/limited/booked-out/stale), eta_minutes, crew_count, updated_at, fallback_reason.
  3. Add Live Availability Chip (CL-15) to the Figma component library. 5 variants (available, limited, booked-out, stale, after-hours). Audrey builds, Robert hydrates with the API in the Bricks template.
Definition of done: 4 T1 city pages show a live chip, refreshes every 60s client-side, falls back gracefully when feed is stale, and GA4 logs a fleet_chip_view event per render. KPI dashboard reads the number day 1.
📋 Google API Stack — Robert build input. Audrey may skip this section for Phase 1 design work.
🧠

Google API Stack · The Full Intelligence Layer

BSP has a ServiceTitan subscription and a Google Ads account. Inside those already-paid relationships sit 20+ Google APIs most KC plumbing competitors never touch. The Location Pages stack should consume every one that moves conversion, data quality, or competitive positioning. This section is the blindspot catalog.

The blindspot framing

Prior Live Fleet analysis covered Distance Matrix and Geocoding only. That was the blindspot. The full Google stack breaks into 7 categories spanning 22 APIs. Grouped below by impact per dollar for BSP's Location Pages specifically, not by Google's own taxonomy. The rule: anything free or inside Google's $200/month Maps credit that touches conversion, data quality, or SEO measurement ships in Tier A. Anything strategic but deferrable waits for Phase 3 or Phase 4.

Three-tier ship order

Every API below is pre-ranked by when it earns a slot in the build. Tier A rides with the Rung 1 Fleet Badge. Tier B stacks on the Tech PWA in Phase 3. Tier C is the compounding strategic layer.

TIER A · SHIP WITH RUNG 1 FLEET BADGE [free or near-free, conversion-critical] │ │ 1. GBP Performance API per-city impressions + calls + direction requests (90d rolling) │ 2. GBP Reviews API feed live-filtered City Review Card │ 3. GBP Posts API programmatic city-specific promos │ 4. Search Console API which queries hit each city page, CTR, position │ 5. GA4 Data API conversion lift per city page (the KPI framework) │ 6. Address Validation API USPS-grade form data at submit time │ 7. Places Autocomplete customer types address, Google autocompletes │ 8. Maps Static API server-render city hero map, no JS, fast LCP │ 9. Places API (New) auto-populate landmarks + neighborhoods per city │ ▼ │ TIER B · PHASE 3 SHIP [alongside Tech PWA] │ │ 10. Routes API v2 traffic-aware ETA, replaces Distance Matrix │ 11. Roads API snap tech PWA coords to nearest road │ 12. GA4 Realtime API dispatcher surge detection │ 13. Google Ads API per-city conversion, bid strategy │ 14. Street View Static API show customer's street, "we've been here" visual │ ▼ │ TIER C · PHASE 4 COMPOUNDING PLAYS [strategic] │ │ 15. Gemini API programmatic city opener copy (unblocks T2/T3 city ramp) │ 16. Cloud Translation API Spanish city pages (Feb 2026 market gap) │ 17. Vision API auto-tag plumbing job photos per city │ 18. Cloud Natural Language review sentiment for hero testimonial selection │ 19. Ads Keyword Planner API prioritize future city builds by volume │ 20. YouTube Data API embed per-city videos │ 21. GBP Q&A API auto-respond to "do you serve X" │ 22. reCAPTCHA Enterprise form spam gate

🗺️ Maps Platform · 9 APIs

APIFree tierBSP Location Page use
Routes API v240k/moTraffic-aware ETA for Live Fleet Availability, replaces Distance Matrix for new builds
Maps JavaScript API$200/mo credit (~28k loads)Embed interactive service-area map per city page
Maps Static API$200/mo creditServer-render static city map image, no JS, fast LCP, stack on every City Hero
Places API (New)$200/mo creditAuto-populate landmarks and neighborhoods per city: "near Oak Park Mall, Deanna Rose, Sprint Campus" copy
Places Autocomplete$200/mo creditCustomer types address, Google autocompletes, faster form conversion
Place Details API$200/mo creditRich data about BSP GBP and competitor GBPs for competitive intel
Street View Static API$200/mo creditShow customer street: "we have been to your neighborhood" trust signal
Address Validation API10k free/moUSPS-grade validation at form submit, catches typos, cleans ST data
Roads API$200/mo creditSnap tech PWA GPS to nearest road plus speed limit

🏢 Google Business Profile · 6 APIs, all FREE

APIBSP Location Page use
GBP Performance APIPer-listing impressions, searches, calls, direction requests over 90-day window. Goldmine for per-city SEO tuning
GBP Reviews APILive monitor and programmatic response. Feeds City Review Card
GBP Q&A APIAnswer "do you serve Olathe?" automatically, flag edge cases for Ashton
GBP Posts APIPush city-specific special offers to GBP ($25 off in Overland Park)
GBP Attributes APIProgrammatically toggle women-owned, 24/7, accepts credit cards, and similar
GBP Verifications APIManage multi-location verification when BSP opens a second location

🔍 Search Console · 3 APIs, FREE

APIBSP Location Page use
Search Analytics APIWhich queries drive clicks to each city URL, position, CTR. Feeds H1 and meta optimization
URL Inspection APICheck if a city page is indexed. Detect crawl errors per slug
Sitemap APIAuto-submit programmatic location-page sitemaps when 45 pages ship

📊 Analytics and Ads · 4 APIs, FREE with accounts

APIBSP Location Page use
GA4 Data APIMeasure per-city-page conversion. Proves whether the Live Fleet badge works. Click-to-call lift per slug
GA4 Realtime APILive traffic on location pages. Dispatcher sees surge and routes extra tech
Google Ads APIPer-campaign per-city conversion data. Best-performing cities, bid strategy
Google Ads Keyword Planner APIPer-city keyword volume to prioritize T1 / T2 / T3 city build order

🤖 Cloud AI · 4 APIs, free tiers

APIBSP Location Page use
Gemini APIGenerate unique city-page openers grounded in real soil, housing, and common-issue data. Unblocks programmatic 15-city ramp
Cloud Translation APISpanish-language city pages at /es/plumber/[city]/. Feb 2026 CSV documented 2 Spanish-speaking customers lost
Vision APIAuto-tag plumbing job photos for city gallery (before / after classification)
Cloud Natural Language APISentiment analysis on reviews. Auto-surface 5-star reviews with specific city praise for hero testimonial placement

📺 YouTube · 2 APIs, FREE

APIBSP Location Page use
YouTube Data APIEmbed city-specific BSP videos on location pages (sewer camera footage, testimonials)
YouTube Analytics APIWhich videos convert per city, inform per-city content strategy

🛡️ Security and UX · 1 API, FREE tier

APIFree tierBSP Location Page use
reCAPTCHA Enterprise1M assessments/mo freeForm spam protection on location-page CTAs

Integration architecture

How the 22 APIs stack with the Live Fleet system already specced in the previous section. Every arrow is an existing or planned feed.

[ServiceTitan] ┼──► fleet_availability.py ──► Live Fleet Badge [Tech PWA] │ │ [GBP Performance] ──► per-city SEO dashboard ──► Ashton + Kalen weekly [GBP Reviews] ──► City Review Card [GBP Posts] ──► city-specific promo scheduler [GBP Q&A] ──► auto-responder + Ashton queue [Search Console] ──► H1 / meta tuner per slug [GA4 Data] ──► conversion measurement ──► KPI dashboard [GA4 Realtime] ──► dispatcher surge alerts [Google Ads API] ──► per-city bid strategy ──► Smart Bidding [Keyword Planner] ──► T2 / T3 city build priority [Places API] ──► neighborhoods + landmarks ──► city copy generator [Maps Static] ──► city hero map (server-rendered) [Street View] ──► "we've been on your street" hero visual [Address Validation]──► form submit pipeline ──► clean ST customer record [Places Autocomplete]──► address field UX on every CTA [Routes API v2] ──► traffic-aware ETA ──► Fleet Badge Rung 2+ [Roads API] ──► tech PWA GPS snap [Gemini] ──► unique city openers ──► 45-page programmatic mill [Translation] ──► /es/ pages [Vision] ──► auto-tagged job photo gallery [Natural Language] ──► review sentiment ──► hero testimonial picker [YouTube Data] ──► embedded city videos [YouTube Analytics] ──► per-city video conversion [reCAPTCHA Ent.] ──► spam gate on every form

5 strategic blindspot wins

These are the five calls we would defend with the loudest voice in a Kalen review.
  • GBP Performance API. The only honest measurement of whether city pages win in Local Pack. Should be a first-class metric on the Nexus dashboard, not a once-a-quarter manual pull.
  • Gemini API. Writing 15 city-opener paragraphs by hand takes a week. Gemini plus a structured prompt plus the city data object equals 15 unique openers in 15 minutes. Unblocks the T2 and T3 ramp.
  • Translation API. 2 Spanish-speaking customers lost per the Feb 2026 CSV. 45 pages times 2 languages equals 90 pages for the price of 45. Real revenue leak closes.
  • Search Analytics API. We built location pages to rank. Measuring whether they DO rank requires Search Console, not GA4. The ranking feedback loop.
  • Address Validation plus Places Autocomplete. Every form submit on a location page touches both. Conversion-critical. Should ship with Rung 1, not Phase 3.

Cost summary, Year 1

Conservative worst-case run cost across the 22-API stack at BSP's current scale. Assumes no special enterprise contracts, no volume commitments.

BucketMonthly run costYear 1 worst case
Tier A (ships with Rung 1)Free to ~$30/mo~$360
Tier B (Phase 3 with Tech PWA)+~$50/mo+$600
Tier C (Phase 4 compounding)+~$50/mo+$600
Total stack~$130/mo~$1,560
Context: Ad spend is $35K/mo, $420K/yr. The full 22-API intelligence stack at worst case is 0.37% of ad spend. Intelligence-amplified pages for the price of one morning's clicks.

Decision tree

Use this flow any time a new Google API surfaces in a roadmap conversation.

Is the API free and Tier A? YES ► wire it this week NO ► next question Is the API paid but Tier A? YES ► check if within Google $200/mo Maps credit YES ► wire this week (free under cap) NO ► defer to Phase 3 NO ► next question Is the API Tier B or C? YES ► add to roadmap, do not block Phase 2 launch NO ► revisit after next quarterly review

What this unlocks beyond city chips

  • City page becomes a living dashboard. Live crew availability, live reviews, live landmarks, live address validation, all at once.
  • Programmatic T2 and T3 city ramp accelerated 10x via Gemini-generated openers against the city data object.
  • Spanish-language segment unlocked. The 2-customer-loss gap per Feb 2026 CSV is a real revenue leak that Translation API closes at /es/ depth.
  • Per-city SEO feedback loop. Search Console API tells Robert which queries win each URL, not guesses, not vanity metrics.
  • Ad strategy informed by page data. Google Ads API plus GA4 Data API closes the loop on which cities deserve bid increases, which need page repair first.
📋 8-Layer Schema — Robert build input. Audrey may skip this section for Phase 1 design work.
🧮

The 8-Layer Schema Stack

Every location page ships with all 8 layers. Robert wires the JSON-LD in the Bricks template. Audrey doesn't design this but must know it exists so Phase 2 animations and components don't break the structured data. Each layer below includes an example snippet Audrey (or anyone) can reference.

Layer 1 · Plumber (not generic LocalBusiness)

// Plumber is a more specific subtype than LocalBusiness. Google prefers specificity.
{
  "@context": "https://schema.org",
  "@type": "Plumber",
  "name": "Bright Side Plumbing",
  "telephone": "+19139631029",
  "url": "https://callbrightside.com/plumber-in-overland-park/",
  "priceRange": "$$"
}

Layer 2 · areaServed + GeoCircle

{
  "areaServed": {
    "@type": "GeoCircle",
    "geoMidpoint": {
      "@type": "GeoCoordinates",
      "latitude": 38.9822,   // from city data object
      "longitude": -94.6708
    },
    "geoRadius": "10000"   // meters — must match GBP service area
  }
}

Layer 3 · sameAs → Wikidata entity

{
  // This is the 10x MOAT: Google connects our page to the Wikidata city entity.
  "sameAs": [
    "https://www.wikidata.org/wiki/Q2030450",  // Overland Park
    "https://en.wikipedia.org/wiki/Overland_Park,_Kansas"
  ]
}

Layer 4 · hasOfferCatalog

{
  "hasOfferCatalog": {
    "@type": "OfferCatalog",
    "name": "Plumbing Services in Overland Park",
    "itemListElement": [
      { "@type": "Offer", "itemOffered": { "@type": "Service", "name": "Sewer Repair" } },
      { "@type": "Offer", "itemOffered": { "@type": "Service", "name": "Drain Cleaning" } },
      { "@type": "Offer", "itemOffered": { "@type": "Service", "name": "Water Heater Service" } }
    ]
  }
}

Layer 5 · knowsAbout (AI-search signal)

{
  // Tells Gemini / Perplexity / ChatGPT what BSP is an expert at.
  "knowsAbout": [
    "Trenchless Sewer Repair",
    "Hydro-Jetting",
    "Backflow Prevention",
    "Sewer Camera Inspection",
    "Tankless Water Heater Installation"
  ]
}

Layer 6 · FAQPage

{
  "@context": "https://schema.org",
  "@type": "FAQPage",
  "mainEntity": [
    {
      "@type": "Question",
      "name": "How much does sewer repair cost in Overland Park?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Sewer repair in Overland Park typically ranges from $3,000 to $15,000..."
      }
    }
    // ... 5 more questions minimum
  ]
}

Layer 7 · AggregateRating + Review

{
  "aggregateRating": {
    "@type": "AggregateRating",
    "ratingValue": "4.9",
    "reviewCount": "384"
  },
  "review": [
    {
      "@type": "Review",
      "author": { "@type": "Person", "name": "Homeowner in Blue Valley" },
      "reviewRating": { "@type": "Rating", "ratingValue": "5" },
      "reviewBody": "Called at 10 PM, they were out to Overland Park by 11..."
    }
  ]
}

Layer 8 · BreadcrumbList

{
  "@context": "https://schema.org",
  "@type": "BreadcrumbList",
  "itemListElement": [
    { "@type": "ListItem", "position": 1, "name": "Home", "item": "https://callbrightside.com/" },
    { "@type": "ListItem", "position": 2, "name": "Service Areas", "item": "https://callbrightside.com/service-areas/" },
    { "@type": "ListItem", "position": 3, "name": "Overland Park", "item": "https://callbrightside.com/plumber-in-overland-park/" }
  ]
}
Stack validation rule: Robert runs each page through Google's Rich Results Test before ship. All 8 layers must validate with zero errors. Schema warnings (e.g., optional fields missing) are acceptable if the primary fields are present.
🔍

City Data Object — Programmatic Multiplication Schema

Each city has one data object. The Bricks template consumes it and renders a full page. Audrey designs the template ONCE — this is how 1 template becomes 15 (Phase 1–2) or 45 pages (Phase 2 full multiplication).

{
  "slug": "overland-park",
  "name": "Overland Park",
  "state": "KS",
  "tier": 1,
  "wikidata_id": "Q2030450",
  "wikipedia_url": "https://en.wikipedia.org/wiki/Overland_Park,_Kansas",
  "lat": 38.9822,
  "lng": -94.6708,
  "geo_radius_m": 10000,
  "h1": "Sewer Repair in Overland Park — 5th-Generation Master Plumbers",
  "meta_title": "Plumber in Overland Park KS | Sewer Repair | Bright Side Plumbing",
  "meta_desc": "Sewer repair, drain cleaning, water heater service in Overland Park KS. 5th-gen family plumber. Same-day. Call (913) 963-1029.",
  "opener_paragraph": "Overland Park homes, especially along 151st and Metcalf, are often built on...",
  "neighborhoods": [
    "Blue Valley", "Corporate Woods", "Deer Creek", "Indian Hills",
    "Leabrooke", "Nottingham", "Oak Park", "Sycamore Hills",
    "The Village", "Wilshire Farms"
  ],
  "landmarks": [
    "Oak Park Mall", "Deanna Rose Farmstead",
    "Corporate Woods", "Overland Park Arboretum",
    "Scheels Overland Park Soccer Complex"
  ],
  "housing_stock_note": "Mix of 1970s-era tree-lined subdivisions north of I-435 and new construction south of 159th.",
  "soil_note": "Kansas clay; expansive soil that shifts with moisture, stressing older cast iron sewer lines.",
  "common_issue": "Tree-root intrusion into clay/cast iron sewer lines; water heater sediment from hard municipal water.",
  "response_time_minutes": 45,
  "nearby_cities": ["olathe", "lenexa", "leawood", "shawnee", "prairie-village", "mission"],
  "review_filter_terms": [
    "overland park", "op ", "blue valley", "oak park mall",
    "151st", "metcalf", "corporate woods"
  ],
  "faq": [
    {
      "q": "How much does sewer repair cost in Overland Park?",
      "a": "Sewer repair in Overland Park typically ranges from $3,000 to $15,000 depending on line length, depth, and method (traditional dig vs. trenchless). Most of the older neighborhoods north of College Blvd see higher costs because of mature trees and cast iron pipe age. We give free on-site estimates."
    },
    { // ... 5 more FAQ entries per Section 13 required types }
  ],
  "photo_set": {
    "hero": "/images/cities/overland-park/hero.jpg",
    "landmark": "/images/cities/overland-park/oak-park-mall.jpg",
    "neighborhood": "/images/cities/overland-park/blue-valley.jpg",
    "before_after": [
      { "before": "...", "after": "...", "street": "W 151st St", "date": "2026-03-15" }
    ]
  }
}
Unique-copy guardrail: Google's duplicate-content penalty hits pages that are > 60% identical. Every city data object must ensure ≥ 40% unique copy by filling: opener_paragraph, neighborhoods, landmarks, housing_stock_note, soil_note, common_issue, 6 city-specific FAQs, review filter terms. Everything else can be template-shared.
📍

Tier Order — 15 KC Metro Cities

Phase 1 ships T1 (4 cities). Phase 2 adds T2 (5 cities). Phase 3 adds T3 (6 cities). Tier order follows search volume, revenue density, and proximity to BSP HQ.

TierCitySlugWhy in this tier
T1Overland Parkoverland-parkBSP HQ city · highest search volume · highest revenue density
T1OlatheolatheSecond-largest KS metro population · heavy emergency-plumber search
T1LenexalenexaAdjacent to HQ · commercial + residential mix · strong GBP proximity
T1ShawneeshawneeOlder housing stock · sewer-repair heavy · close to HQ
T2LeawoodleawoodHigh-income · larger ticket sizes · premium positioning city
T2Prairie Villageprairie-village1950s/60s housing · heavy old-cast-iron sewer work
T2Kansas City KSkansas-city-ksLarge metro · cross-state demand · volume play
T2GardnergardnerGrowing exurb · new construction · water heater heavy
T2Spring Hillspring-hillEdge-of-metro expansion · less competitor presence
T3MerriammerriamSmaller city · filler for cluster coverage
T3MissionmissionSmall population · cluster link equity
T3Roeland Parkroeland-parkTight cluster around PV · internal link play
T3De Sotode-sotoLow competition · rural-edge sewer pumps
T3Bonner Springsbonner-springsWestern metro edge · filler
T3EdwardsvilleedwardsvilleWestern edge · filler
Phase 1 ship list: 4 T1 cities × 1 service (Plumber generic) = 4 pages. Then 4 T1 cities × 3 services = 12 pages. Then Phase 2 T2+T3 = remaining 33. Final target: 45 indexable local pages.
📅

Phase 1 vs Phase 2 — The Split

✅ Phase 1 — "Ship the Template"

Design the 14-section template + 5 new components. Ship 4 T1 cities with static Availability Chip + FAQ + Nearby Cities + Reviews (city filter on). No before/after gallery, no live ST availability, no Wisetack quote widget. Passes Audrey / Robert / Kalen sniff test.

🚀 Phase 2 — "10x Layer"

Add 10x moves: live ST availability, before/after geotagged gallery, crew-ETA map, inline Wisetack rate quote. Multiply to service × city (15 cities × 3 services = 45 pages). Phase 3 expands T2 + T3 cities.

Phase 1 Checklist (what Audrey designs to ship)

  • 🟢 14-section template at 375 / 768 / 1440
  • 🟢 5 new components designed (Emergency Bar, Embedded Map, Availability Chip, City Chip, Landmark Block)
  • 🟢 All inherited components referenced, not redesigned
  • 🟢 All tokens from homepage token library
  • 🟢 Placeholder components (before/after gallery, live availability P2, crew ETA map) dashed-bordered, sized
  • 🟢 City data object spec finalized — 4 T1 cities filled
  • 🟢 Photography shot list flagged for Robert to commission
  • 🟢 All schema layers referenced in a11y notes so build doesn't break structured data
  • 🟢 Figma layer naming: § {section}. {element} · {city-variant} · {breakpoint}

Phase 2 Hook Points (leave Figma placeholders)

  • 🟡 § 05 · Live ST Availability (API-driven)
  • 🟡 § 08 · Live crew-ETA map widget (440×240 mobile / 480h desktop)
  • 🟡 § 11 · Before/After Gallery (full section placeholder)
  • 🟡 § 12 · Inline Wisetack rate quote (340×200 inside financing card)
  • 🟡 § 09 · Neighborhood chips become clickable to /plumber-in-{city}/{neighborhood} hyper-local pages
  • 🟡 All copy · Keyword weaponization pass per BSP_Keyword_Full_Arsenal.html

QA Checklist (pre-handoff to Robert)

Design-side QA Audrey runs before handoff

  1. ✅ All 14 sections designed at 375, 768, 1440 for 1 representative city (Overland Park)
  2. ✅ 5 new components added to Figma library with variants
  3. ✅ Every H1/H2/H3/Body uses a type-scale token (no stray font sizes)
  4. ✅ Every padding/margin/gap uses an 8-px scale token
  5. ✅ Every color is a brand token (no rogue hex codes)
  6. ✅ Every button ≥ 48 × 48 px
  7. ✅ Contrast AA for all text
  8. ✅ Focus rings present on every interactive element
  9. ✅ Every image alt text includes city context in Figma layer description
  10. ✅ Sticky emergency bar designed at mobile + desktop
  11. ✅ Availability Chip designed at mobile + desktop
  12. ✅ City Chip designed at neighborhood variant + nearby-city variant
  13. ✅ Phase 2 placeholders labeled + dashed-bordered
  14. ✅ Per-city photography gaps listed on a shot-list card
  15. ✅ Inheritance verified (Trust Bar, Service Card, Review Card, FAQ Row match homepage playbook)

Build-side QA Robert runs before ship

  1. ✅ LCP < 2.5s on 4G mobile throttle (embedded map lazy-loaded)
  2. ✅ CLS < 0.1
  3. ✅ FID < 100ms
  4. ✅ Sticky emergency bar phone tel: link fires
  5. ✅ Multi-step form submits to ServiceTitan with city pre-filled + GCLID
  6. ✅ All 8 schema layers validate in Google Rich Results Test
  7. ✅ Plumber schema GeoCircle matches GBP service area polygon
  8. ✅ FAQPage schema returns FAQ rich result in Rich Results Test
  9. sameAs Wikidata URL resolves (no 404)
  10. ✅ Review filter returns ≥ 2 matching reviews for each T1 city
  11. ✅ City data object populated for all 4 T1 cities
  12. ✅ Page passes a ≥ 40% uniqueness check against the homepage and against other city pages (tool: copyscape or local diff)
  13. ✅ Embedded map loads without layout shift
  14. ✅ Cloudflare purge + LiteSpeed purge fired after deploy
  15. loading="lazy" on all below-fold images
  16. ✅ One H1 per page, sequential H2s
📬

Deliverables & Handoff

What Audrey hands to Robert

  1. 📐 Figma file — 14 sections × 3 breakpoints for Overland Park as reference city + all 5 new components with variants + all inherited components referenced
  2. 📦 Asset package — new SVGs (Emergency icon, green live dot CSS, city chip) + reused doodles and waves (already in library)
  3. 📷 Per-city photography shot list — 4 T1 cities, 5 shots each, priority ordered
  4. 📋 Copy doc — final opener paragraphs (4 cities), neighborhoods lists (4 cities), landmarks lists (4 cities), FAQ sets (4 cities), meta titles/descs (4 cities)
  5. 🎨 Design-tokens JSON — optional export from Figma Tokens plugin
  6. 📝 Phase 2 hooks list — screenshot each placeholder with API/data spec
  7. 🛡️ City data object — 4 filled examples (JSON) ready for Robert to wire into the Bricks template

Where this playbook lives

LocationURL / Path
📚 Live playbook (this doc)morpheus.callbrightside.com/documents/BSP_Location_Pages_Playbook.html
📚 Parent playbookBSP_Homepage_Redesign_Playbook.html
📁 Local source folderC:/Users/dovew/Documents/Clients/BrightSidePlumbing/BSP_Location_Pages/
🎨 Figma referenceSewer Camera Inspection Landing Page + Homepage Redesign frames (Audrey's file)
🧪 Live template to inheritbricks.callbrightside.com/sewer-camera-inspection/
📚 Master History logBSP_Master_Session_History.html
🏷️

Element Titling + Asset Spec — Audrey's Shopping List

Every layer name, every asset type, every pixel size. Copy-paste ready.
Why this exists: You asked for "exactly what to title each element." This doc gives you that — plus asset format decisions (SVG vs raster), icon sizes, middle-section image heights per viewport, and filenames. Build from this like a parts list.

1. Asset format — when to use what

Asset type Format Why
Service icons (sewer, drain, water heater, gas line, camera, emergency)SVGCrisp at any size, tiny weight, recolorable via CSS fill: currentColor
Step icons (phone, truck, wrench)SVGSame reason — geometric shapes belong in SVG
Trust chip icons (BBB badge, 5-Gen, license, check)SVGScale for retina, theme-tint via CSS
Guarantee icons (shield, clock, dollar)SVGSharp on all displays
Hero image (plumber + customer, truck, job site)WebP q85 + JPG fallbackPhotographic, 30-40% smaller than JPG, browser support > 97%
Landmark photos (Oak Park Mall, Deanna Rose, Arboretum)WebP q80Lazy-loaded, quality can be lower
Before/After photos (§11)WebP q80Real job photography, lazy-loaded below fold
Technician headshotsWebP q85Small square, quality matters for trust
Logos (BSP logo, partner brands)SVGBrand assets must never pixelate
Wave dividers + decorative shapesInline SVGGoes in HTML directly, no file round-trip, fill color tints with theme
Star ratings (⭐⭐⭐⭐⭐)Unicode ★No image at all — HTML entity, CSS-styled color
Phone/email/chat glyphs in CTAsUnicode emoji (📞 📧 💬)Free, accessible, consistent
Google Map embediframeLive Google service, not a static image
TL;DR: icons & logos & decorations → SVG. Photos → WebP. Stars + emoji glyphs → Unicode. Never mix.

2. Icon sizes — exact pixel dimensions per location

Location Mobile size Desktop size SVG viewBox Fill color
Service card icon (§06)56 × 5664 × 640 0 24 24#1D1760
Step icon (§08 How It Works)40 × 4048 × 480 0 24 24#30C5FF
Trust chip icon (§04)18 × 1820 × 200 0 24 24#30C5FF
Guarantee icon (§12)48 × 4856 × 560 0 24 24#22C55E
Financing card icon (§12)40 × 4048 × 480 0 24 24#1D1760
FAQ chevron (§13)18 × 1820 × 200 0 24 24#30C5FF
CTA button glyph (phone/book)16 × 1618 × 180 0 24 24currentColor
Rule: always design SVG on a 24×24 artboard in Figma. Export at 1x and 2x (Figma does this automatically). CSS scales to the display size above.

3. Middle-section image heights — per viewport

"Middle" = everything between the Hero (§02) and the Final CTA (§15). Hero dimensions are separate (see Hero Image section). Mobile-first widths; heights are fixed to keep content vertically predictable.

Image type Mobile (390 w) Tablet (768 w) Desktop (1440 w) Aspect Format
Google Map (§03)350 × 280720 × 3601160 × 4002 : 1 (wide)iframe
Service card icon (§06, ×6)56 × 5660 × 6064 × 641 : 1SVG
Review card (§07, no image, text only)— (text card)
Step icon (§08, ×3)40 × 4044 × 4448 × 481 : 1SVG
Landmark photo (§10, optional ×3-5)350 × 230360 × 240380 × 2503 : 2WebP q80
Before photo (§11, ×3-4)350 × 260360 × 270480 × 3203 : 2WebP q80
After photo (§11, ×3-4)350 × 260360 × 270480 × 3203 : 2WebP q80
Financing card icon (§12)40 × 4044 × 4448 × 481 : 1SVG
Guarantee tile icon (§12, ×3)48 × 4852 × 5256 × 561 : 1SVG
Wave divider (§15 transition)full-width × 60full-width × 80full-width × 100inline SVG
Maximum row-height budget: no middle image exceeds 400 px on desktop or 280 px on mobile. Keeps scroll-depth predictable. Hero is the only "tall" image on the page.

4. Filename convention — the one Robert expects

FORMAT:  bsp_loc_[city-slug]_[section]_[what]_[viewport]_[wxh]@[density].[ext]

EXAMPLES:
  bsp_loc_overland-park_hero_main_desktop_1440x720@2x.webp
  bsp_loc_overland-park_hero_main_mobile_390x520@2x.webp
  bsp_loc_overland-park_landmark_01_oak-park-mall_desktop_380x250@2x.webp
  bsp_loc_overland-park_beforeafter_01_before_main-line_desktop_480x320@2x.webp
  bsp_loc_overland-park_beforeafter_01_after_main-line_desktop_480x320@2x.webp

ICONS (shared across cities, no city slug):
  bsp_icon_sewer.svg
  bsp_icon_drain.svg
  bsp_icon_water-heater.svg
  bsp_icon_gas-line.svg
  bsp_icon_camera.svg
  bsp_icon_emergency.svg
  bsp_icon_step-call.svg
  bsp_icon_step-truck.svg
  bsp_icon_step-fix.svg
  bsp_icon_guarantee-uptime.svg
  bsp_icon_guarantee-warranty.svg
  bsp_icon_guarantee-pricing.svg
  bsp_icon_financing-card.svg
  bsp_icon_chevron-down.svg

Rules

5. Per-section element titles — Figma layer names, exact

Copy these layer names into Figma verbatim. [city-slug] is replaced per-city at build time. Top-level Figma page: BSP Location Pages - [City Name].

§01 · 🚨 Sticky Emergency Bar
Frame:   sec-01_emergency_bar_mobile   (390 × 48)
         sec-01_emergency_bar_desktop  (1440 × 40)

Layers:
  bar_container              (Auto Layout H, red fill #EF4444, fixed top)
  bar_icon_siren             (SVG 16×16 — uses 🚨 emoji or custom SVG)
  bar_label_emergency        (text: "Emergency?")
  bar_phone_link             (text: "(913) 963-1029", yellow #FFEA00, underlined)
  bar_divider_dot            (text: "·", static separator)
  bar_service_note           (text: "Same-day service in [City]")
  bar_live_badge             (optional — "LIVE" pill on right, only if Phase 2)
§02 · 📍 Hero (city-specific)
Frame:   sec-02_hero_mobile   (390 × 520)
         sec-02_hero_desktop  (1440 × 720)

Layers:
  hero_container             (Auto Layout V, navy bg #1D1760)
  hero_image                 (WebP — see Hero Image Dimensions section for exact px)
  hero_gradient_overlay      (linear-gradient navy → transparent, for H1 readability)
  hero_h1                    (text: "Plumber in [City]", 46px desktop / 32px mobile, white)
  hero_subhead               (text: "5th-generation family plumber · Same-day dispatch")
  hero_trust_chip_row        (Auto Layout H, wrap)
    chip_rating              (⭐ 4.9 / 5)
    chip_reviews             (384+ reviews)
    chip_years               (Since 1952)
    chip_licensed            (Licensed + Insured)
  hero_cta_group             (Auto Layout H)
    cta_phone                (📞 Call · yellow bg #FFEA00, navy text)
    cta_book                 (📅 Book · blue bg #30C5FF, white text)
    cta_chat                 (💬 Chat · outline navy border)
§03 · 🗺️ Embedded Google Map
Frame:   sec-03_map_mobile   (350 × 280)
         sec-03_map_desktop  (1160 × 400)

Layers:
  map_container              (Auto Layout V)
  map_embed_placeholder      (dashed border 2px #30C5FF — shows iframe location)
  map_pin                    (BSP pin SVG, centered on city lat/lng)
  map_caption                (text: "Serving [City] and surrounding areas")
  map_address_line           (text: 12022 Blue Valley Pkwy, Overland Park, KS 66213)
§04 · 🛡️ Trust Bar (6 chips, reused from homepage)
Frame:   sec-04_trust_bar_mobile   (full-width × ~120 wrap)
         sec-04_trust_bar_desktop  (full-width × 56)

Layers:
  trust_bar_container        (Auto Layout H, wrap, gap 8 mobile / 12 desktop)
  trust_chip_rating          (⭐ 4.9 / 5)
  trust_chip_bbb             (BBB A+)
  trust_chip_5gen            (5-Generation Family)
  trust_chip_sameday         (Same-Day Service)
  trust_chip_licensed        (Licensed + Insured)
  trust_chip_estimate        (Free Estimate)

Each chip = Auto Layout H with:
  chip_icon                  (SVG 18×18)
  chip_label                 (text, Inter 13px semibold)
§05 · ⚡ Live Availability (10x)
Frame:   sec-05_availability_mobile   (full-width × 160)
         sec-05_availability_desktop  (full-width × 140)

Layers:
  availability_container     (Auto Layout V, green tint bg)
  availability_dot_pulse     (12×12 green circle #22C55E with CSS pulse animation)
  availability_label         (text: "Techs available in [City] today")
  availability_eta           (text: "45-min avg response time")
  availability_city_chip     ([City] name pill, outlined blue)
  availability_cta_call      (📞 Call now CTA, secondary style)

Phase 1 = static "45-min avg". Phase 2 = pulls from ST dispatch.
§06 · 🔧 Services in [City] (6 cards)
Frame:   sec-06_services_mobile   (full-width × auto)
         sec-06_services_desktop  (full-width × auto)

Layers:
  services_container         (Auto Layout V)
  services_h2                (text: "Plumbing Services in [City]", 32px / 24px)
  services_intro             (1-paragraph intro that references local soil, pipe age, a landmark)
  services_grid              (Auto Layout H, 1-col mobile / 2-col tablet / 3-col desktop, wrap)
    service_card_01_sewer        ← SEWER FIRST (highest intent)
      service_icon_01            (SVG 56/64, #1D1760 on #FFEA00 circle)
      service_card_title_01      (text: "Sewer Repair in [City]")
      service_card_body_01       (30-50 word blurb)
      service_card_link_01       (→ "Get a free estimate")
    service_card_02_drain
    service_card_03_water-heater
    service_card_04_gas-line
    service_card_05_camera
    service_card_06_emergency
§07 · 💬 City-Filtered Reviews (moat)
Frame:   sec-07_reviews_mobile   (full-width × auto)
         sec-07_reviews_desktop  (full-width × auto)

Layers:
  reviews_container          (Auto Layout V)
  reviews_h2                 (text: "What [City] Homeowners Say")
  reviews_stats_row          (⭐ 4.9 · 384 reviews · pull from Google)
  reviews_grid               (Auto Layout V mobile / H 2-col desktop)
    review_card_01
      review_card_stars      (Unicode ★★★★★, yellow #FFEA00)
      review_card_quote      (text, italic, max 4 lines)
      review_card_author_row
        review_author_name   (text, Inter 13px bold)
        review_author_badge_city  (text: "· Overland Park", blue chip)
        review_source_chip   (Google logo SVG 14×14)
    review_card_02
    review_card_03
    review_card_04

Live: pulls from Google Places API, regex-filter by [city] or neighborhood names.
§08 · 🚪 How It Works (3 steps)
Frame:   sec-08_howitworks_mobile   (full-width × auto)
         sec-08_howitworks_desktop  (full-width × auto)

Layers:
  howitworks_container       (Auto Layout V, wave-bg optional)
  howitworks_h2              (text: "How We Get You Fixed in [City]")
  howitworks_steps           (Auto Layout V mobile / H 3-col desktop)
    step_01_call
      step_number_01         (text "01" — large, navy, outlined)
      step_icon_01           (SVG 40/48 — phone icon, #30C5FF)
      step_title_01          (text: "Call")
      step_body_01           (1-sentence explanation)
    step_02_arrive
      step_number_02
      step_icon_02           (truck icon)
      step_title_02          ("We Arrive Same-Day")
      step_body_02
    step_03_fix
      step_number_03
      step_icon_03           (wrench icon)
      step_title_03          ("Fixed Right")
      step_body_03

Phase 2: add live crew ETA map below step 02.
§09 · 🏠 Neighborhoods Served (8-12 chips)
Frame:   sec-09_neighborhoods_mobile   (full-width × auto)
         sec-09_neighborhoods_desktop  (full-width × auto)

Layers:
  neighborhoods_container    (Auto Layout V)
  neighborhoods_h2           (text: "We Fix Plumbing Across [City]")
  neighborhoods_copy_block   (1 paragraph: "from Blue Valley to Corporate Woods...")
  neighborhoods_chip_grid    (Auto Layout H, wrap, gap 8 mobile / 10 desktop)
    neighborhood_chip_01     (text-only pill, no icon — outlined blue #30C5FF)
    neighborhood_chip_02
    ...
    neighborhood_chip_12     (last — pulls from city data object)

Per-city data object supplies names (Blue Valley, Corporate Woods, Deer Creek, etc.).
§10 · 🏛️ Local Landmarks (copy block + optional photos)
Frame:   sec-10_landmarks_mobile   (full-width × auto)
         sec-10_landmarks_desktop  (full-width × auto)

Layers:
  landmarks_container        (Auto Layout V, amber ivory bg #FFFBEB)
  landmarks_h3               (optional subhead)
  landmarks_copy             (1-paragraph referencing 3-5 landmarks naturally —
                              "From Oak Park Mall to the homes off 151st...")
  landmarks_photo_row        (Phase 2 optional, Auto Layout H wrap)
    landmark_photo_01        (WebP 380×250)
      landmark_caption_01    (text: "Oak Park Mall, Overland Park")
    landmark_photo_02
    landmark_photo_03

Phase 1: text-only. Phase 2: add photo row.
§11 · 📷 Before/After Gallery (Phase 2)
Frame:   sec-11_beforeafter_mobile   (full-width × auto)
         sec-11_beforeafter_desktop  (full-width × auto)

Layers:
  beforeafter_container      (Auto Layout V)
  beforeafter_h2             (text: "Real Jobs Near You")
  beforeafter_grid           (Auto Layout H wrap 1-col mobile / 2-col desktop)
    beforeafter_01
      beforeafter_01_pair    (Auto Layout H, gap 8)
        beforeafter_01_before     (WebP 480×320)
          beforeafter_01_before_label  ("BEFORE" pill, top-left overlay)
        beforeafter_01_after      (WebP 480×320)
          beforeafter_01_after_label   ("AFTER" pill, top-left overlay)
      beforeafter_01_caption (text: "Main line replacement · 117th & Metcalf · 2h")
    beforeafter_02
    beforeafter_03
    beforeafter_04

Phase 1: dashed-border placeholder frames only, copy reads "Gallery coming soon."
Phase 2: real photos pulled from ST job library tagged by street.
§12 · 💳 Financing + 🛡️ Guarantees
Frame:   sec-12_financing_guarantees_mobile   (full-width × auto)
         sec-12_financing_guarantees_desktop  (full-width × auto)

Layers:
  fg_container               (Auto Layout V, light bg #F8FAFC)

  // Financing subgroup
  financing_card             (Auto Layout H, Wisetack card)
    financing_icon_card      (SVG 40/48 — credit card glyph)
    financing_body           (text: "Approved in 30 seconds. 0% APR options.")
    financing_cta            (→ "See financing options")

  // Guarantees subgroup
  guarantees_h3              (text: "Our Promise")
  guarantees_tiles           (Auto Layout H 1-col mobile / 3-col desktop)
    guarantee_01_uptime
      guarantee_icon_01      (SVG clock, 48/56, green #22C55E)
      guarantee_title_01     ("On Time or It's Free")
      guarantee_body_01      (1-sentence)
    guarantee_02_warranty
      guarantee_icon_02      (SVG shield)
      guarantee_title_02     ("Lifetime Parts Warranty")
    guarantee_03_pricing
      guarantee_icon_03      (SVG dollar)
      guarantee_title_03     ("Upfront Pricing, No Surprises")
§13 · ❓ City FAQ (6 Q&A minimum, FAQPage schema)
Frame:   sec-13_faq_mobile   (full-width × auto)
         sec-13_faq_desktop  (max-width 800, centered × auto)

Layers:
  faq_container              (Auto Layout V)
  faq_h2                     (text: "[City] Plumbing FAQ")
  faq_accordion              (Auto Layout V, gap 0 — rows stack with bottom borders)
    faq_row_01_pricing       (expanded in Figma for preview)
      faq_question_01        (text: "How much does sewer repair cost in [City]?")
      faq_chevron_01         (SVG 18/20, rotates on expand)
      faq_answer_01          (text, 40-80 words, always mentions [City])
    faq_row_02_response      (collapsed in Figma)
    faq_row_03_area
    faq_row_04_emergency
    faq_row_05_financing
    faq_row_06_trust
§14 · 📍 Nearby Cities (internal link grid)
Frame:   sec-14_nearby_mobile   (full-width × auto)
         sec-14_nearby_desktop  (full-width × auto)

Layers:
  nearby_container           (Auto Layout V, centered)
  nearby_intro               (text: "Also serving [City]'s neighbors:")
  nearby_chip_grid           (Auto Layout H, wrap, gap 8 mobile / 10 desktop)
    nearby_chip_01_olathe    (pill: "📍 Plumber in Olathe")
    nearby_chip_02_lenexa
    nearby_chip_03_leawood
    nearby_chip_04_shawnee
    nearby_chip_05_prairie-village
    nearby_chip_06_mission

Each chip = clickable, routes to /plumber-in-[slug]/
§15 · 📞 Final CTA + Multi-Step Form + Footer
Frame:   sec-15_final_cta_mobile   (full-width × auto, ~680 content)
         sec-15_final_cta_desktop  (full-width, centered card 640 wide × auto)

Layers:
  final_cta_container        (Auto Layout V, wave-to-footer bg transition)
  wave_divider_top           (inline SVG, navy fill, 100px tall desktop / 60 mobile)
  cta_card                   (Auto Layout V, navy fill #1D1760, white text)
    cta_h2                   (text: "Ready to fix it right in [City]?")
    cta_subhead              (text: "Call Kansas City's 5th-generation plumbing family.")
    cta_button_trio          (Auto Layout H, wrap 1-col mobile / 3-col desktop)
      cta_call               (📞 Call button — yellow primary)
      cta_book               (📅 Book button — blue secondary)
      cta_chat               (💬 Chat button — outline)
    cta_form                 (Auto Layout V, multi-step)
      form_progress_dots     (3 dots, current highlighted)
      form_step_01_service   (What service? Sewer/Drain/WH/Other)
      form_step_02_urgency   (When? Emergency/Today/This week/Flexible)
      form_step_03_contact   (Name, Phone, Address, ZIP — city pre-selected)
      form_submit_button     (Continue → arrow)
  footer_global              (inherits from homepage playbook §14 — no design work)
Handoff rule: every Figma layer name above matches this doc exactly. If Audrey changes a name, update this doc first, then change Figma. Robert's Bricks build script reads this doc as the layer-name source of truth.

✅ Shopping-list checklist — you are ready to ship Figma when…

Phase 1 Copy Deck - Audrey-Authored

[Copy]Location Pages Copy Deck

Source: /opt/nexus/nexus/scripts/output/audrey_copy/locations/ · Integrated 2026-05-01 · BSP Session 9 Track D Phase 3
TL;DRVariables in {curly braces} are filled per city from the data object. H1 formula: {Service} in {City}. 5th-Generation Master Plumbers.
[#]

Key Stats & Anchors

Same-day
Subhead formula: Same-day service across {City} and nearby....
5 Generations
4.9 Google (384+) · BBB A+ · 5 Generations · Same-Day ·...
Same-Day
4.9 Google (384+) · BBB A+ · 5 Generations · Same-Day ·...
same-day
¶2. We dispatch licensed plumbers with same-day...
$500
Overland Park homes, especially along 151st and Metcalf,...
[1]

Section-by-Section Copy

Location Pages Copy Deck

Source: BSP_Location_Pages_Playbook.html (fetched 2026-05-01). Pattern applies to every /service-areas/{slug} page. Phase 1 covers 14 cities: Overland Park, Olathe, Lenexa, Shawnee, Leawood, Prairie Village, Mission, Kansas City MO, Lee's Summit, Blue Springs, Independence, Gladstone, Liberty, Raytown.

Variables in {curly braces} are filled per city from the data object.


Section 01. Hero

H1 formula: {Service} in {City}. 5th-Generation Master Plumbers.

Subhead formula: Same-day service across {City} and nearby. Licensed, insured, family-owned since 1940.

Meta title (50-60 chars): Plumber in {City} KS | Sewer Repair | Bright Side Plumbing Meta description (150-160 chars): Sewer repair, drain cleaning, water heater service in {City} KS. 5th-generation family plumber. Same-day. Call (913) 963-1029.

Sticky emergency bar (top of page): Emergency? Call (913) 963-1029. Same-day service in {City}.


Section 02. Trust Bar (reused 6 chips)

4.9 Google (384+) · BBB A+ · 5 Generations · Same-Day · Licensed and Insured · Free Estimates

Availability chip line: Techs available in {City} today. 45-min average response time.


Section 03. Local Intro

Two short paragraphs, voice anchored in city name.

¶1. We have been fixing plumbing in {City} for decades. Our crew is the same family that has run trucks across the KC metro since 1940, and {City} homes have been on our route from day one.

¶2. We dispatch licensed plumbers with same-day availability across {City}. Sewer line breaks, drain backups, water heater swaps, and slab leaks. We answer the phone, we show up in the window, and we charge the price we quoted.


Section 06. Services Section Opener (city-specific)

120-160 words drawn from the city data object: housing_stock_note + common_issue.

Overland Park sample: Overland Park homes, especially along 151st and Metcalf, are often built on Kansas clay soil that shifts enough to crack old cast-iron sewer lines. We have run trenchless camera inspections from Oak Park Mall to newer builds south of 179th, and we know which subdivisions have the original 1970s pipe still in the ground. If you are seeing slow drains in multiple fixtures, smelling sewer in the basement, or hearing gurgling in the toilet when the dishwasher runs, you are likely looking at a sewer line problem. We can pull a camera through your line for free with any sewer repair $500 or over and tell you in plain English whether you need a spot repair, a trenchless reline, or a full replacement.

Pattern reuse: Each city gets its own version. Reference 2-3 real streets, neighborhoods, or commercial landmarks. Reference the typical housing-stock issue (clay soil, mature trees, old galvanized lines, post-1990 PVC, etc).


Section 07. Reviews (filtered)

H2: What {City} Homeowners Say

Filter logic: regex match on review body for {city name} OR any 8-12 neighborhood names OR any 3-5 landmark names (case-insensitive).

Fallback when fewer than 2 city-specific reviews surface: show "near {City}" reviews from adjacent neighborhoods with caption "Reviews from the KC metro nearby."


Section 09. Neighborhoods

Intro line: From {Neighborhood A} to {Neighborhood Z}, we have fixed pipes on every side of {City}.

Overland Park sample intro: From Blue Valley to Wilshire Farms, we have fixed pipes on every side of Overland Park.

8-12 real neighborhood names per city, rendered as pill chips. Phase 1 chips are non-clickable. Phase 2 wires each chip to a /service-areas/{city}/{neighborhood} page if volume merits.


Section 10. Landmarks

3-5 landmarks woven naturally into a paragraph. Never force all 5 if it sounds spammy.

Pattern: Whether you are near {Landmark A}, around {Landmark B}, or out by {Landmark C}, our crew knows the streets. We have pulled roots out of sewer lines from the older trees around {Landmark D} and swapped water heaters in new construction off {Landmark E}.


Section 13. FAQ (6 required types)

See faq_seed.md. Six required topics: pricing, response time, area specificity, emergency, financing, trust. Each answer mentions {City} at least once.


Section 15. Final CTA

Headline: Ready to fix it right in {City}? Sub: Call Kansas City's 5th-generation plumbing family.

Multi-step form. City pre-selected from URL slug.

  • Call (913) 963-1029 (yellow #FFEA00)
  • Book Online (blue outline)
  • Chat with Daniel (text link)

Tone Rules

  • Local ownership. Pages must read as if written by someone who knows the neighborhood.
  • Emergency-first. Sticky bar visible in first viewport.
  • Family credential. "5th-generation" or "family-owned since 1940" appears in hero.
  • Never use generic swap-in copy. Every city section differs.
  • City-keyword density. {City} in title, H1, H2, opener, FAQ answers, and chips, naturally.
[2]

Hero Options

Headline Options ({City} substitution)

Option A (playbook canonical, recommended) H1: Sewer Repair in {City}. 5th-Generation Master Plumbers. Sub: Same-day service across {City} and nearby. Licensed, insured, family-owned since 1940. Option B (broader plumbing service hero) H1: {City} Plumbing. Same-Day Service from a 5th-Generation KC Family. Sub: Sewer, drain, and water heater experts. Licensed and insured. Option C (urgency-first, Emergency Eric lead) H1: Emergency Plumber in {City}. Same-Day Sewer, Drain, and Water Heater Service. Sub: Five generations of Kansas City plumbing. Family-owned since 1940. Option D (problem-first, mirror persona) H1: Sewer Backup in {City}? Our Crew Is in Your Neighborhood Today. Sub: 5th-generation Kansas City plumbers. Same-day. Free camera inspection with any sewer repair. Option E (heritage anchor, Maintenance Mike lead) H1: {City}'s 5th-Generation Plumbing Family. Sewer, Drain, and Water Heater Experts. Sub: Same-day service. Two-year warranty. Family-owned since 1940.

Worked example: Overland Park

H1: Sewer Repair in Overland Park. 5th-Generation Master Plumbers. Sub: Same-day service across Overland Park and nearby. Licensed, insured, family-owned since 1940. H1 alt for water heater landing: Water Heater Service in Overland Park. 5th-Generation Master Plumbers. H1 alt for drain landing: Drain Cleaning in Overland Park. From $99. 5th-Generation Master Plumbers.

Recommendation

Use Option A as the default formula and swap the lead service to match the page intent (sewer / water heater / drain / leak). Hold Option D as a paid-traffic emergency variant.

[3]

CTA Copy

Sticky Emergency Bar (top of every location page)

Emergency? Call (913) 963-1029. Same-day service in {City}.

Hero CTAs

Primary: Call (913) 963-1029 (yellow #FFEA00) Variant: Call Now. Same-Day in {City}. Secondary: Book Online (blue outline) Variant: Schedule a {City} Plumber Tertiary: Chat with Daniel (text link)

In-Section CTAs

After Reviews block. Read All Reviews from {City} (text link, #30C5FF) After Neighborhoods chips. See If We Cover Your Block (blue outline) After Landmarks paragraph. Get a Free Sewer Camera Inspection (yellow button, links to coupons)

Final CTA Block

Headline: Ready to fix it right in {City}? Sub: Call Kansas City's 5th-generation plumbing family. Three buttons.

  • Call (913) 963-1029 (yellow #FFEA00, primary)
  • Book Online (blue outline)
  • Chat with Daniel (text link)

Multi-step form. City pre-selected from URL slug. Step 1: Service type (Sewer / Drain / Water Heater / Other). Step 2: Timing (Emergency now / Today / This week / Flexible). Step 3: Name / Phone / Address / ZIP.

Microcopy Rules

  • {City} appears in at least one CTA on every location page.
  • Phone number tap-to-call wired on mobile. 48 px touch targets.
  • No em dashes.
  • Yellow #FFEA00 reserved for primary urgency button only.
[4]

FAQ Seed

Worked example: Overland Park?

Replace {City} with "Overland Park" and {Neighborhood A/B/C} with Blue Valley, Wilshire Farms, Indian Creek.

Worked example: Olathe?

Replace {City} with "Olathe" and {Neighborhood A/B/C} with Stonebridge, Cedar Creek, Forest View.

Source Citations (click to expand)

Location Pages Source Citations

Primary source: BSP_Location_Pages_Playbook.html (fetched 2026-05-01). Brand canon: CLAUDE.md.


| Element | Source | Notes | |---|---|---| | H1 formula "{Service} in {City}. {Credential}" | Playbook §Hero Pattern | Verbatim formula. | | Subhead with "Same-day service across {City}" | Playbook §Hero Pattern | Verbatim. | | Meta title formula | Playbook §Hero Pattern | 50-60 char target. | | Meta description formula | Playbook §Hero Pattern | 150-160 char target. | | Trust bar 6 chips | Playbook §Local Trust Elements | Reused from homepage. | | Sticky emergency bar | Playbook §Local Trust Elements | Verbatim copy. | | Availability chip "Techs available in {City} today, 45-min average response time" | Playbook §Local Trust Elements | Verbatim. | | 120-160 word services opener | Playbook §06 Services Section Opener | Pattern + Overland Park sample. | | Overland Park sample 151st/Metcalf/Oak Park Mall/179th | Playbook §06 worked example | Verbatim guidance. | | Reviews filter logic | Playbook §07 Review Filtering | Regex on city + neighborhoods + landmarks. | | Neighborhoods intro line | Playbook §09 Neighborhoods | "From {Neighborhood A} to {Neighborhood Z}..." | | Landmarks paragraph pattern | Playbook §10 Landmarks Copy Block | 3-5 landmarks, never spammy. | | 6 FAQ types | Playbook §13 FAQ Pattern | pricing/response/area/emergency/financing/trust. | | Final CTA "Ready to fix it right in {City}?" | Playbook §15 Final CTA | Verbatim headline. | | Multi-step form spec | Playbook §15 Final CTA | 3-step. | | Tone rules (local ownership, emergency-first, family credential) | Playbook §Tone & Voice Rules | Verbatim. | | 14-city Phase 1 list | Homepage Playbook §09 Service Areas | Phase 1 scope. | | NAP block | CLAUDE.md BSP Core Facts + Playbook §NAP Consistency | Header/footer/schema canonical. | | Hours 8 AM to 9 PM Mon-Sat | Contact Playbook | Hard rule, applies to FAQ Q4. | | No em dashes / no emojis | CLAUDE.md + Playbook | Voice rule. |


Verbatim quotes preserved

  • "Sewer Repair in Overland Park. 5th-Generation Master Plumbers." (H1 worked example).
  • "From Blue Valley to Wilshire Farms, we've fixed pipes on every side of Overland Park." (Section 09 sample).
  • "Ready to fix it right in {City}?" (Section 15 headline).

Edits

  • Em dashes replaced with periods or commas.
  • "Same-day service across {City} and nearby." retained verbatim.
Generated BSP Session 9 Track D Phase 3 · sources preserved in /opt/nexus/nexus/scripts/output/audrey_copy/locations/ · backups in /tmp/playbook_*_pre_session_9_*.html