Commit Graph

101 Commits

Author SHA1 Message Date
martbost 5c08be1c30 Campaign actions: a centred, even stack with a squarer corner
The column is narrow so these buttons stack. Left-aligned pills of different
widths read as a ragged edge, and a label that wraps inside a 999px radius
becomes a tall oval, which is what "Buy more views" was doing.

They are now centred, the same width, and cornered at 10px, so each label sits
on one line. Scoped to this table: everything else keeps the pill. The phone
card shares a row rather than stacking, as it did before, so that layout is
left alone.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-23 09:10:50 -05:00
martbost 02d60d6ee1 Archive button: it had no text colour, and no room beside its neighbours
I gave it the class "ghost", which on PolHunter is a button and here is the
giant faded wordmark in the footer. That class sets color:transparent, so the
label was not dark, it was invisible. It now uses a new "quiet" variant, grey
on a muted outline, which reads clearly and sits below the mint actions rather
than competing with them.

The campaign actions also had no spacing on desktop. The mobile layout already
gapped them; the wide one leaned on a plain space between tags, so they sat
flush against each other.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-23 08:57:56 -05:00
martbost 489d832546 Sign-in: stop telling a fast returning member off for being fast
The email-code endpoint refuses a request that arrives within two seconds of
the page loading, because a script fills a form instantly and a person does
not. A member whose email is already filled in by the browser has nothing to
type, taps straight away, and gets "Give the page a second, then tap again."

The page now waits out the remainder itself and sends when the window has
passed, so the person sees the normal sending state instead of an error.
The server check is untouched, so anything posting at the endpoint without a
page behind it still fails exactly as before.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-23 05:50:34 -05:00
martbost c47905d89a Overview announcements: let a member put one away for the session
The PolHunter launch card, the Five Dollar Friday card and the "you came from
PolHunter" card repeat on a timer, so anyone who has read one had no way to
clear it. Each now carries a small close control in its heading: the card
stays hidden for the rest of that browser session and comes back at the next
sign-in, so a message nobody acted on is never lost for good.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 06:29:49 -05:00
martbost 68b08ab8cc Five Dollar Friday: +20% purchased-grade credits on any $5+ package bought on a Friday (Central), live wave on the landing strip and Overview card, Telegram every ten packs + Friday kickoff + midnight wrap-up, Thursday notice + email, Five Fridays badge, promo posts + swipe, chatbot; first Friday 2026-09-25
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-20 13:59:15 -05:00
martbost d6bc79c9d6 Home: 'Watch the 4-minute overview' CTA + inline overview video section (Spaces training/platform-overview.mp4)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 13:42:23 -05:00
martbost 616f328db5 Report-an-ad dialog: site dialog with no default reason (was a native prompt pre-filled with "broken")
Campaign #27 (house AdRevSplit text ad) was reported "broken" with no note while the target framed
and loaded fine; the report dialog was prompt() with 'broken' as the default value, so one stray OK
filed it. Now IAP.ask with an explicit, validated reason and an optional note; common.js tag bumped
on all pages.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 10:21:53 -05:00
martbost 4d578fadac Lead with the $20 activation language; free-member POL earning no longer volunteered (Marty); chatbot answers truthfully only when asked point-blank
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-15 15:00:01 -05:00
martbost 310384cdad Credits versus POL spelled out: level 1 needs payouts on (no purchase), free members earn credits; home page block, earning page, join step 3, chatbot (Clinton's question)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-15 14:29:39 -05:00
martbost 825d0de6c3 Header: Leaderboard link
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-14 05:40:26 -05:00
martbost 4caa0ef5a8 Sign-up guard: honeypot is read-only until a trusted focus and ignores browser autofill (own email); masked value logging
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-13 04:57:51 -05:00
martbost 49a98e7ee3 Blog: admin-written coaching articles, server-rendered public /blog with SEO metadata, sitemap, RSS, robots
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-12 18:42:56 -05:00
martbost 9d14af0898 Top menu: 'How it works' and 'Ad packages' anchor to homepage sections (#how, #packages); sections clear the sticky nav
The Ad packages link pointed at /#ladder, an id that did not exist on the homepage.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-12 16:54:55 -05:00
martbost 5b4fe133eb Homepage ticker: speed scales with text length (~70 px/s), pauses on hover/touch; CSS fallback 120 s
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-12 16:40:23 -05:00
martbost 99f6c1f8c6 Dialogs: Cancel used the site's giant .ghost display class by mistake; use the secondary button style
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-12 16:23:39 -05:00
martbost ebac2fcb32 Member area: in-page dialogs replace every browser prompt/confirm
Mobile Safari shows a red 'Suppress dialogs' option on the second native pop-up
in a row and, once tapped, swallows every later prompt on the site until reload
(Hugh, 2026-09-12, campaign top-up). IAP.ask / IAP.confirmBox in common.js render
a modal-card dialog (Enter/Escape, focus, backdrop cancel); all nine call sites in
my.js use them (top-up credits, link insert x2 with selection restore, pay it
forward, adopt note, release, unlink, add position, Trust Wallet check).
Version tags bumped on every page that loads common.js.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-12 09:31:43 -05:00
martbost f6490c9444 Promo tools: hook video cards (matched links, downloads, captions, share buttons)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-10 08:55:29 -05:00
martbost 276618dd73 Member ads everywhere they belong: text strip under every pane title, banner at the foot of each main pane, wall + plays slots; Network views as its own campaign column
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-10 08:35:31 -05:00
martbost bcc895dc8d Member area: loading spinner instead of flashing the sign-in card
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-10 08:17:55 -05:00
martbost 29e18aa48a Wall page: InstantAdPay headline + POL on Polygon; payouts-in-POL copy on join/home/Wallet/objections/chatbot
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-10 08:10:35 -05:00
martbost 9ebb9a86b2 Ledger/earnings rows show when; Earn credits: never your own ads; burner sets Polygon fees + rotates RPCs on node errors
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-10 07:43:24 -05:00
martbost b824711ac5 Campaigns: gold available-to-spend banner + where-your-ads-run (incl. Network Ad Space); Wallet: live POL balances; Earn credits: done-for-today mark
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-10 07:36:24 -05:00
martbost 4156b1f815 Coaching layer + tools: coach your directs (rungs, stalled, one-click nudges), automatic member nudges + weekly sponsor digest, prospects list, per-angle link stats, broadcast templates, Qualified Start calculator, Telegram proof feed, send-failed alerts, admin P&L pane, automatic credit burner (ethers), username lock, home-page comparison, printable checklist, wall link in Promo tools
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-10 07:30:16 -05:00
martbost f785016cb5 Training: section headers; byline 'Brought to you by the Crypto Team Build Network' under every logo
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-10 06:25:58 -05:00
martbost 2ac6549171 Wall ownership ladder: positions 2 and 3 become the member's own at 2 / 5 qualifying buyers
- accounts.wall_offers (JSON, up to 2 offers: label, https link, banner) set from
  Profile > Your wall; upload supported; locks show the buyer threshold.
- /api/wall assembles: position 1 = own line banner; positions 2-3 = own offer
  when unlocked and set, else upline banners (only uplines with a live banner),
  else house ads. Response carries unlocked + buyerCount; wall labels show
  "this wall" / "their line" / "InstantAdPay".
- Chatbot canned answer + facts, follow-up email 7 mention the ladder.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-09 15:31:45 -05:00
martbost 9b32d40874 Wall page: platform icons on the social pills
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-09 08:32:52 -05:00
martbost c7018f6258 My line: POL earned from each person; wallet picker features non-Trust wallets first
- /api/my/line sums TierPaid events per buyer for the signed-in member; the
  downline rows show "+X POL" (what that person has paid you so far).
- AppKit featuredWalletIds: MetaMask, Phantom, SafePal, Coinbase first; Trust
  Wallet stays available under All wallets.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-09 07:47:41 -05:00
martbost 9608d07544 Buy flow: balance pre-flight; Trust Wallet users get a drain-warning heads-up
Stops early when the wallet lacks the POL. Only Trust Wallet users see the
proportion warning (smaller package / add POL / other wallet); everyone else
goes straight to the wallet confirmation. Buy-pane tip + chatbot reworded.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-09 07:23:19 -05:00
martbost 68d68b1ebf Wallet: recover from a dead WalletConnect session instead of failing the buy
Probe the session after connect and retry once after a "disconnected" send
error by wiping the stale session and re-opening the picker (Trust kills the
session after its own security stop).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-09 06:41:09 -05:00
martbost 438b1921e9 Admin portal (/admin) + free house ads; POL amounts show two decimals
- /admin: email magic-code sign-in allowlisted to ADMIN_EMAIL, 12h admin
  session (cookie iap.adm, persisted in the volume). Bearer ADMIN_PASSWORD
  API access still works. Member area shows an Admin link for that email.
- House ads: admin places banner/text/login/solo/video/featured/visits
  campaigns owned by house@instantadpay.com that cost nothing; budget is
  only a delivery cap, spend is never charged or burned.
- Admin APIs: overview, all campaigns (+pause/resume any), members
  (+re-point sponsor), reports (+resolve), pending burns, rates/site
  config get+patch, creative upload.
- fmtPol rounds to two decimals everywhere (dashboard, toasts, prices).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-09 05:15:19 -05:00
martbost 4872fbbfd0 Bump wallet.js cache token to t so the disconnect fix actually loads
The prior commit changed wallet.js but the HTML still referenced ?v=r, so
cached browsers kept the old file. Point the HTML at ?v=t.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-08 14:09:32 -05:00
martbost 8be30bef13 Fix buy crash on wallets that return a numeric chainId
The chain guard did cur.toLowerCase() on the eth_chainId result. Per EIP-695
that's a hex string, but some wallets (Orlando's) return a number, so
cur.toLowerCase was undefined -> "cur.toLowerCase is not a function" aborted the
purchase. Compare chain ids numerically via a chainNum() normalizer (handles
hex string, decimal string, and number) in both the sendTx guard and ensureChain.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-08 11:51:42 -05:00
martbost 2cc21457bd Don't leave the connect modal stuck when already connected
On click, AppKit's session hasn't always rehydrated yet, so currentAddress()
returned null and we popped the picker even for an already-connected wallet —
and nothing closed it, leaving it stuck on screen after a purchase. Wait briefly
for the existing session to rehydrate before opening the picker, and always
close the modal once we have an address.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-08 11:41:07 -05:00
martbost 7b131dad28 Gas floor fix, chain guard, and dashboard footer
- wallet.js: fetch correct EIP-1559 fees from the network (Amoy/Polygon Bor
  enforce a ~25-30 gwei priority floor MetaMask's estimate misses) via new
  /api/gas; hard chain-guard before signing so a tx never lands on the wrong
  network; accept the wallet's usual networks in AppKit so its modal stops
  looping and drive add+switch ourselves.
- chain.js: suggestedFees() from eth_maxPriorityFeePerGas + base fee.
- Dashboard: move the Site links (Ad packages / Live ledger / The contract)
  out of the sidebar into a page footer; hide the sidebar scrollbar.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-08 10:49:34 -05:00
martbost 09804cda22 Fix wallet add/switch-network: use a clean RPC, not the WC proxy
AppKit's built-in networks advertise rpc.walletconnect.org/v1/?chainId=…&projectId=…
as the chain RPC. Wallets reject that query-string URL as "Invalid URL" when
adding/switching the network — Trust showed "Invalid URL", desktop MetaMask
looped on Switch Network, and the mobile buy failed because the switch to Amoy
never completed. Clone the chain with our clean public RPC before handing it to
AppKit/Wagmi.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-08 10:14:23 -05:00
martbost 7d6818a8d4 Buy flow: survive mobile app-switch fetch drops
Mobile drops in-flight fetches when the page returns from the wallet app, which
aborted the purchase before the tx ("Failed to fetch"). Retry /api/me and
/api/sponsor, and make waitTx keep polling through transient fetch failures.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-08 10:08:34 -05:00
martbost bd4040ff4f Rebuild wallet connect on Reown AppKit (universal, all wallets, working icons)
Replaces the low-level ethereum-provider + deprecated modal (broken icons, jank)
with Reown AppKit loaded from the CDN — the standard connector every wallet
supports, with QR + mobile deep-links and a polished picker. Keeps the exact
SIWE sign-in and contract buy/activate logic, driving AppKit's EIP-1193 provider.
CSP widened for the AppKit SDK/RPC + a worker.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-08 09:48:38 -05:00
martbost b0f807f6a5 WalletConnect buy: stop overriding gas (let wallet set fees)
Setting fees made MetaMask show an unclearable "site-suggested fee" alert. Hand
fee control back to the wallet; its own estimate meets Amoy's minimum.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-08 09:38:15 -05:00
martbost 952f2367d6 WalletConnect: always show the wallet picker (no auto-deeplink to last wallet)
Clear WALLETCONNECT_DEEPLINK_CHOICE before each connect so users aren't trapped
auto-launching a wallet that can't finish (e.g. Trust on Amoy); the All Wallets
picker shows every time so they can choose MetaMask.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-08 09:32:38 -05:00
martbost 89d636df82 Disconnect wallet: clear all WC state + reload so the picker truly reappears
Clearing localStorage alone wasn't enough — WalletConnect keeps the last wallet
and session in memory, so it auto-reconnected the same wallet. Broadened the
storage purge and reload the page after disconnect, guaranteeing a clean pick.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-08 09:26:34 -05:00
martbost 379a5352ea Add "Disconnect wallet" button (clears stuck WC session / switch wallets)
A stuck WalletConnect request or wrong-wallet choice could trap testers. New
IAPWallet.disconnect() drops the WC session, forgets the chosen provider, and
clears WC localStorage so the next connect re-offers the picker. Surfaced as a
"Disconnect wallet" button on the Wallet pane.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-08 09:21:42 -05:00
martbost d23eb10c58 WalletConnect buy: lower maxFeePerGas ceiling to 50 gwei
250 gwei ceiling tripped MetaMask's "network fee higher than necessary" alert on
Amoy (base fee ~0). 50 gwei keeps priority above Polygon's ~25 gwei floor while
reading as a normal fee, so buyers get a clean Confirm.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-08 09:12:19 -05:00
martbost 7469a4907c WalletConnect buy: set EIP-1559 fees above Polygon's min priority fee
Amoy/Polygon reject txs whose priority fee is under ~25 gwei ("gas tip below
minimum"); wallets lowball it. Set explicit maxPriorityFeePerGas 30 gwei,
maxFeePerGas 250 gwei ceiling, and a 600k gas limit so buys/activations aren't
rejected or mis-estimated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-08 09:07:00 -05:00
martbost 90e33a42b5 WalletConnect: add redirect metadata (return to dApp after wallet approvals)
Sets metadata.redirect so mobile wallets bounce the user back to /my after each
approval instead of stranding them in the wallet app.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-08 08:56:10 -05:00
martbost dcfc5ca86e WalletConnect: make the chain optional (fixes Trust hang on Amoy)
Requiring Amoy (80002) in the session namespaces made Trust Wallet hang on
"redirecting" — testnet wallets that don't natively list Amoy can't satisfy a
required-chain proposal. Switched to optionalChains so the session establishes;
the chain is switched/added after connect.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-08 08:44:21 -05:00
martbost 9cff0dc667 WalletConnect: gate to public chains (137/Amoy), not the private rehearsal
External wallets reject a WalletConnect session for the private rehearsal chain
(31337) with "user rejected", since they don't recognize custom chains. Only
offer WalletConnect on chains wallets know (Polygon mainnet / Amoy); it lights up
automatically when the site points at a public chain. Rehearsal keeps the
injected/dApp-browser path.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-08 08:05:58 -05:00
martbost f0e42e47bb Add WalletConnect: connect from any mobile browser (no dApp browser)
Mobile users in a normal browser hit "no wallet found" because EIP-6963 only
sees injected/extension wallets. WalletConnect fixes it: the connect picker now
offers WalletConnect (auto-selected when no injected wallet is present), which
shows a QR on desktop and opens the wallet app directly on mobile — no in-app
dApp browser, no second login. Lazy-loads the @walletconnect/ethereum-provider
UMD from jsdelivr; the result is a plain EIP-1193 provider so sign/switch/buy are
unchanged. CSP widened for the SDK + WC relay. Project id lives in site config
(public value); gated so nothing changes until it's set.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-08 07:36:07 -05:00
martbost 0c803c07cb Legal pages (Terms, Privacy, Disclaimer) + always-present footer
- New /terms, /privacy, /disclaimer pages in the site style with real content
  tailored to an on-chain ad+referral platform (no income guarantee, crypto
  risk, rehearsal note, privacy of email/wallet/profile, acceptable use).
- renderNav now appends a persistent footer with legal + site links on every
  public page; member-area sidebar gets a Legal link group.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-07 09:38:08 -05:00
martbost 4e50e02e9d Ship logo (header + favicon) + bolder downline level labels
- Logo lockup (mint lightning/arrow icon + InstantAdPay wordmark) replaces the
  text wordmark in the back-office sidebar and public nav; icon added as the
  favicon on every page. Assets: /logo.png, /logo-icon.png.
- Downline level captions (LEVEL 1 / LEVEL 2 …) are now a bold mint pill instead
  of faint grey — far more readable.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-07 09:26:03 -05:00
martbost ae580b966c 125x125 sidebar banner (sized ad serving) + shorts report link
- New 125x125 "square button" banner size; ad serve now filters by width/height,
  and the sidebar slot serves a 125x125 banner (empty until such inventory exists).
- Shorts reel gets a "report this short" link (posts to /api/report-ad).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-07 08:47:17 -05:00