diff --git a/.planning/phases/02-inline-quick-add-ui/02-RESEARCH.md b/.planning/phases/02-inline-quick-add-ui/02-RESEARCH.md
new file mode 100644
index 0000000..bb8cf31
--- /dev/null
+++ b/.planning/phases/02-inline-quick-add-ui/02-RESEARCH.md
@@ -0,0 +1,360 @@
+# Phase 2: Inline Quick-Add UI - Research
+
+**Researched:** 2026-02-13
+**Domain:** Alpine.js inline AJAX forms with Laravel backend
+**Confidence:** HIGH
+
+## Summary
+
+Phase 2 adds inline note quick-add UI to the existing member list page. The backend API from Phase 1 is complete and ready. Research confirms Alpine.js 3.4 (already in package.json) provides all required functionality for inline state management, AJAX form submission, and reactive UI updates without additional dependencies.
+
+**Key findings:**
+- Alpine.js core handles all requirements (x-data state, @submit.prevent, x-show/x-if directives)
+- Alpine AJAX plugin exists but NOT needed - standard axios (already configured) is simpler and sufficient
+- Laravel's CSRF token already in meta tag, axios auto-includes it via bootstrap.js
+- Tailwind badge patterns already established in codebase (see Member model badge accessor)
+- Pagination works with Alpine state - each row has independent x-data scope
+- No Alpine.js Persist plugin needed - inline forms are ephemeral (state resets on page load by design)
+
+**Primary recommendation:** Use vanilla Alpine.js 3.4 + axios for AJAX, no additional plugins. Follow existing codebase patterns for badges and dark mode.
+
+## Standard Stack
+
+### Core
+| Library | Version | Purpose | Why Standard |
+|---------|---------|---------|--------------|
+| Alpine.js | 3.4.2 | Reactive state management, DOM manipulation | Already in project, lightweight (15KB), perfect for inline forms |
+| Axios | 1.6.4 | AJAX requests with CSRF protection | Already configured in bootstrap.js, auto-includes Laravel CSRF token |
+| Tailwind CSS | 3.1.0 | Styling with dark mode support | Project standard, darkMode: 'class' configured |
+| Laravel Blade | - | Server-side templating | Project standard for admin UI |
+
+### Supporting
+| Library | Version | Purpose | When to Use |
+|---------|---------|---------|-------------|
+| Laravel Pagination | - | Multi-page member list | Already implemented, works with Alpine state |
+| Laravel Validation | - | Server-side validation | Returns 422 JSON errors for AJAX requests |
+
+### Alternatives Considered
+| Instead of | Could Use | Tradeoff |
+|------------|-----------|----------|
+| Axios | Alpine AJAX plugin | Alpine AJAX is HTML-response-oriented (replaces DOM chunks); our API returns JSON. Axios is simpler for JSON APIs. |
+| Alpine.js | Vue.js | Vue is overkill for inline forms; Alpine is already in stack and sufficient |
+| Inline forms | Modal dialog | Modal requires navigation away from list context; inline keeps admin in flow |
+
+**Installation:**
+No new packages needed. All dependencies already in package.json.
+
+## Architecture Patterns
+
+### Recommended Project Structure
+```
+resources/views/admin/members/
+├── index.blade.php # Member list with inline forms
+└── _note-form.blade.php # (optional) Blade partial for note form
+```
+
+### Pattern 1: Per-Row Alpine Component
+**What:** Each table row has independent x-data scope for its note form
+**When to use:** Inline forms where each row needs independent state (open/closed, loading, errors)
+**Example:**
+```html
+@foreach ($members as $member)
+
+
{{ $member->full_name }}
+
+
+
+ 備忘錄
+
+
+
+
+
+
+
+
+
+@endforeach
+```
+
+### Pattern 2: Alpine Method for AJAX Submission
+**What:** x-data method handles form submission, loading state, success/error responses
+**When to use:** AJAX form submission with loading state and error display
+**Example:**
+```javascript
+x-data="{
+ async submitNote() {
+ this.isSubmitting = true;
+ this.errors = {};
+
+ try {
+ const response = await axios.post(
+ '/admin/members/{{ $member->id }}/notes',
+ { content: this.noteContent }
+ );
+
+ // Success: update badge count, clear form, close form
+ this.noteCount++;
+ this.noteContent = '';
+ this.noteFormOpen = false;
+ } catch (error) {
+ if (error.response?.status === 422) {
+ // Validation errors
+ this.errors = error.response.data.errors || {};
+ }
+ } finally {
+ this.isSubmitting = false;
+ }
+ }
+}"
+```
+**Source:** Alpine.js x-data methods documentation (https://github.com/alpinejs/alpine/blob/main/packages/docs/src/en/directives/data.md)
+
+### Pattern 3: Badge Component with Dark Mode
+**What:** Reusable Tailwind classes for count badges with dark mode support
+**When to use:** Displaying counts inline with text (e.g., "3 備忘錄")
+**Example:**
+```html
+
+
+ 備忘錄
+
+```
+**Source:** Member.php line 269 (existing badge pattern in codebase)
+
+### Pattern 4: Error Display Below Form Field
+**What:** Conditionally show validation errors in Traditional Chinese below textarea
+**When to use:** Laravel 422 validation errors from AJAX response
+**Example:**
+```html
+
+
+```
+
+### Anti-Patterns to Avoid
+- **Global Alpine state across pagination:** Don't use Alpine.store() or x-init to share state across pages. Each page load resets state (by design).
+- **Disabling button without :disabled binding:** Always use `:disabled="isSubmitting"` to prevent double-submit.
+- **Forgetting dark mode classes:** Every light color needs a `dark:` equivalent (see existing badge patterns).
+- **Not clearing form on success:** Always reset `noteContent = ''` and `noteFormOpen = false` after successful submission.
+
+## Don't Hand-Roll
+
+| Problem | Don't Build | Use Instead | Why |
+|---------|-------------|-------------|-----|
+| CSRF protection | Custom token injection | Axios + Laravel bootstrap.js | Already configured, auto-includes X-CSRF-TOKEN header from meta tag |
+| JSON error parsing | Custom 422 handler | `error.response.data.errors` | Laravel standardizes error structure in 422 responses |
+| Loading spinners | Custom CSS animations | Tailwind + `:disabled` state | Tailwind provides `opacity-50 cursor-not-allowed` via disabled state |
+| Badge styling | Custom badge component | Existing Member badge pattern | Already dark-mode compatible, proven in production |
+
+**Key insight:** Alpine.js + axios + Tailwind provide 90% of inline form functionality. The remaining 10% (business logic like "increment count on success") is trivial custom code. Don't introduce new dependencies.
+
+## Common Pitfalls
+
+### Pitfall 1: Forgetting CSRF Token in Axios Requests
+**What goes wrong:** POST requests return 419 error (CSRF token mismatch)
+**Why it happens:** Axios auto-includes token, but only if bootstrap.js is imported and meta tag exists
+**How to avoid:** Verify `resources/js/app.js` imports `./bootstrap.js` and `layouts/app.blade.php` has ``
+**Warning signs:** 419 errors in browser network tab for POST requests
+**Resolution:** Already configured correctly in codebase (bootstrap.js line 10, app.blade.php line 6)
+
+### Pitfall 2: N+1 Query for Note Counts
+**What goes wrong:** Database query per member to count notes (15 members = 15 extra queries)
+**Why it happens:** Accessing `$member->notes()->count()` in Blade instead of using withCount
+**How to avoid:** Controller must use `->withCount('notes')` (already implemented in AdminMemberController line 18)
+**Warning signs:** Laravel Debugbar shows N+1 queries
+**Resolution:** Already prevented in Phase 1
+
+### Pitfall 3: Alpine State Lost on Pagination
+**What goes wrong:** Admin opens note form on page 1, navigates to page 2, returns to page 1 → form is closed
+**Why it happens:** Pagination triggers full page reload; Alpine state is JavaScript, not persisted
+**How to avoid:** Accept this as expected behavior. Inline forms are ephemeral. Don't use Alpine.persist() for this.
+**Warning signs:** User confusion if they expect form state to persist
+**Resolution:** Document as expected behavior; users complete or abandon inline forms on same page
+
+### Pitfall 4: Dark Mode Colors Missing
+**What goes wrong:** UI looks broken in dark mode (white text on white background, invisible badges)
+**Why it happens:** Forgetting `dark:` prefix for Tailwind classes
+**How to avoid:** Every light color class needs a dark equivalent. Copy pattern from existing badges (Member.php line 269-272)
+**Warning signs:** White/invisible elements in dark mode
+**Example:**
+```html
+
+Badge
+
+
+Badge
+```
+
+### Pitfall 5: Not Preventing Form Submit Default
+**What goes wrong:** Form submits to server (full page reload) instead of AJAX
+**Why it happens:** Missing `.prevent` modifier on `@submit`
+**How to avoid:** Always use `@submit.prevent` for AJAX forms
+**Warning signs:** Page reloads on form submit instead of AJAX request
+**Example:**
+```html
+
+