diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md
index fe4a8e7..789c6ce 100644
--- a/.planning/ROADMAP.md
+++ b/.planning/ROADMAP.md
@@ -99,7 +99,9 @@ Decimal phases appear between their surrounding integers in numeric order.
**Success Criteria** (what must be TRUE):
1. `/mobile/finance` adopts the new Card and typography scale — no horizontal overflow, spacing legible on small phones
2. Sections that previously rendered wide tables on phone widths now render as stacked lists (no new sections, no new data sources)
-**Plans**: TBD
+**Plans**: 2 plans
+- [ ] 05-01-PLAN.md — FinanceRow + FinanceSkeleton helper components (FIN-01, FIN-02)
+- [ ] 05-02-PLAN.md — Rewrite app/mobile/finance/page.tsx to KPI grid + stacked lists + shadcn Collapsibles (FIN-01, FIN-02)
**UI hint**: yes
### Phase 6: Analyzer Feed (NEW)
@@ -150,7 +152,7 @@ Phases execute in numeric order. Phase 2 unblocks Phases 3–7 (any order, paral
| 2. Mobile Shell + More Drawer | 0/TBD | Not started | - |
| 3. Dashboard Restyle | 0/2 | Not started | - |
| 4. Tickets Restyle | 0/3 | Not started | - |
-| 5. Finance Restyle | 0/TBD | Not started | - |
+| 5. Finance Restyle | 0/2 | Not started | - |
| 6. Analyzer Feed | 0/TBD | Not started | - |
| 7. Engagement Overview | 0/TBD | Not started | - |
| 8. Engagement User Profile | 0/TBD | Not started | - |
diff --git a/.planning/phases/05-finance-restyle/05-01-PLAN.md b/.planning/phases/05-finance-restyle/05-01-PLAN.md
new file mode 100644
index 0000000..63bb80b
--- /dev/null
+++ b/.planning/phases/05-finance-restyle/05-01-PLAN.md
@@ -0,0 +1,309 @@
+---
+phase: 05-finance-restyle
+plan: 01
+type: execute
+wave: 1
+depends_on: []
+files_modified:
+ - components/mobile/FinanceRow.tsx
+ - components/mobile/FinanceSkeleton.tsx
+autonomous: true
+requirements: [FIN-01, FIN-02]
+
+must_haves:
+ truths:
+ - "A 2-line stacked row (customer + amount on line 1, secondary metadata on line 2) is rendered by a reusable FinanceRow component, used by both invoice rows and payment rows in Plan 02"
+ - "An initial-load skeleton (4 KPI tiles + 1 aging row + 3 list-row skeletons) is rendered by a FinanceSkeleton component, ready for Plan 02 to drop into the loading branch"
+ artifacts:
+ - path: "components/mobile/FinanceRow.tsx"
+ provides: "Reusable 2-line stacked row for invoice and payment lists (D-06)"
+ exports: ["FinanceRow", "FinanceRowProps"]
+ - path: "components/mobile/FinanceSkeleton.tsx"
+ provides: "Initial-load skeleton matching final page layout (D-17)"
+ exports: ["FinanceSkeleton"]
+ key_links:
+ - from: "components/mobile/FinanceRow.tsx"
+ to: "components/ui/skeleton.tsx (NOT used here — pure presentational)"
+ via: "(no DB or fetch — pure presentational)"
+ pattern: "export function FinanceRow"
+ - from: "components/mobile/FinanceSkeleton.tsx"
+ to: "components/ui/skeleton.tsx"
+ via: "import { Skeleton }"
+ pattern: "from '@/components/ui/skeleton'"
+---
+
+
+Extract two pure-presentational helper components for the Phase 5 Finance page rewrite:
+`FinanceRow` (the 2-line stacked card row used by both invoice and payment lists per
+UI-SPEC §"Invoice / Payment List Rows"), and `FinanceSkeleton` (the initial-load
+placeholder per UI-SPEC §"Skeleton Loading State", D-17).
+
+Purpose: Plan 02 consumes both directly. Extracting first means Plan 02 can focus on
+composition (data plumbing, Collapsibles, KPI tiles, empty/error states) without
+inline duplication of the row JSX or a sprawling skeleton block. Both components are
+internal helpers — no public app-level export needed.
+
+Output: Two new files in `components/mobile/`, no changes anywhere else.
+
+
+
+@$HOME/.claude/get-shit-done/workflows/execute-plan.md
+@$HOME/.claude/get-shit-done/templates/summary.md
+
+
+
+@.planning/phases/05-finance-restyle/05-CONTEXT.md
+@.planning/phases/05-finance-restyle/05-UI-SPEC.md
+@.planning/phases/04-tickets-restyle/04-01-SUMMARY.md
+@CLAUDE.md
+
+
+
+
+From components/ui/skeleton.tsx:
+```typescript
+function Skeleton({ className, ...props }: React.ComponentProps<"div">): JSX.Element
+// Renders:
+export { Skeleton }
+```
+
+From components/mobile/KpiCardMobile.tsx (Phase 3 — reference only, NOT modified here):
+```typescript
+export type KpiTone = 'default' | 'attention';
+interface KpiCardMobileProps {
+ label: string;
+ value: number | string;
+ caption?: string;
+ tone?: KpiTone;
+}
+export function KpiCardMobile(props: KpiCardMobileProps): JSX.Element
+```
+
+Phase comment block convention (Phase 3/4 pattern — apply to both new files):
+```typescript
+/* ComponentName — phase 05 (FIN-NN).
+ * Purpose: one-line description.
+ * Props: ... */
+```
+
+
+
+
+
+
+ Task 1: Create FinanceRow component
+ components/mobile/FinanceRow.tsx
+
+ - .planning/phases/05-finance-restyle/05-UI-SPEC.md (sections: "Invoice / Payment List Rows — Stacked Card Rows [D-06]", "Typography", "Color")
+ - .planning/phases/05-finance-restyle/05-CONTEXT.md (D-06)
+ - components/mobile/KpiCardMobile.tsx (phase comment block convention)
+ - app/mobile/finance/page.tsx (lines 262–303, current invoice and payment row JSX — extract this shape)
+
+
+Create `components/mobile/FinanceRow.tsx` as a 'use client' file (matching `KpiCardMobile.tsx` pattern). Pure presentational — no fetch, no state.
+
+Open with the phase comment block per Phase 3/4 convention:
+
+```typescript
+'use client';
+
+/* FinanceRow — phase 05 (FIN-02).
+ * Purpose: 2-line stacked row used by Open Invoices and Recent Payments lists.
+ * Line 1: primary label (left, text-sm font-semibold truncate) + amount (right, text-sm font-semibold).
+ * Line 2: secondary metadata (left, text-xs text-muted-foreground) + optional date (right, text-xs text-muted-foreground).
+ * Pure presentational — parent owns the data.
+ * Props: see FinanceRowProps. */
+
+import { cn } from '@/lib/utils';
+```
+
+Export the props interface and component:
+
+```typescript
+export interface FinanceRowProps {
+ /** Primary label on line 1 (e.g., customer name) */
+ primary: string;
+ /** Amount string already formatted (e.g., "$1,234") — caller passes fmt$(value) */
+ amount: string;
+ /** Secondary metadata on line 2 (e.g., "#1234 · Due May 1" or just a date) */
+ secondary?: React.ReactNode;
+ /** Optional right-side date on line 2 (used by payment rows; invoice rows put date in `secondary`) */
+ rightSecondary?: React.ReactNode;
+ /** When true, amount renders in destructive color (overdue invoices). */
+ amountTone?: 'default' | 'destructive' | 'positive';
+}
+
+export function FinanceRow({ primary, amount, secondary, rightSecondary, amountTone = 'default' }: FinanceRowProps) {
+ const amountClass = cn(
+ 'text-sm font-semibold shrink-0',
+ amountTone === 'destructive' && 'text-destructive',
+ amountTone === 'positive' && 'text-emerald-600'
+ );
+ return (
+
+
+
{primary}
+ {secondary &&
{secondary}
}
+
+
+
{amount}
+ {rightSecondary &&
{rightSecondary}
}
+
+
+ );
+}
+```
+
+Hard rules from UI-SPEC (do NOT deviate):
+- Line 1 weight: `text-sm font-semibold` on BOTH primary and amount (per Typography table)
+- Line 2 weight: `text-xs font-normal text-muted-foreground` (per Typography table)
+- Container: `px-4 py-3 flex items-start justify-between gap-2`
+- Hover: `hover:bg-muted/50 transition-colors` (per Color §"Secondary surface")
+- Amount tones: only `text-destructive` (overdue) and `text-emerald-600` (payment positive) — NO other colors per D-06 "Invoice Status Colors" table
+- ONLY two font weights used: `font-normal` (default on `
`) and `font-semibold` — NO `font-medium` per D-03
+
+Do NOT add:
+- A priority left-stripe (Finance has no priority taxonomy per UI-SPEC §"Invoice / Payment List Rows")
+- A rounded card border on the row itself — the parent `divide-y` block owns the border (per UI-SPEC §"Container: divide-y block inside CollapsibleContent")
+- Any onClick / Link wrapping — rows are read-only per D-22
+
+Per CLAUDE.md: kebab-case applies to file names broadly; the established convention in `components/mobile/` (Phase 2/3/4) is PascalCase filenames matching the exported component (`KpiCardMobile.tsx`, `TicketFilterStrip.tsx`, etc.). Match that convention — file is `FinanceRow.tsx`.
+
+
+ npx tsc --noEmit --pretty 2>&1 | grep -E "FinanceRow|components/mobile/FinanceRow" || echo "OK: no FinanceRow type errors"
+
+
+ - File exists: `test -f components/mobile/FinanceRow.tsx`
+ - Exports: `grep -E "^export (interface FinanceRowProps|function FinanceRow)" components/mobile/FinanceRow.tsx | wc -l` returns `2`
+ - Phase comment block present: `grep -q "FinanceRow — phase 05" components/mobile/FinanceRow.tsx`
+ - Uses only the two locked font weights: `grep -E "font-medium|font-bold" components/mobile/FinanceRow.tsx` returns nothing (per D-03; `font-semibold` is allowed, `font-normal` is the default and need not appear literally)
+ - Hover class present: `grep -q "hover:bg-muted/50" components/mobile/FinanceRow.tsx`
+ - No priority stripe colors leaked from Phase 4: `grep -E "border-red-500|border-orange-400|border-amber-400|border-slate-300" components/mobile/FinanceRow.tsx` returns nothing
+ - No onClick / Link / router import: `grep -E "onClick|next/link|useRouter" components/mobile/FinanceRow.tsx` returns nothing
+ - Container padding matches D-11 / UI-SPEC: `grep -q "px-4 py-3" components/mobile/FinanceRow.tsx`
+ - TypeScript clean for this file: `npx tsc --noEmit --pretty 2>&1 | grep -c "components/mobile/FinanceRow.tsx"` returns `0`
+
+
+ `FinanceRow.tsx` exists, type-checks, exports `FinanceRow` + `FinanceRowProps`, renders the 2-line shape from UI-SPEC §"Invoice / Payment List Rows" with correct typography (text-sm font-semibold on line 1, text-xs text-muted-foreground on line 2), supports `amountTone` for destructive/positive variants, no priority stripe, no interactive handlers.
+
+
+
+
+ Task 2: Create FinanceSkeleton component
+ components/mobile/FinanceSkeleton.tsx
+
+ - .planning/phases/05-finance-restyle/05-UI-SPEC.md (section: "Skeleton Loading State [D-17]")
+ - .planning/phases/05-finance-restyle/05-CONTEXT.md (D-10, D-11, D-17)
+ - components/ui/skeleton.tsx (Skeleton primitive)
+
+
+Create `components/mobile/FinanceSkeleton.tsx` as a 'use client' file. Pure presentational placeholder matching the final page's spacing.
+
+Open with the phase comment block:
+
+```typescript
+'use client';
+
+/* FinanceSkeleton — phase 05 (FIN-01).
+ * Purpose: initial-load placeholder for /mobile/finance.
+ * Layout: 4 KPI tile skeletons (2×2 grid) + 1 aging row (3 cells) + 2 list-row skeleton blocks (3 rows each).
+ * Pure presentational — no props.
+ * UI-SPEC §"Skeleton Loading State [D-17]". */
+
+import { Skeleton } from '@/components/ui/skeleton';
+```
+
+Export a single `FinanceSkeleton` function component (no props):
+
+Top-level wrapper matches page container per D-10: `
`
+
+Three blocks inside, separated by the parent `space-y-6`:
+
+1. **KPI grid skeleton** — `grid grid-cols-2 gap-3` containing four `` blocks. (UI-SPEC §"Skeleton Loading State" item 1: "4 KPI tile skeletons: grid grid-cols-2 gap-3 px-4 pt-4 — each a Skeleton h-20 rounded-xl"; the parent provides px-4 already, drop the inner px-4/pt-4 to avoid double padding.)
+
+2. **Aging row skeleton** — `grid grid-cols-3 gap-2` containing three `` blocks. (UI-SPEC item 2.)
+
+3. **Two list-row blocks** — render the same 3-row skeleton block twice (one for invoices, one for payments). Each block is a `
+ ```
+ Note `p-3` on Refresh meets the 44 px touch target rule from UI-SPEC §"Spacing Scale".
+
+3. **D-17 — sync progress banner** (when `syncMsg` is non-null): render in `bg-muted text-xs text-muted-foreground rounded-xl px-3 py-2 mx-4` with a `RefreshCw h-3 w-3 animate-spin shrink-0` icon. Same copy table as before — the table in UI-SPEC §"Copywriting Contract" governs ("Starting sync…", "Syncing with QuickBooks…", "Sync already in progress — refreshing data…", "Refreshing data…"). When sync completes successfully (after `setSyncMsg(null)`) fire `toast.success("QuickBooks sync complete")` per UI-SPEC. When sync fails (HTTP non-OK that isn't 409, or thrown error in catch block) fire `toast.error("Sync failed — check QBO connection")` per UI-SPEC.
+
+4. **D-01, D-02 — KPI grid** (replaces the existing AR Hero gradient and the separate Collected MTD / Revenue YTD cards):
+ ```tsx
+
+
+
+
+
+
+ ```
+ The "Revenue YTD" card the existing page rendered separately is folded into the Paid MTD caption per D-02 / UI-SPEC Copywriting Contract row "Paid MTD caption". DELETE the separate AR Hero block (lines 132–151) and the Revenue KPIs grid (lines 197–213) entirely — they are replaced by the four `KpiCardMobile`s above.
+
+5. **D-08 — aging row** (visible only when `summary.overdue_balance > 0`) — locked colors from UI-SPEC §"Aging Bucket Status Colors":
+ ```tsx
+ {summary.overdue_balance > 0 && (
+
+
+
+ )}
+ ```
+ Note the 60+ uses `text-destructive` token, NOT a raw `text-red-*`. Replace the existing `text-yellow-600 bg-yellow-50 ...` and `text-red-600 bg-red-50 ...` classes (current lines 159–161) with the locked palette from the UI-SPEC table.
+
+6. **D-07 — Top AR by Customer stacked list** (visible only when `top_customers.length > 0`):
+ ```tsx
+ {top_customers.length > 0 && (
+
+
+
+ )}
+ ```
+ Per UI-SPEC §"Top Customers List" — same 2-line shape, but with the proportion bar on the right. This row diverges enough from FinanceRow that we keep it inline (FinanceRow has no proportion-bar slot — extending it for one consumer hurts more than it helps). The previous `text-sm font-medium` (line 181) becomes `text-sm font-semibold` per D-03.
+
+7. **D-09 — Monthly revenue stacked list, NO chart** (visible only when `monthly_revenue.length > 0`):
+ DELETE the entire bar-chart block (current lines 215–235 — flex/items-end/h-full visualization). Replace with:
+ ```tsx
+ {monthly_revenue.length > 0 && (
+
+
+
+ )}
+ ```
+ This is the DASH-04 precedent applied to Phase 5 — no recharts on mobile.
+
+8. **D-15, D-16 — Open Invoices Collapsible with shadcn primitive + restyled tab toggle**:
+ Replace the bare `