Skip to content

Commit 6a600a5

Browse files
stackdumpclaude
andcommitted
Add live demo poll, wallet callout, and deploy section
- Try It Live: fetches active polls and renders inline voting widget - Why ZK Voting: expanded to 3 columns with Wallet-Native Auth card - Deploy to Your Chain: Foundry bundle download with 3-command workflow - CSS for demo poll widget, deploy code blocks, inline code in cards Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 7b58d04 commit 6a600a5

2 files changed

Lines changed: 184 additions & 3 deletions

File tree

public/bitwrap.css

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,15 @@ body {
327327
font-weight: 600;
328328
}
329329

330+
.angle-card code {
331+
font-family: var(--mono);
332+
font-size: 0.85em;
333+
color: var(--accent);
334+
background: var(--accent-dim);
335+
padding: 2px 6px;
336+
border-radius: 3px;
337+
}
338+
330339
/* Footer */
331340
.footer {
332341
border-top: 1px solid var(--border);
@@ -362,6 +371,103 @@ body {
362371
font-size: 0.8rem;
363372
}
364373

374+
/* Demo poll widget */
375+
.demo-poll {
376+
max-width: 600px;
377+
margin: 0 auto;
378+
}
379+
380+
.demo-poll-loading {
381+
text-align: center;
382+
color: var(--text-muted);
383+
padding: 40px;
384+
}
385+
386+
.demo-poll-card {
387+
background: var(--bg-card);
388+
border: 1px solid var(--border);
389+
border-radius: var(--radius);
390+
padding: 28px;
391+
}
392+
393+
.demo-poll-card h3 {
394+
font-size: 1.15rem;
395+
font-weight: 600;
396+
margin: 0 0 4px;
397+
}
398+
399+
.demo-poll-meta {
400+
font-size: 0.8rem;
401+
color: var(--text-muted);
402+
margin-bottom: 20px;
403+
}
404+
405+
.demo-poll-choice {
406+
display: block;
407+
width: 100%;
408+
background: transparent;
409+
border: 1px solid var(--border);
410+
border-radius: var(--radius);
411+
color: var(--text);
412+
padding: 12px 16px;
413+
font-size: 0.95rem;
414+
font-family: var(--font);
415+
cursor: pointer;
416+
margin-bottom: 8px;
417+
text-align: left;
418+
transition: border-color 0.2s, background 0.2s;
419+
}
420+
421+
.demo-poll-choice:hover {
422+
border-color: var(--accent);
423+
background: var(--accent-dim);
424+
}
425+
426+
.demo-poll-status {
427+
font-family: var(--mono);
428+
font-size: 0.75rem;
429+
color: var(--accent);
430+
background: var(--accent-dim);
431+
padding: 3px 8px;
432+
border-radius: 4px;
433+
display: inline-block;
434+
margin-left: 8px;
435+
vertical-align: middle;
436+
}
437+
438+
.demo-poll-empty {
439+
text-align: center;
440+
color: var(--text-muted);
441+
padding: 32px;
442+
background: var(--bg-card);
443+
border: 1px solid var(--border);
444+
border-radius: var(--radius);
445+
}
446+
447+
.demo-poll-empty a {
448+
color: var(--accent);
449+
text-decoration: none;
450+
}
451+
452+
/* Deploy code block */
453+
.deploy-code {
454+
background: var(--bg);
455+
border: 1px solid var(--border);
456+
border-radius: var(--radius);
457+
padding: 20px 24px;
458+
font-family: var(--mono);
459+
font-size: 0.85rem;
460+
line-height: 1.7;
461+
color: var(--text);
462+
margin: 12px 0 0;
463+
overflow-x: auto;
464+
white-space: pre;
465+
}
466+
467+
.deploy-code .comment { color: #555; }
468+
.deploy-code .flag { color: #888; }
469+
.deploy-code .string { color: var(--accent); }
470+
365471
/* Editor toolbar */
366472
.editor-toolbar {
367473
position: fixed;

public/index.html

Lines changed: 78 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,23 +104,66 @@ <h3>Tally</h3>
104104
</div>
105105
</section>
106106

107+
<section class="section" id="try-it">
108+
<h2>Try It Live</h2>
109+
<p class="section-sub">Cast a vote on an active poll. Your choice stays hidden until the poll closes.</p>
110+
<div id="demo-poll" class="demo-poll">
111+
<div class="demo-poll-loading">Loading active polls&hellip;</div>
112+
</div>
113+
<div style="text-align:center; margin-top: 24px;">
114+
<a class="btn-secondary" href="/poll" style="font-size:0.9rem; padding:10px 24px;">View All Polls</a>
115+
</div>
116+
</section>
117+
107118
<section class="section section-dark">
108119
<h2>Why ZK Voting?</h2>
109-
<div class="grid-2">
120+
<div class="grid-3">
110121
<div class="angle-card">
111122
<h3>Voter Privacy</h3>
112123
<p>Nullifiers link each ballot to a registered voter without revealing who voted for what. The proof confirms eligibility; the choice stays hidden.</p>
113124
</div>
125+
<div class="angle-card">
126+
<h3>Wallet-Native Auth</h3>
127+
<p>Connect MetaMask to create polls and derive voting secrets from wallet signatures. No accounts, no passwords &mdash; your key is your identity.</p>
128+
</div>
114129
<div class="angle-card">
115130
<h3>Verifiable Results</h3>
116131
<p>Every vote carries a Groth16 proof that the on-chain verifier checks. No trusted tallier, no back-room count &mdash; the math is the audit.</p>
117132
</div>
118133
</div>
119134
</section>
120135

121-
<section class="section">
136+
<section class="section" id="deploy">
137+
<h2>Deploy to Your Chain</h2>
138+
<p class="section-sub">Download a complete Foundry project. Deploy the ZK poll contract to any EVM chain in minutes.</p>
139+
<div class="grid-2">
140+
<div class="angle-card">
141+
<h3>What You Get</h3>
142+
<p><code>BitwrapZKPoll.sol</code> &mdash; governance contract with nullifier-based double-vote prevention and voter registry Merkle root verification.</p>
143+
<p style="margin-top:12px;"><code>Verifier.sol</code> &mdash; auto-generated Groth16 verifier for the voteCast circuit. Deployed alongside the poll contract.</p>
144+
<p style="margin-top:12px;"><code>Deploy.s.sol</code> &mdash; Foundry deployment script with sample initialization.</p>
145+
</div>
146+
<div class="angle-card">
147+
<h3>Three Commands</h3>
148+
<pre class="deploy-code"><span class="comment"># Download the bundle</span>
149+
<span class="flag">curl</span> -o poll.zip https://api.bitwrap.io/api/bundle/vote
150+
151+
<span class="comment"># Build &amp; test</span>
152+
<span class="flag">cd</span> BitwrapZKPoll && <span class="flag">forge</span> test
153+
154+
<span class="comment"># Deploy</span>
155+
<span class="flag">forge</span> script script/Deploy.s.sol \
156+
--rpc-url <span class="string">$RPC_URL</span> --broadcast</pre>
157+
</div>
158+
</div>
159+
<div style="text-align:center; margin-top: 32px;">
160+
<a class="btn-primary" href="/api/bundle/vote" style="font-size:0.9rem; padding:12px 28px;">Download Foundry Bundle</a>
161+
</div>
162+
</section>
163+
164+
<section class="section section-dark">
122165
<h2>Built on Petri Net Foundations</h2>
123-
<p class="section-sub">The voting protocol is one application of a general-purpose ZK state machine. The same framework powers token standards.</p>
166+
<p class="section-sub" style="max-width:900px; margin-left:auto; margin-right:auto;">The voting protocol is one application of a general-purpose ZK state machine. The same framework powers token standards.</p>
124167
<div class="grid-4" id="template-cards">
125168
<div class="template-card" data-template="vote">
126169
<div class="template-badge">Vote</div>
@@ -159,5 +202,37 @@ <h3>Multi Token</h3>
159202
</footer>
160203
</div>
161204

205+
<script>
206+
(function() {
207+
const el = document.getElementById('demo-poll');
208+
if (!el) return;
209+
210+
fetch('/api/polls')
211+
.then(r => r.json())
212+
.then(data => {
213+
const polls = (data.polls || []).filter(p => p.status === 'active');
214+
if (polls.length === 0) {
215+
el.innerHTML = '<div class="demo-poll-empty">No active polls right now. <a href="/poll">Create one</a> to see it here.</div>';
216+
return;
217+
}
218+
const poll = polls[0];
219+
const choices = (poll.choices || []).map(c => {
220+
const safe = c.replace(/&/g,'&amp;').replace(/</g,'&lt;');
221+
return '<button class="demo-poll-choice" onclick="location.href=\'/poll#' + poll.id + '\'">' + safe + '</button>';
222+
}).join('');
223+
const votes = poll.voteCount || 0;
224+
el.innerHTML =
225+
'<div class="demo-poll-card">' +
226+
'<h3>' + poll.title.replace(/&/g,'&amp;').replace(/</g,'&lt;') +
227+
'<span class="demo-poll-status">active</span></h3>' +
228+
'<div class="demo-poll-meta">' + votes + ' vote' + (votes !== 1 ? 's' : '') + ' cast &middot; results sealed</div>' +
229+
choices +
230+
'</div>';
231+
})
232+
.catch(() => {
233+
el.innerHTML = '<div class="demo-poll-empty">Could not load polls. <a href="/poll">Go to polls</a></div>';
234+
});
235+
})();
236+
</script>
162237
</body>
163238
</html>

0 commit comments

Comments
 (0)