2026-02-13 18:53:09 +00:00
<!DOCTYPE html>
< html lang = "en" >
< head >
< meta charset = "UTF-8" >
< meta name = "viewport" content = "width=device-width, initial-scale=1.0" >
< title > Swiss Post E-Voting: A Cryptographic Election< / title >
< style >
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
2026-07-06 17:15:31 +00:00
background: #ffffff;
color: #24292f;
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Text', 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
font-size: 16px;
line-height: 1.7;
2026-02-13 18:53:09 +00:00
overflow: hidden;
height: 100vh;
}
#progress-bar {
position: fixed; top: 0; left: 0;
2026-07-06 17:15:31 +00:00
height: 3px; background: #0969da;
2026-02-13 18:53:09 +00:00
transition: width 0.4s ease;
z-index: 100;
}
#slide-counter {
position: fixed; top: 10px; right: 20px;
2026-07-06 17:15:31 +00:00
font-size: 12px; color: #8b949e;
2026-02-13 18:53:09 +00:00
z-index: 100;
2026-07-06 17:15:31 +00:00
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Text', 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
2026-02-13 18:53:09 +00:00
}
#stage {
max-width: 920px;
margin: 0 auto;
2026-07-06 17:15:31 +00:00
padding: 40px 30px 80px;
2026-02-13 18:53:09 +00:00
height: 100vh;
overflow-y: auto;
2026-07-06 17:15:31 +00:00
-webkit-overflow-scrolling: touch;
2026-02-13 18:53:09 +00:00
}
.role-banner {
display: inline-block;
padding: 4px 14px;
border-radius: 4px;
font-size: 12px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 1.5px;
margin-bottom: 16px;
}
.slide-title {
font-size: 22px;
font-weight: 700;
margin-bottom: 20px;
2026-07-06 17:15:31 +00:00
color: #1f2328;
2026-02-13 18:53:09 +00:00
}
.content { margin-bottom: 20px; }
.decision-box {
2026-07-06 17:15:31 +00:00
background: #f6f8fa;
border: 1px solid #d0d7de;
border-radius: 10px;
2026-02-13 18:53:09 +00:00
padding: 16px 20px;
margin: 20px 0;
}
.decision-question {
2026-07-06 17:15:31 +00:00
color: #57606a;
2026-02-13 18:53:09 +00:00
font-size: 14px;
margin-bottom: 10px;
}
.decision-answer {
font-size: 16px;
font-weight: 700;
padding: 6px 16px;
border-radius: 4px;
display: inline-block;
}
.crypto-block {
2026-07-06 17:15:31 +00:00
background: #f6f8fa;
border: 1px solid #d8dee4;
border-radius: 8px;
2026-02-13 18:53:09 +00:00
padding: 14px 18px;
margin: 12px 0;
2026-07-06 17:15:31 +00:00
font-family: 'SF Mono', 'Fira Code', Menlo, Consolas, monospace;
2026-02-13 18:53:09 +00:00
font-size: 13px;
overflow-x: auto;
white-space: pre;
line-height: 1.5;
}
2026-07-06 17:15:31 +00:00
.crypto-block .label { color: #57606a; }
.crypto-block .value { color: #0550ae; }
.crypto-block .secret { color: #cf222e; }
.crypto-block .public { color: #1a7f37; }
.crypto-block .highlight { color: #8250df; }
.crypto-block .dim { color: #8b949e; }
2026-02-13 18:53:09 +00:00
.card-box {
2026-07-06 17:15:31 +00:00
border: 2px solid #d4a72c;
2026-02-13 18:53:09 +00:00
border-radius: 8px;
padding: 16px 20px;
margin: 14px 0;
2026-07-06 17:15:31 +00:00
background: #fffbeb;
2026-02-13 18:53:09 +00:00
white-space: pre;
2026-07-06 17:15:31 +00:00
font-family: 'SF Mono', 'Fira Code', Menlo, Consolas, monospace;
2026-02-13 18:53:09 +00:00
font-size: 13px;
line-height: 1.5;
}
.proof-line { margin: 2px 0; font-size: 13px; }
2026-07-06 17:15:31 +00:00
.proof-pass { color: #1a7f37; font-weight: 700; }
.proof-tree { color: #8b949e; }
2026-02-13 18:53:09 +00:00
.result-box {
2026-07-06 17:15:31 +00:00
border: 2px solid #0969da;
2026-02-13 18:53:09 +00:00
border-radius: 8px;
padding: 20px 24px;
margin: 16px 0;
2026-07-06 17:15:31 +00:00
background: #eff6ff;
2026-02-13 18:53:09 +00:00
text-align: center;
}
.result-bar {
display: inline-block;
height: 18px;
border-radius: 3px;
margin-left: 8px;
vertical-align: middle;
}
.verdict-box {
2026-07-06 17:15:31 +00:00
border: 2px solid #1a7f37;
2026-02-13 18:53:09 +00:00
border-radius: 8px;
padding: 24px 28px;
margin: 16px 0;
2026-07-06 17:15:31 +00:00
background: #f0fdf4;
2026-02-13 18:53:09 +00:00
font-size: 14px;
line-height: 1.7;
}
.voter-table {
width: 100%;
border-collapse: collapse;
margin: 12px 0;
font-size: 13px;
}
2026-07-06 17:15:31 +00:00
.voter-table th {
padding: 6px 12px;
text-align: left;
color: #57606a;
font-weight: 600;
border-bottom: 1px solid #d8dee4;
}
.voter-table td {
2026-02-13 18:53:09 +00:00
padding: 6px 12px;
text-align: left;
2026-07-06 17:15:31 +00:00
border-bottom: 1px solid #e8ebef;
2026-02-13 18:53:09 +00:00
}
/* Title card */
.title-card {
text-align: center;
padding-top: 15vh;
}
.title-card h1 {
font-size: 32px;
2026-07-06 17:15:31 +00:00
color: #1f2328;
2026-02-13 18:53:09 +00:00
margin-bottom: 12px;
font-weight: 800;
}
.title-card .subtitle {
font-size: 16px;
2026-07-06 17:15:31 +00:00
color: #57606a;
2026-02-13 18:53:09 +00:00
margin-bottom: 40px;
}
.title-card .swiss-cross {
font-size: 60px;
2026-07-06 17:15:31 +00:00
color: #dc2626;
2026-02-13 18:53:09 +00:00
margin-bottom: 30px;
}
.title-card .start-hint {
2026-07-06 17:15:31 +00:00
color: #8b949e;
2026-02-13 18:53:09 +00:00
font-size: 13px;
margin-top: 50px;
animation: pulse 2s ease-in-out infinite;
}
@keyframes pulse {
0%, 100% { opacity: 0.4; }
50% { opacity: 1; }
}
/* Role colors */
2026-07-06 17:15:31 +00:00
.role-chancellor { background: #fef3c7; color: #92400e; }
.role-cc { background: #d1fae5; color: #065f46; }
.role-voter { background: #dbeafe; color: #1e40af; }
.role-eb { background: #fed7aa; color: #9a3412; }
.role-auditor { background: #fecaca; color: #991b1b; }
.role-system { background: #e5e7eb; color: #374151; }
.role-none { background: #e5e7eb; color: #374151; }
.color-chancellor { color: #b45309; }
.color-cc { color: #059669; }
.color-voter { color: #2563eb; }
.color-eb { color: #c2410c; }
.color-auditor { color: #dc2626; }
.color-system { color: #57606a; }
2026-02-13 18:53:09 +00:00
.fade-in { animation: fadeIn 0.3s ease-in; }
@keyframes fadeIn {
from { opacity: 0; transform: translateY(12px); }
to { opacity: 1; transform: translateY(0); }
}
2026-07-06 17:15:31 +00:00
.em { color: #1f2328; font-weight: 600; }
.dim { color: #8b949e; }
.note { color: #57606a; font-size: 13px; font-style: italic; }
#home-btn {
position: fixed; top: 10px; left: 20px;
background: none; border: 1px solid #d0d7de; color: #57606a;
padding: 3px 10px; border-radius: 4px;
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Text', 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
font-size: 11px; cursor: pointer; z-index: 100;
-webkit-tap-highlight-color: transparent;
}
#home-btn:hover { border-color: #0969da; color: #0969da; }
.manual-cta { text-align: center; margin-top: 30px; padding: 20px; }
.manual-cta button {
background: #0969da; color: #fff; border: none;
padding: 12px 24px; border-radius: 8px;
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Text', 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
font-size: 14px; font-weight: 600; cursor: pointer;
-webkit-tap-highlight-color: transparent;
}
.manual-cta button:hover { background: #0550ae; }
.step-num {
display: inline-flex; align-items: center; justify-content: center;
width: 24px; height: 24px; border-radius: 50%;
background: #0969da; color: #ffffff;
font-size: 13px; font-weight: 700;
}
/* Nav bar */
#nav-bar {
position: fixed; bottom: 0; left: 0; right: 0;
display: flex; align-items: center; justify-content: center;
padding: 10px 20px env(safe-area-inset-bottom);
background: rgba(255,255,255,0.92);
backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
border-top: 1px solid #d8dee4;
z-index: 100; gap: 24px;
}
#nav-bar button {
width: 48px; height: 48px; border-radius: 50%;
border: 1px solid #d0d7de; background: #fff; color: #24292f;
font-size: 20px; cursor: pointer;
-webkit-tap-highlight-color: transparent;
display: flex; align-items: center; justify-content: center;
}
#nav-bar button:active { background: #f6f8fa; }
#nav-bar button:disabled { opacity: 0.25; }
#nav-counter { font-size: 13px; color: #8b949e; min-width: 70px; text-align: center; font-family: -apple-system, sans-serif; }
2026-02-13 18:53:09 +00:00
< / style >
< / head >
< body >
< div id = "progress-bar" > < / div >
< div id = "slide-counter" > < / div >
2026-07-06 17:15:31 +00:00
< button id = "home-btn" onclick = "window.parent.postMessage({type:'home'},'*')" > ← Home< / button >
2026-02-13 18:53:09 +00:00
< div id = "stage" > < / div >
2026-07-06 17:15:31 +00:00
< div id = "nav-bar" >
< button id = "nav-prev" onclick = "goPrev()" aria-label = "Previous" > ← < / button >
< div id = "nav-counter" > < / div >
< button id = "nav-next" onclick = "goNext()" aria-label = "Next" > → < / button >
< / div >
2026-02-13 18:53:09 +00:00
< script >
const SLIDES = [
// 0: Title
{
role: null,
html: `
< div class = "title-card" >
< div class = "swiss-cross" > 🇨 🇭 < / div >
< h1 > Swiss Post E-Voting< / h1 >
< div class = "subtitle" > A Cryptographic Election in 27 Steps< / div >
2026-07-06 17:15:31 +00:00
< div style = "color:#8b949e; font-size:13px; margin-top:10px;" >
2026-02-13 18:53:09 +00:00
6 voters · 2 candidates · 4 control components · 5 shuffle proofs
< / div >
< div class = "start-hint" > Press Space to begin< / div >
< / div >
`
},
// 1: Canton Chancellor - Open e-voting
{
role: 'Canton Chancellor',
roleClass: 'role-chancellor',
colorClass: 'color-chancellor',
title: 'Opening the Electronic Voting Channel',
html: `
< p > You are the < span class = "em color-chancellor" > Canton Chancellor< / span > , the highest authority for this election.< / p >
< p style = "margin-top:12px" > A federal referendum is coming. Before any cryptographic key is generated, before any ballot exists, you must authorize the opening of the electronic voting channel.< / p >
< div class = "decision-box" >
< div class = "decision-question" > Should you open the electronic voting channel for this election?< / div >
< div class = "decision-answer role-chancellor" > Yes, open e-voting< / div >
< / div >
< p class = "note" > The Canton Chancellor's authorization begins the entire ceremony.< / p >
`
},
// 2: Canton Chancellor - Number of candidates
{
role: 'Canton Chancellor',
roleClass: 'role-chancellor',
colorClass: 'color-chancellor',
title: 'Defining the Ballot',
html: `
< p > How many candidates should appear on the ballot?< / p >
< div class = "decision-box" >
< div class = "decision-question" > How many candidates should be on the ballot?< / div >
< div class = "decision-answer role-chancellor" > 2 candidates: Alice & Bob< / div >
< / div >
`
},
// 3: Canton Chancellor - Sign crypto parameters
{
role: 'Canton Chancellor',
roleClass: 'role-chancellor',
colorClass: 'color-chancellor',
title: 'Signing the Cryptographic Group Parameters',
html: `
< p > The system needs a < span class = "em" > safe prime group< / span > for ElGamal encryption. These parameters define the mathematical universe in which all cryptography will happen.< / p >
< div class = "decision-box" >
< div class = "decision-question" > Sign the order to generate the cryptographic group parameters?< / div >
< div class = "decision-answer role-chancellor" > Sign< / div >
< / div >
< div class = "crypto-block" > < span class = "label" > q< / span > = < span class = "value" > 102456006687220362937022221649168886313301978055082970105502958159943003448101< / span >
< span class = "label" > p< / span > = < span class = "value" > 204912013374440725874044443298337772626603956110165940211005916319886006896203< / span >
< span class = "label" > g< / span > = < span class = "value" > 4< / span > < span class = "dim" > (generator)< / span >
< span class = "dim" > q is 256 bits, p is 257 bits
p = 2q + 1 (safe prime, confirmed)
Both prime (confirmed)< / span > < / div >
< p class = "note" > In production: |p| = 3072 bits. This demo uses 256-bit for readability.< / p >
`
},
// 4: CC0 - Generate key
{
role: 'CC0 Operator (Bern)',
roleClass: 'role-cc',
colorClass: 'color-cc',
title: 'Generating Your Secret Key',
html: `
< p > You are now the < span class = "em color-cc" > CC0 Operator in Bern< / span > . You sit in a secure room with an air-gapped machine. Your job: generate a secret key that < em > only you< / em > will ever know.< / p >
< div class = "decision-box" >
< div class = "decision-question" > Ready to generate your key pair?< / div >
< div class = "decision-answer role-cc" > Generate my key< / div >
< / div >
< div class = "crypto-block" > < span class = "label" > === YOUR SECRET KEY (CC0, Bern) ===< / span >
< span class = "label" > Secret key [0]:< / span > < span class = "secret" > 490298081064930195553874605087930422904...< / span >
< span class = "dim" > (77 digits, only you can see this)< / span >
< span class = "label" > Secret key [1]:< / span > < span class = "secret" > 906260723715705230338812685464779149349...< / span >
< span class = "dim" > (77 digits, only you can see this)< / span >
< span class = "label" > === YOUR PUBLIC KEY (CC0, Bern) ===< / span >
< span class = "label" > Public key [0]:< / span > < span class = "public" > 164907173125453164942397900344209023580...< / span >
< span class = "dim" > Computed as: g ^ secret_key[0] mod p< / span >
< span class = "label" > Public key [1]:< / span > < span class = "public" > 865964935823822544135231120873786690830...< / span >
< span class = "dim" > Computed as: g ^ secret_key[1] mod p< / span > < / div >
< p class = "note" > The public key can be shared with everyone. The secret key never leaves this machine.< / p >
`
},
// 5: CC0 - Schnorr proof
{
role: 'CC0 Operator (Bern)',
roleClass: 'role-cc',
colorClass: 'color-cc',
title: 'Generating a Schnorr Proof of Knowledge',
html: `
< p > You must < em > prove< / em > you know your secret key without revealing it. This is a < span class = "em" > zero-knowledge proof< / span > .< / p >
< div class = "decision-box" >
< div class = "decision-question" > Generate the Schnorr proof?< / div >
< div class = "decision-answer role-cc" > Generate proof< / div >
< / div >
< div class = "crypto-block" > < span class = "label" > --- Schnorr Proof Generation ---< / span >
< span class = "highlight" > Step 1:< / span > Pick a random number 'b' (kept secret)
b = < span class = "secret" > 577282606851693328110434804545...< / span > < span class = "dim" > (secret, thrown away after)< / span >
< span class = "highlight" > Step 2:< / span > Compute commitment c = g^b mod p
c = < span class = "public" > 196993736651760197696345266502366676991...< / span >
< span class = "dim" > (This is like sealing your answer in an envelope)< / span >
< span class = "highlight" > Step 3:< / span > Compute challenge e = Hash(p, q, g, public_key, c)
e = < span class = "value" > 280555736662452288549346417809892436748...< / span >
< span class = "dim" > (The hash makes this unpredictable -- you can't cheat)< / span >
< span class = "highlight" > Step 4:< / span > Compute response z = b + e * secret_key mod q
z = < span class = "value" > 137947587127192694451619751067570738829...< / span >
< span class = "label" > === YOUR SCHNORR PROOF ===< / span >
e = < span class = "value" > 280555736662452288549346417809892436748...< / span >
z = < span class = "value" > 137947587127192694451619751067570738829...< / span >
< span class = "dim" > Anyone can verify: g^z =?= c * public_key^e
If it checks out, you MUST know the secret.
But e and z reveal NOTHING about what the secret is.< / span >
Self-check: g^z == c * PK^e < span class = "public" > PROOF VALID< / span > < / div >
`
},
// 6: CC0 - Hand over to CC1-3
{
role: 'CC0 Operator (Bern)',
roleClass: 'role-cc',
colorClass: 'color-cc',
title: 'Handing Over to the Other Control Components',
html: `
< p > Three more operators in Zurich, Geneva, and Lugano must do the same: generate keys and proofs on their air-gapped machines.< / p >
< div class = "decision-box" >
< div class = "decision-question" > Hand over control to CC1, CC2, CC3?< / div >
< div class = "decision-answer role-cc" > Skip to all done< / div >
< / div >
< div class = "crypto-block" > < span class = "label" > CC1 (Zurich):< / span > Key generated + Schnorr proof < span class = "public" > VALID< / span >
< span class = "label" > CC2 (Geneva):< / span > Key generated + Schnorr proof < span class = "public" > VALID< / span >
< span class = "label" > CC3 (Lugano):< / span > Key generated + Schnorr proof < span class = "public" > VALID< / span > < / div >
< p class = "note" > 4 independent operators in 4 Swiss cities. No single one can decrypt anything alone.< / p >
`
},
// 7: EB President - Generate EB key
{
role: 'EB President',
roleClass: 'role-eb',
colorClass: 'color-eb',
title: 'Electoral Board Key Generation',
html: `
< p > You are the < span class = "em color-eb" > Electoral Board President< / span > . You and your board members each enter a password. Combined, these passwords derive the 5th secret key.< / p >
< div class = "decision-box" >
< div class = "decision-question" > Set passwords and generate the Electoral Board key?< / div >
< div class = "decision-answer role-eb" > Set passwords & generate< / div >
< / div >
< div class = "crypto-block" > < span class = "label" > Electoral Board key generated.< / span >
< span class = "label" > PK4 (Electoral Board):< / span > < span class = "public" > 111235888017674427027755775458...< / span >
< span class = "dim" > This key is split across multiple board members' passwords.
On election night, ALL board members must enter their passwords
to reconstruct this key for the final decryption.< / span > < / div >
`
},
// 8: System Admin - Combine keys
{
role: 'System Administrator',
roleClass: 'role-system',
colorClass: 'color-system',
title: 'Combining All 5 Public Keys',
html: `
< p > You are the < span class = "em" > System Administrator< / span > . You combine all 5 public keys into one < span class = "em" > Election Public Key< / span > .< / p >
< div class = "decision-box" >
< div class = "decision-question" > Combine the 5 public keys?< / div >
< div class = "decision-answer role-system" > Combine< / div >
< / div >
< div class = "crypto-block" > < span class = "label" > PK0 CC0 (Bern) =< / span > < span class = "public" > 190643356536098883252739444301...< / span >
< span class = "label" > PK1 CC1 (Zurich) =< / span > < span class = "public" > 763639679015419285323116529301...< / span >
< span class = "label" > PK2 CC2 (Geneva) =< / span > < span class = "public" > 166178915117197264285723029147...< / span >
< span class = "label" > PK3 CC3 (Lugano) =< / span > < span class = "public" > 500402434679977682038657772038...< / span >
< span class = "label" > PK4 Electoral Board =< / span > < span class = "public" > 111235888017674427027755775458...< / span >
< span class = "dim" > Multiplying all 5 together...< / span >
< span class = "label" > Election Public Key =< / span > < span class = "highlight" > 187387247443810368528889414473024386338505450542...< / span >
< span class = "dim" > This single key locks the ballot box.
To unlock it, you need ALL 5 secret keys working together.< / span > < / div >
`
},
// 9: System Admin - Encode candidates
{
role: 'System Administrator',
roleClass: 'role-system',
colorClass: 'color-system',
title: 'Encoding the Candidates',
html: `
< p > Each candidate is mapped to a prime number, then squared to create a < span class = "em" > quadratic residue< / span > in the group. This encoding enables efficient tallying after decryption.< / p >
< div class = "decision-box" >
< div class = "decision-question" > Encode the candidates?< / div >
< div class = "decision-answer role-system" > Encode candidates< / div >
< / div >
< div class = "crypto-block" > < span class = "label" > Candidate encoding:< / span >
< span class = "value" > Alice< / span > = prime 2 → squared: < span class = "highlight" > 4< / span > < span class = "dim" > (2² = 4)< / span >
< span class = "value" > Bob< / span > = prime 3 → squared: < span class = "highlight" > 9< / span > < span class = "dim" > (3² = 9)< / span > < / div >
< p class = "note" > In the production system, the encoding uses larger primes for more complex ballots.< / p >
`
},
// 10: Registry Officer - Show one card
{
role: 'Registry Officer',
roleClass: 'role-chancellor',
colorClass: 'color-chancellor',
title: 'Generating Voting Cards',
html: `
< p > You are the < span class = "em color-chancellor" > Registry Officer< / span > . Each voter receives a physical card in the mail with their secret codes.< / p >
< div class = "decision-box" >
< div class = "decision-question" > Generate the 6 voting cards?< / div >
< div class = "decision-answer role-chancellor" > Show me one card first< / div >
< / div >
< div class = "card-box" > +------------------------------------------------------+
| SWISS CONFEDERATION |
| Electronic Voting Card |
| |
| Voter ID: voter-0000 |
| Start Voting Key: < span class = "highlight" > SVK-0000< / span > |
| Ballot Casting Key: < span class = "highlight" > BCK-0000< / span > |
| |
| Choice Return Codes (to verify your vote): |
| Alice: < span class = "value" > CC00< / span > |
| Bob: < span class = "value" > CC01< / span > |
| |
| Vote Cast Code: < span class = "value" > VCC00< / span > |
| |
| KEEP THIS CARD SAFE. DO NOT SHARE IT. |
+------------------------------------------------------+< / div >
< p class = "note" > The codes on this card are the voter's only way to verify their vote was recorded correctly.< / p >
`
},
// 11: Registry Officer - Generate all 6 cards
{
role: 'Registry Officer',
roleClass: 'role-chancellor',
colorClass: 'color-chancellor',
title: 'Mailing All 6 Voting Cards',
html: `
< div class = "decision-box" >
< div class = "decision-question" > Generate the remaining 5 cards and mail all 6?< / div >
< div class = "decision-answer role-chancellor" > Generate & mail all 6< / div >
< / div >
< table class = "voter-table" >
< tr > < th > Voter< / th > < th > SVK< / th > < th > BCK< / th > < th > Alice< / th > < th > Bob< / th > < th > VCC< / th > < / tr >
< tr > < td > Anna< / td > < td > SVK-0000< / td > < td > BCK-0000< / td > < td > CC00< / td > < td > CC01< / td > < td > VCC00< / td > < / tr >
< tr > < td > Beat< / td > < td > SVK-0001< / td > < td > BCK-0001< / td > < td > CC10< / td > < td > CC11< / td > < td > VCC01< / td > < / tr >
< tr > < td > Clara< / td > < td > SVK-0002< / td > < td > BCK-0002< / td > < td > CC20< / td > < td > CC21< / td > < td > VCC02< / td > < / tr >
< tr > < td > Daniel< / td > < td > SVK-0003< / td > < td > BCK-0003< / td > < td > CC30< / td > < td > CC31< / td > < td > VCC03< / td > < / tr >
< tr > < td > Eva< / td > < td > SVK-0004< / td > < td > BCK-0004< / td > < td > CC40< / td > < td > CC41< / td > < td > VCC04< / td > < / tr >
< tr > < td > Fritz< / td > < td > SVK-0005< / td > < td > BCK-0005< / td > < td > CC50< / td > < td > CC51< / td > < td > VCC05< / td > < / tr >
< / table >
< p class = "note" > Each voter's codes are unique. Only they know their return codes.< / p >
`
},
// 12: Voter Anna - Authenticate
{
role: 'Voter Anna',
roleClass: 'role-voter',
colorClass: 'color-voter',
title: 'Authentication',
html: `
< p > You are < span class = "em color-voter" > Anna< / span > , a Swiss citizen. You received your voting card in the mail. You open your browser and navigate to the voting portal.< / p >
< div class = "decision-box" >
< div class = "decision-question" > Enter your credentials to authenticate?< / div >
< div class = "decision-answer role-voter" > Enter SVK-0000 + DOB< / div >
< / div >
< div class = "crypto-block" > < span class = "public" > Authentication successful.< / span >
< span class = "dim" > Your Start Voting Key was verified using Argon2id (password hash).
The server never stores your SVK in plaintext.< / span >
< span class = "label" > Ballot loaded:< / span >
[ ] Alice
[ ] Bob< / div >
`
},
// 13: Voter Anna - Vote + encryption
{
role: 'Voter Anna',
roleClass: 'role-voter',
colorClass: 'color-voter',
title: 'Casting Your Vote',
html: `
< div class = "decision-box" >
< div class = "decision-question" > Who do you vote for?< / div >
< div class = "decision-answer role-voter" > Alice< / div >
< / div >
< div class = "crypto-block" > < span class = "label" > === INSIDE YOUR BROWSER ===< / span >
< span class = "highlight" > Step 1:< / span > Encode your choice
Alice → prime 2 → squared: < span class = "value" > 4< / span >
Your vote is the number: < span class = "value" > 4< / span >
< span class = "highlight" > Step 2:< / span > Generate random number 'r' (used once, then discarded)
r = < span class = "secret" > 833704513946265251405968611993787771762...< / span >
< span class = "dim" > This randomness makes YOUR ciphertext unique.
Even if someone else also votes Alice, their
encrypted ballot will look completely different.< / span >
< span class = "highlight" > Step 3:< / span > ElGamal encryption
gamma = g^r mod p
= < span class = "value" > 183571083508143527798599308812874641392...< / span >
phi = ElectionPK^r × vote mod p
= < span class = "value" > 136498980446327435592002680136840893911...< / span >
< span class = "dim" > Your vote (4) is now buried inside 'phi',
hidden behind ElectionPK^r -- a huge random-looking number.
To extract the 4, you'd need to compute gamma^(sum of all 5 secrets),
which requires ALL 5 key holders to cooperate.< / span > < / div >
< div class = "crypto-block" > < span class = "label" > === WHAT GETS SENT TO THE SERVER ===< / span >
gamma = < span class = "value" > 183571083508143527798599308812874641392...< / span >
phi = < span class = "value" > 136498980446327435592002680136840893911...< / span >
< span class = "dim" > That's it. Two big numbers. Pure noise to anyone
who doesn't have all 5 secret keys.< / span > < / div >
`
},
// 14: Voter Anna - Return code + confirm
{
role: 'Voter Anna',
roleClass: 'role-voter',
colorClass: 'color-voter',
title: 'Verifying with Return Codes',
html: `
< p > The server processes your encrypted vote through the 4 control components. Each one computes a partial return code. Combined, they produce a < span class = "em" > Choice Return Code< / span > that appears on your screen.< / p >
< div class = "crypto-block" > < span class = "label" > Server displays:< / span > Choice Return Code = < span class = "highlight" > CC00< / span >
< span class = "dim" > You look at your physical voting card...< / span >
< span class = "label" > Your card says:< / span > Alice = < span class = "highlight" > CC00< / span > < span class = "public" > MATCH!< / span >
< span class = "dim" > This confirms the server recorded "Alice" -- not something else.
No single component could fake this code. All 4 CCs contributed.< / span > < / div >
< div class = "decision-box" >
< div class = "decision-question" > Return code matches. Confirm your vote?< / div >
< div class = "decision-answer role-voter" > Enter BCK-0000 to confirm< / div >
< / div >
< div class = "crypto-block" > < span class = "label" > Server displays:< / span > Vote Cast Code = < span class = "highlight" > VCC00< / span >
< span class = "label" > Your card says:< / span > VCC = < span class = "highlight" > VCC00< / span > < span class = "public" > MATCH!< / span >
< span class = "dim" > Your vote is sealed. Done.< / span > < / div >
`
},
// 15: Voter Beat
{
role: 'Voter Beat',
roleClass: 'role-voter',
colorClass: 'color-voter',
title: 'Beat Votes',
html: `
< div class = "decision-box" >
< div class = "decision-question" > Who do you vote for?< / div >
< div class = "decision-answer role-voter" > Alice< / div >
< / div >
< div class = "crypto-block" > < span class = "dim" > Authenticate with SVK-0001...< / span > < span class = "public" > OK< / span >
< span class = "dim" > Encrypt vote (Alice = 4)...< / span > < span class = "public" > OK< / span >
< span class = "dim" > Return code CC10...< / span > < span class = "public" > MATCH< / span >
< span class = "dim" > Confirm with BCK-0001...< / span > < span class = "public" > OK< / span >
< span class = "dim" > Vote Cast Code VCC01...< / span > < span class = "public" > MATCH< / span > < / div >
`
},
// 16: Voter Clara
{
role: 'Voter Clara',
roleClass: 'role-voter',
colorClass: 'color-voter',
title: 'Clara Votes',
html: `
< div class = "decision-box" >
< div class = "decision-question" > Who do you vote for?< / div >
< div class = "decision-answer role-voter" > Alice< / div >
< / div >
< div class = "crypto-block" > < span class = "dim" > Authenticate with SVK-0002...< / span > < span class = "public" > OK< / span >
< span class = "dim" > Encrypt vote (Alice = 4)...< / span > < span class = "public" > OK< / span >
< span class = "dim" > Return code CC20...< / span > < span class = "public" > MATCH< / span >
< span class = "dim" > Confirm with BCK-0002...< / span > < span class = "public" > OK< / span >
< span class = "dim" > Vote Cast Code VCC02...< / span > < span class = "public" > MATCH< / span > < / div >
`
},
// 17: Voter Daniel
{
role: 'Voter Daniel',
roleClass: 'role-voter',
colorClass: 'color-voter',
title: 'Daniel Votes',
html: `
< div class = "decision-box" >
< div class = "decision-question" > Who do you vote for?< / div >
< div class = "decision-answer role-voter" > Alice< / div >
< / div >
< div class = "crypto-block" > < span class = "dim" > Authenticate with SVK-0003...< / span > < span class = "public" > OK< / span >
< span class = "dim" > Encrypt vote (Alice = 4)...< / span > < span class = "public" > OK< / span >
< span class = "dim" > Return code CC30...< / span > < span class = "public" > MATCH< / span >
< span class = "dim" > Confirm with BCK-0003...< / span > < span class = "public" > OK< / span >
< span class = "dim" > Vote Cast Code VCC03...< / span > < span class = "public" > MATCH< / span > < / div >
`
},
// 18: Voter Eva
{
role: 'Voter Eva',
roleClass: 'role-voter',
colorClass: 'color-voter',
title: 'Eva Votes',
html: `
< div class = "decision-box" >
< div class = "decision-question" > Who do you vote for?< / div >
< div class = "decision-answer role-voter" > Alice< / div >
< / div >
< div class = "crypto-block" > < span class = "dim" > Authenticate with SVK-0004...< / span > < span class = "public" > OK< / span >
< span class = "dim" > Encrypt vote (Alice = 4)...< / span > < span class = "public" > OK< / span >
< span class = "dim" > Return code CC40...< / span > < span class = "public" > MATCH< / span >
< span class = "dim" > Confirm with BCK-0004...< / span > < span class = "public" > OK< / span >
< span class = "dim" > Vote Cast Code VCC04...< / span > < span class = "public" > MATCH< / span > < / div >
`
},
// 19: Voter Fritz
{
role: 'Voter Fritz',
roleClass: 'role-voter',
colorClass: 'color-voter',
title: 'Fritz Votes',
html: `
< p class = "note" style = "margin-bottom:12px" > The last voter. Will he break the streak?< / p >
< div class = "decision-box" >
< div class = "decision-question" > Who do you vote for?< / div >
< div class = "decision-answer role-voter" > Bob< / div >
< / div >
< div class = "crypto-block" > < span class = "dim" > Authenticate with SVK-0005...< / span > < span class = "public" > OK< / span >
< span class = "dim" > Encrypt vote (Bob = 9)...< / span > < span class = "public" > OK< / span >
< span class = "dim" > Return code CC51...< / span > < span class = "public" > MATCH< / span >
< span class = "dim" > Confirm with BCK-0005...< / span > < span class = "public" > OK< / span >
< span class = "dim" > Vote Cast Code VCC05...< / span > < span class = "public" > MATCH< / span > < / div >
`
},
// 20: Voting closed
{
role: null,
roleClass: 'role-none',
title: 'Voting Period Closed',
html: `
2026-07-06 17:15:31 +00:00
< p style = "font-size:18px; color:#1f2328; margin-bottom:20px;" > 6 ballots cast. The voting period is over.< / p >
2026-02-13 18:53:09 +00:00
< div class = "crypto-block" > < span class = "label" > What the server sees (hex dump of encrypted ballots):< / span >
< span class = "dim" > Voter 0 | Alice |< / span > < span class = "value" > E01E83B2B8...B8A0745F41< / span >
< span class = "dim" > Voter 1 | Alice |< / span > < span class = "value" > DE73EEB649...C3E0A5FAF2< / span >
< span class = "dim" > Voter 2 | Alice |< / span > < span class = "value" > FF1D9B7179...735424E93A< / span >
< span class = "dim" > Voter 3 | Alice |< / span > < span class = "value" > 4931157C26...9D6A03E982< / span >
< span class = "dim" > Voter 4 | Alice |< / span > < span class = "value" > 13C275835F...BF41E92D91< / span >
< span class = "dim" > Voter 5 | Bob |< / span > < span class = "value" > 1232996CD9...3791B60DCF< / span >
< span class = "dim" > Every ballot looks like random noise.
The server knows WHO voted, but not WHAT they voted.
Even the labels above (Alice/Bob) are invisible to the server.< / span > < / div >
`
},
// 21: CC0 Shuffle
{
role: 'CC0 Operator (Bern)',
roleClass: 'role-cc',
colorClass: 'color-cc',
title: 'Shuffling the Ballots',
html: `
< p > Now you must < span class = "em" > shuffle< / span > and < span class = "em" > re-encrypt< / span > all 6 ballots. This breaks the link between voters and their encrypted votes.< / p >
< div class = "decision-box" >
< div class = "decision-question" > Begin your shuffle?< / div >
< div class = "decision-answer role-cc" > Shuffle now< / div >
< / div >
< div class = "crypto-block" > < span class = "label" > === INPUT: 6 encrypted ballots (in voter order) ===< / span >
< span class = "dim" > [0]< / span > < span class = "value" > E01E83B2B8...B8A0745F41< / span > < span class = "dim" > ← Anna's ballot< / span >
< span class = "dim" > [1]< / span > < span class = "value" > DE73EEB649...C3E0A5FAF2< / span > < span class = "dim" > ← Beat's ballot< / span >
< span class = "dim" > [2]< / span > < span class = "value" > FF1D9B7179...735424E93A< / span > < span class = "dim" > ← Clara's ballot< / span >
< span class = "dim" > [3]< / span > < span class = "value" > 4931157C26...9D6A03E982< / span > < span class = "dim" > ← Daniel's ballot< / span >
< span class = "dim" > [4]< / span > < span class = "value" > 13C275835F...BF41E92D91< / span > < span class = "dim" > ← Eva's ballot< / span >
< span class = "dim" > [5]< / span > < span class = "value" > 1232996CD9...3791B60DCF< / span > < span class = "dim" > ← Fritz's ballot< / span >
< span class = "dim" > Generating random permutation...< / span >
Secret permutation: < span class = "secret" > [0 2 4 3 5 1]< / span >
< span class = "dim" > (This is destroyed after the proof is generated.)< / span >
< span class = "dim" > Re-encrypting each ballot with fresh randomness...< / span > < / div >
< div class = "crypto-block" > < span class = "label" > === OUTPUT: 6 shuffled + re-encrypted ballots ===< / span >
< span class = "dim" > [0]< / span > < span class = "value" > D7B8715E1D...07B4756A00< / span > < span class = "dim" > ← which voter? Nobody knows.< / span >
< span class = "dim" > [1]< / span > < span class = "value" > 19EC21D022...03FA6B7A94< / span > < span class = "dim" > ← which voter? Nobody knows.< / span >
< span class = "dim" > [2]< / span > < span class = "value" > 1FC53796E9...50AD1138C8< / span > < span class = "dim" > ← which voter? Nobody knows.< / span >
< span class = "dim" > [3]< / span > < span class = "value" > 88B2E59D8B...AB2F8F9CB6< / span > < span class = "dim" > ← which voter? Nobody knows.< / span >
< span class = "dim" > [4]< / span > < span class = "value" > F27A76A4D6...7E83ED14F6< / span > < span class = "dim" > ← which voter? Nobody knows.< / span >
< span class = "dim" > [5]< / span > < span class = "value" > 14F747B54D...A2C76D0315< / span > < span class = "dim" > ← which voter? Nobody knows.< / span >
< span class = "dim" > Compare input [0] to ANY output -- completely different numbers.
No way to match them.< / span > < / div >
< div class = "crypto-block" > < span class = "label" > Generating Bayer-Groth shuffle proof...< / span >
< span class = "proof-tree" > ShuffleArgument< / span >
< span class = "proof-tree" > +-- ProductArgument< / span >
< span class = "proof-tree" > | +-- HadamardArgument< / span >
< span class = "proof-tree" > | | +-- ZeroArgument< / span >
< span class = "proof-tree" > | +-- SingleValueProductArgument< / span >
< span class = "proof-tree" > +-- MultiExponentiationArgument< / span >
< span class = "dim" > This proof guarantees: same 6 votes, just reordered.
No votes added, removed, or changed.< / span >
< span class = "public" > PROOF GENERATED< / span > < span class = "dim" > (5ms)< / span >
< span class = "dim" > Partial decryption: removing CC0's encryption layer...
4 layers remain (CC1 + CC2 + CC3 + Electoral Board)< / span > < / div >
`
},
// 22: CC1-CC3 shuffle
{
role: null,
roleClass: 'role-none',
title: 'CC1, CC2, CC3 Shuffle in Sequence',
html: `
< p > Each control component receives the output of the previous one, shuffles again, re-encrypts, generates a proof, and removes its encryption layer.< / p >
< div class = "decision-box" >
< div class = "decision-question" > Process the remaining 3 CC shuffles?< / div >
< div class = "decision-answer role-cc" > Process all 3< / div >
< / div >
< div class = "crypto-block" > < span class = "color-cc" > CC1 (Zurich):< / span > Shuffle < span class = "public" > OK< / span > Re-encrypt < span class = "public" > OK< / span > Partial decrypt < span class = "public" > OK< / span > Proof < span class = "public" > OK< / span >
< span class = "dim" > 3 layers remain< / span >
< span class = "color-cc" > CC2 (Geneva):< / span > Shuffle < span class = "public" > OK< / span > Re-encrypt < span class = "public" > OK< / span > Partial decrypt < span class = "public" > OK< / span > Proof < span class = "public" > OK< / span >
< span class = "dim" > 2 layers remain< / span >
< span class = "color-cc" > CC3 (Lugano):< / span > Shuffle < span class = "public" > OK< / span > Re-encrypt < span class = "public" > OK< / span > Partial decrypt < span class = "public" > OK< / span > Proof < span class = "public" > OK< / span >
< span class = "dim" > 1 layer remains< / span > < / div >
< p class = "note" > After 4 shuffles, the ballots have been permuted 4 times with 4 independent random permutations. The original order is mathematically unrecoverable.< / p >
`
},
// 23: EB - Final shuffle + decrypt
{
role: 'EB President',
roleClass: 'role-eb',
colorClass: 'color-eb',
title: 'Opening the Ballot Box',
html: `
< p > The Electoral Board performs the 5th and final shuffle, then removes the last encryption layer. The ballots are now in plaintext -- but in a random order that nobody can trace back to voters.< / p >
< div class = "decision-box" >
< div class = "decision-question" > Final shuffle + decrypt?< / div >
< div class = "decision-answer role-eb" > Open the ballot box< / div >
< / div >
< div class = "crypto-block" > < span class = "label" > === THE BALLOT BOX IS OPEN ===< / span >
< span class = "dim" > Slot 0:< / span > decrypted value = < span class = "highlight" > 4< / span > → < span class = "value" > Alice< / span >
< span class = "dim" > Slot 1:< / span > decrypted value = < span class = "highlight" > 4< / span > → < span class = "value" > Alice< / span >
< span class = "dim" > Slot 2:< / span > decrypted value = < span class = "highlight" > 4< / span > → < span class = "value" > Alice< / span >
< span class = "dim" > Slot 3:< / span > decrypted value = < span class = "highlight" > 4< / span > → < span class = "value" > Alice< / span >
< span class = "dim" > Slot 4:< / span > decrypted value = < span class = "highlight" > 9< / span > → < span class = "value" > Bob< / span >
< span class = "dim" > Slot 5:< / span > decrypted value = < span class = "highlight" > 4< / span > → < span class = "value" > Alice< / span >
< span class = "dim" > The votes are in random order. Nobody can tell
which slot belongs to which voter.< / span > < / div >
`
},
// 24: Election result
{
role: null,
roleClass: 'role-none',
title: 'Election Result',
html: `
< div class = "result-box" >
2026-07-06 17:15:31 +00:00
< div style = "font-size:20px; font-weight:700; color:#1f2328; margin-bottom:20px;" > ELECTION RESULT< / div >
2026-02-13 18:53:09 +00:00
< div style = "text-align:left; display:inline-block;" >
< div style = "margin:8px 0; font-size:16px;" >
< span class = "value" style = "display:inline-block; width:60px;" > Alice< / span >
< span class = "em" style = "display:inline-block; width:60px;" > 5 votes< / span >
< span class = "result-bar" style = "width:250px; background:#56d364;" > < / span >
< / div >
< div style = "margin:8px 0; font-size:16px;" >
< span class = "value" style = "display:inline-block; width:60px;" > Bob< / span >
< span class = "em" style = "display:inline-block; width:60px;" > 1 vote< / span >
< span class = "result-bar" style = "width:50px; background:#60a0ff;" > < / span >
< / div >
< / div >
2026-07-06 17:15:31 +00:00
< div style = "margin-top:20px; color:#1a7f37; font-weight:700; font-size:16px;" > Alice is elected.< / div >
2026-02-13 18:53:09 +00:00
< / div >
< div class = "crypto-block" > < span class = "label" > Sanity check:< / span >
Expected: Alice=5 Bob=1 < span class = "dim" > (from your votes)< / span >
Got: Alice=5 Bob=1
< span class = "public" > RESULT MATCHES< / span > < / div >
`
},
// 25: Public Auditor - Verification
{
role: 'Public Auditor',
roleClass: 'role-auditor',
colorClass: 'color-auditor',
title: 'Independent Verification',
html: `
< p > You are a < span class = "em color-auditor" > Public Auditor< / span > . You have NO secret keys. You have NO special access. You only have the public data: the proofs, the public keys, and the encrypted/shuffled ballots.< / p >
< p style = "margin-top:10px" > Can you verify the election is honest? < em > Yes.< / em > That's the entire point.< / p >
< div class = "crypto-block" > < span class = "label" > === VERIFYING 4 KEY PROOFS (Schnorr) ===< / span >
CC0 (Bern): g^z == c * PK^e < span class = "proof-pass" > [PASS]< / span >
CC1 (Zurich): g^z == c * PK^e < span class = "proof-pass" > [PASS]< / span >
CC2 (Geneva): g^z == c * PK^e < span class = "proof-pass" > [PASS]< / span >
CC3 (Lugano): g^z == c * PK^e < span class = "proof-pass" > [PASS]< / span > < / div >
< div class = "crypto-block" > < span class = "label" > === VERIFYING 5 SHUFFLE PROOFS (Bayer-Groth) ===< / span >
Shuffle 0 < span class = "dim" > (CC0, Bern)< / span > -- 6 ciphertexts, 2x3 matrix
< span class = "proof-tree" > ProductArgument ..............< / span > < span class = "proof-pass" > PASS< / span >
< span class = "proof-tree" > HadamardArgument ...........< / span > < span class = "proof-pass" > PASS< / span >
< span class = "proof-tree" > ZeroArgument .............< / span > < span class = "proof-pass" > PASS< / span >
< span class = "proof-tree" > SingleValueProductArgument .< / span > < span class = "proof-pass" > PASS< / span >
< span class = "proof-tree" > MultiExponentiationArgument .< / span > < span class = "proof-pass" > PASS< / span >
==> < span class = "proof-pass" > VERIFIED< / span > < span class = "dim" > (2ms)< / span >
Shuffle 1 < span class = "dim" > (CC1, Zurich)< / span > -- 6 ciphertexts, 2x3 matrix
< span class = "proof-tree" > ProductArgument ..............< / span > < span class = "proof-pass" > PASS< / span >
< span class = "proof-tree" > HadamardArgument ...........< / span > < span class = "proof-pass" > PASS< / span >
< span class = "proof-tree" > ZeroArgument .............< / span > < span class = "proof-pass" > PASS< / span >
< span class = "proof-tree" > SingleValueProductArgument .< / span > < span class = "proof-pass" > PASS< / span >
< span class = "proof-tree" > MultiExponentiationArgument .< / span > < span class = "proof-pass" > PASS< / span >
==> < span class = "proof-pass" > VERIFIED< / span > < span class = "dim" > (2ms)< / span >
Shuffle 2 < span class = "dim" > (CC2, Geneva)< / span > -- 6 ciphertexts, 2x3 matrix
< span class = "proof-tree" > ProductArgument ..............< / span > < span class = "proof-pass" > PASS< / span >
< span class = "proof-tree" > HadamardArgument ...........< / span > < span class = "proof-pass" > PASS< / span >
< span class = "proof-tree" > ZeroArgument .............< / span > < span class = "proof-pass" > PASS< / span >
< span class = "proof-tree" > SingleValueProductArgument .< / span > < span class = "proof-pass" > PASS< / span >
< span class = "proof-tree" > MultiExponentiationArgument .< / span > < span class = "proof-pass" > PASS< / span >
==> < span class = "proof-pass" > VERIFIED< / span > < span class = "dim" > (2ms)< / span >
Shuffle 3 < span class = "dim" > (CC3, Lugano)< / span > -- 6 ciphertexts, 2x3 matrix
< span class = "proof-tree" > ProductArgument ..............< / span > < span class = "proof-pass" > PASS< / span >
< span class = "proof-tree" > HadamardArgument ...........< / span > < span class = "proof-pass" > PASS< / span >
< span class = "proof-tree" > ZeroArgument .............< / span > < span class = "proof-pass" > PASS< / span >
< span class = "proof-tree" > SingleValueProductArgument .< / span > < span class = "proof-pass" > PASS< / span >
< span class = "proof-tree" > MultiExponentiationArgument .< / span > < span class = "proof-pass" > PASS< / span >
==> < span class = "proof-pass" > VERIFIED< / span > < span class = "dim" > (2ms)< / span >
Shuffle 4 < span class = "dim" > (Electoral Board)< / span > -- 6 ciphertexts, 2x3 matrix
< span class = "proof-tree" > ProductArgument ..............< / span > < span class = "proof-pass" > PASS< / span >
< span class = "proof-tree" > HadamardArgument ...........< / span > < span class = "proof-pass" > PASS< / span >
< span class = "proof-tree" > ZeroArgument .............< / span > < span class = "proof-pass" > PASS< / span >
< span class = "proof-tree" > SingleValueProductArgument .< / span > < span class = "proof-pass" > PASS< / span >
< span class = "proof-tree" > MultiExponentiationArgument .< / span > < span class = "proof-pass" > PASS< / span >
==> < span class = "proof-pass" > VERIFIED< / span > < span class = "dim" > (2ms)< / span > < / div >
< div class = "crypto-block" > < span class = "label" > Count Verification:< / span >
Decrypted ballots: 6
Ballots submitted: 6
==> < span class = "proof-pass" > PASS: Every ballot is accounted for.< / span > < / div >
`
},
// 26: Final verdict
{
role: null,
roleClass: 'role-none',
title: 'The Verdict',
html: `
< div class = "verdict-box" >
2026-07-06 17:15:31 +00:00
< div style = "text-align:center; font-size:18px; font-weight:700; color:#1a7f37; margin-bottom:16px;" > ALL VERIFICATIONS PASSED.< / div >
2026-02-13 18:53:09 +00:00
< p > As a public auditor, you have independently verified:< / p >
< div style = "margin:14px 0 14px 10px;" >
< div style = "margin:6px 0;" > < span class = "proof-pass" > [x]< / span > All 4 key holders proved they know their secrets< / div >
< div style = "margin:6px 0;" > < span class = "proof-pass" > [x]< / span > All 5 shuffles are mathematically honest< / div >
< div style = "margin:6px 0;" > < span class = "proof-pass" > [x]< / span > No votes were added, removed, or changed< / div >
< div style = "margin:6px 0;" > < span class = "proof-pass" > [x]< / span > The final count matches the number of ballots< / div >
< / div >
< p style = "margin-top:14px;" > You did this < span class = "em" > WITHOUT< / span > any secret keys.< / p >
< p > You did this < span class = "em" > WITHOUT< / span > trusting anyone.< / p >
2026-07-06 17:15:31 +00:00
< p style = "margin-top:10px; color:#1a7f37; font-weight:600;" > Pure mathematics.< / p >
2026-02-13 18:53:09 +00:00
< / div >
2026-07-06 17:15:31 +00:00
< div style = "margin-top:24px; padding:20px; border:1px solid #d0d7de; border-radius:8px; background:#f6f8fa;" >
< div style = "color:#57606a; font-size:13px; margin-bottom:12px;" > RECAP -- What you experienced:< / div >
2026-02-13 18:53:09 +00:00
< div style = "margin:10px 0;" >
< span class = "color-cc" style = "font-weight:700;" > AS THE CC OPERATOR:< / span >
2026-07-06 17:15:31 +00:00
< span style = "color:#24292f;" > You generated keys, shuffled votes, created proofs. You never saw anyone's vote. Even you can't undo your own shuffle -- the permutation was destroyed.< / span >
2026-02-13 18:53:09 +00:00
< / div >
< div style = "margin:10px 0;" >
< span class = "color-voter" style = "font-weight:700;" > AS THE VOTER:< / span >
2026-07-06 17:15:31 +00:00
< span style = "color:#24292f;" > Your vote was encrypted in your browser. The server stored only noise. Return codes on your physical card confirmed it was recorded correctly.< / span >
2026-02-13 18:53:09 +00:00
< / div >
< div style = "margin:10px 0;" >
< span class = "color-auditor" style = "font-weight:700;" > AS THE AUDITOR:< / span >
2026-07-06 17:15:31 +00:00
< span style = "color:#24292f;" > You verified every step with public data and math. No trust required. No access to secrets. The proofs are either valid or they aren't. No gray area.< / span >
2026-02-13 18:53:09 +00:00
< / div >
< / div >
2026-07-06 17:15:31 +00:00
< div style = "margin-top:24px; padding:16px; border:1px solid #d0d7de; border-radius:8px; text-align:center;" >
< p style = "color:#57606a; font-size:13px;" > This is the same protocol used in real Swiss federal elections.< / p >
< p style = "color:#57606a; font-size:13px; margin-top:6px;" > The production system: 14 repositories, 500,000+ lines, Windows + Kubernetes + 50GB RAM.< / p >
< p style = "color:#57606a; font-size:13px; margin-top:6px;" > This demo: one 15MB Go binary on a Mac.< / p >
< p style = "color:#1f2328; font-size:14px; font-weight:600; margin-top:10px;" > The math is identical.< / p >
2026-02-13 18:53:09 +00:00
< / div >
`
}
];
let currentSlide = 0;
const stage = document.getElementById('stage');
const progressBar = document.getElementById('progress-bar');
const slideCounter = document.getElementById('slide-counter');
2026-07-06 17:15:31 +00:00
const navPrev = document.getElementById('nav-prev');
const navNext = document.getElementById('nav-next');
const navCounter = document.getElementById('nav-counter');
function goNext() {
if (currentSlide < SLIDES.length - 1 ) {
currentSlide++;
render();
}
}
function goPrev() {
if (currentSlide > 0) {
currentSlide--;
render();
}
}
2026-02-13 18:53:09 +00:00
function render() {
const slide = SLIDES[currentSlide];
const total = SLIDES.length;
progressBar.style.width = ((currentSlide / (total - 1)) * 100) + '%';
slideCounter.textContent = (currentSlide + 1) + ' / ' + total;
2026-07-06 17:15:31 +00:00
navPrev.disabled = (currentSlide === 0);
navNext.disabled = (currentSlide === total - 1);
navCounter.textContent = (currentSlide + 1) + ' / ' + total;
2026-02-13 18:53:09 +00:00
let html = '< div class = "fade-in" > ';
if (slide.role) {
html += '< div class = "role-banner ' + (slide.roleClass || 'role-none') + '" > ' + slide.role + '< / div > ';
}
if (slide.title) {
html += '< div class = "slide-title" > ' + slide.title + '< / div > ';
}
html += '< div class = "content" > ' + slide.html + '< / div > ';
html += '< / div > ';
stage.innerHTML = html;
stage.scrollTop = 0;
2026-07-06 17:15:31 +00:00
if (currentSlide === SLIDES.length - 1) {
stage.innerHTML += '< div class = "manual-cta" > < button onclick = "window.parent.postMessage({type:\'manual\'},\'*\')" > Continue to Operations Manual → < / button > < / div > ';
}
2026-02-13 18:53:09 +00:00
}
window.addEventListener('keydown', function(e) {
if (e.key === ' ' || e.key === 'Enter' || e.key === 'ArrowRight' || e.key === 'ArrowDown') {
e.preventDefault();
e.stopPropagation();
2026-07-06 17:15:31 +00:00
goNext();
2026-02-13 18:53:09 +00:00
} else if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') {
e.preventDefault();
2026-07-06 17:15:31 +00:00
goPrev();
2026-02-13 18:53:09 +00:00
} else if (e.key === 'Home') {
e.preventDefault();
currentSlide = 0;
render();
} else if (e.key === 'End') {
e.preventDefault();
currentSlide = SLIDES.length - 1;
render();
}
});
2026-07-06 17:15:31 +00:00
// Touch support for mobile — swipe only, no tap-to-advance
2026-02-13 18:53:09 +00:00
let touchStartX = 0;
2026-07-06 17:15:31 +00:00
let touchStartY = 0;
2026-02-13 18:53:09 +00:00
document.addEventListener('touchstart', function(e) {
touchStartX = e.changedTouches[0].screenX;
2026-07-06 17:15:31 +00:00
touchStartY = e.changedTouches[0].screenY;
2026-02-13 18:53:09 +00:00
});
document.addEventListener('touchend', function(e) {
2026-07-06 17:15:31 +00:00
const diffX = e.changedTouches[0].screenX - touchStartX;
const diffY = e.changedTouches[0].screenY - touchStartY;
// Only navigate on horizontal swipe with threshold of 60px,
// and only if horizontal movement exceeds vertical (not a scroll)
if (Math.abs(diffX) > 60 & & Math.abs(diffX) > Math.abs(diffY)) {
if (diffX < 0 & & currentSlide < SLIDES . length - 1 ) {
2026-02-13 18:53:09 +00:00
currentSlide++;
render();
2026-07-06 17:15:31 +00:00
} else if (diffX > 0 & & currentSlide > 0) {
2026-02-13 18:53:09 +00:00
currentSlide--;
render();
}
}
});
render();
< / script >
< / body >
< / html >