diff --git a/.planning/phases/15-dashboard-tablos/15-RESEARCH.md b/.planning/phases/15-dashboard-tablos/15-RESEARCH.md
new file mode 100644
index 0000000..cae8a11
--- /dev/null
+++ b/.planning/phases/15-dashboard-tablos/15-RESEARCH.md
@@ -0,0 +1,592 @@
+# Phase 15: Dashboard & Tablos - Research
+
+**Researched:** 2026-05-16
+**Domain:** Go templ layout shell + CSS porting + HTMX dashboard
+**Confidence:** HIGH
+
+## Summary
+
+Phase 15 replaces the current top-nav `Layout` with a sidebar-based `AppLayout` and reskins the tablo dashboard as a project-card grid. The go-backend (`go-backend/internal/web/views/dashboard_components.templ` + `go-backend/internal/web/ui/app.css`) is the canonical visual reference and source of CSS to port. All architecture decisions are locked in CONTEXT.md — this research confirms their technical feasibility and documents the exact files to touch, Go patterns to follow, and pitfalls to avoid.
+
+The primary work splits into three parallel tracks: (1) new `AppLayout` templ component in `backend/templates/`, (2) CSS extraction from `go-backend/internal/web/ui/app.css` into a new `backend/internal/web/ui/app.css`, and (3) restyled `TablosDashboard` + `TablosEmptyState` + `TabloCard` in `backend/templates/tablos.templ`. Handler changes are minimal — only the signature of `TablosDashboard` call sites changes.
+
+**Primary recommendation:** Port the CSS sections verbatim from go-backend's app.css (lines 455–743 cover all sidebar CSS; lines 894–1046 cover project-card CSS). Then create `AppLayout` following the `AuthLayout` pattern already established in Phase 14.
+
+---
+
+
+## User Constraints (from CONTEXT.md)
+
+### Locked Decisions
+
+#### App Layout Shell
+- **D-L01:** Create a new `AppLayout(title string, user *auth.User, csrfToken string, activePath string, tablos []sqlc.Tablo)` templ in `backend/templates/`. All authenticated dashboard pages use `AppLayout`. The old `Layout` templ is preserved but no longer used by dashboard pages (it becomes dead code, can be removed in a future cleanup).
+- **D-L02:** `AppLayout` uses the `.dashboard-shell` CSS grid (sidebar + main columns) from `go-backend/internal/web/ui/app.css`. Port `.dashboard-shell`, `.dashboard-sidebar`, `.sidebar-nav-shell`, all `.sidebar-*` CSS, `.sidebar-organization`, and `.dashboard-main` into `backend/internal/web/ui/app.css` (a new CSS file, not auth.css).
+- **D-L03:** Active nav item: `AppLayout` receives `activePath string`. A helper function (pure Go, not templ) returns `"is-active"` class when a nav item's href matches `activePath`. Handlers pass the current route path explicitly (e.g., `"/"`, `"/planning"`, `"/tablos"`).
+- **D-L04:** Scripts: `AppLayout` loads `htmx.min.js`, `sortable.min.js`, and `discussion-sse.js` — same as the current `Layout`. No change to script loading.
+
+#### Sidebar Nav Items
+- **D-N01:** Match go-backend nav item set: Dashboard (`/`), Tablos (`/` or tablo overview — same as Dashboard for now), Tasks, Planning (`/planning`), Chat, Files, Settings. Use go-backend's `dashboard_components.templ` icon SVGs verbatim.
+- **D-N02:** Routes that exist in the backend get real `href` values: Dashboard (`/`), Planning (`/planning`). Routes not yet built as standalone pages (Tasks, Files, Settings) render as visual nav items with no `href` attribute (display only, `cursor: default` on the item).
+- **D-N03:** Chat is per-tablo. In the sidebar nav, Chat links to `/` (dashboard) as a placeholder, or is rendered as a visual-only item like Tasks — Claude's discretion.
+- **D-N04:** Sidebar-projects section: shows the user's tablos as a list with a colored circle icon (derived from tablo color field) + truncated title. Each item links to `/tablos/{id}`. Section label: "Projects" (English, matching backend convention).
+
+#### Tablo Project Cards
+- **D-C01:** Tablo dashboard uses the `.project-grid` 3-column CSS grid layout from go-backend. Port `.project-grid`, `.project-card`, `.project-card-top`, and related CSS into the new `app.css`.
+- **D-C02:** Each project card shows (matching go-backend exactly): colored circle avatar (top-left) + title + edit icon button (top-right) + delete icon button (top-right) + creation date (bottom row).
+- **D-C03:** Color avatar: use `tablo.Color` field when set. When null/empty, show a default neutral/gray circle (no derived color, no initials). The circle renders via a small `` styled with background-color, matching go-backend's `.sidebar-project-icon` pattern.
+- **D-C04:** Edit and delete action icons use `@ui.IconButton(...)` from the design system (Phase 13 component). The HTMX attributes for edit (`hx-get=/tablos/{id}/edit`) and delete (`hx-delete=/tablos/{id}`) are preserved from the current `TabloCard` implementation.
+- **D-C05:** The "New tablo" button and inline create form slot (`#create-form-slot`) move to a section header above the grid, matching go-backend's `.overview-section-heading` pattern.
+
+#### Tablo Empty State
+- **D-E01:** When no tablos exist, use `@ui.EmptyState(ui.EmptyStateProps{...})` — the Phase 13 component. Replace the current raw HTML empty state in `TablosEmptyState()`. Title: "No tablos yet". Description: "Create your first tablo to get started." Action: "New tablo" button.
+
+#### Sidebar Footer (User/Account)
+- **D-F01:** Include a sidebar footer matching go-backend's `.sidebar-organization` section. Shows: avatar circle (first letter of email, colored background) + user email + a small account link. Logout is moved from the old top-nav into this footer area.
+- **D-F02:** The account/settings page doesn't exist yet, so the footer button/link only shows the email + logout button. No settings navigation for Phase 15. The `.organization-button` wraps a simple display element, not a dropdown.
+- **D-F03:** Port `.sidebar-organization`, `.organization-button`, `.organization-avatar`, `.organization-name`, `.organization-meta` CSS from go-backend into `app.css`.
+
+### Claude's Discretion
+- Exact set of SVG icons for each nav item (copy from go-backend `dashboard_components.templ` — no new icon design needed)
+- Whether the sidebar-projects section is a separate templ component or inline in `AppLayout` (prefer separate `SidebarProjectsSection` for testability)
+- Exact color for the neutral avatar fallback (use `--color-surface-muted` or similar design token)
+- Whether sidebar-footer avatar background color is derived from email hash or a fixed brand color
+- Chat nav item: visual-only placeholder or links to `/` dashboard
+
+### Deferred Ideas (OUT OF SCOPE)
+- Sidebar collapse toggle (JS interaction)
+- Mobile-responsive sidebar (hamburger menu, slide-in drawer)
+- Tablo color picker in the create/edit form
+- Tasks, Files, Settings as standalone pages
+
+
+---
+
+
+## Phase Requirements
+
+| ID | Description | Research Support |
+|----|-------------|------------------|
+| DASH-01 | Sidebar uses the go-backend sidebar design (brand section, nav items with icons, tablo list, user/account footer) | AppLayout + SidebarProjectsSection templ components; CSS ported from go-backend app.css lines 455–743 |
+| DASH-02 | Tablo list uses project-card layout with color accents, creation date, and action controls | TablosDashboard restyled with `.project-grid` + `.project-card`; CSS from app.css lines 894–1046 |
+| DASH-03 | Dashboard empty state uses the empty-state component | `ui.EmptyState` already implemented (Phase 13, `backend/internal/web/ui/empty_state.templ`), ready to use |
+
+
+---
+
+## Architectural Responsibility Map
+
+| Capability | Primary Tier | Secondary Tier | Rationale |
+|------------|-------------|----------------|-----------|
+| App layout shell (sidebar + main) | Backend (Go templ) | — | Server-rendered HTML; AppLayout is a templ component |
+| Sidebar CSS (`.dashboard-shell`, `.sidebar-nav-shell`, etc.) | Static (CSS file) | — | `backend/internal/web/ui/app.css` serves via `/static/tailwind.css` build |
+| Active nav item computation | Backend (pure Go helper) | — | `isActivePath(activePath, href)` pure function, no UI logic |
+| Tablo project card grid | Backend (Go templ) | — | `TablosDashboard` restyled; CSS ported |
+| Sidebar tablo list (SidebarProjectsSection) | Backend (Go templ) | — | Receives `[]sqlc.Tablo` from `AppLayout` |
+| Color avatar rendering | Backend (Go templ) | — | Inline `style=` attribute with `tablo.Color` or fallback token |
+| Empty state | Backend (Go templ) | — | `@ui.EmptyState(...)` already in Phase 13 |
+| HTMX interactions (edit, delete) | Browser (HTMX attributes) | Backend (handler) | Attributes remain unchanged from current `TabloCard` |
+
+---
+
+## Standard Stack
+
+### Core (already in backend — no new installs)
+
+| Library | Version | Purpose | Why Standard |
+|---------|---------|---------|--------------|
+| templ | see go.mod | HTML templating for Go | Project standard; all templates use it |
+| sqlc | see go.mod | Type-safe SQL | Project standard; `sqlc.Tablo` is the data type |
+| chi | see go.mod | HTTP routing | Project standard |
+| gorilla/csrf | see go.mod | CSRF token injection | Project standard; `csrf.Token(r)` in every handler |
+
+### Supporting (CSS tokens)
+
+| Asset | Location | Purpose | When to Use |
+|-------|----------|---------|-------------|
+| `backend/internal/web/ui/base.css` | Existing | Design tokens (`var(--...)`) | All new CSS must use these tokens, not raw colors |
+| `go-backend/internal/web/ui/app.css` | Reference | Source CSS to port verbatim | Extract only sidebar + project-card sections |
+| `backend/static/logo_dark.png` | Existing | Sidebar brand logo | Already copied in Phase 14 |
+
+**Version verification:** No new packages needed — all required tools are already in the go.mod. [VERIFIED: codebase grep]
+
+---
+
+## Architecture Patterns
+
+### System Architecture Diagram
+
+```
+HTTP GET /
+ |
+ v
+TablosListHandler
+ |-- Queries.ListTablosByUserWithDiscussionUnread(userID) --> []ListRow
+ |-- Queries.ListTablosByUser(userID) --> []sqlc.Tablo (for sidebar)
+ v
+templates.TablosDashboard(user, csrfToken, activePath="/", tablos, cardViews)
+ |
+ v
+AppLayout(title, user, csrfToken, activePath, tablos)
+ |-- DashboardSidebar
+ | |-- brand section (logo_dark.png + "XTablo")
+ | |-- SidebarNavItems (panels/tasks/layers/planning/chat/files)
+ | |-- SidebarProjectsSection(tablos)
+ | |-- SidebarOrganizationFooter(user.Email)
+ v
+ |--
+ { children... }
+ |-- section-heading (New tablo button + #create-form-slot)
+ |-- #tablos-list
+ |-- if empty: @ui.EmptyState(...)
+ |-- else: for each card: @TabloProjectCard(card, csrfToken)
+```
+
+### Recommended Project Structure
+
+```
+backend/
+├── templates/
+│ ├── app_layout.templ # NEW — AppLayout + SidebarProjectsSection + SidebarNavItem
+│ ├── app_layout_helpers.go # NEW — isActivePath, sidebarNavItemClass, sidebarNavItemID
+│ ├── tablos.templ # MODIFIED — TablosDashboard, TablosEmptyState, TabloCard
+│ ├── layout.templ # UNCHANGED (preserved as dead code)
+│ └── ...
+├── internal/web/ui/
+│ ├── app.css # NEW — ported from go-backend app.css (sidebar + project-card sections only)
+│ ├── base.css # UNCHANGED — design tokens used by app.css
+│ └── ...
+├── internal/web/
+│ ├── handlers_tablos.go # MODIFIED — TablosDashboard call adds activePath + tablos params
+│ ├── handlers_planning.go # MODIFIED — Planning page switches to AppLayout
+│ └── ...
+```
+
+### Pattern 1: AppLayout Templ (follow AuthLayout pattern)
+
+The AuthLayout in Phase 14 is the established pattern for a top-level HTML shell. `AppLayout` follows the same convention — package `templates`, explicit param list, `{ children... }` slot.
+
+```go
+// Source: backend/templates/auth_layout.templ (Phase 14 pattern)
+// backend/templates/app_layout.templ
+package templates
+
+import (
+ "backend/internal/auth"
+ "backend/internal/db/sqlc"
+)
+
+templ AppLayout(title string, user *auth.User, csrfToken string, activePath string, tablos []sqlc.Tablo) {
+
+
+
+
+
+ { title }
+
+
+
+
+
+}
+```
+
+[ASSUMED: exact HTMX attribute targets need to be verified against current TabloDeleteButtonFragment and edit handler behavior before writing the final template]
+
+### Pattern 5: Handler Changes — TablosDashboard Call Sites
+
+Currently, `TablosDashboard(user, csrfToken, tabloCards)` is called from `TablosListHandler`. Per D-L01, the new signature is `TablosDashboard(user, csrfToken, activePath, tablos, cardViews)` where `tablos []sqlc.Tablo` feeds the sidebar and `cardViews []TabloCardView` feeds the grid.
+
+The tablos list is **already fetched** by `TablosListHandler` — it calls `ListTablosByUserWithDiscussionUnread` which returns `[]ListRow`. We need a second lightweight query `ListTablosByUser(userID)` → `[]sqlc.Tablo` for the sidebar (or derive from existing rows).
+
+```go
+// Source: backend/internal/db/sqlc/tablos.sql.go line 88–100
+// ListTablosByUser already exists:
+// func (q *Queries) ListTablosByUser(ctx context.Context, userID uuid.UUID) ([]Tablo, error)
+```
+
+[VERIFIED: grep in backend/internal/db/sqlc/tablos.sql.go]
+
+**Simpler option:** Derive `[]sqlc.Tablo` from the already-fetched unread rows without a second DB query:
+
+```go
+// In TabloCardsFromUnreadRows, each row already contains all Tablo fields.
+// We can produce []sqlc.Tablo by extracting the Tablo field from each TabloCardView.
+tablos := make([]sqlc.Tablo, 0, len(cardViews))
+for _, cv := range cardViews {
+ tablos = append(tablos, cv.Tablo)
+}
+```
+
+This avoids an extra query. [ASSUMED: confirm this approach in planning since it avoids a new DB call but relies on already-fetched data order]
+
+### Pattern 6: Planning and Other Layout Callers
+
+Currently `planning.templ` calls `@Layout(...)`. Per D-L01, it must switch to `@AppLayout(...)`. Planning handler (`PlanningPageHandler`) does not currently fetch tablos. It will need to fetch `[]sqlc.Tablo` for the sidebar. The `PlanningDeps` struct already has `Queries *sqlc.Queries`, so `deps.Queries.ListTablosByUser(ctx, user.ID)` is available.
+
+Pages that call `@Layout(...)` today and need to switch:
+- `tablos.templ`: `TablosDashboard`, `TabloDetailPage`, `TabloNotFoundPage`
+- `planning.templ`: `PlanningPage`
+- `account_providers.templ`: `AccountProvidersPage` (uses `@Layout`)
+
+[VERIFIED: grep of Layout( in backend/templates/*.templ]
+
+### Anti-Patterns to Avoid
+
+- **Re-using `.sidebar-project-icon` as a colored SVG wrapper:** In go-backend it wraps an SVG icon. In this phase, per D-C03, it should wrap a plain colored `` (no SVG) — the color is the only visual indicator.
+- **Nesting OOB elements inside AppLayout:** OOB HTMX fragments (`hx-swap-oob`) must be top-level siblings, not nested under AppLayout. This pattern is already established in `TabloCardWithOOBFormClear`.
+- **Hard-coded colors in app.css:** All color values must reference `var(--...)` design tokens from `base.css`. The go-backend app.css already does this — port verbatim.
+- **Two queries where one suffices:** `ListTablosByUserWithDiscussionUnread` already returns all tablo fields; derive `[]sqlc.Tablo` from it rather than issuing a second query in `TablosListHandler`.
+
+---
+
+## Don't Hand-Roll
+
+| Problem | Don't Build | Use Instead | Why |
+|---------|-------------|-------------|-----|
+| Empty state component | Raw HTML `
` with inline styles | `@ui.EmptyState(ui.EmptyStateProps{...})` | Phase 13 component; `.ui-empty-state` CSS already loaded |
+| Icon buttons (edit/delete on card) | Raw `