// Personal invite page (/join/): the full bridge-page story, personalized // to one sponsor. Pulls the sponsor's live position via /api/public/member, // pins their ID on every CTA, and submits new IDs with sponsorId locked to // the page. Prospects who land on a bad/unregistered ID get routed to /start // (the team rotation) instead of a dead end. (function(){ const esc=s=>String(s??'').replace(/[&<>'"]/g,c=>({'&':'&','<':'<','>':'>',"'":''','"':'"'}[c])); const fmt=n=>Number(n||0).toLocaleString(undefined,{maximumFractionDigits:2}); const date=ts=>ts?new Date(ts*1000).toLocaleDateString(undefined,{year:'numeric',month:'short',day:'numeric'}):'—'; function pathId(){const m=location.pathname.match(/^\/join\/(\d+)$/);return m?m[1]:''} const id=pathId(); // Direct link (?direct=1): opt out of moving-link rotation — new joins land // directly under THIS position (spillover in their team), instead of routing // to the next-to-qualify member. Default (no flag) = the Team/moving link. const _dq=new URLSearchParams(location.search).get('direct'); // ?direct=1 forces direct placement, ?direct=0 forces rotation. With // neither, the position's own configured default decides (applied once // the member data arrives, below). let direct=_dq==='1'; let joinTargetId=id; // where the entry actually lands after moving-link routing function setIds(idStr){ document.getElementById('invId').textContent='#'+idStr; document.getElementById('invIdJoin').textContent='#'+idStr; document.getElementById('invIdBtn').textContent='#'+idStr; document.querySelectorAll('.inv-id-inline').forEach(el=>el.textContent='#'+idStr); } function fallbackToRotation(msg){ const facts=document.getElementById('invFacts'); facts.innerHTML=`
${esc(msg)} Open the team rotation instead →
`; const jb=document.getElementById('joinBtn');jb.textContent='Open the Team Sponsor Page →';jb.href='/start';jb.removeAttribute('target'); } async function load(){ if(!id){fallbackToRotation('This invite link is missing its member ID.');return} setIds(id); try{ const r=await fetch('/api/public/member?id='+id); const d=await r.json(); if(!r.ok||!d.registered)throw new Error('This invite ID isn’t registered on the smart contract.'); render(d); }catch(x){fallbackToRotation(x.message)} } function render(d){ const q=document.getElementById('invQualified'); q.outerHTML=d.directCount>=2 ?'★ Qualified' :`Building — ${d.directCount}/2 directs`; document.getElementById('invFacts').innerHTML= `
Level${esc(d.levelName)}
`+ `
Total received${fmt(d.totalEarnedPol)} POL
`+ `
Team below them${d.subtree?`${d.subtree.downCount} member${d.subtree.downCount===1?'':'s'}`:'—'}
`+ `
Directs${d.directCount}/2
`+ `
Tier${esc(d.tierName)}
`+ `
Member since${date(d.joinedAt)}
`; // Qualified sponsor: still a valid join (doctrine: late signups pay them // and spill down), but surface the team wave so nobody's surprised. // Moving-link routing: the entry lands on whoever the smart link resolves to. // A qualified inviter's link routes to the next-to-qualify in their leg, so // the join builds the team down in order instead of spilling onto the inviter. if(_dq!=='1'&&_dq!=='0'&&d.directDefault) direct=true; const t=direct?{id:id,reason:'direct'}:(d.joinTarget||{id:d.id,referralUrl:d.referralUrl,reason:'self'}); joinTargetId=t.id; const jb=document.getElementById('joinBtn'); jb.href='/join-now?ref='+id+(direct?'&direct=1':''); jb.removeAttribute('target'); // self-enroll page, placed in this leg // the join CTA + "join under" header + dApp-sponsor mentions reflect the // ACTUAL sponsor the entry lands on, not the inviter (only the top // "invited by #X" line keeps the inviter's id) document.getElementById('invIdBtn').textContent='#'+t.id; document.getElementById('invIdJoin').textContent='#'+t.id; document.querySelectorAll('.inv-id-inline').forEach(el=>el.textContent='#'+t.id); const note=document.getElementById('invQualNote'); if(direct){ note.innerHTML=`
You're joining directly under Member #${esc(id)} — a spillover placement in their team. Enter sponsor #${esc(id)} in the dApp.
`; }else if(t.reason==='leg'||t.reason==='global'){ note.innerHTML=`
You were invited by Member #${esc(d.id)}, who's already qualified. So your entry goes to Member #${esc(t.id)} — the next spot in the team that needs its 2 — and you'll join directly under them. Same team, filling the next open position in order (no spillover). Enter sponsor #${esc(t.id)} in the dApp.
`; }else if(d.directCount>=2){ note.innerHTML=`
This position is already qualified — joining here still works: your entry pays them and you're placed in their team leg as depth.
`; } const cp=document.getElementById('copySponsor'); cp.addEventListener('click',async()=>{try{await navigator.clipboard.writeText(String(joinTargetId));cp.textContent='Copied ✓';setTimeout(()=>cp.textContent='Copy Sponsor ID',1400)}catch(e){}}); } document.getElementById('joinForm').addEventListener('submit',async e=>{ e.preventDefault(); const form=e.currentTarget,msg=document.getElementById('joinMsg'); const newId=form.elements.newId.value.trim(),memberName=form.elements.memberName.value.trim(); if(!/^\d{1,10}$/.test(newId)){msg.style.color='var(--danger)';msg.textContent='Numbers only — the ID shown by the RM Circle dApp.';return} if(!memberName){msg.style.color='var(--danger)';msg.textContent='Add your name or Telegram handle.';return} msg.style.color='var(--muted)';msg.textContent='Verifying on the blockchain…'; try{ const r=await fetch('/api/public/submit-id',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({newId,memberName,sponsorId:joinTargetId||id||'?',source:'invite-'+(id||'?'),clickid:window.ctbGetClickId?window.ctbGetClickId():''})}); const d=await r.json(); if(!r.ok)throw new Error(d.error||'Submission failed'); form.classList.add('hidden'); msg.style.color='var(--ok)'; const dash=`
Open your own dashboard →`; if(d.duplicate)msg.innerHTML=`✓ ID ${esc(newId)} was already submitted — you're on the list.${dash}`; else if(d.onchain&&d.onchain.registered)msg.innerHTML=`✓ Verified on the blockchain! ID ${esc(newId)} is registered under sponsor #${d.onchain.referrerId} (${esc(d.onchain.tier)}). The team has been notified — welcome aboard.${dash}`; else if(d.path==='notfound'){msg.style.color='var(--danger)';msg.innerHTML=`⚠ ID ${esc(newId)} isn't on the smart contract yet — double-check the number from the RM Circle dApp. Your submission is saved and the team will verify it manually.`;} else msg.innerHTML=`✓ Got it — submitted. The team will verify your placement.${dash}`; }catch(x){msg.style.color='var(--danger)';msg.textContent=x.message} }); // matrix dots (same as bridge) for(const pid of ['p8','p16']){ const node=document.getElementById(pid);if(!node)continue; const count=pid==='p8'?8:16;for(let i=0;i renders a NON-DISTRACTING squeeze step — // matched headline, the hook video, one proceed button. Nothing else. Clicking // through loads the full invite page (same path, no query); attribution // (angle:) is already stamped in sessionStorage by track.js. (function(){ var ANGLES={ pocket:{h:'You already found the money.', v:'/v/rmc-pocket-change-b403fb2f75.mp4',p:'/v/rmc-pocket-change-poster.jpg'}, phone:{h:'Time your phone started paying YOU.', v:'/v/rmc-phone-32ac648844.mp4',p:'/v/rmc-phone-poster.jpg'}, two:{h:'You know two people.
That’s all this takes.', v:'/v/rmc-two-people-4102f2d719.mp4',p:'/v/rmc-two-people-poster.jpg'}, graveyard:{h:'This one runs on teamwork — not your transmission.', v:'/v/rmc-graveyard-290fabee9d.mp4',p:'/v/rmc-graveyard-poster.jpg'}, stopwaiting:{h:'Stop waiting. Start receiving.', v:'/v/rmc-combined-16284edbab.mp4',p:'/v/rmc-combined-poster.jpg'} }; var a=ANGLES[new URLSearchParams(location.search).get('v')]; if(!a)return; var main=document.querySelector('main'); if(!main)return; // clear the stage: hide everything, including the nav's competing buttons Array.prototype.forEach.call(main.children,function(el){el.style.display='none';}); var nav=document.querySelector('.nav-actions'); if(nav)nav.style.display='none'; var s=document.createElement('section'); s.className='hero wrap'; s.innerHTML='

'+a.h+'

'+ '
'+ '
'+ ''+ '
Independent team training resource • Participation involves risk • No income is guaranteed
'; main.appendChild(s); var hp=document.getElementById('hookPlayer'),cta=document.getElementById('hookCta'); if(hp&&cta)hp.addEventListener('ended',function(){ cta.style.boxShadow='0 0 0 4px rgba(243,190,67,.45)'; cta.scrollIntoView({behavior:'smooth',block:'center'}); }); })();