New features and improvements to Simparlia, most recent first. Each entry marks a release of the Corpus explorer.
v0.1.20.0
Added
Public changelog at `/changelog` (SIM-99): a developer-docs-style page, most-recent-first, that parses this CHANGELOG.md at build time as the single source of truth (linked from the nav and sitemap), plus an auto-update pipeline — a `feat` landing on main appends its entry and bumps VERSION via a GitHub Action, so the page rebuilds itself when features drop
v0.1.19.0
Added
Compare two MPs side by side (SIM-118): a new `/corpus/compare/[a]/[b]` view that places two MP profiles in parallel columns — ideology scales, voting-behaviour metrics, and policy positions aligned for direct contrast — with a picker to swap either Member, a shareable URL, and a per-comparison Open Graph card
v0.1.18.0
Added
Evidence drill-down on profile sections (SIM-117): opening a briefing section reveals the source material behind each claim — the specific speeches, divisions, and written questions — so every statement on a profile is traceable back to the parliamentary record
v0.1.17.0
Added
Source-type provenance icons (SIM-15): a monochrome glyph system marking which kind of source backs each piece of evidence (speech, division, written question, committee, early day motion, and more), shown inline on the profile and in the Data & Sources panel
v0.1.16.0
Added
Portrait-led social share cards (SIM-16): per-MP Open Graph and Twitter images that foreground the official parliamentary portrait alongside the Member's name, party, and constituency, for richer previews when a profile is shared
v0.1.15.0
Added
Ideology glossary and tooltips (SIM-119): plain-English definitions for the economic, social, and sovereignty scales, surfaced as tooltips beside each ideology bar and collected in one place so the axes are legible to non-specialists
v0.1.14.0
Changed
Ideology and positions framed as model estimates (SIM-111): profile ideology scores and policy positions are now presented explicitly as model estimates with a confidence level rather than stated facts — honest framing while independent validation is pending
v0.1.13.0
Added
MP roles (SIM-110): sitting post in the profile header (Commons-green accent line — "Prime Minister and First Lord of the Treasury" on Starmer's page; backbenchers stay quiet), and a Government Record briefing section with each MP's dated government/opposition post history (Sunak's page now shows his PM and Chancellor record)
v0.1.12.0
Changed
MP profile page design pass, iteration 1 (SIM-12): numbered briefing sections with hairline rules, ideology bars gain quarter-tick grid + evidence lines + settle-in motion, voting bars thickened with fill animation (all motion honest-HTML and reduced-motion safe), portrait+name header lockup, provenance footer (profile version, model, generation date)
v0.1.11.0
Added
Find your MP by postcode (SIM-25): `POST /api/mp/lookup` resolves UK postcodes via postcodes.io (ONS data) matched against our own MP data — rate-limited, 30-day cached, clear errors for invalid/unknown postcodes and vacant seats; compact lookup form on the Members directory navigating straight to the MP's profile; `mp_lookup` analytics event (never logs the postcode itself)
Production builds cut from ~17 minutes to ~14 seconds (SIM-104): MP profile pages now render on demand with hourly ISR instead of pre-rendering all 650 at build time; data-layer accessors wrapped in React `cache()` to dedupe the metadata + page double-fetch per request
v0.1.9.0
Changed
`/corpus` restored as the marketing page (live-review iterate, SIM-105): description, "Browse the 650 Members" primary CTA, waitlist secondary, Dispatch cross-link — ASCII scene retained
MP directory moved to `/corpus/members` (search, party filter, full grid); sitemap updated; profile pages link back to "All Members"
Data pages (`/corpus/members` + all 650 `/corpus/[slug]` profiles) render a plain background — no ASCII scene behind dense content, which was unreadable
v0.1.8.0
Added
`/corpus` is now the MP discovery portal (SIM-27): browsable card grid of all 650 MPs (surname-ordered), instant client-side search across names and constituencies, party filter with display-level grouping (Labour (Co-op) folds into Labour for filtering; cards show the verbatim party), live result count, `corpus_search`/`corpus_filter` analytics events
`MPCard` and `MPDirectory` components join the mp-profile library
Honest degradation: a data-layer outage shows an unavailable notice instead of failing the page; the waitlist (now a secondary footer CTA) survives
v0.1.7.0
Added
650 MP profile pages at `/corpus/[slug]` (SIM-26): SSG via `generateStaticParams` + 1-hour ISR, persona lede, ideology position bars (economic/social/sovereignty), voting-behaviour metrics, key interests, policy positions, notable rebellions, reasoning style, and background — all rendered from the live `mp_profiles` data layer
Per-MP dynamic Open Graph images (Caslon display, Commons-green accent bar)
SEO: per-page metadata + canonical, JSON-LD Person schema, sitemap entries for all 650 MPs; `mp_profile_view` analytics event; 404 page for unknown MPs
Changed
Background location resolution falls back to the section path, so dynamic routes inherit their section's ASCII scene
Vitest runs on the threads pool (forks pool broken under Node 25; 306 tests now run in ~6s)
v0.1.6.0
Added
`mp_profiles` Supabase table — the Corpus data layer bridging the synthetic-parliament research pipeline to the product (SIM-20). Migrations 002–004: JSONB `layer1_profile`/`layer2_profile`/`ideology_scores`, provenance columns, CHECK constraints (slug format, rates 0–1), `updated_at` trigger, `sort_name` ordering column, RLS locked to service-role reads, documented slug collision rule
Typed data-access module `src/lib/mp-profiles.ts`: `mpSlug()` canonical slug generation (honorific stripping incl. stacked titles, diacritic folding, non-decomposable transliteration), `getMPProfileBySlug()` and `listMPProfiles()` accessors that throw on query errors so outages surface as 500s rather than cached empty states
20 unit tests covering slug rules, fetch hit/miss/error, list ordering/filtering/error
v0.1.5.0
Added
`sitemap.xml` generated via Next.js App Router (`src/app/sitemap.ts`) listing all 9 public routes
`robots.txt` allowing all crawlers with sitemap reference (`src/app/robots.ts`)
Per-page metadata (unique title + description) for all pages via route-level layouts
JSON-LD structured data: Organization + WebSite schema in root layout
Canonical URLs (`alternates.canonical`) on all pages
v0.1.4.4
Fixed
Homepage scroll and CTA button blocked during ASCII background reveal animation and window resize re-render
Prevents broken background transitions when user interacts before the reveal completes
v0.1.4.3
Added
Staggered fade-out for homepage hero content on scroll: heading fades first, then subtitle, then CTA button. All invisible by ~55% scroll depth.
v0.1.4.2
Changed
All buttons full-width on mobile: homepage CTA, Explore Corpus, Explore Dispatch now match waitlist form button width
Desktop button sizing unchanged (min-w-65 inline)
v0.1.4.1
Changed
Corpus page rebuilt from flex column to block layout matching Dispatch pattern
Single responsive layout (no duplicate desktop/mobile render)
Tests restored to `getBy` (single element) instead of `getAllBy` workarounds
v0.1.4.0
Added
Hamburger menu on mobile (<640px) with full-screen overlay and Caslon Old Face links
`prefers-reduced-motion: reduce` support — disables all animations and transitions
`aria-label` on all waitlist form inputs and ASCII canvas
`aria-live` region for waitlist form error announcements
`<main>` semantic wrapper in layout
iOS input zoom prevention (16px minimum on mobile inputs)
Homepage hero left-aligned on mobile with "Parliament," / "Predicted." line break
Subtitle constrained to max-w-lg on desktop for balanced wrapping
Changed
Muted text colour darkened from `#7A7570` to `#736E69` (4.83:1 contrast ratio, passes WCAG AA)
Mobile background: clean off-white, no images (placeholder for custom mobile assets)
Dispatch and Privacy pages: mobile layout left-aligned with 20vh top padding
Research and About pages: static positioning on mobile with 20vh top padding
Corpus page: separate desktop/mobile layouts (mobile is block-level, left-aligned)
Cross-link buttons use `max-sm:flex max-sm:w-full` for full-width on mobile
Button gap between form and cross-link standardised to mt-2 on Corpus and Dispatch
v0.1.3.1
Changed
Page transition mode changed from `sync` to `wait` — exit animation completes before enter starts, eliminating content overlap on slow connections
Nav links preload target route's background image on hover for faster transitions
All route background images preloaded after 2 seconds idle
Removed
6 unused high-res PNG images (~65MB) and 5 unused JPG images (~1.9MB) from `public/images/`
v0.1.3.0
Added
Server-side traffic source classification: every event gets a `traffic_source` field (`direct`, `organic`, `social`, `referral`, or `campaign`) derived from referrer and UTM signals
Referrer domain parsing: `referrer_domain` field extracted from `document.referrer` with `www.` stripping for human-readable querying
Internal page navigation tracking: `from_page` field on `page_view` events enables user journey reconstruction through the site
Domain boundary matching for traffic source classification to prevent false positives (e.g. `googledocs.com` not misclassified as organic)
Waitlist confirmation email via Resend — sends on new signups only, not duplicates
`src/lib/resend.ts` — Resend client singleton (same pattern as Supabase client)
`src/lib/waitlist-email.ts` — plain text + minimal HTML email matching the design system (Geist Mono, monochromatic palette, Caslon wordmark)
`simparlia.com` domain verified with Resend (EU region, enforced TLS, DKIM + SPF configured via Cloudflare)
Changed
Waitlist route now checks for existing email before inserting (duplicates return success silently, no email sent)
Waitlist route handles invalid JSON body gracefully (400 instead of unhandled throw)
Email validation runs after normalisation (trim + lowercase) so whitespace-padded emails are accepted
Select query errors are now logged and return 500 (previously silently swallowed)
Supabase mock supports `selectChain()` for `.select().eq().limit().maybeSingle()` patterns
v0.1.2.0
Added
Dual-layer analytics: Vercel Web Analytics + Speed Insights for the dashboard, Supabase `events` table for SQL-queryable owned data
`POST /api/event` endpoint with event name allowlist, type validation, and Supabase insert
`useAnalytics()` hook and standalone `trackEvent()` for dual-writing to Vercel + Supabase via `sendBeacon`
Anonymous session IDs via `sessionStorage` + `crypto.randomUUID()` (no cookies)
UTM parameter capture from URL on first load, cached in `sessionStorage`
Automatic `page_view` tracking on every route change via `template.tsx`
`page_exit` event with `duration_seconds` metadata via `visibilitychange` and client-side navigation
Privacy policy: added anonymous usage data and Vercel Speed Insights to "Data We Collect" and "Third-Party Sharing"
Cookie policy: updated overview and third-party sections to reflect session storage and Vercel Speed Insights
Corpus page converted to client component (`'use client'`) for cross-link click tracking
v0.1.1.2
Added
`--type-section` type scale token (`clamp(20px, 2.5vw, 24px)`) bridging the gap between hero (80px) and subheading (18px)
Changed
Product card headers on homepage scroll section now use `--type-section` (24px) instead of `--type-subheading` (18px)
Dispatch description copy: removed "affairs" from audience label
Dispatch description extended for line-wrap parity with Corpus card
`/dispatch` CTA button gap equalised with `/corpus` (both `mt-6`)
OG meta description: lowercase "parliamentarian"
Removed
"Two Products, One Parliament" header from homepage scroll section
v0.1.1.1
Added
Open Graph image generated dynamically via `next/og` (Satori) with ASCII texture background and Caslon Old Face headline, matching Figma design system
Twitter card meta (`summary_large_image`) for rich social sharing previews
Custom favicon: Commons green square with outlined Caslon Old Face "S" letterform (vector paths, no font dependency)
Layout metadata tests covering OG, Twitter card, and metadataBase configuration
Changed
Site title updated to "Simparlia — Parliament, Predicted."
OG description updated to "Predict Parliamentarian voting behaviour"
v0.1.1.0
Added
Dual-product showcase on the homepage: scroll down from the headline to discover Corpus and Dispatch side by side, with a scroll-driven ASCII morph between hero and products images
New `useScrollMorph` hook dispatching progress events for canvas transitions
Vertical edge fade on the scroll morph target for gradual top/bottom detail falloff
New baked hero and products images for homepage ASCII art
Changed
Unified ASCII renderer defaults (opacity 0.45, brightness boost 0.03) so every page looks consistent
Exported `OPACITY_DEFAULT` from the renderer, replacing all hardcoded fallbacks
Scroll morph now recovers gracefully when the target image fails to load
Fixed
Background flicker when navigating between pages caused by mismatched opacity/brightness defaults