diff --git a/docs/superpowers/plans/2026-04-25-seo-and-readme.md b/docs/superpowers/plans/2026-04-25-seo-and-readme.md
deleted file mode 100644
index c82e449..0000000
--- a/docs/superpowers/plans/2026-04-25-seo-and-readme.md
+++ /dev/null
@@ -1,616 +0,0 @@
-# SEO & README Implementation Plan
-
-> **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking.
-
-**Goal:** Add comprehensive SEO infrastructure (metadata, OG images, sitemap, robots.txt, manifest) and update the README to reflect the current project state.
-
-**Architecture:** All SEO files live in `app/` as Next.js file conventions. The OG image is dynamically generated using `ImageResponse` from `next/og`, leveraging the existing `app/icon.png` logo. Sitemap and manifest use TypeScript file conventions for future dynamic data readiness. The site URL is `https://deckyvault.xyz`.
-
-**Tech Stack:** Next.js 16.2.4 App Router, `next/og` ImageResponse, TypeScript, Tailwind CSS v4
-
----
-
-## File Structure
-
-### Files to Create
-| File | Purpose |
-|------|---------|
-| `app/layout.tsx` | Modify: add full metadata (title template, description, OpenGraph, Twitter, authors, keywords, metadataBase) |
-| `app/opengraph-image.tsx` | Create: dynamic OG banner image using ImageResponse with logo + brand colors |
-| `app/twitter-image.tsx` | Create: Twitter card image (can share OG image logic or be identical) |
-| `app/sitemap.ts` | Create: programmatic sitemap with static routes, ready for dynamic data |
-| `app/robots.ts` | Create: robots.txt allowing all crawlers, pointing to sitemap |
-| `app/manifest.ts` | Create: PWA web manifest with app name, theme colors, icons |
-| `app/icon.png` | Already exists — used as favicon and in OG image generation |
-| `README.md` | Modify: update tech stack, install commands, development status note |
-
-### Files Unchanged
-| File | Reason |
-|------|--------|
-| `app/page.tsx` | Landing page — no SEO changes needed (metadata is in layout) |
-| `app/globals.css` | Styles — no changes needed |
-| `next.config.ts` | No config changes needed for this scope |
-| `public/` | Stays empty — all assets handled via app directory conventions |
-
----
-
-## Chunk 1: Root Layout Metadata & OG Image Generation
-
-### Task 1: Enhance Root Layout Metadata
-
-**Files:**
-- Modify: `app/layout.tsx`
-
-The current layout only sets `title`. We need to add full metadata for SEO and social sharing.
-
-- [ ] **Step 1: Update `app/layout.tsx` with comprehensive metadata**
-
-Replace the existing `metadata` export with a full configuration:
-
-```tsx
-import type { Metadata } from "next"
-import { Lexend } from "next/font/google"
-import "./globals.css"
-
-const font = Lexend({
- variable: "--font-lexend",
- subsets: ["latin"],
-})
-
-export const metadata: Metadata = {
- metadataBase: new URL("https://deckyvault.xyz"),
- title: {
- default: "DeckyVault - Steam Deck Benchmarks & Settings",
- template: "%s | DeckyVault",
- },
- description:
- "A fast, modern browser for finding game benchmarks, settings, and guides for Steam Deck OLED, Steam Deck LCD, and Steam Machine.",
- keywords: [
- "Steam Deck",
- "benchmarks",
- "settings",
- "performance",
- "FPS",
- "gaming",
- "Steam Machine",
- "Proton",
- "FSR",
- "compatibility",
- ],
- authors: [{ name: "Adrian Bonpin", url: "https://github.com/AdrianBonpin" }],
- creator: "@adrianbonpin",
- openGraph: {
- type: "website",
- locale: "en_US",
- url: "https://deckyvault.xyz",
- siteName: "DeckyVault",
- title: "DeckyVault - Steam Deck Benchmarks & Settings",
- description:
- "A fast, modern browser for finding game benchmarks, settings, and guides for Steam Deck OLED, Steam Deck LCD, and Steam Machine.",
- images: [
- {
- url: "/opengraph-image",
- width: 1200,
- height: 630,
- alt: "DeckyVault - Steam Deck Benchmarks & Settings",
- },
- ],
- },
- twitter: {
- card: "summary_large_image",
- title: "DeckyVault - Steam Deck Benchmarks & Settings",
- description:
- "A fast, modern browser for finding game benchmarks, settings, and guides for Steam Deck OLED, Steam Deck LCD, and Steam Machine.",
- creator: "@adrianbonpin",
- images: ["/twitter-image"],
- },
- robots: {
- index: true,
- follow: true,
- googleBot: {
- index: true,
- follow: true,
- "max-video-preview": -1,
- "max-image-preview": "large",
- "max-snippet": -1,
- },
- },
-}
-
-export default function RootLayout({
- children,
-}: Readonly<{
- children: React.ReactNode
-}>) {
- return (
-
-
- {children}
-
-
- )
-}
-```
-
-- [ ] **Step 2: Verify layout compiles**
-
-Run: `bun run build`
-Expected: Build succeeds with no TypeScript errors.
-
----
-
-### Task 2: Create Dynamic OpenGraph Image
-
-**Files:**
-- Create: `app/opengraph-image.tsx`
-
-Uses `ImageResponse` from `next/og` to generate a branded OG banner at build time. Reads the existing `app/icon.png` as a base64 data URI and renders it with the DeckyVault name and tagline on the brand background color.
-
-- [ ] **Step 1: Create `app/opengraph-image.tsx`**
-
-```tsx
-import { ImageResponse } from "next/og"
-import { readFile } from "node:fs/promises"
-import { join } from "node:path"
-
-export const alt = "DeckyVault - Steam Deck Benchmarks & Settings"
-export const size = {
- width: 1200,
- height: 630,
-}
-export const contentType = "image/png"
-
-export default async function Image() {
- const logoData = await readFile(
- join(process.cwd(), "app/icon.png"),
- "base64"
- )
- const logoSrc = `data:image/png;base64,${logoData}`
-
- return new ImageResponse(
- (
-
-

-
- DeckyVault
-
-
- Steam Deck Benchmarks & Settings
-
-
- ),
- {
- ...size,
- }
- )
-}
-```
-
-- [ ] **Step 2: Verify OG image builds**
-
-Run: `bun run build`
-Expected: Build succeeds. `opengraph-image` route appears in build output.
-
----
-
-### Task 3: Create Twitter Card Image
-
-**Files:**
-- Create: `app/twitter-image.tsx`
-
-Shares the same design as the OG image. Next.js supports separate `twitter-image` files. For now, we duplicate the OG image logic to allow independent customization later.
-
-- [ ] **Step 1: Create `app/twitter-image.tsx`**
-
-```tsx
-import { ImageResponse } from "next/og"
-import { readFile } from "node:fs/promises"
-import { join } from "node:path"
-
-export const alt = "DeckyVault - Steam Deck Benchmarks & Settings"
-export const size = {
- width: 1200,
- height: 630,
-}
-export const contentType = "image/png"
-
-export default async function Image() {
- const logoData = await readFile(
- join(process.cwd(), "app/icon.png"),
- "base64"
- )
- const logoSrc = `data:image/png;base64,${logoData}`
-
- return new ImageResponse(
- (
-
-

-
- DeckyVault
-
-
- Steam Deck Benchmarks & Settings
-
-
- ),
- {
- ...size,
- }
- )
-}
-```
-
-- [ ] **Step 2: Verify Twitter image builds**
-
-Run: `bun run build`
-Expected: Build succeeds. Both `opengraph-image` and `twitter-image` routes appear.
-
----
-
-## Chunk 2: Sitemap, Robots.txt, and Manifest
-
-### Task 4: Create Dynamic Sitemap
-
-**Files:**
-- Create: `app/sitemap.ts`
-
-Programmatically generates `sitemap.xml`. Currently static (just the homepage), but structured so that dynamic game pages can be added later by querying the database.
-
-- [ ] **Step 1: Create `app/sitemap.ts`**
-
-```ts
-import type { MetadataRoute } from "next"
-
-const BASE_URL = "https://deckyvault.xyz"
-
-export default function sitemap(): MetadataRoute.Sitemap {
- return [
- {
- url: BASE_URL,
- lastModified: new Date(),
- changeFrequency: "weekly",
- priority: 1,
- },
- // Future dynamic routes can be added here:
- // {
- // url: `${BASE_URL}/games/${game.slug}`,
- // lastModified: game.updatedAt,
- // changeFrequency: "weekly",
- // priority: 0.8,
- // },
- ]
-}
-```
-
-- [ ] **Step 2: Verify sitemap builds**
-
-Run: `bun run build`
-Expected: Build succeeds. `/sitemap.xml` route available.
-
----
-
-### Task 5: Create Robots.txt
-
-**Files:**
-- Create: `app/robots.ts`
-
-Allows all crawlers, points to the sitemap, and sets the host.
-
-- [ ] **Step 1: Create `app/robots.ts`**
-
-```ts
-import type { MetadataRoute } from "next"
-
-export default function robots(): MetadataRoute.Robots {
- return {
- rules: {
- userAgent: "*",
- allow: "/",
- },
- sitemap: "https://deckyvault.xyz/sitemap.xml",
- host: "https://deckyvault.xyz",
- }
-}
-```
-
-- [ ] **Step 2: Verify robots.txt builds**
-
-Run: `bun run build`
-Expected: Build succeeds. `/robots.txt` route available.
-
----
-
-### Task 6: Create Web App Manifest
-
-**Files:**
-- Create: `app/manifest.ts`
-
-Generates `manifest.webmanifest` for PWA support and better mobile experience. Uses the existing `app/icon.png` and the brand colors from `globals.css`.
-
-- [ ] **Step 1: Create `app/manifest.ts`**
-
-```ts
-import type { MetadataRoute } from "next"
-
-export default function manifest(): MetadataRoute.Manifest {
- return {
- name: "DeckyVault - Steam Deck Benchmarks & Settings",
- short_name: "DeckyVault",
- description:
- "A fast, modern browser for finding game benchmarks, settings, and guides for Steam Deck OLED, Steam Deck LCD, and Steam Machine.",
- start_url: "/",
- display: "standalone",
- background_color: "#100b14",
- theme_color: "#eb3779",
- icons: [
- {
- src: "/icon.png",
- sizes: "any",
- type: "image/png",
- },
- ],
- }
-}
-```
-
-Note: `app/icon.png` is served at `/icon.png` by Next.js (the existing build output confirms this route exists).
-
-- [ ] **Step 2: Verify manifest builds**
-
-Run: `bun run build`
-Expected: Build succeeds. `/manifest.webmanifest` route available.
-
----
-
-## Chunk 3: README Update
-
-### Task 7: Update README.md
-
-**Files:**
-- Modify: `README.md`
-
-The current README is outdated — references npm/yarn/pnpm instead of bun, doesn't mention Elysia, Drizzle, Tiptap, better-auth, S3 storage, or the actual full tech stack. Needs to reflect the current state and note that the site is under development.
-
-- [ ] **Step 1: Replace `README.md` content**
-
-```markdown
-# DeckyVault
-
-An open-source, community-driven database for Steam Deck (OLED & LCD) and Steam Machine compatibility, performance metrics, and settings.
-
-> **Note:** DeckyVault is currently under active development. The site is live at [deckyvault.xyz](https://deckyvault.xyz) but features are being built incrementally. Stay tuned!
-
-## Why DeckyVault?
-
-Gaming on the Steam Deck is incredible, but finding reliable, detailed answers about whether a game will run well — or how to optimize it — can be frustrating. Existing resources are often fragmented, outdated, or lack the depth the community needs.
-
-**DeckyVault** aims to solve this by being the definitive, community-first hub for Steam Deck performance data.
-
-## Features
-
-- **Community-First Contributions** — Built by the community, for the community. Anyone can contribute data and improvements.
-- **Streamlined Filters & Searching** — Quickly find the games you're looking for with powerful search and filtering.
-- **Rich Performance Metrics**
- - Community average FPS (**low**, **avg**, **high**).
- - Extended FPS metrics for technologies like **FSR** and **Frame Generation**.
-- **Beautifully Formatted Settings** — No more endless single-column lists. Every game features **sectioned setting tables** that mirror in-game menus.
-- **Historical FPS Graphs** — Track performance improvements over time across:
- - Game Versions
- - Proton / Native builds
- - SteamOS Versions
-- **Multi-Device Support** — Dedicated metrics and optimized settings for **Steam Deck OLED**, **Steam Deck LCD**, and **Steam Machine**, all on a single page with quick toggles.
-- **Comprehensive Compatibility Tracking**
- - Proton vs. Native status
- - Online Play & Anti-Cheat status
- - Straightforward "Playable" tags
-- **Loading & Storage Metrics** — Track initial launch times, game load times, world loading, and **SD Card** performance.
-- **Community & Steam Integration** — On-site comments plus existing reviews, artwork, and stats pulled directly from Steam.
-- **Open Source** — Fully transparent. Help us build the best resource for Deck gamers.
-
-## Tech Stack
-
-- **[Next.js 16](https://nextjs.org)** — React framework (App Router)
-- **[React 19](https://react.dev)** — UI library
-- **[TypeScript](https://www.typescriptlang.org)** — Type safety
-- **[Tailwind CSS v4](https://tailwindcss.com)** — Utility-first styling
-- **[Elysia](https://elysiajs.com)** — Backend API framework
-- **[Drizzle ORM](https://orm.drizzle.team)** — Type-safe database queries
-- **[PostgreSQL](https://www.postgresql.org)** — Primary database
-- **[better-auth](https://better-auth.com)** — Authentication (Google & Discord OAuth)
-- **[Tiptap](https://tiptap.dev)** — Rich text editor
-- **[Motion](https://motion.dev)** — Animations
-- **[AWS S3](https://aws.amazon.com/s3/)** — File storage
-- **[Bun](https://bun.sh)** — Package manager & runtime
-
-## Getting Started
-
-Clone the repository and install dependencies:
-
-```bash
-bun install
-```
-
-Copy the environment file and configure your values:
-
-```bash
-cp .env.example .env.local
-```
-
-Run the development server:
-
-```bash
-bun run dev
-```
-
-Open [http://localhost:3000](http://localhost:3000) to see the result.
-
-## Scripts
-
-| Command | Description |
-|---------|-------------|
-| `bun run dev` | Start development server (HTTPS) |
-| `bun run build` | Create production build |
-| `bun run start` | Start production server |
-| `bun run lint` | Run ESLint |
-
-## Contributing
-
-We welcome contributions of all kinds! Whether you want to:
-
-- Suggest a new feature
-- Report a bug
-- Improve the design
-- Contribute code
-
-Feel free to open an [issue](https://github.com/AdrianBonpin/deckyvault/issues) or submit a [pull request](https://github.com/AdrianBonpin/deckyvault/pulls).
-
-If you're interested in helping develop or design the project, don't hesitate to reach out!
-
-## Roadmap
-
-This project is actively being developed. Here are some of the major items on our radar:
-
-- [ ] Core database and API for game entries
-- [ ] Advanced search and filter system
-- [ ] Community submission and moderation tools
-- [ ] FPS graphing and historical data visualization
-- [ ] Steam API integration for reviews, artwork, and stats
-- [ ] Support for additional handheld devices *(future consideration)*
-
-Stay tuned for updates!
-
-## License
-
-This project is open-source. *(Add your specific license here, e.g., MIT, GPL-3.0)*
-
-## Acknowledgements
-
-- Built with love for the Steam Deck community.
-- Check out the author's existing project for an idea of what to expect: **[https://grounds.ph](https://grounds.ph)**
-
----
-
-*DeckyVault is an independent project and is not affiliated with Valve Corporation.*
-```
-
-- [ ] **Step 2: Verify no build impact**
-
-Run: `bun run build`
-Expected: Build succeeds (README changes don't affect build).
-
----
-
-## Chunk 4: Verification & Lint/Build Cleanup
-
-### Task 8: Final Verification
-
-**Files:**
-- All files created/modified in Tasks 1-7
-
-- [ ] **Step 1: Run full lint**
-
-Run: `bun run lint`
-Expected: No errors or warnings.
-
-- [ ] **Step 2: Run full build**
-
-Run: `bun run build`
-Expected: Build succeeds. Verify the following routes are generated:
-- `/` — main page
-- `/icon.png` — favicon (already existed)
-- `/opengraph-image` — OG banner
-- `/twitter-image` — Twitter card
-- `/sitemap.xml` — sitemap
-- `/robots.txt` — robots
-- `/manifest.webmanifest` — PWA manifest
-
-- [ ] **Step 3: Fix any lint or build errors**
-
-If lint or build fails, fix the issues before claiming completion. Common issues to watch for:
-- TypeScript errors in OG image files (ensure `readFile` import works)
-- ESLint warnings on unused variables or missing dependencies
-- Build failures from incorrect `ImageResponse` JSX
-
-- [ ] **Step 4: Commit all changes**
-
-```bash
-git add app/layout.tsx app/opengraph-image.tsx app/twitter-image.tsx app/sitemap.ts app/robots.ts app/manifest.ts README.md
-git commit -m "feat: add comprehensive SEO (metadata, OG images, sitemap, robots, manifest) and update README"
-```
-
----
-
-## Summary of Routes After Implementation
-
-| Route | Type | Source File |
-|-------|------|-------------|
-| `/` | Static page | `app/page.tsx` |
-| `/icon.png` | Favicon | `app/icon.png` |
-| `/opengraph-image` | OG banner (PNG) | `app/opengraph-image.tsx` |
-| `/twitter-image` | Twitter card (PNG) | `app/twitter-image.tsx` |
-| `/sitemap.xml` | Sitemap | `app/sitemap.ts` |
-| `/robots.txt` | Robots | `app/robots.ts` |
-| `/manifest.webmanifest` | PWA manifest | `app/manifest.ts` |
diff --git a/lib/auth-client.ts b/lib/auth-client.ts
new file mode 100644
index 0000000..613ab7c
--- /dev/null
+++ b/lib/auth-client.ts
@@ -0,0 +1,15 @@
+import { createAuthClient } from 'better-auth/client'
+import { adminClient, emailOTPClient, lastLoginMethodClient } from 'better-auth/client/plugins'
+import { passkeyClient } from '@better-auth/passkey/client'
+
+export const authClient = createAuthClient({
+ baseURL: process.env.NEXT_PUBLIC_BASE_URL || "https://localhost:3000",
+ plugins: [
+ emailOTPClient(),
+ passkeyClient(),
+ lastLoginMethodClient(),
+ adminClient()
+ ]
+})
+
+export const { signIn, signOut, signUp, getSession, useSession } = authClient
\ No newline at end of file
diff --git a/lib/auth.ts b/lib/auth.ts
new file mode 100644
index 0000000..03a1784
--- /dev/null
+++ b/lib/auth.ts
@@ -0,0 +1,59 @@
+import { betterAuth } from 'better-auth'
+import { admin, emailOTP, lastLoginMethod } from 'better-auth/plugins'
+import { passkey } from '@better-auth/passkey'
+import { drizzleAdapter } from '@better-auth/drizzle-adapter'
+import { db } from '@/lib/db/index'
+
+export const auth = betterAuth({
+ experimental: { joins: true },
+ database: drizzleAdapter(db, {
+ provider: 'pg'
+ }),
+ plugins: [
+ emailOTP({
+ async sendVerificationOTP({ email, otp, type }) {
+ if (type === 'sign-in') {
+ // TODO: send the OTP to the user's email address
+ } else if (type === 'email-verification') {
+ // TODO: send the OTP to the user's email address for email verification
+ } else {
+ // TODO: send the OTP to the user's email address for password reset
+ }
+ }
+ }),
+ passkey(),
+ lastLoginMethod(),
+ admin()
+ ],
+ socialProviders: {
+ google: {
+ clientId: process.env.GOOGLE_CLIENT_ID || "",
+ clientSecret: process.env.GOOGLE_CLIENT_SECRET || "",
+ },
+ discord: {
+ clientId: process.env.DISCORD_CLIENT_ID || "",
+ clientSecret: process.env.DISCORD_CLIENT_SECRET || "",
+ },
+ },
+ trustedOrigins: [process.env.NEXT_PUBLIC_BASE_URL || "https://localhost:3000"],
+ rateLimit: {
+ enabled: true,
+ window: 60,
+ max: 100
+ },
+ session: {
+ expiresIn: 60 * 60 * 24 * 7,
+ updateAge: 60 * 60 * 24,
+ cookieCache: {
+ enabled: true,
+ maxAge: 15 * 60,
+ },
+ },
+ account: {
+ accountLinking: {
+ enabled: true,
+ trustedProviders: ['google', 'discord'],
+ allowDifferentEmails: true
+ }
+ }
+})
\ No newline at end of file