diff --git a/.claude/frontend-poc/.env.development b/.claude/frontend-poc/.env.development
new file mode 100644
index 0000000..9c487dc
--- /dev/null
+++ b/.claude/frontend-poc/.env.development
@@ -0,0 +1,11 @@
+# Development environment configuration
+# These values are used when running `npm run dev`
+
+# Backend API base URL (FastAPI server)
+VITE_API_BASE_URL=http://localhost:8001
+
+# WebSocket URL (Socket.IO server - same as API in development)
+VITE_WS_URL=http://localhost:8001
+
+# OAuth redirect URI (must match OAuth provider configuration)
+VITE_OAUTH_REDIRECT_URI=http://localhost:3001/auth/callback
diff --git a/.claude/frontend-poc/.env.production b/.claude/frontend-poc/.env.production
new file mode 100644
index 0000000..94f4ed1
--- /dev/null
+++ b/.claude/frontend-poc/.env.production
@@ -0,0 +1,11 @@
+# Production environment configuration
+# These values are used when running `npm run build`
+
+# Backend API base URL
+VITE_API_BASE_URL=https://api.pocket.manticorum.com
+
+# WebSocket URL (Socket.IO server)
+VITE_WS_URL=https://api.pocket.manticorum.com
+
+# OAuth redirect URI (must match OAuth provider configuration)
+VITE_OAUTH_REDIRECT_URI=https://pocket.manticorum.com/auth/callback
diff --git a/.claude/frontend-poc/.gitignore b/.claude/frontend-poc/.gitignore
new file mode 100644
index 0000000..8a38d8f
--- /dev/null
+++ b/.claude/frontend-poc/.gitignore
@@ -0,0 +1,25 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-ssr
+*.local
+*.vite
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
diff --git a/.claude/frontend-poc/CLAUDE.md b/.claude/frontend-poc/CLAUDE.md
new file mode 100644
index 0000000..327f1de
--- /dev/null
+++ b/.claude/frontend-poc/CLAUDE.md
@@ -0,0 +1,553 @@
+# Mantimon TCG Frontend - AI Agent Guidelines
+
+Guidelines for AI agents working on the frontend codebase.
+
+## Tech Stack
+
+| Technology | Purpose |
+|------------|---------|
+| Vue 3 | UI framework (Composition API + `
+
+
+
+
+
+
+
+
+
+```
+
+### TypeScript
+
+**Use strict typing:**
+```typescript
+// Good - explicit types
+interface CardProps {
+ cardId: string
+ size?: 'sm' | 'md' | 'lg'
+}
+
+// Good - type imports
+import type { Card, GameState } from '@/types'
+
+// Good - const assertions for literals
+const SIZES = ['sm', 'md', 'lg'] as const
+type Size = typeof SIZES[number]
+
+// Avoid - any
+const data: any = response // NO
+const data: unknown = response // OK if you'll narrow it
+```
+
+### Naming Conventions
+
+| Type | Convention | Example |
+|------|------------|---------|
+| Components | PascalCase | `CardDisplay.vue`, `DeckBuilder.vue` |
+| Composables | camelCase with `use` prefix | `useAuth.ts`, `useDeckValidation.ts` |
+| Stores | camelCase with descriptive name | `auth.ts`, `game.ts` |
+| Types/Interfaces | PascalCase | `Card`, `GameState`, `ApiResponse` |
+| Constants | UPPER_SNAKE_CASE | `MAX_HAND_SIZE`, `API_BASE_URL` |
+| CSS classes | kebab-case (BEM optional) | `card-display`, `card-display--active` |
+
+---
+
+## Pinia Stores
+
+**Use setup store syntax:**
+```typescript
+// stores/game.ts
+import { defineStore } from 'pinia'
+import { ref, computed } from 'vue'
+import type { GameState } from '@/types'
+
+export const useGameStore = defineStore('game', () => {
+ // State
+ const gameState = ref(null)
+ const isConnected = ref(false)
+
+ // Getters (computed)
+ const myHand = computed(() => gameState.value?.myHand ?? [])
+ const isMyTurn = computed(() => gameState.value?.currentPlayer === 'me')
+
+ // Actions
+ function setGameState(state: GameState) {
+ gameState.value = state
+ }
+
+ function clearGame() {
+ gameState.value = null
+ isConnected.value = false
+ }
+
+ return {
+ // State
+ gameState,
+ isConnected,
+ // Getters
+ myHand,
+ isMyTurn,
+ // Actions
+ setGameState,
+ clearGame,
+ }
+})
+```
+
+---
+
+## Composables
+
+**Pattern for API composables:**
+```typescript
+// composables/useDecks.ts
+import { ref } from 'vue'
+import { apiClient } from '@/api/client'
+import type { Deck, DeckCreate } from '@/types'
+
+export function useDecks() {
+ const decks = ref([])
+ const isLoading = ref(false)
+ const error = ref(null)
+
+ async function fetchDecks() {
+ isLoading.value = true
+ error.value = null
+ try {
+ decks.value = await apiClient.get('/api/decks')
+ } catch (e) {
+ error.value = e instanceof Error ? e.message : 'Failed to fetch decks'
+ } finally {
+ isLoading.value = false
+ }
+ }
+
+ async function createDeck(data: DeckCreate) {
+ isLoading.value = true
+ error.value = null
+ try {
+ const deck = await apiClient.post('/api/decks', data)
+ decks.value.push(deck)
+ return deck
+ } catch (e) {
+ error.value = e instanceof Error ? e.message : 'Failed to create deck'
+ throw e
+ } finally {
+ isLoading.value = false
+ }
+ }
+
+ return {
+ decks,
+ isLoading,
+ error,
+ fetchDecks,
+ createDeck,
+ }
+}
+```
+
+---
+
+## Vue-Phaser Integration
+
+**Phaser is for rendering only. Game logic lives in backend.**
+
+### Communication Pattern
+
+```typescript
+// Vue -> Phaser (intentions)
+phaserGame.value?.events.emit('card:play', { cardId, targetZone })
+phaserGame.value?.events.emit('attack:select', { attackIndex })
+
+// Phaser -> Vue (completions/UI requests)
+phaserGame.value?.events.on('animation:complete', handleAnimationComplete)
+phaserGame.value?.events.on('card:clicked', handleCardClicked)
+```
+
+### State Sync
+
+```typescript
+// Phaser scene reads from Pinia store
+import { useGameStore } from '@/stores/game'
+
+class MatchScene extends Phaser.Scene {
+ private gameStore = useGameStore()
+
+ update() {
+ // React to store changes
+ if (this.gameStore.gameState) {
+ this.renderState(this.gameStore.gameState)
+ }
+ }
+}
+```
+
+---
+
+## Tailwind Guidelines
+
+**Mobile-first responsive:**
+```html
+
+
+```
+
+**Use design system colors:**
+```html
+
+Fire Type
+Water Type
+
+
+
+```
+
+---
+
+## Testing
+
+**Component tests with Vitest + Vue Test Utils:**
+```typescript
+import { describe, it, expect } from 'vitest'
+import { mount } from '@vue/test-utils'
+import CardDisplay from '@/components/cards/CardDisplay.vue'
+
+describe('CardDisplay', () => {
+ it('renders card name', () => {
+ /**
+ * Test that the card component displays the card name.
+ *
+ * Card names must be visible for players to identify cards
+ * in their hand and on the board.
+ */
+ const wrapper = mount(CardDisplay, {
+ props: { card: { id: '1', name: 'Pikachu', hp: 60 } }
+ })
+ expect(wrapper.text()).toContain('Pikachu')
+ })
+
+ it('emits click event with card id', async () => {
+ /**
+ * Test that clicking a card emits the correct event.
+ *
+ * Card clicks drive all game interactions - playing cards,
+ * selecting targets, viewing details.
+ */
+ const wrapper = mount(CardDisplay, {
+ props: { card: { id: '1', name: 'Pikachu', hp: 60 } }
+ })
+ await wrapper.trigger('click')
+ expect(wrapper.emitted('click')?.[0]).toEqual(['1'])
+ })
+})
+```
+
+---
+
+## Critical Rules
+
+1. **Mobile-first** - Base styles for mobile, use `md:` and `lg:` for larger screens
+2. **TypeScript strict** - No `any`, explicit types for props/emits/returns
+3. **Composition API only** - No Options API, use `
+