[object Object]

← back to Prestige Car Wash

Discovery: honest 'find us & review' trust block (real off-site profiles)

d13f501084cf3de48b44b8cf3b9171bc8286d765 · 2026-07-26 20:50:10 -0700 · Steve Abrams

DTD A (4/4 valid). Server-renders REAL Google Maps + Yelp + Instagram links from places()
into the homepage; no star number reproduced on-page (routes to live profile — no drift).
Contrarian FIX-FIRST: (1) review-solicitation CTA gated on a verified Google place_id and
routes to Google's writereview dialog — NOT Yelp (Yelp filters solicited reviews); currently
place_id is empty so the CTA is inert until set. (2) whole section stripped server-side if no
real profiles (no floating heading). rel=noopener (follow, for sameAs authority).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Files touched

Diff

commit d13f501084cf3de48b44b8cf3b9171bc8286d765
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Sun Jul 26 20:50:10 2026 -0700

    Discovery: honest 'find us & review' trust block (real off-site profiles)
    
    DTD A (4/4 valid). Server-renders REAL Google Maps + Yelp + Instagram links from places()
    into the homepage; no star number reproduced on-page (routes to live profile — no drift).
    Contrarian FIX-FIRST: (1) review-solicitation CTA gated on a verified Google place_id and
    routes to Google's writereview dialog — NOT Yelp (Yelp filters solicited reviews); currently
    place_id is empty so the CTA is inert until set. (2) whole section stripped server-side if no
    real profiles (no floating heading). rel=noopener (follow, for sameAs authority).
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
 public/index.html |  7 +++++++
 server.js         | 38 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 45 insertions(+)

diff --git a/public/index.html b/public/index.html
index 69c2d0d..e997d86 100644
--- a/public/index.html
+++ b/public/index.html
@@ -86,6 +86,13 @@ fetch('/api/wait').then(r=>r.json()).then(w=>{const el=document.getElementById('
   <div class="grid" id="locPhotos" style="--cols:6;margin-top:16px"></div>
 </div></section>
 
+<section class="section" id="findreview"><div class="wrap">
+  <h2>Find us — and tell the Valley</h2>
+  <p class="sub">We're a real, family-run shop. Look us up, get directions, and if we did right by your car, a review means the world.</p>
+  <!-- Links rendered server-side into #findReview from the REAL places() profiles; no star
+       numbers are reproduced on-page (they'd drift) — the live profile is the source of truth. -->
+  <div id="findReview" style="max-width:820px"></div>
+</div></section>
 <style>
   #faq summary::-webkit-details-marker{display:none}
   #faq summary{list-style:none}
diff --git a/server.js b/server.js
index 4ffc727..f0311c6 100644
--- a/server.js
+++ b/server.js
@@ -491,6 +491,36 @@ function mobileBar(pagePath) {
 }</style>
 <div class="mbar" aria-label="Quick actions">${call}<a class="mbar-btn book" href="${bookHref}">Book a Wash →</a></div>`;
 }
+// Honest "find & review us" trust block — links to the REAL, verified off-site profiles
+// from places() (Google Maps, Yelp, Instagram). Deliberately shows NO star number: ratings
+// drift and would go stale on-page, so we route to the authoritative live profile instead
+// (data-honesty rule — never display a number we can't keep verifiably current). Only a
+// profile with a real non-empty value is rendered (no dead links). Server-rendered so the
+// off-site authority links ship in the crawler HTML.
+function trustBlockHtml() {
+  const p = places() || {};
+  const esc = s => String(s == null ? '' : s).replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
+  const btn = (href, label, cls) => href
+    ? `<a class="${cls}" href="${esc(href)}" target="_blank" rel="noopener" style="text-decoration:none">${esc(label)}</a>` : '';
+  const igHandle = p.instagram ? '@' + String(p.instagram).replace(/^https?:\/\/(www\.)?instagram\.com\//i, '').replace(/\/+$/, '').replace(/^@/, '') : '';
+  const igUrl = p.instagram ? (/^https?:\/\//.test(p.instagram) ? p.instagram : 'https://instagram.com/' + String(p.instagram).replace(/^@/, '')) : '';
+  const links = [
+    btn(p.maps_query, '📍 Find us on Google Maps', 'btn'),
+    btn(p.yelp_url, '⭐ Read reviews on Yelp', 'btn ghost'),
+    btn(igUrl, '📸 ' + igHandle, 'btn ghost')
+  ].filter(Boolean);
+  if (!links.length) return '';
+  // Review-SOLICITATION CTA routes to Google's write-review dialog — and ONLY when a real
+  // Google place_id is present (currently empty). We deliberately do NOT solicit on Yelp
+  // (Yelp's "Don't Ask for Reviews" policy filters solicited reviews into "not recommended",
+  // burying exactly what we'd be trying to grow), and we never point "leave a review" at a
+  // bare search URL or a guessed id. So the CTA stays gated on a verified place_id and simply
+  // appears once it's set — the "Read reviews on Yelp" link above stays (finding ≠ soliciting).
+  const pid = /^[A-Za-z0-9_-]{20,}$/.test(String(p.place_id || '')) ? p.place_id : '';
+  const reviewHref = pid ? `https://search.google.com/local/writereview?placeid=${encodeURIComponent(pid)}` : '';
+  const cta = reviewHref ? `<div style="margin-top:16px">${btn(reviewHref, 'Leave us a review on Google →', 'btn')}</div>` : '';
+  return `<div style="display:flex;flex-wrap:wrap;gap:12px;align-items:center">${links.join('')}</div>${cta}`;
+}
 function buildFaqLd() {
   const list = faqs();
   if (!list.length) return null;
@@ -563,6 +593,14 @@ function pageHtml(file, pagePath, ld) {
   if (html.includes('id="faqList"')) {
     html = html.replace(/(<div id="faqList"[^>]*>)\s*(<\/div>)/, (m, open, close) => open + faqHtml() + close);
   }
+  // Fill the honest find-&-review trust block server-side (real off-site profile links).
+  // If there are no real profiles to show, strip the WHOLE section so a heading never floats
+  // over an empty body (graceful degradation, per contrarian).
+  if (html.includes('id="findReview"')) {
+    const tb = trustBlockHtml();
+    if (tb) html = html.replace(/(<div id="findReview"[^>]*>)\s*(<\/div>)/, (m, open, close) => open + tb + close);
+    else html = html.replace(/<section class="section" id="findreview">[\s\S]*?<\/section>/, '');
+  }
   // Persistent mobile Book/Call bar on every customer page (guard against double-inject).
   if (!/class="mbar"/.test(html)) html = html.replace('</body>', mobileBar(pagePath) + '\n</body>');
   let inject = buildSocialTags(html, pagePath);

← 9d17487 Services: per-service 'Book this' deep-links pre-select the  ·  back to Prestige Car Wash  ·  Guides: evergreen car-care articles (/guides + 3 posts) — BU 2cec353 →