diff --git a/__tests__/lib/reading-preferences.test.ts b/__tests__/lib/reading-preferences.test.ts
new file mode 100644
index 0000000..9a4146f
--- /dev/null
+++ b/__tests__/lib/reading-preferences.test.ts
@@ -0,0 +1,16 @@
+import { getCSSVariables, getPreset } from '@/lib/reading-preferences'
+
+describe('reading-preferences', () => {
+ it('returns default preset', () => {
+ const preset = getPreset('default')
+ expect(preset.fontFamily).toBe('georgia')
+ expect(preset.fontSize).toBe(18)
+ })
+
+ it('generates CSS variables correctly', () => {
+ const preset = getPreset('dyslexia')
+ const vars = getCSSVariables(preset)
+ expect(vars['--font-size']).toBe('18px')
+ expect(vars['--letter-spacing']).toBe('0.08em')
+ })
+})
diff --git a/components/bible/reading-view.tsx b/components/bible/reading-view.tsx
new file mode 100644
index 0000000..ef48268
--- /dev/null
+++ b/components/bible/reading-view.tsx
@@ -0,0 +1,176 @@
+'use client'
+import { useState, useEffect, CSSProperties } from 'react'
+import { Box, Typography, IconButton, Paper, useMediaQuery, useTheme } from '@mui/material'
+import { NavigateBefore, NavigateNext, Settings as SettingsIcon } from '@mui/icons-material'
+import { BibleChapter } from '@/types'
+import { getCSSVariables, loadPreferences } from '@/lib/reading-preferences'
+
+interface ReadingViewProps {
+ chapter: BibleChapter
+ loading: boolean
+ onPrevChapter: () => void
+ onNextChapter: () => void
+ onVerseClick: (verseId: string) => void
+ onSettingsOpen: () => void
+ hasPrevChapter: boolean
+ hasNextChapter: boolean
+}
+
+export function ReadingView({
+ chapter,
+ loading,
+ onPrevChapter,
+ onNextChapter,
+ onVerseClick,
+ onSettingsOpen,
+ hasPrevChapter,
+ hasNextChapter,
+}: ReadingViewProps) {
+ const theme = useTheme()
+ const isMobile = useMediaQuery(theme.breakpoints.down('sm'))
+ const isTablet = useMediaQuery(theme.breakpoints.down('md'))
+ const [preferences, setPreferences] = useState(loadPreferences())
+ const [showControls, setShowControls] = useState(!isMobile)
+
+ useEffect(() => {
+ setPreferences(loadPreferences())
+ }, [])
+
+ const cssVars = getCSSVariables(preferences)
+
+ if (loading) {
+ return (
+
+ Loading chapter...
+
+ )
+ }
+
+ return (
+ {
+ if (isMobile) {
+ const rect = e.currentTarget.getBoundingClientRect()
+ const y = e.clientY - rect.top
+ if (y < rect.height * 0.3) {
+ setShowControls(true)
+ } else if (y > rect.height * 0.7) {
+ setShowControls(!showControls)
+ } else {
+ setShowControls(false)
+ }
+ }
+ }}
+ >
+ {/* Header */}
+ {(showControls || !isMobile) && (
+
+
+ {chapter.bookName} {chapter.chapter}
+
+
+ )}
+
+ {/* Main Text Area */}
+
+ {chapter.verses.map((verse) => (
+ {
+ e.stopPropagation()
+ onVerseClick(verse.id)
+ }}
+ style={{
+ cursor: 'pointer',
+ transition: 'background-color 0.15s',
+ }}
+ onMouseEnter={(e) => {
+ e.currentTarget.style.backgroundColor = 'rgba(255, 193, 7, 0.3)'
+ }}
+ onMouseLeave={(e) => {
+ e.currentTarget.style.backgroundColor = 'transparent'
+ }}
+ >
+
+ {verse.verseNum}
+
+ {verse.text}{' '}
+
+ ))}
+
+
+ {/* Navigation Footer */}
+ {(showControls || !isMobile) && (
+
+
+
+
+
+
+ Chapter {chapter.chapter}
+
+
+
+
+
+
+
+
+
+
+ )}
+
+ )
+}
diff --git a/lib/reading-preferences.ts b/lib/reading-preferences.ts
new file mode 100644
index 0000000..a6ad0f3
--- /dev/null
+++ b/lib/reading-preferences.ts
@@ -0,0 +1,108 @@
+import { ReadingPreference } from '@/types'
+
+const PRESETS: Record = {
+ default: {
+ fontFamily: 'georgia',
+ fontSize: 18,
+ lineHeight: 1.8,
+ letterSpacing: 0,
+ textAlign: 'left',
+ backgroundColor: '#faf8f3',
+ textColor: '#333333',
+ margin: 'normal',
+ preset: 'default'
+ },
+ dyslexia: {
+ fontFamily: 'atkinson',
+ fontSize: 18,
+ lineHeight: 1.9,
+ letterSpacing: 0.08,
+ textAlign: 'left',
+ backgroundColor: '#f5f5dc',
+ textColor: '#333333',
+ margin: 'normal',
+ preset: 'dyslexia'
+ },
+ highContrast: {
+ fontFamily: 'inter',
+ fontSize: 16,
+ lineHeight: 1.6,
+ letterSpacing: 0,
+ textAlign: 'left',
+ backgroundColor: '#000000',
+ textColor: '#ffffff',
+ margin: 'wide',
+ preset: 'highContrast'
+ },
+ minimal: {
+ fontFamily: 'georgia',
+ fontSize: 16,
+ lineHeight: 1.6,
+ letterSpacing: 0,
+ textAlign: 'left',
+ backgroundColor: '#ffffff',
+ textColor: '#000000',
+ margin: 'narrow',
+ preset: 'minimal'
+ }
+}
+
+const STORAGE_KEY = 'bibleReaderPreferences'
+
+export function getPreset(name: keyof typeof PRESETS): ReadingPreference {
+ return PRESETS[name]
+}
+
+export function loadPreferences(): ReadingPreference {
+ if (typeof window === 'undefined') {
+ return PRESETS.default
+ }
+
+ try {
+ const stored = localStorage.getItem(STORAGE_KEY)
+ return stored ? JSON.parse(stored) : PRESETS.default
+ } catch {
+ return PRESETS.default
+ }
+}
+
+export function savePreferences(prefs: ReadingPreference): void {
+ if (typeof window === 'undefined') return
+
+ try {
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(prefs))
+ } catch (e) {
+ console.error('Failed to save preferences:', e)
+ }
+}
+
+export function getCSSVariables(prefs: ReadingPreference): Record {
+ return {
+ '--font-family': getFontStack(prefs.fontFamily),
+ '--font-size': `${prefs.fontSize}px`,
+ '--line-height': `${prefs.lineHeight}`,
+ '--letter-spacing': `${prefs.letterSpacing}em`,
+ '--bg-color': prefs.backgroundColor,
+ '--text-color': prefs.textColor,
+ '--margin-width': getMarginWidth(prefs.margin),
+ }
+}
+
+function getFontStack(fontFamily: string): string {
+ const stacks: Record = {
+ georgia: 'Georgia, serif',
+ inter: 'Inter, -apple-system, BlinkMacSystemFont, sans-serif',
+ atkinson: '"Atkinson Hyperlegible", sans-serif',
+ merriweather: '"Merriweather", serif',
+ }
+ return stacks[fontFamily] || stacks.georgia
+}
+
+function getMarginWidth(margin: string): string {
+ const margins: Record = {
+ narrow: 'max(1rem, 5%)',
+ normal: 'max(2rem, 10%)',
+ wide: 'max(4rem, 15%)',
+ }
+ return margins[margin] || margins.normal
+}