diff --git a/.planning/phases/02-layout-and-brand-identity/02-RESEARCH.md b/.planning/phases/02-layout-and-brand-identity/02-RESEARCH.md
new file mode 100644
index 0000000..3ddab9f
--- /dev/null
+++ b/.planning/phases/02-layout-and-brand-identity/02-RESEARCH.md
@@ -0,0 +1,440 @@
+# Phase 2: Layout and Brand Identity - Research
+
+**Researched:** 2026-03-11
+**Domain:** React UI polish — auth screen branding, shadcn sidebar customization, Tailwind CSS v4 token-based styling
+**Confidence:** HIGH
+
+---
+
+
+## Phase Requirements
+
+| ID | Description | Research Support |
+|----|-------------|-----------------|
+| AUTH-01 | Login screen has a branded pastel gradient background (not plain white) | Replace `bg-background` wrapper with gradient using palette tokens; full-bleed gradient div wrapping the Card |
+| AUTH-02 | Login screen displays a styled app wordmark/logo treatment | Replace plain `CardTitle` text with a styled typographic mark using font weight + letter-spacing or gradient text fill |
+| AUTH-03 | Register screen matches login screen's branded look | Same structural changes as AUTH-01/02 applied to `RegisterPage.tsx` |
+| AUTH-04 | Auth form errors display with styled alert blocks and error icons | Install shadcn `alert` component; replace `
` with `` |
+| NAV-01 | Sidebar has a pastel background color distinct from the main content area | `--sidebar` token already set to `oklch(0.97 0.012 280)` in index.css; sidebar.tsx uses `bg-sidebar` natively — need to verify contrast reads in browser |
+| NAV-02 | Sidebar app name has a branded typographic treatment (not plain h2) | Replace `
` with richer markup: gradient text, tracking-wide, or a custom wordmark span |
+| NAV-03 | Active navigation item has a clearly visible color indicator using sidebar-primary token | `SidebarMenuButton` receives `isActive` prop — need to verify the `isActive` styling is visible; may need to override via className |
+| NAV-04 | Sidebar is collapsible via a toggle button for smaller screens | `SidebarTrigger` component already exists and exported from `ui/sidebar.tsx`; needs to be placed in the layout (currently missing from `AppLayout.tsx`) |
+
+
+---
+
+## Summary
+
+Phase 2 focuses on two surface areas: the auth screens (LoginPage, RegisterPage) and the app shell (AppLayout with the shadcn Sidebar). Both are self-contained files with minimal external dependencies, so the risk profile is low.
+
+The auth screens currently render a plain white Card centered on `bg-background`. Both pages use the same pattern and can be updated in parallel. The wordmark treatment (AUTH-02) is pure CSS — no new libraries needed, just a styled span with gradient text or tracked lettering using existing font/color tokens. The error display (AUTH-04) requires installing the shadcn `alert` component, which does not exist in `frontend/src/components/ui/` yet.
+
+The sidebar is already fully wired with the shadcn Sidebar primitives including the `--sidebar` CSS token (set to a distinct pastel `oklch(0.97 0.012 280)`). The main gap is NAV-04: `SidebarTrigger` is exported from `ui/sidebar.tsx` but is not rendered anywhere in `AppLayout.tsx`. The active-state indicator (NAV-03) is passed via `isActive` to `SidebarMenuButton` — the shadcn component has built-in active styling, but visual verification is needed since the token values may produce low contrast.
+
+**Primary recommendation:** Add shadcn Alert, apply gradient backgrounds to auth pages, add `SidebarTrigger` to AppLayout, and style the sidebar wordmark using CSS gradient text — all within existing files with no new routing or data fetching.
+
+---
+
+## Standard Stack
+
+### Core
+| Library | Version | Purpose | Why Standard |
+|---------|---------|---------|--------------|
+| shadcn/ui | 4.0.0 (installed) | UI component primitives via CSS variables | Project constraint: all UI via shadcn |
+| Tailwind CSS v4 | 4.2.1 (installed) | Utility classes; `@theme inline` exposes CSS vars as utilities | Project constraint |
+| lucide-react | 0.577.0 (installed) | Icon set including `AlertCircle`, `PanelLeft` | Already used throughout |
+| tw-animate-css | 1.4.0 (installed) | Animation utilities (for any transitions) | Out-of-scope alternative to Framer Motion |
+
+### Supporting
+| Library | Version | Purpose | When to Use |
+|---------|---------|---------|-------------|
+| class-variance-authority | 0.7.1 (installed) | Variant-based className logic | If extracting a reusable wordmark component |
+| clsx + tailwind-merge | installed | Conditional class merging | Standard in this project via `cn()` utility |
+
+### Not Needed for This Phase
+- No new npm installs required except `npx shadcn add alert` (fetches from registry, not a new npm dep)
+- No animation library installs — tw-animate-css handles any needed transitions
+- No routing changes
+
+**Installation:**
+```bash
+cd frontend && bunx --bun shadcn add alert
+```
+
+---
+
+## Architecture Patterns
+
+### Recommended File Scope
+
+All work is confined to these existing files:
+```
+frontend/src/
+├── pages/
+│ ├── LoginPage.tsx # AUTH-01, AUTH-02, AUTH-04
+│ └── RegisterPage.tsx # AUTH-03, AUTH-04
+├── components/
+│ ├── AppLayout.tsx # NAV-01, NAV-02, NAV-03, NAV-04
+│ └── ui/
+│ └── alert.tsx # NEW — installed via shadcn CLI
+└── index.css # No changes needed (tokens already set)
+```
+
+### Pattern 1: Gradient Background Wrapper (AUTH-01, AUTH-03)
+
+**What:** Replace the `bg-background` wrapper div on auth pages with a full-bleed gradient using existing palette tokens via inline style or Tailwind arbitrary values.
+
+**When to use:** Full-screen auth layouts where background IS the brand statement.
+
+**Example:**
+```tsx
+// Replace:
+
+
+// With (using CSS custom properties from index.css):
+
+```
+
+The gradient values should pull from the `palette.ts` saving/bill/investment light shades to stay within the established pastel family. Alternatively, add a dedicated `--auth-gradient` CSS custom property to `index.css` and reference it with a Tailwind arbitrary value `bg-[var(--auth-gradient)]`.
+
+### Pattern 2: Styled Wordmark (AUTH-02, NAV-02)
+
+**What:** A CSS gradient text treatment using `background-clip: text` — a standard technique with full browser support.
+
+**When to use:** When the app name needs brand identity treatment without SVG or image assets.
+
+**Example:**
+```tsx
+// Auth page wordmark (inside CardHeader, replacing or supplementing CardDescription)
+
+ Budget Dashboard
+
+
+// Sidebar app name (replacing
)
+
+ {t('app.title')}
+
+```
+
+The gradient values come from `--primary` (`oklch(0.50 0.12 260)`) shifted slightly in hue — no new tokens needed.
+
+### Pattern 3: shadcn Alert for Error Display (AUTH-04)
+
+**What:** Replace `
` with a structured Alert component.
+
+**When to use:** Form-level errors that need icon + message treatment.
+
+**Example:**
+```tsx
+// After: bunx --bun shadcn add alert
+import { Alert, AlertDescription } from '@/components/ui/alert'
+import { AlertCircle } from 'lucide-react'
+
+// In LoginPage and RegisterPage CardContent:
+{error && (
+
+
+ {error}
+
+)}
+```
+
+The shadcn `alert` component uses `--destructive` and `--destructive-foreground` tokens which are already defined in `index.css`.
+
+### Pattern 4: SidebarTrigger Placement (NAV-04)
+
+**What:** Add a visible toggle button that calls `toggleSidebar()` from the sidebar context. `SidebarTrigger` is already implemented and exported from `ui/sidebar.tsx`.
+
+**When to use:** The current `AppLayout.tsx` renders no trigger — collapsing is only possible via the keyboard shortcut `Ctrl+B`.
+
+**Example:**
+```tsx
+import { SidebarTrigger, /* existing imports */ } from '@/components/ui/sidebar'
+
+// In AppLayout, inside SidebarInset, add a header bar:
+
+
+
+
+ {children}
+
+```
+
+The `SidebarTrigger` renders a `PanelLeftIcon` button and calls `toggleSidebar()` internally. The current `collapsible` prop on `` defaults to `"offcanvas"`, which means on mobile it slides in as a Sheet and on desktop it shifts the content. This behavior is already fully implemented in `sidebar.tsx`.
+
+### Pattern 5: Active Nav Indicator (NAV-03)
+
+**What:** Verify and if needed, reinforce the `isActive` visual state on `SidebarMenuButton`.
+
+**Current state:** `AppLayout.tsx` already passes `isActive={location.pathname === item.path}` to `SidebarMenuButton`. The shadcn sidebar component applies `data-active` attribute and styles active items with `bg-sidebar-accent` and `text-sidebar-accent-foreground` by default.
+
+**Potential gap:** The current `--sidebar-accent` is `oklch(0.93 0.020 280)` against `--sidebar` of `oklch(0.97 0.012 280)` — that is a 4-point lightness difference. This may render as visually insufficient contrast. If so, the fix is to update `SidebarMenuButton` via a `className` override or adjust `--sidebar-accent` in index.css to use `--sidebar-primary` for the active state.
+
+**Verification test:** Render in browser, click nav items, confirm visible selection change. If not visible, apply:
+```tsx
+
+```
+
+### Anti-Patterns to Avoid
+
+- **Editing `ui/sidebar.tsx` directly:** Project constraint — customize via `className` props or CSS variable overrides only, never edit `src/components/ui/` source files.
+- **Hardcoded hex or oklch values in component files:** All colors must come from design tokens (`--primary`, `palette.ts`) or explicit inline style with values from the established token system.
+- **Adding `text-green-*` or `text-blue-*` raw classes for wordmark:** Use gradient text via inline style from token values, consistent with Phase 1 decisions.
+- **Adding `BrowserRouter` to auth pages:** Auth pages render outside the router (`App.tsx` renders them before `BrowserRouter`). Do not add routing-dependent hooks to `LoginPage` or `RegisterPage`.
+
+---
+
+## Don't Hand-Roll
+
+| Problem | Don't Build | Use Instead | Why |
+|---------|-------------|-------------|-----|
+| Styled error alerts | Custom div with icon + border CSS | `shadcn add alert` | Handles destructive variant, screen-reader role, proper token use |
+| Sidebar toggle button | Custom button + `useState` for open/closed | `SidebarTrigger` from `ui/sidebar` | Already wired to sidebar context; persists state in cookie |
+| Sidebar collapse state | Manual `useState` + CSS width transitions | `SidebarProvider` + `collapsible="icon"` | Full collapse behavior with keyboard shortcut already built |
+| Gradient text wordmark | SVG logo | CSS `background-clip: text` with inline style | No asset needed; uses existing tokens; responsive |
+
+**Key insight:** The shadcn Sidebar component is unusually complete — toggle, collapse, cookie persistence, mobile Sheet, and keyboard shortcut are all pre-built. The only missing piece is exposing `SidebarTrigger` in the rendered layout.
+
+---
+
+## Common Pitfalls
+
+### Pitfall 1: Alert Component Not Installed
+**What goes wrong:** Importing `@/components/ui/alert` fails at build time — the file does not exist yet.
+**Why it happens:** shadcn components are opt-in; `alert.tsx` is not in `frontend/src/components/ui/`.
+**How to avoid:** Run `bunx --bun shadcn add alert` before writing the import. Verify the file appears at `frontend/src/components/ui/alert.tsx`.
+**Warning signs:** TypeScript error `Cannot find module '@/components/ui/alert'`.
+
+### Pitfall 2: Sidebar Active State Low Contrast
+**What goes wrong:** The `isActive` indicator renders but is nearly invisible due to minimal lightness difference between `--sidebar` and `--sidebar-accent`.
+**Why it happens:** `--sidebar: oklch(0.97 0.012 280)` vs `--sidebar-accent: oklch(0.93 0.020 280)` — 4 lightness points difference in a low-chroma space.
+**How to avoid:** After implementing, visually verify active state. If insufficient, override with `data-[active=true]:bg-sidebar-primary data-[active=true]:text-sidebar-primary-foreground` in the `SidebarMenuButton` className. The `--sidebar-primary` token `oklch(0.50 0.12 260)` provides strong contrast.
+**Warning signs:** Clicking nav items produces no perceptible visual change.
+
+### Pitfall 3: Auth Gradient Feels Jarring
+**What goes wrong:** A high-saturation gradient makes the login screen feel loud rather than refined.
+**Why it happens:** Pastels require low chroma (0.02–0.06) at high lightness (0.92+). Using chart or header gradient values (medium shades at 0.88 lightness) will appear oversaturated as full-screen backgrounds.
+**How to avoid:** Use the `light` shades from `palette.ts` (lightness 0.95–0.97, chroma 0.03–0.04), not `medium` or `base` shades. Keep the gradient subtle — it should feel like tinted paper, not a colorful splash screen.
+**Warning signs:** The gradient overwhelms the card form element visually.
+
+### Pitfall 4: Wordmark Gradient Text Fallback
+**What goes wrong:** `WebkitTextFillColor: 'transparent'` with `backgroundClip: 'text'` renders as invisible text in some edge environments.
+**Why it happens:** The `-webkit-` prefix technique requires both properties to be set correctly.
+**How to avoid:** Always pair `WebkitBackgroundClip: 'text'`, `WebkitTextFillColor: 'transparent'`, AND `backgroundClip: 'text'`. The non-prefixed version is needed for Firefox compatibility.
+**Warning signs:** Wordmark text disappears or renders as black.
+
+### Pitfall 5: SidebarTrigger Outside SidebarProvider
+**What goes wrong:** `useSidebar()` throws "useSidebar must be used within a SidebarProvider."
+**Why it happens:** `SidebarTrigger` calls `useSidebar()` internally and will throw if placed outside ``.
+**How to avoid:** `SidebarTrigger` must be placed inside the `` tree — either inside ``, ``, or any other child. In `AppLayout.tsx` the current structure has both `` and `` inside ``, so placing it in `` is safe.
+
+### Pitfall 6: RegisterPage Missing from Auth Page Update
+**What goes wrong:** AUTH-03 is missed — RegisterPage still renders the plain white card after LoginPage is polished.
+**Why it happens:** The two pages are separate files with identical structure; easy to forget to update both.
+**How to avoid:** Treat AUTH-01/02/04 and AUTH-03 as one logical task that touches both files simultaneously. Register page should be a near-exact structural mirror of Login page.
+
+---
+
+## Code Examples
+
+### Full-Screen Gradient Auth Wrapper
+```tsx
+// Source: palette.ts light shades — keeping within established token system
+
+ )}
+
+```
+
+### SidebarTrigger in Layout Header
+```tsx
+// Source: ui/sidebar.tsx — SidebarTrigger already exported
+import { SidebarTrigger, /* ... */ } from '@/components/ui/sidebar'
+
+
+
+
+
+ {children}
+
+```
+
+### Active Nav Item Override (if default contrast insufficient)
+```tsx
+// Source: AppLayout.tsx — className addition only, no sidebar.tsx edit
+
+
+
+ {item.label}
+
+
+```
+
+---
+
+## State of the Art
+
+| Old Approach | Current Approach | When Changed | Impact |
+|--------------|------------------|--------------|--------|
+| Manual sidebar toggle with useState | shadcn Sidebar with SidebarProvider context | shadcn 2.x | Built-in toggle, cookie persistence, keyboard shortcut |
+| Tailwind v3 `theme.extend.colors` for custom tokens | CSS custom properties in `:root` + `@theme inline` in Tailwind v4 | Tailwind v4 | Tokens are pure CSS, not Tailwind-config-dependent |
+| `lucide-react` named imports | Same — no change | - | Lucide is still the icon standard for shadcn |
+
+**Deprecated/outdated:**
+- `bg-primary` Tailwind class approach for backgrounds: Now use CSS variable direct references (`var(--color-primary)`) or token-based inline styles for gradient backgrounds — Tailwind v4 exposes all custom properties as utilities.
+
+---
+
+## Open Questions
+
+1. **Active sidebar item contrast adequacy**
+ - What we know: `--sidebar-accent` is 4 lightness points from `--sidebar`; `SidebarMenuButton` uses `data-[active=true]:bg-sidebar-accent` by default
+ - What's unclear: Whether this is visually sufficient without browser testing
+ - Recommendation: Plan task includes a visual verification step; if insufficient, apply the `data-[active=true]:bg-sidebar-primary` className override
+
+2. **Wordmark: gradient text vs. font-weight/tracking only**
+ - What we know: Gradient text works cross-browser with correct CSS properties
+ - What's unclear: Whether the design intent is a text-gradient wordmark or just tracked/bold typography
+ - Recommendation: Gradient text is the stronger brand treatment; the planner should implement gradient text as the default and treat simpler alternatives as a fallback
+
+3. **SidebarInset main content padding**
+ - What we know: Current `AppLayout.tsx` has `{children}` with no padding
+ - What's unclear: Whether adding a header bar with `SidebarTrigger` requires padding adjustments to existing page components
+ - Recommendation: Add `p-4` to main only if pages do not already manage their own padding; inspect `DashboardPage` to confirm
+
+---
+
+## Validation Architecture
+
+### Test Framework
+| Property | Value |
+|----------|-------|
+| Framework | Vitest 4.0.18 + @testing-library/react 16.3.2 |
+| Config file | `frontend/vite.config.ts` (test section present) |
+| Quick run command | `cd frontend && bun vitest run --reporter=verbose` |
+| Full suite command | `cd frontend && bun vitest run` |
+
+### Phase Requirements → Test Map
+
+| Req ID | Behavior | Test Type | Automated Command | File Exists? |
+|--------|----------|-----------|-------------------|--------------|
+| AUTH-01 | Login page renders gradient background wrapper | unit | `bun vitest run src/pages/LoginPage.test.tsx` | No — Wave 0 |
+| AUTH-02 | Login page renders wordmark with gradient text style | unit | `bun vitest run src/pages/LoginPage.test.tsx` | No — Wave 0 |
+| AUTH-03 | Register page renders same gradient/wordmark as login | unit | `bun vitest run src/pages/RegisterPage.test.tsx` | No — Wave 0 |
+| AUTH-04 | Error string renders Alert with destructive variant | unit | `bun vitest run src/pages/LoginPage.test.tsx` | No — Wave 0 |
+| NAV-01 | Sidebar renders with bg-sidebar class | unit | `bun vitest run src/components/AppLayout.test.tsx` | No — Wave 0 |
+| NAV-02 | Sidebar header contains branded wordmark element | unit | `bun vitest run src/components/AppLayout.test.tsx` | No — Wave 0 |
+| NAV-03 | Active nav item receives data-active attribute | unit | `bun vitest run src/components/AppLayout.test.tsx` | No — Wave 0 |
+| NAV-04 | SidebarTrigger button is rendered in layout | unit | `bun vitest run src/components/AppLayout.test.tsx` | No — Wave 0 |
+
+**Note on visual requirements:** AUTH-01 (gradient background), AUTH-02 (wordmark appearance), NAV-01 (sidebar color distinction), and NAV-03 (visible color indicator) have a visual correctness dimension that unit tests cannot fully capture. Unit tests verify structural presence (element rendered, class present, inline style set); visual correctness requires browser verification. Plan tasks should include explicit browser check steps alongside automated tests.
+
+### Sampling Rate
+- **Per task commit:** `cd frontend && bun vitest run`
+- **Per wave merge:** `cd frontend && bun vitest run && cd frontend && bun run build`
+- **Phase gate:** Full suite green + production build green before `/gsd:verify-work`
+
+### Wave 0 Gaps
+- [ ] `frontend/src/pages/LoginPage.test.tsx` — covers AUTH-01, AUTH-02, AUTH-04
+- [ ] `frontend/src/pages/RegisterPage.test.tsx` — covers AUTH-03
+- [ ] `frontend/src/components/AppLayout.test.tsx` — covers NAV-01, NAV-02, NAV-03, NAV-04
+- [ ] `frontend/src/components/ui/alert.tsx` — must exist before any test imports it (install via shadcn CLI)
+
+---
+
+## Sources
+
+### Primary (HIGH confidence)
+- Direct file inspection: `frontend/src/components/AppLayout.tsx` — confirms SidebarTrigger not yet rendered
+- Direct file inspection: `frontend/src/pages/LoginPage.tsx` and `RegisterPage.tsx` — confirms plain `bg-background` wrapper and `
` error display
+- Direct file inspection: `frontend/src/components/ui/sidebar.tsx` — confirms SidebarTrigger exported, collapsible behavior fully implemented
+- Direct file inspection: `frontend/src/index.css` — confirms `--sidebar`, `--sidebar-primary`, `--sidebar-accent` token values
+- Direct file inspection: `frontend/src/lib/palette.ts` — confirms light/medium/base shades for gradient construction
+
+### Secondary (MEDIUM confidence)
+- shadcn/ui Alert component documentation pattern — standard destructive variant with AlertCircle icon is the established pattern for form error alerts in shadcn projects
+
+### Tertiary (LOW confidence)
+- None
+
+---
+
+## Metadata
+
+**Confidence breakdown:**
+- Standard stack: HIGH — all libraries verified as installed via package.json; no new npm deps needed beyond `bunx shadcn add alert`
+- Architecture: HIGH — all files inspected directly; changes are confined to known files with no new routing
+- Pitfalls: HIGH — active state contrast gap identified by reading actual token values; alert install gap confirmed by directory listing
+
+**Research date:** 2026-03-11
+**Valid until:** 2026-04-10 (stable shadcn/Tailwind ecosystem; 30-day window)