Previously Register.tsx wrote auth_token directly to localStorage without
calling setToken/setUser in AuthContext. This made isAuthenticated=true
(computed from localStorage) while user=null (React state), so ProtectedRoute's
user.email_verified check was never reached.
- AuthContext: add setAuth(token, userData) that syncs both localStorage and React state
- Register: use setAuth() instead of direct localStorage write
- Register: add !registered guard to auto-redirect effect to prevent bypassing check-inbox screen
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Without .dockerignore, COPY . . was overwriting the npm ci install
with the server's stale node_modules, causing Rollup to pick up the
wrong @radix-ui/react-slot version.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fixes Docker build failure: react-progress brought react-primitive@2.1.4
which needs createSlot from react-slot >=1.2.0. npm overrides ensure
single 1.2.4 copy is used everywhere, no nested conflicts.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
npm ci in Docker resolved react-progress's nested react-primitive which
requires createSlot from react-slot >=1.2.0.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
GET /api/focus-groups/{id}/report/download generates a markdown report
with AI-written executive summary, key themes with quotes, and full
transcript. Frontend adds "Export Full Report" button to ThemesPanel.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- All copy updated: "Session name", "What are you researching?", "Key topics to explore",
"Session length", "Thinking depth", "Response length", "Attach materials (optional)"
- llm_model, reasoning_effort, verbosity moved into collapsible "Advanced settings" accordion
- Word-count indicators now float inside textareas (absolute overlay) instead of below
- Removed duplicate amber text blocks and redundant FormDescriptions
- FocusGroupModerator header: "AI Focus Group Moderator" → "Set Up Your Research Session"
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Epic 1 — OG-image & SEO base:
- Replace wrong og-image.png with branded 1200×630 Cohorta design
- index.html: full title, og:type/url/image dimensions, twitter:card, canonical
Epic 2 — Pricing from admin panel:
- Pricing.tsx: remove hardcoded DEFAULT_PACKS; add loading skeleton and error+retry state
- Features list and personas/sessions counts computed from API credits/costs
- billing.py /packs: also returns persona_cost and run_cost for frontend math
- app_settings.py: add popular:True to pro pack default
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Header: replace banner PNG with Logo SVG mark (44px) + Cohorta wordmark — crisp at all sizes
- ScrollToTop: moved from PublicLayout to App.tsx root — works on all pages,
avoids position:fixed breakage from framer-motion transform ancestors
- ScrollToTop: threshold 100px → appears sooner after scroll
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Header: logo h-48px mobile / h-168px desktop, md:self-start overflow
- PublicLayout: main pt-80px to match new header height
- Hero: -mt-80px cancels PublicLayout pt, responsive inner pt
- AppLayout: replace PNG logo (black box) with SVG mark + text
- billing.py: add public GET /billing/packs endpoint
- api.ts + Pricing.tsx: fetch packs from API, fallback to defaults
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Move cohorta-banner.png into navbar left slot (height 42px, w-auto)
- Navbar py-2/py-1.5 sized to logo — no left spacer needed
- PublicLayout main: pt-20 → pt-[58px] to match new navbar height
- Hero: mt/pt offsets updated to 58px; content starts 45px below navbar
- Hero: banner block removed (now lives in navbar)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Banner moved outside the grid to full max-w-7xl width — naturally ~240px tall at desktop
- Removed banner from left column (no longer constrained to 50% column width)
- ScrollToTop visibility threshold: 400px → 200px (appears after ~1 screen scroll)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Register form: two required checkboxes (Terms of Service / Privacy Policy + UK GDPR data processing)
- Zod schema uses z.literal(true) — form won't submit until both are checked
- Backend: validates accept_terms + accept_data_processing flags (400 if missing)
- User.save() writes created_at, consent_terms_at, consent_data_processing_at to MongoDB
- Admin UsersTab: Registered column, email verified badge, consent timestamps in edit dialog
- Fix: EU-hosted → UK hosted badge in register form
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Header nav: SVG C-mark only (no PNG box, no dark bg)
- Hero: full logo PNG banner below nav with mix-blend-mode:screen (removes dark bg)
- favicon.svg: updated C-mark colours to match rebrand
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>