diff --git a/src/App.flow.test.jsx b/src/App.flow.test.jsx
index 396ab37..aaa89fc 100644
--- a/src/App.flow.test.jsx
+++ b/src/App.flow.test.jsx
@@ -391,3 +391,53 @@ describe('Window title', () => {
await waitFor(() => expect(document.title).toMatch(/^[A-Za-z0-9 ]+ \d+ · Bible Study$/));
});
});
+
+describe('Login page', () => {
+ async function openLogin() {
+ stubServer({ user: null });
+ render();
+ return screen.findByRole('button', { name: /^sign in$/i });
+ }
+
+ test('tells you what the app can do, in groups', async () => {
+ await openLogin();
+ expect(screen.getByRole('heading', { level: 1, name: /read it\. study it\./i })).toBeInTheDocument();
+ for (const group of ['Read', 'Study', 'Teach', 'Everywhere']) expect(screen.getByRole('region', { name: group })).toBeInTheDocument();
+ expect(screen.getByText('A real outline')).toBeInTheDocument();
+ expect(screen.getByText('Compare them')).toBeInTheDocument();
+ });
+
+ test('switches between signing in and creating an account', async () => {
+ const user = userEvent.setup();
+ await openLogin();
+ expect(screen.getByRole('heading', { name: 'Sign in' })).toBeInTheDocument();
+ await user.click(screen.getByRole('button', { name: /need an account/i }));
+ expect(screen.getByRole('heading', { name: 'Create an account' })).toBeInTheDocument();
+ expect(screen.getByText(/at least 8 characters/i)).toBeInTheDocument();
+ expect(screen.getByRole('button', { name: /^create account$/i })).toBeInTheDocument();
+ await user.click(screen.getByRole('button', { name: /already have an account/i }));
+ expect(screen.getByRole('heading', { name: 'Sign in' })).toBeInTheDocument();
+ });
+
+ test('a theme can be picked before signing in', async () => {
+ const user = userEvent.setup();
+ await openLogin();
+ await user.click(screen.getByRole('button', { name: 'Dark' }));
+ expect(screen.getByRole('button', { name: 'Dark' })).toHaveAttribute('aria-pressed', 'true');
+ expect(localStorage.getItem('reader-theme')).toBe('dark');
+ });
+
+ test('a wrong password shows the error', async () => {
+ const user = userEvent.setup();
+ vi.stubGlobal('fetch', vi.fn((url, opts = {}) => {
+ if (url === '/api/auth/me') return json(401, {});
+ if (url === '/api/auth/login') return json(401, { error: 'Incorrect email or password.' });
+ return Promise.resolve({ ok: false });
+ }));
+ render();
+ await user.type(await screen.findByLabelText(/email/i), 'a@b.co');
+ await user.type(screen.getByLabelText(/^password/i), 'wrong-password');
+ await user.click(screen.getByRole('button', { name: /^sign in$/i }));
+ expect(await screen.findByRole('alert')).toHaveTextContent(/incorrect email or password/i);
+ });
+});
diff --git a/src/pages/AuthPage.jsx b/src/pages/AuthPage.jsx
index fe27752..68eccea 100644
--- a/src/pages/AuthPage.jsx
+++ b/src/pages/AuthPage.jsx
@@ -1,8 +1,91 @@
import { useState } from 'react';
import { useApp } from '../context/AppContext.js';
-import { themeVars } from '../components/reader/readerThemes.js';
+import { themeVars, READER_THEMES } from '../components/reader/readerThemes.js';
import { loginUser, registerUser, verifyMfaLogin, logoutUser } from '../syncService.js';
+// What the app does, in four groups. Everything listed here exists in the app.
+const FEATURES = [
+ {
+ title: 'Read',
+ points: [
+ ['Six translations', 'BSB, ESV, NLT, CSB, NASB and NET, with audio and whole-Bible search.'],
+ ['Compare them', 'Read two side by side and see which words differ, verse by verse.'],
+ ['Mark it up', 'Highlight in colours you name, bookmark, and draw notes on the page.'],
+ ['Reading plans', 'Daily readings that open straight into the text, with streaks and reminders.'],
+ ],
+ },
+ {
+ title: 'Study',
+ points: [
+ ['Chunk a passage', 'Break it into sections and write Observe, Interpret and Apply notes.'],
+ ['Original languages', 'Greek and Hebrew word studies with pronunciation, from the interlinear.'],
+ ['Beside the text', 'Commentary, cross-references, and people and places for each chunk.'],
+ ],
+ },
+ {
+ title: 'Teach',
+ points: [
+ ['A real outline', 'Main points and sub-points for the whole study and for each chunk, with notes, verse text and minutes.'],
+ ['Scripts', 'Turn your notes into a script for a lesson, episode or sermon.'],
+ ['Share it', 'Word, PDF and Markdown files, or a read-only link anyone can open.'],
+ ],
+ },
+ {
+ title: 'Everywhere',
+ points: [
+ ['Your devices', 'Studies, marks and plans sync to your account, and nothing is overwritten silently.'],
+ ['Offline and backup', 'Save books to read without a connection, and download everything as one file.'],
+ ['Private', 'Your studies stay in your account, with optional two-factor sign-in.'],
+ ],
+ },
+];
+
+// Small, decorative pictures of two real views, so you can see what you're signing in to.
+function OutlinePreview() {
+ const row = (label, text, depth = 0, tag = '') => (
+
+ {label}
+
+ {tag && {tag}}
+ {text}
+
+
+ );
+ return (
+
+
Outline
+ {row('I.', 'Greeting and the gospel hope')}
+ {row('A.', 'Servant of God', 1, 'Illustration')}
+ {row('B.', 'Apostle of Jesus Christ', 1)}
+ {row('II.', 'Appoint elders in every town')}
+ {row('A.', 'Blameless and faithful', 1, 'Application')}
+
{authMfaUseBackup
? 'Enter one of your saved backup codes.'
@@ -109,83 +192,107 @@ export default function AuthPage() {
}
return (
-
-
- {/* Branding / feature panel — hidden on small screens to keep the form front and center there */}
-
-
Bible Study Project
-
- Deep Bible study, one chunk at a time.
-
-
- Read Scripture in six translations, follow a reading plan into the text, and study a passage one chunk
- at a time with commentary, word studies, and cross-references beside it.
-
-
-
Read in BSB, ESV, NLT, CSB, NASB, or NET, with audio and search
-
Reading plans that open straight into the Reader
-
Observe, Interpret, Apply notes with Greek & Hebrew word studies
-
Turn your notes into a script, then export or share
-
Your studies stay private to your account, with optional 2FA
-
-
- "Make every effort to present yourself approved to God, an unashamed workman who accurately
- handles the word of truth."
-
-
+
+
+
+
+ Bible Study Project
+
+
+
+
+
+ {/* Sign in / create account. First on a phone; on a wide screen it stays in view beside the tour. */}
+
+
+
+
{authMode === 'login' ? 'Sign in' : 'Create an account'}
+
+ {authMode === 'login' ? 'Pick up where you left off.' : 'Free to start. Your studies stay in your account.'}
+
+
+
+
+
+ “Make every effort to present yourself approved to God, an unashamed workman who accurately handles the word of truth.”
+
+
+
- {/* Sign in / register form */}
-
-
-
Bible Study Project
-
- {authMode === 'login' ? 'Sign in' : 'Create an account'}
+ {/* What the app does */}
+
+
Bible study, start to finish
+
+ Read it. Study it. Teach it.
-
-
-
+
+ A place to read Scripture in six translations, work through a passage one chunk at a time, and turn what you learn into an outline,
+ a script, and something you can share.
+