← back to Robet Site
auto-save: 2026-06-30T00:26:45 (2 files) — public/app.js public/styles.css
6e8cd060304ff14c7c44272288ff25d55f8984b1 · 2026-06-30 00:26:49 -0700 · Steve Abrams
Files touched
M public/app.jsM public/styles.css
Diff
commit 6e8cd060304ff14c7c44272288ff25d55f8984b1
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Tue Jun 30 00:26:49 2026 -0700
auto-save: 2026-06-30T00:26:45 (2 files) — public/app.js public/styles.css
---
public/app.js | 42 ++++++++++++++++++++++++++++--------------
public/styles.css | 10 ++++++++++
2 files changed, 38 insertions(+), 14 deletions(-)
diff --git a/public/app.js b/public/app.js
index 05855b4..2cf2ede 100644
--- a/public/app.js
+++ b/public/app.js
@@ -51,21 +51,35 @@
}
});
- // ── Smart Scheduling embed ──
+ // ── Smart Scheduling embed: skeleton → real widget OR fallback (never a perpetual spinner) ──
const sched = cfg.scheduling || {};
- const bookLink = $('#bookLink');
- if (bookLink && sched.bookUrl) bookLink.href = sched.bookUrl;
- if (sched.embedLoader) {
- // The venturacorridor embed loader injects the booking widget where invoked.
- const s = document.createElement('script');
- s.src = sched.embedLoader;
- if (sched.owner) s.setAttribute('data-owner', sched.owner);
- s.async = true;
- s.onerror = () => {
- $('#bookFallback') && ($('#bookFallback').innerHTML =
- `Booking widget unavailable right now — <a class="cta" href="${sched.bookUrl || '#'}" target="_blank" rel="noopener noreferrer">Open booking page →</a>`);
- };
- $('#embedBox').appendChild(s);
+ const embedBox = $('#embedBox');
+ const fallbackHTML = () =>
+ `<div class="fallback mono">Booking opens in a new tab — <a class="cta" href="${sched.bookUrl || '#'}" target="_blank" rel="noopener noreferrer">Open booking page →</a></div>`;
+ if (embedBox) {
+ if (!sched.embedLoader) {
+ embedBox.innerHTML = fallbackHTML();
+ } else {
+ // brutalist loading skeleton, shown immediately
+ embedBox.innerHTML = '<div class="embed-skeleton" aria-hidden="true"><span></span><span></span><span></span><span></span></div>';
+ let settled = false;
+ const settle = (mode) => {
+ if (settled) return; settled = true;
+ clearTimeout(timer); obs.disconnect();
+ const sk = embedBox.querySelector('.embed-skeleton'); if (sk) sk.remove();
+ if (mode === 'fallback' && !embedBox.querySelector('iframe')) embedBox.innerHTML = fallbackHTML();
+ };
+ // a rendered booking widget = an injected <iframe>; that's our "it worked" signal
+ const obs = new MutationObserver(() => { if (embedBox.querySelector('iframe')) settle('embed'); });
+ obs.observe(embedBox, { childList: true, subtree: true });
+ const timer = setTimeout(() => settle('fallback'), 7000); // no widget in 7s → fallback link
+ const s = document.createElement('script');
+ s.src = sched.embedLoader;
+ if (sched.owner) s.setAttribute('data-owner', sched.owner);
+ s.async = true;
+ s.onerror = () => settle('fallback');
+ embedBox.appendChild(s);
+ }
}
// ── contact form ──
diff --git a/public/styles.css b/public/styles.css
index 7bbeee3..b28d5c7 100644
--- a/public/styles.css
+++ b/public/styles.css
@@ -123,6 +123,16 @@ section { padding: 70px 24px; border-bottom: var(--bord); }
/* booking embed */
#booking .embed-box { border: var(--bord); background: #fff; padding: 0; min-height: 420px; }
#booking .fallback { padding: 22px; }
+.embed-skeleton { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; padding: 22px; }
+.embed-skeleton span {
+ height: 72px; border: 4px solid var(--ink);
+ background: repeating-linear-gradient(45deg, #e9e6de, #e9e6de 10px, #dcd9d1 10px, #dcd9d1 20px);
+ animation: embedpulse 1.1s ease-in-out infinite;
+}
+.embed-skeleton span:nth-child(2) { animation-delay: .15s; }
+.embed-skeleton span:nth-child(3) { animation-delay: .3s; }
+.embed-skeleton span:nth-child(4) { animation-delay: .45s; }
+@keyframes embedpulse { 0%, 100% { opacity: 1; } 50% { opacity: .4; } }
/* contact */
.contact-wrap { display: grid; grid-template-columns: 1fr 1fr; gap: 0; }
← 967b466 loop ledger: round2 item (4) done + deployed
·
back to Robet Site
·
loop ledger: round2 item (6) done + deployed 32a72a3 →