diff --git a/README.md b/README.md index 4892589..0814cd4 100644 --- a/README.md +++ b/README.md @@ -11,8 +11,9 @@ In July 2026, the European Parliament let the "Chat Control" machinery advance Its promises, all enforced by tests: - **Static and self-contained** — no tracker, no cookie, not a single request to a third-party domain. -- **Readable with JavaScript disabled** (Tor Browser "safest" mode included) — scripts only power the theme, filters and checklist. +- **Readable with JavaScript disabled** (Tor Browser "safest" mode included) — scripts only power the theme, filters, checklist and quiz. - **Multilingual by design** — every language is a real prerendered route with correct `hreflang`; adding one means adding translation files, not touching code. +- **Educational quiz** — `/quiz` scores your censorship resistance on a 0–100 scale and points you at concrete fixes in the guide. Server-rendered questions and scoring key (readable with JS off); the score itself is computed locally in the browser, nothing sent or stored. - **Printable and mirrorable** — a single-file offline version ships with every build. ## Stack diff --git a/src/components/Quiz.astro b/src/components/Quiz.astro new file mode 100644 index 0000000..48a1653 --- /dev/null +++ b/src/components/Quiz.astro @@ -0,0 +1,470 @@ +--- +// Censorship-resistance quiz — a progressive-enhancement scorer. EVERYTHING +// the reader needs is server-rendered: the twelve questions (native radios, +// selectable with JS off) and the full scoring key (what each band means + +// where to improve, with deep links into the guide). The client script only +// computes the live score and reveals the result panel; with JS off the +// static key still teaches the whole thing. No third-party request, no inline +// styles (bar widths come from a CSS custom property set via the CSSOM, which +// style-src 'self' allows), no inline handlers (delegated on document). +import { localePath, useT, type Locale } from '../i18n/config' +import { loadQuiz } from '../lib/content' +import Ext from './Ext.astro' + +interface Props { + locale: Locale +} + +const { locale } = Astro.props +const t = useT(locale) +const { bands, questions } = loadQuiz(locale) + +const localeRoot = `${localePath(locale)}/`.replace('//', '/') +const guideHref = (anchor: string) => `${localeRoot}#${anchor}` +const total = questions.length +const progress0 = t('quiz.progress').replace('{done}', '0').replace('{total}', String(total)) +--- + +
+
+

{t('quiz.eyebrow')}

+

{t('quiz.title')}

+

{t('quiz.lede')}

+

{t('quiz.privacyNote')}

+ +
+ + + +
+

{t('quiz.privacyNote')}

+
    + { + questions.map((question, i) => ( +
  1. +
    + + {String(i + 1).padStart(2, '0')} {question.prompt} + +
    + {question.options.map((option) => ( + + ))} +
    +
    +
  2. + )) + } +
+
+ + + +
+

{t('quiz.bandsHeading')}

+ + +

{t('quiz.categoriesHeading')}

+ +
+
+ + diff --git a/src/components/TopBar.astro b/src/components/TopBar.astro index 2b61b84..9229ac1 100644 --- a/src/components/TopBar.astro +++ b/src/components/TopBar.astro @@ -17,9 +17,18 @@ const t = useT(locale) > EXIT CHAT CONTROL - + {t('nav.toc')} + + {t('nav.quiz')} +