Commit Graph

207 Commits

Author SHA1 Message Date
martbost f4481a390b Fix off-by-one level name on upgrade payouts (feed + tweets)
The contract books income atLevel = the level the payer upgraded OUT OF, so
levelName(p.level) showed the FROM level ("upgraded to Scintilla" when the member
actually went to Ascensus). For upline (upgrade) payouts, show the destination
level (p.level+1) — consistent with describeIncome and upgrade.newLevel.
Amounts/generations/recipients were always correct; this is the label only.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-16 18:51:04 -05:00
martbost cce3b060b3 Fix join button label to show the resolved sponsor (was showing inviter)
The .inv-id-inline spans (join button + dApp-sponsor mention) were left on the
inviter's id; now they follow joinTarget like the header/href already did.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-16 13:05:43 -05:00
martbost f0ea4469a4 Self-rotating personal links: qualified member's join routes to next-to-qualify
Fixes the spillover leak where a qualified member promoting their own link had
joins land back on themselves (bonus reward) instead of qualifying their team.

- /api/public/member now returns joinTarget: unqualified member -> self;
  qualified -> next-to-qualify in their leg (reuses rotation-aligned nextInLine);
  whole leg qualified -> global rotation sponsor; else -> spillover (prior behavior)
- /join page: the Join button, "join under" header, sponsor-ID copy, and the
  submitted sponsorId all follow joinTarget; a note explains "invited by #X,
  joining to help #Y qualify" so the new member knows their real sponsor
- Member's SHARE link is unchanged (/join/<id>); resolution happens when a
  visitor opens it, so it self-corrects even for links already in the wild

Dry-run verified: /join/27 -> #34, /join/34 -> #34 (self), /join/21 -> #34.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-16 13:02:34 -05:00
martbost 19751a6561 Banner kit: swap subline to instant direct-to-wallet payout angle
Subline now leads with the differentiator — "Instant on-chain payouts,
straight to your wallet, no middleman" (length-appropriate per size).
Framed as the payment mechanism, not an income promise.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-16 12:15:43 -05:00
martbost 6a977eebd0 Neutralize payout-tweet CTA (drop CTBN wording post-rebrand)
"Join the Crypto Team Build Network 👉" -> "See how it works 👉" — the tweets
are public/prospect-facing and link to rmcircle.team, so they stay RM-Circle-only.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-16 07:29:42 -05:00
martbost 95a00e3ac1 Update payout-tweet hashtags to #RMCircle #Polygon #SmartContract #Crypto #Community
Matches the hashtags announced to the team (Zoom huddle). Also set live via
admin config (tweetHashtags), effective immediately for the next payout tweet.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-16 07:27:46 -05:00
martbost 4607c639e8 Make Promo Tools discoverable: footer link (all pages) + dashboard button
- added "Promo Tools" to the footer-links on every page
- added a Promo Tools button in the /my dashboard "Share this position" card

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-16 07:11:52 -05:00
martbost 6f692133dc Rebrand promo videos to RM Circle + retire stale scroll-captions clip
- combined clip: VO scenes 2 & 6 no longer name the Crypto Team Build Network;
  re-captured the rebranded site for the scroll; watermark now "The RM Circle"
- explainer clip: on-screen eyebrow "Crypto Team Build · Polygon" -> "The RM Circle · Polygon"
- both re-rendered + web-compressed (combined 10.9MB, explainer 2.7MB)
- removed rmc-scroll-captions (superseded + carried old branding)
- repo config seed synced to live (siteName "The RM Circle", programName "Premium Team Build · Polygon")

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-16 06:57:54 -05:00
martbost 3a80a50648 Rebrand public site to RM Circle Team Build (remove Crypto Team Build Network)
Public promo is now RM-Circle-only; the Crypto Team Build Network is learned
after joining, not surfaced to prospects.
- nav brand "Crypto Team Build" -> "RM Circle" (subtitle "Premium Team Build")
- all <title>, og:site_name, twitter meta, footers, disclaimers, 404, chatbot
  system prompt + canned answers, config siteName/supportLabel swept
- removed "a project of the Crypto Team Build Network" framing from prose
- banners regenerated: eyebrow now "The RM Circle · Polygon" (was Crypto Team Build)
- og-card.jpg regenerated (was "CRYPTO TEAM BUILD NETWORK PRESENTS")
- roadmap.webp already RM-Circle branded (no change needed)
- social posts/swipes: #CryptoTeamBuild -> #RMCircle, "crypto team build" -> "RM Circle team build"

Videos still reference old branding (VO + embedded site screenshot) — rebuilt next.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-16 06:43:23 -05:00
martbost 791ef721ef Fix join card centering, add video posters, https:// in swipes
- join.html: center the sponsor "invCard" (margin 20px auto)
- tools.html: add poster to explainer clip (was black), replace combined's
  mismatched poster with its own brand frame (rmc-*-poster.jpg from the videos)
- tools.html: prefix bare rmcircle.team with https:// in social posts + swipes
  so links are clickable in email clients that don't auto-linkify

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-16 06:29:05 -05:00
martbost 21bd77c07c Add banner kit + social posts + short/long swipes + Branded Voice CTA to /tools
- 7-size banner kit (1200x630, 1080x1080, 1080x1920, 300x250, 728x90,
  468x60, 125x125) rendered on-brand, exact dimensions, served from /banners
- Promo Tools page: 5 copy-paste social posts (X/Facebook), short + standard
  + long email swipes, banner grid (download / copy-URL), invite-link builder
- Branded Voice CTA (mybrandedvoice.com) for writing promos in your own voice
- Sync chatbot: chat.js canned answer + server.js AI system prompt updated

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-16 06:08:35 -05:00
martbost 4dbbc6780c Add payments video to training + new Promo Tools page (/tools)
Training: new "Video 5 · Why you're not getting paid yet" card with the
payments teaching video (+ poster). Promo Tools page at /tools (noindex,
unlinked from public nav — a team resource): share-ready video clips
with copy-link buttons, a personal invite-link builder, and three
compliant email/message swipes; banners noted as generate-on-request.
Chatbot gains a promo-tools answer. Old scroll-only clip is superseded
by the combined cut (retire it).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 05:47:01 -05:00
martbost 76c55fe4c2 Sweep all self-references to rmcircle.team (SEO/OG/canonical/chatbot)
Domain migration go-forward: replaced every hardcoded rmcircle.saasy.top
across public/*.html (canonical, og:url, og:image, twitter tags),
chat.js (chatbot answer URLs + linkify self-link matcher), my.js, and
server.js (member-page OG injection, unsubUrl, chat system prompt) with
rmcircle.team. Old domain still serves everything; canonical now points
search engines + social previews at the new primary domain.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 05:32:13 -05:00
martbost d0b1eddda2 Show which generation caught each pass-up + finish domain go-forward
Feature (Marty): live payment proof + notifications now show the
generation for upgrade pass-ups (matrix hop distance payer→recipient),
removing the "why did they get it" question. chain.genBetween() adds a
`gen` field to payout events + /api/public/payouts; rendered as a gold
"Gen N" badge in the feed, "Gen N pass-up" in toasts, and "Gen N …" in
Telegram team alerts.

Domain: rmcircle.team is now live (added to Coolify alongside
rmcircle.saasy.top, SSL issued, both serve, no redirect). Switched the
auto-tweet CTA to https://rmcircle.team.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 05:22:47 -05:00
martbost 663948595c Payments video v3: real #46 worked example + org-flow capstone
Per Marty: (1) added a real, on-chain-verifiable worked example — user
#46's actual payments climbing generation by generation with exact
amounts (entry 163.12 + 1st upgrade 310.70 → Gen1 #30; 2nd 621.40 →
Gen2 #24; 3rd 1,242.80 → Gen3 #21), names omitted; (2) a "deeper =
later but bigger" principle scene; (3) an "it all flows up to you" org
pyramid capstone that works at every level. Now 10 scenes ~3.4 min.
Re-encoded over the same /v/ filename.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 05:06:11 -05:00
martbost 37aaccc7f9 Payments video v2: add generations + who-gets-paid-first (order)
Per Marty's field observation (Orlando #30 / Neville #46,#49 see direct
payments flowing but don't grasp the upgrade ORDER): reworked into 7
scenes adding (1) a generations ladder — each generation pays you at a
DIFFERENT upgrade (Gen1→1st→Ascensus ... Gen4→4th→Apex), and (2) a
pass-up routing chain — the payment travels UP, skips the wrong-gen /
unqualified positions, and lands on the first one that's qualified +
leveled. Re-encoded over the same /v/ filename.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 04:32:31 -05:00
martbost af28f2e96e Add team-education video: how payments work (direct vs pass-ups)
New ~96s teaching video in Marty's voice for confused team members who
expect rewards but aren't getting them. Clarifies the two mechanisms:
direct rewards (only from people who join on YOUR link — spillover
doesn't pay you) vs upgrade pass-ups (travel up, skip you unless you're
qualified + leveled). Includes a 3-point self-diagnosis. Hosted at an
unlisted /v/ path; CTA rmcircle.team.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 04:17:29 -05:00
martbost 9717ddbd85 Admin: payment-routing / leak map for the org
New chain.getOrgRouting(rootId, ownerIds) simulates the contract's
_payUpline routing for every member below the org root on their NEXT
upgrade, classifying where the POL lands: your positions, a teammate
in-org (healthy), an outsider above the org (true LEAK), or fees.
Surfaced on /api/admin/income and rendered as a panel in "My Positions
— Income": 4-stat summary + expandable list of payments escaping the
org. Distinguishes real leaks (money leaving the team) from teammates
earning — which the old upgrade-needs alert conflated.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-15 18:35:48 -05:00
martbost 0496584fb2 v1 video: bottom-align the two pay-card buttons
The two cards had different body-text lengths, so the CTA pills sat at
different heights. Made .card a flex column and pushed .pill to the
bottom (margin-top:auto), so both buttons align. Re-encoded over the
same /v/ filename.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-15 18:09:03 -05:00
martbost ad61355d85 Videos: correct new domain to rmcircle.team
Marty switched the domain from rmcircleteambuild.com to rmcircle.team
(bought at NameSilo). Updated v3 (spoken URL + end caption) and v1
(on-screen URL + scene-6 voiceover). Both re-encoded over their existing
/v/ filenames so shared links stay stable. Site/tweet CTAs remain on
rmcircle.saasy.top until DNS is live tomorrow.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-15 17:56:33 -05:00
martbost 9c5ce8b0fd Matrix panel: show full 8-level ladder (Scintilla→Corona), tapered
Dot pyramid (4 gens) on the left; 8-level ladder on the right with real
amounts through Apex and Fastigium/Vertex/Corona ascending (↑) so it's
complete without headlining ~40K POL. Panel raised above the caption
band so all 8 rows + the "not a promise of income" note stay clear.
Combined cut re-encoded over the same /v/ filename.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-15 17:46:26 -05:00
martbost 95b30a1d86 Re-render videos with new domain rmcircleteambuild.com (pre-migration)
Domain lands tomorrow; both domains will serve the site (old kept live
indefinitely). v3 combined cut: spoken URL + clean end caption now read
rmcircleteambuild.com. v1 designed-scenes: on-screen URL + regenerated
scene-6 voiceover updated to the new domain (kept its original wording).
Both re-encoded over their existing /v/ filenames so shared links are
stable. Tweet CTA + site links stay on rmcircle.saasy.top until the new
domain is live tomorrow.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-15 17:25:41 -05:00
martbost 204d12e3cd Combined-cut video: clean single-line rmcircle.saasy.top end caption
The spoken URL is still spelled for clarity, but the caption now collapses
"r m circle dot saasy dot top" into one gold rmcircle.saasy.top line
(no fragments). Re-encoded over the same /v/ filename so the link is stable.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-15 17:06:08 -05:00
martbost 731951d5a2 Add combined-cut explainer (v3): scroll + captions + matrix animation
New primary cut: site-scroll b-roll + word-synced captions, with a
full-screen matrix-expansion animation (YOU→2→4→8→16) during the pay
section showing per-level POL (Ascensus 621, Fabrica 1,243, Culmen
2,486, Apex 4,971) with an honest "contract rule, not a promise" note.
Crypto Team Build Network framing throughout. Web-optimized (~11MB).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-15 16:57:21 -05:00
martbost 61235a7f46 Add scroll+captions explainer (v2) at /v/ for private review
Site-scroll b-roll + word-synced TikTok captions, voiced with ElevenLabs
timestamps. Reframed around the Crypto Team Build Network (watermark +
narration + CTA); RM Circle named as the co-op we build in. Web-optimized
(faststart, ~14MB). Unlisted /v/ path.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-15 16:44:21 -05:00
martbost 09b44c8286 Reframe payout tweets around the Crypto Team Build Network
Per Marty: Crypto Team Build Network is OUR team; The RM Circle is the
third-party co-op we build in, not our brand. CTA changed from "Join
The RM Circle team build" to "Join the Crypto Team Build Network", and
hashtags now lead with #CryptoTeamBuild (then #TheRMCircle as the
program tag). Payout facts unchanged. Longest render 278/280.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-15 16:42:46 -05:00
martbost 1d93395c3f Add explainer video for private review at /v/ (unlisted)
66s 1080p promo (HyperFrames + ElevenLabs VO), placed at an unguessable
/v/ path for Marty to preview on any device. Served with existing mp4
mime + HTTP range support (iOS-friendly). Not linked from the site.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-15 16:25:20 -05:00
martbost 5c53945aee Name both sides of the event in referral tweets (purchase + payout)
Each join/upgrade yields exactly one payment, so one tweet tells the
whole story. Upgrade tweets already named the upgrader + recipient;
referral tweets now name the joining member (evt.fromId) too, e.g.
"Member #129 just joined and Member #30 instantly earned 326.2 POL".
All templates verified <=280.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-15 11:48:47 -05:00
martbost a0f0950c77 Use correct brand: #TheRMCircle and "The RM Circle team build"
Hashtag #RMCircle -> #TheRMCircle and CTA "Join The RM Circle team
build" to match the brand (thermcircle.com). Longest render 273/280.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-15 11:43:30 -05:00
martbost 2992ff414f Name "RM Circle team build" in every payout tweet + #RMCircle
CTA line now reads "Join the RM Circle team build 👉" and hashtags lead
with #RMCircle #CryptoTeamBuild. All templates verified <=280 (longest
270). Branding is in every tweet, not just implied by a hashtag.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-15 11:42:00 -05:00
martbost c52add619a Keep payout tweets under 280 (short CTA URL + hard length guard)
Blotato counts the raw URL length toward the 280 limit (no t.co
shortening), so the long UTM CTA overflowed. Shortened default CTA to
?utm_source=x and added a guard in render(): drop hashtags, then trim
body, so a tweet can never exceed 280. Verified a real test post to
@cryptoteambuild returns HTTP 201.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-15 11:38:32 -05:00
martbost c418aa516a Add auto-tweet of on-chain payout proof to @cryptoteambuild (Blotato)
New tweet.js posts a tweet to X via Blotato each time the indexer emits
a NEW team payout. Org-gated to the #21 organization; fires on all
payout types. Ships DISABLED (config.tweetEnabled default off / env
TWEET_ON_PAYOUT), so nothing posts until explicitly turned on.

Safety: dedupes by tx key (persisted to DATA_DIR/tweeted-payouts.json)
so restarts never re-post; a min-gap queue keeps bursts under Blotato's
30/min limit; API key read from env BLOTATO_API_KEY or DATA_DIR key
file, never committed or logged. Varied emoji/hashtag templates with a
CTA (+UTM) to the main page; deterministic template pick avoids X
duplicate-content rejection. Admin config allowlist gains tweetEnabled,
tweetCtaUrl, tweetHashtags, blotatoTwitterId.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-15 11:35:05 -05:00
martbost d36a405581 Paginate the live payment proof feed (Show more / Show fewer)
The feed showed only the latest 12 of 136 recorded payouts. Now:
- chain.getPayoutsPublic(offset,limit) returns a page of the full
  reversed history plus total/offset/limit/hasMore (limit capped 100).
- /api/public/payouts accepts ?offset & ?limit (defaults 0/40, so the
  live poll + toast detection are unchanged).
- payouts.js keeps a deduped store keyed by payout key; the live poll
  refreshes the recent page while "Show more" pulls older pages 12 at a
  time (with slight overlap so a newly-arrived payout can't open a gap),
  and "Show fewer" collapses back. Verified in-browser: 12 → 48 → 136
  rows, button flips to "Show fewer", collapses to 12, 0 console errors.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-15 11:16:12 -05:00
martbost 95bc5bc90f Remove stray diagnostic script committed by accident
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-15 10:52:55 -05:00
martbost 086e64b3f6 Add home-page "Still have doubts?" trust band + fix CSP blocking inline styles
Home page: new reassurance band between the live payment feed and the
final CTA — surfaces the three doubts (rules can't change, money never
sits in the contract, keeps running if creators vanish) and routes
skeptics to /contract.

CSP fix (the real find): style-src was 'self' with no 'unsafe-inline',
so the browser was silently dropping EVERY inline style="" attribute
site-wide — the attribute stayed in the DOM but never applied. This is
why the earlier margin fix only worked once moved to a class, and why
the new card rendered left-aligned with a teal eyebrow. Added
'unsafe-inline' to style-src only (script-src stays locked to 'self').
Verified via computed styles + full-page screenshots: home, /contract,
/how-pay-works now render as authored.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-15 10:52:43 -05:00
martbost 56890b025a Add "What if the creators disappear?" reassurance (verified on-chain)
Answers the durability question: the contract is autonomous and
immutable (no admin heartbeat, no pause, no expiry), and the four
admin wallets are plain EOAs — read live from contract storage and
confirmed via eth_getCode — so a lost/abandoned admin wallet can
never reject or jam a member payment; at worst the project's own fee
sits uncollected. No stored balance means nothing can be stranded.

- contract.html: new section 6 card
- chat.js: matching canned answer (creators-disappear keywords)
- server.js: RESILIENCE fact added to the AI system prompt

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-15 10:26:58 -05:00
martbost 279bcb5f25 Teach the chatbot everything we added (canned + AI prompt)
chat.js: new canned answers for how-you-get-paid (two streams + depth-
matched levels), Premium vs Standard tier, and the member dashboard/
alerts. server.js AI system prompt: added PAY FLOW, TIERS, and MEMBER
DASHBOARD facts, and refreshed the site-pages list (/how-pay-works,
/my, /disclaimer, spillover article). Both chat modes now answer the
questions this session surfaced (skips, standard tier, dashboards).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-15 10:15:42 -05:00
martbost 1c9048e088 Add Premium vs. Standard tier explainer to /how-pay-works
Preempts the "why did an amount come in smaller?" question (hit live with
Standard-tier #46): a clear card with a Premium/Standard amount table,
that tier is permanent (upgrading advances level not tier), and the team
play of joining Premium so the whole leg pays full value.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-15 10:12:50 -05:00
martbost 1929a928d8 Add /how-pay-works pay-flow diagram page
Visual explainer of the two income streams: entry rewards you keep, and
the depth-matched upgrade ladder (each layer pays you once, at the one
level matching its depth) as an SVG flow diagram. Route + CSS added;
linked from training body and all footers. Educational, on-chain-factual
framing with disclaimer link.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-15 10:06:36 -05:00
martbost 7b33e28d0c Personalized social previews for shared referral links
/my/:id and /join/:id are now served with server-injected, per-member
Open Graph + Twitter Card tags (social scrapers don't run JS, so the
per-ID meta must be in the static HTML). Strips any baked-in og/twitter
tags and injects a fresh ID-specific set — title/description reference
the member #, branded og-card.jpg image, canonical per-ID og:url. Bare
/my and /join paths unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-15 08:30:52 -05:00
martbost db8261be6e Center the stat numbers over their labels
Make .team-stat a centered flex column and force the .num to full-width
centered text — the big numbers were rendering left-aligned. Verified
0px off-center across all four tiles.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-15 07:29:43 -05:00
martbost 9467c4e840 Fix team-stats row spacing (inline margin-top wasn't applying)
The grid-3 inline margin-top:32px computed to 0 (collapsed against the
grid team-stats container), leaving the two rows touching. Use a class
margin-bottom:34px on .team-stats instead — verified 34px gap locally.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-15 07:25:47 -05:00
martbost 5de1eb8ecd Loosen spacing in the home team-stats showcase
Bump the stat-tile grid gap 16→22px and the gap to the mechanics cards
24→32px so the boxes breathe.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-15 07:18:41 -05:00
martbost d805a33608 Add home "team by the numbers" showcase + why-a-winning-team-pays
Public /api/public/org-stats (from config.orgRootId, default 21) feeds a
prominent home-page band: share of the whole network, team size,
generations deep, and on-chain POL to the team — populated by bridge.js,
section hides itself if stats aren't ready. Paired with three mechanics
cards (spillover fills your matrix, depth pays as it climbs, the rotation
qualifies you) that make the "winning team" case honestly, plus the risk
caveat ("most participants may not profit") and a disclaimer link.
Live: 51.1% of members, 48 in the org, 10 generations, 25,423 POL.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-15 07:14:23 -05:00
martbost 7b6f629780 Add "Your Organization vs. the Network" live stat to admin
New chain.getOrgShare(rootId) + /api/admin/org-share endpoint compute an
org's share of the whole contract by both member count and total POL
paid, from in-memory state. Admin card (default root 21, saved to
config.orgRootId, auto-loads) shows the two headline percentages, a bar,
and the member/POL breakdown. Verified: #21 org = 51.1% of members,
49.5% of POL.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-15 07:07:27 -05:00
martbost 026b9e5d9b Show all eight generations in the depth-over-width matrix
Extended the "2 → 4 → 8 → 16 → 32" visual to the full doubling
2→4→8→16→32→64→128→256 (510 positions), keeping the 30-position first
milestone in the caption. Matrix is now data-driven (data-n) with
icons capped at 12 + a "+" for deeper generations so 256 stays compact;
compact icon styling + horizontal scroll safety.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-15 06:11:16 -05:00
martbost f5de98c214 Classify rotation joins by worked-sponsor status, not just active
A rotation joiner submits their ID after the rotation may have advanced
(esp. with auto-advance: the direct who completes a sponsor's 2/2 joined
under it, but the queue has already moved on). Keying "rotation" off the
currently-active sponsor mislabeled them "leg" (hit live: Harvey #92
under #36 after #36 qualified). Now: rotation if the on-chain referrer
is a queue sponsor that is active OR qualified (was/is worked); a
still-waiting or non-queue referrer stays a leg join, preserving the
original #49/#56-under-#46 fix. Auto-enqueue now parents the new
position to the actual on-chain referrer, not the current active.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-15 05:43:03 -05:00
martbost e1d6b782a2 Auto-advance rotation when a sponsor hits 2/2
Reverses the earlier manual-Qualify design (Marty 2026-08-15): when the
auto-count brings a queue sponsor to 2/2 directs, the sponsor is now
auto-qualified and normalizeStatuses activates the next waiting position
— same as clicking Qualify, but automatic. Previously it stopped at 2/2
"active" and waited for a manual click, which left the rotation looking
stuck (hit live with #36 Mad Dog). #36 qualified manually to catch up;
rotation advanced to #34 Janie.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-15 05:36:14 -05:00
martbost 0f5630e6d0 Disclaimer page, self-service member alerts, and admin login lockout
- /disclaimer: independent-resource, affiliate, earnings, risk, and
  not-advice disclosures; linked from all footers.
- Self-service alerts: members opt in with their email on /my/:id to get
  "you've been paid" + "upgrade needed" emails for their own position
  (same watcher as the owner alerts, extended). Signed unsubscribe link
  (/unsubscribe?id=&t=HMAC), on-chain-registration check, rate-limited,
  masked-email status, confirmation email.
- Admin login: timing-safe compare + per-IP lockout (8 fails -> 15 min,
  escalating). Previously unlimited/brute-forceable.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-14 14:45:57 -05:00
martbost 8b3e82d960 Alert when an owned position needs upgrading to catch incoming pay
New owner upgrade watcher: every 5 min it checks config.ownerIds against
the chain state and, when a leg member is ONE upgrade from paying a
position that isn't eligible yet (below the required level, or not
qualified), emails config.ownerAlertEmail + pings Telegram — "upgrade
#24 to Fabrica, #61 is one upgrade from paying you 2,486 POL." Deduped
per (position, level) in owner-alerts.json, re-fires if the situation
recurs. Same warning renders inline in the admin "My Positions" panel
(upgradeNeeds in the income endpoint). New ownerAlertEmail settings
field. Detection logic unit-tested.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-14 14:27:50 -05:00