diff --git a/.impeccable.md b/.impeccable.md
new file mode 100644
index 0000000..603a0a5
--- /dev/null
+++ b/.impeccable.md
@@ -0,0 +1,29 @@
+## Design Context
+
+### Users
+FGA-track gemmology students and self-taught hobbyists working through identification curriculum. They sit with the article open during a focused study block (often evenings, often after-work fatigue). They want signal density without noise. Many will have a 10× loupe or a refractometer next to the screen — the screen is one instrument among many, not the centre of attention. A non-trivial fraction is colour-vision-impaired (gemmology disproportionately attracts people who train themselves around colour weakness).
+
+### Brand Personality
+**Scientifically clean and intriguing.** Three-word distillation: *precise, considered, alive*. The site is reference material that should feel handled — like a well-thumbed mineralogy textbook, the printed legend on a microscope eyepiece, or the engraved scale on a gemmologist's polariscope. Clean does not mean empty; intrigue does not mean decoration. The page must not feel dead.
+
+### Aesthetic Direction
+- **Typographic-led, not graphic-led.** Hierarchy comes from weight, size, leading, and a controlled accent palette — never from drop shadows, gradient fills, or icon decoration.
+- **Quiet accent, sharp rhythm.** The existing `crystal:` (sky-blue) palette is the single chromatic anchor. `gem:` tokens are reserved for mineral-data contexts. Slate is the body neutral.
+- **Instrument-label moments.** Where the interface acts (correct/wrong feedback, "Quick check" header, badges), it should read like an etched marking on a brass scale — confident, small, definite. Avoid SaaS-style coloured pills with rounded everything.
+- **Light-first, dark-fluent.** Light is the default reading surface; dark mode must be a true mirror, not a partial inversion.
+
+### Anti-references
+- **Not Duolingo or gamified-flashcard energy.** No bouncing, no streak fire, no celebration glitter.
+- **Not generic admin/SaaS.** No left-stripe-coloured callouts, no gradient hero text, no purple-to-blue accent gradients, no Inter, no Syne.
+- **Not a "lab tech demo."** No glowing neon-on-near-black, no monospace as decoration. Monospace is reserved for actual readouts (counters, codes, formulae).
+- **Not flat-card-grid templates.** Cards exist; they should not feel mass-produced.
+
+### Theme & Constraints
+- **Theme**: light + dark, both must be first-class. Dark is for late-evening study and must remain readable without reaching for a brightness slider.
+- **Stack**: Tailwind v3 with `crystal:` and `gem:` palette extensions in `tailwind.config.mjs`; dark mode via `class` strategy on ``.
+- **Accessibility**: WCAG 2.2 AA non-negotiable; the site was just remediated. Maintain ≥ 4.5:1 contrast on body text, ≥ 3:1 on large/non-text. Do not regress.
+- **Motion**: subtle, exponential easing (ease-out-quart/quint), under 200ms for state changes. Honour `prefers-reduced-motion`.
+- **No emoji** in product UI. Icons come from Lucide React when needed.
+
+### One-line aesthetic vow
+*Restrained, precise, with one moment per surface that makes the eye pause.*
diff --git a/src/components/quiz/study/LearnQuizWidget.test.tsx b/src/components/quiz/study/LearnQuizWidget.test.tsx
index ef98977..b4fd570 100644
--- a/src/components/quiz/study/LearnQuizWidget.test.tsx
+++ b/src/components/quiz/study/LearnQuizWidget.test.tsx
@@ -114,7 +114,7 @@ describe('LearnQuizWidget', () => {
render(
,
);
- expect(screen.getByText(/1 of 3/i)).toBeInTheDocument();
+ expect(screen.getByRole('complementary', { name: /question 1 of 3/i })).toBeInTheDocument();
});
it('renders answer options as radio buttons', () => {
@@ -197,7 +197,7 @@ describe('LearnQuizWidget', () => {
await answerQuestion(user, 'Cubic');
await user.click(screen.getByRole('button', { name: /next question/i }));
expect(screen.getByText(Q2.questionText)).toBeInTheDocument();
- expect(screen.getByText(/2 of 3/i)).toBeInTheDocument();
+ expect(screen.getByRole('complementary', { name: /question 2 of 3/i })).toBeInTheDocument();
});
// ── Q3 and completion ─────────────────────────────────────────────────────
@@ -244,7 +244,8 @@ describe('LearnQuizWidget', () => {
await user.click(screen.getByRole('button', { name: /continue to article/i }));
// 2 correct out of 3
- expect(screen.getByText(/2 of 3 correct/i)).toBeInTheDocument();
+ expect(screen.getByText(/pretest complete/i)).toBeInTheDocument();
+ expect(screen.getByText(/2\s*\/\s*3/)).toBeInTheDocument();
});
// ── Store integration ─────────────────────────────────────────────────────
@@ -295,7 +296,7 @@ describe('LearnQuizWidget', () => {
questions={[Q1, Q2, Q3, extraQ]}
/>,
);
- expect(screen.getByText(/1 of 3/i)).toBeInTheDocument();
+ expect(screen.getByRole('complementary', { name: /question 1 of 3/i })).toBeInTheDocument();
});
// ── a11y ──────────────────────────────────────────────────────────────────
diff --git a/src/components/quiz/study/LearnQuizWidget.tsx b/src/components/quiz/study/LearnQuizWidget.tsx
index 1e78c6e..037da85 100644
--- a/src/components/quiz/study/LearnQuizWidget.tsx
+++ b/src/components/quiz/study/LearnQuizWidget.tsx
@@ -18,11 +18,10 @@
*/
import { useState, useId } from 'react';
-import { BookOpen, ChevronRight, CheckCircle2 } from 'lucide-react';
+import { ChevronRight, CheckCircle2 } from 'lucide-react';
import type { StudyStore, Confidence } from '../../../lib/quiz/study-types';
import { Card, CardContent } from '../../ui/Card';
import { Button } from '../../ui/Button';
-import { Badge } from '../../ui/Badge';
import { cn } from '../../ui/cn';
import { ConfidenceTap } from './ConfidenceTap';
import { RationalePanel } from './RationalePanel';
@@ -152,16 +151,22 @@ export function LearnQuizWidget({
className="my-6"
aria-label="Pretest complete"
>
-
+
-
+
-
- Pretest complete — {correctCount} of {visibleQuestions.length} correct
+
The article below covers each of these topics in depth. Pay particular
attention to any questions you got wrong — the pretesting effect makes
the content that follows measurably stickier.
@@ -188,25 +193,34 @@ export function LearnQuizWidget({
return (