- {roundState.vote_count} votes
+ )}
+
+ {endTime && !isEnded && !busy && (
+
- You voted
+ )}
+
+ {busy &&
}
+ {isLoading && !roundState && !busy &&
}
+
+ {/* Active poll — voting actions */}
+ {roundState && !isEnded && (
+
+ {noPollSelected && (
+
+ {hasVotedInCurrentRound ? 'Select an option to update your vote' : 'Select your favorite'}
+
+ )}
+
+
+
- )}
- {voteStatusLoading && (
-
- Checking...
-
- )}
-
- )}
-
- {endTime && !isEnded && !isCastingVote && (
-
-
-
- )}
- {(isCastingVote || isMasking) &&
}
- {loading && !isCastingVote && !isMasking &&
}
-
- {pollOptions.map((poll) => (
-
-
handleChecked(poll)}>
- {poll.label}
-
- ))}
+ )}
+
+ {/* Poll over — tallying / results, no more voting */}
+ {roundState && isEnded && (
+
+ {tallyReady ? (
+ <>
+
The threshold committee has decrypted the result.
+
+
+
+ >
+ ) : (
+
+ Voting is closed. Ballots are being tallied under encryption — results will appear here once the committee publishes the
+ decrypted tally.
+
+ )}
+
+ )}
- {roundState && (
-
- {noPollSelected && !isEnded && (
-
- {hasVotedInCurrentRound ? 'Select an option to update your vote' : 'Select your favorite'}
+
+ {/* Right — faceoff + ciphertext */}
+ {hasPoll && (
+
+
+
+
+ vs
- )}
-
-
+
+
+
+
+
+ {busy ? 'Encrypting your ballot…' : 'Your ballot will be encrypted before it leaves this page'}
+
+
+
)}
-
- >
+
+
)
}
diff --git a/examples/CRISP/client/src/pages/Landing/components/Hero.tsx b/examples/CRISP/client/src/pages/Landing/components/Hero.tsx
index b35c32c0e4..055b1696d0 100644
--- a/examples/CRISP/client/src/pages/Landing/components/Hero.tsx
+++ b/examples/CRISP/client/src/pages/Landing/components/Hero.tsx
@@ -5,63 +5,92 @@
// or FITNESS FOR A PARTICULAR PURPOSE.
import React from 'react'
-import Logo from '@/assets/icons/logo.svg'
-import CircularTiles from '@/components/CircularTiles'
import { Link } from 'react-router-dom'
import { Keyhole, ListMagnifyingGlass, ShieldCheck } from '@phosphor-icons/react'
+import { EditorialShell, Cipher, MarkerUnderline } from '@/design/Editorial'
+
+const PRINCIPLES = [
+ {
+ icon: Keyhole,
+ label: 'Private',
+ body: 'Voter privacy through fully homomorphic encryption — ballots are encrypted before they ever leave your device.',
+ },
+ {
+ icon: ListMagnifyingGlass,
+ label: 'Reliable',
+ body: 'Verifiable results while preserving confidentiality. The tally is computed on ciphertext and proven correct.',
+ },
+ {
+ icon: ShieldCheck,
+ label: 'Equitable',
+ body: 'Robust safeguards against coercion and tampering, with a threshold committee that no single party controls.',
+ },
+]
const HeroSection: React.FC = () => {
return (
-
-
-
-
-
-
-
Introducing
-

-
Coercion-Resistant Impartial Selection Protocol
-
-
- -
-
-
-
Private.
- Voter privacy through advanced encryption.
+
+
+
+ {/* Left — editorial copy */}
+
+
+
Coercion-Resistant Impartial Selection Protocol
+
+ Crisp
+
+
+ Secret-ballot voting you can actually verify. Cast an encrypted vote, let a threshold committee open only the final tally —
+ and nobody, not even the people running the election, learns how you voted.
+
-
-
-
-
-
-
Reliable.
- Verifiable results while preserving confidentiality.
+
+
+
+
+
+
+
+ Try the demo →
+
+
-
-
-
-
-
- Equitable.
- Robust safeguards against coercion and tampering.
+
+
+ {/* Right — ciphertext visual */}
+
+
+
+ Your ballot, encrypted
+ FHE
+
+
+
+
+ This is what a vote looks like on-chain — opaque to everyone, tallied without ever being decrypted.
+
-
-
-
-
-
This is a simple demonstration of CRISP technology.
-
-
Learn more.
-
-
-
-
-
-
+
+
)
}
diff --git a/examples/CRISP/client/src/pages/Landing/components/PastPoll.tsx b/examples/CRISP/client/src/pages/Landing/components/PastPoll.tsx
index a68019e987..aac5a56763 100644
--- a/examples/CRISP/client/src/pages/Landing/components/PastPoll.tsx
+++ b/examples/CRISP/client/src/pages/Landing/components/PastPoll.tsx
@@ -9,6 +9,7 @@ import PollCard from '@/components/Cards/PollCard'
import { PollResult } from '@/model/poll.model'
import { useVoteManagementContext } from '@/context/voteManagement'
import { Link } from 'react-router-dom'
+import { EditorialShell } from '@/design/Editorial'
type PastPollSectionProps = {
customLabel?: string
@@ -21,17 +22,19 @@ const PastPollSection: React.FC = ({ customLabel = 'Past p
const pollsToShow = limit ? pastPolls.slice(0, limit) : pastPolls
return (
-
-
{customLabel}
-
- {pollsToShow.map((poll: PollResult) => (
-
- ))}
-
-
-
-
-
+
+
+ {customLabel}
+
+ {pollsToShow.map((poll: PollResult) => (
+
+ ))}
+
+
+ View all polls →
+
+
+
)
}
diff --git a/examples/CRISP/client/src/pages/PollResult/PollResult.tsx b/examples/CRISP/client/src/pages/PollResult/PollResult.tsx
index 4c5db262f8..2dd44d2655 100644
--- a/examples/CRISP/client/src/pages/PollResult/PollResult.tsx
+++ b/examples/CRISP/client/src/pages/PollResult/PollResult.tsx
@@ -13,7 +13,7 @@ import PastPollSection from '@/pages/Landing/components/PastPoll'
import { useParams } from 'react-router-dom'
import LoadingAnimation from '@/components/LoadingAnimation'
import { useVoteManagementContext } from '@/context/voteManagement'
-import CircularTiles from '@/components/CircularTiles'
+import { EditorialShell } from '@/design/Editorial'
import CountdownTimer from '@/components/CountdownTime'
import ConfirmVote from '../DailyPoll/components/ConfirmVote'
@@ -56,11 +56,8 @@ const PollResult: React.FC = () => {
}, [pollResult])
return (
-
-
-
-
-
+
+
{loading && !pollResult && (
@@ -68,22 +65,19 @@ const PollResult: React.FC = () => {
)}
{!loading && pollResult && (
-
-
-
-
Poll {pollResult.roundId}
-
- {type === 'confirmation' ? 'Thanks for voting!' : 'Poll Results'}
-
- {type !== 'confirmation' &&
{formatDate(pollResult.date)}
}
-
- {type === 'confirmation' && roundEndDate && (
-
-
-
- )}
-
+
+
+
Poll {pollResult.roundId}
+
{type === 'confirmation' ? 'Thanks for voting!' : 'Poll Results'}
+ {type !== 'confirmation' &&
{formatDate(pollResult.date)}
}
+ {type === 'confirmation' && roundEndDate && (
+
+ )}
+
{
{type === 'confirmation' && txUrl && }
{type !== 'confirmation' && (
-
-
WHAT JUST HAPPENED?
-
-
- After casting your vote, CRISP securely processed your selection using a blend of Fully Homomorphic Encryption (FHE),
- threshold cryptography, and zero-knowledge proofs (ZKPs), without revealing your identity or choice. Your vote was
- encrypted and anonymously aggregated with others, ensuring the integrity of the voting process while strictly
- maintaining confidentiality. The protocol's advanced cryptographic techniques guarantee that your vote contributes to
- the final outcome without any risk of privacy breaches or undue influence.
-
-
+
+
WHAT JUST HAPPENED?
+
+ After casting your vote, CRISP securely processed your selection using a blend of Fully Homomorphic Encryption (FHE),
+ threshold cryptography, and zero-knowledge proofs (ZKPs), without revealing your identity or choice. Your vote was
+ encrypted and anonymously aggregated with others, ensuring the integrity of the voting process while strictly
+ maintaining confidentiality. The protocol's advanced cryptographic techniques guarantee that your vote contributes to
+ the final outcome without any risk of privacy breaches or undue influence.
+
-
-
WHAT DOES THIS MEAN?
-
+
+
WHAT DOES THIS MEAN?
+
Your participation has directly contributed to a transparent and fair decision-making process, showcasing the power of
privacy-preserving technology in governance and beyond. The use of CRISP in this vote represents a significant step
towards secure, anonymous, and tamper-proof digital elections and polls. This innovation ensures that every vote counts
@@ -119,15 +111,11 @@ const PollResult: React.FC = () => {
)}
- {pastPolls.length > 0 && (
-
- )}
+ {pastPolls.length > 0 &&
}
)}
-
-
+
+
)
}
diff --git a/examples/CRISP/test/crisp.spec.ts b/examples/CRISP/test/crisp.spec.ts
index 9a0ac312c5..cd385bf70c 100644
--- a/examples/CRISP/test/crisp.spec.ts
+++ b/examples/CRISP/test/crisp.spec.ts
@@ -68,7 +68,7 @@ const test = testWithSynpress(metaMaskFixtures(basicSetup))
const { expect } = test
async function ensureHomePageLoaded(page: Page) {
- return await expect(page.locator('h4')).toHaveText('Coercion-Resistant Impartial Selection Protocol')
+ return await expect(page.getByText('Coercion-Resistant Impartial Selection Protocol')).toBeVisible()
}
function log(msg: string) {
@@ -133,7 +133,7 @@ test('CRISP smoke test', async ({ context, page, metamaskPage, extensionId }) =>
log(`connecting to dapp...`)
await metamask.connectToDapp()
log(`clicking try demo...`)
- await page.locator('button:has-text("Try Demo")').click()
+ await page.locator('a:has-text("Try the demo")').click()
log(`waiting for E3 Committee being published...`)
await waitForE3Ready(e3id)
@@ -143,22 +143,22 @@ test('CRISP smoke test', async ({ context, page, metamaskPage, extensionId }) =>
await page.reload()
log(`clicking first vote card...`)
- await page.locator("[data-test-id='poll-button-0'] > [data-test-id='card']").click()
+ await page.locator("[data-test-id='poll-button-0']").click()
log(`clicking Cast Vote...`)
- await page.locator('button:has-text("Cast Vote")').click()
+ await page.locator('button:has-text("Cast")').click()
log(`confirming MetaMask signature request...`)
await metamask.confirmSignature()
const WAIT = E3_DURATION - DKG_DURATION + OUTPUT_DECRYPTION_WAIT
log(`waiting ${WAIT}ms...`)
await page.waitForTimeout(WAIT)
log(`clicking historic polls button...`)
- await page.locator('a:has-text("Historic polls")').click()
+ await page.locator('a:has-text("Historic Polls")').click()
log(`asserting that Historic polls exists...`)
- await expect(page.locator('h1')).toHaveText('Historic polls')
+ await expect(page.locator('h1')).toHaveText('Past polls')
log(`asserting that result has 100% on the vote we clicked on...`)
- await expect(page.locator("[data-test-id='poll-0-0'] [data-test-id='poll-result-0'] h3")).toHaveText('100%')
+ await expect(page.locator("[data-test-id='poll-0-0'] [data-test-id='poll-result-0'] .h2")).toHaveText('100%')
log(`asserting that result has 0% on the vote we did not click on...`)
- await expect(page.locator("[data-test-id='poll-0-0'] [data-test-id='poll-result-1'] h3")).toHaveText('0%')
+ await expect(page.locator("[data-test-id='poll-0-0'] [data-test-id='poll-result-1'] .h2")).toHaveText('0%')
log('============================================')
log(' PLAYWRIGHT TEST IS COMPLETE ')