129e4d0946
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
84 lines
7.5 KiB
JavaScript
84 lines
7.5 KiB
JavaScript
// Invite / lead-capture page: /join/<token>[?v=<angle>]
|
|
// Email first (code sign-in creates the account), wallet later inside the
|
|
// member area. The angle only changes the hook copy; the sponsor cookie was
|
|
// set by the server when this page was served.
|
|
(function () {
|
|
const $ = id => document.getElementById(id);
|
|
const ANGLES = {
|
|
instant: { eyebrow: 'Same-transaction payouts', h: 'Paid before the page <em>reloads.</em>', lead: 'What if your commission landed before the thank-you page finished loading? On InstantAdPay that is not a metaphor. A smart contract on Polygon splits every ad package the moment it sells.',
|
|
points: ['A verified contract splits every package in the same transaction it sells: 50 percent to the sponsor, 20 and 10 up the line, 20 to the platform.', 'It lands in your own wallet in seconds. There is no balance to withdraw because nothing is ever held.', 'Every payment is public on Polygonscan, so you can check the claim before you spend a dollar.'] },
|
|
adspend: { eyebrow: 'For marketers who buy traffic', h: 'You were buying <em>ads anyway.</em>', lead: 'Every ad dollar you ever spent went one direction: out. Here the ad spend in your line pays you, in the same transaction, on a public ledger. Seven formats, dwell-timed views, packages from $5.',
|
|
points: ['Seven formats: banners, text ads, login ads, solo ads to member inboxes, video, featured links and verified visits.', 'Views are timed on the server, so a real person saw your ad. Banners and text also run across a partner ad network.', 'When anyone in your line buys ads, the contract pays you in that same transaction.'] },
|
|
free: { eyebrow: 'Costs nothing to try', h: 'Watch first. <em>Spend never.</em>', lead: 'Join free, view a few ads, earn credits, and run your first campaign for zero dollars. Upgrade only if you want more reach.',
|
|
points: ['Join with just an email. No password, no wallet today.', 'View a few ads each day and earn credits you can spend on your own banner or text campaign.', 'Buy a package only if you want more reach. They start at $5, and every payout on them is public.'] },
|
|
ledger: { eyebrow: 'No back office', h: 'No back office. <em>No payday.</em>', lead: 'Your last program paid you on the 15th, if it paid you. Here every payout is a public transaction on Polygon you can read yourself, and nothing is ever held.',
|
|
points: ['Every payout is a public transaction on Polygon. Click it, read it, verify it yourself.', 'The contract holds zero balance. It splits and sends in the same transaction, with no pause switch and no upgrade path.', 'No back office means nobody can delay, reverse or review your commission.'] },
|
|
two: { eyebrow: 'The referral side, exactly as coded', h: 'Two buyers open <em>level two.</em>', lead: 'Every direct buyer pays you 50 percent from their first package. Two qualifying buyers open level two, five open level three. Written as constants in a verified contract.',
|
|
points: ['Every direct buyer pays you 50 percent from their very first package.', 'Two qualifying buyers open level two at 20 percent. Five open level three at 10 percent. Constants in a verified contract.', 'Until a level opens, its share climbs to the next qualified member above, so the plan rewards the people who build.'] }
|
|
};
|
|
const v = new URLSearchParams(location.search).get('v');
|
|
const a = v && ANGLES[v];
|
|
if (a) {
|
|
$('jnEyebrow').textContent = '· ' + a.eyebrow; $('jnHead').innerHTML = a.h; $('jnLead').textContent = a.lead;
|
|
// the matching hook video + this angle's three points replace the worked example
|
|
const VID = 'https://coolify-saasytop.nyc3.digitaloceanspaces.com/promo/';
|
|
const vid = $('jnVideo'); vid.src = VID + v + '.mp4'; vid.poster = VID + v + '.jpg';
|
|
$('jnPoints').innerHTML = (a.points || []).map(t => '<li>' + t.replace(/[&<>]/g, c => ({ '&': '&', '<': '<', '>': '>' }[c])) + '</li>').join('');
|
|
$('jnMock').hidden = true; $('jnAngle').hidden = false;
|
|
}
|
|
|
|
async function api(path, body) {
|
|
const r = await (await fetch(path, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body || {}) })).json();
|
|
if (r.error) throw new Error(r.error);
|
|
return r;
|
|
}
|
|
const err = m => { const e = $('jnErr'); e.textContent = m || ''; e.hidden = !m; };
|
|
function busy(btn, fn) {
|
|
return async () => { if (btn.disabled) return; btn.disabled = true; err(''); try { await fn(); } catch (e) { err(e.message || 'Something went wrong.'); } finally { btn.disabled = false; } };
|
|
}
|
|
const send = busy($('jnSend'), async () => {
|
|
const r = await api('/api/auth/email/start', { email: $('jnEmail').value });
|
|
$('jnCodeRow').hidden = false; $('jnVerify').hidden = false; $('jnSend').hidden = true; $('jnResend').hidden = false;
|
|
if (r.devCode) $('jnCode').value = r.devCode;
|
|
// the last thing they see before the account is created: who they are joining under
|
|
if (sponsorName) { $('jnUnder').textContent = 'Joining under ' + sponsorName + '. Not who invited you? Open their invite link first, then come back for the code.'; $('jnUnder').hidden = false; }
|
|
IAP.status(r.sent ? 'Code sent. Check your inbox (and spam, the first time).' : 'Dev mode: code filled in.', 'ok');
|
|
$('jnCode').focus();
|
|
});
|
|
$('jnSend').addEventListener('click', send);
|
|
$('jnResend').addEventListener('click', busy($('jnResend'), async () => {
|
|
const r = await api('/api/auth/email/start', { email: $('jnEmail').value });
|
|
if (r.devCode) $('jnCode').value = r.devCode;
|
|
IAP.status('Fresh code sent.', 'ok');
|
|
}));
|
|
$('jnVerify').addEventListener('click', busy($('jnVerify'), async () => {
|
|
await api('/api/auth/email/verify', { email: $('jnEmail').value, code: $('jnCode').value, newsletter: !!$('jnNews').checked, followups: !!$('jnNews').checked });
|
|
IAP.status('You are in. Taking you to your dashboard…', 'ok');
|
|
location.href = '/my?welcome=1'; // dashboard runs the username step + welcome tour on arrival
|
|
}));
|
|
$('jnEmail').addEventListener('keydown', e => { if (e.key === 'Enter') ($('jnVerify').hidden ? $('jnSend') : $('jnVerify')).click(); });
|
|
$('jnCode').addEventListener('keydown', e => { if (e.key === 'Enter') $('jnVerify').click(); });
|
|
|
|
// sponsor line (the link opened most recently sets the sponsor; it locks at account creation)
|
|
let sponsorName = '';
|
|
fetch('/api/sponsor').then(r => r.json()).then(sp => {
|
|
if (sp && sp.invited && sp.name) {
|
|
sponsorName = sp.name;
|
|
$('jnSponName').textContent = sp.name;
|
|
if (sp.avatarUrl) { $('jnSponImg').src = sp.avatarUrl; $('jnSponImg').hidden = false; }
|
|
$('jnSpon').hidden = false;
|
|
}
|
|
}).catch(() => {});
|
|
// package ladder (dollar constants + live POL quote)
|
|
const NAMES = { 1: 'Micro', 2: 'Activation', 3: 'Builder', 4: 'Growth', 5: 'Leader' };
|
|
fetch('/api/catalog').then(r => r.json()).then(c => {
|
|
const wrap = $('jnLadder'); if (!wrap || !c.products) return;
|
|
wrap.innerHTML = c.products.filter(p => p.active !== false).map(p =>
|
|
'<div class="jn-pk"><div class="n">' + (NAMES[p.id] || 'Package ' + p.id) + '</div><div class="p">' + IAP.fmtUsd(p.priceCents) + '</div><div class="c">' + Number(p.creditAmount).toLocaleString() + ' credits</div>'
|
|
+ (p.costWei ? '<div class="small muted">' + IAP.fmtPol(p.costWei) + ' POL now</div>' : '') + '</div>').join('');
|
|
}).catch(() => {});
|
|
fetch('/api/stats').then(r => r.json()).then(s => {
|
|
if (s && s.onchainMembers) $('jnStats').textContent = s.onchainMembers.toLocaleString() + (s.onchainMembers === 1 ? ' member' : ' members') + ' on-chain so far.';
|
|
}).catch(() => {});
|
|
})();
|