[object Object]

← back to Prestige Car Wash

Mobile: persistent Book + click-to-call action bar on customer pages

076013950e162cecd798fbc298bfd9f113d93359 · 2026-07-26 20:34:40 -0700 · Steve Abrams

DTD unanimous (5/5) A. Server-injected via pageHtml, CSS-only <=640px, no JS.
tel: from real places().phone (single source, no fabrication). Contrarian FIX-FIRST:
Book button is page-aware — scrolls to #f on /contact instead of self-linking to top.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Files touched

Diff

commit 076013950e162cecd798fbc298bfd9f113d93359
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Sun Jul 26 20:34:40 2026 -0700

    Mobile: persistent Book + click-to-call action bar on customer pages
    
    DTD unanimous (5/5) A. Server-injected via pageHtml, CSS-only <=640px, no JS.
    tel: from real places().phone (single source, no fabrication). Contrarian FIX-FIRST:
    Book button is page-aware — scrolls to #f on /contact instead of self-linking to top.
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
 server.js | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/server.js b/server.js
index 578c94c..4ffc727 100644
--- a/server.js
+++ b/server.js
@@ -467,6 +467,30 @@ function faqs() {
       a: 'Send a request from the Book / Contact page with your vehicle and preferred time. We follow up to confirm — usually within a few hours during business hours. No pressure, no upsell.' }
   ];
 }
+// Persistent mobile action bar — injected into customer pages so the primary CTA (Book)
+// and a click-to-call are always one thumb-tap away on phones (where most Valley car-wash
+// traffic is). CSS-only visibility (<=640px), no JS. The tel: number comes from the REAL
+// places().phone (single source, no fabrication); the Call button is omitted if no phone.
+function mobileBar(pagePath) {
+  const p = places() || {};
+  let d = String(p.phone || '').replace(/\D/g, '');
+  if (d.length === 11 && d[0] === '1') d = d.slice(1);
+  const tel = d.length === 10 ? '+1' + d : (d ? '+' + d : '');
+  const call = tel ? `<a class="mbar-btn call" href="tel:${tel}" aria-label="Call ${(p.name || 'Prestige Car Wash').replace(/"/g, '')}">📞 Call</a>` : '';
+  // On the contact page the form IS the booking action, so scroll to it (#f) rather than
+  // self-linking to the top of the page and losing any half-filled form state.
+  const bookHref = pagePath === '/contact' ? '#f' : '/contact';
+  return `<style>
+.mbar{display:none}
+@media(max-width:640px){
+  body{padding-bottom:74px}
+  .mbar{display:flex;position:fixed;left:0;right:0;bottom:0;z-index:80;gap:10px;padding:10px 12px calc(10px + env(safe-area-inset-bottom,0px));background:rgba(11,15,22,.93);backdrop-filter:blur(10px);border-top:1px solid var(--line)}
+  .mbar-btn{flex:1;text-align:center;padding:13px 10px;border-radius:12px;font-weight:800;font-size:15px;text-decoration:none;display:flex;align-items:center;justify-content:center;gap:6px}
+  .mbar .call{background:transparent;color:var(--ink);border:1px solid var(--line)}
+  .mbar .book{background:linear-gradient(135deg,var(--brand),var(--brand2));color:#001018}
+}</style>
+<div class="mbar" aria-label="Quick actions">${call}<a class="mbar-btn book" href="${bookHref}">Book a Wash →</a></div>`;
+}
 function buildFaqLd() {
   const list = faqs();
   if (!list.length) return null;
@@ -539,6 +563,8 @@ function pageHtml(file, pagePath, ld) {
   if (html.includes('id="faqList"')) {
     html = html.replace(/(<div id="faqList"[^>]*>)\s*(<\/div>)/, (m, open, close) => open + faqHtml() + close);
   }
+  // 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);
   // ld may be a single JSON-LD object OR an array of them (e.g. AutoWash + FAQPage on the
   // homepage). Emit one <script> per object; escape "<" so the JSON can't break out of the tag.

← cc20300 FAQ: booking-objection accordion + FAQPage JSON-LD, single-s  ·  back to Prestige Car Wash  ·  Services: per-service 'Book this' deep-links pre-select the 9d17487 →