LP Factory

\n\n\n{{meta.title}}\n\n \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n\n\n\n\n
\n
\n \n \"coconut\"\n \n
\n
\n\n\n
\n
\n
\n {{hero.eyebrow|escAmp}}\n

{{hero.h1}}

\n {{hero.accent}}\n

{{hero.lead}}

\n
\n 650+ operators placed\n ·\n 300+ companies served\n ·\n 98% retention\n
\n

40 hours of real work, on us. No long-term commitments.

\n
\n\n \n
\n
\n
\n
\n
\n
Step 1 of 3
\n
\n\n
\n

Let's get you matched

\n

Start with your name and email. Takes 10 seconds.

\n
\n
\n
\n \n
\n\n
\n

Find your perfect fit

\n

{{form.step2Sub}}

\n
\n{{HOURS_FIELD}}
\n \n \n
\n
\n
\n \n \n
\n
\n\n
\n

Last step, let's lock it in

\n

Where can we text you, and how soon are you hiring?

\n
\n
\n \n \n
\n
\n
\n \n \n
\n
\n\n

Starts at $900/month. Built for companies hiring 20+ hours a week. First 40 hours free after qualification and match.

\n
\n\n \n
\n
\n

You're in!

\n

{{form.successText}}

\n Browse example profiles\n
\n
\n
\n
\n
\n\n\n\n
\n
\n
\n
\n{{TESTIMONIAL_CARDS}}\n
\n
\n
\n
\n\n\n
\n
\n
\n

Roles we fill

\n
\n{{ROLE_PILLS}}\n
\n
\n
\n

Works in your stack

\n \n
\n{{TOOL_CHIPS}}\n
\n
\n
\n
\n\n\n
\n
\n
\n

What You Get

\n

Everything in your 40 hour free trial, spelled out.

\n
\n
\n{{WYG_ROWS}}\n
\n \n
\n
\n\n\n
\n
\n \"\"\n \"\"\n

{{ctaBanner.heading}}

\n

{{ctaBanner.sub}}

\n Show me a few matches\n
\n
\n\n\n
\n
\n
\n
Frequently Asked
\n

Fresh answers, straight from the source

\n

The most common questions we get from trades owners.

\n
\n
\n \n
\n
\n

\n Still have questions?\n Book a 20 minute call with Adell\n

\n
\n
\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"; var TESTIMONIALS = {"VJ Gandhi":{"text":"Coconut VA sent me a list of qualified people to interview. We chose Mike, who's now essential to our team. He handles logistics, manages customer service, and takes on high-level projects, all with professionalism and constant follow-up. Highly recommend.","title":"Owner - Kascadia","logoFile":"kascadia.webp","logoAlt":"Kascadia","photoFile":"vj-gandhi.png"},"Kat Weaver":{"text":"The Coconut VA team is extremely diligent in helping us find the right talent. The VA they found us has gone above and beyond, and it's amazing that she works our hours, too. We went from thinking part-time to hiring her full-time. 10/10.","title":"Founder - Power To Pitch","logoFile":"power-to-pitch.png","logoAlt":"Power To Pitch","photoFile":"kat-weaver.png"},"Stephanie Acker":{"text":"Coconut VA moved our back office forward. Recruiting and sourcing the right candidate was easy and smooth. Our VA handles CRM reconciliation, vendor management, and inbound inquiries, and she has surpassed all of our expectations.","title":"Executive Director - PGN Nationals","logoFile":"pgn-nationals.webp","logoAlt":"PGN Nationals","photoFile":"stephanie-acker.png"},"Phil Kirkeiner":{"text":"We hired our first three virtual assistants through Coconut VA, including two of our best ever, and we have 30+ now. They do a great job finding top notch candidates. If you're new to the VA world, I'd recommend them.","title":"Founder - Baton","logoFile":"baton.png","logoAlt":"Baton","photoFile":"phil-kirkeiner.png"},"Liz Germain":{"text":"Coconut VA handles recruiting, onboarding, HR, performance, and payments. Our VA has been incredible and helps our business run more smoothly. The agency makes everything so easy. Highly recommend.","title":"Founder - Vidfluence","logoFile":"vidfluence.png","logoAlt":"Vidfluence","photoFile":"liz-germain.png"},"Bruce Hymas":{"text":"I've always had positive experiences with Coconut VA. Flexible and accommodating. I've worked with my VA for 3 years and he's a very valuable asset to our team. I recommend them to anyone who wants to buy back their time.","title":"COO - Werkt","logoFile":"werkt.png","logoAlt":"Werkt","photoFile":"bruce-hymas.png"}}; var LOGOS = ["billcom.png","buildertrend.png","companycam.png","housecallpro.png","jobber.png","quickbooks.png","servicetitan.png","xero.png"]; var pages = []; // all configs from /api/pages var current = null; // working copy var isNew = false; var pw = sessionStorage.getItem('lp_pw') || ''; var $ = function (id) { return document.getElementById(id); }; /* ---------- auth ---------- */ function tryLogin(p) { return fetch('/api/pages', { headers: { 'x-admin-password': p } }) .then(function (r) { if (!r.ok) throw new Error('auth'); return r.json(); }); } $('loginBtn').addEventListener('click', function () { var p = $('pw').value; tryLogin(p).then(function (data) { pw = p; sessionStorage.setItem('lp_pw', p); boot(data.pages); }).catch(function () { var e = $('loginErr'); e.textContent = 'Wrong password.'; e.className = 'status err'; }); }); $('pw').addEventListener('keydown', function (e) { if (e.key === 'Enter') $('loginBtn').click(); }); if (pw) tryLogin(pw).then(function (d) { boot(d.pages); }).catch(function () { sessionStorage.removeItem('lp_pw'); }); function boot(list) { pages = list; $('login').classList.add('hidden'); $('app').classList.remove('hidden'); var sel = $('pageSelect'); sel.innerHTML = pages.map(function (p) { return ''; }).join(''); sel.addEventListener('change', function () { load(sel.value); }); load(pages[0].slug); } /* ---------- load / duplicate ---------- */ function load(slug) { var src = pages.find(function (p) { return p.slug === slug; }); current = JSON.parse(JSON.stringify(src)); isNew = false; $('f_slug').disabled = true; fillForm(); renderPreview(); } $('dupBtn').addEventListener('click', function () { var name = prompt('New page slug (lowercase-with-dashes, e.g. trades-dispatcher):'); if (!name) return; name = name.trim().toLowerCase(); if (!/^[a-z0-9-]{3,60}$/.test(name)) return alert('Invalid slug.'); if (pages.some(function (p) { return p.slug === name; })) return alert('That slug already exists.'); current = JSON.parse(JSON.stringify(current)); current.slug = name; current.variant = name; current.subdomain = ''; isNew = true; $('f_slug').disabled = false; fillForm(); setStatus('ok', 'Duplicated. Edit the copy, then Publish to create pages/' + name + '.json.'); renderPreview(); }); /* ---------- form <-> config ---------- */ function fillForm() { $('f_slug').value = current.slug; $('f_adsName').value = current.adsName; $('f_pageLabel').value = current.pageLabel; $('f_subdomain').value = current.subdomain || ''; $('f_title').value = current.meta.title; $('f_desc').value = current.meta.description; $('f_ogdesc').value = current.meta.ogDescription; $('f_eyebrow').value = current.hero.eyebrow; $('f_h1').value = current.hero.h1; $('f_accent').value = current.hero.accent; $('f_lead').value = current.hero.lead; $('f_step1btn').value = current.form.step1Button; $('f_step2sub').value = current.form.step2Sub; $('f_needLabel').value = current.form.needLabel; $('f_needOptions').value = current.form.needOptions.join('\n'); $('f_hoursLabel').value = (current.form.hours && current.form.hours.label) || ''; $('f_hoursOptions').value = (current.form.hours && current.form.hours.options || []).join('\n'); $('f_successText').value = current.form.successText; $('f_browseUrl').value = current.form.browseUrl; $('f_roles').value = current.roles.join('\n'); $('f_ctaH').value = current.ctaBanner.heading; $('f_ctaP').value = current.ctaBanner.sub; $('f_calendly').value = current.calendlyUrl; $('f_redirect').value = current.redirectUrl; buildLogoGrid(); buildWyg(); buildFaq(); } function harvest() { current.slug = $('f_slug').value.trim(); current.variant = current.slug; current.adsName = $('f_adsName').value.trim(); current.pageLabel = $('f_pageLabel').value.trim(); current.subdomain = $('f_subdomain').value.trim(); current.meta.title = $('f_title').value; current.meta.description = $('f_desc').value; current.meta.ogDescription = $('f_ogdesc').value; current.hero.eyebrow = $('f_eyebrow').value; current.hero.h1 = $('f_h1').value; current.hero.accent = $('f_accent').value; current.hero.lead = $('f_lead').value; current.form.step1Button = $('f_step1btn').value; current.form.step2Sub = $('f_step2sub').value; current.form.needLabel = $('f_needLabel').value; current.form.needOptions = lines($('f_needOptions').value); var hl = $('f_hoursLabel').value.trim(), ho = lines($('f_hoursOptions').value); if (hl && ho.length) current.form.hours = { label: hl, options: ho }; else delete current.form.hours; current.form.successText = $('f_successText').value; current.form.browseUrl = $('f_browseUrl').value.trim(); current.roles = lines($('f_roles').value); current.ctaBanner.heading = $('f_ctaH').value; current.ctaBanner.sub = $('f_ctaP').value; current.calendlyUrl = $('f_calendly').value.trim(); current.redirectUrl = $('f_redirect').value.trim(); // tools from grid var checked = Array.prototype.slice.call(document.querySelectorAll('#logoGrid input:checked')) .map(function (cb) { return { file: cb.value, alt: cb.dataset.alt }; }); var prevOrder = current.tools.items.map(function (t) { return t.file; }); checked.sort(function (x, y) { var ix = prevOrder.indexOf(x.file), iy = prevOrder.indexOf(y.file); if (ix === -1 && iy === -1) return 0; // both newly added: keep grid order if (ix === -1) return 1; // new logos go after existing ones if (iy === -1) return -1; return ix - iy; // existing logos keep the page's original order }); current.tools.items = checked; // wyg + faq harvested live by their own handlers (arrays mutated in place) } function lines(v) { return v.split('\n').map(function (s) { return s.trim(); }).filter(Boolean); } /* ---------- tools grid ---------- */ var ALT_NAMES = { 'servicetitan.png':'ServiceTitan','jobber.png':'Jobber','housecallpro.png':'Housecall Pro','buildertrend.png':'Buildertrend','companycam.png':'CompanyCam','quickbooks.png':'QuickBooks','xero.png':'Xero','billcom.png':'Bill.com' }; function buildLogoGrid() { var g = $('logoGrid'); g.innerHTML = ''; LOGOS.forEach(function (file) { var alt = ALT_NAMES[file] || file.replace(/\.(png|webp)$/, ''); var on = current.tools.items.some(function (t) { return t.file === file; }); var l = document.createElement('label'); l.className = on ? 'on' : ''; l.innerHTML = '' + '' + alt + ''; l.querySelector('input').addEventListener('change', function (e) { l.className = e.target.checked ? 'on' : ''; onChange(); }); g.appendChild(l); }); } /* ---------- WYG rows ---------- */ function buildWyg() { var box = $('wygList'); box.innerHTML = ''; current.wyg.forEach(function (row, i) { var d = document.createElement('div'); d.className = 'wyg-item'; d.innerHTML = '
dark
' + ''; d.querySelector('input[type=text]').value = row.title; d.querySelector('textarea').value = row.text; d.querySelector('input[type=text]').addEventListener('input', function (e) { row.title = e.target.value; onChange(); }); d.querySelector('textarea').addEventListener('input', function (e) { row.text = e.target.value; onChange(); }); d.querySelector('input[type=checkbox]').addEventListener('change', function (e) { row.featured = e.target.checked; onChange(); }); d.querySelector('button').addEventListener('click', function () { current.wyg.splice(i, 1); buildWyg(); onChange(); }); box.appendChild(d); }); } $('addWyg').addEventListener('click', function () { current.wyg.push({ featured: false, title: 'New benefit', text: 'Describe it.' }); buildWyg(); onChange(); }); /* ---------- FAQ ---------- */ function buildFaq() { var box = $('faqList'); box.innerHTML = ''; current.faq.forEach(function (f, i) { var d = document.createElement('div'); d.className = 'faq-item'; d.innerHTML = '
' + ''; d.querySelector('input').value = f.q; d.querySelector('textarea').value = f.a; d.querySelector('input').addEventListener('input', function (e) { f.q = e.target.value; onChange(); }); d.querySelector('textarea').addEventListener('input', function (e) { f.a = e.target.value; onChange(); }); d.querySelector('button').addEventListener('click', function () { current.faq.splice(i, 1); buildFaq(); onChange(); }); box.appendChild(d); }); } $('addFaq').addEventListener('click', function () { current.faq.push({ q: 'New question?', a: 'Answer.' }); buildFaq(); onChange(); }); /* ---------- preview ---------- */ var t; function onChange() { clearTimeout(t); t = setTimeout(renderPreview, 250); } Array.prototype.slice.call(document.querySelectorAll('#editor input, #editor textarea')).forEach(function (el) { el.addEventListener('input', onChange); }); function renderPreview() { try { harvest(); var html = LPRender.render(TEMPLATE, current, TESTIMONIALS); // base tag so /public/* resolves against this deployment inside srcdoc html = html.replace('', ''); $('preview').srcdoc = html; setStatus('', ''); } catch (e) { setStatus('err', 'Preview error: ' + e.message); } } /* ---------- publish ---------- */ $('publishBtn').addEventListener('click', function () { harvest(); var btn = $('publishBtn'); btn.disabled = true; btn.textContent = 'Publishing…'; fetch('/api/publish', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ password: pw, slug: current.slug, config: current }) }).then(function (r) { return r.json(); }).then(function (res) { btn.disabled = false; btn.textContent = 'Publish'; if (res.ok) { setStatus('ok', (res.created ? 'Created ' : 'Updated ') + res.slug + '. Vercel is deploying (~2 min). Page: ' + res.pageUrl + (current.subdomain ? ' — Daniel was emailed to wire ' + current.subdomain + '.' : '')); if (isNew) { pages.push(JSON.parse(JSON.stringify(current))); boot(pages); } } else { setStatus('err', 'Publish failed: ' + (res.error || 'unknown')); } }).catch(function () { btn.disabled = false; btn.textContent = 'Publish'; setStatus('err', 'Network error.'); }); }); function setStatus(kind, msg) { var s = $('status'); s.className = 'status' + (kind ? ' ' + kind : ''); s.textContent = msg; if (kind) s.scrollIntoView({ block: 'nearest' }); } })();