Page view tracking and ranking update
Page view tracking - new page_views table records anonymous per-entity views for characters, books, series, universes, and groups. A lightweight usePageView hook fires a single POST /api/views per entity per browser session (sessionStorage dedup). The API endpoint is public and unauthenticated, IP-rate-limited at 60/min, and uses Next.js built-in user-agent parsing to tag bot visits (is_bot = true) without dropping them - bot-filtering happens at query time, not at insert time. The endpoint returns 204 always, even on DB errors, so tracking never blocks the page.
Views are not attributed to individual users. No user_id column exists on the table.
Local dev filtering - the usePageView hook skips the fetch entirely when NODE_ENV === 'development', so local browsing during development never pollutes production rankings. Since Next.js inlines process.env.NODE_ENV at build time, the entire tracking path is dead-code-eliminated in dev bundles.
Incomplete-characters ranking update - the appearance_totals CTE and appearanceCount field have been fully removed from the incomplete-characters query. In their place, a view_counts CTE aggregates non-bot character page views from the last 30 days. The sort order is now filled ASC, view_count_30d DESC, name ASC - emptiest pages first, with the most-visited characters surfacing higher among equally-incomplete pages. Characters with zero views rank below those with any traffic but still above more-complete characters.
Book description spoiler audit - 62 books across 28 series flagged with the new description_has_spoilers flag. Covers all Wheel of Time #2-14, all Dune sequels, Mistborn Era 1 #2-3 and all Era 2, Harry Potter #4-7, Lord of the Rings #2-3, all Fitz and the Fool, all Stormlight Archive from Oathbringer onward, all A Song of Ice and Fire sequels, and other late-arc volumes across Farseer, First Law, His Dark Materials, Dark Tower, Expanse, Heroes of Olympus, Liveship Traders, Shadow and Bone, Sprawl Trilogy, Age of Madness, and more. Readers who haven't finished a flagged book see a blurred description with a "Show description" reveal button.
Pruning backlog - page_views table has no automatic pruning yet. The ranking query only reads the last 30 days, so older rows are dead weight. A pruning job is noted in the backlog under Performance and Code Quality in docs/features.md, to be revisited once real traffic indicates row volume.