(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'}):'—';
const prompt=document.getElementById('idPrompt'),dash=document.getElementById('dash');
function pathId(){const m=location.pathname.match(/^\/my\/(\d+)$/);if(m)return m[1];const q=new URLSearchParams(location.search).get('id');if(q&&/^\d+$/.test(q))return q;try{return localStorage.getItem('ctb.myId')||''}catch(e){return ''}}
const treeUI={byId:{},parent:{},homeId:0,rootId:0,mode:'pyramid'};
function indexTree(n,par){if(!n)return;treeUI.byId[n.id]=n;if(par)treeUI.parent[n.id]=par.id;indexTree(n.left,n);indexTree(n.right,n)}
function card(n,focus){
if(!n)return '
open slot
';
const badge=n.tier===2?'P ':'S ';
const qmark=n.directCount>=2?'✓ ':'';
const down=n.downCount?`⬇ ${n.downCount} below · ${fmt(n.downPol)} POL `:'no downline yet ';
return `${qmark}${badge} #${n.id} ${esc(n.levelName)} ${n.directCount}/2 · ${fmt(n.earnedPol)} POL ${down} `;
}
function renderPyramid(){
const out=document.getElementById('dTree'),nav=document.getElementById('dTreeNav');
const root=treeUI.byId[treeUI.rootId];
if(!root){out.innerHTML='Team view is still indexing — check back in a few minutes.
';nav.innerHTML='';return}
const rows=[[root]];
for(let g=1;g<4;g++)rows.push(rows[g-1].flatMap(n=>n?[n.left||null,n.right||null]:[null,null]));
if(!rows[3].some(Boolean))rows.pop();
out.innerHTML=`${rows.map((r,i)=>`
${r.map(n=>card(n,i===0)).join('')}
`).join('')}
`;
const path=[];let cur=treeUI.rootId;
while(cur){path.unshift(cur);if(cur===treeUI.homeId)break;cur=treeUI.parent[cur]}
nav.innerHTML=path.length>1?`Viewing: `+path.map((id,i)=>i===path.length-1?`#${id} `:`#${id} `).join(' › ')+` ↑ Up one `:'';
const wire=el=>el.querySelectorAll('[data-tid]').forEach(b=>b.addEventListener('click',()=>{const t=Number(b.dataset.tid);if(t&&treeUI.byId[t]){treeUI.rootId=t;renderTree()}}));
wire(out);wire(nav);
}
function renderList(){
const out=document.getElementById('dTree');
document.getElementById('dTreeNav').innerHTML='';
const home=treeUI.byId[treeUI.homeId];
if(!home){out.innerHTML='Team view is still indexing — check back in a few minutes.
';return}
const node=(n,depth)=>{
if(!n)return '';
const ch=[n.left,n.right].filter(Boolean);
const qmark=n.directCount>=2?'✓ ':'';
const badge=n.tier===2?'P ':'S ';
const label=`${qmark}${badge} #${n.id} ${esc(n.levelName)} · ${n.directCount}/2 directs · ${fmt(n.earnedPol)} POL${n.downCount?` · ⬇ ${n.downCount} below (${fmt(n.downPol)} POL)`:''} `;
const kids=ch.length?`${ch.map(c=>node(c,depth+1)).join('')} `:'';
return ch.length?`${label} ${kids} `:`${label} `;
};
out.innerHTML=``;
}
function renderTree(){
const tb=document.getElementById('dTreeToggle');
if(tb)tb.textContent=treeUI.mode==='pyramid'?'List view':'Pyramid view';
if(treeUI.mode==='pyramid')renderPyramid();else renderList();
}
document.getElementById('dTreeToggle').addEventListener('click',()=>{
treeUI.mode=treeUI.mode==='pyramid'?'list':'pyramid';
renderTree();
});
function render(d){
prompt.classList.add('hidden');dash.classList.remove('hidden');
document.getElementById('dTitle').textContent='#'+d.id;
const qb=document.getElementById('dQualified');
if(qb)qb.outerHTML=d.directCount>=2
?'★ Qualified '
:`${2-d.directCount} more direct${2-d.directCount===1?'':'s'} to qualify `;
document.getElementById('dFacts').innerHTML=
`Tier ${esc(d.tierName)}
`+
`Level ${esc(d.levelName)}
`+
`Directs ${d.directCount}/2${d.directCount>=2?' ✓ qualified':''}
`+
`Total received ${fmt(d.totalEarnedPol)} POL
`+
`Team below you ${d.subtree?`${d.subtree.downCount} member${d.subtree.downCount===1?'':'s'}`:'—'}
`+
`Member since ${date(d.joinedAt)}
`;
treeUI.byId={};treeUI.parent={};treeUI.homeId=d.id;treeUI.rootId=d.id;
indexTree(d.subtree,null);
renderTree();
const inc=d.income||[];
document.getElementById('dIncome').innerHTML=inc.length
?`When From member For Amount ${inc.map(p=>`${date(p.ts)} #${p.fromId} ${esc(p.desc)} ${fmt(p.pol)} POL `).join('')}
`
:'No payments yet — they appear here the moment they land on-chain.
';
renderShare(d);
document.getElementById('dLineage').innerHTML=d.uplineChain&&d.uplineChain.length
?`#${d.id} → ${d.uplineChain.map(i=>'#'+i).join(' → ')} (root) `
:'You are at the top of your line.';
}
function qrSvg(url){
try{
const q=qrcode(0,'M');q.addData(url);q.make();
return q.createSvgTag({cellSize:4,margin:3,scalable:true});
}catch(e){return ''}
}
function renderShare(d){
const el=document.getElementById('dShare');
if(!el)return;
const dashUrl=`https://rmcircle.saasy.top/my/${d.id}`;
if(d.directCount>=2){
const nx=d.nextInLine;
if(nx){
const nxDash=`https://rmcircle.saasy.top/my/${nx.id}`;
el.innerHTML=`★ Qualified Your link is retired — now the effort moves down. Next in line: #${nx.id} (${nx.directCount}/2 directs). Help them get their 2 — share their page or send prospects the join button, which carries #${nx.id} as sponsor.
${esc(nxDash)}
When #${nx.id} reaches 2/2 this rotates to the next position in your leg — the qualification wave keeps moving down. General traffic can still go through the team rotation .
`;
const qr=document.getElementById('dQr');if(qr)qr.innerHTML=qrSvg(nxDash);
const cp=document.getElementById('copyShare');
if(cp)cp.addEventListener('click',async()=>{try{await navigator.clipboard.writeText(nxDash);cp.textContent='Copied ✓';setTimeout(()=>cp.textContent=`Copy #${nx.id}'s page link`,1400)}catch(e){}});
return;
}
el.innerHTML=`★ Qualified This position has its 2 directs and everyone in your leg is qualified too — outstanding. Send new members through the team rotation — the current sponsor is always live on the start page.
`;
return;
}
const need=2-d.directCount;
el.innerHTML=`You need ${need} more direct${need===1?'':'s'} to qualify. Until then this is your personal recruiting page — share the link or let a prospect scan the code. They'll see this position's live results, and the join button below carries your ID #${d.id} as sponsor.
${esc(dashUrl)}
When you reach 2/2 this page automatically retires your link and points new members to the team rotation.
`;
const qr=document.getElementById('dQr');if(qr)qr.innerHTML=qrSvg(dashUrl);
const cp=document.getElementById('copyShare');
if(cp)cp.addEventListener('click',async()=>{try{await navigator.clipboard.writeText(dashUrl);cp.textContent='Copied ✓';setTimeout(()=>cp.textContent='Copy my page link',1400)}catch(e){}});
}
async function load(id){
try{
const r=await fetch('/api/public/member?id='+id);
const d=await r.json();
if(!r.ok)throw new Error(d.error||'Lookup failed');
if(!d.registered)throw new Error(`ID ${id} isn't registered on the smart contract — double-check the number.`);
try{localStorage.setItem('ctb.myId',String(id))}catch(e){}
if(!/^\/my\/\d+$/.test(location.pathname))history.replaceState(null,'','/my/'+id);
render(d);
}catch(x){
prompt.classList.remove('hidden');dash.classList.add('hidden');
document.getElementById('idError').textContent=x.message;
}
}
document.getElementById('idForm').addEventListener('submit',e=>{
e.preventDefault();
const v=document.getElementById('memberId').value.trim();
if(!/^\d{1,15}$/.test(v)){document.getElementById('idError').textContent='Numbers only — the ID from the RM Circle dApp.';return}
load(v);
});
document.getElementById('switchId').addEventListener('click',()=>{
try{localStorage.removeItem('ctb.myId')}catch(e){}
history.replaceState(null,'','/my');
dash.classList.add('hidden');prompt.classList.remove('hidden');
document.getElementById('memberId').value='';
});
const id=pathId();
if(id)load(id);
})();