diff --git a/public/chat.js b/public/chat.js
index 72f6f6a..9d558ce 100644
--- a/public/chat.js
+++ b/public/chat.js
@@ -78,7 +78,9 @@
{k:['30 positions','goal','milestone','matrix','how many people','team size'],
a:()=>`The first major team goal is 30 properly placed positions: 2 → 4 → 8 → 16 across the first four generations. Then we keep going — 32, 64, 128 and beyond.`},
{k:['moving link','why not my link','own link','my referral','use my link','extra legs'],
- a:()=>`We use a moving-link strategy — and it's automatic: every member's invite link self-rotates. While a position still needs directs, its link credits it; the moment it's qualified, the same link starts routing new signups to the next position in that leg that needs help. Nobody retires anything and nobody stops promoting — the link hunts for the next spot on its own. Joining fresh? The Getting Started page always shows the current team placement. (A signup straight to a qualified position's raw ID is a harmless bonus — it pays that member and spills downward as depth — but spillover alone never qualifies anyone.)`},
+ a:()=>`We use a moving-link strategy — and it's automatic: every member's invite link self-rotates. While a position still needs directs, its link credits it; the moment it's qualified, the same link starts routing new signups to the next position in that leg that needs help. Nobody retires anything and nobody stops promoting — the link hunts for the next spot on its own. Prefer new people to land under YOU instead of rotating? Your dashboard also gives you a Direct link (adds ?direct=1) that places every join directly under your position as spillover. Team link (default) = help your team qualify; Direct link = build your own depth. Joining fresh? The Getting Started page always shows the current team placement.`},
+ {k:['direct link','team link','keep my link','spillover under me','stop rotating','not rotate','build my own','two links','which link','sticky link'],
+ a:()=>`Your dashboard gives you two links under "Share this position": Team link (the default /join/<id>) — once you're qualified it routes new joins to the next teammate who needs directs, building your team in order; and Direct link (/join/<id>?direct=1) — every join lands directly under you as spillover, building your own depth. Both credit you the entry reward on anyone you personally bring. Team link is recommended (it grows the whole leg), but the Direct link is there if you'd rather stack spillover under yourself. Copy either from your dashboard.`},
{k:['risk','safe','scam','lose money','guarantee','guaranteed','income','earnings','legit'],
a:()=>`Straight answer: participation involves cryptocurrency and smart-contract risk, and no income is guaranteed. Results depend on team effort, duplication, upgrades, smart-contract rules, and POL's market value. Only use funds you can afford to lose.`},
{k:['video','videos','training','watch','tutorial','learn','how to'],
diff --git a/public/join-now.js b/public/join-now.js
index 72fd051..c8666d3 100644
--- a/public/join-now.js
+++ b/public/join-now.js
@@ -46,7 +46,13 @@ fetch('/api/public/config').then(function(r){return r.json()}).then(function(c){
async function loadSponsor(){
try{
// personal invite: ?ref= -> place in that member's leg via the site's moving-link joinTarget
- var ref=new URLSearchParams(location.search).get('ref');
+ var qp=new URLSearchParams(location.search);
+ var ref=qp.get('ref');
+ // Direct link (?direct=1): opt out of rotation — register straight under
+ // the inviter (spillover in their team), never the next-to-qualify member.
+ if(ref&&/^\d{1,15}$/.test(ref)&&qp.get('direct')==='1'){
+ sponsorId=String(ref); $('sponsorLine').innerHTML='You’ll join directly under #'+ref+' — a spillover placement in their team.'; return;
+ }
if(ref&&/^\d{1,15}$/.test(ref)){
try{ var rm=await (await fetch('/api/public/member?id='+ref)).json(); var t=rm&&rm.joinTarget;
if(t&&t.id){ sponsorId=String(t.id); $('sponsorLine').innerHTML='You’ll join under #'+t.id+''+(String(t.id)!==String(ref)?' — the next open spot in #'+ref+'’s team':'')+'.'; return; }
diff --git a/public/join.js b/public/join.js
index eb0ef12..83fada7 100644
--- a/public/join.js
+++ b/public/join.js
@@ -10,6 +10,10 @@
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 direct=new URLSearchParams(location.search).get('direct')==='1';
let joinTargetId=id; // where the entry actually lands after moving-link routing
function setIds(idStr){
@@ -53,10 +57,10 @@
// 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.
- const t=d.joinTarget||{id:d.id,referralUrl:d.referralUrl,reason:'self'};
+ 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; jb.removeAttribute('target'); // self-enroll page, placed in this leg
+ 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)
@@ -64,7 +68,9 @@
document.getElementById('invIdJoin').textContent='#'+t.id;
document.querySelectorAll('.inv-id-inline').forEach(el=>el.textContent='#'+t.id);
const note=document.getElementById('invQualNote');
- if(t.reason==='leg'||t.reason==='global'){
+ 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.
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.
Independent team training resource • Participation involves risk • No income is guaranteed
';
main.appendChild(s);
var hp=document.getElementById('hookPlayer'),cta=document.getElementById('hookCta');
diff --git a/public/my.js b/public/my.js
index a058885..fae715d 100644
--- a/public/my.js
+++ b/public/my.js
@@ -537,6 +537,7 @@
const el=document.getElementById('dShare');
if(!el)return;
const dashUrl=`https://rmcircle.team/join/${d.id}`;
+ const directUrl=`${dashUrl}?direct=1`;
const q2=d.directCount>=2, nx=d.nextInLine;
// The personal QR is ALWAYS shown: /join/ self-rotates via the moving
// link, so a scan is always placed correctly — even after 2/2. This is the
@@ -555,6 +556,12 @@
The link above is your Team link ${q2?'— now that you’re qualified it routes new joins to the next teammate who needs directs, building your team in order (recommended).':'— it credits you and builds your first two.'} Want new people to land under YOU as spillover instead? Use your Direct link:
+
${esc(directUrl)}
+
Team link helps your team qualify. Direct link places every join under you (own spillover). Either way you keep the entry reward on anyone you personally bring.
`;
const qr=document.getElementById('dQr');if(qr)qr.innerHTML=qrSvg(dashUrl);
const qb=document.getElementById('dQrBtn');
@@ -576,6 +583,8 @@
});
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){}});
+ const cd=document.getElementById('copyDirect');
+ if(cd)cd.addEventListener('click',async()=>{try{await navigator.clipboard.writeText(directUrl);cd.textContent='Copied ✓';setTimeout(()=>cd.textContent='Copy Direct link',1400)}catch(e){}});
}
async function load(id){
diff --git a/server.js b/server.js
index a2ac788..af7836d 100644
--- a/server.js
+++ b/server.js
@@ -43,6 +43,7 @@ FACTS:
- First team goal: 30 properly placed positions (2+4+8+16), then 32, 64, 128 and beyond.
- 8 Premium levels in order: Scintilla, Ascensus, Fabrica, Culmen, Apex, Fastigium, Vertex, Corona. Everyone starts at Scintilla. Upgrade as quickly as practical, ideally with earned POL; the first two payments at each level help fund the next upgrade. Stay aware of your active downline's levels so you don't fall behind and miss payments.
- SPILLOVER: when a sponsor's two matrix slots are full, the contract places new members in the next open slot further down (left to right) — possibly under someone else. Spillover fills that member's matrix and sets up future upgrade income to their position, but does NOT count toward qualification: only people who join using a member's own ID are their directs. Upgrade payments travel up the MATRIX (not who-referred-whom) and only stop at positions that are qualified (2 directs) AND already at the level being bought — otherwise the payment passes them by to the next eligible upline. Members can see their own matrix, spillover tags, and payments at https://rmcircle.team/my
+- TWO SHARE LINKS (on the dashboard "Share this position" panel): (1) TEAM LINK = the default rmcircle.team/join/ — a MOVING link that, once the member is qualified, auto-routes new joins to the next teammate in their leg who needs directs (builds the team in order; recommended). (2) DIRECT LINK = rmcircle.team/join/?direct=1 — opts OUT of rotation so every join lands DIRECTLY under that member as spillover (builds their own depth). Both pay the member the entry reward on anyone they personally bring. Recommend the Team link (team-first), but the Direct link exists for members who want to stack spillover under themselves. Angle links (?v=pocket|phone|two|graveyard) work on either.
- PAY FLOW (full diagram at https://rmcircle.team/how-pay-works): TWO income streams. (1) Entry rewards — when a direct joins under a member's link, that member gets the entry reward (~326 POL at Premium) and keeps it, on EVERY direct (not just the first two). (2) Upgrade payments — each person below you pays you ONCE, at the single level matching how far below you they sit: someone 1 layer down pays you at their Ascensus upgrade, 2 layers down at Fabrica, 3 at Culmen, 4 at Apex, and so on. To catch each one you must be at that level yourself AND qualified — so stay one level ahead of your team's deepest active layer. This is why a member can be "skipped" on a payment: it was reserved for the position at the matching depth, or they weren't leveled up in time.
- THE TRIPLE PLAY (optional fast-start; people ask "should I get one position or three?"): Most start with ONE position — get 2 directs, help them, duplicate (the standard path, all anyone needs). OPTIONAL alternative: buy a top position PLUS its own 2 directs at the same time. Those two count as the top's directs, so the TOP is instantly qualified (2/2) and catching upgrade pass-ups from day one; then you promote only the top position's moving link, which auto-routes new joiners to fill/qualify the positions beneath. Because ONE WALLET ADDRESS CAN ONLY HOLD ONE POSITION (blockchain rule), this needs THREE separate addresses. Foolproof = a separate wallet (or browser profile) per position. You CAN use "Add account" in one MetaMask (Account 1/2/3 are separate addresses), BUT MetaMask's account switch doesn't always follow into a connected site — if the join page still shows the old address the join reverts with "already registered", so you must confirm the NEW address is shown before signing each (or disconnect+reconnect the site with the new account). Separate wallets avoid this. HARD framing to always include: costs ~3x a single entry; ONLY money you can comfortably afford to lose; NEVER borrowed; NEVER front/fund it for someone else (it puts one person in control of positions on another's wallet); it's a faster START, not an income guarantee; and one position is perfectly fine. Never push it — present it and let them choose. UPGRADE STRATEGY for a Triple Play: most members (and the founder on #21) CONCENTRATE UPGRADES ON THE TOP position and leave wallets 2 & 3 lower — because pass-ups the lower positions aren't leveled to catch bubble UP to the first eligible position (the catcher-of-last-resort mechanic), so keeping the TOP most-leveled funnels the deep catches into the top wallet, and since you own all three the money stays in your family; upgrading all three works too but costs 3x the upgrades, so concentrating on the top is leaner. Upgrades always optional/self-paced/funded by earnings, never out of pocket. Full steps: https://rmcircle.team/how-pay-works#triple-play.
- LEVEL CAP / "does it stop at 8 levels?": there are 8 levels (Scintilla..Corona); Corona is the max LEVEL — you can't upgrade past it. But EARNING is not capped. (1) You catch an upgrade pass-up from every person in your first seven generations as they level up, and those generations hold up to 254 positions that keep filling via recruiting + spillover — one payment per person, but people keep arriving. (2) Pass-ups travel UP past anyone who can't catch them: an upgrade payment rises to the first upline who is qualified (2 directs) AND leveled high enough (level >= the level being bought), searching up to the 16-layer MAX_MATRIX_DEPTH; if nobody qualifies in range it goes to the project fee wallet. So a Corona member is the "catcher of last resort" — at max level they're eligible for every pass-up, never the bottleneck, and they catch the ones under-leveled members below them let slip. That's the mechanical reason to keep leveling up. Full explainer: https://rmcircle.team/how-pay-works#cap. Still contract mechanics, never an income promise.