diff --git a/.planning/REQUIREMENTS.md b/.planning/REQUIREMENTS.md
index 4d3fc2d..797fb70 100644
--- a/.planning/REQUIREMENTS.md
+++ b/.planning/REQUIREMENTS.md
@@ -20,13 +20,13 @@ Every line of code written or modified in v0.2 must be:
## CODE — Codebase Quality
-- [ ] **CODE-01**: Backend `api/admin.py` (934L) decomposed into `api/admin/` package with `users.py`, `quotas.py`, `ai.py`; `__init__.py` aggregates sub-routers under the existing `/api/admin` prefix. No URL changes, no behavior changes, all tests stay green.
-- [ ] **CODE-02**: `api/documents.py` (852L) decomposed into focused sub-modules (upload flow, content proxy, document CRUD, search/listing) within `api/documents/` package. Prefix and behavior unchanged.
-- [ ] **CODE-03**: `api/auth.py` (825L) decomposed into focused sub-modules (login/tokens, TOTP, password management, session management) within `api/auth/` package. Prefix and behavior unchanged.
+- [x] **CODE-01**: Backend `api/admin.py` (934L) decomposed into `api/admin/` package with `users.py`, `quotas.py`, `ai.py`; `__init__.py` aggregates sub-routers under the existing `/api/admin` prefix. No URL changes, no behavior changes, all tests stay green.
+- [x] **CODE-02**: `api/documents.py` (852L) decomposed into focused sub-modules (upload flow, content proxy, document CRUD, search/listing) within `api/documents/` package. Prefix and behavior unchanged.
+- [x] **CODE-03**: `api/auth.py` (825L) decomposed into focused sub-modules (login/tokens, TOTP, password management, session management) within `api/auth/` package. Prefix and behavior unchanged.
- [x] **CODE-04**: Frontend `api/client.js` (635L) decomposed into domain modules (`documents.js`, `auth.js`, `admin.js`, `folders.js`, `shares.js`, `cloud.js`, `topics.js`); `client.js` becomes the HTTP transport layer and re-export barrel. Zero changes to any of the 35+ consumer files.
-- [ ] **CODE-05**: All inline SVG blocks (~66 instances) replaced with ``; all icon path data centralized in `components/ui/AppIcon.vue`. No duplicated path strings.
+- [x] **CODE-05**: All inline SVG blocks (~66 instances) replaced with ``; all icon path data centralized in `components/ui/AppIcon.vue`. No duplicated path strings.
- [x] **CODE-06**: Tailwind `safelist` configured for all dynamic class name patterns in `formatters.js` (provider colors, backgrounds, badge text). Production builds render topic and provider colors correctly.
-- [ ] **CODE-07**: All unreferenced files, components, stores, and unused imports deleted. No dead code retained.
+- [x] **CODE-07**: All unreferenced files, components, stores, and unused imports deleted. No dead code retained.
- [x] **CODE-08**: No duplicated Pydantic model definitions or shared validators across router files. Shared schemas extracted to dedicated modules.
- [x] **CODE-09**: No comment in any file describes what the code does. Comments exist only where intent or constraint would not be obvious to a competent reader.
@@ -35,7 +35,7 @@ Every line of code written or modified in v0.2 must be:
## ADMIN — Admin Panel
- [x] **ADMIN-08**: Admin panel moved to `/admin/*` route subtree with `AdminLayout.vue` as the route component. `AdminLayout` has its own sidebar with admin-specific nav only — no user quota bar, no folder tree, no topic list. `AdminView.vue` is deleted.
-- [x] **ADMIN-09**: Admin sidebar nav links (in order): Overview, Users, Quotas, AI Config, Audit Log. "Back to app" link at the bottom returns to `/`.
+- [x] **ADMIN-09**: Admin sidebar nav links (in order): Overview, Users, Quotas, AI Config, Audit Log. Per Phase 09 decision D-06, admin accounts are administration-only and no "Back to app" link is rendered.
- [x] **ADMIN-10**: Each admin section is its own deep-linkable URL (`/admin/users`, `/admin/quotas`, `/admin/ai`, `/admin/audit`). Browser back button works within the admin section.
- [x] **ADMIN-11**: Admin overview page (`/admin`) shows: total registered user count, total platform storage in use, document status breakdown (processing/ready/failed), last 10 audit log entries. Requires new backend aggregate query endpoints.
- [x] **ADMIN-12**: `requiresAdmin` guard enforced for all `/admin/*` child routes via `to.matched.some(r => r.meta.requiresAdmin)`. No admin child route is accessible to non-admin users.
@@ -44,47 +44,47 @@ Every line of code written or modified in v0.2 must be:
## UX — UX and Interaction
-- [ ] **UX-01**: Every zero-content context shows a contextually distinct empty state using a shared `EmptyState.vue` component (headline, subtext, optional CTA). Applied to: root file list, individual folder, search with no results, shared-with-me, topics list, audit log, cloud connections. No plain "No items" text remains.
-- [ ] **UX-02**: Document list (`StorageBrowser`) displays 5-col skeleton grid rows during loading (matching actual grid layout, `animate-pulse`). "Loading..." text removed.
-- [ ] **UX-03**: Sidebar folder tree and topics list display skeleton placeholder items during loading. "Loading..." text removed.
-- [ ] **UX-04**: Admin user table and audit log table display skeleton table rows during loading.
-- [ ] **UX-05**: Pressing `/` when no input is focused moves keyboard focus to the search bar.
-- [ ] **UX-06**: Pressing `Escape` closes any open modal (`ShareModal`, `FolderDeleteModal`, `DocumentPreviewModal`) and clears active search. Handled at the overlay level, not only inside inputs.
-- [ ] **UX-07**: Pressing `U` when no input is focused triggers the file upload picker.
-- [ ] **UX-08**: Pressing `N` when no input is focused starts the new folder inline input in the file manager.
-- [ ] **UX-09**: Dragging files from the OS anywhere onto the browser window shows a full-screen drop overlay. Releasing uploads the files via the existing upload flow. Only activates when `dataTransfer.types` includes `'Files'`.
-- [ ] **UX-10**: Toast notification system shows transient messages (auto-dismiss 4 seconds, manual dismiss on click) for: upload complete, upload error, document deleted, share revoked, rename successful. Toasts stack vertically and do not block interaction.
-- [ ] **UX-11**: Drag-to-move a document onto a folder row in `StorageBrowser` works correctly end-to-end. Drop highlight (`ring-2 ring-inset ring-amber-300`) renders on valid folder targets. "Move to folder" button remains as non-drag alternative.
-- [ ] **UX-12**: All views display a breadcrumb reflecting the full current navigation path. Examples: `Folders › Reports › Q1`, `Cloud › Google Drive › Projects`, `Admin › Users`, `Settings › Account`, `Folders › Reports › document.pdf`. Breadcrumb updates on navigation. A single shared breadcrumb component is used across all view types — no per-view implementations.
-- [ ] **UX-13**: All dropdown menus and modal dialogs render their full content within the viewport. No text or interactive element is clipped by a viewport edge or sibling element. Dropdowns that risk edge-clipping use fixed positioning computed from `getBoundingClientRect()` or ``.
-- [ ] **UX-14**: The inline "New" button next to the Folders section header in `AppSidebar.vue` is removed. Folder creation accessible only from within the file manager view.
+- [x] **UX-01**: Every zero-content context shows a contextually distinct empty state using a shared `EmptyState.vue` component (headline, subtext, optional CTA). Applied to: root file list, individual folder, search with no results, shared-with-me, topics list, audit log, cloud connections. No plain "No items" text remains.
+- [x] **UX-02**: Document list (`StorageBrowser`) displays 5-col skeleton grid rows during loading (matching actual grid layout, `animate-pulse`). "Loading..." text removed.
+- [x] **UX-03**: Sidebar folder tree and topics list display skeleton placeholder items during loading. "Loading..." text removed.
+- [x] **UX-04**: Admin user table and audit log table display skeleton table rows during loading.
+- [x] **UX-05**: Pressing `/` when no input is focused moves keyboard focus to the search bar.
+- [x] **UX-06**: Pressing `Escape` closes any open modal (`ShareModal`, `FolderDeleteModal`, `DocumentPreviewModal`) and clears active search. Handled at the overlay level, not only inside inputs.
+- [x] **UX-07**: Pressing `U` when no input is focused triggers the file upload picker.
+- [x] **UX-08**: Pressing `N` when no input is focused starts the new folder inline input in the file manager.
+- [x] **UX-09**: Dragging files from the OS anywhere onto the browser window shows a full-screen drop overlay. Releasing uploads the files via the existing upload flow. Only activates when `dataTransfer.types` includes `'Files'`.
+- [x] **UX-10**: Toast notification system shows transient messages (auto-dismiss 4 seconds, manual dismiss on click) for: upload complete, upload error, document deleted, share revoked, rename successful. Toasts stack vertically and do not block interaction.
+- [x] **UX-11**: Drag-to-move a document onto a folder row in `StorageBrowser` works correctly end-to-end. Drop highlight (`ring-2 ring-inset ring-amber-300`) renders on valid folder targets. "Move to folder" button remains as non-drag alternative.
+- [x] **UX-12**: All views display a breadcrumb reflecting the full current navigation path. Examples: `Folders › Reports › Q1`, `Cloud › Google Drive › Projects`, `Admin › Users`, `Settings › Account`, `Folders › Reports › document.pdf`. Breadcrumb updates on navigation. A single shared breadcrumb component is used across all view types — no per-view implementations.
+- [x] **UX-13**: All dropdown menus and modal dialogs render their full content within the viewport. No text or interactive element is clipped by a viewport edge or sibling element. Dropdowns that risk edge-clipping use fixed positioning computed from `getBoundingClientRect()` or ``.
+- [x] **UX-14**: The inline "New" button next to the Folders section header in `AppSidebar.vue` is removed. Folder creation accessible only from within the file manager view.
---
## VISUAL — Visual Design
-- [ ] **VISUAL-01**: Consistent spacing scale across all components using Tailwind utilities only. No arbitrary `px-[13px]` values or inline `style` margins.
-- [ ] **VISUAL-02**: All form elements (inputs, selects, textareas, checkboxes, radio buttons) have consistent cross-browser baseline styling via `@tailwindcss/forms` plugin.
-- [ ] **VISUAL-03**: All interactive elements (buttons, links, card rows) have consistent hover states, `focus-visible:` rings, and active states.
-- [ ] **VISUAL-04**: Typography scale is consistent: one heading size per level, one body size, one label/caption size. No per-component font-size overrides.
+- [x] **VISUAL-01**: Consistent spacing scale across all components using Tailwind utilities only. No arbitrary `px-[13px]` values or inline `style` margins.
+- [x] **VISUAL-02**: All form elements (inputs, selects, textareas, checkboxes, radio buttons) have consistent cross-browser baseline styling via `@tailwindcss/forms` plugin.
+- [x] **VISUAL-03**: All interactive elements (buttons, links, card rows) have consistent hover states, `focus-visible:` rings, and active states.
+- [x] **VISUAL-04**: Typography scale is consistent: one heading size per level, one body size, one label/caption size. No per-component font-size overrides.
---
## RESP — Responsive Layout
-- [ ] **RESP-01**: Sidebar hidden below `lg` (1024px). Hamburger button in mobile-only header toggles a slide-in overlay drawer. Overlay closes on nav tap or backdrop tap.
-- [ ] **RESP-02**: Document list hides Size column below `md` (768px) and Modified column below `sm` (640px). Icon, name, and actions always visible.
-- [ ] **RESP-03**: Inline icon action buttons have minimum 36×36px touch target on viewports below `md`.
-- [ ] **RESP-04**: All modal dialogs are scrollable on viewports below 640px. No modal content overflows the screen.
-- [ ] **RESP-05**: Admin layout has the same responsive behavior as the user layout: admin sidebar hidden on mobile, hamburger shows admin nav drawer.
+- [x] **RESP-01**: Sidebar hidden below `lg` (1024px). Hamburger button in mobile-only header toggles a slide-in overlay drawer. Overlay closes on nav tap or backdrop tap.
+- [x] **RESP-02**: Document list hides Size column below `md` (768px) and Modified column below `sm` (640px). Icon, name, and actions always visible.
+- [x] **RESP-03**: Inline icon action buttons have minimum 36×36px touch target on viewports below `md`.
+- [x] **RESP-04**: All modal dialogs are scrollable on viewports below 640px. No modal content overflows the screen.
+- [x] **RESP-05**: Admin layout has the same responsive behavior as the user layout: admin sidebar hidden on mobile, hamburger shows admin nav drawer.
---
## PERF — Performance and Stack
-- [ ] **PERF-01**: Frontend dependencies bumped/added: `vue@^3.5.0`, `vite@^6.4.3`, `@vueuse/core@^14.3.0`, `@vueuse/integrations@^14.3.0`, `sortablejs@^1.15.7`, `@tailwindcss/forms@^0.5.11`. Dev: `rollup-plugin-visualizer@^7.0.1`, `@types/sortablejs`.
-- [ ] **PERF-02**: Bundle baseline measured with `rollup-plugin-visualizer` before optimizations begin. Second measurement taken after all refactoring is complete. Both reports committed to `.planning/`.
-- [ ] **PERF-03**: All non-initial-render route components are lazy-loaded. Admin views explicitly lazy-loaded. No synchronous import for routes not on the critical render path.
+- [x] **PERF-01**: Frontend dependencies bumped/added: `vue@^3.5.0`, `vite@^8.0.16`, `@vueuse/core@^14.3.0`, `@vueuse/integrations@^14.3.0`, `sortablejs@^1.15.7`, `@tailwindcss/forms@^0.5.11`. Dev: `rollup-plugin-visualizer@^7.0.1`, `@types/sortablejs`.
+- [x] **PERF-02**: Bundle baseline measured with `rollup-plugin-visualizer` before optimizations begin. Second measurement taken after all refactoring is complete. Both reports committed to `.planning/`.
+- [x] **PERF-03**: All non-initial-render route components are lazy-loaded. Admin views explicitly lazy-loaded. No synchronous import for routes not on the critical render path.
---
@@ -108,10 +108,10 @@ Every line of code written or modified in v0.2 must be:
| REQ-ID | Phase | Plan |
|--------|-------|------|
-| PERF-01 | Phase 8 | Pending |
-| CODE-01 | Phase 8 | Pending |
-| CODE-02 | Phase 8 | Pending |
-| CODE-03 | Phase 8 | Pending |
+| PERF-01 | Phase 8 | Complete |
+| CODE-01 | Phase 8 | Complete |
+| CODE-02 | Phase 8 | Complete |
+| CODE-03 | Phase 8 | Complete |
| CODE-04 | Phase 8 | Complete |
| CODE-08 | Phase 8 | Complete |
| ADMIN-08 | Phase 9 | Complete |
@@ -121,30 +121,30 @@ Every line of code written or modified in v0.2 must be:
| ADMIN-12 | Phase 9 | Complete |
| CODE-06 | Phase 9 | Complete |
| CODE-09 | Phase 9 | Complete |
-| UX-01 | Phase 10 | Pending |
-| UX-02 | Phase 10 | Pending |
-| UX-03 | Phase 10 | Pending |
-| UX-04 | Phase 10 | Pending |
-| UX-05 | Phase 10 | Pending |
-| UX-06 | Phase 10 | Pending |
-| UX-07 | Phase 10 | Pending |
-| UX-08 | Phase 10 | Pending |
-| UX-09 | Phase 10 | Pending |
-| UX-10 | Phase 10 | Pending |
-| UX-11 | Phase 10 | Pending |
-| UX-12 | Phase 10 | Pending |
-| UX-13 | Phase 10 | Pending |
-| UX-14 | Phase 10 | Pending |
-| CODE-05 | Phase 10 | Pending |
-| VISUAL-01 | Phase 11 | Planned: 11-05 |
-| VISUAL-02 | Phase 11 | Planned: 11-04 |
-| VISUAL-03 | Phase 11 | Planned: 11-05 |
-| VISUAL-04 | Phase 11 | Planned: 11-05 |
-| RESP-01 | Phase 11 | Planned: 11-03 |
-| RESP-02 | Phase 11 | Planned: 11-03 |
-| RESP-03 | Phase 11 | Planned: 11-03 |
-| RESP-04 | Phase 11 | Planned: 11-04 |
-| RESP-05 | Phase 11 | Planned: 11-03 |
-| CODE-07 | Phase 11 | Planned: 11-06 |
-| PERF-02 | Phase 11 | Planned: 11-01, 11-06 |
-| PERF-03 | Phase 11 | Planned: 11-02 |
+| UX-01 | Phase 10 | Complete |
+| UX-02 | Phase 10 | Complete |
+| UX-03 | Phase 10 | Complete |
+| UX-04 | Phase 10 | Complete |
+| UX-05 | Phase 10 | Complete |
+| UX-06 | Phase 10 | Complete |
+| UX-07 | Phase 10 | Complete |
+| UX-08 | Phase 10 | Complete |
+| UX-09 | Phase 10 | Complete |
+| UX-10 | Phase 10 | Complete |
+| UX-11 | Phase 10 | Complete |
+| UX-12 | Phase 10 | Complete |
+| UX-13 | Phase 10 | Complete |
+| UX-14 | Phase 10 | Complete |
+| CODE-05 | Phase 10 | Complete |
+| VISUAL-01 | Phase 11 | Complete |
+| VISUAL-02 | Phase 11 | Complete |
+| VISUAL-03 | Phase 11 | Complete |
+| VISUAL-04 | Phase 11 | Complete |
+| RESP-01 | Phase 11 | Complete |
+| RESP-02 | Phase 11 | Complete |
+| RESP-03 | Phase 11 | Complete |
+| RESP-04 | Phase 11 | Complete |
+| RESP-05 | Phase 11 | Complete |
+| CODE-07 | Phase 11 | Complete |
+| PERF-02 | Phase 11 | Complete |
+| PERF-03 | Phase 11 | Complete |
diff --git a/.planning/perf/phase11-final-summary.md b/.planning/perf/phase11-final-summary.md
index 92905d4..2494b01 100644
--- a/.planning/perf/phase11-final-summary.md
+++ b/.planning/perf/phase11-final-summary.md
@@ -1,7 +1,7 @@
# Phase 11 Bundle Final Summary — Baseline vs. Final Comparison
**Captured:** 2026-06-17
-**Vite version:** 6.4.3
+**Vite version:** 8.0.16
**Node environment:** production
## Command
@@ -15,37 +15,43 @@ cd frontend && ANALYZE=true npm run build # generates stats.html → .planning
| Metric | Baseline (pre-11-02) | Final (post-Phase-11) | Delta |
|--------|---------------------|----------------------|-------|
-| Main bundle — raw | 264.63 kB | 183.62 kB | **-81.01 kB (-30.6%)** |
-| Main bundle — gzip | 89.34 kB | 64.83 kB | **-24.51 kB (-27.4%)** |
-| CSS main — raw | 98.74 kB | 102.35 kB | +3.61 kB |
-| CSS main — gzip | 17.12 kB | 17.69 kB | +0.57 kB |
-| JS chunk count | 15 chunks | 21 chunks | **+6 chunks** |
-| Total output files | 15 | 23 | +8 |
+| Main entry chunk — raw | 264.63 kB | 30.31 kB | **-234.32 kB (-88.5%)** |
+| Main entry chunk — gzip | 89.34 kB | 9.59 kB | **-79.75 kB (-89.3%)** |
+| CSS main — raw | 98.74 kB | 100.39 kB | +1.65 kB |
+| CSS main — gzip | 17.12 kB | 17.83 kB | +0.71 kB |
+| JS chunk count | 15 chunks | 37 chunks | **+22 chunks** |
+| Total output files | 15 | 39 | +24 |
+
+> Note: The final measurement was refreshed after the Vite 8 security bump that closed the esbuild high-severity audit finding. Vite 8 performs more shared/runtime chunk splitting than the earlier Vite 6 final measurement, so the "main entry chunk" is no longer directly equivalent to the old single large app chunk.
## What Changed
-### Main bundle reduction (−81 kB raw)
+### Main entry reduction
-Plan 11-02 lazy-loaded 5 user routes that were previously synchronous imports. This split their code out of the main bundle and into dedicated chunks:
+Plan 11-02 lazy-loaded 5 user routes that were previously synchronous imports. The later Vite 8 security bump also split shared runtime/vendor code into smaller chunks, leaving the main entry chunk at 30.31 kB raw / 9.59 kB gzip.
| New lazy chunk | Size (raw) | Size (gzip) | Route |
|----------------|-----------|------------|-------|
-| `SettingsView-*.js` | 62.72 kB | 20.08 kB | `/settings` |
-| `TopicsView-*.js` | 12.31 kB | 3.96 kB | `/topics`, `/topics/:name` |
-| `DocumentView-*.js` | 10.17 kB | 3.77 kB | `/document/:id` |
-| `CloudStorageView-*.js` | 2.32 kB | 1.24 kB | `/cloud` |
-| `CloudFolderView-*.js` | 1.99 kB | 1.06 kB | `/cloud/:provider/:folderId` |
-| `AppSpinner-*.js` | 0.51 kB | 0.37 kB | (shared sub-chunk for spinner) |
+| `SettingsView-*.js` | 60.95 kB | 19.27 kB | `/settings` |
+| `TopicsView-*.js` | 12.46 kB | 4.02 kB | `/topics`, `/topics/:name` |
+| `DocumentView-*.js` | 10.29 kB | 3.88 kB | `/document/:id` |
+| `CloudStorageView-*.js` | 2.53 kB | 1.32 kB | `/cloud` |
+| `CloudFolderView-*.js` | 2.14 kB | 1.13 kB | `/cloud/:provider/:folderId` |
+| `AppSpinner-*.js` | 0.54 kB | 0.39 kB | (shared sub-chunk for spinner) |
-`SettingsView` contains the TotpEnrollment, PasswordStrengthBar, and cloud-connection components — it alone accounts for ~62 kB of the reduction.
+`SettingsView` contains the TotpEnrollment, PasswordStrengthBar, and cloud-connection components, so keeping it lazy remains the largest route-level win.
-### CSS slight increase (+3.6 kB raw)
+### CSS slight increase (+1.65 kB raw)
The responsive layout additions in Plans 11-03/11-04/11-05 added new Tailwind utility classes (`translate-x-0`, `-translate-x-full`, `max-h-[90vh]`, `overflow-y-auto`, `focus-visible:ring-*`, `min-h-[36px]`, `min-w-[36px]`) that were not in the purged baseline. The modest increase confirms these classes are in active use.
-### AdminLayout now includes drawer JS (+1.44 kB)
+### AdminLayout now includes drawer JS
-AdminLayout grew from 2.71 kB to 4.15 kB due to the responsive hamburger drawer state added in Plan 11-03. The drawer logic lives in AdminLayout.vue per the D-04/D-05 sidebar-state rule.
+AdminLayout is 4.23 kB raw / 1.72 kB gzip due to the responsive hamburger drawer state added in Plan 11-03. The drawer logic lives in AdminLayout.vue per the D-04/D-05 sidebar-state rule.
+
+### Vite 8 security remediation
+
+During milestone audit remediation, `npm audit --audit-level=high` reported GHSA-gv7w-rqvm-qjhr through the Vite 6 esbuild dependency. Vite was upgraded to `^8.0.16`; the current final analyzer artifact and this summary reflect the post-remediation build.
## New Lazy Route Chunks (Plan 11-02)
@@ -65,7 +71,7 @@ After Plan 11-02, each of these routes is a separate chunk loaded only when the
## Interpretation
-A **30.6% reduction** in the main bundle raw size (−81 kB) means first-load JS for authenticated users drops from 264 kB to 183 kB. At an average mobile 4G connection (~1 Mbps effective throughput), this saves approximately **640 ms of download time** before decompression, and about **195 ms after gzip** decompression savings are factored in.
+A large reduction in the main entry chunk remains after the Vite 8 refresh: the entry is 30.31 kB raw / 9.59 kB gzip, with route and shared code split into on-demand chunks. The exact first-load byte count now depends on the browser's module graph preloading behavior, but the critical path no longer forces Settings, Topics, Document detail, or Cloud views into the initial application entry.
The chunk-splitting strategy is correct: the 6 new chunks are only fetched on demand, adding zero latency to the critical `/` home path.
diff --git a/.planning/perf/phase11-final.html b/.planning/perf/phase11-final.html
index 4ffbae0..f10a6f4 100644
--- a/.planning/perf/phase11-final.html
+++ b/.planning/perf/phase11-final.html
@@ -4930,7 +4930,7 @@ var drawChart = (function (exports) {