Pool spent: board notice + closed missions, starts/claims refused until midnight Central; no-wallet onboarding panel; landing: Before your first hunt steps + teaser player above the fold

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
martbost
2026-09-19 19:16:43 -05:00
parent 6e2b5b7dc5
commit 4beda14950
10 changed files with 83 additions and 19 deletions
+1 -1
View File
@@ -5,7 +5,7 @@
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Admin · PolHunter</title>
<meta name="robots" content="noindex,nofollow">
<link rel="stylesheet" href="/style.css?v=5">
<link rel="stylesheet" href="/style.css?v=6">
<style>
label{display:block;font-size:12px;color:var(--dim);letter-spacing:.08em;text-transform:uppercase;margin:10px 0 4px}
input,textarea,select{width:100%;padding:10px 12px;border-radius:10px;border:1px solid var(--edge);background:rgba(0,0,0,.35);color:var(--ink);font:14px var(--font)}
+2 -2
View File
@@ -5,7 +5,7 @@
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Your board · PolHunter</title>
<meta name="robots" content="noindex">
<link rel="stylesheet" href="/style.css?v=5">
<link rel="stylesheet" href="/style.css?v=6">
</head>
<body>
<div class="wrap">
@@ -29,6 +29,6 @@
<footer class="foot">Rewards are for completed missions, not income. The daily pool is limited; a find made after it is spent queues and pays next. Cryptocurrency involves risk of loss.</footer>
</div>
<script src="/app.js?v=3"></script>
<script src="/app.js?v=4"></script>
</body>
</html>
+18 -4
View File
@@ -13,7 +13,7 @@
+ '<span class="tag' + (m.done ? ' done' : '') + '">' + (m.done ? 'Completed' : esc(m.site)) + '</span>'
+ '<h3>' + esc(m.name) + '</h3><p>' + esc(m.brief) + '</p>'
+ '<p style="margin-top:10px"><span class="range">' + r.minPol + ' to ' + r.maxPol + ' POL</span> <span style="color:var(--dim);font-size:12px">· ' + m.dwell + 's on the site</span></p>'
+ (m.done ? '' : isOpen
+ (m.done ? '' : (board.pool && board.pool.spent && !isOpen) ? '<div style="margin-top:14px"><span class="tag gold">Closed until midnight Central</span></div>' : isOpen
? '<div class="timer" id="timer">Your code appears on the site after <b>' + m.dwell + 's</b>. Keep that tab open.</div>'
+ '<div class="codebox"><input id="code" maxlength="6" placeholder="CODE" autocomplete="off" spellcheck="false"><button class="btn pol" id="submit">Claim</button></div>'
+ '<div class="msg" id="msg"></div>'
@@ -21,11 +21,25 @@
: '<div style="margin-top:14px"><button class="btn" data-start="' + esc(m.id) + '">Start mission</button></div>')
+ '</div>';
}
const IAP = 'https://instantadpay.com';
function until(ms) { const s = Math.max(0, Math.round((ms - Date.now()) / 60000)); const h = Math.floor(s / 60), m = s % 60; return h ? h + 'h ' + m + 'm' : m + 'm'; }
// no wallet on the InstantAdPay account yet: the three steps, in place of the missions
function onboarding() {
return '<div class="notice"><b>One thing before your first hunt.</b> Drips are paid to the wallet on your InstantAdPay account, and yours has none yet.</div>'
+ '<div class="steps" style="margin-top:14px">'
+ '<div class="step"><b>Create your free InstantAdPay account.</b> Already done: you are signed in as ' + esc(board.me.username ? '@' + board.me.username : '#' + board.me.memberId) + '.</div>'
+ '<div class="step"><b>Link your wallet.</b> In your InstantAdPay dashboard open <a href="' + IAP + '/my#wallet" target="_blank" rel="noopener">Wallet</a>, press connect wallet and approve. Any Polygon wallet works (MetaMask, Trust, Phantom with Polygon on).</div>'
+ '<div class="step"><b>Come back through the door.</b> Return to your dashboard and press <a href="' + IAP + '/api/my/polhunter">Open my board</a> again. Your missions unlock the moment a wallet is on the account.</div>'
+ '</div>';
}
function render() {
$('who').innerHTML = '<i></i> ' + esc(board.me.username ? '@' + board.me.username : '#' + board.me.memberId) + (board.me.wallet ? '' : ' · no wallet linked');
const r = board.missions[0] && board.missions[0].reward;
if (r) $('rangeLine').textContent = 'Each find pays a random drip between ' + r.minPol + ' and ' + r.maxPol + ' POL. Your codes are yours alone.';
$('missions').innerHTML = board.missions.length ? board.missions.map(card).join('') : '<div class="card"><p>No missions are open right now. Check back soon.</p></div>';
const pool = board.pool || {};
if (!board.me.wallet) $('missions').innerHTML = onboarding();
else $('missions').innerHTML = (pool.spent ? '<div class="notice" style="grid-column:1/-1"><b>Today\u2019s POL pool is spent.</b> No more claims today. Claims reopen at midnight Central' + (pool.resetsAt ? ', in ' + until(pool.resetsAt) : '') + '.</div>' : '')
+ (board.missions.length ? board.missions.map(card).join('') : '<div class="card"><p>No missions are open right now. Check back soon.</p></div>');
$('mine').innerHTML = board.drips.length ? board.drips.map(d => '<div class="row"><span>' + esc(d.site) + '</span><span class="site">' + ({ paid: 'paid', sent: 'sending', due: 'paying next', queued: 'queued for the next pool', failed: 'held, being looked at' }[d.status] || d.status) + '</span><span class="pol">+' + d.pol + ' POL</span>' + (d.tx ? '<a class="when" target="_blank" rel="noopener" href="' + explorer + '/tx/' + d.tx + '">verify ↗</a>' : '<span class="when"></span>') + '</div>').join('')
: '<div class="row"><span class="site">Nothing yet. Your first find goes here.</span></div>';
document.querySelectorAll('[data-start]').forEach(b => b.addEventListener('click', () => start(b.dataset.start)));
@@ -35,7 +49,7 @@
async function load() { board = await api('/api/my/board'); render(); }
async function start(id) {
const r = await api('/api/my/start', { missionId: id });
if (r.error) { alert(r.error); return; }
if (r.error) { if (r.spent) { await load(); } else alert(r.error); return; }
open = { missionId: id, token: r.token, url: r.url, dwell: r.dwell, started: Date.now(), expires: r.expires };
window.open(r.url, '_blank', 'noopener');
render();
@@ -53,7 +67,7 @@
const r = await api('/api/my/submit', { token: open.token, code });
const m = $('msg'); m.className = 'msg ' + (r.error ? 'bad' : 'ok'); m.textContent = r.error || r.message;
$('submit').disabled = false;
if (!r.error) { open = null; setTimeout(load, 900); }
if (!r.error || r.spent) { open = null; setTimeout(load, 900); }
}
fetch('/api/config').then(r => r.json()).then(c => { explorer = c.explorer || explorer; }).catch(() => {});
load();
+22 -3
View File
@@ -5,13 +5,14 @@
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>PolHunter</title>
<meta name="description" content="Missions across the network. Visit a site, find the thing, get a drip of POL to your wallet.">
<link rel="stylesheet" href="/style.css?v=5">
<link rel="stylesheet" href="/style.css?v=6">
</head>
<body>
<div class="wrap">
<header class="top">
<a class="mark" href="/"><span class="coin"></span>PolHunter</a>
<nav class="nav">
<a class="btn ghost sm" href="#start">Get started</a>
<a class="btn ghost sm" href="#how">How it works</a>
<a class="btn ghost sm" href="#ledger">Paid so far</a>
<a class="btn sm" id="signin" href="https://instantadpay.com/api/my/polhunter">Open my board</a>
@@ -24,16 +25,27 @@
<source type="image/webp" srcset="/img/hero.webp">
<img src="/img/hero.jpg" alt="" fetchpriority="high" decoding="async">
</picture>
<button class="play" id="play" type="button" aria-label="Watch the 30-second teaser"><span class="tri"></span>Watch the 30-second teaser</button>
<div class="hero-copy">
<span class="pill"><i></i> Paying in POL on Polygon</span>
<h1>Visit it. Find it.<br><b>Get paid in POL.</b></h1>
<p>Every mission is a few minutes on one of our sites, one thing to find, and a drip of POL straight to your wallet when you find it. Your code is yours alone, and the hiding place moves.</p>
<a class="btn" href="https://instantadpay.com/api/my/polhunter">Start hunting</a>
<a class="btn ghost" href="#how" style="margin-left:8px">See how</a>
<a class="btn ghost" href="#start" style="margin-left:8px">Before your first hunt</a>
<p id="signinNote" style="font-size:13px;color:var(--dim);margin-top:14px;display:none">Sign in with your InstantAdPay account: the button sends you through InstantAdPay and straight back here. No password, no new account.</p>
</div>
</section>
<section class="section" id="start">
<h2>Before your first hunt</h2>
<div class="sub">Drips are paid to the wallet on your InstantAdPay account. Three steps, once.</div>
<div class="steps">
<div class="step"><b>Create your free InstantAdPay account</b> at <a href="https://instantadpay.com" target="_blank" rel="noopener">instantadpay.com</a>. Email first, no wallet needed to sign up.</div>
<div class="step"><b>Link your wallet.</b> In your InstantAdPay dashboard open <a href="https://instantadpay.com/my#wallet" target="_blank" rel="noopener">Wallet</a>, press connect wallet and approve. That address is where every drip lands, on Polygon.</div>
<div class="step"><b>Open your board.</b> Press <a href="https://instantadpay.com/api/my/polhunter">Open my board</a> from your dashboard or up top. No new password: InstantAdPay signs you straight into PolHunter.</div>
</div>
</section>
<section class="section" id="how">
<h2>How a hunt works</h2>
<div class="sub">Three steps, one code, real POL.</div>
@@ -54,13 +66,20 @@
<div class="adslot" data-ad="home-footer"><!-- nas: 468x60 on phones, 728x90 on wider screens --><script>(function(){var m=window.innerWidth<600;document.write('<scr'+'ipt src="https://www.networkadspace.com/showadss.php?'+(m?'w=468&h=60&n=1&bw=468&bh=60':'w=728&h=90&n=1&bw=728&bh=90')+'&c=999"></scr'+'ipt>');})();</script></div>
<footer class="foot">
PolHunter rewards completed missions. Rewards are not income and are not guaranteed; the daily pool is limited and drips are random within the posted range. Cryptocurrency involves risk of loss. Hunters sign in with an InstantAdPay account; no account is created here.
PolHunter rewards completed missions. Rewards are not income and are not guaranteed; the daily pool is limited, and when it is spent claims close until midnight Central. Drips are random within the posted range. Cryptocurrency involves risk of loss. Hunters sign in with an InstantAdPay account; no account is created here.
<br><a href="https://instantadpay.com">InstantAdPay</a> · <a href="/admin" rel="nofollow" style="color:var(--dim)">·</a>
</footer>
</div>
<script>
(async () => {
if (new URLSearchParams(location.search).get('signin')) document.getElementById('signinNote').style.display = 'block';
// the teaser: the play button swaps the hero photograph for the video, sound on, in place
document.getElementById('play').addEventListener('click', () => {
const hero = document.querySelector('.hero'); hero.classList.add('playing');
const art = hero.querySelector('.hero-art'); const v = document.createElement('video');
v.controls = true; v.autoplay = true; v.playsInline = true; v.poster = '/video/polhunter-teaser-poster.jpg'; v.src = '/video/polhunter-teaser.mp4';
art.replaceWith(v); v.className = 'hero-video'; v.play().catch(() => {});
});
try {
const r = await (await fetch('/api/ledger')).json();
const t = r.totals || {};
+10 -1
View File
@@ -35,6 +35,15 @@ a{color:var(--pol-hi);text-decoration:none} a:hover{color:var(--gold-hi)}
.hero h1 b{background:linear-gradient(90deg,var(--gold-hi),var(--gold));-webkit-background-clip:text;background-clip:text;color:transparent}
.hero p{font-size:19px;color:var(--muted);max-width:520px;margin:20px 0 26px}
.hero h1,.hero p{text-shadow:0 2px 24px rgba(8,6,20,.9)}
/* the teaser */
.play{position:absolute;right:6%;bottom:44px;z-index:3;display:inline-flex;align-items:center;gap:12px;padding:12px 20px 12px 14px;border-radius:999px;border:1px solid rgba(255,255,255,.28);background:rgba(8,6,20,.55);backdrop-filter:blur(10px);color:#fff;font:600 14px var(--font);cursor:pointer;box-shadow:0 10px 40px rgba(0,0,0,.45);transition:transform .15s,border-color .15s}
.play:hover{transform:translateY(-1px);border-color:var(--gold)}
.play .tri{width:34px;height:34px;border-radius:50%;background:linear-gradient(135deg,var(--gold-hi),var(--gold));position:relative;flex:none;box-shadow:0 0 22px rgba(243,190,67,.6)}
.play .tri::after{content:"";position:absolute;left:13px;top:10px;border-style:solid;border-width:7px 0 7px 12px;border-color:transparent transparent transparent #160a33}
.hero.playing{min-height:0;display:block;background:#000}.hero.playing::before,.hero.playing .hero-copy,.hero.playing .play{display:none}
.hero-video{display:block;width:100%;aspect-ratio:16/9;max-height:80vh;background:#000;object-fit:contain}
.notice{padding:14px 18px;border-radius:14px;background:rgba(243,190,67,.1);border:1px solid rgba(243,190,67,.4);color:var(--gold-hi);font-size:15px}
.notice b{color:var(--ink)}
.pill{display:inline-flex;align-items:center;gap:8px;padding:6px 12px;border-radius:999px;background:var(--glass);border:1px solid var(--edge);color:var(--muted);font-size:12px;letter-spacing:.1em;text-transform:uppercase}
.pill i{width:8px;height:8px;border-radius:50%;background:var(--ok);box-shadow:0 0 10px var(--ok)}
/* cards */
@@ -71,6 +80,6 @@ a{color:var(--pol-hi);text-decoration:none} a:hover{color:var(--gold-hi)}
.msg{margin-top:10px;font-size:14px} .msg.ok{color:var(--ok)} .msg.bad{color:var(--bad)}
.foot{padding:40px 0 30px;color:var(--dim);font-size:12.5px;line-height:1.7;border-top:1px solid var(--edge);margin-top:30px}
.adslot{min-height:90px;display:flex;align-items:center;justify-content:center;margin:26px 0;color:var(--dim);font-size:12px}
@media (max-width:820px){.hero{min-height:0;margin:0 -20px 6px;border-top:0;display:block}.hero-art{position:relative;inset:auto;display:block;height:270px}.hero-art img{object-position:60% 50%}.hero::before{inset:auto 0 auto 0;top:150px;height:121px;background:linear-gradient(180deg,transparent,var(--bg))}.hero-copy{padding:6px 20px 32px}.hero p{font-size:16.5px}.adslot{overflow-x:auto;justify-content:flex-start}.row{grid-template-columns:1fr auto;row-gap:6px}.row .when{grid-column:1/-1;font-size:13px;color:var(--pol-hi)}.adslot{margin:18px -4px}}
@media (max-width:820px){.hero{min-height:0;margin:0 -20px 6px;border-top:0;display:block}.hero-art{position:relative;inset:auto;display:block;height:270px}.hero-art img{object-position:60% 50%}.hero::before{inset:auto 0 auto 0;top:150px;height:121px;background:linear-gradient(180deg,transparent,var(--bg))}.hero-copy{padding:6px 20px 32px}.hero p{font-size:16.5px}.play{right:auto;bottom:auto;left:50%;top:135px;transform:translate(-50%,-50%)}.play:hover{transform:translate(-50%,-50%)}.adslot{overflow-x:auto;justify-content:flex-start}.row{grid-template-columns:1fr auto;row-gap:6px}.row .when{grid-column:1/-1;font-size:13px;color:var(--pol-hi)}.adslot{margin:18px -4px}}
/* every drip row carries its blockchain verify link; on phones it sits on its own line (Marty, 2026-09-19) */
.row a.when{color:var(--pol-hi)} .row a.when:hover{color:var(--gold-hi)}
Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.