Add low-friction 3-step lead form to contact section (posts to n8n)

Soft conversion path alongside booking; SW steel/Georgia styling. Posts JSON to
https://n8n.shopwisdom.ca/webhook/sw-lead, honeypot-protected, email fallback.
Staging branch only.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Michael Victory
2026-06-17 21:26:51 -07:00
parent a1482e6152
commit c9776d053b
2 changed files with 109 additions and 0 deletions
+84
View File
@@ -271,6 +271,37 @@
<h2>Start with a conversation</h2> <h2>Start with a conversation</h2>
<p>No pitch. No proposal until we've had a real conversation. Describe the friction — even roughly. The more context you share, the more useful our first conversation will be.</p> <p>No pitch. No proposal until we've had a real conversation. Describe the friction — even roughly. The more context you share, the more useful our first conversation will be.</p>
<p>I read every message personally and usually reply within one business day. If your problem isn't something I can help with, I'll tell you honestly — and point you somewhere that can.</p> <p>I read every message personally and usually reply within one business day. If your problem isn't something I can help with, I'll tell you honestly — and point you somewhere that can.</p>
<form class="lf" data-endpoint="https://n8n.shopwisdom.ca/webhook/sw-lead" data-source="shopwisdom" novalidate>
<div class="lf-progress" aria-hidden="true"><span class="lf-dot is-active"></span><span class="lf-dot"></span><span class="lf-dot"></span></div>
<fieldset class="lf-step is-active" data-step="1">
<legend class="lf-q">What's the situation in your shop?</legend>
<div class="lf-choices">
<label class="lf-choice"><input type="radio" name="topic" value="A key person is retiring soon" required> A key person is retiring soon</label>
<label class="lf-choice"><input type="radio" name="topic" value="Know-how lives in a few heads"> Know-how lives in just a few heads</label>
<label class="lf-choice"><input type="radio" name="topic" value="A process or tool needs building"> A process or tool needs building</label>
<label class="lf-choice"><input type="radio" name="topic" value="Just exploring"> Just exploring for now</label>
</div>
<div class="lf-nav"><button type="button" class="btn lf-next" data-next>Continue →</button></div>
</fieldset>
<fieldset class="lf-step" data-step="2">
<legend class="lf-q">Anything you want me to know? <span class="lf-opt">(optional)</span></legend>
<textarea class="lf-input" name="detail" rows="3" placeholder="Even a rough sentence helps."></textarea>
<div class="lf-nav"><button type="button" class="btn ghost lf-back" data-back>← Back</button><button type="button" class="btn lf-next" data-next>Continue →</button></div>
</fieldset>
<fieldset class="lf-step" data-step="3">
<legend class="lf-q">Where do I reach you?</legend>
<input class="lf-input" type="text" name="name" placeholder="Your name" required autocomplete="name">
<input class="lf-input" type="email" name="email" placeholder="Email" required autocomplete="email">
<input class="lf-input" type="tel" name="phone" placeholder="Phone (optional)" autocomplete="tel">
<input type="text" name="company_website" class="lf-hp" tabindex="-1" autocomplete="off" aria-hidden="true">
<div class="lf-nav"><button type="button" class="btn ghost lf-back" data-back>← Back</button><button type="submit" class="btn lf-submit">Send it →</button></div>
<p class="lf-err" hidden>Hmm — that didn't go through. Please <a href="mailto:michael@opencommunications.consulting">email me directly</a> and I'll get right back to you.</p>
</fieldset>
<div class="lf-done" hidden><p class="lf-done-msg">Got it — thanks. I read every message personally and usually reply within a business day.</p></div>
</form>
<p class="lf-alt">Prefer to reach out directly?</p>
<p class="contact-action"> <p class="contact-action">
<a class="btn" href="mailto:michael@opencommunications.consulting?subject=Fit%20call%20—%20Shop%20Wisdom">Email Michael</a> <a class="btn" href="mailto:michael@opencommunications.consulting?subject=Fit%20call%20—%20Shop%20Wisdom">Email Michael</a>
<a class="btn ghost tel" data-p1="778" data-p2="985" data-p3="6736" href="mailto:michael@opencommunications.consulting">Call 778&#8209;985&#8209;OPEN (6736)</a> <a class="btn ghost tel" data-p1="778" data-p2="985" data-p3="6736" href="mailto:michael@opencommunications.consulting">Call 778&#8209;985&#8209;OPEN (6736)</a>
@@ -320,6 +351,59 @@
})(); })();
</script> </script>
<!-- Lead form: 3-step navigation + submit to the n8n webhook (honeypot-protected) -->
<script>
(function () {
document.querySelectorAll('form.lf').forEach(function (form) {
var steps = [].slice.call(form.querySelectorAll('.lf-step'));
var dots = [].slice.call(form.querySelectorAll('.lf-dot'));
var done = form.querySelector('.lf-done');
var err = form.querySelector('.lf-err');
var progress = form.querySelector('.lf-progress');
var cur = 0;
function show(i) {
steps.forEach(function (s, k) { s.classList.toggle('is-active', k === i); });
dots.forEach(function (d, k) { d.classList.toggle('is-active', k <= i); });
cur = i;
var fld = steps[i].querySelector('input:not([type=radio]),textarea'); if (fld) { try { fld.focus(); } catch (e) {} }
}
function valid(i) {
var reqs = steps[i].querySelectorAll('[required]');
for (var j = 0; j < reqs.length; j++) { if (!reqs[j].checkValidity()) { reqs[j].reportValidity(); return false; } }
return true;
}
form.addEventListener('click', function (e) {
if (e.target.closest('[data-next]')) { if (valid(cur)) show(Math.min(cur + 1, steps.length - 1)); }
else if (e.target.closest('[data-back]')) { show(Math.max(cur - 1, 0)); }
});
form.addEventListener('submit', function (e) {
e.preventDefault();
if (!valid(cur)) return;
var hp = form.querySelector('.lf-hp');
if (hp && hp.value) { return; }
if (err) err.hidden = true;
var data = {};
new FormData(form).forEach(function (v, k) { if (k !== 'company_website') data[k] = v; });
data.source = form.getAttribute('data-source') || location.hostname;
data.submittedAt = new Date().toISOString();
var btn = form.querySelector('.lf-submit'); var label = btn.textContent;
btn.disabled = true; btn.textContent = 'Sending…';
fetch(form.getAttribute('data-endpoint'), {
method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data)
}).then(function (r) {
if (!r.ok) throw new Error('status ' + r.status);
steps.forEach(function (s) { s.style.display = 'none'; });
if (progress) progress.style.display = 'none';
if (done) done.hidden = false;
}).catch(function () {
btn.disabled = false; btn.textContent = label;
if (err) err.hidden = false;
});
});
});
})();
</script>
<!-- Cal.com booking embed (opens the booking popup on-brand; href is the no-JS fallback) --> <!-- Cal.com booking embed (opens the booking popup on-brand; href is the no-JS fallback) -->
<script> <script>
(function (C, A, L) { let p = function (a, ar) { a.q.push(ar); }; let d = C.document; C.Cal = C.Cal || function () { let cal = C.Cal; let ar = arguments; if (!cal.loaded) { cal.ns = {}; cal.q = cal.q || []; d.head.appendChild(d.createElement("script")).src = A; cal.loaded = true; } if (ar[0] === L) { const api = function () { p(api, arguments); }; const namespace = ar[1]; api.q = api.q || []; if (typeof namespace === "string") { cal.ns[namespace] = cal.ns[namespace] || api; p(cal.ns[namespace], ar); p(cal, ["initNamespace", namespace]); } else p(cal, ar); return; } p(cal, ar); }; })(window, "https://cal.shopwisdom.ca/embed/embed.js", "init"); (function (C, A, L) { let p = function (a, ar) { a.q.push(ar); }; let d = C.document; C.Cal = C.Cal || function () { let cal = C.Cal; let ar = arguments; if (!cal.loaded) { cal.ns = {}; cal.q = cal.q || []; d.head.appendChild(d.createElement("script")).src = A; cal.loaded = true; } if (ar[0] === L) { const api = function () { p(api, arguments); }; const namespace = ar[1]; api.q = api.q || []; if (typeof namespace === "string") { cal.ns[namespace] = cal.ns[namespace] || api; p(cal.ns[namespace], ar); p(cal, ["initNamespace", namespace]); } else p(cal, ar); return; } p(cal, ar); }; })(window, "https://cal.shopwisdom.ca/embed/embed.js", "init");
+25
View File
@@ -241,3 +241,28 @@ blockquote p { margin: 0; font-style: italic; color: var(--ink-soft); }
.hero { padding: 56px 0 44px; } .hero { padding: 56px 0 44px; }
section { padding: 48px 0; } section { padding: 48px 0; }
} }
/* ── Lead form: low-friction 3-step ─────────────────────────────────────── */
.lf { max-width: 30rem; margin: 2rem auto 1.4rem; text-align: left; }
.lf-progress { display: flex; gap: 8px; justify-content: center; margin-bottom: 1.6rem; }
.lf-dot { width: 30px; height: 4px; border-radius: 2px; background: var(--line); transition: background-color 0.3s ease; }
.lf-dot.is-active { background: var(--steel); }
.lf-step { display: none; border: 0; padding: 0; margin: 0; min-inline-size: 0; }
.lf-step.is-active { display: block; animation: lfIn 0.25s ease; }
@keyframes lfIn { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
.lf-q { font-family: Georgia, serif; font-weight: 700; font-size: 1.2rem; line-height: 1.3; color: var(--ink); margin: 0 0 1rem; padding: 0; }
.lf-opt { font-family: Verdana, Geneva, sans-serif; font-size: 0.78rem; color: var(--ink-soft); font-weight: normal; }
.lf-choices { display: flex; flex-direction: column; gap: 10px; }
.lf-choice { display: flex; align-items: center; gap: 12px; padding: 13px 16px; border: 1px solid var(--line); border-radius: 6px; font-family: Georgia, serif; font-size: 0.98rem; color: var(--ink); background: var(--panel); cursor: pointer; transition: border-color 0.2s ease, background-color 0.2s ease; }
.lf-choice:hover { border-color: var(--steel); }
.lf-choice input { accent-color: var(--steel); width: 16px; height: 16px; flex: none; }
.lf-choice:has(input:checked) { border-color: var(--steel); background: rgba(44, 93, 124, 0.06); }
.lf-input { width: 100%; box-sizing: border-box; padding: 12px 14px; border: 1px solid var(--line); border-radius: 6px; font-family: Georgia, serif; font-size: 1rem; color: var(--ink); background: var(--panel); margin-bottom: 12px; }
.lf-input:focus { outline: none; border-color: var(--steel); box-shadow: 0 0 0 3px rgba(44, 93, 124, 0.12); }
.lf-hp { position: absolute !important; left: -9999px; width: 1px; height: 1px; opacity: 0; }
.lf-nav { display: flex; justify-content: flex-end; align-items: center; gap: 14px; margin-top: 1.2rem; }
.lf-nav .lf-back { margin-right: auto; }
.lf-done-msg { font-family: Georgia, serif; font-size: 1.1rem; line-height: 1.6; color: var(--ink); text-align: center; padding: 1.4rem 0; margin: 0; }
.lf-err { font-family: Verdana, Geneva, sans-serif; font-size: 0.85rem; color: #b4452f; margin: 1rem 0 0; }
.lf-err a { color: #b4452f; }
.lf-alt { font-family: Verdana, Geneva, sans-serif; font-size: 0.8rem; letter-spacing: 0.04em; text-transform: uppercase; color: var(--ink-soft); text-align: center; margin: 0.4rem 0 0.7rem; }