← back to Ventura Claw Leads
yolo tick 1: JSON-LD LocalBusiness + ItemList structured data
520e445ac651f834d287215a006c0b30a83f1072 · 2026-05-06 17:00:03 -0700 · Steve Abrams
views/public/business.ejs: emit LocalBusiness JSON-LD with name/url/description/
phone/email/sameAs/address/geo. Always type=LocalBusiness (parent type) rather
than mapping to vertical-specific subtypes (Restaurant/BeautySalon/Pet…) since
proper subtype mapping requires per-vertical schema.org alignment, while
LocalBusiness is the safe parent for all 8 verticals.
views/public/find.ejs: emit ItemList JSON-LD on the directory list page —
top-30 businesses as ListItem entries with rank/url/name. Search engines surface
this as 'rich list' in results.
Live on prod 2026-05-06: parsed cleanly, 30 items rendered for /find,
LocalBusiness with geo+address for /business/:slug.
Files touched
M views/public/business.ejsM views/public/find.ejs
Diff
commit 520e445ac651f834d287215a006c0b30a83f1072
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Wed May 6 17:00:03 2026 -0700
yolo tick 1: JSON-LD LocalBusiness + ItemList structured data
views/public/business.ejs: emit LocalBusiness JSON-LD with name/url/description/
phone/email/sameAs/address/geo. Always type=LocalBusiness (parent type) rather
than mapping to vertical-specific subtypes (Restaurant/BeautySalon/Pet…) since
proper subtype mapping requires per-vertical schema.org alignment, while
LocalBusiness is the safe parent for all 8 verticals.
views/public/find.ejs: emit ItemList JSON-LD on the directory list page —
top-30 businesses as ListItem entries with rank/url/name. Search engines surface
this as 'rich list' in results.
Live on prod 2026-05-06: parsed cleanly, 30 items rendered for /find,
LocalBusiness with geo+address for /business/:slug.
---
views/public/business.ejs | 31 +++++++++++++++++++++++++++++++
views/public/find.ejs | 23 +++++++++++++++++++++++
2 files changed, 54 insertions(+)
diff --git a/views/public/business.ejs b/views/public/business.ejs
index 95942cb..1768baa 100644
--- a/views/public/business.ejs
+++ b/views/public/business.ejs
@@ -9,7 +9,38 @@
}
var _site = _safeUrl(business.website);
var _addressOneline = [business.street, business.neighborhood || business.city, business.state, business.zip].filter(Boolean).join(', ');
+
+ // JSON-LD LocalBusiness — Google search rich-results + voice-search surface.
+ // We always emit type=LocalBusiness rather than mapping to subtypes (Restaurant,
+ // BeautySalon, etc.) because subtype mapping requires careful per-vertical
+ // schema.org alignment; LocalBusiness is the safe parent for all 8 verticals.
+ var _publicUrl = (typeof publicUrl !== 'undefined' && publicUrl) ? publicUrl.replace(/\/+$/, '') : 'https://leads.venturaclaw.com';
+ var _ldBusiness = {
+ '@context': 'https://schema.org',
+ '@type': 'LocalBusiness',
+ '@id': _publicUrl + '/business/' + business.slug,
+ name: business.business_name,
+ url: _publicUrl + '/business/' + business.slug
+ };
+ if (business.headline) _ldBusiness.description = business.headline;
+ if (business.phone) _ldBusiness.telephone = business.phone;
+ if (business.email) _ldBusiness.email = business.email;
+ if (_site) _ldBusiness.sameAs = [_site];
+ if (business.street || business.city) {
+ _ldBusiness.address = {
+ '@type': 'PostalAddress',
+ streetAddress: business.street || undefined,
+ addressLocality: business.city || undefined,
+ addressRegion: business.state || 'CA',
+ postalCode: business.zip || undefined,
+ addressCountry: 'US'
+ };
+ }
+ if (business.latitude && business.longitude) {
+ _ldBusiness.geo = { '@type': 'GeoCoordinates', latitude: business.latitude, longitude: business.longitude };
+ }
%>
+<script type="application/ld+json"><%- JSON.stringify(_ldBusiness) %></script>
<section class="profile-hero">
<p class="kicker"><%= verticalMeta ? verticalMeta.icon + ' ' + verticalMeta.label : business.vertical %></p>
diff --git a/views/public/find.ejs b/views/public/find.ejs
index d16cad3..a0a3295 100644
--- a/views/public/find.ejs
+++ b/views/public/find.ejs
@@ -1,6 +1,29 @@
<%- include('../partials/head', { title }) %>
<%- include('../partials/header') %>
+<%
+ // JSON-LD ItemList — surfaces the result page as a structured list to
+ // search engines. Each business appears as a ListItem with rank + URL +
+ // name, so searchers see "1. Olive & Salt Trattoria · 2. Boulevard Bagels …"
+ // in rich-results carousels.
+ var _publicUrl = (typeof publicUrl !== 'undefined' && publicUrl) ? publicUrl.replace(/\/+$/, '') : 'https://leads.venturaclaw.com';
+ var _ldList = {
+ '@context': 'https://schema.org',
+ '@type': 'ItemList',
+ itemListOrder: 'https://schema.org/ItemListOrderAscending',
+ numberOfItems: businesses.length,
+ itemListElement: businesses.slice(0, 30).map(function(b, i){
+ return {
+ '@type': 'ListItem',
+ position: i + 1,
+ url: _publicUrl + '/business/' + b.slug,
+ name: b.business_name
+ };
+ })
+ };
+%>
+<script type="application/ld+json"><%- JSON.stringify(_ldList) %></script>
+
<section class="find-page">
<p class="kicker">Find a business</p>
<h1 class="display-sm">Search Ventura Blvd</h1>
← cc3cfef v0.2 #3: admin/claim flow + lead inbox + profile editor + bi
·
back to Ventura Claw Leads
·
yolo tick 2: /unsubscribe handler — RFC 8058 one-click + lan 9c3866d →