Your Agency
Enter password to continue
License activation
Activate your
suite.
Enter your purchase email to activate. Takes 5 seconds — done once, remembered forever.
Verifying with Stripe…
Can't find your purchase? Email [email protected]
All Pages · Landing Page Builder
⌘K
0
Total Pages
0
Live Pages
0
Total Visits
0%
Avg. Conversion
Your Pages
Drag blocks to build · Connect forms to Ignis
Untitled Page
Draft
🖥
📱
📲
Add Blocks
Start Building
Drag blocks from the left panel or click to add
Properties
Select a block
to edit its
properties
Capture Forms
Forms feed leads directly into Ignis
Ignis Integration — Every form submission is pushed as a new lead to your Ignis inbox with full field data captured
Form NameFieldsSubmissionsConversionPageStatus
Page Templates
Start fast · Customize everything
Page Analytics
Views, conversions, lead captures per page
0
Total Visits (30d)
0
Leads Captured
0%
Conversion Rate
0
Active Pages
Page Performance
PageStatusVisitsLeadsConv %Avg TimeLast Updated
Preview
Desktop
Tablet
Mobile
Forma AI
×
Forma AI
I can help you build high-converting landing pages, write copy, suggest layouts, and optimize your capture forms for more Ignis leads.
What makes a high-converting landing page?
Write a hero headline for a marketing agency
Suggest a layout for a lead capture page
How do I connect my page form to Ignis?
`; } function closePreview() { document.getElementById('preview-overlay').classList.remove('open'); } function setPreviewDevice(dev, btn) { const widths = {desktop:'1200px', tablet:'768px', mobile:'390px'}; document.getElementById('preview-frame').style.width = widths[dev]; document.querySelectorAll('#preview-devices .device-btn').forEach(b=>b.classList.remove('active')); btn.classList.add('active'); } function setDevice(dev) { currentDevice = dev; document.querySelectorAll('.device-btn').forEach(b=>b.classList.remove('active')); document.getElementById('dev-'+dev)?.classList.add('active'); renderCanvas(); } function promptRenamePage() { const page = getCurrentPage(); if (!page) return; const name = prompt('Page name:', page.name); if (name && name.trim()) { page.name = name.trim(); document.getElementById('builder-page-name').textContent = page.name; saveState(); renderPages(); } } function undoAction() { toast('Undo coming soon', 'info'); } // ═══════════════════════════════════════════════════════════════ // FORMS // ═══════════════════════════════════════════════════════════════ function renderForms() { const tbody = document.getElementById('forms-tbody'); const empty = document.getElementById('forms-empty'); if (!state.forms.length) { if(tbody) tbody.innerHTML = ''; if(empty) empty.style.display = 'flex'; return; } if(empty) empty.style.display = 'none'; if(tbody) tbody.innerHTML = state.forms.map(f => { const page = state.pages.find(p=>p.id===f.pageId); return ` ${f.name} ${(f.fields||'').split(',').length} fields ${f.submissions||0} ${f.submissions > 0 ? Math.round((f.submissions/(f.views||1))*100)+'%' : '—'} ${page ? page.name : 'Unlinked'} Active `; }).join(''); } function openNewFormModal() { const pageOptions = state.pages.map(p=>``).join(''); showModal('New Capture Form', `
`, [ {label:'Cancel', cls:'btn-ghost', fn:'closeModal()'}, {label:'Create Form', cls:'btn-forma', fn:'createForm()'} ]); } function createForm() { const name = document.getElementById('fm-name')?.value.trim(); if (!name) { toast('Form name required','error'); return; } const form = { id: state.nextId++, name, fields: document.getElementById('fm-fields')?.value || 'Name,Email', ctaText: document.getElementById('fm-cta')?.value || 'Submit', pageId: parseInt(document.getElementById('fm-page')?.value) || null, ignis: document.getElementById('fm-ignis')?.value === 'true', submissions: 0, views: 0, createdAt: new Date().toISOString() }; state.forms.push(form); saveState(); renderForms(); updateBadges(); closeModal(); toast('Form created', 'success'); } function deleteForm(id) { if (!confirm('Delete this form?')) return; state.forms = state.forms.filter(f=>f.id!==id); saveState(); renderForms(); updateBadges(); toast('Form deleted', 'error'); } // ═══════════════════════════════════════════════════════════════ // TEMPLATES // ═══════════════════════════════════════════════════════════════ function renderTemplates() { const grid = document.getElementById('tmpl-grid'); if (!grid) return; grid.innerHTML = TEMPLATES.map(t => `
${t.icon}
${t.name}
${t.desc}
${t.count}
`).join(''); } function useTemplate(id) { const tmpl = TEMPLATES.find(t=>t.id===id); if (!tmpl) return; const name = prompt(`Page name for "${tmpl.name}":`, tmpl.name) || tmpl.name; const blocks = tmpl.blocks.map(type => { const def = findBlockDef(type); if (!def) return null; return {id: Date.now() + Math.random(), type, data: JSON.parse(JSON.stringify(def.defaults))}; }).filter(Boolean); const page = { id: state.nextId++, name, status: 'draft', blocks, visits: 0, leads: 0, createdAt: new Date().toISOString() }; state.pages.push(page); saveState(); renderAll(); openBuilderForPage(page.id); toast(`"${name}" created from template`, 'success'); } // ═══════════════════════════════════════════════════════════════ // ANALYTICS // ═══════════════════════════════════════════════════════════════ function renderAnalytics() { const tbody = document.getElementById('analytics-tbody'); if (!tbody) return; tbody.innerHTML = state.pages.length ? state.pages.map(p => { const conv = p.visits > 0 ? Math.round((p.leads/p.visits)*100) : 0; const updated = p.updatedAt || p.createdAt || ''; const badgeClass = {live:'badge-live',draft:'badge-draft',paused:'badge-paused',archived:'badge-archived'}[p.status]||'badge-draft'; return ` ${p.name} ${p.status} ${(p.visits||0).toLocaleString()} ${p.leads||0} ${conv}% — ${updated ? new Date(updated).toLocaleDateString() : '—'} `; }).join('') : `NO PAGES YET`; } // ═══════════════════════════════════════════════════════════════ // NEW PAGE MODAL // ═══════════════════════════════════════════════════════════════ function openNewPageModal() { showModal('New Page', `
You can choose a template here or browse all templates from the sidebar.
`, [ {label:'Cancel', cls:'btn-ghost', fn:'closeModal()'}, {label:'Create Page', cls:'btn-forma', fn:'createPageFromModal()'} ]); setTimeout(() => document.getElementById('np-name')?.focus(), 100); } function createPageFromModal() { const name = document.getElementById('np-name')?.value.trim(); if (!name) { toast('Page name required','error'); return; } const tmplId = document.getElementById('np-tmpl')?.value || 'blank'; closeModal(); if (tmplId === 'blank') { const page = {id:state.nextId++, name, status:'draft', blocks:[], visits:0, leads:0, createdAt:new Date().toISOString()}; state.pages.push(page); saveState(); renderAll(); openBuilderForPage(page.id); } else { useTemplate(tmplId); // Fix the name after template creation const lastPage = state.pages[state.pages.length-1]; if (lastPage) { lastPage.name = name; saveState(); renderPages(); } } } // ═══════════════════════════════════════════════════════════════ // VIEW SWITCHING // ═══════════════════════════════════════════════════════════════ const VIEW_TITLES = { pages: ['All Pages','Landing Page Builder'], builder: ['Builder','Edit · Drag & Drop'], forms: ['Capture Forms','Ignis Lead Integration'], templates: ['Templates','Start Fast'], analytics: ['Analytics','Page Performance'], }; function switchView(name) { document.querySelectorAll('.view').forEach(v=>v.classList.remove('active')); document.querySelectorAll('.nav-item').forEach(n=>n.classList.remove('active')); const el = document.getElementById(name+'-view'); if (el) el.classList.add('active'); const nav = document.getElementById('nav-'+name); if (nav) nav.classList.add('active'); const [title, sub] = VIEW_TITLES[name] || [name,'']; document.getElementById('topbar-title').textContent = title; document.getElementById('topbar-sub').textContent = sub; if (name === 'builder') { renderBlockList(); renderCanvas(); renderProps(); } } // ═══════════════════════════════════════════════════════════════ // MODAL // ═══════════════════════════════════════════════════════════════ function showModal(title, body, buttons=[]) { document.getElementById('modal-title').textContent = title; document.getElementById('modal-body').innerHTML = body; document.getElementById('modal-footer').innerHTML = buttons.map(b => ``).join(''); document.getElementById('modal-overlay').style.display = 'flex'; } function closeModal(e) { if (e && e.target !== document.getElementById('modal-overlay')) return; document.getElementById('modal-overlay').style.display = 'none'; } // ═══════════════════════════════════════════════════════════════ // TOAST // ═══════════════════════════════════════════════════════════════ function toast(msg, type='info') { const wrap = document.getElementById('toast-wrap'); const el = document.createElement('div'); el.className = `toast toast-${type}`; el.innerHTML = `${msg}`; wrap.appendChild(el); setTimeout(() => el.remove(), 3000); } // ═══════════════════════════════════════════════════════════════ // COMMAND PALETTE // ═══════════════════════════════════════════════════════════════ document.addEventListener('keydown', e => { if ((e.metaKey||e.ctrlKey) && e.key==='k') { e.preventDefault(); openCmdPal(); } if (e.key==='Escape') { closeCmdPal(); closeModal(); closePreview(); } }); function openCmdPal() { document.getElementById('cmdpal').classList.add('open'); setTimeout(() => document.getElementById('cmd-input')?.focus(), 50); updateCmdResults(''); } function closeCmdPal(e) { if (e && e.target !== document.getElementById('cmdpal')) return; document.getElementById('cmdpal').classList.remove('open'); document.getElementById('cmd-input').value = ''; } let cmdIdx = -1; function updateCmdResults(q) { q = q.toLowerCase(); cmdIdx = -1; const results = []; // Pages state.pages.forEach(p => { if (!q || p.name.toLowerCase().includes(q)) { results.push({icon:'⬜', main:p.name, sub:`${p.status} · ${(p.blocks||[]).length} blocks`, fn:`openBuilderForPage(${p.id})`}); } }); // Forms state.forms.forEach(f => { if (!q || f.name.toLowerCase().includes(q)) { results.push({icon:'📋', main:f.name, sub:`Form · ${f.submissions||0} submissions`, fn:`''`}); } }); // Actions const actions = [ {k:'new page',l:'New Page',s:'Create a new landing page',fn:"openNewPageModal()"}, {k:'template',l:'Browse Templates',s:'Start from a template',fn:"switchView('templates');closeCmdPal()"}, {k:'form',l:'New Form',s:'Create a capture form',fn:"openNewFormModal()"}, {k:'analytics',l:'Analytics',s:'View page performance',fn:"switchView('analytics');closeCmdPal()"}, {k:'ignis leads',l:'Open Ignis',s:'View captured leads',fn:"window.open('https://leads.youragency.com','_blank')"}, {k:'crm',l:'Open CRM',s:'Vinculum CRM',fn:"window.open('https://crm.youragency.com','_blank')"}, ]; actions.forEach(a => { if (!q || a.k.includes(q) || a.l.toLowerCase().includes(q)) { results.push({icon:'⚡', main:a.l, sub:a.s, fn:a.fn}); } }); const el = document.getElementById('cmd-results'); el.innerHTML = results.slice(0,8).map((r,i) => `
${r.icon}
${r.main}
${r.sub}
`).join('') || `
NO RESULTS
`; } function cmdKeyNav(e) { const items = document.querySelectorAll('.cmd-result'); if (e.key==='ArrowDown') { cmdIdx=Math.min(cmdIdx+1,items.length-1); highlightCmd(); } else if (e.key==='ArrowUp') { cmdIdx=Math.max(cmdIdx-1,0); highlightCmd(); } else if (e.key==='Enter' && cmdIdx>=0) { items[cmdIdx]?.click(); } } function highlightCmd() { document.querySelectorAll('.cmd-result').forEach((el,i) => el.classList.toggle('active', i===cmdIdx)); } // ═══════════════════════════════════════════════════════════════ // AI ASSISTANT // ═══════════════════════════════════════════════════════════════ let aiOpen = false; function toggleAI() { aiOpen = !aiOpen; document.getElementById('ai-drawer').classList.toggle('open', aiOpen); } function askAI(prompt) { document.getElementById('ai-suggestions').style.display = 'none'; document.getElementById('ai-input').value = prompt; sendAI(); } async function sendAI() { const input = document.getElementById('ai-input'); const msg = input.value.trim(); if (!msg) return; input.value = ''; appendAIMsg('user', msg); // Typing indicator const typingId = 'typing-' + Date.now(); const msgs = document.getElementById('ai-messages'); const typingEl = document.createElement('div'); typingEl.id = typingId; typingEl.className = 'ai-msg from-ai'; typingEl.innerHTML = `
Forma AI
`; msgs.appendChild(typingEl); msgs.scrollTop = msgs.scrollHeight; const context = buildAIContext(); try { const res = await fetch('https://api.ridgelinecrm.com/v1/messages', { method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify({ model:'claude-sonnet-4-20250514', max_tokens:1000, system: `You are Forma AI, an expert in landing page design and conversion optimization for the [Your Agency] suite. You help users build high-converting pages, write compelling copy, and optimize their Ignis lead capture forms. Current workspace data: ${context} Be concise, practical, and focused on conversion optimization. Suggest specific improvements. When writing copy, make it punchy and action-oriented.`, messages:[{role:'user',content:msg}] }) }); const data = await res.json(); typingEl.remove(); const reply = data?.content?.[0]?.text || 'Unable to get response.'; appendAIMsg('ai', reply); } catch(err) { typingEl.remove(); appendAIMsg('ai', 'API error. Check your connection and try again.'); } } function buildAIContext() { const live = state.pages.filter(p=>p.status==='live'); return `Pages: ${state.pages.length} total, ${live.length} live Forms: ${state.forms.length} capture forms ${state.pages.slice(0,5).map(p=>`- "${p.name}" (${p.status}, ${(p.blocks||[]).length} blocks, ${p.visits||0} visits)`).join('\n')}`; } function appendAIMsg(role, text) { const msgs = document.getElementById('ai-messages'); const el = document.createElement('div'); el.className = `ai-msg ${role==='ai'?'from-ai':''}`; el.innerHTML = `
${role==='ai'?'Forma AI':'You'}
${text.replace(/\n/g,'
')}
`; msgs.appendChild(el); msgs.scrollTop = msgs.scrollHeight; }
[YOUR AGENCY]
Suite v1.1
All systems live
· youragency.com
K command palette