diff --git a/FRONTEND_IMPLEMENTATION_PLAN_COMIC_METADATA.md b/FRONTEND_IMPLEMENTATION_PLAN_COMIC_METADATA.md
new file mode 100644
index 0000000..8f159fc
--- /dev/null
+++ b/FRONTEND_IMPLEMENTATION_PLAN_COMIC_METADATA.md
@@ -0,0 +1,510 @@
+# Frontend Implementation Plan: Add Comic Metadata Fields to Book Detail Page
+
+**Status:** Planning Document (Not Implemented)
+**Date:** March 30, 2026
+**Scope:** Display new comic/manga metadata fields on the book detail page
+
+---
+
+## Overview
+
+This document outlines how to add the new ComicInfo.xml metadata fields to the book detail page frontend. These fields are already added to the database schema and backend models (per `IMPLEMENTATION_PLAN_MERGE_METADATA_READING_DIRECTION.md`).
+
+## New Fields to Display
+
+### Universal Fields (apply to all formats: ebooks, audiobooks, comics)
+- `series_count` - Total items in series
+- `volume` - Volume/omnibus number
+- `imprint` - Publisher imprint (e.g., Vertigo, HarperCollinsEpic)
+- `age_rating` - Age rating: Everyone, Teen, Mature, Adult
+- `web_url` - URL to info page (Goodreads, ComicVine, MangaUpdates, etc.)
+- `metadata_notes` - Notes from metadata files (ComicInfo.xml, EPUB, PDF)
+- `community_rating` - Pre-existing community rating (0.0-10.0) - **distinct from user ratings**
+
+### Comic-Specific Fields
+- `manga_type` - Raw Manga field: unknown, no, yes, yes_and_right_to_left
+- `reading_direction` - Computed reading direction: auto, ltr, rtl, vertical
+- `story_arc` - Story arc name (e.g., "The Dark Phoenix Saga", "Civil War")
+- `is_black_and_white` - Black and white flag
+- `alternate_info` - JSONB: {alternate_series, alternate_number, alternate_count}
+- `scan_information` - Scan information (scanner group, resolution, etc.)
+- `summary` - Summary from ComicInfo.xml (may merge with description)
+
+---
+
+## Current Book Detail Page Structure
+
+**File:** `templates/book_detail.templ`
+
+### Current Layout Sections:
+
+1. **Top Section** (lines 23-130)
+ - Cover image (left)
+ - Title, author, action buttons (right)
+ - Rating display (user ratings)
+ - Series badge
+ - Description/Synopsis
+
+2. **Progress Section** (lines 132-185)
+ - Reading progress bar
+ - Progress stats grid
+
+3. **Metadata Grid** (lines 186-253)
+ - Publication info (Publisher, Published, ISBN, Language, Edition, Pages, Genre, Copyright Year)
+ - Technical info (Format, File Size)
+ - External links section
+
+4. **Collections Section** (lines 347-367)
+
+---
+
+## Implementation Plan
+
+### Step 1: Add Reading Direction Badge (Display Priority: HIGH)
+
+**Location:** After Series Badge (line 117)
+
+**Rationale:** Reading direction is critical for manga/comic readers. Display prominently like the series badge.
+
+```templ
+
+if book.ReadingDirection.Valid && book.ReadingDirection.String != "" && book.ReadingDirection.String != "auto" {
+
+
+ 📖 { strings.ToUpper(book.ReadingDirection.String) }
+
+
+}
+```
+
+**Styling Notes:**
+- Use `var(--accent)` for consistency with series badge
+- Only show if not "auto" (i.e., explicitly set to ltr, rtl, or vertical)
+- Icon: 📖 for visual clarity
+
+---
+
+### Step 2: Add Community Rating Display (Display Priority: HIGH)
+
+**Location:** After User Rating Display (line 103)
+
+**Rationale:** Community rating complements user rating. Show side-by-side for comparison.
+
+```templ
+
+if book.CommunityRating.Valid && book.CommunityRating.Float64 > 0 {
+
+
+ Community Rating:
+
+ @templ.Raw(renderStars(getBookRating(int(book.CommunityRating.Float64 * 2))))
+
+
+ ({ fmt.Sprintf("%.1f", book.CommunityRating.Float64) } / 10)
+
+
+
+}
+```
+
+**Styling Notes:**
+- Smaller text than user rating (text-lg vs text-2xl)
+- Gray/subtle color to distinguish from user rating
+- Scale: Community rating is 0-10, user rating display is 0-5 stars
+ - Conversion: `community_rating * 2` to get 0-10 scale for star display
+
+---
+
+### Step 3: Add Comic-Specific Badges (Display Priority: MEDIUM)
+
+**Location:** After Reading Direction Badge (line 117)
+
+**Rationale:** Group all manga/comic-specific badges together for easy scanning.
+
+```templ
+
+
+
+ if book.AgeRating.Valid && book.AgeRating.String != "" {
+
+ { book.AgeRating.String }
+
+ }
+
+
+ if book.IsBlackAndWhite.Valid && book.IsBlackAndWhite.Bool {
+
+ B&W
+
+ }
+
+
+ if book.StoryArc.Valid && book.StoryArc.String != "" {
+
+ 📚 { book.StoryArc.String }
+
+ }
+
+```
+
+**Styling Notes:**
+- Use subtle styling (smaller, outlined) to differentiate from primary badges
+- Flex wrap for responsive layout
+- Icons: 📚 for story arc
+
+---
+
+### Step 4: Add Universal Series Info to Metadata Grid (Display Priority: HIGH)
+
+**Location:** In Metadata Grid section, after Series/Number fields
+
+**Note:** The current template doesn't display Series/Number in the metadata grid (only as badge). We'll add all series-related fields together.
+
+**Insertion Point:** After Genre field (line 235)
+
+```templ
+
+if book.SeriesCount.Valid && book.SeriesCount.Int32 > 0 {
+
+
Series Count
+
{ book.SeriesCount.Int32 } items
+
+}
+if book.Volume.Valid && book.Volume.Int32 > 0 {
+
+
Volume
+
Vol. { book.Volume.Int32 }
+
+}
+if book.Imprint.Valid && book.Imprint.String != "" {
+
+
Imprint
+
{ book.Imprint.String }
+
+}
+```
+
+---
+
+### Step 5: Add Comic-Specific Metadata to Grid (Display Priority: MEDIUM)
+
+**Location:** In Metadata Grid section, after Copyright Year (line 241)
+
+```templ
+
+if book.MangaType.Valid && book.MangaType.String != "" && book.MangaType.String != "unknown" {
+
+
Manga Type
+
{ strings.ReplaceAll(book.MangaType.String, "_", " ") }
+
+}
+if book.ScanInformation.Valid && book.ScanInformation.String != "" {
+
+
Scan Info
+
{ book.ScanInformation.String }
+
+}
+if book.AlternateInfo.Valid && len(book.AlternateInfo.Bytes) > 0 {
+
+
Alternate Series
+
{ string(book.AlternateInfo.Bytes) }
+
+}
+```
+
+**Note for AlternateInfo:** This is JSONB data stored as `[]byte`. You may want to:
+1. Parse the JSON and display formatted
+2. Or create a helper function to extract specific fields
+
+Helper function example (add to `templates/helpers.go`):
+
+```go
+// getAlternateSeries extracts the alternate series name from JSONB data
+func getAlternateSeries(data pgtype.Bytes) string {
+ if !data.Valid || len(data.Bytes) == 0 {
+ return ""
+ }
+ var result map[string]interface{}
+ if err := json.Unmarshal(data.Bytes, &result); err != nil {
+ return ""
+ }
+ if series, ok := result["alternate_series"].(string); ok {
+ return series
+ }
+ return ""
+}
+```
+
+Then in template:
+```templ
+if altSeries := getAlternateSeries(book.AlternateInfo); altSeries != "" {
+
+
Alternate Series
+
{ altSeries }
+
+}
+```
+
+---
+
+### Step 6: Add Summary Section (Display Priority: MEDIUM)
+
+**Location:** After Description/Synopsis section (line 128)
+
+**Rationale:** Summary from ComicInfo.xml may differ from Calibre description. Show both if present.
+
+```templ
+
+if book.Summary.Valid && book.Summary.String != "" && book.Summary.String != book.Description.String {
+
+
Comic Summary
+
+ @UnsafeHTML(
+ bluemonday.UGCPolicy().Sanitize(book.Summary.String),
+ ).ToComponent()
+
+
+}
+```
+
+---
+
+### Step 7: Add Metadata Notes Section (Display Priority: LOW)
+
+**Location:** After Summary section (or at the bottom before Collections)
+
+**Rationale:** Metadata notes are technical/scanner information. Less important for end users.
+
+```templ
+
+if book.MetadataNotes.Valid && book.MetadataNotes.String != "" {
+
+
Metadata Notes
+
+ { book.MetadataNotes.String }
+
+
+}
+```
+
+---
+
+### Step 8: Add Web URL Link (Display Priority: MEDIUM)
+
+**Location:** In External Links section (line 258)
+
+**Rationale:** Add web_url as another external link alongside Goodreads, OpenLibrary, etc.
+
+```templ
+
+if book.WebUrl.Valid && book.WebUrl.String != "" {
+
+ 🔗 { getDomainName(book.WebUrl.String) }
+
+}
+```
+
+Helper function to extract domain name (add to `templates/helpers.go`):
+
+```go
+// getDomainName extracts a clean domain name from URL for display
+func getDomainName(rawURL string) string {
+ if rawURL == "" {
+ return "Source"
+ }
+ u, err := url.Parse(rawURL)
+ if err != nil {
+ return "Source"
+ }
+ // Return hostname without www.
+ hostname := u.Hostname()
+ return strings.TrimPrefix(hostname, "www.")
+}
+```
+
+---
+
+## TypeScript Updates (No Changes Required)
+
+**File:** `web/src/book-detail.ts`
+
+**Status:** No changes needed. This file only handles Alpine.js modal visibility functions.
+
+**Note:** The book detail page uses SSR (server-side rendering) exclusively. No client-side data fetching or Alpine.js data manipulation is needed for these new fields.
+
+---
+
+## Template Helper Functions Needed
+
+Add these functions to `templates/helpers.go` (or existing helpers file):
+
+```go
+package templates
+
+import (
+ "encoding/json"
+ "net/url"
+ "strings"
+
+ "github.com/jackc/pgx/v5/pgtype"
+)
+
+// getAlternateSeries extracts the alternate series name from JSONB data
+func getAlternateSeries(data pgtype.Bytes) string {
+ if !data.Valid || len(data.Bytes) == 0 {
+ return ""
+ }
+ var result map[string]interface{}
+ if err := json.Unmarshal(data.Bytes, &result); err != nil {
+ return ""
+ }
+ if series, ok := result["alternate_series"].(string); ok {
+ return series
+ }
+ return ""
+}
+
+// getDomainName extracts a clean domain name from URL for display
+func getDomainName(rawURL string) string {
+ if rawURL == "" {
+ return "Source"
+ }
+ u, err := url.Parse(rawURL)
+ if err != nil {
+ return "Source"
+ }
+ hostname := u.Hostname()
+ return strings.TrimPrefix(hostname, "www.")
+}
+
+// formatAlternateInfo formats alternate series info as readable string
+func formatAlternateInfo(data pgtype.Bytes) string {
+ if !data.Valid || len(data.Bytes) == 0 {
+ return ""
+ }
+ var result map[string]interface{}
+ if err := json.Unmarshal(data.Bytes, &result); err != nil {
+ return ""
+ }
+
+ var parts []string
+ if series, ok := result["alternate_series"].(string); ok && series != "" {
+ parts = append(parts, series)
+ }
+ if num, ok := result["alternate_number"].(float64); ok && num > 0 {
+ parts = append(parts, fmt.Sprintf("#%d", int(num)))
+ }
+ if count, ok := result["alternate_count"].(float64); ok && count > 0 {
+ parts = append(parts, fmt.Sprintf("(of %d)", int(count)))
+ }
+
+ return strings.Join(parts, " ")
+}
+```
+
+---
+
+## Implementation Checklist
+
+- [ ] **Step 1:** Add Reading Direction Badge (after line 117)
+- [ ] **Step 2:** Add Community Rating Display (after line 103)
+- [ ] **Step 3:** Add Comic-Specific Badges (after line 117)
+- [ ] **Step 4:** Add Universal Series Info to Metadata Grid (after line 235)
+- [ ] **Step 5:** Add Comic-Specific Metadata to Grid (after line 241)
+- [ ] **Step 6:** Add Summary Section (after line 128)
+- [ ] **Step 7:** Add Metadata Notes Section (before Collections)
+- [ ] **Step 8:** Add Web URL Link (in External Links section)
+- [ ] Add template helper functions to `templates/helpers.go`
+- [ ] Test with manga book (RTL reading direction)
+- [ ] Test with Western comic (LTR reading direction)
+- [ ] Test with webtoon (vertical reading direction)
+- [ ] Test with ebook (no comic metadata)
+- [ ] Verify responsive layout on mobile/desktop
+- [ ] Verify dark/light theme compatibility
+
+---
+
+## Testing Scenarios
+
+### Test Case 1: Japanese Manga
+**Expected Display:**
+- Reading Direction: "RTL" badge
+- Manga Type: "yes_and_right_to_left" in metadata grid
+- Community Rating: displayed alongside user rating
+
+### Test Case 2: Western Comic
+**Expected Display:**
+- Reading Direction: "LTR" badge (or hidden if auto)
+- Age Rating: "Teen" or "Mature" badge
+- Story Arc: displayed if present
+- Publisher/Imprint: displayed in metadata grid
+
+### Test Case 3: Webtoon/Manhwa
+**Expected Display:**
+- Reading Direction: "vertical" badge
+- Language: "ko" (Korean) in metadata grid
+- Format-specific metadata displayed
+
+### Test Case 4: Regular Ebook
+**Expected Display:**
+- No reading direction badge (or "auto" hidden)
+- No comic-specific fields
+- Only universal fields (series count, volume, imprint, age rating) if present
+
+---
+
+## Design Principles Followed
+
+1. **✅ SSR-First:** All data server-side rendered, no client-side fetching
+2. **✅ Progressive Enhancement:** Page works without JavaScript
+3. **✅ TailwindCSS Only:** No custom CSS added
+4. **✅ Existing Patterns:** Follows current badge/grid styling
+5. **✅ Responsive:** Uses existing responsive grid classes
+6. **✅ Theme-Aware:** Uses CSS variables (`var(--accent)`, `var(--text-secondary)`, etc.)
+7. **✅ Accessible:** Semantic HTML, proper contrast
+8. **✅ Conditional Rendering:** Only show fields if they have values
+9. **✅ No Backend Changes:** Frontend-only, uses existing API/handler types
+
+---
+
+## Related Documentation
+
+- `IMPLEMENTATION_PLAN_MERGE_METADATA_READING_DIRECTION.md` - Full backend implementation
+- `PROJECT_GUIDELINES.md` - Project coding standards and conventions
+- `templates/book_detail.templ` - Current book detail template
+- `internal/handlers/media_detail.go` - MediaDetail struct definition
+
+---
+
+## Notes
+
+- **No TypeScript changes needed:** All data is SSR'd from Go handlers
+- **No API changes needed:** MediaDetail already embeds database.MediaItems with new fields
+- **Template helpers:** May need helper functions for JSONB parsing and URL display
+- **Styling consistency:** All new elements use existing CSS variables and Tailwind classes
+- **Conditional display:** Most fields should only display if they have values (check `.Valid`)
+
+---
+
+**End of Document**