diff --git a/app/(app)/books/bbd/[chapter]/page.tsx b/app/(app)/(reader)/books/bbd/[chapter]/page.tsx
similarity index 100%
rename from app/(app)/books/bbd/[chapter]/page.tsx
rename to app/(app)/(reader)/books/bbd/[chapter]/page.tsx
diff --git a/app/(app)/books/bbd/page.tsx b/app/(app)/(reader)/books/bbd/page.tsx
similarity index 100%
rename from app/(app)/books/bbd/page.tsx
rename to app/(app)/(reader)/books/bbd/page.tsx
diff --git a/app/(app)/books/bg/[chapter]/[verse]/page.tsx b/app/(app)/(reader)/books/bg/[chapter]/[verse]/page.tsx
similarity index 98%
rename from app/(app)/books/bg/[chapter]/[verse]/page.tsx
rename to app/(app)/(reader)/books/bg/[chapter]/[verse]/page.tsx
index b702cd83..83ae49c7 100644
--- a/app/(app)/books/bg/[chapter]/[verse]/page.tsx
+++ b/app/(app)/(reader)/books/bg/[chapter]/[verse]/page.tsx
@@ -4,7 +4,7 @@ import { PageHeader } from "@/components/layout/page-header";
import { FindInPage } from "@/components/layout/find-in-page";
import { notFound } from "next/navigation";
import { getBookMeta, getSingleVerse, getChapterVerseIndex } from "@/lib/api";
-import { ChapterViewer } from "@/app/(app)/books/chapter-viewer";
+import { ChapterViewer } from "@/app/(app)/(reader)/books/chapter-viewer";
import type { MobileNavGroup } from "@/components/layout/mobile-nav";
import { ogForVerse, extractQuoteFromHl, buildOgMeta } from "@/lib/og";
diff --git a/app/(app)/books/bg/[chapter]/page.tsx b/app/(app)/(reader)/books/bg/[chapter]/page.tsx
similarity index 100%
rename from app/(app)/books/bg/[chapter]/page.tsx
rename to app/(app)/(reader)/books/bg/[chapter]/page.tsx
diff --git a/app/(app)/books/bg/page.tsx b/app/(app)/(reader)/books/bg/page.tsx
similarity index 100%
rename from app/(app)/books/bg/page.tsx
rename to app/(app)/(reader)/books/bg/page.tsx
diff --git a/app/(app)/books/bk/[chapter]/page.tsx b/app/(app)/(reader)/books/bk/[chapter]/page.tsx
similarity index 100%
rename from app/(app)/books/bk/[chapter]/page.tsx
rename to app/(app)/(reader)/books/bk/[chapter]/page.tsx
diff --git a/app/(app)/books/bk/page.tsx b/app/(app)/(reader)/books/bk/page.tsx
similarity index 100%
rename from app/(app)/books/bk/page.tsx
rename to app/(app)/(reader)/books/bk/page.tsx
diff --git a/app/(app)/books/books-grid.tsx b/app/(app)/(reader)/books/books-grid.tsx
similarity index 100%
rename from app/(app)/books/books-grid.tsx
rename to app/(app)/(reader)/books/books-grid.tsx
diff --git a/app/(app)/books/bs/[chapter]/[verse]/page.tsx b/app/(app)/(reader)/books/bs/[chapter]/[verse]/page.tsx
similarity index 98%
rename from app/(app)/books/bs/[chapter]/[verse]/page.tsx
rename to app/(app)/(reader)/books/bs/[chapter]/[verse]/page.tsx
index 0ed53e90..68ec3f26 100644
--- a/app/(app)/books/bs/[chapter]/[verse]/page.tsx
+++ b/app/(app)/(reader)/books/bs/[chapter]/[verse]/page.tsx
@@ -12,7 +12,7 @@ import {
type VerseIndexEntry,
type VerseRecord,
} from "@/lib/api";
-import { ChapterViewer } from "@/app/(app)/books/chapter-viewer";
+import { ChapterViewer } from "@/app/(app)/(reader)/books/chapter-viewer";
import type { MobileNavGroup } from "@/components/layout/mobile-nav";
import { ogForVerse, extractQuoteFromHl, buildOgMeta } from "@/lib/og";
diff --git a/app/(app)/books/bs/[chapter]/page.tsx b/app/(app)/(reader)/books/bs/[chapter]/page.tsx
similarity index 100%
rename from app/(app)/books/bs/[chapter]/page.tsx
rename to app/(app)/(reader)/books/bs/[chapter]/page.tsx
diff --git a/app/(app)/books/bs/page.tsx b/app/(app)/(reader)/books/bs/page.tsx
similarity index 100%
rename from app/(app)/books/bs/page.tsx
rename to app/(app)/(reader)/books/bs/page.tsx
diff --git a/app/(app)/books/cbhc/[chapter]/page.tsx b/app/(app)/(reader)/books/cbhc/[chapter]/page.tsx
similarity index 100%
rename from app/(app)/books/cbhc/[chapter]/page.tsx
rename to app/(app)/(reader)/books/cbhc/[chapter]/page.tsx
diff --git a/app/(app)/books/cbhc/page.tsx b/app/(app)/(reader)/books/cbhc/page.tsx
similarity index 100%
rename from app/(app)/books/cbhc/page.tsx
rename to app/(app)/(reader)/books/cbhc/page.tsx
diff --git a/app/(app)/books/cc/[lila]/[chapter]/[verse]/page.tsx b/app/(app)/(reader)/books/cc/[lila]/[chapter]/[verse]/page.tsx
similarity index 98%
rename from app/(app)/books/cc/[lila]/[chapter]/[verse]/page.tsx
rename to app/(app)/(reader)/books/cc/[lila]/[chapter]/[verse]/page.tsx
index b6999bf0..417ab0b0 100644
--- a/app/(app)/books/cc/[lila]/[chapter]/[verse]/page.tsx
+++ b/app/(app)/(reader)/books/cc/[lila]/[chapter]/[verse]/page.tsx
@@ -6,7 +6,7 @@ import { PageHeader } from "@/components/layout/page-header";
import { FindInPage } from "@/components/layout/find-in-page";
import { notFound } from "next/navigation";
import { getBookMeta, getSingleVerse, getChapterVerseIndex } from "@/lib/api";
-import { ChapterViewer } from "@/app/(app)/books/chapter-viewer";
+import { ChapterViewer } from "@/app/(app)/(reader)/books/chapter-viewer";
import type { MobileNavGroup } from "@/components/layout/mobile-nav";
import { ogForVerse, extractQuoteFromHl, buildOgMeta } from "@/lib/og";
diff --git a/app/(app)/books/cc/[lila]/[chapter]/page.tsx b/app/(app)/(reader)/books/cc/[lila]/[chapter]/page.tsx
similarity index 100%
rename from app/(app)/books/cc/[lila]/[chapter]/page.tsx
rename to app/(app)/(reader)/books/cc/[lila]/[chapter]/page.tsx
diff --git a/app/(app)/books/cc/[lila]/page.tsx b/app/(app)/(reader)/books/cc/[lila]/page.tsx
similarity index 100%
rename from app/(app)/books/cc/[lila]/page.tsx
rename to app/(app)/(reader)/books/cc/[lila]/page.tsx
diff --git a/app/(app)/books/cc/page.tsx b/app/(app)/(reader)/books/cc/page.tsx
similarity index 100%
rename from app/(app)/books/cc/page.tsx
rename to app/(app)/(reader)/books/cc/page.tsx
diff --git a/app/(app)/books/chapter-viewer.tsx b/app/(app)/(reader)/books/chapter-viewer.tsx
similarity index 100%
rename from app/(app)/books/chapter-viewer.tsx
rename to app/(app)/(reader)/books/chapter-viewer.tsx
diff --git a/app/(app)/books/ds/[chapter]/page.tsx b/app/(app)/(reader)/books/ds/[chapter]/page.tsx
similarity index 100%
rename from app/(app)/books/ds/[chapter]/page.tsx
rename to app/(app)/(reader)/books/ds/[chapter]/page.tsx
diff --git a/app/(app)/books/ds/page.tsx b/app/(app)/(reader)/books/ds/page.tsx
similarity index 100%
rename from app/(app)/books/ds/page.tsx
rename to app/(app)/(reader)/books/ds/page.tsx
diff --git a/app/(app)/books/ej/[chapter]/page.tsx b/app/(app)/(reader)/books/ej/[chapter]/page.tsx
similarity index 100%
rename from app/(app)/books/ej/[chapter]/page.tsx
rename to app/(app)/(reader)/books/ej/[chapter]/page.tsx
diff --git a/app/(app)/books/ej/page.tsx b/app/(app)/(reader)/books/ej/page.tsx
similarity index 100%
rename from app/(app)/books/ej/page.tsx
rename to app/(app)/(reader)/books/ej/page.tsx
diff --git a/app/(app)/books/ek/[chapter]/page.tsx b/app/(app)/(reader)/books/ek/[chapter]/page.tsx
similarity index 100%
rename from app/(app)/books/ek/[chapter]/page.tsx
rename to app/(app)/(reader)/books/ek/[chapter]/page.tsx
diff --git a/app/(app)/books/ek/page.tsx b/app/(app)/(reader)/books/ek/page.tsx
similarity index 100%
rename from app/(app)/books/ek/page.tsx
rename to app/(app)/(reader)/books/ek/page.tsx
diff --git a/app/(app)/books/extracted-verse-count.tsx b/app/(app)/(reader)/books/extracted-verse-count.tsx
similarity index 100%
rename from app/(app)/books/extracted-verse-count.tsx
rename to app/(app)/(reader)/books/extracted-verse-count.tsx
diff --git a/app/(app)/books/gg/[chapter]/page.tsx b/app/(app)/(reader)/books/gg/[chapter]/page.tsx
similarity index 100%
rename from app/(app)/books/gg/[chapter]/page.tsx
rename to app/(app)/(reader)/books/gg/[chapter]/page.tsx
diff --git a/app/(app)/books/gg/page.tsx b/app/(app)/(reader)/books/gg/page.tsx
similarity index 100%
rename from app/(app)/books/gg/page.tsx
rename to app/(app)/(reader)/books/gg/page.tsx
diff --git a/app/(app)/books/hkh/[chapter]/page.tsx b/app/(app)/(reader)/books/hkh/[chapter]/page.tsx
similarity index 100%
rename from app/(app)/books/hkh/[chapter]/page.tsx
rename to app/(app)/(reader)/books/hkh/[chapter]/page.tsx
diff --git a/app/(app)/books/hkh/page.tsx b/app/(app)/(reader)/books/hkh/page.tsx
similarity index 100%
rename from app/(app)/books/hkh/page.tsx
rename to app/(app)/(reader)/books/hkh/page.tsx
diff --git a/app/(app)/books/iso/[mantra]/page.tsx b/app/(app)/(reader)/books/iso/[mantra]/page.tsx
similarity index 100%
rename from app/(app)/books/iso/[mantra]/page.tsx
rename to app/(app)/(reader)/books/iso/[mantra]/page.tsx
diff --git a/app/(app)/books/iso/page.tsx b/app/(app)/(reader)/books/iso/page.tsx
similarity index 100%
rename from app/(app)/books/iso/page.tsx
rename to app/(app)/(reader)/books/iso/page.tsx
diff --git a/app/(app)/books/kb/[volume]/[slug]/page.tsx b/app/(app)/(reader)/books/kb/[volume]/[slug]/page.tsx
similarity index 100%
rename from app/(app)/books/kb/[volume]/[slug]/page.tsx
rename to app/(app)/(reader)/books/kb/[volume]/[slug]/page.tsx
diff --git a/app/(app)/books/kb/[volume]/page.tsx b/app/(app)/(reader)/books/kb/[volume]/page.tsx
similarity index 100%
rename from app/(app)/books/kb/[volume]/page.tsx
rename to app/(app)/(reader)/books/kb/[volume]/page.tsx
diff --git a/app/(app)/books/kb/page.tsx b/app/(app)/(reader)/books/kb/page.tsx
similarity index 100%
rename from app/(app)/books/kb/page.tsx
rename to app/(app)/(reader)/books/kb/page.tsx
diff --git a/app/(app)/books/lcl/[chapter]/page.tsx b/app/(app)/(reader)/books/lcl/[chapter]/page.tsx
similarity index 100%
rename from app/(app)/books/lcl/[chapter]/page.tsx
rename to app/(app)/(reader)/books/lcl/[chapter]/page.tsx
diff --git a/app/(app)/books/lcl/page.tsx b/app/(app)/(reader)/books/lcl/page.tsx
similarity index 100%
rename from app/(app)/books/lcl/page.tsx
rename to app/(app)/(reader)/books/lcl/page.tsx
diff --git a/app/(app)/books/lob/[chapter]/page.tsx b/app/(app)/(reader)/books/lob/[chapter]/page.tsx
similarity index 100%
rename from app/(app)/books/lob/[chapter]/page.tsx
rename to app/(app)/(reader)/books/lob/[chapter]/page.tsx
diff --git a/app/(app)/books/lob/page.tsx b/app/(app)/(reader)/books/lob/page.tsx
similarity index 100%
rename from app/(app)/books/lob/page.tsx
rename to app/(app)/(reader)/books/lob/page.tsx
diff --git a/app/(app)/books/mg/[chapter]/page.tsx b/app/(app)/(reader)/books/mg/[chapter]/page.tsx
similarity index 100%
rename from app/(app)/books/mg/[chapter]/page.tsx
rename to app/(app)/(reader)/books/mg/[chapter]/page.tsx
diff --git a/app/(app)/books/mg/page.tsx b/app/(app)/(reader)/books/mg/page.tsx
similarity index 100%
rename from app/(app)/books/mg/page.tsx
rename to app/(app)/(reader)/books/mg/page.tsx
diff --git a/app/(app)/books/mm/[chapter]/[verse]/page.tsx b/app/(app)/(reader)/books/mm/[chapter]/[verse]/page.tsx
similarity index 98%
rename from app/(app)/books/mm/[chapter]/[verse]/page.tsx
rename to app/(app)/(reader)/books/mm/[chapter]/[verse]/page.tsx
index 32b3eccc..ff27ea2a 100644
--- a/app/(app)/books/mm/[chapter]/[verse]/page.tsx
+++ b/app/(app)/(reader)/books/mm/[chapter]/[verse]/page.tsx
@@ -11,7 +11,7 @@ import {
type VerseIndexEntry,
type VerseRecord,
} from "@/lib/api";
-import { ChapterViewer } from "@/app/(app)/books/chapter-viewer";
+import { ChapterViewer } from "@/app/(app)/(reader)/books/chapter-viewer";
import type { MobileNavGroup } from "@/components/layout/mobile-nav";
import type { Metadata } from "next";
import { ogForVerse, extractQuoteFromHl, buildOgMeta } from "@/lib/og";
diff --git a/app/(app)/books/mm/[chapter]/page.tsx b/app/(app)/(reader)/books/mm/[chapter]/page.tsx
similarity index 100%
rename from app/(app)/books/mm/[chapter]/page.tsx
rename to app/(app)/(reader)/books/mm/[chapter]/page.tsx
diff --git a/app/(app)/books/mm/page.tsx b/app/(app)/(reader)/books/mm/page.tsx
similarity index 100%
rename from app/(app)/books/mm/page.tsx
rename to app/(app)/(reader)/books/mm/page.tsx
diff --git a/app/(app)/books/mog/[chapter]/page.tsx b/app/(app)/(reader)/books/mog/[chapter]/page.tsx
similarity index 100%
rename from app/(app)/books/mog/[chapter]/page.tsx
rename to app/(app)/(reader)/books/mog/[chapter]/page.tsx
diff --git a/app/(app)/books/mog/page.tsx b/app/(app)/(reader)/books/mog/page.tsx
similarity index 100%
rename from app/(app)/books/mog/page.tsx
rename to app/(app)/(reader)/books/mog/page.tsx
diff --git a/app/(app)/books/nbs/[chapter]/[verse]/page.tsx b/app/(app)/(reader)/books/nbs/[chapter]/[verse]/page.tsx
similarity index 98%
rename from app/(app)/books/nbs/[chapter]/[verse]/page.tsx
rename to app/(app)/(reader)/books/nbs/[chapter]/[verse]/page.tsx
index 3d136e38..4d0f7ade 100644
--- a/app/(app)/books/nbs/[chapter]/[verse]/page.tsx
+++ b/app/(app)/(reader)/books/nbs/[chapter]/[verse]/page.tsx
@@ -15,7 +15,7 @@ import {
type VerseRecord,
} from "@/lib/api";
import { ogForVerse, extractQuoteFromHl, buildOgMeta } from "@/lib/og";
-import { ChapterViewer } from "@/app/(app)/books/chapter-viewer";
+import { ChapterViewer } from "@/app/(app)/(reader)/books/chapter-viewer";
import type { MobileNavGroup } from "@/components/layout/mobile-nav";
type NbsMeta = {
diff --git a/app/(app)/books/nbs/[chapter]/page.tsx b/app/(app)/(reader)/books/nbs/[chapter]/page.tsx
similarity index 100%
rename from app/(app)/books/nbs/[chapter]/page.tsx
rename to app/(app)/(reader)/books/nbs/[chapter]/page.tsx
diff --git a/app/(app)/books/nbs/page.tsx b/app/(app)/(reader)/books/nbs/page.tsx
similarity index 100%
rename from app/(app)/books/nbs/page.tsx
rename to app/(app)/(reader)/books/nbs/page.tsx
diff --git a/app/(app)/books/nod/[chapter]/page.tsx b/app/(app)/(reader)/books/nod/[chapter]/page.tsx
similarity index 100%
rename from app/(app)/books/nod/[chapter]/page.tsx
rename to app/(app)/(reader)/books/nod/[chapter]/page.tsx
diff --git a/app/(app)/books/nod/page.tsx b/app/(app)/(reader)/books/nod/page.tsx
similarity index 100%
rename from app/(app)/books/nod/page.tsx
rename to app/(app)/(reader)/books/nod/page.tsx
diff --git a/app/(app)/books/noi/[text]/page.tsx b/app/(app)/(reader)/books/noi/[text]/page.tsx
similarity index 100%
rename from app/(app)/books/noi/[text]/page.tsx
rename to app/(app)/(reader)/books/noi/[text]/page.tsx
diff --git a/app/(app)/books/noi/page.tsx b/app/(app)/(reader)/books/noi/page.tsx
similarity index 100%
rename from app/(app)/books/noi/page.tsx
rename to app/(app)/(reader)/books/noi/page.tsx
diff --git a/app/(app)/books/ochkm/[chapter]/page.tsx b/app/(app)/(reader)/books/ochkm/[chapter]/page.tsx
similarity index 100%
rename from app/(app)/books/ochkm/[chapter]/page.tsx
rename to app/(app)/(reader)/books/ochkm/[chapter]/page.tsx
diff --git a/app/(app)/books/ochkm/page.tsx b/app/(app)/(reader)/books/ochkm/page.tsx
similarity index 100%
rename from app/(app)/books/ochkm/page.tsx
rename to app/(app)/(reader)/books/ochkm/page.tsx
diff --git a/app/(app)/books/owk/[chapter]/page.tsx b/app/(app)/(reader)/books/owk/[chapter]/page.tsx
similarity index 100%
rename from app/(app)/books/owk/[chapter]/page.tsx
rename to app/(app)/(reader)/books/owk/[chapter]/page.tsx
diff --git a/app/(app)/books/owk/page.tsx b/app/(app)/(reader)/books/owk/page.tsx
similarity index 100%
rename from app/(app)/books/owk/page.tsx
rename to app/(app)/(reader)/books/owk/page.tsx
diff --git a/app/(app)/books/page.tsx b/app/(app)/(reader)/books/page.tsx
similarity index 100%
rename from app/(app)/books/page.tsx
rename to app/(app)/(reader)/books/page.tsx
diff --git a/app/(app)/books/pk/[chapter]/page.tsx b/app/(app)/(reader)/books/pk/[chapter]/page.tsx
similarity index 100%
rename from app/(app)/books/pk/[chapter]/page.tsx
rename to app/(app)/(reader)/books/pk/[chapter]/page.tsx
diff --git a/app/(app)/books/pk/page.tsx b/app/(app)/(reader)/books/pk/page.tsx
similarity index 100%
rename from app/(app)/books/pk/page.tsx
rename to app/(app)/(reader)/books/pk/page.tsx
diff --git a/app/(app)/books/poy/[chapter]/page.tsx b/app/(app)/(reader)/books/poy/[chapter]/page.tsx
similarity index 100%
rename from app/(app)/books/poy/[chapter]/page.tsx
rename to app/(app)/(reader)/books/poy/[chapter]/page.tsx
diff --git a/app/(app)/books/poy/page.tsx b/app/(app)/(reader)/books/poy/page.tsx
similarity index 100%
rename from app/(app)/books/poy/page.tsx
rename to app/(app)/(reader)/books/poy/page.tsx
diff --git a/app/(app)/books/pqpa/[chapter]/page.tsx b/app/(app)/(reader)/books/pqpa/[chapter]/page.tsx
similarity index 100%
rename from app/(app)/books/pqpa/[chapter]/page.tsx
rename to app/(app)/(reader)/books/pqpa/[chapter]/page.tsx
diff --git a/app/(app)/books/pqpa/page.tsx b/app/(app)/(reader)/books/pqpa/page.tsx
similarity index 100%
rename from app/(app)/books/pqpa/page.tsx
rename to app/(app)/(reader)/books/pqpa/page.tsx
diff --git a/app/(app)/books/rv/[chapter]/page.tsx b/app/(app)/(reader)/books/rv/[chapter]/page.tsx
similarity index 100%
rename from app/(app)/books/rv/[chapter]/page.tsx
rename to app/(app)/(reader)/books/rv/[chapter]/page.tsx
diff --git a/app/(app)/books/rv/page.tsx b/app/(app)/(reader)/books/rv/page.tsx
similarity index 100%
rename from app/(app)/books/rv/page.tsx
rename to app/(app)/(reader)/books/rv/page.tsx
diff --git a/app/(app)/books/sb/[canto]/[chapter]/[verse]/page.tsx b/app/(app)/(reader)/books/sb/[canto]/[chapter]/[verse]/page.tsx
similarity index 98%
rename from app/(app)/books/sb/[canto]/[chapter]/[verse]/page.tsx
rename to app/(app)/(reader)/books/sb/[canto]/[chapter]/[verse]/page.tsx
index d7c3411a..35cb5136 100644
--- a/app/(app)/books/sb/[canto]/[chapter]/[verse]/page.tsx
+++ b/app/(app)/(reader)/books/sb/[canto]/[chapter]/[verse]/page.tsx
@@ -6,7 +6,7 @@ import { PageHeader } from "@/components/layout/page-header";
import { FindInPage } from "@/components/layout/find-in-page";
import { notFound } from "next/navigation";
import { getBookMeta, getSingleVerse, getChapterVerseIndex } from "@/lib/api";
-import { ChapterViewer } from "@/app/(app)/books/chapter-viewer";
+import { ChapterViewer } from "@/app/(app)/(reader)/books/chapter-viewer";
import type { MobileNavGroup } from "@/components/layout/mobile-nav";
import { ogForVerse, extractQuoteFromHl, buildOgMeta } from "@/lib/og";
diff --git a/app/(app)/books/sb/[canto]/[chapter]/page.tsx b/app/(app)/(reader)/books/sb/[canto]/[chapter]/page.tsx
similarity index 100%
rename from app/(app)/books/sb/[canto]/[chapter]/page.tsx
rename to app/(app)/(reader)/books/sb/[canto]/[chapter]/page.tsx
diff --git a/app/(app)/books/sb/[canto]/page.tsx b/app/(app)/(reader)/books/sb/[canto]/page.tsx
similarity index 100%
rename from app/(app)/books/sb/[canto]/page.tsx
rename to app/(app)/(reader)/books/sb/[canto]/page.tsx
diff --git a/app/(app)/books/sb/page.tsx b/app/(app)/(reader)/books/sb/page.tsx
similarity index 100%
rename from app/(app)/books/sb/page.tsx
rename to app/(app)/(reader)/books/sb/page.tsx
diff --git a/app/(app)/books/sb1962/[chapter]/[verse]/page.tsx b/app/(app)/(reader)/books/sb1962/[chapter]/[verse]/page.tsx
similarity index 98%
rename from app/(app)/books/sb1962/[chapter]/[verse]/page.tsx
rename to app/(app)/(reader)/books/sb1962/[chapter]/[verse]/page.tsx
index aaea5cda..6a974c92 100644
--- a/app/(app)/books/sb1962/[chapter]/[verse]/page.tsx
+++ b/app/(app)/(reader)/books/sb1962/[chapter]/[verse]/page.tsx
@@ -4,7 +4,7 @@ import { PageHeader } from "@/components/layout/page-header";
import { FindInPage } from "@/components/layout/find-in-page";
import { notFound } from "next/navigation";
import { getBookMeta, getSingleVerse, getChapterVerseIndex } from "@/lib/api";
-import { ChapterViewer } from "@/app/(app)/books/chapter-viewer";
+import { ChapterViewer } from "@/app/(app)/(reader)/books/chapter-viewer";
import type { MobileNavGroup } from "@/components/layout/mobile-nav";
import { ogForVerse, extractQuoteFromHl, buildOgMeta } from "@/lib/og";
diff --git a/app/(app)/books/sb1962/[chapter]/page.tsx b/app/(app)/(reader)/books/sb1962/[chapter]/page.tsx
similarity index 100%
rename from app/(app)/books/sb1962/[chapter]/page.tsx
rename to app/(app)/(reader)/books/sb1962/[chapter]/page.tsx
diff --git a/app/(app)/books/sb1962/page.tsx b/app/(app)/(reader)/books/sb1962/page.tsx
similarity index 100%
rename from app/(app)/books/sb1962/page.tsx
rename to app/(app)/(reader)/books/sb1962/page.tsx
diff --git a/app/(app)/books/ssr/[chapter]/page.tsx b/app/(app)/(reader)/books/ssr/[chapter]/page.tsx
similarity index 100%
rename from app/(app)/books/ssr/[chapter]/page.tsx
rename to app/(app)/(reader)/books/ssr/[chapter]/page.tsx
diff --git a/app/(app)/books/ssr/page.tsx b/app/(app)/(reader)/books/ssr/page.tsx
similarity index 100%
rename from app/(app)/books/ssr/page.tsx
rename to app/(app)/(reader)/books/ssr/page.tsx
diff --git a/app/(app)/books/sva/[chapter]/page.tsx b/app/(app)/(reader)/books/sva/[chapter]/page.tsx
similarity index 100%
rename from app/(app)/books/sva/[chapter]/page.tsx
rename to app/(app)/(reader)/books/sva/[chapter]/page.tsx
diff --git a/app/(app)/books/sva/page.tsx b/app/(app)/(reader)/books/sva/page.tsx
similarity index 100%
rename from app/(app)/books/sva/page.tsx
rename to app/(app)/(reader)/books/sva/page.tsx
diff --git a/app/(app)/books/tlc/[chapter]/page.tsx b/app/(app)/(reader)/books/tlc/[chapter]/page.tsx
similarity index 100%
rename from app/(app)/books/tlc/[chapter]/page.tsx
rename to app/(app)/(reader)/books/tlc/[chapter]/page.tsx
diff --git a/app/(app)/books/tlc/page.tsx b/app/(app)/(reader)/books/tlc/page.tsx
similarity index 100%
rename from app/(app)/books/tlc/page.tsx
rename to app/(app)/(reader)/books/tlc/page.tsx
diff --git a/app/(app)/books/tlk/[chapter]/page.tsx b/app/(app)/(reader)/books/tlk/[chapter]/page.tsx
similarity index 100%
rename from app/(app)/books/tlk/[chapter]/page.tsx
rename to app/(app)/(reader)/books/tlk/[chapter]/page.tsx
diff --git a/app/(app)/books/tlk/page.tsx b/app/(app)/(reader)/books/tlk/page.tsx
similarity index 100%
rename from app/(app)/books/tlk/page.tsx
rename to app/(app)/(reader)/books/tlk/page.tsx
diff --git a/app/(app)/books/tqk/[chapter]/page.tsx b/app/(app)/(reader)/books/tqk/[chapter]/page.tsx
similarity index 100%
rename from app/(app)/books/tqk/[chapter]/page.tsx
rename to app/(app)/(reader)/books/tqk/[chapter]/page.tsx
diff --git a/app/(app)/books/tqk/page.tsx b/app/(app)/(reader)/books/tqk/page.tsx
similarity index 100%
rename from app/(app)/books/tqk/page.tsx
rename to app/(app)/(reader)/books/tqk/page.tsx
diff --git a/app/(app)/books/ttp/[chapter]/page.tsx b/app/(app)/(reader)/books/ttp/[chapter]/page.tsx
similarity index 100%
rename from app/(app)/books/ttp/[chapter]/page.tsx
rename to app/(app)/(reader)/books/ttp/[chapter]/page.tsx
diff --git a/app/(app)/books/ttp/page.tsx b/app/(app)/(reader)/books/ttp/page.tsx
similarity index 100%
rename from app/(app)/books/ttp/page.tsx
rename to app/(app)/(reader)/books/ttp/page.tsx
diff --git a/app/(app)/books/va/[chapter]/page.tsx b/app/(app)/(reader)/books/va/[chapter]/page.tsx
similarity index 100%
rename from app/(app)/books/va/[chapter]/page.tsx
rename to app/(app)/(reader)/books/va/[chapter]/page.tsx
diff --git a/app/(app)/books/va/page.tsx b/app/(app)/(reader)/books/va/page.tsx
similarity index 100%
rename from app/(app)/books/va/page.tsx
rename to app/(app)/(reader)/books/va/page.tsx
diff --git a/app/(app)/disciples/disciples-browser.tsx b/app/(app)/(reader)/disciples/disciples-browser.tsx
similarity index 100%
rename from app/(app)/disciples/disciples-browser.tsx
rename to app/(app)/(reader)/disciples/disciples-browser.tsx
diff --git a/app/(app)/disciples/page.tsx b/app/(app)/(reader)/disciples/page.tsx
similarity index 100%
rename from app/(app)/disciples/page.tsx
rename to app/(app)/(reader)/disciples/page.tsx
diff --git a/app/(app)/essays/[slug]/page.tsx b/app/(app)/(reader)/essays/[slug]/page.tsx
similarity index 100%
rename from app/(app)/essays/[slug]/page.tsx
rename to app/(app)/(reader)/essays/[slug]/page.tsx
diff --git a/app/(app)/essays/essays-grid.tsx b/app/(app)/(reader)/essays/essays-grid.tsx
similarity index 100%
rename from app/(app)/essays/essays-grid.tsx
rename to app/(app)/(reader)/essays/essays-grid.tsx
diff --git a/app/(app)/essays/page.tsx b/app/(app)/(reader)/essays/page.tsx
similarity index 100%
rename from app/(app)/essays/page.tsx
rename to app/(app)/(reader)/essays/page.tsx
diff --git a/app/(app)/glossary/[letter]/page.tsx b/app/(app)/(reader)/glossary/[letter]/page.tsx
similarity index 100%
rename from app/(app)/glossary/[letter]/page.tsx
rename to app/(app)/(reader)/glossary/[letter]/page.tsx
diff --git a/app/(app)/glossary/glossary-client.tsx b/app/(app)/(reader)/glossary/glossary-client.tsx
similarity index 100%
rename from app/(app)/glossary/glossary-client.tsx
rename to app/(app)/(reader)/glossary/glossary-client.tsx
diff --git a/app/(app)/glossary/page.tsx b/app/(app)/(reader)/glossary/page.tsx
similarity index 100%
rename from app/(app)/glossary/page.tsx
rename to app/(app)/(reader)/glossary/page.tsx
diff --git a/app/(app)/journals/[slug]/[chapter]/page.tsx b/app/(app)/(reader)/journals/[slug]/[chapter]/page.tsx
similarity index 100%
rename from app/(app)/journals/[slug]/[chapter]/page.tsx
rename to app/(app)/(reader)/journals/[slug]/[chapter]/page.tsx
diff --git a/app/(app)/journals/[slug]/page.tsx b/app/(app)/(reader)/journals/[slug]/page.tsx
similarity index 100%
rename from app/(app)/journals/[slug]/page.tsx
rename to app/(app)/(reader)/journals/[slug]/page.tsx
diff --git a/app/(app)/journals/page.tsx b/app/(app)/(reader)/journals/page.tsx
similarity index 100%
rename from app/(app)/journals/page.tsx
rename to app/(app)/(reader)/journals/page.tsx
diff --git a/app/(app)/(reader)/layout.tsx b/app/(app)/(reader)/layout.tsx
new file mode 100644
index 00000000..11c7e92a
--- /dev/null
+++ b/app/(app)/(reader)/layout.tsx
@@ -0,0 +1,37 @@
+import { Suspense } from "react";
+import { TextSelectionToolbar } from "@/components/text-selection-toolbar";
+import { TextHighlighter } from "@/components/text-highlighter";
+import { PageBookmarks } from "@/components/social/page-bookmarks";
+
+/**
+ * Reader layout — the annotation surface for the actual scripture library
+ * (books, letters, lectures, essays, glossary, journals, magazines, other
+ * documents, disciples).
+ *
+ * The text-selection toolbar and its two companions (the `?hl=` highlighter
+ * and the persistent in-text bookmark marks) only make sense over long-form
+ * scripture prose. Mounting them in this nested route group — rather than the
+ * parent `(app)` layout — keeps them off the home feed, search, profiles,
+ * settings, downloads, Kautilya, etc., so a stray text selection on chrome
+ * text no longer pops the toolbar.
+ *
+ * This is a route group: it adds NO path segment, so every URL underneath is
+ * unchanged (`/books/...`, `/letters/...`, …). All the outer `(app)` chrome
+ * (header, sidebar, footer, bottom nav) still wraps these pages.
+ */
+export default function ReaderLayout({
+ children,
+}: {
+ children: React.ReactNode;
+}) {
+ return (
+ <>
+ {children}
+
+
+
+
+
+ >
+ );
+}
diff --git a/app/(app)/letters/[slug]/page.tsx b/app/(app)/(reader)/letters/[slug]/page.tsx
similarity index 100%
rename from app/(app)/letters/[slug]/page.tsx
rename to app/(app)/(reader)/letters/[slug]/page.tsx
diff --git a/app/(app)/letters/filter-year-browser.tsx b/app/(app)/(reader)/letters/filter-year-browser.tsx
similarity index 100%
rename from app/(app)/letters/filter-year-browser.tsx
rename to app/(app)/(reader)/letters/filter-year-browser.tsx
diff --git a/app/(app)/letters/letters-browser.tsx b/app/(app)/(reader)/letters/letters-browser.tsx
similarity index 100%
rename from app/(app)/letters/letters-browser.tsx
rename to app/(app)/(reader)/letters/letters-browser.tsx
diff --git a/app/(app)/letters/letters-nav.tsx b/app/(app)/(reader)/letters/letters-nav.tsx
similarity index 100%
rename from app/(app)/letters/letters-nav.tsx
rename to app/(app)/(reader)/letters/letters-nav.tsx
diff --git a/app/(app)/letters/location/[name]/page.tsx b/app/(app)/(reader)/letters/location/[name]/page.tsx
similarity index 100%
rename from app/(app)/letters/location/[name]/page.tsx
rename to app/(app)/(reader)/letters/location/[name]/page.tsx
diff --git a/app/(app)/letters/page.tsx b/app/(app)/(reader)/letters/page.tsx
similarity index 100%
rename from app/(app)/letters/page.tsx
rename to app/(app)/(reader)/letters/page.tsx
diff --git a/app/(app)/letters/recipient/[name]/page.tsx b/app/(app)/(reader)/letters/recipient/[name]/page.tsx
similarity index 100%
rename from app/(app)/letters/recipient/[name]/page.tsx
rename to app/(app)/(reader)/letters/recipient/[name]/page.tsx
diff --git a/app/(app)/letters/year/[year]/page.tsx b/app/(app)/(reader)/letters/year/[year]/page.tsx
similarity index 100%
rename from app/(app)/letters/year/[year]/page.tsx
rename to app/(app)/(reader)/letters/year/[year]/page.tsx
diff --git a/app/(app)/letters/year/[year]/year-month-browser.tsx b/app/(app)/(reader)/letters/year/[year]/year-month-browser.tsx
similarity index 100%
rename from app/(app)/letters/year/[year]/year-month-browser.tsx
rename to app/(app)/(reader)/letters/year/[year]/year-month-browser.tsx
diff --git a/app/(app)/magazines/[slug]/[issue]/page.tsx b/app/(app)/(reader)/magazines/[slug]/[issue]/page.tsx
similarity index 100%
rename from app/(app)/magazines/[slug]/[issue]/page.tsx
rename to app/(app)/(reader)/magazines/[slug]/[issue]/page.tsx
diff --git a/app/(app)/magazines/[slug]/page.tsx b/app/(app)/(reader)/magazines/[slug]/page.tsx
similarity index 100%
rename from app/(app)/magazines/[slug]/page.tsx
rename to app/(app)/(reader)/magazines/[slug]/page.tsx
diff --git a/app/(app)/magazines/page.tsx b/app/(app)/(reader)/magazines/page.tsx
similarity index 100%
rename from app/(app)/magazines/page.tsx
rename to app/(app)/(reader)/magazines/page.tsx
diff --git a/app/(app)/other-documents/[slug]/[chapter]/page.tsx b/app/(app)/(reader)/other-documents/[slug]/[chapter]/page.tsx
similarity index 100%
rename from app/(app)/other-documents/[slug]/[chapter]/page.tsx
rename to app/(app)/(reader)/other-documents/[slug]/[chapter]/page.tsx
diff --git a/app/(app)/other-documents/[slug]/page.tsx b/app/(app)/(reader)/other-documents/[slug]/page.tsx
similarity index 100%
rename from app/(app)/other-documents/[slug]/page.tsx
rename to app/(app)/(reader)/other-documents/[slug]/page.tsx
diff --git a/app/(app)/other-documents/other-documents-grid.tsx b/app/(app)/(reader)/other-documents/other-documents-grid.tsx
similarity index 100%
rename from app/(app)/other-documents/other-documents-grid.tsx
rename to app/(app)/(reader)/other-documents/other-documents-grid.tsx
diff --git a/app/(app)/other-documents/page.tsx b/app/(app)/(reader)/other-documents/page.tsx
similarity index 100%
rename from app/(app)/other-documents/page.tsx
rename to app/(app)/(reader)/other-documents/page.tsx
diff --git a/app/(app)/transcriptions/[slug]/page.tsx b/app/(app)/(reader)/transcriptions/[slug]/page.tsx
similarity index 100%
rename from app/(app)/transcriptions/[slug]/page.tsx
rename to app/(app)/(reader)/transcriptions/[slug]/page.tsx
diff --git a/app/(app)/transcriptions/[slug]/play-audio-button.tsx b/app/(app)/(reader)/transcriptions/[slug]/play-audio-button.tsx
similarity index 100%
rename from app/(app)/transcriptions/[slug]/play-audio-button.tsx
rename to app/(app)/(reader)/transcriptions/[slug]/play-audio-button.tsx
diff --git a/app/(app)/transcriptions/book/[book]/book-browser.tsx b/app/(app)/(reader)/transcriptions/book/[book]/book-browser.tsx
similarity index 99%
rename from app/(app)/transcriptions/book/[book]/book-browser.tsx
rename to app/(app)/(reader)/transcriptions/book/[book]/book-browser.tsx
index 1c1e61b3..3a8fbb4f 100644
--- a/app/(app)/transcriptions/book/[book]/book-browser.tsx
+++ b/app/(app)/(reader)/transcriptions/book/[book]/book-browser.tsx
@@ -8,7 +8,7 @@ import { Badge } from "@/components/ui/badge";
import { buttonVariants } from "@/components/ui/button";
import { cn } from "@/lib/utils";
import { ChevronLeft, ChevronRight, Menu, Calendar, Clock, MapPin } from "lucide-react";
-import { PlayAudioButton } from "@/app/(app)/transcriptions/[slug]/play-audio-button";
+import { PlayAudioButton } from "@/app/(app)/(reader)/transcriptions/[slug]/play-audio-button";
import { parseChapter, parseVerse } from "@/lib/book-reference";
import {
Drawer,
diff --git a/app/(app)/transcriptions/book/[book]/page.tsx b/app/(app)/(reader)/transcriptions/book/[book]/page.tsx
similarity index 100%
rename from app/(app)/transcriptions/book/[book]/page.tsx
rename to app/(app)/(reader)/transcriptions/book/[book]/page.tsx
diff --git a/app/(app)/transcriptions/page.tsx b/app/(app)/(reader)/transcriptions/page.tsx
similarity index 100%
rename from app/(app)/transcriptions/page.tsx
rename to app/(app)/(reader)/transcriptions/page.tsx
diff --git a/app/(app)/transcriptions/place/[place]/page.tsx b/app/(app)/(reader)/transcriptions/place/[place]/page.tsx
similarity index 97%
rename from app/(app)/transcriptions/place/[place]/page.tsx
rename to app/(app)/(reader)/transcriptions/place/[place]/page.tsx
index f9624532..e9e5ee0e 100644
--- a/app/(app)/transcriptions/place/[place]/page.tsx
+++ b/app/(app)/(reader)/transcriptions/place/[place]/page.tsx
@@ -9,7 +9,7 @@ import { PageHeader } from "@/components/layout/page-header";
import { Card, CardContent } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { Calendar, Clock } from "lucide-react";
-import { PlayAudioButton } from "@/app/(app)/transcriptions/[slug]/play-audio-button";
+import { PlayAudioButton } from "@/app/(app)/(reader)/transcriptions/[slug]/play-audio-button";
export async function generateMetadata({
params,
diff --git a/app/(app)/transcriptions/transcriptions-browser.tsx b/app/(app)/(reader)/transcriptions/transcriptions-browser.tsx
similarity index 100%
rename from app/(app)/transcriptions/transcriptions-browser.tsx
rename to app/(app)/(reader)/transcriptions/transcriptions-browser.tsx
diff --git a/app/(app)/transcriptions/transcriptions-nav.tsx b/app/(app)/(reader)/transcriptions/transcriptions-nav.tsx
similarity index 100%
rename from app/(app)/transcriptions/transcriptions-nav.tsx
rename to app/(app)/(reader)/transcriptions/transcriptions-nav.tsx
diff --git a/app/(app)/transcriptions/type/[type]/page.tsx b/app/(app)/(reader)/transcriptions/type/[type]/page.tsx
similarity index 100%
rename from app/(app)/transcriptions/type/[type]/page.tsx
rename to app/(app)/(reader)/transcriptions/type/[type]/page.tsx
diff --git a/app/(app)/transcriptions/type/[type]/type-browser.tsx b/app/(app)/(reader)/transcriptions/type/[type]/type-browser.tsx
similarity index 99%
rename from app/(app)/transcriptions/type/[type]/type-browser.tsx
rename to app/(app)/(reader)/transcriptions/type/[type]/type-browser.tsx
index 1288a920..c3fa265b 100644
--- a/app/(app)/transcriptions/type/[type]/type-browser.tsx
+++ b/app/(app)/(reader)/transcriptions/type/[type]/type-browser.tsx
@@ -14,7 +14,7 @@ import {
Clock,
MapPin,
} from "lucide-react";
-import { PlayAudioButton } from "@/app/(app)/transcriptions/[slug]/play-audio-button";
+import { PlayAudioButton } from "@/app/(app)/(reader)/transcriptions/[slug]/play-audio-button";
import {
Drawer,
DrawerClose,
diff --git a/app/(app)/transcriptions/year/[year]/page.tsx b/app/(app)/(reader)/transcriptions/year/[year]/page.tsx
similarity index 100%
rename from app/(app)/transcriptions/year/[year]/page.tsx
rename to app/(app)/(reader)/transcriptions/year/[year]/page.tsx
diff --git a/app/(app)/transcriptions/year/[year]/year-browser.tsx b/app/(app)/(reader)/transcriptions/year/[year]/year-browser.tsx
similarity index 98%
rename from app/(app)/transcriptions/year/[year]/year-browser.tsx
rename to app/(app)/(reader)/transcriptions/year/[year]/year-browser.tsx
index ab9d39a6..11f441e2 100644
--- a/app/(app)/transcriptions/year/[year]/year-browser.tsx
+++ b/app/(app)/(reader)/transcriptions/year/[year]/year-browser.tsx
@@ -7,7 +7,7 @@ import { Card, CardContent } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { cn } from "@/lib/utils";
import { Calendar, ChevronRight, Clock, MapPin } from "lucide-react";
-import { PlayAudioButton } from "@/app/(app)/transcriptions/[slug]/play-audio-button";
+import { PlayAudioButton } from "@/app/(app)/(reader)/transcriptions/[slug]/play-audio-button";
type TranscriptionItem = {
slug: string;
diff --git a/app/(app)/layout.tsx b/app/(app)/layout.tsx
index d00777fa..5bdc4a95 100644
--- a/app/(app)/layout.tsx
+++ b/app/(app)/layout.tsx
@@ -1,12 +1,8 @@
-import { Suspense } from "react";
import { SiteHeader } from "@/components/layout/site-header";
import { SiteSidebar } from "@/components/layout/site-sidebar";
import { SiteBottomNav } from "@/components/layout/site-bottom-nav";
import { SiteFooter } from "@/components/layout/site-footer";
import { VerifyEmailBanner } from "@/components/auth/verify-email-banner";
-import { TextSelectionToolbar } from "@/components/text-selection-toolbar";
-import { TextHighlighter } from "@/components/text-highlighter";
-import { PageBookmarks } from "@/components/social/page-bookmarks";
import { PushPrompt } from "@/components/push/push-prompt";
/**
@@ -17,6 +13,11 @@ import { PushPrompt } from "@/components/push/push-prompt";
* Sibling groups (e.g. `(legal)/`) opt out of this chrome by living in their
* own route group with their own layout. Everything in the root layout
* (providers, fonts, toaster, floating audio player) still wraps this layer.
+ *
+ * The text-selection toolbar and its highlight/bookmark companions live in
+ * the nested `(reader)/` group instead of here, so they only mount over the
+ * scripture library and never fire on chrome text (home, search, profile,
+ * settings, …).
*/
export default function AppLayout({
children,
@@ -33,11 +34,6 @@ export default function AppLayout({
-
-
-
-
-
>
);
diff --git a/app/(app)/settings/account/page.tsx b/app/(app)/settings/account/page.tsx
new file mode 100644
index 00000000..0305ba10
--- /dev/null
+++ b/app/(app)/settings/account/page.tsx
@@ -0,0 +1,24 @@
+/**
+ * /settings/account — connected accounts, email, and password.
+ *
+ * Dedicated page for what used to be the "Authentication" section on the old
+ * /settings screen. All the interactive bits (OAuth link flows, the post-link
+ * result banner, add-email / change-password modals) live in the client
+ * AuthenticationSection component.
+ */
+
+import { AuthenticationSection } from "@/components/settings/authentication-section";
+import { SettingsShell } from "@/components/settings/settings-ui";
+
+export default function AccountSettingsPage() {
+ return (
+
+
+
+ );
+}
diff --git a/app/(app)/settings/legal/page.tsx b/app/(app)/settings/legal/page.tsx
new file mode 100644
index 00000000..a482e18b
--- /dev/null
+++ b/app/(app)/settings/legal/page.tsx
@@ -0,0 +1,25 @@
+/**
+ * /settings/legal — links to the standalone legal documents.
+ *
+ * Dedicated page for what used to be the "Legal" section on the old /settings
+ * screen. Each row hard-links to its own top-level route (/terms, /privacy,
+ * /data-deletion).
+ */
+
+import { NavList, NavRow, SettingsShell } from "@/components/settings/settings-ui";
+
+export default function LegalSettingsPage() {
+ return (
+
+
+
+
+
+
+
+ );
+}
diff --git a/app/(app)/settings/notifications/page.tsx b/app/(app)/settings/notifications/page.tsx
new file mode 100644
index 00000000..32fccb18
--- /dev/null
+++ b/app/(app)/settings/notifications/page.tsx
@@ -0,0 +1,26 @@
+/**
+ * /settings/notifications — push opt-in + per-channel notification matrix.
+ *
+ * Dedicated page for what used to be the "Notifications" section on the old
+ * /settings screen. Push goes to every device where the toggle is on.
+ */
+
+import { PushToggle } from "@/components/push/push-toggle";
+import { NotificationMatrix } from "@/components/notifications/notification-matrix";
+import { SettingsShell } from "@/components/settings/settings-ui";
+
+export default function NotificationSettingsPage() {
+ return (
+
+
+
+
+
+
+ );
+}
diff --git a/app/(app)/settings/page.tsx b/app/(app)/settings/page.tsx
index 7bc27ba5..511b9fe6 100644
--- a/app/(app)/settings/page.tsx
+++ b/app/(app)/settings/page.tsx
@@ -1,99 +1,85 @@
"use client";
/**
- * /settings — the top-level user-settings screen.
+ * /settings — the settings hub.
*
- * Lives outside the /u/me profile layout so it doesn't carry the
- * ProfileHeader + tabs that frame the profile views. It sits directly
- * under (app)/, which gives it the default site chrome (sidebar, header,
- * etc.) without the profile-specific frame on top.
+ * A plain entry menu: one tappable row per setting type, each routing to its
+ * own dedicated page. This keeps the top level uncluttered — the actual
+ * controls live on the sub-pages (`/settings/reading`, `/settings/notifications`,
+ * `/settings/account`, `/settings/legal`) plus the existing
+ * `/settings/profile` editor. The "Offline" row is a direct shortcut to the
+ * device-local download library at `/downloads` (no intermediate page).
*
- * Sections are stacked plain — no per-section card chrome — so the page
- * reads as a single ordered list of preferences rather than a dashboard
- * of widgets. To add a new section, drop another in the
- * `` below.
+ * Sign out is an action rather than a destination, so it sits on its own
+ * below the menu.
*/
-import { Suspense, useEffect, useState } from "react";
-import Link from "next/link";
-import { useRouter, useSearchParams } from "next/navigation";
+import { useState } from "react";
+import { useRouter } from "next/navigation";
import {
- AlertCircle,
- Check,
- ChevronLeft,
- ChevronRight,
+ Bell,
+ BookOpenText,
+ DownloadCloud,
LogOut,
+ Scale,
+ ShieldCheck,
+ UserRound,
} from "lucide-react";
-import { auth, AuthError } from "@/lib/auth";
import { useAuth } from "@/components/auth/auth-provider";
-import { AddEmailModal } from "@/components/auth/add-email-modal";
-import { ChangePasswordModal } from "@/components/auth/change-password-modal";
-import { ReadingSettings } from "@/components/reading-settings";
-import { PushToggle } from "@/components/push/push-toggle";
-import { NotificationMatrix } from "@/components/notifications/notification-matrix";
-
-const BFF_BASE =
- process.env.NEXT_PUBLIC_BFF_URL || "http://localhost:8080";
+import {
+ NavList,
+ NavRow,
+ SettingsShell,
+} from "@/components/settings/settings-ui";
export default function SettingsPage() {
return (
-
-
-
-
- Back to profile
-
-
- Settings
-
-
+
+
+ }
+ label="Profile"
+ description="Name, handle, bio, and avatar."
+ />
+ }
+ label="Reading preferences"
+ description="Theme, palette, font, and size — synced across devices."
+ />
+ }
+ label="Notifications"
+ description="What reaches you, and on which channels."
+ />
+ }
+ label="Offline downloads"
+ description="Manage pages and lectures saved to this device."
+ />
+ }
+ label="Account & security"
+ description="Connected accounts, email, and password."
+ />
+ }
+ label="Legal"
+ description="Terms, privacy, and data deletion."
+ />
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {/* Suspense boundary required because LinkResult reads
- useSearchParams — Next.js bails out of SSR otherwise. */}
-
-
-
-
-
-
-
-
-
-
-
-
+
+
);
}
-function SignOutSection() {
+function SignOut() {
const { logOut } = useAuth();
const router = useRouter();
const [busy, setBusy] = useState(false);
@@ -111,372 +97,17 @@ function SignOutSection() {
}
}
- // Renders as a full-width clickable row matching LegalLink — same
- // padding, same hover, with LogOut taking the slot ChevronRight uses
- // on link rows. The whole row is the click target.
return (
-
+
+ {busy ? "Signing out…" : "Sign out from this device"}
+
+
+
);
}
diff --git a/app/(app)/settings/reading/page.tsx b/app/(app)/settings/reading/page.tsx
new file mode 100644
index 00000000..9fd8b9e4
--- /dev/null
+++ b/app/(app)/settings/reading/page.tsx
@@ -0,0 +1,23 @@
+/**
+ * /settings/reading — theme, palette, font, and size.
+ *
+ * A dedicated page for the reading preferences that used to be a section on
+ * the old monolithic /settings screen. ReadingSettings ("bare" variant)
+ * owns all the controls and their sync to the server.
+ */
+
+import { ReadingSettings } from "@/components/reading-settings";
+import { SettingsShell } from "@/components/settings/settings-ui";
+
+export default function ReadingSettingsPage() {
+ return (
+
+
+
+ );
+}
diff --git a/components/settings/authentication-section.tsx b/components/settings/authentication-section.tsx
new file mode 100644
index 00000000..0e55f683
--- /dev/null
+++ b/components/settings/authentication-section.tsx
@@ -0,0 +1,293 @@
+"use client";
+
+/**
+ * AuthenticationSection — the connect-accounts / email / password controls
+ * for the /settings/account page.
+ *
+ * Split out of the old monolithic /settings page so account & security lives
+ * on its own dedicated screen. OAuth link flows return to /settings/account
+ * (via `next`), and the post-link banner (LinkResult) reads/clears the
+ * `?link=` param there.
+ */
+
+import { Suspense, useEffect, useState } from "react";
+import { useRouter, useSearchParams } from "next/navigation";
+import { AlertCircle, Check } from "lucide-react";
+import { auth, AuthError } from "@/lib/auth";
+import { useAuth } from "@/components/auth/auth-provider";
+import { AddEmailModal } from "@/components/auth/add-email-modal";
+import { ChangePasswordModal } from "@/components/auth/change-password-modal";
+import { Row } from "@/components/settings/settings-ui";
+
+const BFF_BASE = process.env.NEXT_PUBLIC_BFF_URL || "http://localhost:8080";
+
+/** Where OAuth link flows and the result banner return to. */
+const RETURN_TO = "/settings/account";
+
+export function AuthenticationSection() {
+ const { user } = useAuth();
+ const [pwModalOpen, setPwModalOpen] = useState(false);
+ const [emailModalOpen, setEmailModalOpen] = useState(false);
+ const [linkedProviders, setLinkedProviders] = useState | null>(
+ null,
+ );
+
+ // Fetch linked providers on mount. A null set means "not yet loaded" — we
+ // render the rows as if nothing is connected until the answer comes back,
+ // which is fine because the only thing that changes after load is the
+ // "Connected" pill swapping in for "Connect".
+ useEffect(() => {
+ let cancelled = false;
+ auth
+ .linkedProviders()
+ .then(({ providers }) => {
+ if (!cancelled) setLinkedProviders(new Set(providers));
+ })
+ .catch(() => {
+ if (!cancelled) setLinkedProviders(new Set());
+ });
+ return () => {
+ cancelled = true;
+ };
+ }, []);
+
+ function startOAuth(provider: "facebook" | "google") {
+ // /link/start — links the provider to the currently-authenticated
+ // user (auth cookie travels on the top-level navigation). The login
+ // page still uses /start for sign-in / sign-up.
+ const u = new URL(`${BFF_BASE}/api/v1/auth/oauth/${provider}/link/start`);
+ u.searchParams.set("next", RETURN_TO);
+ return u.toString();
+ }
+
+ function providerAction(provider: "google" | "facebook") {
+ if (linkedProviders?.has(provider)) {
+ return ;
+ }
+ return (
+
+ Connect
+
+ );
+ }
+
+ // Three contextual states for the email-related rows:
+ // • user has no email → "Add your email" row only
+ // • user has email, NOT verified → "Verify your email" row +
+ // "Change your password" row
+ // • user has email, verified → "Change your password" row
+ const needsEmail = !user?.email;
+ const needsVerify = !!user?.email && !user.email_verified_at;
+
+ return (
+ <>
+ {/* Suspense boundary required because LinkResult reads
+ useSearchParams — Next.js bails out of SSR otherwise. */}
+
+
+
+