Frontend Design โ€” Glyphoxa Web Management Service

1. Overview

This document defines the frontend architecture, page designs, component hierarchy, and interaction patterns for the Glyphoxa Web Management Service โ€” a self-service portal for Dungeon Masters to manage campaigns, NPCs, sessions, billing, and more.

Key constraints:

  • Self-service for non-technical DMs โ€” no YAML, no CLI, no API keys
  • Separate service โ€” own Docker container, own domain (app.glyphoxa.com)
  • Mobile-friendly โ€” DMs manage NPCs from their phone during sessions
  • Scale to >1000 users โ€” multi-tenant, tenant-isolated, performant
  • English MVP โ€” i18n-ready for German (and beyond) in Phase 2

Deployment model: The frontend is a standalone SPA served by its own container (nginx or Node SSR), communicating with the Glyphoxa Gateway API over HTTPS. This decouples frontend release cycles from backend deployments and allows independent scaling, CDN caching, and A/B testing.

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”         โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  app.glyphoxa.com    โ”‚  HTTPS  โ”‚  api.glyphoxa.com    โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚ โ”€โ”€โ”€โ”€โ”€โ”€โ†’ โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚
โ”‚  โ”‚  SPA (Next.js) โ”‚  โ”‚         โ”‚  โ”‚  Gateway API   โ”‚  โ”‚
โ”‚  โ”‚  Docker: nginx  โ”‚  โ”‚         โ”‚  โ”‚  (Go)          โ”‚  โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚         โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜         โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
         โ”‚                                โ”‚
         โ”‚  CDN (Cloudflare)              โ”‚  PostgreSQL + Vault
         โ”‚  Static assets cached          โ”‚  Per-tenant schemas

2. Tech Stack

Framework: Next.js 15 (App Router)

Choice Rationale
Next.js 15 SSR for landing/marketing pages (SEO), SPA behavior for dashboard. App Router enables React Server Components for fast initial loads. Standalone output mode for Docker.
React 19 Concurrent features, Suspense boundaries, use() hook for data loading.
TypeScript 5.5+ End-to-end type safety with generated API client.

Why Next.js over Vite+React (from original plan): The original plan recommended Vite+React embedded in the gateway binary. Since weโ€™re now a separate service with its own domain, Next.js provides critical advantages:

  • SSR for the landing page (SEO, social sharing, fast FCP)
  • Built-in API routes for OAuth callback handling
  • Image optimization for NPC avatars
  • Middleware for auth guards (edge runtime)
  • output: 'standalone' produces a minimal Docker image (~50MB)

Vite remains an excellent choice if we revert to gateway-embedded. The architecture below is framework-agnostic at the component level.

UI & Styling

Choice Rationale
shadcn/ui Copy-paste Radix primitives. No npm lock-in. Accessible by default (ARIA). Customizable with Tailwind.
Tailwind CSS 4 Utility-first, design tokens via CSS variables, responsive out of the box.
Lucide icons Tree-shakeable, consistent with shadcn/ui.
Geist font Clean, modern, excellent readability at small sizes.

Data & State

Choice Rationale
TanStack Query v5 Server-state cache, background refetch, optimistic mutations, infinite scroll. Replaces Redux for API data.
Zustand Minimal client state (sidebar open, theme, modal stack). No boilerplate.
React Hook Form + Zod Performant forms with schema validation. Zod schemas shared with API types.
nuqs Type-safe URL search params for filters, pagination, search. Shareable URLs.

Visualization & Real-time

Choice Rationale
Recharts Lightweight, composable, responsive charts for usage/billing. Built on D3.
Native WebSocket Live transcript streaming. TanStack Query subscription pattern for reconnect.
Web Audio API Voice preview playback with waveform visualization.
@dnd-kit Accessible drag-and-drop for NPC ordering, behavior rules.

i18n

Choice Rationale
next-intl Next.js-native i18n with App Router support. ICU message format.
English MVP All strings extracted to messages/en.json from day one. German locale added in Phase 2.

API Client

Choice Rationale
openapi-typescript + openapi-fetch Generate types from OpenAPI 3.1 spec. Zero-runtime type safety. Works with TanStack Query.

3. Routing Structure

/                                    Landing page (public, SSR)
/pricing                             Pricing page (public, SSR)
/login                               Login/Register (public)
/auth/callback/discord               OAuth2 callback handler
/auth/callback/google                OAuth2 callback handler

/dashboard                           DM dashboard (authenticated)
/campaigns                           Campaign list
/campaigns/new                       Create campaign
/campaigns/[id]                      Campaign detail/edit
/campaigns/[id]/npcs                 NPC list for campaign
/campaigns/[id]/npcs/new             Create NPC
/campaigns/[id]/npcs/[npcId]         NPC editor
/campaigns/[id]/sessions             Session history for campaign
/campaigns/[id]/lore                 Lore editor (markdown)

/sessions                            All sessions (across campaigns)
/sessions/[id]                       Session detail + transcript
/sessions/[id]/live                  Live session monitor (WebSocket)
/sessions/[id]/replay                Session replay (future)

/billing                             Billing & usage
/billing/plans                       Plan comparison & upgrade

/settings                            Account settings
/settings/api-keys                   API key management (BYOK)
/settings/notifications              Notification preferences

/admin                               Super admin dashboard
/admin/tenants                       Tenant management
/admin/tenants/[id]                  Tenant detail
/admin/system                        System health & metrics
/admin/billing                       All-tenant billing overview
/admin/users                         User management

/support                             Help & support
/support/tickets                     Ticket list
/support/tickets/new                 Create ticket
/support/docs                        Documentation links

Route Groups & Layouts

app/
โ”œโ”€โ”€ (public)/                        # No auth required
โ”‚   โ”œโ”€โ”€ layout.tsx                   # Marketing header/footer
โ”‚   โ”œโ”€โ”€ page.tsx                     # Landing page
โ”‚   โ”œโ”€โ”€ pricing/page.tsx
โ”‚   โ”œโ”€โ”€ login/page.tsx
โ”‚   โ””โ”€โ”€ auth/callback/[provider]/route.ts  # API route for OAuth
โ”‚
โ”œโ”€โ”€ (app)/                           # Authenticated, sidebar layout
โ”‚   โ”œโ”€โ”€ layout.tsx                   # Sidebar + topbar + auth guard
โ”‚   โ”œโ”€โ”€ dashboard/page.tsx
โ”‚   โ”œโ”€โ”€ campaigns/
โ”‚   โ”‚   โ”œโ”€โ”€ page.tsx                 # List
โ”‚   โ”‚   โ”œโ”€โ”€ new/page.tsx
โ”‚   โ”‚   โ””โ”€โ”€ [id]/
โ”‚   โ”‚       โ”œโ”€โ”€ page.tsx             # Detail/edit
โ”‚   โ”‚       โ”œโ”€โ”€ npcs/
โ”‚   โ”‚       โ”‚   โ”œโ”€โ”€ page.tsx
โ”‚   โ”‚       โ”‚   โ”œโ”€โ”€ new/page.tsx
โ”‚   โ”‚       โ”‚   โ””โ”€โ”€ [npcId]/page.tsx
โ”‚   โ”‚       โ”œโ”€โ”€ sessions/page.tsx
โ”‚   โ”‚       โ””โ”€โ”€ lore/page.tsx
โ”‚   โ”œโ”€โ”€ sessions/
โ”‚   โ”‚   โ”œโ”€โ”€ page.tsx
โ”‚   โ”‚   โ””โ”€โ”€ [id]/
โ”‚   โ”‚       โ”œโ”€โ”€ page.tsx             # Detail + transcript
โ”‚   โ”‚       โ”œโ”€โ”€ live/page.tsx
โ”‚   โ”‚       โ””โ”€โ”€ replay/page.tsx
โ”‚   โ”œโ”€โ”€ billing/
โ”‚   โ”‚   โ”œโ”€โ”€ page.tsx
โ”‚   โ”‚   โ””โ”€โ”€ plans/page.tsx
โ”‚   โ”œโ”€โ”€ settings/
โ”‚   โ”‚   โ”œโ”€โ”€ page.tsx
โ”‚   โ”‚   โ”œโ”€โ”€ api-keys/page.tsx
โ”‚   โ”‚   โ””โ”€โ”€ notifications/page.tsx
โ”‚   โ””โ”€โ”€ support/
โ”‚       โ”œโ”€โ”€ page.tsx
โ”‚       โ”œโ”€โ”€ tickets/
โ”‚       โ”‚   โ”œโ”€โ”€ page.tsx
โ”‚       โ”‚   โ””โ”€โ”€ new/page.tsx
โ”‚       โ””โ”€โ”€ docs/page.tsx
โ”‚
โ””โ”€โ”€ (admin)/                         # super_admin only
    โ”œโ”€โ”€ layout.tsx                   # Admin layout with system nav
    โ””โ”€โ”€ admin/
        โ”œโ”€โ”€ page.tsx                 # Admin dashboard
        โ”œโ”€โ”€ tenants/
        โ”‚   โ”œโ”€โ”€ page.tsx
        โ”‚   โ””โ”€โ”€ [id]/page.tsx
        โ”œโ”€โ”€ system/page.tsx
        โ”œโ”€โ”€ billing/page.tsx
        โ””โ”€โ”€ users/page.tsx

4. Authentication Flow

Login Options

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                                                          โ”‚
โ”‚              Welcome to Glyphoxa                         โ”‚
โ”‚                                                          โ”‚
โ”‚    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”      โ”‚
โ”‚    โ”‚  ๐ŸŽฎ  Continue with Discord                   โ”‚      โ”‚
โ”‚    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜      โ”‚
โ”‚    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”      โ”‚
โ”‚    โ”‚  G   Continue with Google                    โ”‚      โ”‚
โ”‚    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜      โ”‚
โ”‚                                                          โ”‚
โ”‚    โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ or โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€                   โ”‚
โ”‚                                                          โ”‚
โ”‚    Email:    [                              ]             โ”‚
โ”‚    Password: [                              ]             โ”‚
โ”‚                                                          โ”‚
โ”‚    [         Sign in          ]                           โ”‚
โ”‚                                                          โ”‚
โ”‚    Don't have an account?  Sign up                       โ”‚
โ”‚    Forgot password?                                      โ”‚
โ”‚                                                          โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

OAuth2 Flow (Discord primary)

User clicks "Continue with Discord"
  โ†’ Next.js redirects to Discord OAuth2 authorize URL
     (scopes: identify, email, guilds)
  โ†’ User authorizes on Discord
  โ†’ Discord redirects to /auth/callback/discord?code=...
  โ†’ Next.js API route exchanges code for tokens
  โ†’ API route calls POST /api/v1/auth/discord with Discord access token
  โ†’ Backend validates, creates/finds user, returns JWT
  โ†’ Next.js sets JWT in HttpOnly cookie (Secure, SameSite=Lax)
  โ†’ Redirect to /dashboard

Token Management

  • Access token: JWT, 15min expiry, stored in HttpOnly cookie
  • Refresh token: Opaque, 30-day expiry, stored in HttpOnly cookie
  • Silent refresh: TanStack Query interceptor calls /api/v1/auth/refresh when a 401 is received. On failure, redirect to /login.
  • CSRF protection: Double-submit cookie pattern. The API requires a X-CSRF-Token header matching a non-HttpOnly cookie value.

Role-Based UI

The JWT payload contains { user_id, tenant_id, role }. The frontend uses role to conditionally render navigation items and redirect unauthorized access:

Role Visible Sections
super_admin Everything + /admin/*
tenant_admin Dashboard, campaigns, NPCs, sessions, billing, settings, users
dm Dashboard, campaigns (own), NPCs, sessions (own), settings
viewer Dashboard (read-only), sessions (read-only), transcripts

Middleware in (app)/layout.tsx reads the JWT cookie server-side and redirects to /login if expired or missing. Role checks happen both client-side (hide UI) and server-side (API returns 403).


5. Page Designs

5.1 Landing Page (/)

Purpose: Marketing, feature overview, pricing CTA, trust signals.

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  [Logo] Glyphoxa          Features  Pricing  Docs     [Sign In]     โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                                      โ”‚
โ”‚           Bring Your NPCs to Life                                    โ”‚
โ”‚                                                                      โ”‚
โ”‚     AI-powered voice NPCs for tabletop RPGs.                         โ”‚
โ”‚     Distinct voices. Real personalities.                             โ”‚
โ”‚     Persistent memory across sessions.                               โ”‚
โ”‚                                                                      โ”‚
โ”‚     [  Get Started Free  ]    [ Watch Demo โ–ถ ]                       โ”‚
โ”‚                                                                      โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                                      โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                  โ”‚
โ”‚  โ”‚ ๐ŸŽญ Unique   โ”‚  โ”‚ ๐ŸŽ™ Real     โ”‚  โ”‚ ๐Ÿง  Memory   โ”‚                  โ”‚
โ”‚  โ”‚ Personalitiesโ”‚  โ”‚ Voices      โ”‚  โ”‚ That Lasts  โ”‚                  โ”‚
โ”‚  โ”‚             โ”‚  โ”‚             โ”‚  โ”‚             โ”‚                  โ”‚
โ”‚  โ”‚ Each NPC hasโ”‚  โ”‚ ElevenLabs, โ”‚  โ”‚ NPCs recall โ”‚                  โ”‚
โ”‚  โ”‚ their own   โ”‚  โ”‚ Azure, or   โ”‚  โ”‚ past events,โ”‚                  โ”‚
โ”‚  โ”‚ personality โ”‚  โ”‚ bring your  โ”‚  โ”‚ build        โ”‚                  โ”‚
โ”‚  โ”‚ and behaviorโ”‚  โ”‚ own voice   โ”‚  โ”‚ relationshipsโ”‚                  โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                  โ”‚
โ”‚                                                                      โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                                      โ”‚
โ”‚  How It Works                                                        โ”‚
โ”‚                                                                      โ”‚
โ”‚  1. Create your campaign and define NPCs                             โ”‚
โ”‚     โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                 โ”‚
โ”‚     โ”‚  [Screenshot: NPC editor with voice config]  โ”‚                 โ”‚
โ”‚     โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                 โ”‚
โ”‚                                                                      โ”‚
โ”‚  2. Start a session โ€” NPCs join your voice channel                   โ”‚
โ”‚     โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                 โ”‚
โ”‚     โ”‚  [Screenshot: Discord voice with NPC]        โ”‚                 โ”‚
โ”‚     โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                 โ”‚
โ”‚                                                                      โ”‚
โ”‚  3. Players talk naturally โ€” NPCs respond in character               โ”‚
โ”‚     โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                 โ”‚
โ”‚     โ”‚  [Screenshot: Live transcript view]          โ”‚                 โ”‚
โ”‚     โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                 โ”‚
โ”‚                                                                      โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                                      โ”‚
โ”‚  Pricing โ€” see /pricing for detail                                   โ”‚
โ”‚                                                                      โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”     โ”‚
โ”‚  โ”‚ Free     โ”‚  โ”‚ Adventurerโ”‚  โ”‚ Dungeon Masterโ”‚  โ”‚ Guild      โ”‚     โ”‚
โ”‚  โ”‚ $0/mo    โ”‚  โ”‚ $9/mo     โ”‚  โ”‚ $19/mo        โ”‚  โ”‚ $29/mo     โ”‚     โ”‚
โ”‚  โ”‚ 2 sess.  โ”‚  โ”‚ 8 sess.   โ”‚  โ”‚ Unlimited     โ”‚  โ”‚ + 5 seats  โ”‚     โ”‚
โ”‚  โ”‚ 2 NPCs   โ”‚  โ”‚ 10 NPCs   โ”‚  โ”‚ Premium voice โ”‚  โ”‚ Custom     โ”‚     โ”‚
โ”‚  โ”‚          โ”‚  โ”‚           โ”‚  โ”‚               โ”‚  โ”‚ voices     โ”‚     โ”‚
โ”‚  โ”‚ [Start]  โ”‚  โ”‚ [Start]   โ”‚  โ”‚ [Start]       โ”‚  โ”‚ [Contact]  โ”‚     โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜     โ”‚
โ”‚                                                                      โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                                      โ”‚
โ”‚  Trusted by DMs running campaigns in D&D 5e, Pathfinder 2e,         โ”‚
โ”‚  and more. Works with Discord and WebRTC.                            โ”‚
โ”‚                                                                      โ”‚
โ”‚  [Logo] [Logo] [Logo]  (game system logos, Discord logo)             โ”‚
โ”‚                                                                      โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚  ยฉ 2026 Glyphoxa   Privacy   Terms   Discord   GitHub               โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Implementation notes:

  • Server-rendered (RSC) for SEO and fast FCP
  • Hero section uses subtle CSS animation (floating NPC silhouettes)
  • โ€œWatch Demoโ€ opens an inline video player (lazy-loaded)
  • Pricing cards link to /pricing with anchor to selected tier
  • Mobile: single-column stack, pricing cards horizontally scrollable

5.2 Login / Register (/login)

See wireframe in Section 4 above. Additional details:

  • Social auth buttons prominent at top (Discord primary โ€” most DMs have it)
  • Email/password form below the separator
  • Register toggle: โ€œDonโ€™t have an account? Sign upโ€ swaps the form to registration (name + email + password + confirm), or a separate /register page
  • Password requirements: 8+ chars, shown inline as user types
  • Error states: Inline validation, red border + error text below field
  • Loading state: Button shows spinner, disabled during request
  • Mobile: Full-width, large touch targets (min 44px)

5.3 Dashboard (/dashboard)

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  โ˜ฐ  Glyphoxa                                    [๐Ÿ””] [Avatar โ–ผ]    โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚            โ”‚                                                         โ”‚
โ”‚  Dashboard โ”‚   Good evening, Luk                                     โ”‚
โ”‚  โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”‚                                                         โ”‚
โ”‚  Campaigns โ”‚   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”         โ”‚
โ”‚  Sessions  โ”‚   โ”‚ Campaigns  โ”‚ โ”‚  Active    โ”‚ โ”‚  This Month โ”‚         โ”‚
โ”‚  Billing   โ”‚   โ”‚     3      โ”‚ โ”‚  Sessions  โ”‚ โ”‚  47 / 100h  โ”‚         โ”‚
โ”‚  โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”‚   โ”‚            โ”‚ โ”‚     2      โ”‚ โ”‚  โ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘  โ”‚         โ”‚
โ”‚  Settings  โ”‚   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜         โ”‚
โ”‚  Support   โ”‚                                                         โ”‚
โ”‚            โ”‚   Active Sessions                                       โ”‚
โ”‚            โ”‚   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”        โ”‚
โ”‚            โ”‚   โ”‚ ๐ŸŸข Die Chroniken von Rabenheim             โ”‚        โ”‚
โ”‚            โ”‚   โ”‚   Guild: Pen & Paper DE  โ€ข  Duration: 1:42 โ”‚        โ”‚
โ”‚            โ”‚   โ”‚   NPCs: Heinrich, Elara, Erzรคhler          โ”‚        โ”‚
โ”‚            โ”‚   โ”‚                    [View Live] [Stop]       โ”‚        โ”‚
โ”‚            โ”‚   โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค        โ”‚
โ”‚            โ”‚   โ”‚ ๐ŸŸข Tutorial Campaign                       โ”‚        โ”‚
โ”‚            โ”‚   โ”‚   Guild: Demo Server  โ€ข  Duration: 0:05    โ”‚        โ”‚
โ”‚            โ”‚   โ”‚   NPCs: Guide                              โ”‚        โ”‚
โ”‚            โ”‚   โ”‚                    [View Live] [Stop]       โ”‚        โ”‚
โ”‚            โ”‚   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜        โ”‚
โ”‚            โ”‚                                                         โ”‚
โ”‚            โ”‚   Recent Activity                                       โ”‚
โ”‚            โ”‚   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”        โ”‚
โ”‚            โ”‚   โ”‚  โœ“  Session ended: Rabenheim (1h 23m)      โ”‚        โ”‚
โ”‚            โ”‚   โ”‚     Today, 19:42                            โ”‚        โ”‚
โ”‚            โ”‚   โ”‚  +  NPC created: "Erzรคhler" in Rabenheim   โ”‚        โ”‚
โ”‚            โ”‚   โ”‚     Today, 14:15                            โ”‚        โ”‚
โ”‚            โ”‚   โ”‚  โœ“  Session ended: Tutorial (0h 12m)       โ”‚        โ”‚
โ”‚            โ”‚   โ”‚     Yesterday, 21:30                        โ”‚        โ”‚
โ”‚            โ”‚   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜        โ”‚
โ”‚            โ”‚                                                         โ”‚
โ”‚            โ”‚   Quick Actions                                         โ”‚
โ”‚            โ”‚   [+ New Campaign]  [+ New NPC]  [๐Ÿ“„ View Transcripts]  โ”‚
โ”‚            โ”‚                                                         โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Key interactions:

  • Metric cards are clickable โ€” link to respective detail pages
  • Active sessions auto-refresh every 10s via TanStack Query refetchInterval
  • โ€œView Liveโ€ navigates to /sessions/[id]/live
  • โ€œStopโ€ shows a confirmation dialog before calling DELETE /api/v1/sessions/{id}
  • Activity feed is a reverse-chronological list (last 20 items, โ€œShow moreโ€ link)
  • Greeting uses time-of-day logic (morning/afternoon/evening)
  • Mobile: Metric cards in a 2x2 grid, sidebar collapses to hamburger menu

5.4 Campaign Management (/campaigns, /campaigns/[id])

Campaign List:

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  Campaigns                                   [+ New Campaign]   โ”‚
โ”‚                                                                  โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”     โ”‚
โ”‚  โ”‚  ๐Ÿฐ Die Chroniken von    โ”‚  โ”‚  ๐Ÿ“š Tutorial Campaign     โ”‚     โ”‚
โ”‚  โ”‚     Rabenheim             โ”‚  โ”‚                           โ”‚     โ”‚
โ”‚  โ”‚                           โ”‚  โ”‚  System: D&D 5e           โ”‚     โ”‚
โ”‚  โ”‚  System: Das Schwarze Augeโ”‚  โ”‚  NPCs: 1                  โ”‚     โ”‚
โ”‚  โ”‚  NPCs: 5                  โ”‚  โ”‚  Last session: 2 days ago โ”‚     โ”‚
โ”‚  โ”‚  Last session: 2 hours agoโ”‚  โ”‚                           โ”‚     โ”‚
โ”‚  โ”‚                           โ”‚  โ”‚  [Open]                   โ”‚     โ”‚
โ”‚  โ”‚  ๐ŸŸข Active session        โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜     โ”‚
โ”‚  โ”‚  [Open]                   โ”‚                                   โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”     โ”‚
โ”‚                                 โ”‚  + Create New Campaign    โ”‚     โ”‚
โ”‚                                 โ”‚                           โ”‚     โ”‚
โ”‚                                 โ”‚  Click to get started     โ”‚     โ”‚
โ”‚                                 โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜     โ”‚
โ”‚                                                                  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Campaign Detail/Edit (/campaigns/[id]):

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  โ† Campaigns  /  Die Chroniken von Rabenheim        [Save] [โ‹ฏ] โ”‚
โ”‚                                                                  โ”‚
โ”‚  โ”Œโ”€ Details โ”€โ”ฌโ”€ NPCs โ”€โ”ฌโ”€ Lore โ”€โ”ฌโ”€ Sessions โ”€โ”                  โ”‚
โ”‚  โ”‚           โ”‚        โ”‚        โ”‚             โ”‚                  โ”‚
โ”‚  โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                  โ”‚
โ”‚                                                                  โ”‚
โ”‚  Campaign Name:                                                  โ”‚
โ”‚  [ Die Chroniken von Rabenheim              ]                    โ”‚
โ”‚                                                                  โ”‚
โ”‚  Game System:                                                    โ”‚
โ”‚  [ Das Schwarze Auge โ–ผ ]                                         โ”‚
โ”‚                                                                  โ”‚
โ”‚  Description:                                                    โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”‚
โ”‚  โ”‚  **B** _I_ ~~S~~ `<>` H1 H2 โ€ข โ”€โ”€ ๐Ÿ”— ๐Ÿ“ท                โ”‚    โ”‚
โ”‚  โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค    โ”‚
โ”‚  โ”‚  Die Stadt Rabenheim liegt am Rand des Dรผsterwalds.     โ”‚    โ”‚
โ”‚  โ”‚  Seit dem Verschwinden des alten Bรผrgermeisters          โ”‚    โ”‚
โ”‚  โ”‚  herrscht Unruhe in der Bevรถlkerung...                  โ”‚    โ”‚
โ”‚  โ”‚                                                          โ”‚    โ”‚
โ”‚  โ”‚  ## Wichtige Orte                                        โ”‚    โ”‚
โ”‚  โ”‚  - Das Rathaus                                           โ”‚    โ”‚
โ”‚  โ”‚  - Der Dรผsterwald                                        โ”‚    โ”‚
โ”‚  โ”‚  - Die Taverne "Zum Goldenen Raben"                      โ”‚    โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ”‚
โ”‚                                                                  โ”‚
โ”‚  Campaign Settings (JSONB):                                      โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”‚
โ”‚  โ”‚  Language:     [ Deutsch โ–ผ ]                              โ”‚    โ”‚
โ”‚  โ”‚  Max Players:  [ 6        ]                              โ”‚    โ”‚
โ”‚  โ”‚  Auto-recap:   [โœ“]                                       โ”‚    โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ”‚
โ”‚                                                                  โ”‚
โ”‚  โ”€โ”€ Danger Zone โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€   โ”‚
โ”‚  [ Delete Campaign ]  (requires typing campaign name)            โ”‚
โ”‚                                                                  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Tab navigation: Details | NPCs | Lore | Sessions โ€” each tab is a sub-route that preserves campaign context without full page reload.

Lore editor: Uses a split-pane markdown editor (edit left, preview right) built with @uiw/react-md-editor or a custom Tiptap setup. Supports image upload via drag-and-drop (stored as presigned S3 URLs or inline base64 for MVP).

5.5 NPC Editor (/campaigns/[id]/npcs/[npcId])

The most complex page. Organized into collapsible sections with a sticky save bar at the top.

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  โ† Rabenheim / NPCs  /  Heinrich der Wรคchter                        โ”‚
โ”‚                                              [Discard] [Save Changes]โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”โ”‚
โ”‚  โ”‚                               โ”‚                                  โ”‚โ”‚
โ”‚  โ”‚  โ”€โ”€ Identity โ”€โ”€               โ”‚  โ”€โ”€ Preview โ”€โ”€                   โ”‚โ”‚
โ”‚  โ”‚                               โ”‚                                  โ”‚โ”‚
โ”‚  โ”‚  Name:                        โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚โ”‚
โ”‚  โ”‚  [ Heinrich der Wรคchter  ]    โ”‚  โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”                 โ”‚  โ”‚โ”‚
โ”‚  โ”‚                               โ”‚  โ”‚  โ”‚Avatarโ”‚  Heinrich der   โ”‚  โ”‚โ”‚
โ”‚  โ”‚  Avatar:                      โ”‚  โ”‚  โ”‚  ๐Ÿ›ก๏ธ  โ”‚  Wรคchter        โ”‚  โ”‚โ”‚
โ”‚  โ”‚  [Choose file] or drag here   โ”‚  โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                 โ”‚  โ”‚โ”‚
โ”‚  โ”‚                               โ”‚  โ”‚                            โ”‚  โ”‚โ”‚
โ”‚  โ”‚                               โ”‚  โ”‚  "Halt! Wer geht da?"     โ”‚  โ”‚โ”‚
โ”‚  โ”‚                               โ”‚  โ”‚        [โ–ถ Play Voice]      โ”‚  โ”‚โ”‚
โ”‚  โ”‚                               โ”‚  โ”‚                            โ”‚  โ”‚โ”‚
โ”‚  โ”‚                               โ”‚  โ”‚  Engine: Cascaded          โ”‚  โ”‚โ”‚
โ”‚  โ”‚                               โ”‚  โ”‚  Tier: Standard            โ”‚  โ”‚โ”‚
โ”‚  โ”‚                               โ”‚  โ”‚  Voice: ElevenLabs/Helmut  โ”‚  โ”‚โ”‚
โ”‚  โ”‚                               โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚โ”‚
โ”‚  โ”‚                               โ”‚                                  โ”‚โ”‚
โ”‚  โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”คโ”‚
โ”‚  โ”‚                                                                   โ”‚โ”‚
โ”‚  โ”‚  โ”€โ”€ Personality โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ [โ–ผ collapse]  โ”‚โ”‚
โ”‚  โ”‚                                                                   โ”‚โ”‚
โ”‚  โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”‚โ”‚
โ”‚  โ”‚  โ”‚  Ein strenger aber gerechter Stadtwรคchter, der seit       โ”‚    โ”‚โ”‚
โ”‚  โ”‚  โ”‚  รผber 20 Jahren die Tore von Rabenheim bewacht. Er kennt โ”‚    โ”‚โ”‚
โ”‚  โ”‚  โ”‚  jeden Bewohner beim Namen und misstraut Fremden          โ”‚    โ”‚โ”‚
โ”‚  โ”‚  โ”‚  zunรคchst, kann aber durch Ehrlichkeit รผberzeugt werden.  โ”‚    โ”‚โ”‚
โ”‚  โ”‚  โ”‚                                                           โ”‚    โ”‚โ”‚
โ”‚  โ”‚  โ”‚  Heinrich hat eine tiefe, raue Stimme und spricht in     โ”‚    โ”‚โ”‚
โ”‚  โ”‚  โ”‚  kurzen, prรคgnanten Sรคtzen. Er nennt Respektspersonen    โ”‚    โ”‚โ”‚
โ”‚  โ”‚  โ”‚  beim Titel.                                              โ”‚    โ”‚โ”‚
โ”‚  โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ”‚โ”‚
โ”‚  โ”‚  Characters: 423 / 2000                                          โ”‚โ”‚
โ”‚  โ”‚                                                                   โ”‚โ”‚
โ”‚  โ”‚  โ”€โ”€ Voice โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ [โ–ผ]        โ”‚โ”‚
โ”‚  โ”‚                                                                   โ”‚โ”‚
โ”‚  โ”‚  Provider:    [ ElevenLabs โ–ผ ]                                    โ”‚โ”‚
โ”‚  โ”‚                                                                   โ”‚โ”‚
โ”‚  โ”‚  Voice:       [ Helmut โ–ผ ]   [โ–ถ Preview]                          โ”‚โ”‚
โ”‚  โ”‚                                                                   โ”‚โ”‚
โ”‚  โ”‚  Sample text: [ Halt! Wer geht da? Niemand passiert     ]        โ”‚โ”‚
โ”‚  โ”‚               [ dieses Tor ohne gรผltigen Passierschein.  ]        โ”‚โ”‚
โ”‚  โ”‚                                                                   โ”‚โ”‚
โ”‚  โ”‚  โ”Œโ”€ Waveform โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”          โ”‚โ”‚
โ”‚  โ”‚  โ”‚  โ–โ–‚โ–ƒโ–…โ–‡โ–…โ–ƒโ–‚โ–โ–‚โ–ƒโ–…โ–‡โ–ˆโ–ˆโ–ˆโ–ˆโ–…โ–ƒโ–‚โ–โ–โ–‚โ–ƒโ–…โ–‡โ–…โ–ƒโ–‚โ–โ–‚โ–ƒโ–…โ–‡โ–ˆโ–ˆโ–ˆโ–ˆโ–…โ–ƒ        โ”‚          โ”‚โ”‚
โ”‚  โ”‚  โ”‚  0:00 โ”€โ”€โ”€โ”€โ”€โ—โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ 0:04     โ”‚          โ”‚โ”‚
โ”‚  โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜          โ”‚โ”‚
โ”‚  โ”‚                                                                   โ”‚โ”‚
โ”‚  โ”‚  Pitch:  -10 โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ—โ”€โ”€โ”€โ”€ +10  (-2.0 semitones)              โ”‚โ”‚
โ”‚  โ”‚  Speed:  0.5 โ”€โ”€โ”€โ”€โ”€โ”€โ—โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ 2.0  (1.0x)                       โ”‚โ”‚
โ”‚  โ”‚                                                                   โ”‚โ”‚
โ”‚  โ”‚  Custom Voice Sample:                                             โ”‚โ”‚
โ”‚  โ”‚  [ Upload .wav / .mp3 (max 10MB) ]                                โ”‚โ”‚
โ”‚  โ”‚  Used for voice cloning (ElevenLabs Instant Voice Clone)          โ”‚โ”‚
โ”‚  โ”‚                                                                   โ”‚โ”‚
โ”‚  โ”‚  โ”€โ”€ Engine & Tier โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ [โ–ผ]          โ”‚โ”‚
โ”‚  โ”‚                                                                   โ”‚โ”‚
โ”‚  โ”‚  Engine:                                                          โ”‚โ”‚
โ”‚  โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”            โ”‚โ”‚
โ”‚  โ”‚  โ”‚ โ— Cascaded  โ”‚ โ”‚ โ—‹ S2S       โ”‚ โ”‚ โ—‹ Sentence       โ”‚            โ”‚โ”‚
โ”‚  โ”‚  โ”‚   STTโ†’LLMโ†’  โ”‚ โ”‚   Direct    โ”‚ โ”‚   Cascade        โ”‚            โ”‚โ”‚
โ”‚  โ”‚  โ”‚   TTS       โ”‚ โ”‚   speech-to โ”‚ โ”‚   Hybrid          โ”‚            โ”‚โ”‚
โ”‚  โ”‚  โ”‚             โ”‚ โ”‚   -speech   โ”‚ โ”‚                   โ”‚            โ”‚โ”‚
โ”‚  โ”‚  โ”‚ Best qualityโ”‚ โ”‚ Lowest      โ”‚ โ”‚ Good balance      โ”‚            โ”‚โ”‚
โ”‚  โ”‚  โ”‚             โ”‚ โ”‚ latency     โ”‚ โ”‚                   โ”‚            โ”‚โ”‚
โ”‚  โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜            โ”‚โ”‚
โ”‚  โ”‚                                                                   โ”‚โ”‚
โ”‚  โ”‚  Budget Tier:                                                     โ”‚โ”‚
โ”‚  โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                      โ”‚โ”‚
โ”‚  โ”‚  โ”‚ โ—‹ Fast   โ”‚ โ”‚ โ— Standard   โ”‚ โ”‚ โ—‹ Deep   โ”‚                      โ”‚โ”‚
โ”‚  โ”‚  โ”‚ Quick    โ”‚ โ”‚ Balanced     โ”‚ โ”‚ Thorough โ”‚                      โ”‚โ”‚
โ”‚  โ”‚  โ”‚ responsesโ”‚ โ”‚ quality &    โ”‚ โ”‚ reasoningโ”‚                      โ”‚โ”‚
โ”‚  โ”‚  โ”‚          โ”‚ โ”‚ speed        โ”‚ โ”‚ + tools  โ”‚                      โ”‚โ”‚
โ”‚  โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                      โ”‚โ”‚
โ”‚  โ”‚                                                                   โ”‚โ”‚
โ”‚  โ”‚  โ”€โ”€ Knowledge โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ [โ–ผ]           โ”‚โ”‚
โ”‚  โ”‚                                                                   โ”‚โ”‚
โ”‚  โ”‚  Knowledge Scope (topics this NPC knows about):                   โ”‚โ”‚
โ”‚  โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”             โ”‚โ”‚
โ”‚  โ”‚  โ”‚ [Rabenheim history โœ•] [guard duties โœ•]           โ”‚             โ”‚โ”‚
โ”‚  โ”‚  โ”‚ [city layout โœ•] [+ type to add...]               โ”‚             โ”‚โ”‚
โ”‚  โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜             โ”‚โ”‚
โ”‚  โ”‚                                                                   โ”‚โ”‚
โ”‚  โ”‚  Secret Knowledge (facts NPC knows but won't volunteer):          โ”‚โ”‚
โ”‚  โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”             โ”‚โ”‚
โ”‚  โ”‚  โ”‚ [The mayor's corruption โœ•] [+ type to add...]    โ”‚             โ”‚โ”‚
โ”‚  โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜             โ”‚โ”‚
โ”‚  โ”‚                                                                   โ”‚โ”‚
โ”‚  โ”‚  โ”€โ”€ Behavior Rules โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ [โ–ผ]           โ”‚โ”‚
โ”‚  โ”‚                                                                   โ”‚โ”‚
โ”‚  โ”‚  โ‰ก  Spricht immer Deutsch                          [โœ•]           โ”‚โ”‚
โ”‚  โ”‚  โ‰ก  Misstraut Fremden zunรคchst                     [โœ•]           โ”‚โ”‚
โ”‚  โ”‚  โ‰ก  Nennt Respektspersonen beim Titel              [โœ•]           โ”‚โ”‚
โ”‚  โ”‚  [+ Add Rule]                                                     โ”‚โ”‚
โ”‚  โ”‚                                                                   โ”‚โ”‚
โ”‚  โ”‚  (โ‰ก = drag handle for reordering via @dnd-kit)                    โ”‚โ”‚
โ”‚  โ”‚                                                                   โ”‚โ”‚
โ”‚  โ”‚  โ”€โ”€ Advanced โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ [โ–ผ]          โ”‚โ”‚
โ”‚  โ”‚                                                                   โ”‚โ”‚
โ”‚  โ”‚  MCP Tools:                                                       โ”‚โ”‚
โ”‚  โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”             โ”‚โ”‚
โ”‚  โ”‚  โ”‚ [patrol_route โœ•] [check_papers โœ•] [+ add...]    โ”‚             โ”‚โ”‚
โ”‚  โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜             โ”‚โ”‚
โ”‚  โ”‚                                                                   โ”‚โ”‚
โ”‚  โ”‚  [โœ“] Address Only โ€” only responds when directly addressed         โ”‚โ”‚
โ”‚  โ”‚  [ ] GM Helper โ€” acts as narrator/GM assistant (1 per campaign)   โ”‚โ”‚
โ”‚  โ”‚                                                                   โ”‚โ”‚
โ”‚  โ”‚  Custom Attributes (JSON):                                        โ”‚โ”‚
โ”‚  โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”             โ”‚โ”‚
โ”‚  โ”‚  โ”‚  {                                                โ”‚             โ”‚โ”‚
โ”‚  โ”‚  โ”‚    "alignment": "lawful neutral",                 โ”‚             โ”‚โ”‚
โ”‚  โ”‚  โ”‚    "age": 52,                                     โ”‚             โ”‚โ”‚
โ”‚  โ”‚  โ”‚    "faction": "Stadtwache"                        โ”‚             โ”‚โ”‚
โ”‚  โ”‚  โ”‚  }                                                โ”‚             โ”‚โ”‚
โ”‚  โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜             โ”‚โ”‚
โ”‚  โ”‚                                                                   โ”‚โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Key interactions:

  • Voice preview flow:
    1. User selects provider + voice ID + adjusts pitch/speed
    2. User types or accepts default sample text
    3. Clicks โ€œPreviewโ€ โ†’ POST /api/v1/npcs/{id}/voice-preview with { text, voice_config } body
    4. Returns audio blob (opus/mp3)
    5. Web Audio API plays audio, waveform visualizer shows amplitude
    6. Rate-limited: 5 previews/minute client-side, server enforces per-tenant
  • Custom voice upload:
    1. User uploads .wav/.mp3 file (max 10MB)
    2. Client validates format and duration (5-30 seconds)
    3. POST /api/v1/voices/upload with multipart form data
    4. Backend sends to ElevenLabs Instant Voice Clone API
    5. Returns a new voice_id that can be selected in the voice dropdown
    6. Show progress bar during upload + cloning
  • Drag-and-drop behavior rules: @dnd-kit/core + @dnd-kit/sortable for reordering. Keyboard accessible (Enter to grab, arrows to move, Enter to drop). Persists order index.

  • Tag inputs (knowledge scope, secrets, tools): Combobox with free-text entry. Type to filter existing tags, Enter to add. Click โœ• to remove. Tags are stored as string[] in the NPC definition.

  • Unsaved changes warning: useBeforeUnload hook + React Router blocker. Sticky save bar turns yellow when form is dirty. โ€œDiscardโ€ resets to server state.

  • Mobile layout: Single column. Preview panel moves above the form. Collapsible sections start collapsed except Identity and Personality. Voice controls use full-width sliders.

5.6 Session Monitoring (/sessions, /sessions/[id]/live)

Session List:

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  Sessions                              [Active โ–ผ] [All time โ–ผ]   โ”‚
โ”‚                                                                   โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚
โ”‚  โ”‚Status โ”‚ Campaign          โ”‚ Guild    โ”‚ Duration โ”‚ Actions    โ”‚ โ”‚
โ”‚  โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚
โ”‚  โ”‚ ๐ŸŸข    โ”‚ Rabenheim         โ”‚ PP DE    โ”‚ 1:42:15  โ”‚ [Live] [โ‹ฏ]โ”‚ โ”‚
โ”‚  โ”‚ ๐ŸŸข    โ”‚ Tutorial          โ”‚ Demo     โ”‚ 0:05:30  โ”‚ [Live] [โ‹ฏ]โ”‚ โ”‚
โ”‚  โ”‚ โšช    โ”‚ Rabenheim         โ”‚ PP DE    โ”‚ 1:23:00  โ”‚ [View] [โ‹ฏ]โ”‚ โ”‚
โ”‚  โ”‚ โšช    โ”‚ Tutorial          โ”‚ Demo     โ”‚ 0:12:45  โ”‚ [View] [โ‹ฏ]โ”‚ โ”‚
โ”‚  โ”‚ ๐Ÿ”ด    โ”‚ Rabenheim         โ”‚ PP DE    โ”‚ 0:03:12  โ”‚ [View] [โ‹ฏ]โ”‚ โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚
โ”‚                                                                   โ”‚
โ”‚  Showing 5 of 47 sessions           [โ—„ 1  2  3  4  5 โ–บ]         โ”‚
โ”‚                                                                   โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Status indicators: ๐ŸŸข active, โšช ended, ๐Ÿ”ด ended with error. Filter bar: status dropdown, campaign filter, date range picker. Search: full-text across campaign name, guild name, error messages.

Live Session Monitor (/sessions/[id]/live):

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  โ† Sessions  /  Live: Rabenheim                      ๐ŸŸข Connected   โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                         โ”‚                            โ”‚
โ”‚  Live Transcript                        โ”‚  Session Info              โ”‚
โ”‚                                         โ”‚                            โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”‚  Campaign: Rabenheim       โ”‚
โ”‚  โ”‚                                 โ”‚    โ”‚  Guild: Pen & Paper DE     โ”‚
โ”‚  โ”‚  [19:42:15] Player (Luk):      โ”‚    โ”‚  Channel: #taverne         โ”‚
โ”‚  โ”‚  "Guten Abend, Wรคchter.        โ”‚    โ”‚  Duration: 1:42:15         โ”‚
โ”‚  โ”‚   Wir suchen den Bรผrger-       โ”‚    โ”‚  Worker: worker-2          โ”‚
โ”‚  โ”‚   meister."                     โ”‚    โ”‚                            โ”‚
โ”‚  โ”‚                                 โ”‚    โ”‚  Active NPCs               โ”‚
โ”‚  โ”‚  [19:42:18] Heinrich:          โ”‚    โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”‚
โ”‚  โ”‚  "Halt! Der Bรผrgermeister      โ”‚    โ”‚  โ”‚ Heinrich  โ”‚ 3 resp โ”‚    โ”‚
โ”‚  โ”‚   empfรคngt keine Besucher      โ”‚    โ”‚  โ”‚ Elara     โ”‚ 1 resp โ”‚    โ”‚
โ”‚  โ”‚   nach Sonnenuntergang."       โ”‚    โ”‚  โ”‚ Erzรคhler  โ”‚ 5 resp โ”‚    โ”‚
โ”‚  โ”‚                                 โ”‚    โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ”‚
โ”‚  โ”‚  [19:42:22] Player (Sara):     โ”‚    โ”‚                            โ”‚
โ”‚  โ”‚  "Aber es ist dringend!"       โ”‚    โ”‚  Audio Stats               โ”‚
โ”‚  โ”‚                                 โ”‚    โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”‚
โ”‚  โ”‚  โ–ผ auto-scroll                  โ”‚    โ”‚  โ”‚ VAD:  โ–โ–‚โ–ƒโ–…โ–‡โ–…โ–ƒโ–‚โ–   โ”‚    โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ”‚  โ”‚ STT:  142ms avg    โ”‚    โ”‚
โ”‚                                         โ”‚  โ”‚ LLM:  380ms avg    โ”‚    โ”‚
โ”‚                                         โ”‚  โ”‚ TTS:  210ms avg    โ”‚    โ”‚
โ”‚                                         โ”‚  โ”‚ E2E:  890ms avg    โ”‚    โ”‚
โ”‚                                         โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ”‚
โ”‚                                         โ”‚                            โ”‚
โ”‚                                         โ”‚  [Force Stop Session]      โ”‚
โ”‚                                         โ”‚                            โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚  Connection: WebSocket  โ€ข  Latency: 23ms  โ€ข  Messages: 47           โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

WebSocket connection:

  • Connect to wss://api.glyphoxa.com/api/v1/sessions/{id}/live
  • JWT sent as query param or first message (WebSocket doesnโ€™t support cookies)
  • Server pushes: transcript_entry, audio_stats, session_state_change
  • Client auto-reconnects with exponential backoff (1s, 2s, 4s, max 30s)
  • Connection status indicator in header (๐ŸŸข Connected / ๐ŸŸก Reconnecting / ๐Ÿ”ด Disconnected)
  • Auto-scroll toggle: locks to bottom by default, unlocks if user scrolls up

Mobile: Transcript full-width, info panel in a collapsible bottom sheet.

5.7 Transcript Viewer (/sessions/[id])

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  โ† Sessions  /  Session 2026-03-24 19:00                             โ”‚
โ”‚                                                                       โ”‚
โ”‚  โ”Œโ”€ Transcript โ”€โ”ฌโ”€ Details โ”€โ”ฌโ”€ Stats โ”€โ”                              โ”‚
โ”‚  โ”‚              โ”‚           โ”‚         โ”‚                              โ”‚
โ”‚  โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                              โ”‚
โ”‚                                                                       โ”‚
โ”‚  Search: [ Search transcript...        ๐Ÿ” ]     [Raw โ—‹ โ— Corrected]  โ”‚
โ”‚                                                                       โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”‚
โ”‚  โ”‚                                                               โ”‚    โ”‚
โ”‚  โ”‚  19:00:12                                                     โ”‚    โ”‚
โ”‚  โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”     โ”‚    โ”‚
โ”‚  โ”‚  โ”‚  ๐Ÿ‘ค Luk (Player)                                     โ”‚     โ”‚    โ”‚
โ”‚  โ”‚  โ”‚  "Wir betreten die Taverne und schauen uns um."      โ”‚     โ”‚    โ”‚
โ”‚  โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜     โ”‚    โ”‚
โ”‚  โ”‚                                                               โ”‚    โ”‚
โ”‚  โ”‚  19:00:15                                                     โ”‚    โ”‚
โ”‚  โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”     โ”‚    โ”‚
โ”‚  โ”‚  โ”‚  ๐ŸŽญ Erzรคhler (NPC)                                   โ”‚     โ”‚    โ”‚
โ”‚  โ”‚  โ”‚  "Die Taverne 'Zum Goldenen Raben' ist an diesem     โ”‚     โ”‚    โ”‚
โ”‚  โ”‚  โ”‚   Abend gut besucht. Am Tresen steht ein breit-      โ”‚     โ”‚    โ”‚
โ”‚  โ”‚  โ”‚   schultriger Mann und poliert Glรคser."              โ”‚     โ”‚    โ”‚
โ”‚  โ”‚  โ”‚                                          1.1s โšก     โ”‚     โ”‚    โ”‚
โ”‚  โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜     โ”‚    โ”‚
โ”‚  โ”‚                                                               โ”‚    โ”‚
โ”‚  โ”‚  19:00:22                                                     โ”‚    โ”‚
โ”‚  โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”     โ”‚    โ”‚
โ”‚  โ”‚  โ”‚  ๐Ÿ‘ค Sara (Player)                                    โ”‚     โ”‚    โ”‚
โ”‚  โ”‚  โ”‚  "Ich gehe zum Wirt und bestelle ein Bier."          โ”‚     โ”‚    โ”‚
โ”‚  โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜     โ”‚    โ”‚
โ”‚  โ”‚                                                               โ”‚    โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ”‚
โ”‚                                                                       โ”‚
โ”‚  Export: [๐Ÿ“„ Text] [๐Ÿ“‹ JSON] [๐Ÿ“Š CSV]                                โ”‚
โ”‚                                                                       โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Key features:

  • Raw vs Corrected toggle: Shows original STT output vs LLM-corrected text
  • Search: Client-side filter with highlighted matches (for loaded entries). Server-side full-text search via GET /api/v1/sessions/{id}/transcript?q=...
  • Infinite scroll: Load 50 entries at a time, scroll to load more (TanStack Query useInfiniteQuery)
  • Response time badges: NPC responses show end-to-end latency (โšก < 1.2s, โš ๏ธ > 2s)
  • Export: Client-side generation for text/JSON, server-side for CSV (large transcripts)
  • Color coding: Player messages left-aligned (neutral), NPC messages right-aligned (tinted by NPC color)

5.8 Session Replay (Future โ€” /sessions/[id]/replay)

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  โ† Session  /  Replay: Rabenheim 2026-03-24                         โ”‚
โ”‚                                                                       โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”‚
โ”‚  โ”‚                                                               โ”‚    โ”‚
โ”‚  โ”‚   (Transcript entries appear synchronized with audio)         โ”‚    โ”‚
โ”‚  โ”‚                                                               โ”‚    โ”‚
โ”‚  โ”‚   [Erzรคhler's message highlighted as audio plays]             โ”‚    โ”‚
โ”‚  โ”‚                                                               โ”‚    โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ”‚
โ”‚                                                                       โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”‚
โ”‚  โ”‚  โ–โ–‚โ–ƒโ–…โ–‡โ–…โ–ƒโ–‚โ–โ–‚โ–ƒโ–…โ–‡โ–ˆโ–ˆโ–ˆโ–ˆโ–…โ–ƒโ–‚โ–โ–โ–‚โ–ƒโ–…โ–‡โ–…โ–ƒโ–‚โ–โ–‚โ–ƒโ–…โ–‡โ–ˆโ–ˆโ–ˆโ–ˆโ–…โ–ƒโ–‚โ–โ–‚โ–ƒโ–…โ–‡โ–ˆโ–ˆโ–ˆโ–ˆโ–…โ–ƒโ–‚โ–  โ”‚    โ”‚
โ”‚  โ”‚  0:00 โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ—โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ 1:42:15 โ”‚    โ”‚
โ”‚  โ”‚                                                               โ”‚    โ”‚
โ”‚  โ”‚  [โฎ]  [โ—€โ—€]  [ โ–ถ Play ]  [โ–ถโ–ถ]  [โญ]    1x โ–ผ    ๐Ÿ”Š โ”€โ”€โ”€โ”€โ—โ”€โ”€    โ”‚    โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ”‚
โ”‚                                                                       โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Design notes (future implementation):

  • Audio stored as opus/webm per-session (requires backend audio recording feature)
  • Transcript entries have timestamps that sync with audio playback position
  • Currently-playing entry highlighted and auto-scrolled
  • Playback speed: 0.5x, 1x, 1.5x, 2x
  • Skip between entries with forward/back buttons
  • This page is a placeholder until audio recording is implemented

5.9 Billing & Usage (/billing)

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  Billing & Usage                                                      โ”‚
โ”‚                                                                       โ”‚
โ”‚  Current Plan                                                         โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”‚
โ”‚  โ”‚  ๐Ÿ†  Dungeon Master Plan โ€” $19/month                          โ”‚    โ”‚
โ”‚  โ”‚                                                               โ”‚    โ”‚
โ”‚  โ”‚  Unlimited sessions  โ€ข  Premium voices  โ€ข  Unlimited NPCs     โ”‚    โ”‚
โ”‚  โ”‚  Next billing date: April 1, 2026                             โ”‚    โ”‚
โ”‚  โ”‚                                                               โ”‚    โ”‚
โ”‚  โ”‚  [Change Plan]   [Manage Payment Method]                      โ”‚    โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ”‚
โ”‚                                                                       โ”‚
โ”‚  This Month's Usage  (March 2026)                                     โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”‚
โ”‚  โ”‚ Session hrs  โ”‚ โ”‚ LLM Tokens  โ”‚ โ”‚ STT Seconds โ”‚ โ”‚ TTS Chars   โ”‚    โ”‚
โ”‚  โ”‚   47.2h     โ”‚ โ”‚  1.2M       โ”‚ โ”‚  4,230s     โ”‚ โ”‚  892K       โ”‚    โ”‚
โ”‚  โ”‚   โ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘  โ”‚ โ”‚             โ”‚ โ”‚             โ”‚ โ”‚             โ”‚    โ”‚
โ”‚  โ”‚   47/100h   โ”‚ โ”‚ (no limit)  โ”‚ โ”‚ (no limit)  โ”‚ โ”‚ (no limit)  โ”‚    โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ”‚
โ”‚                                                                       โ”‚
โ”‚  Usage Over Time                                                      โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”‚
โ”‚  โ”‚  Session Hours by Day                        [This month โ–ผ]   โ”‚    โ”‚
โ”‚  โ”‚                                                               โ”‚    โ”‚
โ”‚  โ”‚  5h โ”ค                                                         โ”‚    โ”‚
โ”‚  โ”‚  4h โ”ค          โ–ˆโ–ˆ                                             โ”‚    โ”‚
โ”‚  โ”‚  3h โ”ค    โ–ˆโ–ˆ    โ–ˆโ–ˆ         โ–ˆโ–ˆ                                  โ”‚    โ”‚
โ”‚  โ”‚  2h โ”ค    โ–ˆโ–ˆ    โ–ˆโ–ˆ    โ–ˆโ–ˆ   โ–ˆโ–ˆ    โ–ˆโ–ˆ                             โ”‚    โ”‚
โ”‚  โ”‚  1h โ”ค โ–ˆโ–ˆ โ–ˆโ–ˆ โ–ˆโ–ˆ โ–ˆโ–ˆ โ–ˆโ–ˆ โ–ˆโ–ˆ  โ–ˆโ–ˆ โ–ˆโ–ˆ โ–ˆโ–ˆ                             โ”‚    โ”‚
โ”‚  โ”‚  0h โ”ผโ”€โ”€โ”ฌโ”€โ”€โ”ฌโ”€โ”€โ”ฌโ”€โ”€โ”ฌโ”€โ”€โ”ฌโ”€โ”€โ”ฌโ”€โ”€โ”ฌโ”€โ”€โ”ฌโ”€โ”€โ”ฌโ”€โ”€                           โ”‚    โ”‚
โ”‚  โ”‚       1  3  5  7  9 11 13 15 17 19 ...                        โ”‚    โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ”‚
โ”‚                                                                       โ”‚
โ”‚  Session Breakdown                                                    โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”‚
โ”‚  โ”‚ Date             โ”‚ Duration โ”‚ Tokens   โ”‚ STT      โ”‚ TTS       โ”‚   โ”‚
โ”‚  โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค   โ”‚
โ”‚  โ”‚ Mar 24, 19:00    โ”‚ 1h 42m   โ”‚ 45,230   โ”‚ 156s     โ”‚ 23,400    โ”‚   โ”‚
โ”‚  โ”‚ Mar 23, 20:15    โ”‚ 2h 10m   โ”‚ 62,100   โ”‚ 210s     โ”‚ 31,200    โ”‚   โ”‚
โ”‚  โ”‚ Mar 22, 19:30    โ”‚ 1h 23m   โ”‚ 38,900   โ”‚ 134s     โ”‚ 19,800    โ”‚   โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚
โ”‚                                                                       โ”‚
โ”‚  [Download CSV]                                                       โ”‚
โ”‚                                                                       โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

โ€œChange Planโ€ flow: Navigates to /billing/plans which shows the pricing comparison (same tiers as landing page) with the current plan highlighted. Upgrade is immediate; downgrade takes effect at end of billing period. Payment via Stripe Checkout or Stripe Elements embedded form.

5.10 Settings (/settings)

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  Settings                                                         โ”‚
โ”‚                                                                   โ”‚
โ”‚  โ”Œโ”€ Account โ”€โ”ฌโ”€ API Keys โ”€โ”ฌโ”€ Notifications โ”€โ”                    โ”‚
โ”‚  โ”‚           โ”‚            โ”‚                  โ”‚                    โ”‚
โ”‚  โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                    โ”‚
โ”‚                                                                   โ”‚
โ”‚  Profile                                                          โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”       โ”‚
โ”‚  โ”‚  Name:     [ Luk                          ]            โ”‚       โ”‚
โ”‚  โ”‚  Email:    [ luk@example.com              ]  (verified)โ”‚       โ”‚
โ”‚  โ”‚  Discord:  Connected as LukTTRPG#1234      [Unlink]    โ”‚       โ”‚
โ”‚  โ”‚  Google:   Not connected                   [Link]      โ”‚       โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜       โ”‚
โ”‚                                                                   โ”‚
โ”‚  Appearance                                                       โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”       โ”‚
โ”‚  โ”‚  Theme:   ( ) Light  (โ—) Dark  ( ) System              โ”‚       โ”‚
โ”‚  โ”‚  Language: [ English โ–ผ ]                                โ”‚       โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜       โ”‚
โ”‚                                                                   โ”‚
โ”‚  โ”€โ”€ Danger Zone โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€        โ”‚
โ”‚  [ Delete Account ]  (requires confirmation)                      โ”‚
โ”‚                                                                   โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

API Keys tab (/settings/api-keys):

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  API Keys โ€” Bring Your Own                                        โ”‚
โ”‚                                                                   โ”‚
โ”‚  Use your own API keys to avoid usage limits on your plan.        โ”‚
โ”‚  Keys are encrypted and stored securely via Vault Transit.        โ”‚
โ”‚                                                                   โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”       โ”‚
โ”‚  โ”‚  OpenAI (LLM)                                          โ”‚       โ”‚
โ”‚  โ”‚  Key: sk-โ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ข3kF7    [Change] [Remove]     โ”‚       โ”‚
โ”‚  โ”‚  Status: โœ“ Valid (tested 2h ago)                       โ”‚       โ”‚
โ”‚  โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค       โ”‚
โ”‚  โ”‚  ElevenLabs (TTS)                                      โ”‚       โ”‚
โ”‚  โ”‚  Key: Not configured              [Add Key]            โ”‚       โ”‚
โ”‚  โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค       โ”‚
โ”‚  โ”‚  Deepgram (STT)                                        โ”‚       โ”‚
โ”‚  โ”‚  Key: Not configured              [Add Key]            โ”‚       โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜       โ”‚
โ”‚                                                                   โ”‚
โ”‚  [Test All Keys]                                                  โ”‚
โ”‚                                                                   โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

5.11 Admin Dashboard (/admin โ€” super_admin only)

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  Admin Dashboard                                                      โ”‚
โ”‚                                                                       โ”‚
โ”‚  System Overview                                                      โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”        โ”‚
โ”‚  โ”‚ Tenants    โ”‚ โ”‚ Active     โ”‚ โ”‚ Total      โ”‚ โ”‚ System     โ”‚        โ”‚
โ”‚  โ”‚    12      โ”‚ โ”‚ Sessions   โ”‚ โ”‚ Session hrsโ”‚ โ”‚ Health     โ”‚        โ”‚
โ”‚  โ”‚            โ”‚ โ”‚    5       โ”‚ โ”‚  342h/mo   โ”‚ โ”‚  โœ“ All OK  โ”‚        โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜        โ”‚
โ”‚                                                                       โ”‚
โ”‚  Provider Health                                                      โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚
โ”‚  โ”‚ LLM  โœ“  โ”‚ โ”‚ STT  โœ“  โ”‚ โ”‚ TTS  โœ“  โ”‚ โ”‚ VAD  โœ“  โ”‚ โ”‚ EMB  โœ“  โ”‚  โ”‚
โ”‚  โ”‚ OpenAI   โ”‚ โ”‚ Deepgram โ”‚ โ”‚ Eleven   โ”‚ โ”‚ Silero   โ”‚ โ”‚ Gemini   โ”‚  โ”‚
โ”‚  โ”‚ P50: 380 โ”‚ โ”‚ P50: 142 โ”‚ โ”‚ P50: 210 โ”‚ โ”‚ P50: 12  โ”‚ โ”‚ P50: 95  โ”‚  โ”‚
โ”‚  โ”‚ P99: 920 โ”‚ โ”‚ P99: 310 โ”‚ โ”‚ P99: 540 โ”‚ โ”‚ P99: 28  โ”‚ โ”‚ P99: 220 โ”‚  โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚
โ”‚                                                                       โ”‚
โ”‚  Active Sessions Across Tenants                                       โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”‚
โ”‚  โ”‚ Tenant   โ”‚ Campaign       โ”‚ Guild    โ”‚ Duration โ”‚ Worker     โ”‚    โ”‚
โ”‚  โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค    โ”‚
โ”‚  โ”‚ luk      โ”‚ Rabenheim      โ”‚ PP DE    โ”‚ 1:42:15  โ”‚ worker-2   โ”‚    โ”‚
โ”‚  โ”‚ demo     โ”‚ Tutorial       โ”‚ Demo     โ”‚ 0:05:30  โ”‚ worker-1   โ”‚    โ”‚
โ”‚  โ”‚ team_a   โ”‚ Ravenloft      โ”‚ D&D Club โ”‚ 3:10:42  โ”‚ worker-3   โ”‚    โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ”‚
โ”‚                                                                       โ”‚
โ”‚  Revenue This Month                                                   โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”‚
โ”‚  โ”‚  MRR: $156  โ€ข  New: 3  โ€ข  Churned: 0  โ€ข  Upgraded: 1         โ”‚    โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ”‚
โ”‚                                                                       โ”‚
โ”‚  Quick Links                                                          โ”‚
โ”‚  [Grafana Dashboard]  [OTel Traces]  [Prometheus Metrics]             โ”‚
โ”‚                                                                       โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

5.12 Support (/support)

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  Help & Support                                                   โ”‚
โ”‚                                                                   โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”              โ”‚
โ”‚  โ”‚ ๐Ÿ“š Docs     โ”‚  โ”‚ ๐Ÿ’ฌ Discord  โ”‚  โ”‚ ๐ŸŽซ Tickets  โ”‚              โ”‚
โ”‚  โ”‚ Browse the  โ”‚  โ”‚ Join our    โ”‚  โ”‚ Submit a    โ”‚              โ”‚
โ”‚  โ”‚ documenta-  โ”‚  โ”‚ community   โ”‚  โ”‚ support     โ”‚              โ”‚
โ”‚  โ”‚ tion        โ”‚  โ”‚ server      โ”‚  โ”‚ ticket      โ”‚              โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜              โ”‚
โ”‚                                                                   โ”‚
โ”‚  Frequently Asked Questions                                       โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”       โ”‚
โ”‚  โ”‚ โ–ถ How do I add my Discord bot to a server?             โ”‚       โ”‚
โ”‚  โ”‚ โ–ถ Why is my NPC's voice sounding different?            โ”‚       โ”‚
โ”‚  โ”‚ โ–ถ How do I use my own API keys?                        โ”‚       โ”‚
โ”‚  โ”‚ โ–ถ What game systems are supported?                     โ”‚       โ”‚
โ”‚  โ”‚ โ–ถ How does the knowledge graph work?                   โ”‚       โ”‚
โ”‚  โ”‚ โ–ถ Can players interact with NPCs in text chat?         โ”‚       โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜       โ”‚
โ”‚                                                                   โ”‚
โ”‚  Your Tickets                                                     โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚
โ”‚  โ”‚ #        โ”‚ Subject                       โ”‚ Status   โ”‚ Date   โ”‚ โ”‚
โ”‚  โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚
โ”‚  โ”‚ 42       โ”‚ NPC not responding in voice   โ”‚ ๐ŸŸก Open  โ”‚ Mar 22 โ”‚ โ”‚
โ”‚  โ”‚ 38       โ”‚ Billing question              โ”‚ โœ“ Closed โ”‚ Mar 15 โ”‚ โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚
โ”‚                                                                   โ”‚
โ”‚  [+ New Ticket]                                                   โ”‚
โ”‚                                                                   โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

6. Component Hierarchy

Shared Layout Components

<RootLayout>                              # HTML shell, font loading, providers
โ”œโ”€โ”€ <ThemeProvider>                        # Dark/light/system theme
โ”œโ”€โ”€ <QueryClientProvider>                  # TanStack Query
โ”œโ”€โ”€ <IntlProvider>                         # next-intl translations
โ”‚
โ”œโ”€โ”€ (public) <MarketingLayout>            # Landing, pricing, login
โ”‚   โ”œโ”€โ”€ <MarketingHeader />               # Logo, nav links, sign-in button
โ”‚   โ”œโ”€โ”€ {children}
โ”‚   โ””โ”€โ”€ <MarketingFooter />               # Links, legal, social
โ”‚
โ”œโ”€โ”€ (app) <AppLayout>                     # Authenticated pages
โ”‚   โ”œโ”€โ”€ <AuthGuard />                     # Redirect to /login if unauthenticated
โ”‚   โ”œโ”€โ”€ <Sidebar>                         # Collapsible navigation
โ”‚   โ”‚   โ”œโ”€โ”€ <SidebarLogo />
โ”‚   โ”‚   โ”œโ”€โ”€ <SidebarNav>                  # Role-filtered nav items
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ <NavItem />              # Dashboard, Campaigns, Sessions, etc.
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ <NavGroup />             # Collapsible groups (Settings, Admin)
โ”‚   โ”‚   โ””โ”€โ”€ <SidebarFooter />            # User avatar, logout
โ”‚   โ”œโ”€โ”€ <Topbar>
โ”‚   โ”‚   โ”œโ”€โ”€ <Breadcrumb />               # Auto-generated from route segments
โ”‚   โ”‚   โ”œโ”€โ”€ <SearchCommand />            # Cmd+K global search (cmdk)
โ”‚   โ”‚   โ”œโ”€โ”€ <NotificationBell />         # Unread count badge
โ”‚   โ”‚   โ””โ”€โ”€ <UserMenu />                 # Avatar dropdown (profile, settings, logout)
โ”‚   โ””โ”€โ”€ <MainContent>
โ”‚       โ”œโ”€โ”€ <Suspense fallback={<PageSkeleton />}>
โ”‚       โ””โ”€โ”€ {children}
โ”‚
โ””โ”€โ”€ (admin) <AdminLayout>                 # Super admin pages
    โ”œโ”€โ”€ <AuthGuard requiredRole="super_admin" />
    โ”œโ”€โ”€ <AdminSidebar />                  # Admin-specific navigation
    โ””โ”€โ”€ {children}

Page-Level Components

<DashboardPage>
โ”œโ”€โ”€ <MetricCardGrid>
โ”‚   โ”œโ”€โ”€ <MetricCard />                    # Campaigns count
โ”‚   โ”œโ”€โ”€ <MetricCard />                    # Active sessions
โ”‚   โ””โ”€โ”€ <MetricCard variant="progress" /> # Usage quota bar
โ”œโ”€โ”€ <ActiveSessionsList>
โ”‚   โ””โ”€โ”€ <ActiveSessionCard />             # Per-session row with actions
โ”œโ”€โ”€ <RecentActivityFeed>
โ”‚   โ””โ”€โ”€ <ActivityItem />                  # Timestamped event entry
โ””โ”€โ”€ <QuickActions />                      # CTA buttons

<CampaignListPage>
โ”œโ”€โ”€ <PageHeader title actions={<CreateButton />} />
โ”œโ”€โ”€ <CampaignCardGrid>
โ”‚   โ””โ”€โ”€ <CampaignCard />                  # Name, system, NPC count, status
โ””โ”€โ”€ <EmptyState />                        # Shown when no campaigns exist

<CampaignDetailPage>
โ”œโ”€โ”€ <PageHeader title breadcrumb actions={<SaveButton />} />
โ”œโ”€โ”€ <Tabs>
โ”‚   โ”œโ”€โ”€ <Tab label="Details">
โ”‚   โ”‚   โ””โ”€โ”€ <CampaignForm />             # React Hook Form
โ”‚   โ”œโ”€โ”€ <Tab label="NPCs">
โ”‚   โ”‚   โ””โ”€โ”€ <NPCListForCampaign />
โ”‚   โ”œโ”€โ”€ <Tab label="Lore">
โ”‚   โ”‚   โ””โ”€โ”€ <MarkdownEditor />           # Split-pane edit/preview
โ”‚   โ””โ”€โ”€ <Tab label="Sessions">
โ”‚       โ””โ”€โ”€ <SessionHistoryTable />
โ””โ”€โ”€ <DangerZone />

<NPCEditorPage>                           # Most complex page
โ”œโ”€โ”€ <StickyHeader>
โ”‚   โ”œโ”€โ”€ <Breadcrumb />
โ”‚   โ”œโ”€โ”€ <UnsavedIndicator />
โ”‚   โ”œโ”€โ”€ <DiscardButton />
โ”‚   โ””โ”€โ”€ <SaveButton />
โ”œโ”€โ”€ <TwoColumnLayout>
โ”‚   โ”œโ”€โ”€ <LeftColumn>                      # Form (scrollable)
โ”‚   โ”‚   โ”œโ”€โ”€ <IdentitySection>
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ <TextInput name="name" />
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ <AvatarUpload />
โ”‚   โ”‚   โ”œโ”€โ”€ <CollapsibleSection title="Personality">
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ <Textarea />
โ”‚   โ”‚   โ”œโ”€โ”€ <CollapsibleSection title="Voice">
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ <VoiceProviderSelect />
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ <VoiceIdSelect />
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ <VoicePreviewButton />
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ <AudioWaveform />
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ <PitchSlider />
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ <SpeedSlider />
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ <VoiceSampleUpload />
โ”‚   โ”‚   โ”œโ”€โ”€ <CollapsibleSection title="Engine & Tier">
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ <EngineRadioCards />
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ <BudgetTierRadioCards />
โ”‚   โ”‚   โ”œโ”€โ”€ <CollapsibleSection title="Knowledge">
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ <TagInput name="knowledgeScope" />
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ <TagInput name="secretKnowledge" />
โ”‚   โ”‚   โ”œโ”€โ”€ <CollapsibleSection title="Behavior Rules">
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ <SortableRulesList />    # @dnd-kit sortable
โ”‚   โ”‚   โ””โ”€โ”€ <CollapsibleSection title="Advanced">
โ”‚   โ”‚       โ”œโ”€โ”€ <TagInput name="tools" />
โ”‚   โ”‚       โ”œโ”€โ”€ <Checkbox name="addressOnly" />
โ”‚   โ”‚       โ”œโ”€โ”€ <Checkbox name="gmHelper" />
โ”‚   โ”‚       โ””โ”€โ”€ <JsonEditor name="attributes" />
โ”‚   โ””โ”€โ”€ <RightColumn>                     # Preview (sticky on desktop)
โ”‚       โ””โ”€โ”€ <NPCPreviewCard />
โ””โ”€โ”€ <UnsavedChangesDialog />

<LiveSessionPage>
โ”œโ”€โ”€ <ConnectionStatusBar />               # WebSocket status
โ”œโ”€โ”€ <TwoColumnLayout>
โ”‚   โ”œโ”€โ”€ <TranscriptStream>               # Virtualized scrolling list
โ”‚   โ”‚   โ”œโ”€โ”€ <TranscriptEntry variant="player" />
โ”‚   โ”‚   โ””โ”€โ”€ <TranscriptEntry variant="npc" />
โ”‚   โ””โ”€โ”€ <SessionInfoPanel>
โ”‚       โ”œโ”€โ”€ <SessionMetadata />
โ”‚       โ”œโ”€โ”€ <ActiveNPCsList />
โ”‚       โ”œโ”€โ”€ <AudioStatsWidget />          # Real-time latency metrics
โ”‚       โ””โ”€โ”€ <ForceStopButton />
โ””โ”€โ”€ <StatusFooter />                      # Connection, latency, message count

<BillingPage>
โ”œโ”€โ”€ <CurrentPlanCard />
โ”œโ”€โ”€ <UsageMetricGrid>
โ”‚   โ””โ”€โ”€ <UsageMetricCard />               # Session hours, tokens, etc.
โ”œโ”€โ”€ <UsageChart />                        # Recharts line/bar chart
โ”œโ”€โ”€ <SessionBreakdownTable />             # Paginated table
โ””โ”€โ”€ <ExportButton />

Reusable UI Components (shadcn/ui + custom)

Primitives (shadcn/ui):                   Custom composites:
โ”œโ”€โ”€ Button                                โ”œโ”€โ”€ MetricCard
โ”œโ”€โ”€ Input                                 โ”œโ”€โ”€ TagInput (combobox + chips)
โ”œโ”€โ”€ Textarea                              โ”œโ”€โ”€ CollapsibleSection
โ”œโ”€โ”€ Select                                โ”œโ”€โ”€ SortableList (@dnd-kit)
โ”œโ”€โ”€ Checkbox                              โ”œโ”€โ”€ AudioWaveform (Web Audio)
โ”œโ”€โ”€ RadioGroup                            โ”œโ”€โ”€ VoicePreviewButton
โ”œโ”€โ”€ Slider                                โ”œโ”€โ”€ MarkdownEditor
โ”œโ”€โ”€ Dialog                                โ”œโ”€โ”€ JsonEditor (Monaco or simple)
โ”œโ”€โ”€ DropdownMenu                          โ”œโ”€โ”€ DataTable (TanStack Table)
โ”œโ”€โ”€ Tabs                                  โ”œโ”€โ”€ EmptyState
โ”œโ”€โ”€ Badge                                 โ”œโ”€โ”€ StatusBadge (๐ŸŸข๐ŸŸก๐Ÿ”ด)
โ”œโ”€โ”€ Tooltip                               โ”œโ”€โ”€ PageHeader (title + breadcrumb + actions)
โ”œโ”€โ”€ Skeleton                              โ”œโ”€โ”€ ConfirmDialog
โ”œโ”€โ”€ Toast (sonner)                        โ”œโ”€โ”€ FileUpload (drag & drop zone)
โ”œโ”€โ”€ Command (cmdk)                        โ””โ”€โ”€ InfiniteScrollList
โ”œโ”€โ”€ Sheet (mobile sidebar)
โ”œโ”€โ”€ Card
โ”œโ”€โ”€ Avatar
โ””โ”€โ”€ Separator

7. Key Interactions

7.1 Voice Preview

User action                        System behavior
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€                        โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
Select voice provider       โ†’      Load available voices for provider
Select voice ID             โ†’      Enable preview button
Click "Preview"             โ†’      1. Disable button, show spinner
                                   2. POST /api/v1/npcs/{id}/voice-preview
                                      body: { text, voice: { provider, voice_id, pitch, speed } }
                                   3. Receive audio blob (opus/mp3)
                                   4. Decode with Web Audio API (AudioContext)
                                   5. Display waveform (AnalyserNode โ†’ canvas)
                                   6. Auto-play audio
                                   7. Re-enable button
Adjust pitch/speed slider   โ†’      Debounce 300ms, then auto-preview
Click waveform              โ†’      Seek to position in audio
Rate limit exceeded         โ†’      Toast: "Too many previews. Wait 30s."
                                   Button disabled with countdown

Web Audio API setup:

// Simplified โ€” actual implementation in src/hooks/useVoicePreview.ts
const audioContext = new AudioContext();
const analyser = audioContext.createAnalyser();

async function playPreview(audioBlob: Blob) {
  const buffer = await audioContext.decodeAudioData(await audioBlob.arrayBuffer());
  const source = audioContext.createBufferSource();
  source.buffer = buffer;
  source.connect(analyser);
  analyser.connect(audioContext.destination);
  source.start();
  drawWaveform(analyser); // requestAnimationFrame loop
}

7.2 Drag-and-Drop NPC Ordering

Within a campaignโ€™s NPC list, NPCs can be reordered via drag-and-drop to control their priority (first NPC is the default responder).

Drag start (mouse/touch)    โ†’      1. Show drag overlay with NPC card
                                   2. Other items animate to make space
                                   3. Keyboard: Enter to pick up, arrows to move
Drop                        โ†’      1. Animate into new position
                                   2. Optimistic update (TanStack Query)
                                   3. PATCH /api/v1/campaigns/{id}/npcs/order
                                      body: { npc_ids: ["id1", "id3", "id2"] }
                                   4. On error: revert optimistic update + toast

7.3 Live Transcript WebSocket

Component mount             โ†’      1. Connect to wss://api/v1/sessions/{id}/live
                                   2. Send auth: { type: "auth", token: jwt }
                                   3. Server sends session snapshot (last 50 entries)
                                   4. Render initial transcript

Server push                 โ†’      Message types:
                                   - transcript_entry: { speaker, text, npc_id, ts }
                                   - audio_stats: { vad_active, stt_ms, llm_ms, tts_ms, e2e_ms }
                                   - session_state: { state: "ended", error? }
                                   - heartbeat: { ts } (every 30s)

Client receives entry       โ†’      1. Append to virtualized list
                                   2. Auto-scroll if at bottom
                                   3. If scrolled up: show "N new messages โ†“" pill

Connection lost             โ†’      1. Show ๐ŸŸก Reconnecting
                                   2. Exponential backoff: 1s, 2s, 4s, ..., 30s max
                                   3. On reconnect: request entries since last ts
                                   4. Show ๐ŸŸข Connected

Session ends                โ†’      1. Show "Session ended" banner
                                   2. Close WebSocket
                                   3. Show link to transcript viewer

7.4 Global Search (Cmd+K)

Powered by cmdk (shadcn/ui Command component):

Cmd+K                       โ†’      Open command palette
Type query                  โ†’      Search across:
                                   - Campaigns (by name)
                                   - NPCs (by name, campaign)
                                   - Sessions (by campaign, date)
                                   - Pages (Dashboard, Settings, etc.)
                                   Debounced 200ms, client-side for pages,
                                   server-side for entities
Select result               โ†’      Navigate to entity page
Escape                      โ†’      Close palette

8. Accessibility

WCAG 2.1 AA Compliance

  • Color contrast: All text meets 4.5:1 ratio (Tailwind defaults + verified). Status indicators use shapes + text alongside color (not color alone).
  • Keyboard navigation: All interactive elements focusable with Tab. Custom components (sliders, drag-and-drop, tag inputs) have keyboard handlers.
  • Screen readers: shadcn/ui components use Radix ARIA patterns. Custom components have aria-label, aria-describedby, role attributes.
  • Focus management: Dialog opens โ†’ focus trapped. Dialog closes โ†’ focus returns. Page navigation โ†’ focus moves to main heading (via useEffect + ref.focus()).
  • Motion: prefers-reduced-motion respected. Drag-and-drop falls back to button-based reorder. Waveform animation paused.
  • Forms: Every input has a visible label. Error messages linked via aria-describedby. Required fields marked with aria-required. Form-level error summary at top.
  • Skip links: โ€œSkip to main contentโ€ link visible on focus.
  • Language: <html lang="en"> set, updated for i18n. NPC content thatโ€™s in a different language can be wrapped in <span lang="de">.

Touch Targets

  • Minimum 44x44px for all interactive elements (buttons, links, inputs)
  • Sidebar nav items: 48px height
  • Mobile: 12px minimum gap between adjacent targets

9. Performance Strategy

Code Splitting & Lazy Loading

Route-based splitting (automatic with Next.js App Router):
/                  โ†’ ~50KB (landing, SSR, minimal JS)
/dashboard         โ†’ ~80KB (charts, metric cards)
/campaigns/[id]/npcs/[npcId]  โ†’ ~120KB (heaviest: editor, audio, dnd-kit)
/sessions/[id]/live โ†’ ~60KB (WebSocket, virtualized list)
/billing           โ†’ ~70KB (Recharts)

Component-level lazy loading:
- MarkdownEditor     โ†’ dynamic(() => import('./MarkdownEditor'))
- JsonEditor         โ†’ dynamic(() => import('./JsonEditor'))
- AudioWaveform      โ†’ dynamic(() => import('./AudioWaveform'))
- UsageChart         โ†’ dynamic(() => import('./UsageChart'))
- KnowledgeGraph     โ†’ dynamic(() => import('./KnowledgeGraph'))  // Phase 3

Data Loading

Pattern Used for
Server Components Landing page, initial page shells, SEO content
TanStack Query All API data. staleTime: 30s for lists, 60s for detail.
Optimistic updates NPC save, campaign edit, rule reorder, session stop
Infinite scroll Transcript entries (50/page), session history, activity feed
Polling Active sessions (10s), dashboard metrics (30s)
WebSocket Live transcript only (dedicated connection per active session)
Prefetch Hover on campaign card โ†’ prefetch campaign detail + NPCs

Bundle Optimization

  • Tree shaking: ES modules only. No barrel exports in component library.
  • Font subsetting: Geist loaded via next/font with Latin subset.
  • Image optimization: next/image with WebP/AVIF, lazy loading, blur placeholders.
  • CSS: Tailwind purge removes unused classes. Final CSS ~15-25KB gzipped.
  • Icons: Lucide tree-shakes to only imported icons.

Target Metrics

Metric Target Measurement
FCP (landing) < 1.0s Lighthouse, real user monitoring
LCP (landing) < 2.0s Lighthouse
TTI (dashboard) < 2.5s Lighthouse
CLS < 0.1 Lighthouse
JS bundle (initial) < 100KB gzipped Build analysis
API response (p95) < 200ms Server-side metrics

10. Responsive Design

Breakpoints (Tailwind defaults)

Breakpoint Width Layout
sm 640px Single column, hamburger nav
md 768px Two columns where needed
lg 1024px Full sidebar + content
xl 1280px Wider content area
2xl 1536px Max-width container

Mobile Adaptations

Desktop Mobile
Sidebar always visible Sheet overlay (hamburger toggle)
Two-column NPC editor Single column, preview at top
Data tables Card-based list or horizontal scroll
Inline form sections Collapsed accordions (expand on tap)
Hover tooltips Long-press or info icons
Cmd+K search Search icon in topbar
Side-by-side markdown editor Tabs: Edit / Preview
Live monitor split view Transcript full width, info in bottom sheet

PWA Considerations (Future)

The app is structured to support Progressive Web App features later:

  • manifest.json for home screen installation
  • Service worker for offline NPC editing (drafts saved to IndexedDB)
  • Push notifications for session events

11. Error Handling & Loading States

Loading Patterns

Initial page load     โ†’  Full-page skeleton (shadcn Skeleton components)
Data refetch          โ†’  Subtle shimmer on stale data (no layout shift)
Mutation in progress  โ†’  Button spinner + disabled state
Long operation        โ†’  Progress bar or step indicator
WebSocket reconnect   โ†’  Banner: "Reconnecting..." with spinner

Error Patterns

API 400 (validation)  โ†’  Inline field errors (red border + message below)
API 401 (auth)        โ†’  Silent refresh attempt โ†’ if fails, redirect to /login
API 403 (forbidden)   โ†’  Toast: "You don't have permission for this action"
API 404 (not found)   โ†’  Full-page "Not Found" with link to parent
API 429 (rate limit)  โ†’  Toast: "Too many requests. Please wait."
API 500 (server)      โ†’  Toast: "Something went wrong. Please try again."
                         + Retry button for idempotent operations
Network error         โ†’  Banner: "Connection lost. Retrying..."
                         + TanStack Query auto-retry (3 attempts)
Form submission fail  โ†’  Error summary at top of form + scroll to first error
WebSocket disconnect  โ†’  Connection status indicator + auto-reconnect

Empty States

Every list view has a designed empty state:

  • No campaigns: Illustration + โ€œCreate your first campaignโ€ CTA
  • No NPCs: โ€œAdd an NPC to bring your campaign to lifeโ€ + example templates
  • No sessions: โ€œStart a session in Discord to see it hereโ€
  • No transcripts: โ€œRun a session to generate transcriptsโ€

12. i18n Strategy

Phase 1 (English MVP)

  • All user-facing strings extracted to messages/en.json using next-intl
  • ICU message format for plurals, dates, numbers:
    {
      "dashboard.activeSessions": "{count, plural, =0 {No active sessions} one {# active session} other {# active sessions}}",
      "billing.usage": "Used {used} of {total} hours"
    }
    
  • Date/time formatting via Intl.DateTimeFormat (locale-aware)
  • Number formatting via Intl.NumberFormat (locale-aware)
  • No hardcoded strings in components

Phase 2 (German)

  • Add messages/de.json
  • URL prefix routing: /de/dashboard, /en/dashboard
  • Language picker in settings + browser Accept-Language detection
  • NPC content (personalities, rules) is user-authored and not translated โ€” the UI chrome is translated, content stays in whatever language the DM writes

RTL / Other Languages (Future)

  • Tailwindโ€™s logical properties (ms-4 instead of ml-4) from day one
  • dir attribute responsive to locale

13. Directory Structure

web/
โ”œโ”€โ”€ public/
โ”‚   โ”œโ”€โ”€ favicon.ico
โ”‚   โ”œโ”€โ”€ og-image.png                      # Social sharing image
โ”‚   โ””โ”€โ”€ fonts/                            # Geist font files
โ”‚
โ”œโ”€โ”€ src/
โ”‚   โ”œโ”€โ”€ app/                              # Next.js App Router (see Section 3)
โ”‚   โ”‚   โ”œโ”€โ”€ layout.tsx                    # Root layout
โ”‚   โ”‚   โ”œโ”€โ”€ (public)/
โ”‚   โ”‚   โ”œโ”€โ”€ (app)/
โ”‚   โ”‚   โ””โ”€โ”€ (admin)/
โ”‚   โ”‚
โ”‚   โ”œโ”€โ”€ components/
โ”‚   โ”‚   โ”œโ”€โ”€ ui/                           # shadcn/ui primitives (generated)
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ button.tsx
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ input.tsx
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ ...
โ”‚   โ”‚   โ”œโ”€โ”€ layout/                       # Layout shells
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ sidebar.tsx
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ topbar.tsx
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ breadcrumb.tsx
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ page-header.tsx
โ”‚   โ”‚   โ”œโ”€โ”€ campaign/                     # Campaign-specific composites
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ campaign-card.tsx
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ campaign-form.tsx
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ campaign-settings.tsx
โ”‚   โ”‚   โ”œโ”€โ”€ npc/                          # NPC-specific composites
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ npc-card.tsx
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ npc-form.tsx
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ voice-preview.tsx
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ voice-sample-upload.tsx
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ engine-selector.tsx
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ knowledge-tags.tsx
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ behavior-rules.tsx
โ”‚   โ”‚   โ”œโ”€โ”€ session/                      # Session-specific composites
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ session-table.tsx
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ live-transcript.tsx
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ transcript-entry.tsx
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ audio-stats.tsx
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ connection-status.tsx
โ”‚   โ”‚   โ”œโ”€โ”€ billing/                      # Billing composites
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ plan-card.tsx
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ usage-chart.tsx
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ usage-metric.tsx
โ”‚   โ”‚   โ””โ”€โ”€ shared/                       # Cross-cutting composites
โ”‚   โ”‚       โ”œโ”€โ”€ metric-card.tsx
โ”‚   โ”‚       โ”œโ”€โ”€ tag-input.tsx
โ”‚   โ”‚       โ”œโ”€โ”€ collapsible-section.tsx
โ”‚   โ”‚       โ”œโ”€โ”€ sortable-list.tsx
โ”‚   โ”‚       โ”œโ”€โ”€ audio-waveform.tsx
โ”‚   โ”‚       โ”œโ”€โ”€ markdown-editor.tsx
โ”‚   โ”‚       โ”œโ”€โ”€ json-editor.tsx
โ”‚   โ”‚       โ”œโ”€โ”€ data-table.tsx
โ”‚   โ”‚       โ”œโ”€โ”€ empty-state.tsx
โ”‚   โ”‚       โ”œโ”€โ”€ confirm-dialog.tsx
โ”‚   โ”‚       โ”œโ”€โ”€ file-upload.tsx
โ”‚   โ”‚       โ””โ”€โ”€ infinite-scroll.tsx
โ”‚   โ”‚
โ”‚   โ”œโ”€โ”€ hooks/                            # Custom React hooks
โ”‚   โ”‚   โ”œโ”€โ”€ use-voice-preview.ts          # Web Audio playback + waveform
โ”‚   โ”‚   โ”œโ”€โ”€ use-websocket.ts              # WebSocket with reconnect
โ”‚   โ”‚   โ”œโ”€โ”€ use-auth.ts                   # JWT token management
โ”‚   โ”‚   โ”œโ”€โ”€ use-role.ts                   # Role-based access checks
โ”‚   โ”‚   โ”œโ”€โ”€ use-unsaved-changes.ts        # Form dirty tracking
โ”‚   โ”‚   โ””โ”€โ”€ use-debounce.ts
โ”‚   โ”‚
โ”‚   โ”œโ”€โ”€ api/                              # API client layer
โ”‚   โ”‚   โ”œโ”€โ”€ client.ts                     # openapi-fetch configured instance
โ”‚   โ”‚   โ”œโ”€โ”€ types.ts                      # Generated from OpenAPI spec
โ”‚   โ”‚   โ”œโ”€โ”€ queries/                      # TanStack Query hooks (per domain)
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ campaigns.ts
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ npcs.ts
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ sessions.ts
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ usage.ts
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ users.ts
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ auth.ts
โ”‚   โ”‚   โ””โ”€โ”€ mutations/                    # TanStack mutation hooks
โ”‚   โ”‚       โ”œโ”€โ”€ campaigns.ts
โ”‚   โ”‚       โ”œโ”€โ”€ npcs.ts
โ”‚   โ”‚       โ”œโ”€โ”€ sessions.ts
โ”‚   โ”‚       โ””โ”€โ”€ auth.ts
โ”‚   โ”‚
โ”‚   โ”œโ”€โ”€ lib/                              # Utilities
โ”‚   โ”‚   โ”œโ”€โ”€ auth.ts                       # Token parsing, role checks
โ”‚   โ”‚   โ”œโ”€โ”€ format.ts                     # Date, number, duration formatting
โ”‚   โ”‚   โ”œโ”€โ”€ cn.ts                         # Tailwind class merge utility
โ”‚   โ”‚   โ””โ”€โ”€ constants.ts                  # Route paths, config values
โ”‚   โ”‚
โ”‚   โ”œโ”€โ”€ messages/                         # i18n translation files
โ”‚   โ”‚   โ”œโ”€โ”€ en.json
โ”‚   โ”‚   โ””โ”€โ”€ de.json                       # Phase 2
โ”‚   โ”‚
โ”‚   โ””โ”€โ”€ styles/
โ”‚       โ””โ”€โ”€ globals.css                   # Tailwind base + custom tokens
โ”‚
โ”œโ”€โ”€ next.config.ts
โ”œโ”€โ”€ tailwind.config.ts
โ”œโ”€โ”€ tsconfig.json
โ”œโ”€โ”€ package.json
โ”œโ”€โ”€ Dockerfile
โ””โ”€โ”€ .env.example                          # NEXT_PUBLIC_API_URL, etc.

14. Deployment

Docker Image

FROM node:22-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:22-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/public ./public

EXPOSE 3000
CMD ["node", "server.js"]

Image size: ~50MB (standalone output, Alpine base)

Environment Variables

NEXT_PUBLIC_API_URL=https://api.glyphoxa.com     # Gateway API base URL
NEXT_PUBLIC_WS_URL=wss://api.glyphoxa.com        # WebSocket base URL
NEXT_PUBLIC_DISCORD_CLIENT_ID=...                 # For OAuth redirect
NEXT_PUBLIC_GOOGLE_CLIENT_ID=...                  # For OAuth redirect

K3s Deployment

# Abbreviated โ€” full manifest in infra/
apiVersion: apps/v1
kind: Deployment
metadata:
  name: glyphoxa-web
spec:
  replicas: 2                    # Stateless, horizontally scalable
  template:
    spec:
      containers:
      - name: web
        image: ghcr.io/glyphoxa/web:latest
        ports:
        - containerPort: 3000
        resources:
          requests: { cpu: 50m, memory: 64Mi }
          limits: { cpu: 200m, memory: 256Mi }
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: glyphoxa-web
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
  rules:
  - host: app.glyphoxa.com
    http:
      paths:
      - path: /
        backend:
          service:
            name: glyphoxa-web
            port: { number: 3000 }
  tls:
  - hosts: [app.glyphoxa.com]
    secretName: glyphoxa-web-tls

CORS Configuration (Gateway)

Since the frontend is on a separate domain, the Gateway API needs CORS headers:

Access-Control-Allow-Origin: https://app.glyphoxa.com
Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE, OPTIONS
Access-Control-Allow-Headers: Authorization, Content-Type, X-CSRF-Token
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 86400

15. Phase Breakdown (Frontend)

Phase 1: MVP

Scope: Core CRUD pages, API key auth, basic monitoring.

  • Next.js scaffolding (App Router, Tailwind, shadcn/ui, TanStack Query)
  • API client generation from OpenAPI spec
  • Marketing layout (landing page, pricing)
  • API key login page
  • App layout (sidebar, topbar, breadcrumbs)
  • Dashboard (metrics, active sessions, activity feed)
  • Campaign list + create/edit
  • NPC list + full editor (all sections)
  • Voice preview with Web Audio API
  • Session list + transcript viewer
  • Basic usage display
  • Settings page (profile, appearance)
  • Support page (FAQ, docs links)
  • Responsive design pass (mobile)
  • Docker image + K3s deployment
  • i18n setup (English strings extracted)

Estimated: ~50-60 components, ~30 route pages

Phase 2: Auth + Live + i18n

  • Discord OAuth2 login flow
  • Google OAuth2 login flow
  • JWT-based auth with silent refresh
  • Role-based navigation and access guards
  • User management page (admin)
  • Live session monitoring (WebSocket)
  • Audio stats real-time visualization
  • German locale (messages/de.json)
  • Notification preferences
  • Billing integration (Stripe)
  • Plan upgrade/downgrade flow
  • Voice sample upload for custom voices

Phase 3: Advanced

  • Session replay with synced audio
  • Knowledge graph visualization
  • Admin dashboard (all tenants, system health)
  • Audit log viewer
  • Usage CSV export
  • PWA support (offline NPC editing)
  • A/B testing infrastructure

16. Open Questions

  1. Separate domain vs subdomain: app.glyphoxa.com (subdomain) vs glyphoxa.app (separate domain)? Subdomain is simpler for shared cookies with api.glyphoxa.com if both are under *.glyphoxa.com.

  2. SSR vs static export: Should the dashboard pages be SSR (for server-side auth checks) or static with client-side auth? Recommendation: SSR for auth pages, static for marketing pages.

  3. Stripe integration timeline: Should billing/payments be in Phase 1 (if we want to charge from launch) or Phase 2 (launch free, add payments later)?

  4. NPC templates: Should we ship pre-built NPC templates (tavern keeper, guard, merchant) that DMs can clone and customize? This would significantly improve onboarding for new users.

  5. Collaborative editing: Multiple DMs editing the same campaign simultaneously? This adds significant complexity (CRDTs or OT). Defer to Phase 3+ unless thereโ€™s strong demand.

  6. Analytics: Do we need user analytics (page views, feature usage) for product decisions? If yes, recommend PostHog (self-hosted, privacy-friendly) or Plausible.


This site uses Just the Docs, a documentation theme for Jekyll.