f05d5a2746
Completes the ladder — no tier is a placeholder now. Every tier below builds one thing at a time, which is right for someone still finding their words. At the top the constraint is different: these are people running organisations who don't have an afternoon to spend clicking. So one pass produces five social posts across five different angles, three outreach messages (first approach, follow-up, pyramid-scheme answer), an email, and three text ads — all carrying their link, all in the member's own voice profile if they have one. Partial failures are reported rather than hidden: if the engine can't finish a section the pack says which, instead of quietly handing over a short week. API access is real, not a label: a personal key over GET /me, GET /team, POST /generate. Keys are stored hashed and shown exactly once, compared in constant time, and every call goes through the same meters and compliance rules as the web tools. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
127 lines
9.3 KiB
JavaScript
127 lines
9.3 KiB
JavaScript
// The Circle Suite — P0 tool wall. Wallet sign-in (same challenge/verify flow
|
||
// as the training gate) -> live level from the contract -> tiles light up.
|
||
// Every tile is always VISIBLE: unlocked at-or-below the member's level,
|
||
// dimmed above it. Future tools carry an honest "in development" tag.
|
||
(function () {
|
||
'use strict';
|
||
var LEVELS = ['Scintilla', 'Ascensus', 'Fabrica', 'Culmen', 'Apex', 'Fastigium', 'Vertex', 'Corona'];
|
||
var TOOLS = [
|
||
{ lv: 1, ico: '🎬', name: 'Promo Center', desc: 'Videos, posts, swipes, emails and the banner kit — every piece pre-personalized with your link.', href: '/tools', live: true },
|
||
{ lv: 1, ico: '🖨️', name: 'Printable Handouts', desc: 'Five full-color designs that print two per page — with YOUR QR code on the paper.', href: '/flyers', live: true },
|
||
{ lv: 1, ico: '✅', name: 'Fast Start + Weekly Rhythm', desc: 'The 48-hour launch checklist and the 20-minute weekly routine, personalized and printable.', href: '/fast-start', live: true },
|
||
{ lv: 1, ico: '📊', name: 'Generation Pay Chart', desc: 'Your personal when-does-each-generation-pay-me chart, read live from the contract.', href: '/generation-pay', live: true },
|
||
{ lv: 1, ico: '🎓', name: 'The Circle Method', desc: 'The 10-lesson recruiting and coaching course. Lessons unlock with this same wallet.', href: '/training', live: true },
|
||
{ lv: 1, ico: '📈', name: 'Live Dashboard', desc: 'Your pipeline, organization bar, coach panel and wallet-verified team messages.', href: '/my', live: true },
|
||
{ lv: 1, ico: '🤖', name: 'AI Coach', desc: '24/7 answers about the system, in 21+ languages — the chat bubble on every page of this site.', href: '#', live: true, coach: true },
|
||
{ lv: 2, ico: '✍️', name: 'Copy Engine', desc: 'An AI copywriter tuned to this business: posts, DMs, follow-ups and objection replies with your link filled in.', href: '/suite/copy', live: true },
|
||
{ lv: 2, ico: '🧱', name: 'Page Builder', desc: 'Answer four questions, get your own hosted invitation page — your story, your angle video, your QR.', href: '/suite/page', live: true },
|
||
{ lv: 3, ico: '📧', name: 'Email Engine', desc: 'Welcome series, follow-up sequences and broadcasts in the team voice — exportable to any autoresponder.', href: '/suite/email', live: true },
|
||
{ lv: 3, ico: '🎥', name: 'Video Maker', desc: 'The team’s master promo videos rendered with your personal end-card — your name, your QR, your link.', href: '/suite/video', live: true },
|
||
{ lv: 4, ico: '🗣️', name: 'Voice Profile', desc: 'Teach the Copy Engine and Email Engine how you actually talk, so everything they write for you stops sounding like a company.', href: '/suite/voice', live: true },
|
||
{ lv: 5, ico: '🔬', name: 'Split Tester', desc: 'Run two or three versions of an ad head to head and find out which wording people actually click. Apex is where your impressions jump to 50,000 — enough for the answer to mean something.', href: '/suite/split', live: true },
|
||
{ lv: 1, ico: '🚦', name: 'Traffic Desk', desc: 'Syndicated network display advertising — run banners or AI-written text ads on the team’s own ad network. Monthly impressions scale with your level: 2,500 at Scintilla up to 150,000 at Corona.', href: '/suite/traffic', live: true },
|
||
{ lv: 6, ico: '🏭', name: 'Funnel Factory', desc: 'A separate hosted landing page for every audience you talk to — each on its own address, each one something you can point a different ad at.', href: '/suite/funnel', live: true },
|
||
{ lv: 7, ico: '🧭', name: 'Leader Ops', desc: 'Everyone below you, read live from the contract and sorted by who needs you most — plus a written coaching plan you can teach forward to your own two.', href: '/suite/leader', live: true },
|
||
{ lv: 8, ico: '👑', name: 'Founder Desk', desc: 'A whole week of promotion built in one pass — posts, outreach messages, an email and text ads — plus an API key to pull your position, your organisation and the writer into anything you already run.', href: '/suite/founder', live: true }
|
||
];
|
||
|
||
var $ = function (id) { return document.getElementById(id); };
|
||
var me = null; // {id, level, levelName, tierName, directCount, inOrg}
|
||
|
||
function pips() {
|
||
var host = $('suPips');
|
||
host.querySelectorAll('.su-pip').forEach(function (p) { p.remove(); });
|
||
for (var k = 1; k <= 8; k++) {
|
||
var p = document.createElement('span');
|
||
p.className = 'su-pip' + (me && me.inOrg && me.level >= k ? (k === 8 ? ' gold' : ' on') : '');
|
||
p.title = 'Level ' + k + ' · ' + LEVELS[k - 1];
|
||
host.appendChild(p);
|
||
}
|
||
}
|
||
|
||
function render() {
|
||
var grid = $('suGrid');
|
||
grid.innerHTML = '';
|
||
var lastLv = 0;
|
||
TOOLS.forEach(function (t) {
|
||
if (t.lv !== lastLv) {
|
||
lastLv = t.lv;
|
||
var h = document.createElement('div');
|
||
h.className = 'su-lvhead';
|
||
h.innerHTML = '<span class="n">L' + t.lv + '</span><span class="nm">' + LEVELS[t.lv - 1] + '</span><span class="rule"></span>';
|
||
grid.appendChild(h);
|
||
}
|
||
var open = !!(me && me.inOrg && me.level >= t.lv);
|
||
var d = document.createElement('div');
|
||
d.className = 'su-tile ' + (me ? (open ? 'open' : 'locked') : 'locked');
|
||
var badge = me
|
||
? (open ? '✓ YOURS' : 'UNLOCKS AT ' + LEVELS[t.lv - 1].toUpperCase())
|
||
: 'LEVEL ' + t.lv;
|
||
d.innerHTML = '<span class="badge">' + badge + '</span><span class="ico">' + t.ico + '</span><h3>' + t.name + '</h3><p>' + t.desc + '</p>'
|
||
+ (t.live
|
||
? (open || !me ? '<a class="go" href="' + t.href + '"' + (t.coach ? ' data-coach="1"' : '') + '>Open →</a>' : '')
|
||
: '<span class="dev">IN DEVELOPMENT — ships with the Suite build-out</span>');
|
||
grid.appendChild(d);
|
||
});
|
||
var coach = grid.querySelector('[data-coach]');
|
||
if (coach) coach.addEventListener('click', function (ev) { ev.preventDefault(); var f = document.querySelector('.chat-fab'); if (f) f.click(); });
|
||
}
|
||
|
||
async function refreshMe() {
|
||
try {
|
||
var r = await fetch('/api/public/suite-me');
|
||
if (!r.ok) return false;
|
||
me = await r.json();
|
||
if (me.beta && !me.allowed) {
|
||
$('suMe').style.display = 'block';
|
||
$('suMe').innerHTML = '<b>#' + me.id + '</b> — you’re early! The Circle Suite is in <b>team beta</b> right now. Your license is already reserved by your position, and every tool below activates for you the moment we open the doors. Nothing to do — watch the team channel.';
|
||
$('suConnect').style.display = 'none';
|
||
me = null; pips(); render();
|
||
return true;
|
||
}
|
||
$('suMe').style.display = 'block';
|
||
$('suMe').innerHTML = me.inOrg
|
||
? '<b>#' + me.id + '</b> · ' + me.tierName + ' · Level ' + me.level + ' (' + me.levelName + ')' +
|
||
(me.directCount >= 2 ? ' · <span style="color:var(--teal)">✓ qualified</span>' : ' · ' + me.directCount + '/2 directs') +
|
||
'<br>Your license covers <b>Level ' + me.level + '</b> and everything below it' +
|
||
(me.level < 8 ? ' — your next upgrade extends it to ' + LEVELS[me.level] + '.' : ' — full Suite.')
|
||
: '<b>#' + me.id + '</b> — this position isn’t inside our team’s organization. The Suite is a benefit of building with our circle; ask the person who shared this page about joining through the team.';
|
||
$('suConnect').style.display = 'none';
|
||
pips(); render();
|
||
return true;
|
||
} catch (e) { return false; }
|
||
}
|
||
|
||
async function connect() {
|
||
var btn = $('suConnect'), err = $('suErr');
|
||
err.style.display = 'none';
|
||
btn.disabled = true; btn.textContent = 'Connecting…';
|
||
try {
|
||
var eth = await window.RMCWallet.pick();
|
||
if (!eth) { err.textContent = 'No wallet found in this browser. On a phone, open this page inside your wallet app’s browser (the DApps tab) — type rmcircle.team/suite there.'; err.style.display = 'block'; btn.disabled = false; btn.textContent = '🔑 Connect wallet — light up my tools'; return; }
|
||
var accs = await eth.request({ method: 'eth_requestAccounts' });
|
||
var account = accs[0];
|
||
try { await window.RMCWallet.ensureChain(eth); } catch (ce) {}
|
||
var ch = await (await fetch('/api/public/msg-challenge', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ address: account }) })).json();
|
||
if (!ch.message) throw new Error(ch.error || 'Could not start sign-in.');
|
||
var sig = await eth.request({ method: 'personal_sign', params: [ch.message, account] });
|
||
var vr = await (await fetch('/api/public/msg-verify', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ address: account, signature: sig }) })).json();
|
||
if (!vr.ok) throw new Error(vr.error || 'Signature check failed.');
|
||
await refreshMe();
|
||
} catch (e) {
|
||
var m = String((e && e.message) || e);
|
||
err.textContent = /not registered|no position/i.test(m)
|
||
? 'This wallet doesn’t hold a position yet. The Suite comes with one — see the Getting Started page.'
|
||
: 'Sign-in didn’t complete: ' + m + ' — tap the button to try again.';
|
||
err.style.display = 'block';
|
||
}
|
||
btn.disabled = false; btn.textContent = '🔑 Connect wallet — light up my tools';
|
||
}
|
||
|
||
document.addEventListener('DOMContentLoaded', function () {
|
||
pips(); render();
|
||
$('suConnect').addEventListener('click', connect);
|
||
refreshMe(); // silent: already signed in (training gate / Mini App bridge)
|
||
});
|
||
})();
|