← back to Ventura Claw
/services demo widget: 'Try it now' domain+email orchestration plan; structured 7-step API call sequence + cost + deliverables + lead-modal pre-fill
03cee4106a32765b1d3c85253463584ea1ae73e0 · 2026-05-06 16:29:59 -0700 · Steve
Files touched
A server/data/services-demo.jsonlM server/public/services.htmlM server/server.js
Diff
commit 03cee4106a32765b1d3c85253463584ea1ae73e0
Author: Steve <steve@designerwallcoverings.com>
Date: Wed May 6 16:29:59 2026 -0700
/services demo widget: 'Try it now' domain+email orchestration plan; structured 7-step API call sequence + cost + deliverables + lead-modal pre-fill
---
server/data/services-demo.jsonl | 1 +
server/public/services.html | 217 ++++++++++++++++++++++++++++++++++++++++
server/server.js | 72 +++++++++++++
3 files changed, 290 insertions(+)
diff --git a/server/data/services-demo.jsonl b/server/data/services-demo.jsonl
new file mode 100644
index 0000000..61c67bb
--- /dev/null
+++ b/server/data/services-demo.jsonl
@@ -0,0 +1 @@
+{"at":"2026-05-06T23:29:54.632Z","kind":"domain-email","domain":"acmecorp.com","business_name":"Acme Corp","forward_to":"steve@example.com","ip":"127.0.0.1","ua":"curl/8.7.1"}
diff --git a/server/public/services.html b/server/public/services.html
index 2999ffb..b9919cc 100644
--- a/server/public/services.html
+++ b/server/public/services.html
@@ -311,6 +311,105 @@
footer a { color: var(--ink-mute); }
footer a:hover { color: var(--gold); }
+ /* Live demo widget — domain + email setup */
+ .demo-section { padding-top: 36px; padding-bottom: 36px; }
+ .demo-form {
+ display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 14px;
+ margin-bottom: 20px;
+ align-items: end;
+ }
+ .demo-row { display: flex; flex-direction: column; gap: 4px; }
+ .demo-row label {
+ font-family: var(--mono); font-size: 9px; letter-spacing: 0.18em; text-transform: uppercase;
+ color: var(--ink-mute);
+ }
+ .demo-row label .opt { color: var(--ink-faint); text-transform: none; letter-spacing: 0.04em; }
+ .demo-row input {
+ background: var(--bg-2); color: var(--ink);
+ border: 1px solid var(--rule); border-radius: 3px;
+ padding: 12px 14px;
+ font-family: var(--mono); font-size: 14px; font-weight: 400;
+ outline: none; transition: border-color 200ms;
+ }
+ .demo-row input:focus { border-color: var(--gold); box-shadow: 0 0 0 2px var(--gold-soft); }
+ .demo-go {
+ background: var(--gold); color: var(--bg); border: 0;
+ padding: 13px 20px;
+ font-family: var(--mono); font-size: 11px; letter-spacing: 0.18em; text-transform: uppercase; font-weight: 600;
+ cursor: pointer; border-radius: 3px;
+ transition: background 200ms;
+ grid-column: span 1;
+ }
+ .demo-go:hover { background: var(--gold-glow); }
+ .demo-go:disabled { opacity: 0.55; cursor: wait; }
+
+ .demo-result { display: none; margin-top: 24px; }
+ .demo-result.show { display: block; }
+ .demo-summary {
+ padding: 18px 22px;
+ background: linear-gradient(180deg, rgba(212,160,74,0.10), rgba(212,160,74,0.02));
+ border: 1px solid var(--gold);
+ border-left: 3px solid var(--gold);
+ margin-bottom: 18px;
+ }
+ .demo-summary .total {
+ font-family: var(--serif); font-size: 28px; color: var(--gold); font-style: italic;
+ font-weight: 500; letter-spacing: -0.005em;
+ }
+ .demo-summary .total .lbl {
+ font-family: var(--mono); font-size: 9px; letter-spacing: 0.18em;
+ text-transform: uppercase; color: var(--ink-mute);
+ font-style: normal; margin-right: 8px;
+ }
+ .demo-summary .breakdown {
+ font-family: var(--mono); font-size: 11px; color: var(--ink-soft); margin-top: 6px;
+ }
+ .demo-summary .summary-line {
+ color: var(--ink); font-size: 14px; margin-top: 12px; line-height: 1.55;
+ }
+ .demo-steps { background: var(--bg-2); border: 1px solid var(--rule); margin-bottom: 18px; }
+ .demo-step { padding: 14px 18px; border-bottom: 1px solid var(--rule); display: grid; grid-template-columns: 36px 1fr; gap: 16px; }
+ .demo-step:last-child { border-bottom: 0; }
+ .demo-step .n {
+ font-family: var(--mono); font-size: 11px; color: var(--gold); font-weight: 600;
+ letter-spacing: 0.06em;
+ text-align: right;
+ }
+ .demo-step .api { font-family: var(--mono); font-size: 11px; color: var(--ink-mute); margin-bottom: 4px; }
+ .demo-step .action { font-size: 14px; color: var(--ink); margin-bottom: 4px; }
+ .demo-step .meta {
+ font-family: var(--mono); font-size: 10px; color: var(--ink-mute); display: flex; gap: 14px; flex-wrap: wrap;
+ }
+ .demo-step .meta .risk { color: var(--ink-faint); }
+ .demo-step .meta .risk.destructive { color: var(--bad); }
+ .demo-step .meta .cost { color: var(--gold); }
+ .demo-step .meta .needs { color: var(--bad); }
+
+ .demo-deliv { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
+ @media (max-width: 720px) { .demo-deliv { grid-template-columns: 1fr; } }
+ .demo-deliv .panel {
+ background: var(--bg-2); border: 1px solid var(--rule);
+ padding: 16px 20px;
+ }
+ .demo-deliv .panel h4 {
+ font-family: var(--mono); font-size: 10px; letter-spacing: 0.16em; text-transform: uppercase;
+ color: var(--gold); margin: 0 0 10px;
+ }
+ .demo-deliv .panel.not h4 { color: var(--ink-mute); }
+ .demo-deliv .panel ul { margin: 0; padding-left: 18px; color: var(--ink-soft); font-size: 13px; line-height: 1.6; }
+
+ .demo-cta-row {
+ margin-top: 20px; display: flex; gap: 14px; align-items: center; flex-wrap: wrap;
+ padding: 16px 18px; background: var(--bg-3); border: 1px solid var(--gold);
+ }
+ .demo-cta-row .pre { color: var(--ink-soft); font-size: 13px; flex: 1; min-width: 240px; }
+ .demo-cta-run {
+ background: var(--gold); color: var(--bg); border: 0;
+ padding: 12px 22px; font-family: var(--mono); font-size: 11px; letter-spacing: 0.18em; text-transform: uppercase; font-weight: 600;
+ cursor: pointer; border-radius: 3px;
+ }
+ .demo-cta-run:hover { background: var(--gold-glow); }
+
/* "How it actually runs" 3-step flow */
.how-section { padding-top: 56px; }
.flow-grid {
@@ -632,6 +731,37 @@
</div>
</section>
+<!-- DEMO WIDGET — domain + email setup, real plumbing -->
+<section class="cat-section demo-section" id="try-it">
+ <div class="cat-head">
+ <div>
+ <span class="cat-icon">▲ TRY IT NOW · LIVE</span>
+ <h2>Type a domain. See exactly <em>what we'd do.</em></h2>
+ </div>
+ <div class="cat-blurb">No payment, no signup. Type the domain you'd want, we return the precise sequence of API calls (Porkbun → Cloudflare → DNS → SPF/DKIM/DMARC → email forwarding) we'd fire, with cost. Run it for $39 + the registration fee when you're ready.</div>
+ </div>
+
+ <form class="demo-form" id="demoForm" autocomplete="off">
+ <div class="demo-row">
+ <label for="demoDomain">Domain you want</label>
+ <input type="text" id="demoDomain" name="domain" required
+ placeholder="mybiz.com" pattern="[a-z0-9-]+\.[a-z]{2,24}"
+ autocapitalize="off" spellcheck="false" />
+ </div>
+ <div class="demo-row">
+ <label for="demoBiz">Business name <span class="opt">(optional)</span></label>
+ <input type="text" id="demoBiz" name="business_name" placeholder="Acme Consulting LLC" maxlength="80" />
+ </div>
+ <div class="demo-row">
+ <label for="demoFwd">Forward info@ to <span class="opt">(your inbox)</span></label>
+ <input type="email" id="demoFwd" name="forward_to" placeholder="you@gmail.com" />
+ </div>
+ <button type="submit" class="demo-go">Show the plan →</button>
+ </form>
+
+ <div class="demo-result" id="demoResult"></div>
+</section>
+
<!-- (Formation section removed — government-portal services pivoted out 2026-05-06; Operating Agreement relocated to Documents) -->
<!-- BANKING -->
@@ -790,6 +920,93 @@
</main>
<script>
+ // ----- TRY-IT-NOW domain demo widget -----
+ (function wireDemo() {
+ const form = document.getElementById('demoForm');
+ const result = document.getElementById('demoResult');
+ if (!form || !result) return;
+ form.addEventListener('submit', async e => {
+ e.preventDefault();
+ const btn = form.querySelector('.demo-go');
+ btn.disabled = true; btn.textContent = 'Calling APIs…';
+ const payload = {
+ domain: document.getElementById('demoDomain').value.trim().toLowerCase(),
+ business_name: document.getElementById('demoBiz').value.trim(),
+ forward_to: document.getElementById('demoFwd').value.trim(),
+ };
+ try {
+ const r = await fetch('/api/services-demo/domain-email', {
+ method: 'POST', headers: {'Content-Type':'application/json'},
+ body: JSON.stringify(payload)
+ });
+ const j = await r.json();
+ if (!r.ok) throw new Error(j.msg || j.error || 'Demo failed.');
+ renderDemo(j.plan, payload);
+ } catch (err) {
+ result.innerHTML = `<div style="padding:14px;border:1px solid var(--bad);color:var(--bad);font-size:13px">${err.message}</div>`;
+ result.classList.add('show');
+ } finally {
+ btn.disabled = false; btn.textContent = 'Show the plan →';
+ }
+ });
+
+ function escHtml(s) { return (s||'').replace(/[<>&"]/g, c=>({'<':'<','>':'>','&':'&','"':'"'}[c])); }
+
+ function renderDemo(plan, payload) {
+ const stepsHtml = plan.steps.map(s => `<div class="demo-step">
+ <div class="n">${s.n}</div>
+ <div>
+ <div class="api">${escHtml(s.api)}</div>
+ <div class="action">${escHtml(s.action)}</div>
+ <div class="meta">
+ <span class="risk ${s.risk === 'destructive: changes ownership of DNS' ? 'destructive' : ''}">${escHtml(s.risk||'')}</span>
+ ${s.cost ? `<span class="cost">${escHtml(s.cost)}</span>` : ''}
+ ${s.needs_input ? `<span class="needs">needs your input</span>` : ''}
+ </div>
+ </div>
+ </div>`).join('');
+
+ const youGet = plan.you_get.map(x => `<li>${escHtml(x)}</li>`).join('');
+ const notIncl = plan.not_included.map(x => `<li>${escHtml(x)}</li>`).join('');
+
+ result.innerHTML = `
+ <div class="demo-summary">
+ <div class="total"><span class="lbl">Total</span>$${plan.fees.total.toFixed(2)}</div>
+ <div class="breakdown">
+ VenturaClaw orchestration: <b>$${plan.fees.ventura_claw_orchestration.toFixed(2)}</b>
+ · Domain registration (year 1): <b>$${plan.fees.domain_registration.toFixed(2)}</b>
+ · Cloudflare DNS: <b>free</b>
+ · ETA: <b>~${plan.estimated_total_seconds}s end-to-end</b>
+ </div>
+ <div class="summary-line">${escHtml(plan.summary)}</div>
+ </div>
+ <div class="demo-steps">${stepsHtml}</div>
+ <div class="demo-deliv">
+ <div class="panel"><h4>You get</h4><ul>${youGet}</ul></div>
+ <div class="panel not"><h4>Not included</h4><ul>${notIncl}</ul></div>
+ </div>
+ <div class="demo-cta-row">
+ <div class="pre">Like the plan? Hit the button — opens a Get-Started form pre-filled with these inputs and connects you to a human who'll run the API calls live (we don't run them automatically yet).</div>
+ <button type="button" class="demo-cta-run" id="demoCtaRun">Run this plan →</button>
+ </div>`;
+ result.classList.add('show');
+ result.scrollIntoView({ behavior: 'smooth', block: 'start' });
+
+ const runBtn = document.getElementById('demoCtaRun');
+ runBtn.addEventListener('click', () => {
+ // Open the lead modal pre-filled with the demo inputs
+ document.getElementById('leadSvcTag').textContent = 'Domain + email setup';
+ document.getElementById('leadService').value = 'Domain + email setup';
+ document.getElementById('leadModalTitle').textContent = 'Run: Domain + email setup';
+ document.getElementById('leadEmail').value = payload.forward_to || '';
+ document.getElementById('leadNotes').value = `Demo plan for ${payload.domain}${payload.business_name ? ' / '+payload.business_name : ''}. Forward to ${payload.forward_to || '(unspecified)'}. Total ~$${plan.fees.total.toFixed(2)}, ~${plan.estimated_total_seconds}s.`;
+ document.getElementById('leadPrice').innerHTML = `<b>Total:</b> $${plan.fees.total.toFixed(2)} · <b>ETA:</b> ~${plan.estimated_total_seconds}s`;
+ document.getElementById('leadModal').classList.add('show');
+ setTimeout(() => document.getElementById('leadEmail').focus(), 100);
+ });
+ }
+ })();
+
// ----- "not legal advice" badge on every service card -----
document.querySelectorAll('.svc').forEach(el => {
const b = document.createElement('span');
diff --git a/server/server.js b/server/server.js
index 8874f24..db5f8da 100644
--- a/server/server.js
+++ b/server/server.js
@@ -654,6 +654,78 @@ app.post("/api/demo-classify", demoLimiter, express.json({ limit: "2kb" }), asyn
res.status(500).json({ error: "classify_failed" });
}
});
+// Public services-demo endpoint — "Try it now" widget.
+// Returns a structured plan-of-API-calls for a given operation (no external hits yet — risk-free demo).
+// Captures the desired domain + visitor IP for follow-up via the lead viewer.
+const SERVICES_DEMO_PATH = path.join(__dirname, "data", "services-demo.jsonl");
+const servicesDemoLimiter = rateLimit({
+ windowMs: 60 * 1000, max: 30, standardHeaders: true, legacyHeaders: false,
+ message: { error: "rate_limited", msg: "Slow down — 30/min." }
+});
+app.post("/api/services-demo/domain-email", servicesDemoLimiter, express.json({ limit: "2kb" }), async (req, res) => {
+ const { domain, business_name, forward_to } = req.body || {};
+ if (typeof domain !== "string" || !/^[a-z0-9-]+\.[a-z]{2,24}$/i.test(domain.trim()) || domain.length > 60) {
+ return res.status(400).json({ error: "bad_domain", msg: "Domain must be a valid hostname like 'mybiz.com'." });
+ }
+ const dom = domain.trim().toLowerCase();
+ const tld = dom.split(".").pop();
+
+ // Approximate pricing per TLD. Real Porkbun pricing API would replace this; for the demo it's a fixed table.
+ const priceTable = { com: 9.13, net: 11.55, org: 9.66, io: 32.99, ai: 70.45, co: 26.41, app: 15.18, dev: 16.30, me: 24.99, biz: 17.99, info: 6.95 };
+ const reg_price = priceTable[tld] ?? 18.99;
+
+ const plan = {
+ summary: `Register ${dom}, point DNS at Cloudflare, set up info@${dom} with SPF/DKIM/DMARC, return working business email.`,
+ estimated_total_seconds: 47,
+ steps: [
+ { n: 1, api: "Porkbun /api/json/v3/domain/checkAvailability", action: `check ${dom}`, expected: "available / unavailable + reg price", risk: "read-only" },
+ { n: 2, api: "Porkbun /api/json/v3/domain/registerDomain", action: `register ${dom} for 1 year`, expected: `success → domain in your account`, cost: `$${reg_price.toFixed(2)} reg fee` },
+ { n: 3, api: "Cloudflare POST /zones", action: `create zone for ${dom}`, expected: "zone_id + nameservers", risk: "create" },
+ { n: 4, api: "Porkbun /api/json/v3/domain/updateNs", action: `set nameservers to Cloudflare`, expected: "propagation begins (~5 min)", risk: "destructive: changes ownership of DNS" },
+ { n: 5, api: "Cloudflare POST /zones/{id}/dns_records ×6", action: `create A, MX, SPF, DKIM, DMARC, _domainkey records`, expected: "all records green in CF dashboard", risk: "create" },
+ { n: 6, api: "Cloudflare email-routing API", action: `enable forwarding info@${dom} → ${forward_to || "[your inbox]"}`, expected: "rule active", risk: "create", needs_input: !forward_to },
+ { n: 7, api: "internal verification job", action: `wait for SPF/DKIM/DMARC to validate via Cloudflare`, expected: "all 3 = pass", risk: "read-only" },
+ ],
+ fees: {
+ ventura_claw_orchestration: 39.00,
+ domain_registration: reg_price,
+ cloudflare: 0,
+ total: +(39 + reg_price).toFixed(2),
+ },
+ you_get: [
+ `${dom} registered in your name (you own it, not us)`,
+ `info@${dom} forwarding to wherever you want`,
+ `SPF / DKIM / DMARC configured so your email lands in inboxes`,
+ `Cloudflare dashboard handed over to you`,
+ `Audit trail of every API call we fired (Cloudflare CFs, Porkbun receipt)`,
+ ],
+ not_included: [
+ "Mailbox storage (we set up forwarding, not Gmail/Outlook)",
+ "Trademark search on the domain name",
+ "WHOIS privacy (Porkbun does it free; on by default)",
+ ],
+ next_step: forward_to && business_name ? "ready_to_run" : "needs_inputs",
+ };
+
+ // Persist for follow-up
+ try {
+ fs.mkdirSync(path.dirname(SERVICES_DEMO_PATH), { recursive: true });
+ fs.appendFileSync(SERVICES_DEMO_PATH, JSON.stringify({
+ at: new Date().toISOString(),
+ kind: "domain-email",
+ domain: dom,
+ business_name: (business_name || "").slice(0, 80) || null,
+ forward_to: (forward_to || "").slice(0, 200) || null,
+ ip: (req.headers["x-forwarded-for"] || req.ip || "").toString().split(",")[0].trim(),
+ ua: (req.headers["user-agent"] || "").slice(0, 200),
+ }) + "\n");
+ } catch (e) {
+ console.warn("[services-demo] persist failed:", e.message);
+ }
+
+ res.json({ ok: true, plan });
+});
+
// Public services-lead endpoint — captures "Get started" form submissions.
// Writes to JSONL (lossless) + best-effort email via George Gmail (non-blocking).
const SERVICES_LEADS_PATH = path.join(__dirname, "data", "services-leads.jsonl");
← ff436d8 /services chat: welcome message points at API offerings (Str
·
back to Ventura Claw
·
ENV_KEY_TO_CONNECTOR: add ELEVENLABS_API_KEY, PURELYMAIL_API 0c593b6 →