← back to Prestige Car Wash
Booking: honest 'typical busy-ness today' wait indicator (GET /api/wait, LA-tz, qualitative not fabricated minutes) — dtd:A, contrarian-gated
3ccfc3cb0632b716e89483c0b3f0d47c2fe5e96c · 2026-07-26 08:36:43 -0700 · steve@designerwallcoverings.com
Files touched
M public/contact.htmlM public/index.htmlM server.js
Diff
commit 3ccfc3cb0632b716e89483c0b3f0d47c2fe5e96c
Author: steve@designerwallcoverings.com <steve@designerwallcoverings.com>
Date: Sun Jul 26 08:36:43 2026 -0700
Booking: honest 'typical busy-ness today' wait indicator (GET /api/wait, LA-tz, qualitative not fabricated minutes) — dtd:A, contrarian-gated
---
public/contact.html | 7 +++++++
public/index.html | 7 +++++++
server.js | 22 ++++++++++++++++++++++
3 files changed, 36 insertions(+)
diff --git a/public/contact.html b/public/contact.html
index 777abc1..b55d99c 100644
--- a/public/contact.html
+++ b/public/contact.html
@@ -31,6 +31,7 @@
<section class="section"><div class="wrap">
<h2>Book your wash</h2>
<p class="sub">Tell us what you need — we'll follow up to confirm a time, usually within a few hours. No upsell pressure, ever.</p>
+ <div id="waitchip" style="display:none;margin:0 0 18px;padding:8px 15px;border:1px solid var(--line);border-radius:999px;font-size:14px;width:fit-content" title=""></div>
<div class="ok" id="ok"></div>
<form id="f">
<div class="two">
@@ -53,6 +54,12 @@
<footer class="foot"><div class="wrap">© 2026 Prestige Car Wash · San Fernando Valley, CA · Instagram @prestigecwash</div></footer>
<script>
+// Honest "typical wait" chip — estimate from the demand model, never a live queue count.
+fetch('/api/wait').then(r=>r.json()).then(w=>{
+ const el=document.getElementById('waitchip');
+ el.innerHTML=`⏱️ ${w.day} is typically <b>${w.level}</b> — ${w.phrase} <span style="opacity:.65">· book ahead to skip it</span>`;
+ el.title=w.basis||'';el.style.display='';
+}).catch(()=>{});
fetch('/api/services?sort=featured').then(r=>r.json()).then(list=>{
document.getElementById('svcSel').innerHTML += list.map(s=>`<option value="${s.name}">${s.name} — ${s.price_display||('$'+s.price)}</option>`).join('');
});
diff --git a/public/index.html b/public/index.html
index 2ac8604..d71c226 100644
--- a/public/index.html
+++ b/public/index.html
@@ -29,7 +29,14 @@
<a class="btn" href="/contact">Book a Wash</a>
<a class="btn ghost" href="/services">See Services & Pricing</a>
</div>
+ <div id="waitchip" style="display:none;margin-top:16px;padding:7px 14px;border:1px solid rgba(255,255,255,.35);border-radius:999px;font-size:13.5px;width:fit-content" title=""></div>
</div></header>
+<script>
+// Honest "typical wait" chip on the hero — estimate from the demand model, not a live count.
+fetch('/api/wait').then(r=>r.json()).then(w=>{const el=document.getElementById('waitchip');
+ el.innerHTML=`⏱️ ${w.day} is typically <b>${w.level.toLowerCase()}</b> — ${w.phrase}. <a href="/contact" style="color:inherit;text-decoration:underline">book ahead</a>`;
+ el.title=w.basis||'';el.style.display='';}).catch(()=>{});
+</script>
<section class="section"><div class="wrap">
<h2>Why drivers switch to Prestige</h2>
diff --git a/server.js b/server.js
index 8c5983a..3d255f1 100644
--- a/server.js
+++ b/server.js
@@ -312,6 +312,28 @@ app.get('/api/admin/leads', (req, res) => {
} catch { res.json([]); }
});
+// Public: HONEST "typical wait" estimate derived from the demand model.
+// It is NOT a live queue count — it's a transparent function of best-times.json
+// demand for the current weekday, always labeled as an estimate, so we never
+// display a number we can't verify (the places.json data-honesty rule). dtd:A 2026-07-26.
+app.get('/api/wait', (req, res) => {
+ const days = ((bestTimes() || {}).traffic || {}).by_day || [];
+ // Weekday in the shop's LOCAL tz (LA) — the server clock is UTC, so a Friday-evening
+ // CA visitor must not be shown Saturday's level. (contrarian gate, 2026-07-26)
+ const dayName = new Date().toLocaleDateString('en-US', { timeZone: 'America/Los_Angeles', weekday: 'short' });
+ const today = days.find(d => d.day === dayName);
+ const demand = today ? Math.max(1, Math.min(5, Number(today.demand) || 3)) : 3;
+ // Report the demand LEVEL the data actually measures — a qualitative busy-ness, NOT a
+ // fabricated minute count we can't verify (Steve's data-honesty rule).
+ const LEVELS = { 1: ['Quiet', 'little to no wait'], 2: ['Quiet', 'little to no wait'], 3: ['Moderate', 'a short wait is typical'], 4: ['Busy', 'expect a wait'], 5: ['Very busy', 'expect a longer wait'] };
+ const [level, phrase] = LEVELS[demand];
+ res.json({
+ level, phrase, demand, day: dayName,
+ estimate: true,
+ basis: 'Based on typical demand for this day — not a live queue count'
+ });
+});
+
// ---- static + clean URLs ---------------------------------------------------
// Google Place photo proxy — fetches photo media with the server-side key and streams
// it, so the key never reaches the browser. Cached to avoid re-billing on every view.
← cbf5a8b Growth ideas: backfill conservative SWOT + light costs for m
·
back to Prestige Car Wash
·
Membership: 'launching soon' call-out + interest capture via 2f4ad36 →