← back to Ventura Claw Leads
Add 37 vertical+city combo pages to sitemap; unclaimed banner on listing detail
d421ce8fb9d8951048fa82b5e59e4e64cf4afe8c · 2026-05-07 11:55:25 -0700 · Steve Abrams
Two coordinated SEO + monetization improvements:
1. sitemap.xml now lists 37 'vertical in city' combos (8v×5c minus 3
thin combos with <3 businesses) — '/find?vertical=beauty&city=
Sherman+Oaks' etc. The /find route already renders unique H1 +
meta + canonical for each combo (e.g. '155 beauty businesses on
Ventura Blvd in Sherman Oaks'), so these are real landing pages
from Google's perspective, not duplicates.
2. /business/<slug> now shows a top-of-page 'unclaimed' banner with
anchor link to the existing #claim form for the 1,819 corridor-
seeded shells. The empty 'About' section now reads with context
('listed from public records, owners can claim this page free')
instead of the generic 'this business hasn't added a description'.
Closes the gap between 'we have 1,819 unclaimed listings' and
'visitors can find the claim CTA above the fold on mobile'.
Files touched
M routes/public.jsM views/public/business.ejs
Diff
commit d421ce8fb9d8951048fa82b5e59e4e64cf4afe8c
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Thu May 7 11:55:25 2026 -0700
Add 37 vertical+city combo pages to sitemap; unclaimed banner on listing detail
Two coordinated SEO + monetization improvements:
1. sitemap.xml now lists 37 'vertical in city' combos (8v×5c minus 3
thin combos with <3 businesses) — '/find?vertical=beauty&city=
Sherman+Oaks' etc. The /find route already renders unique H1 +
meta + canonical for each combo (e.g. '155 beauty businesses on
Ventura Blvd in Sherman Oaks'), so these are real landing pages
from Google's perspective, not duplicates.
2. /business/<slug> now shows a top-of-page 'unclaimed' banner with
anchor link to the existing #claim form for the 1,819 corridor-
seeded shells. The empty 'About' section now reads with context
('listed from public records, owners can claim this page free')
instead of the generic 'this business hasn't added a description'.
Closes the gap between 'we have 1,819 unclaimed listings' and
'visitors can find the claim CTA above the fold on mobile'.
---
routes/public.js | 24 +++++++++++++++++++++++-
views/public/business.ejs | 12 ++++++++++++
2 files changed, 35 insertions(+), 1 deletion(-)
diff --git a/routes/public.js b/routes/public.js
index 9da3dc9..de8569e 100644
--- a/routes/public.js
+++ b/routes/public.js
@@ -601,13 +601,35 @@ router.get('/sitemap.xml', async (req, res, next) => {
loc: `/neighborhood/${n.slug}`, changefreq: 'daily', priority: '0.7'
}));
+ // 8 verticals × 5 cities = 40 long-tail combo landing pages. The /find
+ // route already supports both filters and renders a unique H1 + meta
+ // for each combo (e.g. "Beauty in Sherman Oaks on Ventura Blvd").
+ // Only include combos that actually have ≥3 businesses — empty results
+ // would be soft-404s in Google's eyes.
+ const comboCounts = await db.many(`
+ SELECT vertical, city, COUNT(*)::int AS n
+ FROM businesses
+ WHERE status = 'active' AND city IS NOT NULL
+ GROUP BY vertical, city
+ HAVING COUNT(*) >= 3
+ `);
+ const verticalCityPages = [];
+ for (const c of comboCounts) {
+ // city goes through encodeURIComponent so 'Sherman Oaks' → 'Sherman%20Oaks'.
+ // Sitemap URLs need to be percent-encoded, then '&' XML-escaped by escape().
+ verticalCityPages.push({
+ loc: `/find?vertical=${c.vertical}&city=${encodeURIComponent(c.city)}`,
+ changefreq: 'weekly', priority: '0.5'
+ });
+ }
+
// image: namespace declared on <urlset> so Google Image search can ingest
// <image:image> children for businesses that have a photo on file. Unphotographed
// listings still appear (just without an <image:image> child) — falling back
// to the dynamic OG card here would produce thin Image-search results since
// those aren't real photos of the business.
let xml = '<?xml version="1.0" encoding="UTF-8"?>\n<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1">\n';
- for (const p of [...staticPages, ...verticalPages, ...neighborhoodPages]) {
+ for (const p of [...staticPages, ...verticalPages, ...neighborhoodPages, ...verticalCityPages]) {
xml += ` <url><loc>${baseUrl}${escape(p.loc)}</loc><lastmod>${today}</lastmod><changefreq>${p.changefreq}</changefreq><priority>${p.priority}</priority></url>\n`;
}
for (const b of businesses) {
diff --git a/views/public/business.ejs b/views/public/business.ejs
index 7a78159..dba4856 100644
--- a/views/public/business.ejs
+++ b/views/public/business.ejs
@@ -66,6 +66,13 @@
%>
<script type="application/ld+json"><%- JSON.stringify(_ldCrumbs) %></script>
+<% if (business.claim_status === 'unclaimed') { %>
+ <div role="note" style="background:var(--bg);border-bottom:1px solid var(--border);padding:10px 28px;font-size:13px;text-align:center;color:var(--fg-muted)">
+ <span style="display:inline-block;vertical-align:middle">📋 This listing is unclaimed — sourced from Ventura corridor public records.</span>
+ <a href="#claim" style="display:inline-block;vertical-align:middle;margin-left:6px;font-weight:600;color:var(--brass)">Are you the owner? Claim it free →</a>
+ </div>
+<% } %>
+
<nav class="breadcrumb" aria-label="Breadcrumb" style="padding:16px 28px 0;max-width:1100px;margin:0 auto;font-size:12px;color:var(--fg-muted);letter-spacing:0.04em">
<% _crumbs.forEach(function(c, i){
var isLast = i === _crumbs.length - 1;
@@ -137,6 +144,11 @@
<% if (business.description) { %>
<h2>About</h2>
<p><%= business.description %></p>
+ <% } else if (business.claim_status === 'unclaimed') { %>
+ <h2>About</h2>
+ <p class="muted">This listing is sourced from Ventura corridor public records and hasn't been claimed by the business yet.
+ If you're the owner, you can <a href="#claim">claim this page free</a> to add hours, photos, and a real description.
+ </p>
<% } else { %>
<h2>About</h2>
<p class="muted">This business hasn't added a full description yet. Reach out below — they'll send you details directly.</p>
← 05ae6c7 Add per-vertical sample rails to home page (food/beauty/reta
·
back to Ventura Claw Leads
·
Cross-link neighborhood pages to vertical+city combo pages dbc5da9 →