← back to Wallpapercontractor
views/lead.ejs
297 lines
<%- include('partials/head', { title: 'Find Your Wallpaper Installer — ' + site.name, description: 'Tell us about your project. We match you with vetted, bonded, insured wallpaper installers in your area in 24 hours.' }) %>
<link rel="stylesheet" href="/css/funnel.css">
<main class="funnel">
<div class="funnel-shell" data-step="1">
<div class="funnel-progress" aria-hidden="true">
<span class="dot active" data-dot="1"></span>
<span class="dot" data-dot="2"></span>
<span class="dot" data-dot="3"></span>
</div>
<form id="leadForm" novalidate>
<!-- ───── STEP 1 — Project type ──────────────────────────────── -->
<section class="step step-1" data-step="1">
<div class="step-eyebrow">Step 1 of 3 · Project type</div>
<h1 class="step-h">What kind of project<br>are we sizing?</h1>
<p class="step-sub">Pick the closest match. We use this to route to the right specialty crew.</p>
<div class="choice-grid" role="radiogroup" aria-label="Project type">
<% const TYPES = [
{ id:'residential', title:'Single home', sub:'Owner-occupied house, condo, or apartment.', icon:'home' },
{ id:'multifamily', title:'Multifamily', sub:'Apartments, lofts, condo lobbies, common areas.', icon:'building' },
{ id:'hospitality', title:'Hospitality', sub:'Hotels, restaurants, bars, spas, resorts.', icon:'wine' },
{ id:'retail', title:'Retail / showroom', sub:'Stores, boutiques, salons, dealerships.', icon:'shop' },
{ id:'corporate', title:'Corporate', sub:'Offices, lobbies, conference rooms.', icon:'briefcase' },
{ id:'healthcare', title:'Healthcare', sub:'Hospitals, clinics, dental, senior living.', icon:'cross' },
] %>
<% TYPES.forEach(function(t){ %>
<label class="choice" data-icon="<%= t.icon %>">
<input type="radio" name="project_type" value="<%= t.id %>" required>
<div class="choice-body">
<div class="choice-title"><%= t.title %></div>
<div class="choice-sub"><%= t.sub %></div>
</div>
<div class="choice-check" aria-hidden="true">✓</div>
</label>
<% }) %>
</div>
<div class="step-actions">
<button type="button" class="btn-primary" data-next="2" disabled>Continue →</button>
</div>
</section>
<!-- ───── STEP 2 — Scope + location ──────────────────────────── -->
<section class="step step-2" data-step="2" hidden>
<div class="step-eyebrow">Step 2 of 3 · Scope & location</div>
<h1 class="step-h">Where, when,<br>and how big?</h1>
<p class="step-sub">Rough numbers are fine. Installers will refine on the walkthrough.</p>
<div class="form-grid">
<label class="field">
<span class="field-label">ZIP code</span>
<input type="text" inputmode="numeric" pattern="[0-9]{5}" maxlength="5" name="zip" required placeholder="90210">
</label>
<label class="field">
<span class="field-label">Approx. wall area</span>
<select name="square_footage" required>
<option value="">Select…</option>
<option value="<200">Under 200 sq ft (one accent wall)</option>
<option value="200-500">200–500 sq ft (one room)</option>
<option value="500-1500">500–1,500 sq ft (multiple rooms)</option>
<option value="1500-5000">1,500–5,000 sq ft (large home / suite)</option>
<option value="5000+">5,000+ sq ft (commercial)</option>
</select>
</label>
<label class="field">
<span class="field-label">Timeline</span>
<select name="timeline" required>
<option value="">Select…</option>
<option value="asap">As soon as possible</option>
<option value="2-4w">2–4 weeks</option>
<option value="1-3m">1–3 months</option>
<option value="3-6m">3–6 months</option>
<option value="planning">Just planning</option>
</select>
</label>
<label class="field">
<span class="field-label">Wallpaper status</span>
<select name="material_status" required>
<option value="">Select…</option>
<option value="have">I already have the wallpaper</option>
<option value="picked">I've picked it, not ordered</option>
<option value="need_help">I still need to choose</option>
<option value="commercial_grade">I need commercial-grade</option>
</select>
</label>
<label class="field field-full">
<span class="field-label">Project notes <span class="field-opt">(optional)</span></span>
<textarea name="notes" rows="3" placeholder="Brief description, special requirements, mural/grasscloth/silk, prep needed, etc."></textarea>
</label>
</div>
<div class="step-actions">
<button type="button" class="btn-ghost" data-prev="1">← Back</button>
<button type="button" class="btn-primary" data-next="3">Continue →</button>
</div>
</section>
<!-- ───── STEP 3 — Contact ───────────────────────────────────── -->
<section class="step step-3" data-step="3" hidden>
<div class="step-eyebrow">Step 3 of 3 · Where to send your matches</div>
<h1 class="step-h">Last bit. Then<br>installers come to you.</h1>
<p class="step-sub">We send you 2–4 vetted matches in <strong>nationalpaperhangers.com</strong>'s installer marketplace within 24 hours. No spam, no shared lists.</p>
<div class="form-grid">
<label class="field">
<span class="field-label">Your name</span>
<input type="text" name="name" required autocomplete="name" placeholder="Jane Doe">
</label>
<label class="field">
<span class="field-label">Email</span>
<input type="email" name="email" required autocomplete="email" placeholder="jane@example.com">
</label>
<label class="field">
<span class="field-label">Phone <span class="field-opt">(optional, faster matching)</span></span>
<input type="tel" name="phone" autocomplete="tel" placeholder="(555) 123-4567">
</label>
<label class="field">
<span class="field-label">I am a…</span>
<select name="role" required>
<option value="">Select…</option>
<option value="homeowner">Homeowner</option>
<option value="designer">Interior designer</option>
<option value="architect">Architect</option>
<option value="gc">General contractor</option>
<option value="property_manager">Property manager</option>
<option value="business_owner">Business owner</option>
<option value="other">Other</option>
</select>
</label>
</div>
<label class="consent">
<input type="checkbox" name="consent" required>
<span>I agree to be contacted by matched installers about this project. <a href="/privacy" target="_blank" rel="noopener noreferrer">Privacy</a> · I can opt out anytime.</span>
</label>
<div class="step-actions">
<button type="button" class="btn-ghost" data-prev="2">← Back</button>
<button type="submit" class="btn-primary btn-submit">
<span class="btn-label">Get my installer matches →</span>
<span class="btn-spinner" aria-hidden="true"></span>
</button>
</div>
<div id="leadError" class="lead-error" role="alert" hidden></div>
</section>
<!-- ───── SUCCESS ────────────────────────────────────────────── -->
<section class="step step-done" data-step="done" hidden>
<div class="done-mark" aria-hidden="true">✓</div>
<h1 class="step-h">You're matched.</h1>
<p class="step-sub">We're routing you to <strong>National Paper Hangers</strong> — our installer marketplace — where you can browse the vetted installers we've selected for your project.</p>
<p class="step-meta">Redirecting in <span id="redirCount">3</span>s…</p>
<p><a id="redirNow" href="https://nationalpaperhangers.com" class="btn-primary">Take me there now →</a></p>
</section>
</form>
<!-- Trust strip — visible all steps -->
<aside class="trust-strip">
<div class="trust-item">
<div class="trust-num">524</div>
<div class="trust-label">Vetted installers</div>
</div>
<div class="trust-item">
<div class="trust-num">100%</div>
<div class="trust-label">Bonded & insured</div>
</div>
<div class="trust-item">
<div class="trust-num">24h</div>
<div class="trust-label">Match turnaround</div>
</div>
<div class="trust-item">
<div class="trust-num">$0</div>
<div class="trust-label">To get matched</div>
</div>
</aside>
</div>
</main>
<script>
(function(){
var shell = document.querySelector('.funnel-shell');
var form = document.getElementById('leadForm');
var steps = form.querySelectorAll('.step');
var dots = document.querySelectorAll('.funnel-progress .dot');
function go(stepNum){
steps.forEach(function(s){
s.hidden = (s.dataset.step !== String(stepNum));
});
dots.forEach(function(d){
var n = Number(d.dataset.dot);
d.classList.toggle('active', n <= Number(stepNum));
});
shell.dataset.step = String(stepNum);
window.scrollTo({ top: 0, behavior: 'smooth' });
if (window.gtag) gtag('event','funnel_step',{step: stepNum});
}
// Step-1 enable continue when a project type is chosen
form.querySelectorAll('input[name="project_type"]').forEach(function(r){
r.addEventListener('change', function(){
var btn = form.querySelector('button[data-next="2"]');
btn.disabled = false;
});
});
// next/prev wiring
form.addEventListener('click', function(ev){
var t = ev.target.closest('button');
if (!t) return;
if (t.dataset.next){
// validate the current step's required fields before advancing
var current = form.querySelector('.step:not([hidden])');
var bad = false;
current.querySelectorAll('input[required], select[required], textarea[required]').forEach(function(el){
if (!el.checkValidity()){
el.reportValidity();
bad = true;
}
});
if (bad) return;
go(t.dataset.next);
} else if (t.dataset.prev){
go(t.dataset.prev);
}
});
// submit
form.addEventListener('submit', function(ev){
ev.preventDefault();
var btn = form.querySelector('.btn-submit');
var err = document.getElementById('leadError');
err.hidden = true;
btn.classList.add('loading');
btn.disabled = true;
var data = {};
new FormData(form).forEach(function(v,k){ data[k]=v; });
fetch('/api/lead', {
method:'POST',
headers:{'Content-Type':'application/json'},
body: JSON.stringify(data)
})
.then(function(r){ return r.json().then(function(j){ return {ok:r.ok, status:r.status, body:j}; }); })
.then(function(res){
if (!res.ok){
err.textContent = (res.body && res.body.error) || 'Something went wrong. Please try again.';
err.hidden = false;
btn.classList.remove('loading');
btn.disabled = false;
return;
}
if (window.gtag) gtag('event','generate_lead',{value:1, currency:'USD'});
go('done');
var redirUrl = res.body.redirect || 'https://nationalpaperhangers.com';
var a = document.getElementById('redirNow');
a.href = redirUrl;
var n = 3;
var counter = setInterval(function(){
n -= 1;
document.getElementById('redirCount').textContent = String(Math.max(n,0));
if (n <= 0){
clearInterval(counter);
window.location.href = redirUrl;
}
}, 1000);
})
.catch(function(){
err.textContent = 'Network error. Please try again.';
err.hidden = false;
btn.classList.remove('loading');
btn.disabled = false;
});
});
go(1);
})();
</script>
<%- include('partials/foot') %>