diff --git a/docs/superpowers/plans/2026-07-17-birth-date-picker.md b/docs/superpowers/plans/2026-07-17-birth-date-picker.md
new file mode 100644
index 00000000..ddee222a
--- /dev/null
+++ b/docs/superpowers/plans/2026-07-17-birth-date-picker.md
@@ -0,0 +1,439 @@
+# Birth Date Picker Implementation Plan
+
+> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
+
+**Goal:** Replace the browser-native birth-date input with the user-supplied shadcn Popover + Calendar Date Picker while preserving the existing `YYYY-MM-DD` profile contract.
+
+**Architecture:** Add the official shadcn Base Nova `Calendar` and `Popover` primitives, then compose them in a focused `BirthDatePicker` client component. Keep local-calendar parsing and serialization beside the existing birth-time draft model so every consumer receives the same date-only string without UTC shifts.
+
+**Tech Stack:** Next.js 16, React 19, TypeScript, shadcn Base Nova, Base UI, React DayPicker, date-fns, Lucide, Node test runner.
+
+## Global Constraints
+
+- The closed control follows the exact shadcn composition supplied by the user: `PopoverTrigger render={ }` plus `PopoverContent` containing a single-select `Calendar`.
+- Date values remain `YYYY-MM-DD`; the API payload and database schema do not change.
+- The selectable range is `1900-01-01` through the user's current local date; future dates are disabled.
+- The calendar uses Chinese locale with month and year dropdowns, newest years first.
+- Confirmed birth-time profiles keep the date trigger disabled.
+- Use only existing `frontend/DESIGN.md` tokens and shadcn semantic tokens; add no raw colors.
+- Preserve 44px targets, keyboard operation, focus visibility, and collision-safe popover positioning.
+
+---
+
+### Task 1: Date-only value adapter
+
+**Files:**
+- Modify: `frontend/package.json`
+- Modify: `frontend/package-lock.json`
+- Modify: `frontend/src/lib/birth-time-intake-model.ts`
+- Test: `frontend/tests/birth-time-intake.test.ts`
+
+**Interfaces:**
+- Produces: `parseBirthDate(value: string): Date | undefined`
+- Produces: `formatBirthDate(value: Date): string`
+- Consumes: date-fns `parse`, `format`, and `isValid`
+
+- [ ] **Step 1: Add failing date-only behavior tests**
+
+Extend the existing import and append these Given/When/Then tests:
+
+```ts
+import {
+ assistantIntentCopy,
+ birthTimePersistenceValues,
+ describeBirthTimeDraft,
+ formatBirthDate,
+ isBirthTimeDraftReady,
+ parseBirthDate,
+ type BirthTimeDraft,
+} from "../src/lib/birth-time-intake-model.ts";
+
+test("birth date parsing preserves the local calendar day", () => {
+ const parsed = parseBirthDate("1993-04-17");
+
+ assert.equal(parsed?.getFullYear(), 1993);
+ assert.equal(parsed?.getMonth(), 3);
+ assert.equal(parsed?.getDate(), 17);
+});
+
+test("birth date values round trip leap days and reject invalid input", () => {
+ const leapDay = parseBirthDate("2000-02-29");
+
+ assert.equal(leapDay === undefined ? undefined : formatBirthDate(leapDay), "2000-02-29");
+ assert.equal(parseBirthDate(""), undefined);
+ assert.equal(parseBirthDate("2001-02-29"), undefined);
+});
+```
+
+- [ ] **Step 2: Run the tests in a negative UTC offset and confirm red**
+
+Run:
+
+```bash
+TZ=America/Los_Angeles node --test tests/birth-time-intake.test.ts
+```
+
+Expected: FAIL because `parseBirthDate` and `formatBirthDate` are not exported yet.
+
+- [ ] **Step 3: Install date-fns as a direct dependency**
+
+Run:
+
+```bash
+npm install date-fns
+```
+
+Expected: `package.json` and `package-lock.json` declare `date-fns` directly.
+
+- [ ] **Step 4: Implement strict local-calendar parsing and formatting**
+
+Add to `birth-time-intake-model.ts`:
+
+```ts
+import { format, isValid, parse } from "date-fns";
+
+const birthDatePattern = "yyyy-MM-dd";
+
+export function parseBirthDate(value: string): Date | undefined {
+ if (value === "") return undefined;
+ const parsed = parse(value, birthDatePattern, new Date(2000, 0, 1));
+ if (!isValid(parsed) || format(parsed, birthDatePattern) !== value) return undefined;
+ return parsed;
+}
+
+export function formatBirthDate(value: Date): string {
+ return format(value, birthDatePattern);
+}
+```
+
+- [ ] **Step 5: Run the adapter tests and confirm green**
+
+Run:
+
+```bash
+TZ=America/Los_Angeles node --test tests/birth-time-intake.test.ts
+```
+
+Expected: all birth-time intake tests PASS.
+
+- [ ] **Step 6: Commit the adapter**
+
+```bash
+git add frontend/package.json frontend/package-lock.json frontend/src/lib/birth-time-intake-model.ts frontend/tests/birth-time-intake.test.ts
+git commit -m "feat: add birth date value adapter"
+```
+
+---
+
+### Task 2: shadcn Calendar and Popover primitives
+
+**Files:**
+- Create: `frontend/src/components/ui/calendar.tsx`
+- Create: `frontend/src/components/ui/popover.tsx`
+- Create: `frontend/tests/birth-date-picker-contract.test.ts`
+- Modify: `frontend/package.json`
+- Modify: `frontend/package-lock.json`
+
+**Interfaces:**
+- Produces: `Calendar(props: React.ComponentProps)`
+- Produces: `Popover`, `PopoverTrigger`, and `PopoverContent`
+- Consumes: the existing `Button`, `buttonVariants`, `cn`, Lucide icons, and shadcn semantic CSS variables.
+
+- [ ] **Step 1: Write the failing primitive contract test**
+
+Create `tests/birth-date-picker-contract.test.ts`:
+
+```ts
+import assert from "node:assert/strict";
+import { existsSync, readFileSync } from "node:fs";
+import test from "node:test";
+
+const packageJson = readFileSync(new URL("../package.json", import.meta.url), "utf8");
+
+test("provides the shadcn calendar and popover primitives", () => {
+ assert.equal(existsSync(new URL("../src/components/ui/calendar.tsx", import.meta.url)), true);
+ assert.equal(existsSync(new URL("../src/components/ui/popover.tsx", import.meta.url)), true);
+ assert.match(packageJson, /"react-day-picker"/);
+});
+```
+
+- [ ] **Step 2: Run the primitive contract and confirm red**
+
+Run:
+
+```bash
+node --test tests/birth-date-picker-contract.test.ts
+```
+
+Expected: FAIL because the primitive files and direct dependency do not exist.
+
+- [ ] **Step 3: Add the official Base Nova primitives**
+
+Run from `frontend/`:
+
+```bash
+npx shadcn@latest add calendar popover --yes
+```
+
+Expected: shadcn reads the existing `components.json`, creates `src/components/ui/calendar.tsx` and `src/components/ui/popover.tsx`, reuses the existing `button.tsx`, and adds `react-day-picker` plus required direct dependencies without changing the project style preset.
+
+- [ ] **Step 4: Review generated changes before accepting them**
+
+Run:
+
+```bash
+git diff -- frontend/package.json frontend/package-lock.json frontend/src/components/ui/button.tsx frontend/src/components/ui/calendar.tsx frontend/src/components/ui/popover.tsx
+```
+
+Expected: no overwrite of the project's existing Button behavior; generated Calendar uses `react-day-picker`, and Popover uses the Base UI composition required by `style: base-nova`.
+
+- [ ] **Step 5: Run the primitive contract and TypeScript**
+
+Run:
+
+```bash
+node --test tests/birth-date-picker-contract.test.ts
+npx tsc --noEmit
+```
+
+Expected: both commands PASS.
+
+- [ ] **Step 6: Commit the primitives**
+
+```bash
+git add frontend/package.json frontend/package-lock.json frontend/src/components/ui/calendar.tsx frontend/src/components/ui/popover.tsx frontend/tests/birth-date-picker-contract.test.ts
+git commit -m "feat: add shadcn calendar primitives"
+```
+
+---
+
+### Task 3: BirthDatePicker composition and intake integration
+
+**Files:**
+- Create: `frontend/src/components/birth-date-picker.tsx`
+- Modify: `frontend/src/components/birth-time-intake.tsx`
+- Modify: `frontend/DESIGN.md`
+- Test: `frontend/tests/birth-date-picker-contract.test.ts`
+
+**Interfaces:**
+- Consumes: `parseBirthDate(value: string)` and `formatBirthDate(value: Date)` from Task 1.
+- Consumes: `Calendar`, `Popover`, `PopoverContent`, `PopoverTrigger`, `Button`, `CalendarIcon`, and `zhCN`.
+- Produces: `BirthDatePicker({ value, disabled, onChange })` where `value` is `YYYY-MM-DD` and `onChange` receives `YYYY-MM-DD`.
+
+- [ ] **Step 1: Document the Date Picker primitive before product code**
+
+Extend `frontend/DESIGN.md` Section 5 with:
+
+```md
+### Birth date picker
+
+- **Composition:** shadcn outline Button trigger, Base UI Popover, and a single-select React DayPicker Calendar.
+- **Range:** local dates from 1900-01-01 through today; future dates are disabled. Month and year dropdowns provide direct navigation, with newest years first.
+- **Value:** display Chinese long dates while emitting the existing `YYYY-MM-DD` profile value without UTC conversion.
+- **States:** empty, open, selected, focus-visible, disabled confirmed profile, and unavailable date.
+- **Accessibility:** visible label, explicit trigger naming, 44px targets, keyboard calendar navigation, focus return, and collision-safe popup positioning.
+```
+
+- [ ] **Step 2: Add a failing integration contract**
+
+Append to `birth-date-picker-contract.test.ts`:
+
+```ts
+const intake = readFileSync(new URL("../src/components/birth-time-intake.tsx", import.meta.url), "utf8");
+const pickerUrl = new URL("../src/components/birth-date-picker.tsx", import.meta.url);
+
+test("replaces the native birth date input with the shadcn date picker", () => {
+ assert.equal(existsSync(pickerUrl), true);
+ assert.doesNotMatch(intake, /type="date"/);
+ assert.match(intake, / void;
+};
+
+export function BirthDatePicker({ value, disabled, onChange }: BirthDatePickerProps) {
+ const labelId = useId();
+ const valueId = useId();
+ const [open, setOpen] = useState(false);
+ const selected = parseBirthDate(value);
+ const today = new Date();
+ today.setHours(0, 0, 0, 0);
+
+ return (
+
+
出生日期
+
+
+ }
+ >
+
+
+ {selected === undefined
+ ? "选择出生日期"
+ : format(selected, "PPP", { locale: zhCN })}
+
+
+
+ {
+ if (nextDate === undefined) return;
+ onChange(formatBirthDate(nextDate));
+ setOpen(false);
+ }}
+ />
+
+
+
+ );
+}
+```
+
+- [ ] **Step 5: Replace the native input without changing the patch contract**
+
+In `birth-time-intake.tsx`, import `BirthDatePicker` and replace the first `... ` block with:
+
+```tsx
+ onPatch({ date })}
+/>
+```
+
+- [ ] **Step 6: Run targeted contracts and TypeScript**
+
+Run:
+
+```bash
+node --test tests/birth-date-picker-contract.test.ts tests/birth-time-intake.test.ts
+npx tsc --noEmit
+```
+
+Expected: all tests and TypeScript PASS.
+
+- [ ] **Step 7: Commit the composition**
+
+```bash
+git add frontend/DESIGN.md frontend/src/components/birth-date-picker.tsx frontend/src/components/birth-time-intake.tsx frontend/tests/birth-date-picker-contract.test.ts
+git commit -m "feat: replace native birth date input"
+```
+
+---
+
+### Task 4: Real-surface QA and release gates
+
+**Files:**
+- Modify only if QA finds a defect in the files owned by Tasks 1–3.
+
+**Interfaces:**
+- Consumes: the complete BirthDatePicker flow.
+- Produces: browser evidence for selection, dismissal, range constraints, disabled state, and responsive layout.
+
+- [ ] **Step 1: Run the full automated gates**
+
+```bash
+npm test
+npm run lint
+npm run build
+```
+
+Expected: 100% test pass, zero lint errors, and a successful production build.
+
+- [ ] **Step 2: Measure every modified TypeScript file**
+
+```bash
+for file in src/lib/birth-time-intake-model.ts src/components/ui/calendar.tsx src/components/ui/popover.tsx src/components/birth-date-picker.tsx src/components/birth-time-intake.tsx tests/birth-date-picker-contract.test.ts tests/birth-time-intake.test.ts; do
+ awk '!/^[[:space:]]*$/ && !/^[[:space:]]*(\/\/|#|--)/' "$file" | wc -l
+done
+```
+
+Expected: each file is at or below 250 pure lines; no type escape hatch, parameter bloat, negative flag name, or unrelated helper is introduced.
+
+- [ ] **Step 3: Verify the real interaction at 375px, 768px, and 1280px**
+
+At each width, use the in-app browser to:
+
+1. Open the birth date picker and confirm the popup stays inside the viewport.
+2. Confirm Chinese month/year dropdowns and keyboard focus are visible.
+3. Navigate to 1993, select April 17, and confirm the trigger displays `1993年4月17日`.
+4. Confirm the popover closes and the controlled draft contains `1993-04-17`.
+5. Reopen and confirm April 1993 remains selected.
+6. Confirm dates after today and before 1900 are unavailable.
+7. Load a confirmed profile and confirm the trigger cannot open.
+8. Confirm no browser-native date chooser appears.
+
+Expected: every scenario passes with 44px targets, no clipping, no horizontal overflow, and no console errors.
+
+- [ ] **Step 4: Run the visual QA dual-oracle gate**
+
+Provide fresh 375px, 768px, and 1280px evidence to two independent read-only reviewers. One reviews design-system fidelity and visual polish; the other reviews interaction, accessibility, and regression risk.
+
+Expected: both reviewers return PASS. Fix any concrete issue and repeat only the affected evidence.
+
+- [ ] **Step 5: Final diff hygiene**
+
+```bash
+git diff --check
+git status --short
+```
+
+Expected: no whitespace errors; unrelated pre-existing user changes remain untouched and are named in the handoff.