diff --git a/apps/readest-app/docs/architecture.md b/apps/readest-app/docs/architecture.md
new file mode 100644
index 00000000..79e27b72
--- /dev/null
+++ b/apps/readest-app/docs/architecture.md
@@ -0,0 +1,581 @@
+# Readest Architecture
+
+This document gives a system-level view of Readest: how the pieces fit together,
+which side of the wire each piece runs on, and what each module is responsible
+for. It complements [`code-layout.md`](./code-layout.md), which focuses on the
+directory layout. Read this one first if you want to understand the system; read
+that one when you need to find a specific file.
+
+The diagrams use [Mermaid](https://mermaid.js.org/) and render natively on
+GitHub.
+
+## 1. High-level picture
+
+Readest is a single TypeScript/React codebase (`apps/readest-app`) compiled into
+multiple targets:
+
+- a **desktop app** (Windows / macOS / Linux) via Tauri v2
+- a **mobile app** (Android / iOS) via Tauri v2 mobile
+- a **web app** running on Next.js / Cloudflare Workers (OpenNext) at
+ [web.readest.com](https://web.readest.com)
+- two **side surfaces**: a "Send to Readest" browser extension
+ (`apps/readest-app/extension/send-to-readest`) and a Windows thumbnail
+ shell extension (`apps/readest-app/extensions/windows-thumbnail`)
+
+The same React UI runs in all targets. What differs is the **host shell** under
+the UI and the **set of services** that the UI binds to at runtime — see
+section 4.
+
+```mermaid
+flowchart LR
+ subgraph Clients
+ Desktop["Desktop app
(Tauri shell + React UI)"]
+ Mobile["Mobile app
(Tauri Android/iOS + React UI)"]
+ Web["Web app
(Next.js + React UI)"]
+ Ext["Browser extension
(Send to Readest)"]
+ WinExt["Windows shell ext
(thumbnail provider)"]
+ end
+
+ subgraph Backend["Readest backend (Next.js routes + Cloudflare Worker)"]
+ AppApi["src/app/api/*
(App Router)"]
+ PagesApi["src/pages/api/*
(Pages Router)"]
+ RuntimeCfg["/runtime-config.js
(server-injected config)"]
+ Worker["workers/send-email
(Cloudflare Worker)"]
+ end
+
+ subgraph Cloud["External services"]
+ Supabase["Supabase
(auth + Postgres)"]
+ S3["Object storage
(S3 / R2)"]
+ Stripe["Stripe
(billing)"]
+ AI["AI providers
(OpenAI / Ollama / ...)"]
+ Trans["Translators
(DeepL / Google / Azure / Yandex)"]
+ Meta["Metadata providers
(Google Books / Open Library)"]
+ Dict["Dictionary sources
(Wikipedia / Wiktionary / StarDict)"]
+ OPDS["OPDS catalogs / Calibre"]
+ Hardcover["Hardcover GraphQL"]
+ Readwise["Readwise"]
+ TTS["Edge TTS"]
+ IAP["Apple / Google IAP"]
+ end
+
+ Desktop --> Backend
+ Mobile --> Backend
+ Web --> Backend
+ Ext --> PagesApi
+ WinExt -.reads files.-> Desktop
+
+ PagesApi --> Supabase
+ PagesApi --> S3
+ PagesApi --> Trans
+ AppApi --> Supabase
+ AppApi --> Stripe
+ AppApi --> AI
+ AppApi --> Meta
+ AppApi --> OPDS
+ AppApi --> Hardcover
+ AppApi --> TTS
+ AppApi --> IAP
+
+ Web -.direct.-> Dict
+ Desktop -.direct.-> Dict
+ Mobile -.direct.-> Dict
+ Web -.direct.-> Readwise
+ Desktop -.direct.-> Readwise
+```
+
+The `Backend` box is **the same code on all clients**. In the web target it is
+deployed as a Cloudflare Worker (via `@opennextjs/cloudflare` and
+`wrangler.toml`). In the Tauri targets the same routes are served by a Next.js
+runtime, but most clients hit the production deployment over HTTPS.
+
+## 2. Process boundaries
+
+There are three runtimes in play:
+
+```mermaid
+flowchart TB
+ subgraph Browser["Web runtime (browser / Tauri webview)"]
+ UI["React UI
(src/app, src/components, src/hooks, src/store)"]
+ Domain["Shared domain layer
(src/services, src/utils, src/libs)"]
+ Foliate["foliate-js
(packages/foliate-js)"]
+ SW["Service worker (sw.ts)"]
+ TursoWasm["Turso WASM
(replica DB in browser)"]
+ end
+
+ subgraph Native["Tauri native host (Rust)"]
+ TauriCore["src-tauri/src/lib.rs
(commands, dir_scanner, transfer_file, clip_url, discord_rpc)"]
+ Plugins["Tauri plugins
(fs, dialog, http, oauth, deep-link, opener, updater,
native-bridge, native-tts, turso, webview-upgrade)"]
+ end
+
+ subgraph Server["Next.js server (Worker / Node)"]
+ Routes["App Router + Pages Router routes"]
+ Mw["middleware.ts
(CORS + COOP/COEP)"]
+ RuntimeRoute["app/runtime-config.js
(server-rendered config script)"]
+ end
+
+ UI --> Domain
+ Domain --> Foliate
+ UI --> SW
+ Domain --> TursoWasm
+
+ Domain -- "@tauri-apps/api invoke()" --> TauriCore
+ TauriCore --> Plugins
+
+ Domain -- "fetch(/api/...)" --> Routes
+ Browser -- "