7bb1a78ca3
Hero now reads Advertise and earn instantly. New Branded Voice sections: ad-format showcase (3 live, 3 soon) and free-vs-package member benefits. The earn-deeper diagram cycles levels 1-3, lighting the paying generation with its percent; chips are clickable, auto-advance respects reduced motion. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
102 lines
4.9 KiB
JavaScript
102 lines
4.9 KiB
JavaScript
// Landing page: live ladder, buy buttons, sponsor attribution line.
|
||
(async function () {
|
||
await IAP.renderNav('home');
|
||
const c = await IAP.getConfig();
|
||
IAP.$('contractLink').href = c.explorer + '/address/' + c.contract;
|
||
|
||
const sp = await (await fetch('/api/sponsor')).json();
|
||
if (sp.sponsorId) {
|
||
const el = IAP.$('sponsorLine');
|
||
el.hidden = false;
|
||
el.textContent = 'You were invited by member #' + sp.sponsorId + '. Your purchases pay their team, and your own link will do the same for you.';
|
||
}
|
||
|
||
async function loadLadder() {
|
||
const { products } = await (await fetch('/api/catalog')).json();
|
||
const wrap = document.getElementById('tiles');
|
||
wrap.innerHTML = '';
|
||
const NAMES = { 1: 'Micro', 2: 'Activation', 3: 'Builder', 4: 'Growth', 5: 'Leader' };
|
||
for (const p of products) {
|
||
const bonus = p.creditAmount - p.priceCents; // credits above 1cr/cent = bulk bonus
|
||
const div = document.createElement('div');
|
||
div.className = 'tile' + (p.priceCents === 5000 ? ' hot' : '');
|
||
div.innerHTML = '<div class="name">' + (NAMES[p.id] || 'Package ' + p.id) + '</div>'
|
||
+ '<div class="price">$' + Math.round(p.priceCents / 100) + '</div>'
|
||
+ '<div class="cr">' + p.creditAmount.toLocaleString() + ' credits</div>'
|
||
+ '<div class="bonus">' + (bonus > 0 ? '+' + bonus.toLocaleString() + ' bonus credits' : ' ') + '</div>'
|
||
+ '<div class="pol">' + (p.costWei ? IAP.fmtPol(p.costWei) + ' POL right now' : 'paused') + '</div>'
|
||
+ '<button class="btn small" data-id="' + p.id + '" data-cost="' + (p.costWei || '') + '"'
|
||
+ (p.costWei ? '' : ' disabled') + '>Buy</button>';
|
||
wrap.appendChild(div);
|
||
}
|
||
wrap.querySelectorAll('button[data-id]').forEach(b => b.addEventListener('click', () => buyPack(b)));
|
||
}
|
||
async function buyPack(btn) {
|
||
try {
|
||
btn.disabled = true;
|
||
// email members get their wallet linked to the account at buy time
|
||
const me = await (await fetch('/api/me')).json();
|
||
if (me.signedIn && me.email && !me.address) {
|
||
IAP.status('First, a free signature links your wallet to your account…');
|
||
await IAPWallet.signIn();
|
||
}
|
||
IAP.status('Confirm the purchase in your wallet…');
|
||
const r = await IAPWallet.buy(Number(btn.dataset.id), sp.sponsorId || 0, btn.dataset.cost);
|
||
if (r.receipt.status !== '0x1') throw new Error('Transaction reverted. Check the explorer.');
|
||
IAP.status('Purchase settled on-chain. Credits are yours, payouts delivered. Watch it on the ledger.', 'ok');
|
||
IAP.refreshNavWallet();
|
||
} catch (e) {
|
||
IAP.status('Purchase failed: ' + (e.message || e), 'bad');
|
||
} finally { btn.disabled = false; }
|
||
}
|
||
async function loadStats() {
|
||
try {
|
||
const s = await (await fetch('/api/stats')).json();
|
||
IAP.$('stMembers').textContent = (s.onchainMembers || 0).toLocaleString();
|
||
IAP.$('stPurchases').textContent = (s.purchases || 0).toLocaleString();
|
||
IAP.$('stPaid').textContent = IAP.fmtPol(s.paidInWei || '0');
|
||
IAP.$('stPayouts').textContent = (s.payouts || 0).toLocaleString();
|
||
} catch (e) {}
|
||
}
|
||
async function loadTicker() {
|
||
try {
|
||
const { events } = await (await fetch('/api/feed?n=30')).json();
|
||
if (!events.length) return;
|
||
const inner = IAP.$('tickerInner');
|
||
inner.innerHTML = events.map(ev => '<span>' + IAP.describeEvent(ev, c) + '</span>').join('');
|
||
IAP.$('ticker').hidden = false;
|
||
} catch (e) {}
|
||
}
|
||
// level cycler: chips + generation highlighting + auto-advance
|
||
const LVL = {
|
||
1: { pct: '50%', desc: 'Level 1 is open to every member: your direct referrals each pay you 50 percent of every package they ever buy, straight to your wallet.' },
|
||
2: { pct: '20%', desc: 'Bring 2 buyers of $20 or more and level 2 unlocks: 20 percent of every package your referrals’ referrals buy, on every purchase, forever.' },
|
||
3: { pct: '10%', desc: 'At 5 qualifying buyers, level 3 opens the third generation: 10 percent of everything they buy. Eight positions deep in this picture, and it keeps growing.' }
|
||
};
|
||
const viz = document.getElementById('genViz');
|
||
if (viz) {
|
||
const chips = [...document.querySelectorAll('.chips [data-lvl]')];
|
||
const setLvl = n => {
|
||
viz.dataset.lvl = n;
|
||
document.getElementById('vizPct').textContent = LVL[n].pct;
|
||
document.getElementById('lvlDesc').textContent = LVL[n].desc;
|
||
chips.forEach(ch => ch.classList.toggle('on', ch.dataset.lvl === String(n)));
|
||
};
|
||
let cur = 1;
|
||
let auto = null;
|
||
if (!matchMedia('(prefers-reduced-motion: reduce)').matches) {
|
||
auto = setInterval(() => { cur = cur % 3 + 1; setLvl(cur); }, 4200);
|
||
}
|
||
chips.forEach(ch => ch.addEventListener('click', () => {
|
||
if (auto) { clearInterval(auto); auto = null; } // a click takes the wheel
|
||
cur = Number(ch.dataset.lvl);
|
||
setLvl(cur);
|
||
}));
|
||
}
|
||
|
||
loadLadder();
|
||
loadStats();
|
||
loadTicker();
|
||
setInterval(loadStats, 60000);
|
||
})();
|