What's new
SuperSvenska updates
4.13
Vocabulary Image & Illustration Audits v4.13Update
4 July 2026

Missing Vocabulary Images Resolved — Generated and converted flat vector permit card illustrations for arbetstillstand.webp and uppehallstillstand.webp using DALL-E and Pillow, and duplicated base terms for their definite variations (kassan.webp, beslutet.webp, arendenummer.webp).

Missing Lesson Illustrations Resolved — Mapped directions, map, and briefcase to customized icon/gradient fallbacks, and linked housing to apartment.webp and transport to bus.webp inside IntroStepView.tsx.

4.12
Sign In Form Refactor & Header Navigation v4.12Update
1 July 2026

Theme-Aware Backgrounds — Swapped the hardcoded white login card background with the theme-aware var(--color-bg-card) variable, ensuring the login form correctly renders as dark grey in dark mode.

Login Form Scaling — Increased font sizes, input padding, button dimensions, card border radius, and overall component scaling for a highly accessible and spacious login interface.

Header Sign In Link — Integrated a dedicated 'Sign in' link directly in the sticky navigation header of the home page, making login visible without scrolling to the footer.

4.11
Parallax Grid Alignment Fix v4.11Fix
1 July 2026

Parallax Grid Alignment — Fixed card column baseline misalignments on scroll by translating grid row containers as unified units instead of applying index-based offsets to individual cards.

Hardware Acceleration — Added will-change: transform css rules to scroll containers to optimize rendering and ensure smooth, jitter-free scrolling.

4.10
Staggered Card Parallax & Spotlight Micro-interactions v4.10Update
1 July 2026

Every-Section Scroll Parallax — Infused dynamic scroll translations throughout all landing page sections (Module Grid, Three Principles, and Testimonials), adding organic drifting glow blobs and floating 3D spheres.

Staggered Card Offsets — Programmed page grids to slide vertically at staggered velocity rates depending on their card index (idx * 0.01 offsets). This produces a fluid waves-like layering drift as the user scrolls.

3D Hover Spotlight Grids — Scaled the 3D mouse hover perspective tilt and mouse-tracking color spotlights to the Three Principles cards and the Testimonial cards for unified tactile responses.

Styled Section Headers — Structured section headings with uppercase contextual tag overlays to match high-fidelity agency layout aesthetics.

4.09
Studied Cards Hiding & Interactive List Filter v4.09Update
1 July 2026

Hiding Studied Cards — Modified the Study Mode list to completely hide studied questions by default, preventing scroll fatigue when multiple questions are learned.

Visa/Dölj Toggle — Added an interactive 'Visa/Dölj inlärda frågor' (Show/Hide learned questions) toggle button above the questions list for inline reviews and undos.

Learned Completion Empty State — Created a responsive completion empty state card that congratulates the user when all 20 sample questions are marked as studied and filters are active.

IconEyeOff Support — Added the IconEyeOff component mapping inside Icon.tsx to handle the toggled eye hide states.

4.08
Collapsible Studied Cards & Persistent Knowledge Book v4.08Update
1 July 2026

Collapsible Studied Questions — Redesigned the question list inside the citizenship test study tab. When a question is marked as studied (Kan det ✓), the details collapse into a compact Knowledge Book entry that summarizes the core fact (correct answer and explanation) to save screen space.

Persistent Knowledge Book Sidebar — Embedded a reusable, collapsible Knowledge Book drawer panel inside the left sidebar layout. The panel is visible across all test preparation pages, providing instant, scrollable access to all studied facts.

Syntax Verification — Cleared syntax anomalies at the bottom of the main app landing page, ensuring type-safe and compilation-friendly production bundles.

4.07
Outcrowd Scroll Parallax & Heading Fading v4.07Update
30 June 2026

Scroll Parallax Event Listeners — Implemented real-time scroll tracking using passive window event listeners to translate page depths dynamically on scroll.

Cinematic Text Fading — Integrated scroll-responsive scaling and opacity fades on the primary marketing headline banner. Scrolling down smoothly lifts the title and fades it away.

Multi-Velocity Vector Drift — Mapped background 3D glass spheres and metallic gold rings to travel at fast velocity rates relative to scroll depth, producing clear camera-lens depth planes.

Card & Dock Scroll Sliders — Added slower vertical scroll translations to the action cards and glassmorphic stats dock to create fluid multi-layer foreground offsets.

4.06
Outcrowd 3D Spheres & Card Perspective Tilt v4.06Update
29 June 2026

Photorealistic 3D Vector Spheres — Rendered custom 3D glass spheres and gold rings using pure SVG gradients. These assets float dynamically behind the cards at offset parallax depth layers.

3D Card Perspective Tilt — Programmed cards to rotate and skew in 3D perspective space (rotateX, rotateY, translateZ) when hovered, snapping back smoothly on mouse leave.

Modern Sans-Serif Typography — Upgraded card title displays from classic serif to an ultra-bold, high-contrast sans-serif font layout for a sleek, contemporary digital agency aesthetic.

4.05
Advanced Parallax & Spotlight Cards v4.05Update
29 June 2026

Real-time Cursor Spotlights — Implemented hover tracking spotlight overlays. Cursor movement projects a high-contrast radial spotlight gradient inside each card reflecting cursor focus.

Cinematic Headline Gradients — Styled the hero header calling-to-action text with a vibrant moss-green to sky-blue linear text color gradient.

Double-Layer Parallax Backgrounds — Integrated responsive parallax listener layers controlling large ambient light glows and background wireframe shapes sliding in opposition to the mouse cursor.

Glassmorphic Stats Dock — Refactored the statistics shelf into a unified floating glassmorphism dock featuring borders, shadow filters, and backdrop blurring.

4.04
Compact Hero Layout & 3D Emblem Integration v4.04Update
29 June 2026

Laptop Screen Heights Fitting — Reduced padding, margin, title sizes, and text descriptions throughout the Hero section to ensure the main calls-to-action fit comfortably above the fold on compact laptop monitors (like Lenovo Thinkpads).

Visual Card Headers & Inline Chips — Relocated visual assets (Dala Horse and Swedish emblem) into the card header rows, and replaced vertical checkmark lists with space-saving inline feature pills, saving 150px+ vertical height.

Premium 3D Swedish Emblem — Generated a custom 3D glassmorphic Tre Kronor (Three Crowns) gold emblem WebP image, resolving the Windows system letters 'SE' fallback for the Swedish flag emoji.

4.03
Parallax Split-Hero Homepage v4.03Update
29 June 2026

Above-the-Fold Citizenship Prep — Redesigned the homepage hero as a dual-choice split grid, positioning SFI Everyday Swedish and Citizenship Prep side-by-side. Users can instantly choose their learning route without scrolling.

Interactive Cursor Parallax — Injected an immersive mouse-movement listener that dynamically moves background depth glows, Dala Horse, and official flag badges at varying velocities.

Trilingual Card Translation — Added localized headings, descriptions, and feature lists for all cards in English, Arabic, and Farsi.

4.02
Visual Assets Upgrade v4.02Update
29 June 2026

Level 0 & 1 Core Vocabulary Illustrations — Generated 14 missing core vocabulary card illustrations in our target impressionistic oil-painting style (with thick brushstrokes, centered layouts, and warm Swedish contexts).

Format Conversion & Integration — Programmatically optimized all PNG image generations to WebP format, stashed them in public assets, and configured the TS unit files to display them directly on study and review cards.

4.01
Citizenship Study Mode Evolution v4.01Update
29 June 2026

Interactive Choice-Picking in Study Mode — Turned static 'Reveal Answer' study cards into interactive choice selectors. Tapping any option immediately highlights correct (green) and incorrect (red) choices, exposes explanation guides, and triggers the standard study feedback footer.

Bilingual Swedish/L1 Translation Toggles — Integrated globe icons beside the questions across Study Mode, Free Sample tests (both Exam & Study), and premium Exam Mode. Instantly switches questions, options, and explanations between Swedish and the chosen translation (English, Arabic, or Farsi) to verify vocabulary.

4.00
Citizenship Prep Roadmap Implementation v4.00New
29 June 2026

Category Study Drilldowns — Paid users can now drill down directly into any of the 12 categories (e.g. Historia, Demokrati, Arbetsmarknad) from the landing page, launching a focused study session containing only questions matching that topic.

Question Bookmarking Loop — Added a golden star icon in Study, Quiz, and Sample views. Bookmarked questions are stashed in local storage and accessible directly from a new 'Sparade frågor' (Saved Questions) quick-launch card on the landing page.

Quick-Launch Weak Areas — Added a 'Svaga områden' card directly on the landing page if the user has wrong answers from their last test, making active recall and drilling targeted questions frictionless.

SFI Learning Path Integration — Added an interactive 'Swedish Citizenship Challenge' milestone callout card at the start of Level 3 (Formal & Civic CEFR A2 section) on the main learning path page, guiding language learners directly to the civic test preparation.

3.99
Citizenship Study Mode Default v3.99New
28 June 2026

Low-Pressure Study Default — Swapped the default tab in the sample citizenship test view from 'Exam Mode' to 'Study Mode'. Now, when a new user first clicks the sample test, they land directly on the self-paced, list-based study view showing all questions, answers, and explanations, reducing onboarding friction and exam anxiety.

3.98
Admin Premium Access Fix v3.98New
28 June 2026

Admin Premium Gate Bypass — Resolved an issue where administrators were still prompted with the 'Unlock Premium' paywall modal on Level 2+ unit pages. Updated the subscription verification utilities and status endpoints to automatically grant full premium access to administrators, aligning backend gates with frontend layout indicators.

3.97
UX & Social Expansion v3.97 — Real Weekly Leaderboard & Grammar HubNew
28 June 2026

Weekly Global Leaderboard API & UI — Established a database-driven global weekly leaderboard page at `/leaderboard` backed by a new `/api/leaderboard` MongoDB projection. Shows the top 50 users ranked by XP, displaying user initials, L1 language flags (🇸🇦, 🇮🇷, 🇬🇧), streaks, and highlighted current user rows.

Unified Grammar Reference Hub — Added a dedicated `/grammar` reference page showing core grammar hints (word order, modal verbs, noun genders) with trilingual translations. Accessible via a new card in the Practice hub.

Referral Tracking Loop — Injected an inline script check into the HTML head in `app/layout.tsx` to automatically parse and stash referral codes (`ref=USER_ID`) in localStorage for viral re-engagement loops.

3.96
UX Expansion v3.96 — Progressive Paywall Previews & Citizenship Study ModeUpdate
27 June 2026

Progressive Paywall Previews — Replaced hard paywall locks on the main course path with interactive blurred previews. Tapping locked premium units now opens a beautiful preview modal showing a syllabus outline, durations, and key vocabulary to build interest before the paywall.

Citizenship Test Study Mode — Introduced a toggle on the free sample citizenship quiz allowing users to switch between Exam Mode and a relaxed, self-paced Study Mode. This displays the 20 sample questions with answers and explanations visible immediately, helping users learn key concepts without testing pressure.

3.95
Layout Fix v3.95 — PC Web Profile Page AlignmentFix
27 June 2026

Profile layout fix — Restructured the profile page columns to keep the learning menu, settings menu, and admin actions inside the left column. This prevents a large vertical empty space on desktop/PC web viewports when the sidebar is tall.

3.94
UX Sweep v3.94 — Mobile Streak Modal, Guest Mode, Citizenship TestUpdate
27 June 2026

Streak modal fix — Added max-height (90vh) and vertical scrolling (overflow-y-auto) to the streak badge details modal. This prevents the close (X) button from being pushed off-screen and freezing the UI on mobile web viewports.

Guest mode indicator — Added a subtle, persistent banner at the top of the course path page for unauthenticated users, letting them know progress is local-only and providing a direct 'Sign In' link.

Citizenship test quick check — Split the citizenship test start options to offer a 'Quick Test (10 questions)' mode alongside the 'Full Test (all questions)' and 'Challenge Mode'. Slices and shuffles the questions dynamically to prevent user overwhelm.

Practice page fix — Fixed a trailing closing brace syntax error at the end of the practice page code.

3.93
Offline sync v3.93 — Background Sync API + IndexedDB queueUpdate
8 June 2026

Offline background sync — progress no longer lost when the network drops mid-session. Failed POSTs are queued in IndexedDB; an `online` event listener flushes immediately on reconnect. On Chrome/Edge, the Background Sync API lets the service worker retry even if the tab is closed.

BACKLOG cleanup — stale audio note for numbers 11–100 corrected (15 MP3s were already generated in commit 2997679, not TTS fallback).

3.92
UX sweep v3.92 — numbers 11–100, RTL label fix, PWA manifestUpdate
7 June 2026

Numbers 11–100 sub-lesson (L0-1-4) — Interactive practice for elva through hundra: 14 listen_look steps, 3 pick_sound drills, 2 speak steps, and a pattern tip (tjugoett = tjugo + ett). Fulfils the highest-priority SFI content gap.

RoleplayStepView i18n — 'Roleplay' label was hardcoded English for all users. Now reads strings.roleplay (ar: تمثيل الأدوار · fa: نقش‌آفرینی).

RoleplayStepView design — scene background replaced hardcoded hex gradient (#DCE7D9→#B5CDB0) with bg-[var(--color-moss-soft)], consistent with design system.

PWA manifest — removed invalid maskable SVG icon entry (spec requires raster for maskable); SVG remains as purpose: any.

3.91
Backlog sweep v3.91 — Migrationsverket unit, SF2 level breakdownNew
7 June 2026

L2-9 Migrationsverket — New premium unit (A2): checking application status, giving case number, asking about the decision, renewing a residence permit. 5 sub-lessons: context, phrases, dialogue, roleplay, cultural tips. Critical gap for the target audience.

SF2 — Teacher dashboard now shows per-level progress: click 'Show levels' on any student card to see L0–L6 chips (green = complete, blue = in progress).

3.90
Backlog sweep v3.90 — audio, SF6, pre-cachingUpdate
7 June 2026

Audio — 8 MP3s generated/regenerated via ElevenLabs (Sanna Hartfield): tip-j-as-y, var-forsiktig-bluffannonser, forberedelseklass-nyanlanda-barn (new); skolmaten, patientavgift, grundkonto, sfi-kombineras, ja-akuta-jouren (corrected text from v3.88 audit).

Audio pre-caching (P3) — LessonPlayer now prefetches all vocab + dialogue MP3s for a lesson on mount using fetch(force-cache). Eliminates first-play delay on spotty connections.

SF6 — Join link persistence: stashes join code in localStorage on arrival; PendingJoinRedirect global component redirects authenticated users back to /join/[code] if onboarding lost the callbackUrl.

generate-audio.mjs — script updated: 3 new entries, 5 corrected entries aligned with v3.88 content fixes.

3.89
Backlog sweep v3.89 — curriculum, admin, and i18nUpdate
7 June 2026

L0-1 greetings — Added 'j = y' pronunciation tip (info_card, sky) before the first hej step. Biggest stumbling block for Arabic/Farsi learners.

SF3 — L2-8 (SFI registration) now links to the 'school' phrasebook category from the unit page.

SF5 — Shadowing difficulty filter (Easy / Medium / Hard tabs) confirmed already live — marked done.

Admin leads view — New /admin/leads page with table, l1 breakdown stats, and email search. API at /api/admin/leads.

SF1 — TeacherSection now trilingual (ar/fa/en): Classes, Teaching, Enrolled, Join, Create, Leave, empty state.

3.88
Content audit v3.88 — 11 curriculum fixes across L2 unitsFix
7 June 2026

L2-5 dialogue — Fixed typo: 'halsinfekion' → 'halsinfektion' in doctor dialogue vocab.

L2-4 tip-skolmat — Corrected false claim: free school meals cover lunch only (not breakfast or afternoon snack) in grundskolan.

L2-5 tip-patientavgift — Updated fee range to 200–400 kr (was 200–350); replaced outdated 'landstinget' with 'regionen'; added 'Barn under 18 år betalar ingenting.'

L2-1 cultural — Added rental scam warning (6th card, berry): fake listings on Blocket/Facebook, foreign account payment requests.

L2-4 cultural — Added förberedelseklass card (new 6th card, sky): preparatory Swedish class for newly arrived children.

L2-7 dialogue — Removed placeholder phone number '020-123 456'; replaced with instruction to check landlord's website.

L2-3 phrases — Added note for Persian speakers to substitute 'persiska' for 'arabiska' in the language phrase.

L2-8 tip-kombination — Fixed incorrect program name 'SFI med lärlingsspår' → 'SFI-kombination'.

L2-2 tip-gratis-konto — Hedged 'vanligtvis gratis' to 'ofta gratis eller billiga' with advice to compare fees.

3.87
Animation audit v3.87 — 14 motion fixes across lesson, review, paywall, nav, certificateUpdate
7 June 2026

globals.css — 2 new keyframes (slideUp, flipReveal) + 3 new utility classes (animate-slide-up, animate-flip-in, animate-fade-in-fill with animation-fill-mode: both for delay-safe staggered entrances).

AudioButton.tsx — Added duration-150 and active:scale-95 to base class so the scale press feels responsive rather than snapping.

app/shadowing/page.tsx — Blur reveal now fades opacity 0→1 alongside blur removal (transition-all duration-500 ease-out) for a smooth reveal even in browsers where filter doesn't transition.

BottomNav.tsx — Active indicator pill always rendered; uses opacity/scale-x transition (duration-200) so it fades in/out rather than appearing instantly.

app/review/page.tsx — Translation reveal uses animate-flip-in (translateY + scale entrance) instead of animate-fade-in for a card-reveal feel.

PaywallBanner.tsx — Sheet entrance now slides up from bottom (animate-slide-up) instead of appearing instantly.

LessonPlayer.tsx — Right-panel step container gains animate-fade-in on key={stepIdx} remount, so each step transition cross-fades.

app/citizenship-test/page.tsx — Explanation feedback box gets animate-scale-in so the correct-answer reveal feels earned.

UnitCertificateModal.tsx — Stats row items stagger-fade in (150ms, 230ms, 310ms delay) and vocab chips stagger in from 380ms in 30ms increments using animate-fade-in-fill.

app/path/page.tsx — All 5 contextual nudge cards (l1complete, milestone, streak, review, continue, signin) get animate-fade-in so they pop in when conditions become true.

PickSoundStepView.tsx — Correct answer button gets animate-pop (scale bounce) matching the shake already on wrong answers.

PronunciationResult.tsx — Result banner uses animate-scale-in; word diff badges stagger in 60ms per word using animate-fade-in-fill.

3.84
Conversion — landing page testimonial, 7-day free trial, SFI framing confirmedNew
6 June 2026

app/page.tsx — Fatima's testimonial ('I booked my dentist appointment without a translator — for the first time ever.') added above the principles section. First-time visitors now see social proof before they read the product description.

app/api/checkout/route.ts — Monthly plan gets trial_period_days: 7. Yearly plan unchanged. isPremiumStatus already handles 'trialing' — no subscription.ts change needed.

components/PaywallBanner.tsx — Monthly CTA changes to 'Start 7-day free trial'; trial note 'X days free · then 29 SEK/month · cancel any time' shown below the button. S.ctaTrial and S.trialNote added (en/ar/fa).

3.83
Retention — lifecycle email, Day-2 bridge, mid-journey L1 upsellNew
6 June 2026

app/api/cron/lapse-email/route.ts — New daily cron (18:00 UTC). Two behaviors in one run: (1) Day-2 bridge — users with last_active_date === yesterday and streak≥1 get a 'come back today' email (skipped Mondays to avoid double-emailing with the weekly cron); (2) 7-day lapse — users with last_active_date 7–9 days ago get a re-engagement email. Both are trilingual (en/ar/fa), RTL-aware, with GDPR unsubscribe link. Tracks cron_result event for admin CronHealthCard.

vercel.json — /api/cron/lapse-email added at schedule '0 18 * * *' (18:00 UTC daily).

app/path/page.tsx — L1 upsell card now triggers at 5+ completed L1 units (was: all 9). Title is adaptive: 'Ready for Level 2?' for mid-journey, 'Level 1 complete!' when all 9 are done. Users who reach the halfway point and are clearly committed now see the upgrade nudge instead of having to exhaust all free content first.

3.82
Activation funnel — 3 onboarding friction fixesFix
6 June 2026

app/path/page.tsx — L1 now unlocks after the first L0 sub-lesson (greetings vocab) instead of requiring all 3. Users who stopped after numbers were seeing a fully locked path page with no explanation — a silent exit point.

app/onboarding/page.tsx — Commitment screen hints rewritten from time-to-completion ('Level 1 in ~5 weeks') to daily-benefit framing ('One new phrase every day', 'Real conversations in weeks', 'Fastest path to fluency'). The old framing filtered out motivated users who needed Swedish urgently.

app/onboarding/page.tsx — Email screen heading changed from 'Keep your streak alive' (incoherent — no streak exists yet) to 'Get weekly Swedish challenges'. Sub copy and CTA rewritten to match the actual moment: pre-streak, post-commitment.

3.81
PM audit fixes — paywall copy, activation funnel, onboarding_started eventFix
5 June 2026

components/PaywallBanner.tsx — S.sub and S.features[0] corrected: '25 scenarios across Levels 2–5' → '31 scenarios across Levels 2–6' (en/ar/fa). The /upgrade page was already correct; the banner — the highest-intent conversion surface — was the only surface still showing stale numbers.

lib/analytics.ts — onboardingStarted(l1) event added (Plausible + MongoDB). Fires on language selection (first deliberate user action) in app/onboarding/page.tsx.

app/api/analytics/route.ts — onboarding_started count added to funnel; D2 multi-day return aggregation added (session_ids with lesson_started on 2+ distinct dates in last 30d). Both included in funnel response.

components/AnalyticsDashboard.tsx — FunnelCard rebuilt as 6-step activation + conversion funnel (started onboarding → completed onboarding → started lesson → completed lesson → hit paywall → converted). Three rate KPIs added: onboarding drop %, paywall→paid %, and D2 return % with absolute counts.

3.80
A5–A9: Admin audit — indexes, cron health, email fallback, webhook testsFix
5 June 2026

lib/db-init.ts — A5: compound index {event:1, ts:-1} on events collection; A6: index on progress.data.last_active_date. Eliminates full-collection scans on every /admin load and every weekly-email cron run.

app/api/cron/daily-push + weekly-email — A7: each cron writes a cron_result event doc on completion ({cron, sent, gone/errors, ts}). Admin dashboard CronHealthCard reads most-recent run per cron and shows last-fired time, sent count, and errors — red dot + 'Never run — check CRON_SECRET' when absent.

app/api/webhooks/stripe/route.ts — A8: invoice.payment_failed now falls back to a transactional Resend email when the user has no push subscription or it is gone (expired). Payment failure is unambiguously transactional — no opt-out required.

lib/stripe-helpers.ts — A9: calculatePeriodEnd() extracted from the webhook handler as a pure, testable function. __tests__/stripe-webhook.test.ts adds 13 tests covering cancel_at, monthly/yearly advance, missing fields, future anchor, and the 36-iteration safety cap. Total test count: 53 → 66.

3.79
A4: Admin dashboard sessions metric correctly labeledFix
5 June 2026

components/AnalyticsDashboard.tsx — KPI card label changed from 'Sessions' to 'Browser sessions'; sub text updated to '30d · not unique users'. The sessions30 value counts distinct session_id values (per localStorage per browser), not unique users — one user on two devices or after clearing storage inflates the count.

app/api/analytics/route.ts — expanded inline comment to document the browser-session limitation.

3.78
A3: Daily push notifications now trilingual (en/ar/fa)Fix
5 June 2026

app/api/cron/daily-push/route.ts — getPushMessages(l1, streak, due) helper added. Returns localised title/body/url for all 3 push variants (streak-at-risk + due, streak-at-risk only, review-only) in English, Arabic, and Farsi. l1 read from progress.l1_choice ?? 'en'. Arabic/Farsi users now receive streak warnings and review reminders in their own language.

3.77
A2 audit: Stripe webhook event gap documentedFix
3 June 2026

CLAUDE.md — new 'Stripe webhook events' subsection added to Development workflow. Lists all 6 events the handler processes, their confirmed status, and flags invoice.upcoming as never confirmed in the Stripe Dashboard (required since v3.1, but the v3.15 confirmation only covered invoice.paid and invoice.payment_failed). No code change — handler is correct. Action required: verify invoice.upcoming is subscribed in Stripe Dashboard → Developers → Webhooks → [endpoint].

3.76
GDPR A1: weekly email now has a one-click unsubscribe linkFix
2 June 2026

types/lesson.ts — email_unsub?: boolean added to UserProgress.

lib/sanitize-progress.ts — email_unsub sanitized (only true passes; anything else becomes undefined).

app/api/unsub/route.ts — new GET endpoint. Verifies HMAC-SHA256 token (base64url(email).hex(32)), sets data.email_unsub = true in MongoDB, returns a branded HTML confirmation page.

app/api/cron/weekly-email/route.ts — makeUnsubToken() helper added; MongoDB query now filters data.email_unsub: { $ne: true }; email footer includes signed Unsubscribe link alongside Settings link.

3.75
Premium P7: Notifications row shows real browser permission statusFix
2 June 2026

app/profile/page.tsx — Notifications settings row no longer says 'Soon'. useEffect reads Notification.permission synchronously on mount and sets notifStatus: 'enabled' (green), 'blocked' (berry), 'off' (faint), or 'unsupported' (faint). notificationsSoon string replaced with notificationsEnabled / notificationsOff / notificationsBlocked / notificationsNA, all trilingual.

3.74
Premium P6: level-completion milestone cards on /path for L2–L5New
2 June 2026

app/path/page.tsx — LEVEL_MILESTONE constant added with title/sub strings for L2–L5 completions in all 3 languages. levelMilestone useMemo finds the highest complete premium level whose next level hasn't been started. Milestone card (moss, trophy icon, dismiss ×, Link to first next-level unit) inserts into the 2-card contextual nudge system between l1complete and streak. Dismissed state stored in ss_levelcomplete_dismissed localStorage key (comma-separated level numbers). L2 → 'Formal & civic Swedish is next' → /unit/L3-1; L3 → 'Everyday life…'; L4 → 'Culture & society…'; L5 → 'Fluency & independence…'.

3.73
Premium P5: listening nudge shown for all L2+ unit completionsFix
2 June 2026

components/UnitCertificateModal.tsx — listening nudge condition widened from relatedEpisode != null to relatedEpisode != null || unit.level >= 2. Units mapped in UNIT_EPISODE_MAP still show the specific episode title; all other L2–L6 units (27 previously showing nothing) now show a generic 'Browse all listening episodes' subtitle pointing to /listening. listenBrowse string added to S in all 3 languages.

3.72
Premium P3: progress page subtitle correct for A2–B2 learnersFix
2 June 2026

app/progress/page.tsx — subtitle and progress bar label now branch on isPremium. Premium users see 'Your Swedish journey (A1–B2).' and 'Course progress'; free users see the original 'Your A1 Swedish journey.' and 'A1 course complete'. Trilingual across en/ar/fa.

3.71
Premium P2: streak shields count visible in profileFix
2 June 2026

app/profile/page.tsx — streak motivation card (shown when streak >= 2) now shows a shield count line for premium subscribers: IconShieldCheck icon + 'N shields remaining' in amber (or 'No shields left' in faint when 0). Previously, streak shields were promised on the upgrade success page but had no visible representation anywhere in the app.

3.70
Premium subscriber audit P1 & P4: success page and profile chip updated to 31 scenarios / Levels 2–6Fix
2 June 2026

app/upgrade/success/page.tsx — S.sub updated to 'Levels 2 through 6 are now unlocked.' S.confirms[0] updated to '31 scenarios across Levels 2–6 unlocked' (was stale at L2–5 / 25 scenarios since L6 was added in v3.40).

app/profile/page.tsx — scenariosChip updated to '31 scenarios' (was '25 scenarios'). Shown as the right-label on the Upgrade to Premium row for free authenticated users.

3.69
Guest persona audit G3–G8: onboarding, landing, path copy fixesUpdate
2 June 2026

G3 — app/page.tsx: 'Browse course' CTA now routes to /onboarding for first-time visitors (same condition as primary CTA), so Arabic/Farsi speakers aren't dropped into an English /path.

G4 — app/onboarding/page.tsx: PreviewScreen now shows L0 'Survival Kit' as a pinned 'Start here' card above a 'Then — Level 1' divider and the 9 L1 units. Heading updated to 'What's coming — after your first 7 words'. Removes the bait-and-switch of showing L1 scenarios when the first lesson is actually L0.

G5 — app/path/page.tsx: l1CompleteSub updated from 'Unlock 8 Level 2 scenarios' to 'Unlock 31 premium scenarios across Levels 2–6'. Consistent with PaywallBanner and upgrade page.

G6 — app/page.tsx: principles section now appears before citizenship test section. Trust-building (how the app works) precedes feature promotion.

G7 — app/onboarding/page.tsx: XP badge removed from CommitmentScreen options. XP is unexplained at this point; the completion-time hint ('Level 1 in ~2 weeks') carries the weight. xpLabel field and sub copy cleaned up.

G8 — app/path/page.tsx: saveSub rewritten from 'Closing your browser means losing your progress' (threat) to 'Your progress is saved in this browser — sign in free to sync across devices' (reassurance).

3.68
Guest G2: paywall sign-in is a real button; guestNote rewrittenFix
2 June 2026

components/PaywallBanner.tsx — unauthenticated CTA promoted from a plain text link to a full-width moss button (matches the authenticated upgrade button in size and weight). guestNote rewritten from "Your subscription will be securely linked to the email you use at checkout" (implied guest checkout is possible) to "You'll need a free account before upgrading — no credit card needed until then" (accurate).

3.67
Guest G1: PreviewScreen gets dir attribute for RTL layoutFix
2 June 2026

app/onboarding/page.tsx — dir={isRtl ? "rtl" : "ltr"} added to PreviewScreen wrapper. CommitmentScreen and EmailScreen already had this; PreviewScreen was the only onboarding screen missing it. Arabic/Farsi users on screen 2 now see unit titles and descriptions with correct RTL text alignment.

3.66
Farsi L2: numbers 1–10 show Eastern Arabic numerals (یک · ۱)Update
2 June 2026

content/units/L0-1-survival-kit.ts — fa fields for all 10 number entries updated from bare word ("یک") to word · Eastern numeral ("یک · ۱"), mirroring the en pattern ("one · 1"). Farsi-script numerals are more native for Iranian users and the · ۹ on nio also visually disambiguates it from nej's «نه».

3.65
Farsi L1 fix: SeeWordStepView outer container gets dir attributeFix
2 June 2026

components/lesson-steps/SeeWordStepView.tsx — dir={isRtl(l1) ? "rtl" : "ltr"} added to the outer container div. The Swedish letter grid already had dir="ltr"; without this fix, RTL instruction text and the optional tip box could inherit conflicting direction from the page root. Letter grid is unaffected (its own dir="ltr" takes precedence).

3.64
Farsi M4 fix: masteryMsg unified to single sv() patternFix
2 June 2026

app/citizenship-test/page.tsx — masteryMsg rewritten as l1==="en" ? English : sv(key, l1) across all three branches. Eliminates two-style maintenance: Arabic and Farsi now both read from UI_I18N. Also fixes a latent Arabic bug where the count branch used sv("masteryLabel") (the zero-state key) instead of sv("masteryProgress") (the per-fact label).

3.63
Farsi M3 fix: ja vocab card shows «بله» with Dari variant noteFix
2 June 2026

L0-1-survival-kit.ts — fa: "بله / بلی" changed to fa: "بله". The slash notation rendered as a typographic artifact on the vocab card. Afghan Dari variant «بلی» now surfaced via a note field (same amber info box pattern used for nio/nej disambiguation in v3.55).

3.62
Farsi M2 fix: label_staff uses neutral register for all lesson contextsFix
2 June 2026

lib/i18n.ts — Farsi label_staff changed from «کارمند» (office worker) to «طرف مقابل» (the other party). «کارمند» sounds wrong above a pharmacist or nurse bubble; «طرف مقابل» is register-neutral and fits shop workers, doctors, drivers, and officials equally.

3.61
Farsi M1 fix: tack dialogue translation matches vocab cardFix
2 June 2026

L0-1-survival-kit.ts — dialogue line fa: "ممنون!" → "ممنونم!" (adds first-person suffix). Vocab card was corrected to «ممنونم» in v3.19; dialogue was missed. Learners no longer see two different Farsi translations for «Tack!» within the same unit.

3.60
Farsi H5 fix: L0-1 conversation intro clarifies Ahmed is an example nameFix
2 June 2026

L0-1-survival-kit.ts conversation sub-lesson intro description — added parenthetical in all 4 languages (sv/en/ar/fa): 'Ahmed is an example name — say your own name.' Afghan/Dari speakers no longer see an identity marker that signals the app was built only for Arabic speakers.

3.59
Farsi H3 fix: strip Arabic vowel diacritics from all fa_phonetic fieldsFix
2 June 2026

41 unit files (L0–L6) — 772 fa_phonetic lines stripped of Arabic diacritics (fatha/damma/kasra/shadda/sukun/tanwin). e.g. «فُرلوت» → «فورلوت», «اورسِکتا» → «اورسکتا».

ar_phonetic fields untouched — diacritics are standard pedagogical practice in Arabic but alien to everyday Farsi/Dari readers.

3.58
Farsi H2 fix: label_you register corrected from formal شما to informal توFix
2 June 2026

lib/i18n.ts — Farsi label_you changed from «شما» (formal/plural) to «تو» (informal/direct). Matches SESSION_S.speakerYou which already used «تو». Learners are now taught the correct everyday register for spoken Swedish contexts.

3.57
Farsi H1 fix: info_card explaining förlåt ≠ ursäkta for Persian speakersFix
1 June 2026

content/units/L0-1-survival-kit.ts — new vocab entry 'tip-forlat-ursakta' (index 7) added to greetings vocab array. Trilingual (en/ar/fa).

content/units/L0-1-survival-kit.ts — info_card step (vocab_id: 7, accent: amber) inserted between the förlåt and ursäkta listen_look steps. Farsi users see: «در فارسی هر دو ببخشید می‌شوند — اما سوئدی‌ها فرق قائل‌اند». Arabic and English versions also provided.

3.56
Farsi C2 fix: SeeWordStepView 3 hardcoded English strings translatedFix
1 June 2026

lib/i18n.ts — 3 new UiStrings keys added (all 3 languages): read_word_label, letters_count (n: number) => string, play_letter_by_letter.

components/lesson-steps/SeeWordStepView.tsx — 'Read the word' chip label, '{n} letters ·' count, and 'Play letter by letter' button now use strings.* — Farsi and Arabic users no longer see English in this step.

3.55
Farsi C1 fix: nio/nej homograph warning + reusable note field on vocabFix
1 June 2026

types/lesson.ts — VocabWord gains optional note?: Partial<Record<L1Lang, string>> field for language-specific clarifications on ambiguous translations.

content/units/L0-1-survival-kit.ts — nio (9) entry gets a Farsi note explaining that «نه» is spelled identically to nej (no) but pronounced differently — prevents learners from confusing them in pick_sound exercises.

components/lesson-steps/ListenLookStepView.tsx — renders word.note[l1] as an amber info box below the vocab card when present; zero impact on words without a note.

3.54
Farsi learner persona audit — content bugs and i18n gaps identifiedFix
1 June 2026

Farsi persona review conducted: identified 2 critical content bugs, 5 high-impact UX gaps, 4 medium quality issues across L0-1 survival kit, SeeWordStepView, lib/i18n.ts, and onboarding.

Critical: nio (9) and nej (no) both map to «نه» in Farsi — pick_sound exercises actively mislead learners. Needs a note field on the nio vocab entry.

Critical: SeeWordStepView has 3 hardcoded English strings ('Read the word' label, 'N letters', 'Play letter by letter') visible to all Farsi users in every lesson.

High: label_you is «شما» (formal/plural) in lib/i18n.ts but «تو» (informal) in SESSION_S — wrong register mismatch; lesson player teaches wrong formality level.

High: fa_phonetic fields use Arabic vowel diacritics (damma ُ, kasra ِ) that Persian readers don't use or recognize in everyday writing — should be stripped.

3.53
Practice sessions: full Arabic/Farsi i18n + RTL layout + missing iconsFix
1 June 2026

components/Icon.tsx — 3 missing icons registered (were silently rendering as IconCircleDot): IconStar (DoneScreen stars), IconRepeat (Review card), IconUser (shadowing/roleplay speaker avatar).

app/practice/page.tsx — All 4 session modes (Drills, Shadowing, Review, Roleplay) now fully trilingual. Added SESSION_S strings object with 30+ keys in en/ar/fa. DoneScreen titles, stat labels, and all button/hint text translated.

app/practice/page.tsx — SessionShell root div gets dir=rtl for Arabic/Farsi — entire session UI flips correctly. Arrow icons get arrow-flip class for RTL navigation.

app/practice/page.tsx — Empty-state heading on Practice hub changed from 'Coming soon' to 'Start a lesson to unlock' (trilingual) — previous text read as a broken/incomplete page.

app/path/page.tsx — 'X of Y scenarios complete' completion counter now translates into Arabic and Farsi (was hardcoded English shown to all users).

3.52
Fix profile page stuck loading in local devFix
31 May 2026

app/profile/page.tsx — Removed status === 'loading' early return. Page now renders immediately with unauthenticated state while NextAuth resolves, instead of blocking indefinitely. Fixes blank spinner in local dev when NEXTAUTH_SECRET / NEXTAUTH_URL / MONGODB_URI are absent from .env.local.

3.51
UI/UX polish — onboarding, path, profile (UI UX Pro Max review)Update
31 May 2026

app/onboarding/page.tsx — Back button added on steps 2–5 (RTL-aware: IconArrowRight for Arabic/Farsi). Screen-to-screen fade transition via key={screen} + animate-fade-in. Progress bar h-1 → h-1.5.

app/onboarding/page.tsx — cursor-pointer added to all 6 button elements (was missing everywhere). text-left → text-start on ReadyScreen unit card for RTL correctness.

app/path/page.tsx — 'Complete' badge now trilingual (was hardcoded English). cursor-pointer on locked-unit button. Nudge card spacing mb-1 → mb-2. Sign-in dismiss button p-1 → p-2 (44px touch target). End-of-list IconFlame → IconMap.

app/profile/page.tsx — StatCards get shadow-sm for elevation against page background. MenuRow onClick buttons get cursor-pointer. 🇸🇪 emoji replaced with IconId (no-emoji-icons rule). Citizenship card moved before InviteCard. Streak motivation hidden until streak ≥ 2.

components/Icon.tsx — 5 missing icons registered (were silently rendering as IconCircleDot): IconCoffee, IconUserPlus, IconCreditCard, IconShieldCheck, IconClockHour4.

3.50
UI UX Pro Max design intelligence skill installedUpdate
31 May 2026

Installed UI UX Pro Max Claude Code skill (uipro-cli v2.2.3). Adds 67 UI styles, 96 color palettes, 57 font pairings, and 99 UX guidelines to Claude's design reasoning.

Skill auto-activates on UI/UX requests (design, review, improve). Searchable via python3 .claude/skills/ui-ux-pro-max/scripts/search.py.

3.49
Arabic learner UX fixes — onboarding & bottom navFix
31 May 2026

app/onboarding/page.tsx — Language picker footer note now trilingual (en/ar/fa); was English-only even after user selected Arabic.

app/onboarding/page.tsx — Arabic button labels ('العربية', 'الشرح بالعربية') now have explicit var(--font-arabic); previously could render in Latin fallback font.

app/onboarding/page.tsx — Language selector buttons: text-left → text-start (Tailwind logical property) so Arabic/Farsi text aligns to the correct edge in RTL.

app/onboarding/page.tsx — Email submission now catches network errors and shows a trilingual berry-red message; previously silent failure always advanced to 'done' state.

components/BottomNav.tsx — Nav labels bumped from text-[10px] to text-[11px]; Arabic x-height is shorter than Latin, making 10px labels nearly unreadable on mid-range Android screens.

3.48
4 new vocab illustrations (L3-3 & L3-4)Update
30 May 2026

public/images/vocab/ — deklaration.webp (L3-3 tax return), skatteaterb.webp (L3-3 skatteåterbäring), receptfornyelse.webp (L3-4 prescription renewal), 1177.webp (L3-4 healthcare helpline) added and wired via image field in their respective vocab entries.

3.47
Fix broken CI lint step (Next.js 16 removed next lint)Fix
30 May 2026

.github/workflows/ci.yml — removed the npm run lint step; next lint no longer exists as a CLI subcommand in Next.js 16 (was treating 'lint' as a directory argument and failing).

package.json — lint script replaced with an informational echo until ESLint is set up separately; typecheck + build steps in CI are unaffected.

3.46
ElevenLabs audio for all 148 phrasebook phrasesUpdate
30 May 2026

app/phrasebook/page.tsx — PlayButton now uses /audio/sv/phrasebook/<id>.mp3 (ElevenLabs) instead of browser TTS fallback. TTS remains as safety net if file fails to load.

public/audio/sv/phrasebook/ — 148 MP3s generated via ElevenLabs (Sanna Hartfield Swedish Conversational voice). Covers all 12 categories: pharmacy, doctor, housing, school, supermarket, bank, transport, job interview, numbers, days & months, family, colors.

scripts/generate-phrasebook-audio.mjs — new script for future phrasebook audio regeneration.

3.45
4 new vocab illustrations (L3-1 & L3-2)Update
30 May 2026

public/images/vocab/ — bifoga.webp (L3-1 formal email), myndighet.webp (L3-1 formal email), beslut.webp (L3-2 official letters), sista-datum.webp (L3-2 official letters) added and wired via image field in their respective vocab entries.

3.44
i18n pattern migration + 3 new vocab illustrationsUpdate
30 May 2026

20 components and pages migrated from inline ternary strings (l1 === 'ar' ? ... : l1 === 'fa' ? ... : ...) to getL1Text() object pattern — groundwork for adding Somali and future languages without touching every file twice.

public/images/vocab/ — studievagledare.webp (L2-8 SFI), fungerar-inte.webp (L2-7 felanmälan), arende.webp (L3-1 formal email) added and wired via image field in their respective vocab entries.

3.43
Citizenship test: 22 new questions + expanded cheat sheet (90 → 112 questions)New
30 May 2026

content/citizenship-questions.ts — 22 new questions added across all 9 categories: Vikingatiden, Swedish emigration to America, Miljonprogrammet (historia); the 4 fundamental laws, NATO membership (2024), KU (demokrati); personnummer, folkbokföring, gymnasieskolan (samhälle); Astrid Lindgren, kräftskivan, IKEA (kultur); midnattssolen, Skandinaviska halvön (geografi); presstödet, källkritik (medier); asylrätt, demonstrationsrätt (rättigheter); den svenska modellen, semesterlagen (arbetsmarknad); religious demographics, SST (religion).

content/citizenship-questions.ts — CHEAT_SHEET expanded with 2–3 new facts per category (historia +3, demokrati +3, samhälle +3, kultur +3, geografi +2, medier +2, rättigheter +2, arbetsmarknad +2, religion +2). Total cheat sheet facts grew from 48 to 70.

content/citizenship-i18n.ts — Full Arabic and Farsi translations for all 22 new questions (QUESTION_I18N) and all new cheat sheet facts (CHEAT_I18N).

Total question count: 90 → 112. Each category now has 12–13 questions for more varied daily challenge draws.

3.42
Audio: generate MP3s for L5-3 through L6-3 (133 new files)New
30 May 2026

scripts/generate-audio.mjs — 137 new phrase entries added for all 6 new units: L5-3 (arbetskultur), L5-4 (bostadsköen), L5-5 (medborgarskap), L6-1 (jobbintervjun), L6-2 (lokala val), L6-3 (utvecklingssamtal).

public/audio/sv/ — 133 new MP3s generated (ElevenLabs, Sanna Hartfield - Swedish Conversational voice). 4 files skipped as already present (betyg.mp3 etc.).

Coverage: all vocab words, phrase sentences, dialogue lines, and cultural tip narrations for each unit. Enables audio playback and TTS fallback across all B1–B2 content.

3.41
Fix: feedback widget floating on all pages + send errorFix
30 May 2026

components/FeedbackWidget.tsx: usePathname() added — widget now returns null on /lesson/*, /onboarding/*, /admin*, /auth/*, /join/*, /teacher/*, /login. Previously the floating button appeared over every page including lesson cards and the onboarding flow.

app/api/feedback/route.ts: insertOne wrapped in try-catch. A MongoDB write failure now returns JSON 503 instead of crashing the handler → HTML 500 → parse error in the widget → 'Something went wrong' shown incorrectly.

3.40
Curriculum: L5 expanded (3 units) + L6 added (3 units B2) + feedback bug fixNew
30 May 2026

content/units/L5-3-arbetskultur.ts — Swedish Work Culture (B1): fika, lagom, flat hierarchy, flex hours. 5 sub-lessons, full roleplay.

content/units/L5-4-bostadskon.ts — The Housing Queue (B1): bostadskö, hyresrätt vs bostadsrätt, accepting an apartment offer, deposit rules, notice period.

content/units/L5-5-medborgarskap.ts — Citizenship (B1): requirements, dual citizenship, no language test, Migrationsverket process, ceremony.

content/units/L6-1-jobbintervjun.ts — Job Interview (B2): CV, personligt brev, interview questions, salary negotiation, thank-you email.

content/units/L6-2-lokala-val.ts — Local Elections (B2): who can vote (EU citizens + PUT), kommunfullmäktige vs riksdag, ballot procedure, Valkompassen.

content/units/L6-3-utvecklingssamtal.ts — Parent-Teacher Meeting (B2): school system, frånvaroanmälan, betyg from year 6, school breaks, kurator.

types/lesson.ts — cefr union extended to include 'B2'. app/path/page.tsx — L6 level divider 'Fluency & independence' added between L5 and L6 units.

app/upgrade/page.tsx — premium feature count updated from 25 to 31 scenarios, Levels 2–6, A2–B2.

lib/db.ts — getDb() now returns null on connection errors (wraps connect() in try-catch, resets client). Previously an M0 connection failure caused an unhandled throw → HTML 500 → FeedbackWidget showed 'Something went wrong'.

3.39
Finance: MRR dashboard, upgrade CTA, tip jar gating, yearly cross-sellUpdate
30 May 2026

components/AnalyticsDashboard.tsx + app/api/analytics/route.ts: SubscriberCard now shows estimated MRR (monthly × 29 SEK + yearly × 199/12) and churned-this-month count alongside active subscriber count.

app/profile/page.tsx: free authenticated users now see an 'Upgrade to Premium' row in Settings (was no upgrade CTA on the profile page). Monthly subscribers see a 'Switch to yearly — Save 43%' row below their subscription row.

app/profile/page.tsx: SupportCard (Ko-fi / tip jar) moved from free users to premium-only. Was competing with subscription conversion for the most cost-sensitive segment.

3.38
Marketing: testimonial, outcome headline, email benefit copyUpdate
30 May 2026

components/PaywallBanner.tsx: testimonial block added (moss-tinted, above plan toggle) — realistic quote from 'Fatima, Gothenburg', trilingual en/ar/fa.

app/upgrade/page.tsx: headline changed from 'SuperSvenska Premium' to 'From struggling to speaking' — outcome-oriented. 'No credit card needed' note added under the Free column header.

app/onboarding/page.tsx: preview screen now shows unit descriptions instead of unit codes (L1-1 etc.) — communicates value, not developer labels. Email screen heading changed to 'Keep your streak alive' with benefit-framing sub ('weekly Swedish challenge + magic link, no password').

3.37
Sponsor slot: date range, second placement, admin statsUpdate
30 May 2026

content/sponsor.ts: added optional startDate/endDate fields (YYYY-MM-DD). Banner renders only within the campaign window — enables time-limited sponsorships without a deploy.

components/SponsorBanner.tsx: date range gate applied before render. lib/analytics.ts: sponsorImpression and sponsorClicked now also write to MongoDB (were Plausible-only).

app/practice/page.tsx: second SponsorBanner placement added between Drills & Speaking cards and the Currently drilling info block.

components/AnalyticsDashboard.tsx + app/api/analytics/route.ts: SponsorStatsCard added — shows 30-day impressions, clicks, and CTR. When no sponsor is active, shows an inline prompt to activate.

3.36
Admin: Facebook post widget with image supportNew
30 May 2026

app/api/admin/facebook-post/route.ts: new admin-gated POST endpoint. Text-only posts go to /{page}/feed; photo posts (when imageUrl provided) go to /{page}/photos with caption — Facebook fetches the image from the URL.

components/FacebookPostWidget.tsx: admin panel widget. Auto-selects today's post type from the day-of-week rotation (Phrase of the Day Mon/Wed/Fri, Did you know Tue, Feature highlight Thu, Testimonial Sat/Sun). Pre-fills trilingual EN/AR/FA template. Optional image URL field with live preview and validity check. Publish button posts directly to the SuperSvenska Facebook page via Graph API.

app/admin/page.tsx: FacebookPostWidget rendered between Quick links and Analytics sections.

3.35
Billing: past_due portal access + upgrade page copy refreshFix
30 May 2026

profile/page.tsx: past_due subscribers can now reach the billing portal. Previously isPremium gated the portal row — payment failures hid the portal from users who needed it most.

profile/page.tsx: subscription fetch now runs for all authenticated users, not just premium. past_due row uses berry icon + 'Update payment' label.

upgrade/page.tsx: free feature count corrected to 10 scenarios (L0 + Level 1). Premium column updated to '25 scenarios across Levels 2–5 (A2–B1)' with streak shields added.

3.34
PWA: offline fallback, global install prompt, start_url fixFix
29 May 2026

manifest.ts: start_url changed from '/' to '/path' — the old value triggered a redirect that fails offline, showing a browser error on first offline open.

public/offline.html: branded trilingual (en/ar/fa) offline page shown instead of Chrome/Safari's generic error when navigation fails without network.

sw.js: precaches offline.html on install so it's available from the very first offline visit; navigation fetch handler now serves it as fallback.

InstallPrompt: moved from app/path to app/layout — now appears on any hub page after first lesson, not just the Learn tab. Fixed-positioned above the bottom nav.

InstallPrompt: iOS share instructions now include the ⬆ icon so users can identify the Share button without guessing.

3.33
Security: 5 fixes — class-join IP spoofing, email cooldown, input validation, push rate limitFix
29 May 2026

class/join: inline XFF extraction replaced with clientIp() — rate limit was bypassable via spoofed X-Forwarded-For header.

onboarding-email: 24h per-email cooldown added — prevents IP-rotating attackers from spamming a victim address.

admin/feedback: type filter now whitelisted against valid types (bug/feature/content/other) — any string was previously accepted.

admin/feedback + admin/subscribers: parseInt() NaN guard added — invalid query params no longer produce undefined skip/limit.

push/subscribe: rate limiting added (10/hr per email) — endpoint was auth-gated but otherwise unbounded.

3.32
DevOps: cron N+1 fix, Sentry noise filter, CI lint, backup checksumFix
29 May 2026

daily-push cron: progress docs now batch-loaded in one query instead of N+1 findOne calls — O(2) regardless of subscriber count.

maxDuration = 60 added to both cron routes — prevents silent timeout on Vercel Hobby (10s default).

Sentry beforeSend filter: drops AbortError and network noise so the 5k/month free-tier quota isn't exhausted by benign browser events.

CI: npm run lint step added — catches ESLint issues not caught by TypeScript.

Backup: SHA-256 checksum verification added for mongodump binary download.

Env var table: CRON_SECRET, VAPID_SUBJECT, SENTRY_AUTH_TOKEN documented — all were silently required but missing from the table.

3.31
Security: 3 architectural fixes — IP spoofing, missing DB index, inline index creationFix
29 May 2026

clientIp() now reads x-real-ip first (Vercel edge-set, unspoofable) instead of X-Forwarded-For[0] — prevents IP rate-limit bypass on waitlist, class-join, and onboarding-email routes.

subscriptions.email unique index added to db-init.ts — isPremiumUser() was doing a full collection scan on every L2+ lesson access.

used_magic_tokens TTL index moved from magic-verify POST handler to server-startup ensureIndexes() — removes unnecessary createIndex round-trip on every login.

3.30
Content: 3 new vocab images wired — centrum, ansökan, identifikationUpdate
29 May 2026

centrum.webp → L1-3 (åka buss) vocab card.

ansokan.webp → L1-5 (Skatteverket) and L2-3 (Arbetsförmedlingen) ansökan vocab cards.

identifikation.webp → L2-2 (öppna bankkonto) vocab card.

3.29
Backend audit: 4 API bugs fixed — webhook retry, class query, email validationFix
29 May 2026

Stripe webhook: deduplication entry now removed on processing error so Stripe can retry — previously a failed subscriptions.retrieve() call would silently drop the subscription update.

GET /api/class/my: added .limit(100) to class memberships query — was unbounded.

DELETE /api/class/my: class code now validated against ^[A-Z0-9]{6}$ before hitting MongoDB.

POST /api/onboarding-email: email regex tightened to match the stricter pattern used by all other routes.

3.28
Fix: completed L1 units locked after L0 introductionFix
29 May 2026

L1-1 and other previously completed units were showing as locked (lock icon, no navigation) for users who finished them before the Survival Kit (L0) was added.

Root cause: isComplete was computed after isUnlocked in unitStates, so it couldn't be used as an unlock condition.

Fix: compute isComplete first, then use it as the first unlock condition — a completed unit is always accessible for replay.

3.27
Frontend audit: 6 bugs fixed — design system, RTL, hover statesFix
29 May 2026

Fixed --color-moss-dark circular CSS var (was self-referencing → hover states on all primary buttons were broken).

Added --color-amber-dark and --color-amber-text CSS vars; replaced 14 hardcoded hex values across 9 files.

Citizenship test page: RTL dir attribute added to all 3 return paths — layout now flips for Arabic/Farsi users.

Citizenship test wrong-answer feedback now uses design system berry tokens instead of hardcoded pink hex.

BottomNav bg-white → bg-[var(--color-bg)] for theme consistency.

Changelog back arrow now has arrow-flip for RTL navigation.

3.26
PM fixes: L1 open unlock, email capture, day-1 streak nudge, commitment hintsUpdate
29 May 2026

All Level 1 units now unlock together once the Survival Kit (L0) is complete — pick the scenario you need most urgently.

New email capture screen in onboarding: optional, sends a 'continue learning' link via email, trilingual.

Day-1 streak card on path: 'Come back tomorrow to keep it going' after completing the first lesson.

Commitment screen now shows completion estimates: '10 min/day → Level 1 in ~2 weeks'.

3.25
PM fixes: paywall headline + guest sign-in nudge on pathFix
29 May 2026

Paywall headline updated: 'Unlock Level 2 & 3' → 'Unlock Premium' with accurate sub-text covering all 25 scenarios.

Guests who complete ≥1 lesson now see a one-time sign-in nudge on the path page: 'Save your progress — sign in free'.

3.24
Feedback: open to guests + resolved state in admin inboxUpdate
29 May 2026

Feedback widget now open to unauthenticated users — guests see the full form with an 'anonymous' note.

Admin feedback inbox defaults to showing only unresolved items; toggle to see resolved ones.

Resolve checkmark on each feedback item — optimistic update, persisted to DB.

Anonymous submissions rate-limited by IP (3/hr) to prevent spam; authenticated still 10/hr.

3.23
Admin: conversion funnel + language distribution in analytics dashboardUpdate
29 May 2026

Analytics dashboard now shows a conversion funnel card: Onboarded → Started lesson → Hit paywall → Converted.

Paywall → paid conversion rate shown inline (green ≥5%, amber >0%, grey if no data).

Language distribution bar chart shows Arabic / Farsi / English split at onboarding — key for content prioritisation.

Invite-shared count surfaced in the funnel card (was tracked but never displayed).

3.22
SFI supplement: numbers, days, months, family, colors phrasebookNew
29 May 2026

Phrasebook: 'Numbers' category — 11 through 100 (elva, tolv…hundra), all irregular teens, round tens.

Phrasebook: 'Days & Months' category — all 7 weekdays, 12 months, 4 seasons, idag/imorgon/igår.

Phrasebook: 'Family' category — mamma/pappa through all grandparent and aunt/uncle terms, with en/ett notes.

Phrasebook: 'Colors' category — 11 color adjectives (en/ett forms shown), Swedish flag phrase included.

Classes section: 'Join a class' is now the primary button — correct priority for SFI students receiving a teacher code.

3.21
Premium subscriber fixes — upgrade success page, sequential lock hintFix
29 May 2026

Upgrade success page sub-text updated: 'Level 2 & 3 are now unlocked' → 'Levels 2 through 5 are now unlocked'.

Upgrade success confirms[0] updated: '25 scenarios across Levels 2–5' instead of 'Level 2 & 3 scenarios'.

'Start Level 2' CTA now links directly to /unit/L2-1 instead of /path — subscribers land in their first premium unit, not at the top of the path page.

L2 level divider now shows a small hint ('Complete all Level 1 units to unlock this level') when a premium subscriber hasn't finished L1 yet — prevents confusion about why L2 units are still locked after upgrading.

3.20
First-time visitor fixes — onboarding route, paywall copy, callbackUrlFix
29 May 2026

Onboarding now routes new users to L0-1 (Survival Kit) instead of L1-1 — fixes post-v3.17 regression where the ready screen previewed L0 but the CTA launched L1.

Onboarding ready screen sub-text updated to describe the Survival Kit (7 essential words) rather than the old 'picking up a package at the post office' copy.

Preview screen count corrected: 'first 8 real situations' → 'first 9 real situations' (L1 has 9 units).

PaywallBanner features list updated: was '8 L2 + 4 L3 scenarios' (12 implied), now correctly surfaces all 25 premium scenarios across Levels 2–5.

PaywallBanner sign-in link now uses current pathname as callbackUrl instead of hardcoded '/path' — users land back at the unit/page where they hit the paywall after logging in.

3.19
Farsi dialect fixes — Iranian & Afghan Dari coverageFix
29 May 2026

L0-1: 'tack' Farsi translation ممنون → ممنونم (first-person suffix; more natural standalone).

L0-1: 'förlåt' Farsi translation متأسفم → ببخشید (heavy formal 'I am sorry' → natural everyday apology, matching how Persian speakers use the word).

L0-1: 'ja' Farsi translation بله → بله / بلی to cover both Iranian Farsi (بله) and Afghan Dari (بلی).

Onboarding screen 1: Arabic and Farsi subtitle lines lightened from ink-faint to ink-soft — more legible in bright outdoor light.

3.18
Vocab images: kvitto, påse, linjeUpdate
29 May 2026

kvitto.webp wired to receipt vocab in L1-2 (Handla ICA).

påse.webp wired to bag vocab in L1-2 (Handla ICA).

linje.webp wired to bus line vocab in L1-3 (Åka buss).

3.17
L0 Survival Kit — 7 words, 10 numbers, your first conversationNew
29 May 2026

L0-1 Survival Kit — a zero-to-one unit before Lesson 1. Three sub-lessons: 7 essential words (hej, tack, förlåt, ursäkta, ja, nej), numbers 1–10, and the first name-and-country conversation.

23 new MP3s generated for all L0 phrases and dialogue lines.

Path page: L0 now gates L1-1. A 'Level 1 · A1 · Free' divider appears between L0 and L1. Header subtitle updates dynamically for each level.

Lesson routes: generateStaticParams now pre-renders L0 + L1 routes (level ≤ 1).

3.16
5 new units: Reklamation, BankID, Bilprovning, Försäkringskassan, MidsommarNew
29 May 2026

L4-5 Reklamation — returning defective goods, consumer rights (konsumentköplagen 3-year rule, Hallå Konsument, ångerrätt).

L4-6 BankID & personnummer — applying at Skatteverket, folkbokföring documents, activating BankID.

L4-7 Bilprovning — booking mandatory car inspection, reading the plate-digit period system, pass/fail rules.

L5-1 Försäkringskassan — reporting VAB, karensdagen explained, SGI registration, same-day reporting.

L5-2 Midsommar — first midsommar celebration, ring dances, traditional food, cultural tips for newcomers.

106 new MP3s generated (ElevenLabs, Sanna Hartfield voice). cefr type extended to B1 for L5 tier.

3.15
Billing reliability: webhook deduplication, renewal trackingFix
29 May 2026

Stripe webhooks are now deduplicated — retries from Stripe no longer send duplicate push notifications.

Subscription renewal dates now stay accurate after every billing cycle via a new invoice.paid handler.

Users receive a push notification when a payment fails, prompting them to update their card.

3.14
Citizenship test: 90 → 102 questions, expanded cheat sheetUpdate
29 May 2026

12 new questions added — medier, rättigheter, arbetsmarknad and religion now have 10 questions each (up from 7).

New topics: ansvarig utgivare, MPRT, public service mission, oskuldspresumtionen, förvaltningslagen, DO, provanställning, A-kassan, 40h arbetstid, nationella minoriteter, religiös klädsel, sekulär stat.

Cheat sheet expanded with 12 new facts across the same four categories — full Arabic and Farsi translations included.

3.13
Infrastructure: uptime monitoring, backups, faster repeat visitsUpdate
29 May 2026

Added /api/health endpoint — enables uptime monitoring tools to check both server and database health.

Weekly automated database backup now runs every Sunday via GitHub Actions (90-day artifact retention).

Service worker now caches JavaScript bundles and audio files on first load — repeat visits and lesson audio load faster.

Sentry error stack traces now show readable source code instead of minified output (requires SENTRY_AUTH_TOKEN in Vercel).

3.12
Mobile lesson layout fixed + new illustrationsFix
29 May 2026

Lesson player now uses the full screen width on mobile — the conversation sidebar is hidden on small screens so the active step card is no longer squished.

Three new lesson intro illustrations added: doctor appointment (L1-4, L2-5), Skatteverket (L1-5), and fika/café (L1-6).

SuperSvenska logo now appears correctly in Google search results.

3.9
Security hardening — rate limits & citizenship question count updatedFix
29 May 2026

Citizenship test paywall now shows the live question count dynamically — always accurate as more questions are added.

Added rate limiting to waitlist signup, class join, and teacher class creation endpoints.

Events API now validates session_id format before storing — prevents analytics poisoning.

Removed magic link URL from development console log — tokens no longer appear in any log output.

Push subscribe endpoint now validates keys object structure before storing to DB.

3.8
Illustrations added for first 3 lessonsUpdate
29 May 2026

Package pickup, supermarket, and bus illustrations now show on lesson intro screens.

3.7
RTL & accessibility pass — Arabic/Farsi layout now correct on 6 more pagesFix
29 May 2026

Added RTL direction to lesson player, unit detail, flashcard review, shadowing, progress, and listening pages — Arabic/Farsi users now see correct layout on every screen.

Added --color-moss-dark CSS variable and replaced 49 hardcoded #1F3D33 hex values across the app and all components.

Citizenship test 'wrong answer' backgrounds now use the design system berry-soft tint instead of Tailwind red-50.

L1Lang type imported from the shared type definition in citizenship test — removes a local duplicate.

Added aria-label to the icon-only play button on the shadowing page.

AudioButton error state now uses design system berry colors instead of raw Tailwind red classes.

3.6
Content quality pass — more natural Swedish, accurate cultural tipsUpdate
28 May 2026

L1-1: Staff dialogue now reads as natural spoken Swedish — 'Här är ditt paket!' replaces the awkward combined phrase.

L1-2: Self-checkout help phrase updated to 'Kan någon hjälpa mig? Jag förstår inte självkassan.' ICA hours and Systembolaget Sunday info now include accurate caveats.

L1-3: Fixed phonetic transcription for 'vid' — correctly represents the short /ɪ/ vowel for Arabic and Farsi speakers.

L1-5: folkbokföring tip now explains the word means 'registrera din adress hos Skatteverket'.

L1-7: 'Trevligt att träffas!' no longer repeated between phrases and dialogue sections.

L1-8: Standardised on 'tvättpass' throughout — 'tvättid' removed to avoid confusion.

3.5
Streak at risk — never miss a moment to protectNew
28 May 2026

When you return after missing a day, a new interstitial appears before your streak resets — showing your streak count and a clear path to protect it with Premium.

Streak shields are now front-and-centre at the moment they matter most, not just a bullet point in the paywall.

3.4
Citizenship test: 90 questions + Daily ChallengeNew
28 May 2026

Expanded from 43 to 90 questions across all 9 categories — deeper coverage of history, law, society, culture, and geography.

New Daily Challenge mode: 10 randomised questions every day with a streak counter. Come back daily to build your streak 🔥

All 47 new questions fully translated to Arabic and Farsi.

3.3
Swedish audio for all 8 new unitsUpdate
28 May 2026

181 new MP3 files generated via ElevenLabs for L3-5 through L4-4 — every vocab word, phrase, dialogue line, and cultural tip now has native Swedish audio.

Voice: Sanna Hartfield (Swedish Narration). Audio generation script updated to include all new units.

3.2
8 new units — Level 3 expanded + Level 4 addedNew
28 May 2026

Level 3 grows from 4 to 8 units: parental leave (Försäkringskassan), unemployment benefits (A-kassa), rental contract & tenant rights, and getting a Swedish driver's license.

New Level 4 — Everyday Life: dental appointment, filing a police report, library card & services, and the first maternal health centre (MVC) visit.

All 8 new units are fully trilingual (English, Arabic, Farsi) with complete dialogues, roleplay, and cultural tips.

3.1
Level 1 complete upsell cardUpdate
28 May 2026

When you finish all 9 Level 1 scenarios and haven't upgraded yet, a green card now appears at the top of your path — the highest-intent moment in the funnel.

Tapping it opens the Premium paywall directly. You can dismiss it permanently with the × button.

3.0
Activation funnel trackingUpdate
28 May 2026

Three new analytics events — Onboarding Complete, Paywall Hit, and Paywall Converted — are now tracked in both Plausible and MongoDB, making the full free-to-premium funnel visible for the first time.

Paywall hits are tagged by source (path page vs. unit page) so you can see which context drives more upgrades.

2.9
Admin: subscriber overview, feedback inbox, and subscriber listUpdate
28 May 2026

The admin analytics dashboard now shows a subscriber card at the top — active count with monthly/yearly split, past-due alert, and canceled count, all without leaving the app.

New feedback inbox at /admin/feedback: filter by type (bug, feature, content, other), see sender, page context, and relative timestamps.

New subscriber list at /admin/subscribers: color-coded status badges, plan, renewal/cancellation dates, and a clickable mailto link for quick support replies.

2.8
Renewal reminder push notificationUpdate
28 May 2026

Premium subscribers who have push notifications enabled will now receive a reminder 3 days before their subscription renews — giving time to review or manage the subscription before being charged.

2.7
Streak shields now mentioned at upgradeUpdate
28 May 2026

The upgrade success page now lists what you get — including streak shields — so subscribers know from day one that a single missed day won't reset their streak.

Streak shields also appear in the paywall feature list before checkout.

2.6
Premium badge on profileUpdate
28 May 2026

Premium subscribers now see a small amber crown 'Premium' chip next to their name on the profile page — a clear visual confirmation of their subscription status.

2.5
Level 2 divider shows 'Unlocked' for subscribersFix
28 May 2026

Premium subscribers no longer see an amber crown on the Level 2 divider — it now shows a green ✓ Unlocked chip in their language. Free users still see the amber crown and upgrade button as before.

2.4
Practice hub shows premium word countUpdate
28 May 2026

The Review card in Practice now shows how many words in your deck come from premium (L2/L3) lessons — e.g. 'incl. 12 from premium lessons'. Reinforces that your subscription is actively helping you.

2.3
Lesson player fully translatedUpdate
28 May 2026

All lesson player labels are now trilingual — 'Staff' / 'You' speaker labels, 'Good to know' info cards, intro step feature list ('X phrases to listen to', 'Say the phrases aloud', 'Roleplay the real conversation', 'Cultural tips'), and the 'min · new words' header

Arabic and Farsi speakers no longer see English strings inside every lesson card

2.2
Cleaner certificate and lesson share textFix
28 May 2026

Certificate and lesson completion share messages no longer say 'Free' — just the URL. Premium subscribers won't accidentally signal the product is free when sharing their achievements.

2.1
Subscription management in profileNew
28 May 2026

Premium subscribers can now see their plan type (Monthly / Yearly) and next renewal date directly on the profile page

Tapping 'My Subscription' opens the Stripe Customer Portal — cancel, update payment method, or download invoices without contacting support

If your subscription is set to cancel at period end, the profile shows 'Cancels [date]' in red so there are no surprise endings

2.0
Premium subscriber experience improvementsFix
28 May 2026

Premium subscribers no longer see the 'This app is free — support it' Ko-fi card on their profile — it only shows for free users now

Lesson intro banners now support 16:9 illustration images — upload to /images/illustrations/ and they appear automatically with gradient fallback

1.9
Trilingual onboarding welcome + guest checkout reassuranceUpdate
28 May 2026

Onboarding language selector now shows the welcome title and subtitle in English, Arabic, and Farsi simultaneously — before you've chosen your language

Paywall now tells unauthenticated users that their subscription will be securely linked to the email they use at checkout, reducing drop-off for guests who haven't signed in yet

Vocabulary image added for hämta (to pick up) in Unit L1-1

1.8
Farsi pronunciation guide + fully trilingual profile pageUpdate
28 May 2026

Farsi phonetics added to every lesson across all 21 units — Arabic and Farsi speakers now both get a pronunciation guide written in their own script

Arabic and Farsi use different letters for the same Swedish sounds (پ vs ب for /p/, گ vs غ for /g/, و vs ف for /v/) — each language now gets the correct version

Profile page is now fully trilingual: stat labels (Lessons, Streak, Words, XP), section headers (Learning, Settings), and all menu items (My Progress, Achievements, My Vocabulary, Interface language, Notifications, What's new, Sign out) display in Arabic and Farsi

1.7
RTL layout fixes across Learn, Practice, lessons, and profileFix
27 May 2026

Learn page and Practice hub now flip to right-to-left layout for Arabic and Farsi — text alignment, card order, and icons all correct

Conversation bubbles inside lessons now align correctly in RTL: your speech on the left, the instructor on the right

Arabic unit descriptions no longer cut off mid-word — they now wrap to a second line instead of being clipped

Sync conflict sheet (shown when progress is found on another device) is now fully translated into Arabic and Farsi

Profile page streak card and sign-in prompt now display in Arabic and Farsi

1.6
Full Arabic & Farsi support across login and achievementsUpdate
27 May 2026

Sign-in page is now fully trilingual — Arabic and Farsi speakers can sign in, send magic links, and read error messages in their own language

All 10 achievements now display in Arabic and Farsi — names, descriptions, and the achievements page header

Achievement toast notification also uses the correct language when it pops up after completing a lesson

1.5
Reliability: error monitoring + database maintenanceFix
27 May 2026

Sentry error monitoring is now active — production crashes, failed API calls, and server errors are captured automatically so we can fix them faster

Events database auto-purges after 90 days — prevents unbounded growth over time

Rate-limit counters cleaned up automatically — no stale data accumulation

Server startup optimised — database indexes created once at cold start, not on every request

1.4
Security: premium content gated server-sideFix
27 May 2026

L2 and L3 lesson content is now verified on the server on every request — premium content requires a valid subscription at the routing level, not just in the UI

Non-premium users are redirected to the unit page before any lesson content is loaded or rendered

L2-1 free preview (first context sub-lesson) remains accessible to all users

1.3
Quick reference: phrasebook links from every unitNew
27 May 2026

Each unit now shows a "Quick reference" card at the bottom of the sub-lesson list, linking directly to the relevant phrasebook section

Tap it during or after a lesson to see all phrases for that situation in one place

9 units linked: shopping, transport, doctor, housing, bank, employment, school, pharmacy, and more

1.2
Emergency unit + 7 new listening episodesNew
27 May 2026

New unit: L1-9 Nödläge (Emergency) — how to call 112, what to say to police, fire, and ambulance; permanently free for everyone

7 new real-conversation listening episodes added — total is now 13

New episodes cover: parcel pickup, tax office, café ordering, introducing yourself, laundry room, employment office, and emergency calls

All 9 Level 1 units now have a matching listening episode to reinforce what you learned

1.1
Add to Home Screen — install SuperSvenska as an appNew
27 May 2026

After your first lesson, a prompt appears on the learning path to install SuperSvenska on your home screen

Android / Chrome: one tap triggers the native install dialog — no app store needed

iOS Safari: instructions guide you through Share → Add to Home Screen

Hidden if already installed; dismissible and never shown again once you've decided

1.0
Version 1.0 — SuperSvenska is liveNew
26 May 2026

Full L1 curriculum free forever — 8 units covering the first 30 days in Sweden

Premium subscription (29 SEK/month or 199 SEK/year) unlocks L2 & L3

Citizenship test — 69 SEK one-time unlock, 42 official questions across 9 categories

Arabic & Farsi UI throughout — built for the immigrant community

Spaced repetition, XP, streaks, achievements, and daily goals

Sponsor slot and Ko-fi/card support for the community to keep it running

0.65
Sponsor slot — one business adNew
26 May 2026

Single sponsor banner injected between L1 and L2 on the learning path — seen by every user who progresses

Content/sponsor.ts config file: flip active: true and fill in the fields to go live, no env vars needed

SPONSORED disclosure label, rel="sponsored" on link — legally compliant with Swedish marknadsföringslagen

Translated tagline support (EN/AR/FA) and RTL layout

Click tracked in Plausible (Sponsor Clicked event)

Renders nothing when active: false — safe to deploy now, enable when deal is signed

0.64
Ko-fi tip card on profileNew
26 May 2026

Added a 'Buy me a coffee' card on the profile page linking to Ko-fi

Translated to Arabic and Farsi — matches the audience

Click tracked in Plausible (Kofi Clicked event)

0.63
Citizenship test — 69 SEK one-time unlockNew
26 May 2026

Citizenship test is now a separate 69 SEK one-time purchase (forever access)

Compelling paywall landing page with urgency copy, feature list, and price

Premium subscribers (199 SEK/year) get citizenship test included automatically

Webhook-confirmed unlock: page polls up to 16s after Stripe redirect, shows 'Activating…' state

Premium upsell line on paywall — 'get this + Level 2 & 3 for 199 SEK/year'

New unlocks collection in MongoDB for durable one-time purchase records

0.62
Freemium paywall — Stripe subscriptionNew
26 May 2026

L1 (8 units) remains free forever; L2–L3 (12 units) require a Premium subscription

Stripe Checkout integration — 29 SEK/month or 199 SEK/year (early access)

PaywallBanner bottom sheet: plan toggle, upgrade CTA, fully translated to Arabic and Farsi

Upgrade page (/upgrade) with free vs premium feature comparison

L2 level divider shows crown + 'Upgrade to unlock' button for free users

Premium users get immediate L2 access without completing all of L1

Webhook handler for subscription lifecycle events (active, past_due, canceled)

0.61
Feedback widgetNew
25 May 2026

Floating 'Feedback' button appears on every page above the bottom nav

Four categories: Bug report, Feature idea, Content issue, Other

Fully translated to Arabic and Farsi

Submissions saved to database and emailed to the owner via Resend

0.60
Complete audio coverage + leaderboard hiddenUpdate
25 May 2026

19 missing audio files generated with ElevenLabs (Elin Nilsson — Clear and Calm Swedish Narration) — app now has 100% audio coverage, no TTS fallbacks

Leaderboard links removed from Profile and Progress pages until real ranking data is wired up

0.58
Teacher dashboard: words mastered columnUpdate
25 May 2026

Student cards now show words mastered (book icon · count) alongside lessons and streak

New 'Words' sort option on the student roster

0.57
Vocabulary searchNew
25 May 2026

Search input on the vocabulary page filters by Swedish word or L1 translation in real time

Works in combination with existing filter chips (mastery, due, unit)

Clear button resets search; fully RTL-aware for Arabic and Farsi

0.56
Lesson resume promptNew
25 May 2026

Returning to a mid-lesson now shows a 'Continue / Start over' prompt instead of silently restarting

Prompt shows how far through the lesson you got (% complete)

Translated into Arabic and Farsi

0.55
Security: four hardening fixes from auditFix
25 May 2026

Citizenship POST now sanitises data server-side — knownIds filtered against real question IDs, test scores clamped, invalid categories dropped

Magic-link email validation upgraded from includes('@') to strict EMAIL_RE regex

Class name capped at 200 chars to prevent DB bloat

Progress and citizenship writes rate-limited at 30/min per user

0.51
Fix: weekly cron no longer loads full progress collection into memoryFix
25 May 2026

Replaced .find({}).toArray() with a filtered MongoDB cursor — only active users with completed lessons are fetched, one doc at a time

0.50
Security: magic-link token no longer logged in Vercel access logsFix
25 May 2026

Token moved from query string (?token=…) to URL fragment (#token) — fragments are never sent to the server and never appear in access logs

Magic-verify endpoint changed from GET to POST; /auth/verify client page reads the hash and POSTs the token

0.49
Security: use crypto.randomInt for class code generationFix
25 May 2026

Replaced Math.random() with crypto.randomInt() in generateCode() — codes are now drawn from a CSPRNG, making them unpredictable

0.48
Security: remove dangerouslySetInnerHTML from layoutFix
25 May 2026

RTL-detection script moved to /public/rtl-init.js and loaded as a plain render-blocking <script src> — no inline script, no dangerouslySetInnerHTML

0.47
Security: constant-time cron secret comparisonFix
25 May 2026

Replaced === with crypto.timingSafeEqual in both cron routes — prevents timing-based secret enumeration

0.46
Security: stop returning teacher email to studentsFix
25 May 2026

GET /api/class/my no longer includes teacher_email in the response — students receive only class code, name, and their own join date

Removed teacher_email from the StudentClass type and the optimistic join insert in TeacherSection

0.45
Security: server-side progress sanitisation (academic integrity)Fix
25 May 2026

completed_lessons filtered against the real sublesson ID set on every POST — forged lesson counts are stripped before storage

vocab_scores values clamped 0–100 and total entries capped at 2,000 — bounds vocab_count inflation visible to teachers

streak_current, streak_longest, xp_total, xp_today clamped to non-negative reasonable maxima

l1_choice validated against known L1Lang values

0.44
Security: fix HTML injection in waitlist admin emailFix
25 May 2026

Replaced loose includes('@') check with a strict EMAIL_RE regex that rejects addresses containing HTML special characters

Added escapeHtml() — email is escaped before interpolation into the HTML body, blocking tag/link injection

0.43
Security: payload size limit on progress upsertsFix
25 May 2026

POST /api/progress and POST /api/citizenship now reject bodies over 64 KB with HTTP 413

Body is read as raw text before JSON parsing so the limit is enforced before any allocation

0.42
Security: HTTP security headersFix
25 May 2026

Added Content-Security-Policy — restricts scripts to self + Plausible, fonts to Google Fonts, frames blocked entirely

Added X-Frame-Options: DENY, X-Content-Type-Options: nosniff, Referrer-Policy, HSTS (2-year, preload), Permissions-Policy

Removed X-Powered-By: Next.js header (poweredByHeader: false)

0.41
Security: remove admin emails from client JS bundleFix
25 May 2026

Renamed NEXT_PUBLIC_ADMIN_EMAILS → ADMIN_EMAILS — the list is now server-only and never inlined into the JS bundle

isAdmin flag baked into the JWT at sign-in (both Google OAuth and magic link paths) so client components read a boolean from their session, not the email list

Added NextAuth type augmentation so session.user.isAdmin is properly typed

0.40
Security: remove hardcoded fallback secret from magic linkFix
25 May 2026

NEXTAUTH_SECRET no longer has a fallback — missing it now fails hard (500 / redirect to /login?error=ServerError) instead of silently using a public default

createToken and verifyToken now take the secret as an explicit parameter with no module-level constant

0.39
Security: harden /api/events against DB exhaustionFix
25 May 2026

props payload is now capped at 1 KB — oversized payloads are silently dropped (204)

IP rate limit: 60 events per minute per IP — well above any real user, stops flood writes

Extracted shared rate-limit helpers to lib/rate-limit.ts (used by magic-link and events)

0.38
Security: rate limiting on magic link endpointFix
25 May 2026

Magic link requests are now rate-limited: 3 per email per 10 minutes, 10 per IP per 10 minutes

Blocks email-bombing a victim's inbox and cost-exhaustion attacks cycling through many addresses

Implemented via MongoDB atomic counters with a TTL index — no new infrastructure required

Returns 429 with Retry-After: 600 when limits are exceeded

0.37
Security: block SSRF via push subscription endpointFix
25 May 2026

Push subscription endpoint is now validated before being stored or used

Rejects any endpoint that is not HTTPS, points to a private/internal IP range, or is not a valid URL

Prevents an attacker from registering a crafted subscription to make the cron job send HTTP requests to internal infrastructure

0.36
Security: close Host Header Injection on magic linkFix
25 May 2026

Magic link URL is now always built from NEXTAUTH_URL — never the request Host header

If NEXTAUTH_URL is unset the endpoint returns 500 instead of silently using an attacker-controlled host

Prevents an attack where a spoofed Host header caused the sign-in link to point to an attacker's server

0.35
Magic link: enforce single-use tokensFix
25 May 2026

Magic link tokens can no longer be replayed within their 15-minute window

Each token is fingerprinted (SHA-256) and recorded in MongoDB on first use — a second click redirects to /login?error=TokenAlreadyUsed

The used_tokens collection self-purges via a TTL index so no manual cleanup is needed

0.34
L1-4: 1177 robot menu — the biggest practical gapUpdate
25 May 2026

Context intro now warns that 1177 starts with an automated menu, not a human

Added robot phrases to phrases sublesson: "Välkommen till 1177" and "Tryck 1 för att boka tid" — heard before the receptionist

New cultural tip (shown first): full explanation of the robot menu, what to press, and the tip to use 1177.se instead

0.33
L1-2: "Har du ICA-kort?" addedUpdate
25 May 2026

Added "Har du ICA-kort?" as a "they say" phrase in the phrases sublesson

Appears right after the bag question in the step sequence, matching the real checkout order

Two new pick_sound quizzes drill both cashier opening questions against each other

0.32
L1-2: self-checkout help phraseUpdate
25 May 2026

Added "Kan du hjälpa mig med självkassan?" as a practised phrase (listen + speak) in the phrases sublesson

Updated self-checkout cultural tip to include the help phrase and reassure that staff are always nearby

Fixed vocab index bug: ange-din-kod (5) and tack-hdb-ica (6) steps were swapped

0.31
L1-2: PIN prompt phrase + tipUpdate
25 May 2026

Added "Ange din kod" as a recognisable terminal phrase in the phrases sublesson (with listen + quiz)

Added cultural tip: 'Ange din kod' = enter your PIN, then press the green button

0.30
L1-2: contactless vs chip tipUpdate
25 May 2026

Added cultural tip: sometimes insert, sometimes tap — always look at the screen

Prevents the freeze moment when the terminal behaviour doesn't match what the roleplay taught

0.29
L1-1: no-payment explainer tipUpdate
25 May 2026

Added cultural tip: "You don't pay anything — staff scan the barcode from your SMS, check your ID, hand it over. Done."

Removes anxiety about an expected PIN or payment step that doesn't exist

0.28
L1-1: "Har du en avisering?" addedUpdate
25 May 2026

Added "Har du en avisering?" (Do you have a notification?) as a clerk phrase in phrases and dialogue sublessons

Dialogue now flows: greeting → Har du en avisering? → show SMS → Kan jag se din legitimation? → collect package

New pick_sound quiz tests recognition of the phrase alongside similar-sounding clerk lines

0.27
L1-1: clerk's silence tipUpdate
25 May 2026

Added 6th cultural tip to L1-1 (Hämta ett paket): staff at the ombud counter often say nothing — just show your phone (the SMS) and your ID

Intro description updated from "Five things" to "Six things"

0.26
Fix missing lesson intro bannersFix
25 May 2026

Added 15 missing illustration types to the intro banner map (grocery, bus, doctor, skatteverket, fika, laundry, apartment, bank, office, school, pharmacy, maintenance, email, letter, tax)

All unit intro screens now show a matching icon and colour gradient instead of the default stethoscope fallback

0.25
Wrong answer: explanation + Try againUpdate
24 May 2026

Wrong answer on pick-picture and pick-sound now shows a persistent explanation strip

Strip reveals the correct Swedish word and its translation in your language

"Try again" resets the question so you can practise once more

"Continue" lets you move on if you prefer

Fully localised for Arabic and Farsi (RTL strip layout)

0.24
Shareable certificate imageNew
24 May 2026

"Share certificate" button on the unit completion modal generates a 1080×540 PNG

On mobile: opens the native share sheet (WhatsApp, iMessage, etc.) with the image attached

On desktop: downloads the PNG directly

Image shows unit color banner, trophy, heading, unit title, stats, and vocab chips

Fully supports Arabic and Farsi (RTL canvas layout for ar/fa)

"Copy link" secondary action replaces the old share button

0.23
Explore → My VocabularyUpdate
24 May 2026

Replaced the unit grid + basic word list with a focused vocabulary manager

Filter chips: All · Due · Untested · Needs work · Practicing · Mastered · per-unit

"Due" chip shows a live count; "Review now" CTA appears and links straight to the review queue

Each row shows a mastery dot, translation, unit badge, and "Due" badge or next-due countdown

Mastery legend items are tappable shortcuts to the matching filter

Fully translated into Arabic and Farsi; RTL layout applied

0.22
Onboarding: daily study commitment screenNew
24 May 2026

New screen between course preview and ready: "How long can you study per day?"

Three options — 5 min (10 XP), 10 min (20 XP), 20 min (40 XP) — tap to select and advance

Sets daily_xp_goal in progress immediately; the path page XP ring reflects the choice from day one

Fully translated into Arabic and Farsi; RTL layout applied for ar/fa

0.21
Remove unreviewed Farsi translation markersFix
24 May 2026

Removed all // REVIEW: fa developer annotations from 32 source files — translations are live, not placeholders

Farsi text is present and correct across all units, UI components, and onboarding screens

0.20
Push prompt: guard against missing VAPID keyFix
24 May 2026

Prompt no longer appears when NEXT_PUBLIC_VAPID_PUBLIC_KEY is unset — subscribing would silently fail

subscribe() now resets the loading state in all exit paths (moved vapidKey check inside try/finally)

0.19
Citizenship cheat sheet: fix RTL layout for Arabic / FarsiFix
24 May 2026

Cheat sheet container now sets dir="rtl" when the interface language is Arabic or Farsi

Text, bullet dots, and category headers all align correctly for right-to-left reading

Back-arrow icons flip via the existing arrow-flip utility; removed the hardcoded ← character

0.18
Magic link: resend and wrong-email escapeFix
24 May 2026

"Resend link" button on the confirmation screen — re-fires the same email without navigating away

"Wrong email? Try a different address" resets the form so you can correct a typo

Resend button shows a brief "Sent!" confirmation after the second email goes out

0.17
Review: rating buttons show next review dateUpdate
24 May 2026

Again / Hard / Good sublabels now show when the card will resurface ("tomorrow", "in 5d", …)

Computed live from SM-2 so the preview reflects your actual card's interval, not a fixed hint

Again stays red, Hard stays amber, Good stays green — colors unchanged

0.16
Timed quiz: timer stops cleanly on auto-submitFix
24 May 2026

Added a stopped-ref guard so the timer tick chain halts the moment time expires

Prevents onDone from being called more than once if the parent re-renders before QuizView unmounts

0.15
Citizenship test: autosave quiz progressFix
24 May 2026

Answers are saved to localStorage after each question — navigating away no longer loses your progress

Returning to the quiz resumes from where you left off, with a banner confirming it

"Start over" button lets you discard the draft and restart from Q1

Timed (45-min) quizzes are excluded from autosave — challenge mode stays strict

0.14
Estimated time remaining on path cardsUpdate
24 May 2026

Each scenario card now shows ~X min next to the lesson count

Time reflects only the remaining sub-lessons — shrinks as you progress

Disappears once the unit is complete

0.13
Word explorer: filter by unit or weak wordsUpdate
24 May 2026

Filter chips: All · Weak · per-unit (L1-1, L1-2 …) — horizontally scrollable

Weak filter shows words where your pronunciation score is below 60

Mastery dot on every word: grey = untested, red = needs work, amber = practicing, green = mastered

Translations now shown in your interface language (Arabic / Farsi)

0.12
Remove fake leaderboard rankFix
24 May 2026

Removed the mock 'Rank #X of 21' from the profile page — it was based on hardcoded scores, not real users

0.11
Streak-at-risk push notificationFix
24 May 2026

Daily push now fires when your streak is in danger (2+ days, not yet practiced today)

Message shows your streak count so you know exactly what's on the line

Combined message when both a streak and review words are due

0.10
Back button in lessonsFix
24 May 2026

Tap ‹ in the lesson header to go back one step

Revisit any audio, vocabulary card, or question you missed

Step state resets cleanly on back (answer selections, recording)

0.9
Citizenship test: Arabic & Farsi + timed mode + cloud syncNew
24 May 2026

Full Arabic and Farsi translations for all 43 questions, answers, and explanations

Timed mock test mode — 45-minute countdown with auto-submit (real exam pressure)

Citizenship test card on the learning path for easier discovery

Cloud sync: test results and known-card state saved to your account

0.8
Push notifications + weekly emailNew
24 May 2026

Daily push reminder when SR words are due — tap to open the review queue

Opt-in banner on the path page after your first lesson (one tap to enable)

Weekly email every Monday: streak, XP, and how many words need review

Cron jobs run at 07:00 UTC daily and 08:00 UTC every Monday

0.7
Sentence pronunciation in roleplayUpdate
24 May 2026

Roleplay steps now listen to your full sentence, not just the first word

Word-by-word colour diff shows which words landed (green / amber / red)

Tap the mic again to retry as many times as you like

Remaining dialogue lines play automatically after a successful take

0.6
Spaced repetition review queueNew
24 May 2026

SM-2 flashcard review — words resurface at growing intervals (1→3→7→20 days)

Review queue on the path page: tap to drill any words due today

Again / Hard / Good ratings tune each word's schedule individually

Vocab from every completed lesson enters the queue automatically

+2 XP per correct card to keep review streaks rewarding

0.5
User accounts & cloud syncNew
24 May 2026

Sign in with Google or a magic link — no password needed

Progress syncs automatically across all your devices

Smart merge resolves conflicts when two devices diverge

Progress stays safe on-device even without an account

Teacher classes and student roster now tied to your account

0.4
L3 units, voice recording & full audioNew
24 May 2026

4 new L3 units: formal email, official letters, tax return, prescription renewal

Voice recording with real-time pronunciation feedback in every speak step

Full Swedish audio for all 20 units — L1, L2, and L3

Pronunciation result shows word-by-word colour diff (correct / close / wrong)

Practice page: Drills and Roleplay sessions now use real voice checking

0.3
Streak shields, XP & achievementsNew
24 May 2026

Daily XP goal ring in the learning path header

Streak shields — auto-protect your streak when you miss a day

10 achievements to unlock as you progress

Streak rescue nudge on the path page when you haven't practised today

XP stat on your profile page

0.2
Teacher & class toolsNew
10 May 2026

Create a class and share a 6-character join code with students

Student roster with progress bars, streak indicators, and last-active dates

Students can join classes via a share link (/join/CODE)

Teacher and student sections on the profile page

0.1
Launch — first 30 days of SwedishNew
28 April 2026

8 real-life scenarios for the first 30 days in Sweden

Listen, look, speak, and roleplay in each unit

Arabic and Farsi interface support

Streak tracking and word collector

Offline-capable with local progress sync

More scenarios and features arriving regularly.