Promo tools built out: posts, text-a-friend, swipes, banner kit, handouts, objections

- Social posts for X / Facebook / LinkedIn / Telegram-WhatsApp with post and
  share buttons; 5 SMS-sized Text-a-friend messages (Text it / WhatsApp /
  Telegram / Copy); email swipes short, standard, long, follow-up.
- Banner kit adds 1080x1080, 1080x1920 and 1280x720 (tools/gen-social-banners.cjs)
  with Download buttons.
- Printable half-sheet handouts at /flyers with the member's QR + invite link
  (qrlib.js, two per letter page, cut line, print-one).
- Objection handling bank: truth + ready-to-send reply per objection.
- Invite-link strip, Branded Voice CTA, pill menu covers every kit; chatbot
  canned answer + prompt facts updated. Angle links carry ?v= for the
  upcoming hook pages.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
martbost
2026-09-09 05:54:18 -05:00
parent e7ee7d0614
commit f3f9662d99
13 changed files with 2752 additions and 47 deletions
+81
View File
@@ -0,0 +1,81 @@
// Printable half-sheet handouts with the member's QR + invite link.
// The ref token (username, share code or member #) comes from ?ref=, then
// localStorage (set by Promo tools), then the signed-in account.
(function () {
'use strict';
var $ = function (id) { return document.getElementById(id); };
var ORIGIN = location.origin;
var DESIGNS = [
{ angle: 'instant', tag: 'Polygon · same-transaction payouts', h: 'Advertise &amp; earn.<br>Paid on-chain, <em>instantly.</em>',
lead: 'Every ad package splits to real wallets the second it sells. No pending. No withdraw button.',
bullets: ['Free to join with just an email', 'Ad packages from $5, seven ad formats', 'Every payout public on the blockchain'] },
{ angle: 'adspend', tag: 'For marketers who buy traffic', h: 'You were buying <em>ads anyway.</em>',
lead: 'Here the ad spend in your line pays you, in the same transaction, on a public ledger.',
bullets: ['Banners, text, login, solo, video, featured, visits', 'Dwell-timed views: real people, not scripts', '50 / 20 / 10 sponsor split, written in code'] },
{ angle: 'free', tag: 'Costs nothing to try', h: 'Watch first.<br><em>Spend never.</em>',
lead: 'Join free, view a few ads, earn credits, and run your first campaign for zero dollars.',
bullets: ['Email sign-in, no password, no wallet needed', 'Earn credits for your attention every day', 'Upgrade only if you want more reach'] }
];
function getRef() {
var q = new URLSearchParams(location.search).get('ref');
if (q && /^[A-Za-z0-9_]{1,20}$/.test(q)) return q;
try { var s = localStorage.getItem('iap.ref'); if (s && /^[A-Za-z0-9_]{1,20}$/.test(s)) return s; } catch (e) {}
return '';
}
function qrSvg(url) {
try { var q = qrcode(0, 'M'); q.addData(url); q.make(); return q.createSvgTag({ cellSize: 4, margin: 2, scalable: true }); } catch (e) { return ''; }
}
function el(tag, cls, html) { var d = document.createElement(tag); if (cls) d.className = cls; if (html != null) d.innerHTML = html; return d; }
function card(d, url, shortUrl) {
var hs = el('div', 'hs');
hs.appendChild(el('div', 'lines'));
var inner = el('div', 'in');
var logo = el('img', 'logo'); logo.src = '/logo.png'; logo.alt = 'InstantAdPay'; inner.appendChild(logo);
inner.appendChild(el('span', 'tag', d.tag));
inner.appendChild(el('h2', null, d.h));
inner.appendChild(el('p', 'lead', d.lead));
var ul = el('ul'); d.bullets.forEach(function (b) { ul.appendChild(el('li', null, b)); }); inner.appendChild(ul);
inner.appendChild(el('div', 'foot', 'Scan the code or visit <b>' + shortUrl + '</b>'));
hs.appendChild(inner);
var ov = el('div', 'hs-qrov');
ov.appendChild(el('div', 'q', url ? qrSvg(url) : '<div class="qph"></div>'));
ov.appendChild(el('div', 'scan', 'SCAN ME'));
ov.appendChild(el('div', 'lnk', shortUrl));
hs.appendChild(ov);
hs.appendChild(el('div', 'hs-disc', 'Advertising, not investing. No income is guaranteed; results depend on your effort. Crypto carries risk of loss.'));
return hs;
}
function build(ref) {
var wrap = $('halfsheets'); wrap.innerHTML = '';
DESIGNS.forEach(function (d) {
var url = ref ? ORIGIN + '/join/' + ref + '?v=' + d.angle : '';
var shortUrl = ref ? 'instantadpay.com/join/' + ref : 'instantadpay.com';
var page = el('div', 'hs-page');
var pr = el('button', 'btn small hs-print', 'Print this one'); pr.type = 'button';
pr.addEventListener('click', function () {
document.body.classList.add('print-one'); page.classList.add('printing');
window.print();
setTimeout(function () { document.body.classList.remove('print-one'); page.classList.remove('printing'); }, 400);
});
page.appendChild(pr);
page.appendChild(card(d, url, shortUrl));
page.appendChild(el('div', 'hs-cut', '✂ cut here'));
var copy = card(d, url, shortUrl); copy.classList.add('hs-copy'); page.appendChild(copy);
wrap.appendChild(page);
});
}
var input = $('flRef');
var ref = getRef();
if (ref) input.value = ref;
build(ref);
input.addEventListener('input', function () {
var v = input.value.trim();
if (!v || /^[A-Za-z0-9_]{1,20}$/.test(v)) { build(v); try { if (v) localStorage.setItem('iap.ref', v); } catch (e) {} }
});
if (!ref) { // signed-in member without a stored ref: ask the site
fetch('/api/me').then(function (r) { return r.json(); }).then(function (me) {
var t = me && me.signedIn && (me.username || me.refCode || me.memberId);
if (t && !input.value) { input.value = String(t); build(String(t)); }
}).catch(function () {});
}
})();
+11 -35
View File
@@ -401,7 +401,7 @@
} catch (e) {}
// ready-to-send share message + promo tools, personalized
const link = location.origin + '/join/' + (d.username || d.refCode || d.memberId || '');
fillPromo(link);
fillPromo(link, d);
if (d.username) { // wall link rides the username
const wl = location.origin + '/wall/' + d.username;
$('wallLine').textContent = wl;
@@ -1013,7 +1013,7 @@
b.addEventListener('click', () => setEarnSub(b.dataset.earn)));
// promo tools: pill menu switches between posts / swipes / banners / wall / videos
function setPromoSub(name) {
const ids = ['posts', 'swipe', 'banners', 'wall', 'videos'];
const ids = ['posts', 'text', 'swipe', 'banners', 'flyers', 'wall', 'objections', 'videos'];
if (!ids.includes(name)) name = 'posts';
ids.forEach(id => { const el = $('promo-' + id); if (el) el.hidden = id !== name; });
document.querySelectorAll('.promo-pills [data-promo]').forEach(b => {
@@ -1124,45 +1124,18 @@
}
$('ibBack').addEventListener('click', ev => { ev.preventDefault(); clearInterval(ibTimer); loadInbox(); });
// ── promo tools: Branded Voice copy, personalized with the member link ──
const PROMO_POSTS = [
'A membership site where money is handled by code, not people. Every purchase splits instantly to sponsor wallets on the Polygon blockchain. Nothing to withdraw. The money just lands in your wallet. Plus you earn ad credits for viewing ads while you\'re there. {{LINK}}',
'No withdrawal button. Think about that. A smart contract on Polygon splits every payment the second it hits. 50% to the sponsor. 20% to the next level. 10% to the next. Lands straight in your own wallet. No button to push. No waiting. Just money where it belongs. See how it works: {{LINK}}',
'Every payment is public on the blockchain. You can watch the ledger move in real time. Every split, every wallet, every transaction. Nothing hidden. Nothing you have to take on faith. That\'s the difference between a platform that asks for trust and one where trust isn\'t needed. See for yourself: {{LINK}}'
];
const PROMO_SWIPE = {
subject: 'Your Wallet Gets Paid Instantly..',
body: 'You know the usual drill. Someone buys on your link, you wait for a payout. Maybe days. Maybe an approval hold. Maybe a "your account is under review."\n\nInstantAdPay doesn\'t work like that.\n\nA smart contract on the Polygon blockchain handles every purchase the second it happens. 50% to the sponsor. 20% to the next level. 10% to the one after that. 20% to the platform. Each split lands directly in your own wallet. No withdrawal button. No "request payout." No approval queue.\n\nThe money just shows up.\n\nYou can watch every transaction on the public ledger. Real time. Anyone can verify it.\n\nFree to join. Packages from $5 to $250. No income promises. It\'s advertising, not investing.\n\n{{LINK}}'
};
function promoBlock(text) {
const div = document.createElement('div');
div.className = 'promo-block';
div.textContent = text;
const btn = document.createElement('button');
btn.className = 'btn small sec';
btn.textContent = 'Copy';
btn.addEventListener('click', async () => {
try { await navigator.clipboard.writeText(text); IAP.status('Copied. Paste it anywhere.', 'ok'); }
catch (e) { IAP.status('Copy failed. Select the text instead.', 'bad'); }
});
div.appendChild(btn);
return div;
}
function fillPromo(link) {
const posts = $('promoPosts');
if (!posts || posts.dataset.filled === link) return;
posts.dataset.filled = link;
posts.innerHTML = '';
for (const p of PROMO_POSTS) posts.appendChild(promoBlock(p.replace('{{LINK}}', link)));
const sw = $('promoSwipeWrap');
sw.innerHTML = '';
sw.appendChild(promoBlock('Subject: ' + PROMO_SWIPE.subject + '\n\n' + PROMO_SWIPE.body.replace('{{LINK}}', link)));
// ── promo tools: content + rendering live in promo.js (IAPPromo.fill) ──
function fillPromo(link, me) {
if (window.IAPPromo) IAPPromo.fill(link, me || {});
// banner kit
const bwrap = $('promoBanners');
if (bwrap && !bwrap.dataset.filled) {
bwrap.dataset.filled = '1';
const BANNERS = [
{ file: 'iap-hero-1200x630.png', size: '1200×630 (social / hero)' },
{ file: 'iap-1080x1080.png', size: '1080×1080 (Instagram / Facebook square)' },
{ file: 'iap-1080x1920.png', size: '1080×1920 (story / reel)' },
{ file: 'iap-1280x720.png', size: '1280×720 (Telegram group post)' },
{ file: 'iap-728x90.png', size: '728×90 (leaderboard)' },
{ file: 'iap-336x280.png', size: '336×280 (large rectangle)' },
{ file: 'iap-300x250.png', size: '300×250 (rectangle)' },
@@ -1179,6 +1152,9 @@
d.className = 'pb-item';
d.innerHTML = '<img src="/banners/' + b.file + '?v=3" alt="InstantAdPay banner ' + b.size + '" loading="lazy">'
+ '<div class="pb-row"><span class="pb-size">' + b.size + '</span></div>';
const dl = document.createElement('a');
dl.className = 'btn small'; dl.textContent = 'Download'; dl.href = '/banners/' + b.file; dl.setAttribute('download', b.file);
d.querySelector('.pb-row').appendChild(dl);
const btn = document.createElement('button');
btn.className = 'btn small sec';
btn.textContent = 'Copy image URL';
+152
View File
@@ -0,0 +1,152 @@
// Promo tools content + rendering for the member area (Promo tools pane).
// Everything is personalized from the member's join link. Angle links carry
// ?v=<angle> so they keep working when the matched squeeze pages ship.
// Copy rules: no income promises, package prices in dollars are fine, never a
// POL figure, no em dashes.
window.IAPPromo = (function () {
const $ = id => document.getElementById(id);
const esc = s => String(s == null ? '' : s).replace(/[&<>"']/g, c => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c]));
const status = (m, c) => (window.IAP && IAP.status) ? IAP.status(m, c) : console.log(m);
const angleLink = (link, angle) => angle ? link + '?v=' + angle : link;
// ── social posts ──
const POSTS = [
{ net: 'X', label: 'X · instant payout', text: 'No withdraw button. A smart contract on Polygon splits every ad package the second it sells: 50% to the sponsor, 20% to level 2, 10% to level 3. Lands in your own wallet. Watch the ledger move: {{LINK:instant}}' },
{ net: 'X', label: 'X · advertiser', text: 'Marketers: you were buying traffic anyway. Here the ad spend in your line pays you, in the same transaction, on a public ledger. Packages from $5. Join free: {{LINK:adspend}}' },
{ net: 'X', label: 'X · free join', text: 'Join free. View a few ads, earn credits, run your first campaign for zero dollars. Seven ad formats, real dwell-timed attention, every payout public on Polygon. {{LINK:free}}' },
{ net: 'Facebook', label: 'Facebook · story post', text: 'I got tired of "your commission is pending." So I joined a platform where there is no pending. A smart contract on the Polygon blockchain splits every ad package the moment it sells: half to the sponsor, then levels two and three, then the platform. It lands in your own wallet in seconds. No approval queue, no withdrawal button, no 15th of the month.\n\nYou are buying real advertising: banners, text ads, login ads, solo ads to member inboxes, video ads, featured links and verified visits. Members earn credits for their attention, so the ads actually get seen.\n\nJoin free, look around, and check the public ledger before you spend a dollar: {{LINK:instant}}\n\nNo income promises. It is advertising, not investing, and crypto carries risk.' },
{ net: 'Facebook', label: 'Facebook · skeptic angle', text: 'Every online program I ever joined asked me to trust a back office. This one does not. Every payout is a public transaction on Polygon you can look up yourself, with the wallet addresses and amounts right there. The split is written in a verified smart contract nobody can quietly edit.\n\nFree to join with just an email. The wallet comes out only if you buy a package or switch on payouts. Packages run $5 to $250, and every one of them is ad delivery you can watch running.\n\nTake the tour: {{LINK:ledger}}' },
{ net: 'LinkedIn', label: 'LinkedIn · professional', text: 'An experiment in transparent affiliate payouts: InstantAdPay sells advertising packages ($5 to $250) whose sponsor commissions are settled by a verified smart contract on Polygon in the same transaction as the purchase. 50 / 20 / 10 across three levels, 20 percent to the platform, every payment public on the chain.\n\nWhat I find interesting is not the commission. It is that "pending payout" stops being a concept. If you buy traffic for a living and want to see how same-transaction settlement works in practice, the tour is free: {{LINK:adspend}}' },
{ net: 'Telegram', label: 'Telegram or WhatsApp group', text: 'Quick one for the group. Ad platform on Polygon where every package splits to sponsor wallets the second it sells. No withdrawals, no pending. Free to join by email, packages from $5, seven ad formats, and you earn credits for viewing. Tour here: {{LINK:instant}}' },
{ net: 'Telegram', label: 'Telegram or WhatsApp group · free angle', text: 'If you want to test an ad network without spending anything: join free, view a handful of ads, earn credits, run your first campaign on the house. Payouts are on the public Polygon ledger. {{LINK:free}}' }
];
// ── text a friend (SMS-sized) ──
const TEXTS = [
{ angle: '', title: '"Thought of you"', text: 'Hey, found an ad platform where the commission lands in your wallet the second someone buys. No pending, no withdraw button. Free to look: {{LINK}}' },
{ angle: 'instant', title: '"Before the page reloads"', text: 'Random thought. What if your commission showed up before the thank-you page finished loading? That is literally how this works: {{LINK:instant}}' },
{ angle: 'adspend', title: '"You buy ads anyway"', text: 'You already buy traffic. This one pays your line every time someone in it buys ads, same transaction, on Polygon. Two-minute look: {{LINK:adspend}}' },
{ angle: 'free', title: '"Costs nothing to try"', text: 'Try this without spending a dollar: join free, view a few ads, earn credits, run your first campaign. {{LINK:free}}' },
{ angle: 'ledger', title: '"No back office"', text: 'Remember waiting on payouts that never came? This one has no back office. Every payment is public on the blockchain. Check it yourself: {{LINK:ledger}}' }
];
// ── email swipes ──
const SWIPES = [
{ tier: 'Short', subject: 'Paid the second it sells', body: 'Quick one.\n\nI joined an ad platform where every package splits to sponsor wallets in the same transaction as the sale. No pending payouts, no withdraw button. It is on the Polygon blockchain, and every payment is public.\n\nJoin free, look at the ledger, decide later: {{LINK:instant}}\n\nNo income promises. Advertising, not investing.' },
{ tier: 'Standard', subject: 'Your wallet gets paid instantly', body: 'You know the usual drill. Someone buys on your link, you wait for a payout. Maybe days. Maybe an approval hold. Maybe a "your account is under review."\n\nInstantAdPay does not work like that.\n\nA smart contract on the Polygon blockchain handles every purchase the second it happens. 50% to the sponsor. 20% to the next level. 10% to the one after that. 20% to the platform. Each split lands directly in your own wallet. No withdrawal button. No "request payout." No approval queue.\n\nThe money just shows up.\n\nYou can watch every transaction on the public ledger. Real time. Anyone can verify it.\n\nFree to join. Packages from $5 to $250. No income promises. It is advertising, not investing.\n\n{{LINK}}' },
{ tier: 'Long', subject: 'The ad network with no pending payouts', body: 'Let me tell you what you are actually looking at, because "crypto ad platform" can mean anything.\n\nInstantAdPay sells advertising. Five packages, $5 to $250. A package mints ad credits, and one credit is one cent of delivery across seven formats: display banners, text ads, full-screen login ads, solo ads delivered into member inboxes, video ads, featured links, and verified visits. Members earn credits for their attention, with a timer that pauses when they look away, so your ad is seen by a person, not a script.\n\nNow the part that made me join.\n\nWhen anyone in your line buys a package, a verified smart contract on Polygon splits the payment in that same transaction: 50 percent to their direct sponsor, 20 percent to level two, 10 percent to level three, 20 percent to the platform. It lands in real wallets in seconds. There is no balance to withdraw because nothing is ever held.\n\nEvery one of those payments is public. Open the ledger, click a transaction, read it on Polygonscan.\n\nJoining is free and only needs an email. Your wallet comes out when you buy a package or switch on payouts. If you never spend a dollar, you can still view ads, earn credits, and run a small campaign on those.\n\nHave a look: {{LINK:instant}}\n\nOne honest line: nobody is promising you an income. Results depend on your effort, and crypto carries risk of loss.' },
{ tier: 'Follow-up', subject: 'Did you see the ledger?', body: 'Following up on the ad platform I sent over.\n\nIf you only look at one thing, look at the live ledger. Every purchase and every payout, with real wallet addresses, in the order they happened. That is the whole pitch: nothing to take on faith.\n\n{{LINK:ledger}}\n\nIf it is not for you, no worries at all.' }
];
// ── objection bank (truth + say this) ──
const OBJECTIONS = [
{ q: 'Is this a pyramid scheme?', truth: 'You are buying advertising that actually runs: a credit is one cent of delivery across seven live formats, and members earn credits for dwell-timed attention. Sponsor payments are referral commissions written as constants in a verified contract, paid only when a real package sells. Nobody has to recruit to use the ads, and joining costs nothing.', say: 'Fair question. Here is the test I use: is there a real product that people would buy with no referral involved? Here the product is ad delivery, seven formats, one cent per credit, and you can watch your campaign serving. The sponsor split is a referral commission on those sales, fixed in a public contract. Join free and run the ads with zero referrals if you like. {{LINK:free}}' },
{ q: 'I do not have a crypto wallet. I am not technical.', truth: 'Joining needs only an email and a 6-digit code. The wallet comes out only when someone buys a package or switches on payouts, and the site walks them through it. A card on-ramp (MoonPay) delivers POL straight to their own wallet. Email first, wallet second.', say: 'You do not need one to join. It is email only, no password even. The wallet shows up later, only if you decide to buy a package or want payouts, and the site walks you through it step by step. Start here and look around first: {{LINK}}' },
{ q: 'What am I actually buying?', truth: 'Ad credits minted on-chain the moment a package is bought: $5 = 500, $20 = 2,000, $50 = 5,500, $100 = 12,000, $250 = 32,500. Credits spend on banners, text ads, login ads, solo ads to member inboxes, video ads, featured links and verified visits, with live stats per campaign. Only the buyer\'s campaigns can spend them.', say: 'Advertising. A package mints credits, one credit is one cent of delivery, and you spend them on seven ad formats from your own dashboard with live stats. The $5 package is 500 credits, the $250 package is 32,500. You can see the formats before you spend anything: {{LINK:adspend}}' },
{ q: 'Who holds my money?', truth: 'Nobody. A purchase is one Polygon transaction that splits to the sponsor line and the platform wallets immediately. The site never holds balances, and the contract source is verified on Polygonscan and Sourcify. Overpayment refunds itself in the same transaction.', say: 'That is the part I like most: nobody holds it. The purchase is a single blockchain transaction that pays the sponsor line and the platform in the same moment. There is no balance sitting anywhere waiting for a withdrawal. The contract code is public and verified, and every payment is on the ledger: {{LINK:ledger}}' },
{ q: 'What if POL drops?', truth: 'Packages are priced in dollars and settled in POL at the live Chainlink rate at the moment of purchase. Payouts arrive as POL in the recipient\'s own wallet immediately, so what they do with it is their call. This is advertising, not an investment, and POL is volatile like any crypto asset.', say: 'Packages are dollar priced, so $20 is $20 worth of POL at that moment. Payouts land in your wallet right away, and it is your money from that second. Crypto does move, so treat it as advertising you bought, not an investment. {{LINK}}' },
{ q: 'Why would anyone buy ads here?', truth: 'The members are marketers who already buy traffic. Viewers earn credits only after a server-timed dwell, so impressions are real people. Solo ads reach member inboxes with a guaranteed delivery count, verified visits are one unique member per visit, and banner and text ads also syndicate to a partner ad network.', say: 'Because the people on it are marketers, and they get paid to actually look. Every view is dwell-timed on the server, solo ads land in real inboxes with a guaranteed count, and verified visits are one real person each. You can watch your own campaign serve: {{LINK:adspend}}' },
{ q: 'I do not know anyone to refer.', truth: 'Referrals are optional. Members can view ads, earn credits and advertise with no line at all. When they do refer, every direct buyer pays them 50 percent from the very first package, and their line banner is shown to their next three levels during welcome tours.', say: 'You do not have to refer anyone. Join free, earn credits by viewing, run ads. If one person ever joins through you and buys a $5 package, half of it hits your wallet in that transaction. That is the whole referral side, and it is optional. {{LINK:free}}' },
{ q: 'How much can I make?', truth: 'No income is guaranteed or implied. Sponsor commissions are 50 / 20 / 10 across three levels on packages that actually sell; level 2 needs 2 qualifying buyers ($20 or more) and level 3 needs 5. Results depend on the member\'s effort, and crypto carries risk of loss. Never quote a figure.', say: 'Nobody can tell you that, and I would not trust anyone who did. What I can tell you is exactly how it is split: 50 percent to the direct sponsor, 20 and 10 to the next two levels, paid the moment a package sells, all public. What that adds up to depends entirely on what you build. {{LINK:instant}}' }
];
function fillLink(text, link) {
return text.replace(/\{\{LINK(?::([a-z]+))?\}\}/g, (m, a) => angleLink(link, a));
}
function copyBtn(text, label) {
const b = document.createElement('button');
b.className = 'btn small sec'; b.type = 'button'; b.textContent = label || 'Copy';
b.addEventListener('click', async () => {
try { await navigator.clipboard.writeText(text); status('Copied. Paste it anywhere.', 'ok'); }
catch (e) { status('Copy failed. Select the text instead.', 'bad'); }
});
return b;
}
function block(text, head, extraBtns) {
const div = document.createElement('div');
div.className = 'promo-block';
if (head) { const h = document.createElement('div'); h.className = 'pb-head'; h.innerHTML = head; div.appendChild(h); }
const pre = document.createElement('div'); pre.className = 'pb-text'; pre.textContent = text; div.appendChild(pre);
const row = document.createElement('div'); row.className = 'pb-actions';
row.appendChild(copyBtn(text));
(extraBtns || []).forEach(b => row.appendChild(b));
div.appendChild(row);
return div;
}
function linkBtn(href, label, cls) {
const a = document.createElement('a');
a.className = 'btn small ' + (cls || 'sec'); a.href = href; a.textContent = label;
if (!/^sms:/.test(href)) { a.target = '_blank'; a.rel = 'noopener'; }
return a;
}
function fill(link, me) {
const token = (me && (me.username || me.refCode || me.memberId)) || '';
// invite strip
if ($('promoLink')) $('promoLink').textContent = link;
if ($('promoLinkCopy')) $('promoLinkCopy').onclick = async () => {
try { await navigator.clipboard.writeText(link); status('Link copied.', 'ok'); } catch (e) { status('Copy failed.', 'bad'); }
};
// posts
const posts = $('promoPosts');
if (posts && posts.dataset.filled !== link) {
posts.dataset.filled = link; posts.innerHTML = '';
for (const p of POSTS) {
const text = fillLink(p.text, link);
const extras = [];
if (p.net === 'X') extras.push(linkBtn('https://twitter.com/intent/tweet?text=' + encodeURIComponent(text), 'Post on X'));
if (p.net === 'Facebook') extras.push(linkBtn('https://www.facebook.com/sharer/sharer.php?u=' + encodeURIComponent(angleLink(link, (/\{\{LINK:([a-z]+)\}\}/.exec(p.text) || [])[1] || '')), 'Share on Facebook'));
if (p.net === 'LinkedIn') extras.push(linkBtn('https://www.linkedin.com/sharing/share-offsite/?url=' + encodeURIComponent(angleLink(link, 'adspend')), 'Share on LinkedIn'));
if (p.net === 'Telegram') extras.push(linkBtn('https://t.me/share/url?url=' + encodeURIComponent(angleLink(link, (/\{\{LINK:([a-z]+)\}\}/.exec(p.text) || [])[1] || '')) + '&text=' + encodeURIComponent(text.replace(/\s*\{\{LINK[^}]*\}\}\s*$/, '').replace(/https?:\/\/\S+$/, '').trim()), 'Share on Telegram'));
posts.appendChild(block(text, '<span class="pb-net">' + esc(p.label) + '</span>', extras));
}
}
// text a friend
const texts = $('promoTexts');
if (texts && texts.dataset.filled !== link) {
texts.dataset.filled = link; texts.innerHTML = '';
TEXTS.forEach((t, i) => {
const text = fillLink(t.text, link);
const enc = encodeURIComponent(text);
const tgUrl = angleLink(link, t.angle);
const tgLead = text.replace(/https?:\/\/\S+/, '').trim();
const extras = [
linkBtn('sms:?&body=' + enc, 'Text it', ''),
linkBtn('https://wa.me/?text=' + enc, 'WhatsApp'),
linkBtn('https://t.me/share/url?url=' + encodeURIComponent(tgUrl) + '&text=' + encodeURIComponent(tgLead), 'Telegram')
];
texts.appendChild(block(text, '<span class="pb-net">Text ' + (i + 1) + (t.angle ? ' · ' + esc(t.angle) + ' angle' : ' · general') + '</span> <b>' + esc(t.title) + '</b>', extras));
});
}
// swipes
const sw = $('promoSwipeWrap');
if (sw && sw.dataset.filled !== link) {
sw.dataset.filled = link; sw.innerHTML = '';
for (const s of SWIPES) {
const text = 'Subject: ' + s.subject + '\n\n' + fillLink(s.body, link);
sw.appendChild(block(text, '<span class="pb-net">' + esc(s.tier) + '</span> <b>' + esc(s.subject) + '</b>'));
}
}
// objections
const ob = $('promoObjections');
if (ob && ob.dataset.filled !== link) {
ob.dataset.filled = link; ob.innerHTML = '';
for (const o of OBJECTIONS) {
const d = document.createElement('details'); d.className = 'obj';
const s = document.createElement('summary'); s.textContent = o.q; d.appendChild(s);
const body = document.createElement('div'); body.className = 'obj-body';
const t = document.createElement('div'); t.className = 'obj-truth';
t.innerHTML = '<span class="lab">The truth</span><p>' + esc(o.truth) + '</p>'; body.appendChild(t);
const say = fillLink(o.say, link);
const sayEl = block(say, '<span class="lab lab-say">Say this</span>');
sayEl.classList.add('obj-say'); body.appendChild(sayEl);
d.appendChild(body); ob.appendChild(d);
}
}
// handouts link
if ($('flyersOpen')) $('flyersOpen').href = '/flyers' + (token ? '?ref=' + encodeURIComponent(token) : '');
try { if (token) localStorage.setItem('iap.ref', String(token)); } catch (e) {}
}
return { fill, POSTS, TEXTS, SWIPES, OBJECTIONS };
})();
File diff suppressed because it is too large Load Diff
+24
View File
@@ -582,3 +582,27 @@ img{max-width:100%}
.sh-report{display:block;text-align:center;margin-top:10px;color:rgba(255,255,255,.45);font-size:12px}
.sh-report:hover{color:rgba(255,255,255,.8)}
/* ── promo tools (2026-09-09) ── */
.promo-strip{display:flex;justify-content:space-between;align-items:center;gap:14px;flex-wrap:wrap;padding:16px 20px}
.promo-pills{margin:4px 0 14px}
.promo-block .pb-head{display:flex;gap:8px;align-items:center;margin-bottom:8px;font-size:13.5px}
.promo-block .pb-net{font-family:var(--mono);font-size:11px;letter-spacing:.08em;text-transform:uppercase;color:var(--mint)}
.promo-block .pb-text{white-space:pre-wrap;font-size:14.5px;line-height:1.5}
.promo-block .pb-actions{display:flex;gap:8px;flex-wrap:wrap;margin-top:10px}
.promo-block .pb-actions .btn{margin-top:0}
.promo-bv{display:flex;gap:12px;align-items:center;flex-wrap:wrap;margin-top:16px;padding:14px 16px;border:1px dashed var(--line-strong);border-radius:12px}
.promo-bv b{font-family:var(--disp)}
.obj-wrap{display:flex;flex-direction:column;gap:8px}
.obj{border:1px solid var(--line);border-radius:12px;background:rgba(4,8,7,.4)}
.obj summary{cursor:pointer;padding:12px 16px;font-family:var(--disp);font-weight:700;font-size:15px;list-style:none;display:flex;justify-content:space-between;gap:10px}
.obj summary::after{content:"+";color:var(--mint);font-weight:800}
.obj[open] summary::after{content:"–"}
.obj summary::-webkit-details-marker{display:none}
.obj summary::marker{content:""}
.obj summary{text-align:left}
.obj-body{padding:0 16px 14px;display:grid;gap:12px}
.obj .lab{display:inline-block;font-family:var(--mono);font-size:11px;letter-spacing:.1em;text-transform:uppercase;color:var(--mint);margin-bottom:4px}
.obj .lab-say{color:var(--amber)}
.obj-truth p{margin:0;font-size:14.5px;color:var(--ink)}
.obj-say{margin:0}
Binary file not shown.

After

Width:  |  Height:  |  Size: 408 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 626 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 328 KiB

+70
View File
@@ -0,0 +1,70 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="robots" content="noindex">
<title>Printable handouts | InstantAdPay</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Sora:wght@600;700;800&display=swap">
<link rel="icon" type="image/png" href="/logo-icon.png">
<link rel="stylesheet" href="/assets/site.css?v=20260909a">
<style>
.flw{max-width:760px;margin:0 auto;padding:26px 16px 70px}
.fl-id{display:flex;gap:10px;flex-wrap:wrap;align-items:center;margin:14px 0 18px}
.fl-id input{max-width:220px}
.hs-page{margin:0 auto 26px;max-width:680px}
.hs{position:relative;aspect-ratio:8.5/5.5;border-radius:14px;overflow:hidden;box-shadow:0 10px 30px rgba(0,0,0,.45);
background:radial-gradient(120% 90% at 8% 0%,#16352c 0%,#0b1512 58%,#07100d 100%);color:#eef7f3;
font-family:var(--disp);-webkit-print-color-adjust:exact;print-color-adjust:exact;container-type:inline-size}
.hs .lines{position:absolute;inset:0;background:repeating-linear-gradient(0deg,rgba(67,232,195,.07) 0 1px,transparent 1px 5.2%)}
.hs .in{position:absolute;inset:0;padding:5% 6% 10.5%;display:flex;flex-direction:column;gap:1.8%}
.hs img.logo{width:30%;height:auto;display:block}
.hs .tag{display:inline-block;width:max-content;border:1px solid rgba(67,232,195,.5);color:#43e8c3;border-radius:999px;padding:.6% 2.4%;font-size:2.2cqw;font-weight:700;letter-spacing:.08em;text-transform:uppercase}
.hs h2{font-size:5.2cqw;line-height:1.05;margin:0;letter-spacing:-.02em;max-width:66%;text-wrap:balance}
.hs h2 em{font-style:normal;color:#43e8c3}
.hs p.lead{font-size:2.2cqw;line-height:1.35;margin:0;color:#b7cdc4;max-width:64%}
.hs ul{margin:0;padding:0;list-style:none;display:flex;flex-direction:column;gap:.6%;font-size:2.15cqw;max-width:64%;color:#eef7f3}
.hs ul li::before{content:"✓ ";color:#43e8c3;font-weight:800}
.hs .foot{display:none}
.hs .foot b{color:#43e8c3}
.hs-qrov{position:absolute;right:4%;top:50%;transform:translateY(-50%);width:24%;background:#fff;border-radius:6%/9%;
padding:2.4% 2% 1.8%;text-align:center;color:#0b1512;box-shadow:0 6px 20px rgba(0,0,0,.55)}
.hs-qrov .q{width:100%;line-height:0}
.hs-qrov .q svg{width:100%;height:auto;display:block}
.hs-qrov .q .qph{width:100%;aspect-ratio:1;border:2px dashed #99a;border-radius:6px;box-sizing:border-box}
.hs-qrov .scan{font-weight:800;letter-spacing:.12em;font-size:1.9cqw;margin-top:5%}
.hs-qrov .lnk{font-weight:700;font-size:1.6cqw;overflow-wrap:anywhere;line-height:1.2;color:#1c7c65}
.hs-disc{position:absolute;left:6%;bottom:2.4%;width:60%;font-size:1.45cqw;line-height:1.3;color:#8ba69c}
.hs-print{position:absolute;top:10px;right:10px;z-index:10}
.hs-copy,.hs-cut{display:none}
@media print{
@page{size:letter;margin:0}
body{background:#fff!important}
.fl-top,.fl-id,.fl-intro,.hs-print,#status{display:none!important}
.flw{max-width:none;padding:0;margin:0}
.hs-page{max-width:none;margin:0;height:11in;page-break-after:always;page-break-inside:avoid;display:flex;flex-direction:column}
.hs-page .hs{border-radius:0;box-shadow:none;flex:1;aspect-ratio:auto;display:flex}
.hs-copy{display:flex}
.hs-cut{display:flex;justify-content:center;align-items:center;gap:8px;border-top:2.5px dashed #98a2ad;font-size:10px;color:#667;padding:2px 0;background:#fff}
body.print-one .hs-page:not(.printing){display:none!important}
#halfsheets .hs-page:last-child{page-break-after:auto}
body.print-one .hs-page.printing{page-break-after:auto!important}
}
</style>
</head>
<body>
<main class="flw">
<div class="fl-top" style="display:flex;justify-content:space-between;align-items:center;gap:12px;flex-wrap:wrap">
<a href="/"><img src="/logo.png" alt="InstantAdPay" style="height:34px;display:block"></a>
<a class="btn small sec" href="/my#promo">← Back to Promo tools</a>
</div>
<p class="eyebrow" style="margin-top:22px">For members · belly-to-belly</p>
<h1 style="font-size:clamp(28px,5vw,40px);margin:6px 0 8px">Printable <em>handouts</em></h1>
<p class="fl-intro muted">Three half-sheet designs, two per letter page with a cut line. Each one carries your personal QR code and invite link, so a scan lands the prospect in your line. Print in color; the QR scans in black and white too.</p>
<div class="fl-id"><label style="font-weight:700">Your username or member #:</label><input id="flRef" placeholder="e.g. martbost" autocomplete="off"><span class="small muted">Opened from Promo tools? It is filled in for you.</span></div>
<div id="halfsheets"></div>
</main>
<script src="/assets/common.js?v=20260909a"></script>
<script src="/assets/qrlib.js"></script>
<script src="/assets/flyers.js?v=20260909a"></script>
</body>
</html>
+46 -12
View File
@@ -5,7 +5,7 @@
<title>Member area | InstantAdPay</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Sora:wght@600;700;800&display=swap">
<link rel="icon" type="image/png" href="/logo-icon.png">
<link rel="stylesheet" href="/assets/site.css?v=20260908f">
<link rel="stylesheet" href="/assets/site.css?v=20260909a">
</head>
<body class="bo-body">
@@ -534,22 +534,54 @@
</div>
<div class="pane" id="pane-promo" hidden>
<div class="card promo-strip">
<div><span class="eyebrow" style="margin:0 0 4px">Your invite link</span><div class="mono small" id="promoLink" style="overflow-wrap:anywhere">…</div></div>
<button class="btn small" id="promoLinkCopy" type="button">Copy link</button>
</div>
<div class="chips promo-pills" role="tablist" aria-label="Promo tools sections">
<button class="chip-t on" data-promo="posts" type="button" role="tab">Social posts</button>
<button class="chip-t" data-promo="text" type="button" role="tab">Text a friend</button>
<button class="chip-t" data-promo="swipe" type="button" role="tab">Email swipes</button>
<button class="chip-t" data-promo="banners" type="button" role="tab">Banners</button>
<button class="chip-t" data-promo="flyers" type="button" role="tab">Handouts</button>
<button class="chip-t" data-promo="wall" type="button" role="tab">Banner wall</button>
<button class="chip-t" data-promo="objections" type="button" role="tab">Objections</button>
<button class="chip-t" data-promo="videos" type="button" role="tab">Videos</button>
</div>
<div class="card promo-sec" id="promo-posts">
<h3>Share-ready posts, personalized with your link</h3>
<p class="muted small">Copy, paste anywhere, done. Every post already carries your invite link,
so the credit is always yours.</p>
<h3>Social posts, personalized with your link</h3>
<p class="muted small">Copy and paste, or post straight from here. Every post already carries your invite link,
so the credit is always yours. Angle links keep working when the matching hook pages ship.</p>
<div id="promoPosts"></div>
<div class="promo-bv">
<b>Want these in your own voice?</b>
<span class="muted small">Branded Voice writes posts, emails and stories the way you actually talk.</span>
<a class="btn small sec" href="https://mybrandedvoice.com" target="_blank" rel="noopener">mybrandedvoice.com</a>
</div>
</div>
<div class="card promo-sec" id="promo-text" hidden>
<h3>Text a friend</h3>
<p class="muted small">Short, low-pressure messages sized for a text. On your phone, <b>Text it</b> opens your
messaging app with the message ready, <b>WhatsApp</b> pre-fills it there, <b>Telegram</b> shares your link
with a lead-in, and <b>Copy</b> works for Messenger, Instagram DMs, anywhere.</p>
<div id="promoTexts"></div>
</div>
<div class="card promo-sec" id="promo-swipe" hidden>
<h3>Email swipe</h3>
<p class="muted small" id="promoSwipeWrap"></p>
<h3>Email swipes</h3>
<p class="muted small">Short, standard and long versions plus a follow-up. Subject line included. Edit freely, keep the honesty lines.</p>
<div id="promoSwipeWrap"></div>
</div>
<div class="card promo-sec" id="promo-banners" hidden>
<h3>Banners and graphics</h3>
<p class="muted small">Branded InstantAdPay banners in every standard ad size, plus square, story and Telegram
group sizes. Pair one with your invite link. Download, or copy the image URL for ad networks.</p>
<div id="promoBanners" class="promo-banners"></div>
</div>
<div class="card promo-sec" id="promo-flyers" hidden>
<h3>Printable handouts</h3>
<p class="muted small">Old-school still works. Three half-sheet designs, two per letter page with a cut line,
each with <b>your</b> QR code and invite link baked in. Coffee shops, gyms, community boards, laundromats.</p>
<p><a class="btn small" id="flyersOpen" href="/flyers" target="_blank" rel="noopener">Open your handouts</a></p>
</div>
<div class="card promo-sec" id="promo-wall" hidden>
<h3>Your banner wall</h3>
@@ -560,11 +592,12 @@
<p><button class="btn small sec" id="wallCopy" type="button" hidden>Copy wall link</button>
<a class="btn small sec" id="wallOpen" target="_blank" rel="noopener" hidden>Open your wall</a></p>
</div>
<div class="card promo-sec" id="promo-banners" hidden>
<h3>Banners</h3>
<p class="muted small">Branded InstantAdPay banners, ready to drop anywhere. Pair one with your
invite link (copy it from "My line"). Right-click to save, or copy the image URL.</p>
<div id="promoBanners" class="promo-banners"></div>
<div class="card promo-sec" id="promo-objections" hidden>
<h3>Objection handling</h3>
<p class="muted small">The walls every builder hits, with answers that hold up. Open one: you get
<b>the truth</b>, so you never say something a prospect can poke a hole in, and a
<b>ready-to-send reply</b> with your link. Golden rule: lead with a real example, never a promise.</p>
<div id="promoObjections" class="obj-wrap"></div>
</div>
<div class="card promo-sec" id="promo-videos" hidden>
<h3>Hook videos</h3>
@@ -710,7 +743,8 @@
</div>
<script src="/assets/common.js?v=20260909a"></script>
<script src="/assets/wallet.js?v=20260908t"></script>
<script src="/assets/my.js?v=20260909c"></script>
<script src="/assets/promo.js?v=20260909a"></script>
<script src="/assets/my.js?v=20260909d"></script>
<script src="/assets/chat.js?v=20260907l"></script>
</body>
</html>