# CLAUDE.md — app/

## Nuxt Frontend (SPA Mode)

This is a **client-side only** Nuxt 4 app (`ssr: false`). No server routes, no `server/` directory. All data comes from PocketBase via its SDK.

## PocketBase Client Access

```ts
const { pb, authStore } = usePocketBase()
```

- `pb` — PocketBase SDK instance (configured in `plugins/pocketbase.ts`)
- `authStore` — reactive Vue ref wrapping PocketBase's auth state (`.isValid`, `.record`, `.token`)

**Never instantiate PocketBase directly.** Always use the `usePocketBase()` composable.

## Stores (Pinia)

All backend interaction goes through Pinia stores in `stores/`:

| Store | Collection | Key patterns |
|-------|-----------|--------------|
| `user.ts` | `users` | Auth flows (OTP, OAuth), profile CRUD, `isAuthenticated` getter |
| `counter.ts` | `counters` | Realtime subscription, increment/reset |
| `notifications.ts` | `notifications` + `fcm_tokens` | Realtime subscription, FCM token registration |
| `avatar.ts` | `users` (file field) | Avatar URL getter, file upload |

**Realtime pattern:** Stores call `pb.collection('x').subscribe('*', callback)` in a `subscribeToChanges()` action. Components call this on mount and `unsubscribe()` on unmount.

## Routing & Middleware

Pages use Nuxt file-based routing. Two middleware:

- `middleware/auth.ts` — route-level guard, add via `definePageMeta({ middleware: 'auth' })`
- `middleware/00.fetchUser.global.ts` — runs on every navigation, refreshes auth token

Two layouts: `default` (with AppHeader) and `empty` (login/confirm pages).

## Components

Organized by feature domain: `App/` (header, notifications), `Counter/` (widget), `Profile/` (avatar, upload, lang select). Components use Nuxt UI v3 primitives (`UButton`, `UCard`, `UForm`, `UModal`, etc.).

## Styling

Tailwind CSS v4 + Nuxt UI. Theme colors set in `app.config.ts` (primary: teal, neutral: slate). Custom font: Public Sans (defined in `assets/css/main.css`).

## i18n

Strategy: `no_prefix` — URLs are not prefixed with locale. Two locales: `en`, `de`. Translation files in `i18n/locales/`. Use `$t('key')` in templates or `const { t } = useI18n()` in setup.

`LanguageCode` type defined in `types/i18n.types.ts`.

## Types

- `types/pocketbase.types.ts` — **auto-generated**, do not edit. Contains typed collection names (`Collections` enum) and record interfaces. Regenerate with `pnpm pocketbase:types`.
- `types/i18n.types.ts` — manual type for supported language codes.

## Mobile (Capacitor)

`app.vue` handles Capacitor-specific setup: deep link listener (`App.addListener('appUrlOpen')`), push notification registration, and safe-area CSS. Check `Capacitor.isNativePlatform()` before using native APIs.
