Member area rebuilt as a back-office shell

Fixed left sidebar (iconed menu with active accent, site links, user chip,
logout), sticky top bar with per-section title and rehearsal notice,
content region per pane. Mobile gets a hamburger off-canvas drawer that
closes on selection. Marketing nav no longer wraps the member area;
signed-out visitors get a centered auth page. All flows and pane IDs
preserved; verified on desktop and 390px. Assets v=20260904r.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
martbost
2026-09-05 04:55:20 -05:00
parent c10af1d1e7
commit f848112e2d
6 changed files with 231 additions and 160 deletions
+17 -4
View File
@@ -1,8 +1,15 @@
// My account: email-first join/login, wallet link at purchase time,
// free payout activation, invite link, on-chain activity.
(async function () {
await IAP.renderNav('my');
// back-office shell: no marketing nav here; rehearsal notice lives in the top bar
const $ = IAP.$;
try {
const c = await IAP.getConfig();
if (c.rehearsal && $('boRehearsal')) {
$('boRehearsal').hidden = false;
$('boRehearsal').innerHTML = '<b>Testnet rehearsal</b> · ' + c.chainName;
}
} catch (e) {}
async function api(path, body) {
const r = await (await fetch(path, { method: 'POST',
@@ -58,21 +65,27 @@
} catch (e) {}
}
// ── members-area menu: hash-routed panes ──────────────
// ── back-office menu: hash-routed panes ───────────────
const PANES = ['overview', 'line', 'campaigns', 'earnings', 'wallet'];
const TITLES = { overview: 'Overview', line: 'My line', campaigns: 'Campaigns',
earnings: 'Earnings', wallet: 'Wallet & account' };
function setPane(name) {
if (!PANES.includes(name)) name = 'overview';
for (const p of PANES) {
const el = $('pane-' + p);
if (el) el.hidden = p !== name;
}
document.querySelectorAll('.subnav [data-pane]').forEach(b =>
document.querySelectorAll('.bo-menu [data-pane]').forEach(b =>
b.classList.toggle('on', b.dataset.pane === name));
if ($('boTitle')) $('boTitle').textContent = TITLES[name];
document.getElementById('memberArea').classList.remove('side-open'); // close mobile drawer
if (location.hash !== '#' + name) history.replaceState(null, '', '#' + name);
}
document.querySelectorAll('.subnav [data-pane]').forEach(b =>
document.querySelectorAll('.bo-menu [data-pane]').forEach(b =>
b.addEventListener('click', () => setPane(b.dataset.pane)));
window.addEventListener('hashchange', () => setPane(location.hash.slice(1)));
if ($('boBurger')) $('boBurger').addEventListener('click', () =>
document.getElementById('memberArea').classList.toggle('side-open'));
async function render() {
const me = await IAP.refreshNavWallet();
+35
View File
@@ -222,6 +222,41 @@ input[type=range]{padding:0;border:0;background:transparent;accent-color:var(--m
input:focus,select:focus{border-color:var(--mint)}
:focus-visible{outline:2px solid var(--mint);outline-offset:2px}
.hero-note{font-family:var(--mono);font-size:12px;color:var(--muted);margin-top:26px}
/* ── member back-office shell ─────────────────────────── */
.bo-body{background:var(--ground)}
.bo{display:grid;grid-template-columns:236px 1fr;min-height:100vh}
.bo-side{position:sticky;top:0;height:100vh;display:flex;flex-direction:column;gap:22px;
padding:22px 16px;background:rgba(10,18,15,.92);border-right:1px solid var(--line)}
.bo-side .logo{font-size:19px;padding:0 8px}
.bo-menu{display:flex;flex-direction:column;gap:4px}
.bo-menu button{display:flex;align-items:center;gap:11px;background:transparent;border:0;color:var(--muted);
font:600 14px var(--disp);padding:11px 12px;border-radius:11px;cursor:pointer;text-align:left;
border-left:3px solid transparent}
.bo-menu button svg{width:18px;height:18px;stroke:currentColor;fill:none;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round;flex:0 0 auto}
.bo-menu button:hover{color:var(--ink);background:rgba(67,232,195,.05)}
.bo-menu button.on{color:var(--mint);background:rgba(67,232,195,.09);border-left-color:var(--mint)}
.bo-links{display:flex;flex-direction:column;gap:2px;padding:14px 12px;border-top:1px solid var(--line)}
.bo-cap{font-family:var(--mono);font-size:10.5px;letter-spacing:.18em;text-transform:uppercase;color:var(--muted);margin-bottom:6px}
.bo-links a{color:var(--muted);font-size:13.5px;padding:5px 0}
.bo-links a:hover{color:var(--ink);text-decoration:none}
.bo-foot{margin-top:auto;padding:14px 12px 0;border-top:1px solid var(--line);display:flex;flex-direction:column;gap:6px;overflow-wrap:anywhere}
.bo-main{min-width:0;display:flex;flex-direction:column}
.bo-top{display:flex;align-items:center;gap:16px;padding:16px 26px;border-bottom:1px solid var(--line);
background:rgba(6,10,8,.75);backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);position:sticky;top:0;z-index:5}
.bo-top h2{font-size:20px}
#boBurger{display:none;background:transparent;border:1px solid var(--line-strong);color:var(--ink);
border-radius:9px;font-size:17px;padding:5px 11px;cursor:pointer}
.bo-rehearsal{margin-left:auto;color:var(--muted)}
.bo-rehearsal b{color:var(--mint)}
.bo-content{padding:26px;max-width:1060px;width:100%}
@media(max-width:959px){
.bo{grid-template-columns:1fr}
.bo-side{position:fixed;left:0;top:0;bottom:0;width:250px;z-index:20;transform:translateX(-100%);
transition:transform .22s ease;height:100dvh}
.bo.side-open .bo-side{transform:none;box-shadow:0 0 60px rgba(0,0,0,.6)}
#boBurger{display:block}
.bo-content{padding:18px}
}
/* member sub-navigation */
.subnav{display:flex;gap:6px;flex-wrap:wrap;background:rgba(16,28,24,.7);border:1px solid var(--line);
border-radius:999px;padding:6px 8px;width:fit-content;margin:0 0 22px}
+4 -4
View File
@@ -5,7 +5,7 @@
<title>The contract | InstantAdPay</title>
<meta name="description" content="Plain-language review of the InstantAdPay settlement contract: what it does, what nobody can change, what the operator can and cannot touch, and how to verify all of it yourself.">
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Sora:wght@600;700;800&display=swap">
<link rel="stylesheet" href="/assets/site.css?v=20260904q">
<link rel="stylesheet" href="/assets/site.css?v=20260904r">
</head>
<body>
<div class="wrap">
@@ -129,8 +129,8 @@
<div class="small">Advertising services with a performance referral program. Not an investment product; no income guarantees. Crypto transactions are irreversible. Never spend what you cannot afford.</div>
</footer>
</div>
<script src="/assets/common.js?v=20260904q"></script>
<script src="/assets/contract.js?v=20260904q"></script>
<script src="/assets/chat.js?v=20260904q"></script>
<script src="/assets/common.js?v=20260904r"></script>
<script src="/assets/contract.js?v=20260904r"></script>
<script src="/assets/chat.js?v=20260904r"></script>
</body>
</html>
+5 -5
View File
@@ -5,7 +5,7 @@
<title>InstantAdPay: advertise and earn, locked in code</title>
<meta name="description" content="Real ad packages with instant on-chain settlement. Every purchase pays the sponsor line in the same transaction, verifiable by anyone on the live ledger.">
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Sora:wght@600;700;800&display=swap">
<link rel="stylesheet" href="/assets/site.css?v=20260904q">
<link rel="stylesheet" href="/assets/site.css?v=20260904r">
</head>
<body>
@@ -398,9 +398,9 @@
</div>
</section>
<script src="/assets/common.js?v=20260904q"></script>
<script src="/assets/wallet.js?v=20260904q"></script>
<script src="/assets/home.js?v=20260904q"></script>
<script src="/assets/chat.js?v=20260904q"></script>
<script src="/assets/common.js?v=20260904r"></script>
<script src="/assets/wallet.js?v=20260904r"></script>
<script src="/assets/home.js?v=20260904r"></script>
<script src="/assets/chat.js?v=20260904r"></script>
</body>
</html>
+4 -4
View File
@@ -5,7 +5,7 @@
<title>Live ledger | InstantAdPay</title>
<meta name="description" content="Every purchase, payout, and pass-up on InstantAdPay, streamed straight from the blockchain with a verify link on every line.">
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Sora:wght@600;700;800&display=swap">
<link rel="stylesheet" href="/assets/site.css?v=20260904q">
<link rel="stylesheet" href="/assets/site.css?v=20260904r">
</head>
<body>
<div class="wrap">
@@ -25,8 +25,8 @@
<div>InstantAdPay · <a href="/">how it works</a> · <a id="contractLink" href="#" target="_blank" rel="noopener">contract source ↗</a></div>
</footer>
</div>
<script src="/assets/common.js?v=20260904q"></script>
<script src="/assets/ledger.js?v=20260904q"></script>
<script src="/assets/chat.js?v=20260904q"></script>
<script src="/assets/common.js?v=20260904r"></script>
<script src="/assets/ledger.js?v=20260904r"></script>
<script src="/assets/chat.js?v=20260904r"></script>
</body>
</html>
+57 -34
View File
@@ -2,40 +2,40 @@
<html lang="en">
<head>
<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<title>My account | InstantAdPay</title>
<title>Member area | InstantAdPay</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Sora:wght@600;700;800&display=swap">
<link rel="stylesheet" href="/assets/site.css?v=20260904q">
<link rel="stylesheet" href="/assets/site.css?v=20260904r">
</head>
<body>
<div class="wrap">
<section class="hero" style="padding:56px 0 10px">
<h1>My <em>account</em></h1>
<p class="lead" id="introLead">Join free with your email. Your wallet only comes out when you buy
a package or switch on payouts, and it stays yours the whole time.</p>
</section>
<body class="bo-body">
<div id="authArea">
<!-- ── signed-out: centered auth ─────────────────────────── -->
<div id="authArea">
<div class="wrap" style="max-width:560px">
<section class="hero" style="padding:64px 0 10px;text-align:left">
<p class="eyebrow">Member area</p>
<h1 style="font-size:34px">Sign <em>in</em></h1>
<p class="lead" id="introLead" style="font-size:16px">Join free with your email. Your wallet only
comes out when you buy a package or switch on payouts, and it stays yours the whole time.</p>
</section>
<div class="card" id="magicCard" hidden>
<h3>Sign in or join free</h3>
<p class="muted small">Type your email and we send a 6-digit code. No password to invent,
no password to forget. New emails get a free account automatically.</p>
<p><input id="mcEmail" type="email" placeholder="Email" autocomplete="email" style="width:100%;max-width:420px"></p>
<p id="mcCodeRow" hidden><input id="mcCode" inputmode="numeric" placeholder="6-digit code" style="width:100%;max-width:420px"></p>
<p><input id="mcEmail" type="email" placeholder="Email" autocomplete="email" style="width:100%"></p>
<p id="mcCodeRow" hidden><input id="mcCode" inputmode="numeric" placeholder="6-digit code" style="width:100%"></p>
<button class="btn" id="mcSendBtn">Email me a code</button>
<button class="btn" id="mcVerifyBtn" hidden>Sign in</button>
<button class="btn sec small" id="mcResend" hidden>Send a fresh code</button>
</div>
<div class="grid c2" id="passCards">
<div id="passCards">
<div class="card">
<h3>Create your free account</h3>
<p class="muted small">Takes ten seconds. No wallet needed to join.</p>
<p><input id="suEmail" type="email" placeholder="Email" autocomplete="email" style="width:100%"></p>
<p><input id="suPass" type="password" placeholder="Password (8+ characters)" autocomplete="new-password" style="width:100%"></p>
<button class="btn" id="signupBtn">Join free</button>
</div>
<div class="card">
<h3>Log in</h3>
<p class="muted small">Welcome back.</p>
<p><input id="liEmail" type="email" placeholder="Email" autocomplete="email" style="width:100%"></p>
<p><input id="liPass" type="password" placeholder="Password" autocomplete="current-password" style="width:100%"></p>
<button class="btn sec" id="loginBtn">Log in</button>
@@ -43,18 +43,41 @@
</div>
<p class="small muted">Crypto-native? You can also <a href="#" id="walletSigninLink">sign in with just your wallet</a>.
One free signature, no email needed.</p>
<p class="small muted"><a href="/">← Back to the site</a></p>
</div>
</div>
<div id="memberArea" hidden>
<div class="subnav" role="tablist">
<button data-pane="overview" class="on" type="button">Overview</button>
<button data-pane="line" type="button">My line</button>
<button data-pane="campaigns" type="button">Campaigns</button>
<button data-pane="earnings" type="button">Earnings</button>
<button data-pane="wallet" type="button">Wallet</button>
<!-- ── signed-in: back-office shell ──────────────────────── -->
<div id="memberArea" class="bo" hidden>
<aside class="bo-side" id="boSide">
<a class="logo" href="/">Instant<b>AdPay</b></a>
<nav class="bo-menu" aria-label="Member menu">
<button data-pane="overview" class="on" type="button"><svg viewBox="0 0 24 24"><rect x="3" y="3" width="8" height="8" rx="2"/><rect x="13" y="3" width="8" height="8" rx="2"/><rect x="3" y="13" width="8" height="8" rx="2"/><rect x="13" y="13" width="8" height="8" rx="2"/></svg>Overview</button>
<button data-pane="line" type="button"><svg viewBox="0 0 24 24"><circle cx="12" cy="7" r="3.2"/><circle cx="5" cy="17" r="2.6"/><circle cx="19" cy="17" r="2.6"/><path d="M12 10v3M12 13l-5 2M12 13l5 2"/></svg>My line</button>
<button data-pane="campaigns" type="button"><svg viewBox="0 0 24 24"><path d="M3 11l14-5v12L3 13v-2z"/><path d="M17 8a4 4 0 0 1 0 8M7 13v5a2 2 0 0 0 4 0v-3"/></svg>Campaigns</button>
<button data-pane="earnings" type="button"><svg viewBox="0 0 24 24"><path d="M4 17l5-5 4 3 7-8"/><path d="M14 7h6v6"/></svg>Earnings</button>
<button data-pane="wallet" type="button"><svg viewBox="0 0 24 24"><rect x="3" y="6" width="18" height="13" rx="2"/><path d="M3 10h18"/><circle cx="16.5" cy="14.5" r="1.4"/></svg>Wallet</button>
</nav>
<div class="bo-links">
<span class="bo-cap">Site</span>
<a href="/">Ad packages</a>
<a href="/ledger">Live ledger</a>
<a href="/contract">The contract</a>
</div>
<div class="bo-foot">
<span id="navWallet" class="small muted">…</span>
<a href="#" id="logoutLink" class="small">Log out</a>
</div>
</aside>
<div class="bo-main">
<header class="bo-top">
<button id="boBurger" aria-label="Menu" type="button">☰</button>
<h2 id="boTitle" style="margin:0">Overview</h2>
<span class="bo-rehearsal small" id="boRehearsal" hidden></span>
</header>
<main class="bo-content">
<!-- ── Overview ─────────────────────────────────────── -->
<div class="pane" id="pane-overview">
<div class="stats" style="margin-top:0">
<div class="stat"><div class="n" id="dbCredits">0</div><div class="l">Ad credits</div></div>
@@ -72,6 +95,7 @@
</div>
</div>
</div>
<div class="grid c2">
<div class="card"><h3>Earning levels</h3>
<p id="qualLine" class="muted small">…</p></div>
<div class="card">
@@ -80,8 +104,8 @@
Purchases from your linked wallet automatically credit this account.</p>
</div>
</div>
</div>
<!-- ── My line ──────────────────────────────────────── -->
<div class="pane" id="pane-line" hidden>
<div class="card">
<h3>Your invite link, live from day one</h3>
@@ -103,7 +127,6 @@
</div>
</div>
<!-- ── Campaigns ────────────────────────────────────── -->
<div class="pane" id="pane-campaigns" hidden>
<div class="card" id="campGate" hidden>
<h3>Campaigns unlock with your first package</h3>
@@ -132,7 +155,6 @@
</div>
</div>
<!-- ── Earnings ─────────────────────────────────────── -->
<div class="pane" id="pane-earnings" hidden>
<div id="activityArea" hidden>
<div class="grid c2">
@@ -160,13 +182,14 @@
</div>
</div>
<!-- ── Wallet ───────────────────────────────────────── -->
<div class="pane" id="pane-wallet" hidden>
<div class="grid c2">
<div class="card"><h3>Your account</h3>
<p id="posLine" class="muted small">…</p></div>
<div class="card"><h3>Ad credits</h3>
<p class="mono" style="font-size:26px;margin:0" id="creditLine">0</p>
<p class="muted small">1 credit = 1 cent of ad delivery across the network. Recorded on-chain. Only your campaigns can spend them.</p></div>
</div>
<div class="card" id="linkCard" hidden>
<h3>Link your wallet</h3>
<p class="muted small">Your earnings pay straight to your own wallet, so we need to know which one is yours.
@@ -179,15 +202,15 @@
and it unlocks the full flow. Buying any package does this automatically.</p>
<button class="btn sec" id="activateBtn">Activate payouts</button>
</div>
<p class="small"><a href="#" id="logoutLink">Log out</a></p>
</div>
</div>
<footer><div>InstantAdPay · <a href="/ledger">live ledger</a> · <a href="/contract">the contract</a></div></footer>
</main>
</div>
</div>
<script src="/assets/common.js?v=20260904q"></script>
<script src="/assets/wallet.js?v=20260904q"></script>
<script src="/assets/my.js?v=20260904q"></script>
<script src="/assets/chat.js?v=20260904q"></script>
<script src="/assets/common.js?v=20260904r"></script>
<script src="/assets/wallet.js?v=20260904r"></script>
<script src="/assets/my.js?v=20260904r"></script>
<script src="/assets/chat.js?v=20260904r"></script>
</body>
</html>