diff --git a/.claude/skills/frontend-code-audit/SKILL.md b/.claude/skills/frontend-code-audit/SKILL.md
new file mode 100644
index 0000000..ac06d96
--- /dev/null
+++ b/.claude/skills/frontend-code-audit/SKILL.md
@@ -0,0 +1,153 @@
+# Frontend Code Audit Skill
+
+Perform systematic code audits on Vue/TypeScript/Phaser frontend code to find anti-patterns, hidden errors, security issues, and architecture violations.
+
+## Usage
+
+```
+/frontend-code-audit [category] [path]
+```
+
+**Categories:**
+- `errors` - Unhandled errors, silent failures, missing error states
+- `security` - XSS risks, data exposure, auth token handling
+- `architecture` - Vue/Pinia patterns, Phaser integration, component design
+- `all` - Run all audit categories (default)
+
+**Path:** Optional path to audit (defaults to `frontend/src/`)
+
+## Examples
+
+```
+/frontend-code-audit # Full audit of frontend/src/
+/frontend-code-audit errors # Just error handling patterns
+/frontend-code-audit security stores/ # Security audit of stores/
+/frontend-code-audit architecture game/ # Architecture audit of Phaser code
+```
+
+## Instructions
+
+When this skill is invoked:
+
+1. **Load Pattern Definitions**
+ Read the YAML files in `patterns/` directory for the requested category.
+
+2. **Search Codebase**
+ For each pattern, use Grep with the specified regex to find matches.
+ Focus on production code (skip test files unless pattern specifies otherwise).
+
+3. **Analyze Context**
+ For each match, read surrounding code to determine verdict:
+ - **ISSUE**: Code should be changed (explain why)
+ - **WARNING**: Potential problem, needs review
+ - **OK**: Pattern detected but implementation is correct (explain why)
+
+4. **Generate Report**
+ Output structured report with:
+ - Summary stats (issues/warnings/ok by category)
+ - Detailed findings with file:line references
+ - Recommendations for each issue
+
+## Verdict Guidelines
+
+### Error Handling
+
+**ISSUE** when:
+- Promise rejection unhandled (no `.catch()` or try/catch)
+- Fetch error caught but UI shows no feedback
+- Error logged but component renders as if success
+- Empty catch block swallows error
+
+**OK** when:
+- Error caught and user notified (toast, error state)
+- Error boundary catches and displays fallback
+- Retry logic with eventual user feedback
+- Graceful degradation documented
+
+### Security
+
+**ISSUE** when:
+- `v-html` used with any user-provided data
+- Auth token stored in localStorage without httpOnly consideration
+- Sensitive data (passwords, tokens) in console.log
+- Query params contain auth tokens
+
+**OK** when:
+- `v-html` only used with sanitized/trusted content
+- Token storage follows project auth pattern
+- Logging excludes sensitive fields
+- Auth handled via httpOnly cookies or secure headers
+
+### Architecture
+
+**ISSUE** when:
+- Game logic (damage calc, turn state) in Phaser scene
+- API calls made directly in component (not via composable/store)
+- Phaser scene imports from stores without bridge pattern
+- Component has >200 lines (should split)
+
+**OK** when:
+- Phaser only handles rendering/animation
+- API access through typed composables
+- Vue-Phaser communication via event bridge
+- Large component is page-level layout
+
+## Pattern File Format
+
+```yaml
+name: Category Name
+description: What this category audits
+
+patterns:
+ - id: unique-pattern-id
+ description: Human-readable description
+ grep_pattern: 'regex pattern for Grep tool'
+ file_glob: '*.vue' # Optional, defaults to *.{vue,ts}
+ exclude_tests: true # Optional, defaults to true
+ multiline: false # Optional, for cross-line patterns
+ context_lines: 3 # Lines to read around match
+ verdict_hints:
+ ISSUE_IF: "condition that makes this an issue"
+ OK_IF: "condition that makes this acceptable"
+```
+
+## Output Format
+
+```markdown
+# Frontend Code Audit Report
+
+**Scope:** frontend/src/
+**Categories:** errors, security, architecture
+**Date:** YYYY-MM-DD
+
+## Summary
+
+| Category | Issues | Warnings | OK |
+|----------|--------|----------|-----|
+| Errors | 2 | 1 | 5 |
+| Security | 0 | 2 | 3 |
+| Architecture | 1 | 0 | 4 |
+
+## Issues (Fix Required)
+
+### [ISSUE] security.v-html-user-data
+**File:** src/components/CardDescription.vue:42
+**Pattern:** v-html with potentially unsafe content
+
+```vue
+
+```
+
+**Problem:** Card description from API could contain malicious scripts.
+**Recommendation:** Use text interpolation or sanitize with DOMPurify.
+
+---
+
+## Warnings (Review Needed)
+
+...
+
+## OK (Verified Correct)
+
+...
+```
diff --git a/.claude/skills/frontend-code-audit/patterns/architecture.yaml b/.claude/skills/frontend-code-audit/patterns/architecture.yaml
new file mode 100644
index 0000000..4078847
--- /dev/null
+++ b/.claude/skills/frontend-code-audit/patterns/architecture.yaml
@@ -0,0 +1,204 @@
+# Frontend Architecture Anti-Patterns
+# Patterns that violate Mantimon TCG frontend architecture principles
+
+name: Architecture
+description: |
+ Detects violations of the project's frontend architecture principles:
+ - Vue Composition API patterns (no Options API)
+ - Pinia store patterns (setup syntax, proper actions)
+ - Phaser integration (rendering only, no game logic)
+ - Component organization (composables for reuse)
+ - Backend authority (client never authoritative for game state)
+
+patterns:
+ # Vue Composition API
+ - id: options-api-usage
+ description: Options API instead of Composition API
+ grep_pattern: 'export default \{[\s\S]*?(data\(\)|methods:|computed:|watch:)'
+ multiline: true
+ file_glob: '*.vue'
+ context_lines: 5
+ verdict_hints:
+ ISSUE_IF: "Using Options API - project uses Composition API only"
+ OK_IF: "Never OK - convert to '
+ file_glob: '*.vue'
+ context_lines: 0
+ verdict_hints:
+ ISSUE_IF: "Script section >150 lines - split into composables"
+ OK_IF: "Page component; or logic is cohesive and documented"
+
+ - id: prop-drilling
+ description: Props passed through multiple levels
+ grep_pattern: 'defineProps.*\{[\s\S]*?(Props|\.\.\.)'
+ multiline: true
+ file_glob: '*.vue'
+ context_lines: 10
+ verdict_hints:
+ ISSUE_IF: "Props drilled >2 levels - use provide/inject or store"
+ OK_IF: "Props are component-specific; not passed through"
+
+ # Type Safety
+ - id: any-type-usage
+ description: Using 'any' type
+ grep_pattern: ':\s*any\b|as\s+any\b|'
+ file_glob: '*.{vue,ts}'
+ context_lines: 2
+ verdict_hints:
+ ISSUE_IF: "Explicit any - defeats TypeScript benefits"
+ OK_IF: "Temporary during migration; or external lib without types"
+
+ - id: type-assertion-abuse
+ description: Type assertion (as) that might hide errors
+ grep_pattern: '\s+as\s+[A-Z]\w+'
+ file_glob: '*.{vue,ts}'
+ context_lines: 3
+ verdict_hints:
+ ISSUE_IF: "Assertion used to bypass type error"
+ OK_IF: "Narrowing from unknown; or after runtime check"
+
+ - id: missing-prop-types
+ description: Props without TypeScript types
+ grep_pattern: 'defineProps\(\[\s*["\']'
+ file_glob: '*.vue'
+ context_lines: 3
+ verdict_hints:
+ ISSUE_IF: "Array prop syntax - no type safety"
+ OK_IF: "Never OK - use defineProps<{...}>()"
+
+ # Import Organization
+ - id: relative-import-deep
+ description: Deep relative imports (use @ alias)
+ grep_pattern: 'from ["\']\.\.\/\.\.\/\.\.\/'
+ file_glob: '*.{vue,ts}'
+ context_lines: 2
+ verdict_hints:
+ ISSUE_IF: "Deep relative path - use @/ alias"
+ OK_IF: "Never OK for 3+ levels - always use @/"
+
+ - id: missing-type-import
+ description: Type import without 'import type'
+ grep_pattern: 'import \{[^}]*(Type|Interface|Props)[^}]*\} from'
+ file_glob: '*.{vue,ts}'
+ context_lines: 2
+ verdict_hints:
+ ISSUE_IF: "Type imported as value - use 'import type'"
+ OK_IF: "Also used as value (class, enum); not pure type"
+
+ # Backend Authority
+ - id: client-state-authority
+ description: Client treating its state as authoritative
+ grep_pattern: '(emit|send)\([^)]*\{[^}]*(hp|damage|winner|gameState)'
+ file_glob: '*.{vue,ts}'
+ context_lines: 5
+ verdict_hints:
+ ISSUE_IF: "Sending computed game state to server - server is authority"
+ OK_IF: "Sending action intent (playCard, attack); server computes result"
+
+ - id: local-game-calculation
+ description: Calculating game outcomes client-side
+ grep_pattern: '(damage|hp|energy)\s*[-+*]=|Math\.(random|floor).*damage'
+ file_glob: '*.{vue,ts}'
+ context_lines: 5
+ verdict_hints:
+ ISSUE_IF: "Game calculation on client - server must compute"
+ OK_IF: "Animation preview; actual result from server"
diff --git a/.claude/skills/frontend-code-audit/patterns/error-handling.yaml b/.claude/skills/frontend-code-audit/patterns/error-handling.yaml
new file mode 100644
index 0000000..71a0666
--- /dev/null
+++ b/.claude/skills/frontend-code-audit/patterns/error-handling.yaml
@@ -0,0 +1,103 @@
+# Frontend Error Handling Anti-Patterns
+# Patterns that hide errors or fail to provide user feedback
+
+name: Error Handling
+description: |
+ Detects patterns where errors are silently swallowed, promises unhandled,
+ or users left without feedback when operations fail. These patterns lead
+ to confusing UX and hard-to-debug issues.
+
+patterns:
+ - id: unhandled-promise
+ description: Async function called without await or .catch()
+ grep_pattern: '^\s+\w+\([^)]*\)\s*$'
+ file_glob: '*.{vue,ts}'
+ context_lines: 3
+ verdict_hints:
+ ISSUE_IF: "Async function (fetch, API call) with no error handling"
+ OK_IF: "Sync function; or fire-and-forget with documented reason"
+
+ - id: empty-catch-block
+ description: Catch block that does nothing with the error
+ grep_pattern: 'catch\s*\([^)]*\)\s*\{\s*\}'
+ file_glob: '*.{vue,ts}'
+ context_lines: 3
+ verdict_hints:
+ ISSUE_IF: "Error completely swallowed - user gets no feedback"
+ OK_IF: "Never OK - at minimum log or set error state"
+
+ - id: catch-only-console
+ description: Catch block that only logs to console
+ grep_pattern: 'catch.*\{[\s\n]*console\.(log|error|warn)'
+ multiline: true
+ file_glob: '*.{vue,ts}'
+ context_lines: 5
+ verdict_hints:
+ ISSUE_IF: "Error logged but UI shows success/no feedback to user"
+ OK_IF: "Also sets error state or shows toast notification"
+
+ - id: fetch-no-error-check
+ description: Fetch without checking response.ok
+ grep_pattern: 'fetch\([^)]+\)[\s\S]*?\.json\(\)'
+ multiline: true
+ file_glob: '*.{vue,ts}'
+ context_lines: 7
+ verdict_hints:
+ ISSUE_IF: "Response not checked for errors before parsing JSON"
+ OK_IF: "Uses wrapper that handles errors; or response.ok checked"
+
+ - id: missing-loading-state
+ description: Async operation without loading indicator
+ grep_pattern: 'async.*\{[\s\S]*?await.*fetch'
+ multiline: true
+ file_glob: '*.vue'
+ context_lines: 10
+ verdict_hints:
+ ISSUE_IF: "No isLoading ref set before/after async operation"
+ OK_IF: "Loading state managed by composable; or instant operation"
+
+ - id: missing-error-state
+ description: API call without error state handling
+ grep_pattern: 'await.*(fetch|api|axios)'
+ file_glob: '*.vue'
+ context_lines: 8
+ verdict_hints:
+ ISSUE_IF: "No error ref or error handling visible in component"
+ OK_IF: "Error handling in composable; or uses error boundary"
+
+ - id: silent-store-action
+ description: Store action that catches but doesn't surface errors
+ grep_pattern: 'catch.*\{[\s\S]*?return (false|null|undefined|\[\]|\{\})'
+ multiline: true
+ file_glob: 'stores/*.ts'
+ context_lines: 6
+ verdict_hints:
+ ISSUE_IF: "Store swallows error, returns falsy - caller can't show error"
+ OK_IF: "Also sets store.error state that UI can display"
+
+ - id: onmounted-no-error-handling
+ description: onMounted with async call but no error handling
+ grep_pattern: 'onMounted\(\s*async'
+ file_glob: '*.vue'
+ context_lines: 10
+ verdict_hints:
+ ISSUE_IF: "Async onMounted without try/catch - errors crash silently"
+ OK_IF: "Has try/catch with error state; or uses error boundary"
+
+ - id: watch-async-no-catch
+ description: Watch callback with async but no error handling
+ grep_pattern: 'watch\([^)]+,\s*async'
+ file_glob: '*.vue'
+ context_lines: 8
+ verdict_hints:
+ ISSUE_IF: "Async watch without error handling"
+ OK_IF: "Has try/catch; or errors handled by called function"
+
+ - id: socket-no-error-handler
+ description: Socket.io without error event handler
+ grep_pattern: 'socket\.(on|emit)\('
+ file_glob: '*.{vue,ts}'
+ context_lines: 10
+ verdict_hints:
+ ISSUE_IF: "Socket used without socket.on('error') or socket.on('connect_error')"
+ OK_IF: "Error handlers registered elsewhere (socket setup file)"
diff --git a/.claude/skills/frontend-code-audit/patterns/security.yaml b/.claude/skills/frontend-code-audit/patterns/security.yaml
new file mode 100644
index 0000000..7e4fff9
--- /dev/null
+++ b/.claude/skills/frontend-code-audit/patterns/security.yaml
@@ -0,0 +1,152 @@
+# Frontend Security Anti-Patterns
+# Patterns that may expose security vulnerabilities in Vue/TypeScript
+
+name: Security
+description: |
+ Detects patterns that could lead to security vulnerabilities including
+ XSS risks, sensitive data exposure, insecure storage, and auth issues.
+ Specific to Vue 3, TypeScript, and browser security concerns.
+
+patterns:
+ # XSS Risks
+ - id: v-html-usage
+ description: v-html directive (potential XSS)
+ grep_pattern: 'v-html\s*='
+ file_glob: '*.vue'
+ context_lines: 3
+ verdict_hints:
+ ISSUE_IF: "Content from API, user input, or any external source"
+ OK_IF: "Only static/trusted content; or sanitized with DOMPurify"
+
+ - id: innerhtml-assignment
+ description: Direct innerHTML assignment
+ grep_pattern: '\.innerHTML\s*='
+ file_glob: '*.{vue,ts}'
+ context_lines: 3
+ verdict_hints:
+ ISSUE_IF: "Assigns user-controlled or API content"
+ OK_IF: "Static trusted HTML only; prefer v-html with sanitization"
+
+ - id: document-write
+ description: document.write usage (XSS and performance)
+ grep_pattern: 'document\.write'
+ file_glob: '*.{vue,ts}'
+ context_lines: 2
+ verdict_hints:
+ ISSUE_IF: "Almost never acceptable in modern apps"
+ OK_IF: "Never OK - refactor to DOM manipulation"
+
+ # Sensitive Data Exposure
+ - id: console-log-sensitive
+ description: Console logging potentially sensitive data
+ grep_pattern: 'console\.(log|debug|info)\(.*\b(token|password|secret|auth|credential|key)\b'
+ file_glob: '*.{vue,ts}'
+ context_lines: 2
+ verdict_hints:
+ ISSUE_IF: "Logs sensitive values - visible in browser devtools"
+ OK_IF: "Logs field names only, not values; or dev-only code"
+
+ - id: localstorage-sensitive
+ description: Storing sensitive data in localStorage
+ grep_pattern: 'localStorage\.setItem\([^)]*\b(password|secret|credit|ssn)\b'
+ file_glob: '*.{vue,ts}'
+ context_lines: 3
+ verdict_hints:
+ ISSUE_IF: "Sensitive data in localStorage - accessible to XSS"
+ OK_IF: "Never OK for passwords/secrets; tokens need careful review"
+
+ - id: token-in-url
+ description: Auth token in URL or query params
+ grep_pattern: '(url|href|src).*\?(.*&)?(token|auth|key)='
+ file_glob: '*.{vue,ts}'
+ context_lines: 3
+ verdict_hints:
+ ISSUE_IF: "Token in URL - logged in server logs, browser history"
+ OK_IF: "One-time tokens (email verification); not session tokens"
+
+ - id: hardcoded-secret
+ description: Hardcoded API key or secret
+ grep_pattern: '(api_?key|secret|password)\s*[:=]\s*["\'][^"\']{8,}["\']'
+ file_glob: '*.{vue,ts}'
+ context_lines: 2
+ exclude_tests: false
+ verdict_hints:
+ ISSUE_IF: "Real credential in source code"
+ OK_IF: "Placeholder; real value from VITE_* env variable"
+
+ # Auth Issues
+ - id: auth-check-client-only
+ description: Auth check only on client side
+ grep_pattern: '(isAuthenticated|isLoggedIn|user)\s*\?\s*<'
+ file_glob: '*.vue'
+ context_lines: 5
+ verdict_hints:
+ ISSUE_IF: "UI hidden but API not protected - security by obscurity"
+ OK_IF: "UI hint only; API has proper auth middleware"
+
+ - id: token-in-state
+ description: Auth token stored in reactive state without persistence consideration
+ grep_pattern: 'ref<.*token.*>\s*\('
+ file_glob: '*.{vue,ts}'
+ context_lines: 4
+ verdict_hints:
+ ISSUE_IF: "Token in memory only - lost on refresh without secure persistence"
+ OK_IF: "Intentional session-only token; or persisted securely elsewhere"
+
+ # Game-Specific Security
+ - id: game-state-trust
+ description: Trusting game state from client
+ grep_pattern: '(damage|hp|energy|prize|winner)\s*=\s*(props|data|event)'
+ file_glob: '*.{vue,ts}'
+ context_lines: 5
+ verdict_hints:
+ ISSUE_IF: "Game state from client used without server validation"
+ OK_IF: "Display only; actual state from server via WebSocket"
+
+ - id: hidden-info-client
+ description: Accessing hidden game info that should be server-only
+ grep_pattern: '\.(deck|opponentHand|prizes)\.(cards|contents|order)'
+ file_glob: '*.{vue,ts}'
+ context_lines: 4
+ verdict_hints:
+ ISSUE_IF: "Accessing hidden zone contents - should only have counts"
+ OK_IF: "Own hand/prizes (visible to player); or mock data in tests"
+
+ # Input Validation
+ - id: unsanitized-user-input
+ description: User input used directly without validation
+ grep_pattern: '(v-model|@input).*\n.*(?!.*validate|sanitize|escape)'
+ multiline: true
+ file_glob: '*.vue'
+ context_lines: 5
+ verdict_hints:
+ ISSUE_IF: "Input sent to API without validation"
+ OK_IF: "Validated before submission; or display-only local state"
+
+ - id: eval-usage
+ description: eval() or Function() constructor
+ grep_pattern: '\b(eval|Function)\s*\('
+ file_glob: '*.{vue,ts}'
+ context_lines: 3
+ verdict_hints:
+ ISSUE_IF: "Executing dynamic code - XSS/injection risk"
+ OK_IF: "Never OK in frontend code"
+
+ # Network Security
+ - id: http-url
+ description: HTTP (non-HTTPS) URL in code
+ grep_pattern: '["\']http://(?!localhost|127\.0\.0\.1)'
+ file_glob: '*.{vue,ts}'
+ context_lines: 2
+ verdict_hints:
+ ISSUE_IF: "Production URL using HTTP - data sent unencrypted"
+ OK_IF: "Development URL; production uses HTTPS"
+
+ - id: cors-credential-any
+ description: Credentials with wildcard or any origin
+ grep_pattern: 'credentials:\s*["\']include["\']'
+ file_glob: '*.{vue,ts}'
+ context_lines: 4
+ verdict_hints:
+ ISSUE_IF: "Credentials sent to any origin - CSRF risk"
+ OK_IF: "Server has strict CORS; or same-origin requests only"
diff --git a/frontend/.gitignore b/frontend/.gitignore
new file mode 100644
index 0000000..a547bf3
--- /dev/null
+++ b/frontend/.gitignore
@@ -0,0 +1,24 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-ssr
+*.local
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
diff --git a/frontend/README.md b/frontend/README.md
new file mode 100644
index 0000000..33895ab
--- /dev/null
+++ b/frontend/README.md
@@ -0,0 +1,5 @@
+# Vue 3 + TypeScript + Vite
+
+This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 `
+