Big content update

This commit is contained in:
Greg Kilmer
2026-07-11 20:31:13 -04:00
parent 67f3fd311f
commit e0f41c98a7
76 changed files with 3659 additions and 165 deletions
+107
View File
@@ -0,0 +1,107 @@
---
/**
* MediaCard.astro
* ---------------------------------------------------------------------------
* Card for a media favorite (anime / tv / movies / reading). Mirrors the
* GameCard / InterestCard idiom (`.card card-interactive`): a poster-style art
* slot with a kind-derived icon fallback, the title, an optional status pill,
* an optional year/count detail, and a blurb body.
*
* Driven by the `MediaItem` shape in src/lib/media.ts. Populate the optional
* `image` field via the matching `scripts/fetch-*-art.mjs` tool; until then the
* icon fallback slab keeps the grid looking intentional.
*/
import Icon from '@components/Icon.astro';
import type { MediaKind } from '@lib/media';
interface Props {
title: string;
/** Which medium — drives the fallback poster icon + accent. */
kind: MediaKind;
/** Local public path to poster/cover art, e.g. "/images/anime/demon-slayer.jpg". */
image?: string;
/** Alt text for the art; defaults to the title. */
imageAlt?: string;
/** Optional status pill text, e.g. "Completed", "Watching". */
status?: string;
/** Optional release year shown next to the status pill. */
year?: number;
/** Optional span/count for franchise cards, e.g. "All 4 films". */
count?: string;
/** Long-form blurb shown as the card body. */
blurb?: string;
}
const { title, kind, image, imageAlt, status, year, count, blurb } = Astro.props;
const fallbackIcon: Record<MediaKind, string> = {
anime: 'animation-play',
tv: 'television-classic',
movies: 'movie-open',
reading: 'book-open-page-variant',
};
const alt = imageAlt ?? title;
const detail = [year ? String(year) : null, count].filter(Boolean).join(' \u00b7 ');
---
<article class="card card-interactive flex h-full flex-col gap-0 overflow-hidden p-0">
<div class="relative aspect-[3/4] w-full overflow-hidden">
{
image ? (
<img
src={image}
alt={alt}
loading="lazy"
decoding="async"
class="h-full w-full object-cover"
/>
) : (
<div
class="flex h-full w-full items-center justify-center"
style="background: color-mix(in srgb, var(--color-secondary) 16%, transparent);"
aria-hidden="true"
>
<Icon
name={fallbackIcon[kind]}
class="h-12 w-12 opacity-50"
style="color: var(--color-text-muted);"
/>
</div>
)
}
{/* Favorite star badge, always present on these cards. */}
<span
class="absolute right-2.5 top-2.5 inline-flex h-7 w-7 items-center justify-center rounded-full backdrop-blur-sm"
style="background: color-mix(in srgb, var(--color-surface) 70%, transparent); color: var(--color-accent);"
aria-label="Favorite"
>
<Icon name="star" class="h-4 w-4" />
</span>
</div>
<div class="flex flex-col gap-2 p-4">
<div class="flex items-start justify-between gap-2">
<h3 class="font-sans text-lg leading-tight" style="color: var(--color-text);">
{title}
</h3>
{status && <span class="pill shrink-0">{status}</span>}
</div>
{
detail && (
<span
class="font-mono text-[0.65rem] uppercase tracking-[0.18em]"
style="color: var(--color-text-muted);"
>
{detail}
</span>
)
}
{
blurb && (
<p class="text-sm leading-relaxed" style="color: var(--color-text-muted);">
{blurb}
</p>
)
}
</div>
</article>
+74
View File
@@ -0,0 +1,74 @@
---
/**
* CategoryList.astro
* ---------------------------------------------------------------------------
* Renders a media kind's full library grouped by category: for each group a
* small subheader (the category name + item count), then a wrapping row of
* pills (title + optional status). Reuses the existing `.pill` + `<Icon>`
* pattern already established on the old reading page.
*
* Pass the output of `getGrouped(kind)` directly. When the list is empty the
* caller should fall back to `<Placeholder>` (see the pages); this component
* assumes at least one group.
*/
import Icon from '@components/Icon.astro';
import type { MediaGroup } from '@lib/media';
interface Props {
groups: MediaGroup[];
/** mdi icon shown before each pill. */
pillIcon?: string;
}
const { groups, pillIcon = 'play-circle' } = Astro.props;
---
<div class="flex flex-col gap-8">
{
groups.map((group) => (
<div class="flex flex-col gap-3">
<div class="flex items-baseline gap-2.5">
<h3
class="font-sans text-lg"
style="color: var(--color-text);"
>
{group.category}
</h3>
<span
class="font-mono text-[0.65rem] uppercase tracking-[0.18em]"
style="color: var(--color-text-muted);"
>
{group.items.length}
</span>
</div>
<ul class="flex flex-wrap gap-2.5">
{group.items.map((item) => (
<li
class="pill inline-flex items-center gap-1.5 text-sm"
title={item.note ?? ''}
>
<Icon name={pillIcon} class="h-3.5 w-3.5" />
<span>{item.title}</span>
{item.status && (
<span
class="font-mono text-[0.6rem] uppercase tracking-[0.14em]"
style="color: var(--color-accent);"
>
{item.status}
</span>
)}
{item.year && (
<span
class="font-mono text-[0.6rem] tracking-[0.04em]"
style="color: var(--color-text-muted);"
>
{item.year}
</span>
)}
</li>
))}
</ul>
</div>
))
}
</div>
+676
View File
@@ -0,0 +1,676 @@
[
{
"title": "Demon Slayer",
"favorite": true,
"blurb": "Modern shonen masterpiece. Ufotable's animation is legendary. Emotional core with Tanjiro and Nezuko.",
"image": "/images/anime/kimetsu-no-yaiba.jpg"
},
{
"title": "Demon Slayer",
"category": "Major Modern Hits",
"status": "Completed",
"note": "All 4 seasons. Ufotable's animation is legendary. Tanjiro and Nezuko's relationship is the emotional core."
},
{
"title": "Jujutsu Kaisen",
"category": "Major Modern Hits",
"status": "Completed",
"note": "Through Season 2. The Shibuya Incident arc redefined what shonen anime can do: dark, unpredictable, devastating."
},
{
"title": "Attack on Titan",
"category": "Major Modern Hits",
"status": "Completed",
"note": "Full series. Dark fantasy with political themes and shocking twists. The basement reveal redefined the series."
},
{
"title": "My Hero Academia",
"category": "Major Modern Hits",
"favorite": true,
"status": "Caught up",
"note": "Through Season 6. Character-driven with incredible growth arcs. The war arc is darker.",
"image": "/images/anime/my-hero-academia.jpg"
},
{
"title": "Chainsaw Man",
"category": "Major Modern Hits",
"status": "Completed",
"note": "Full series. Dark, chaotic, unique. Denji's journey from poverty to hope is compelling."
},
{
"title": "Black Clover",
"category": "Major Modern Hits",
"status": "Completed",
"note": "Full series. Underdog shonen that gets better as it develops."
},
{
"title": "Dragon Ball",
"category": "Classics",
"status": "Completed",
"note": "Original series."
},
{
"title": "Dragon Ball Z",
"category": "Classics",
"status": "Completed",
"note": "Legendary shonen. Saiyans, Frieza, Cell, Buu."
},
{
"title": "Naruto",
"category": "Classics",
"status": "Completed",
"note": "Ninja, friendship, power."
},
{
"title": "Naruto Shippuden",
"category": "Classics",
"status": "Completed",
"note": "Mature sequel with war arc."
},
{
"title": "YuYu Hakusho",
"category": "Classics",
"status": "Completed",
"note": "Classic 90s shonen with Yusuke becoming Spirit Detective."
},
{
"title": "Sailor Moon",
"category": "Classics",
"status": "Completed",
"note": "Classic magical girl."
},
{
"title": "Death Note",
"category": "Classics",
"status": "Completed",
"note": "Detective thriller with supernatural death note."
},
{
"title": "Cowboy Bebop",
"category": "Classics",
"status": "Completed",
"note": "Space western bounty hunters. Style, jazz, melancholy. Watched both anime and Netflix live-action."
},
{
"title": "One Piece",
"category": "Classics",
"status": "Caught up",
"note": "Caught up through current episodes. Epic adventure spanning 1,100+ episodes."
},
{
"title": "Bleach",
"category": "Classics",
"favorite": true,
"status": "Completed",
"note": "Completed including Thousand-Year Blood War arc.",
"image": "/images/anime/bleach.jpg"
},
{
"title": "Gundam (All Series)",
"category": "Classics",
"favorite": true,
"status": "Completed",
"note": "All timelines, all series. War, politics, humanity through mecha.",
"image": "/images/anime/gundam-all-series.jpg"
},
{
"title": "Evangelion",
"category": "Classics",
"status": "Completed",
"note": "Psychological mecha deconstruction. Trauma, depression, identity."
},
{
"title": "Code Geass",
"category": "Classics",
"favorite": true,
"status": "Completed",
"note": "Strategy, rebellion, moral questions. The ending is legendary.",
"image": "/images/anime/code-geass.jpg"
},
{
"title": "Fullmetal Alchemist: Brotherhood",
"category": "Classics",
"status": "Completed",
"note": "Perfection in shonen storytelling."
},
{
"title": "Hunter x Hunter",
"category": "Classics",
"status": "Completed",
"note": "Complex battle shonen with Nen power system. The Chimera Ant arc is philosophical."
},
{
"title": "Saint Seiya",
"category": "Classics",
"status": "Completed",
"note": "Classic shonen with saints and cloths."
},
{
"title": "That Time I Got Reincarnated as a Slime",
"category": "Isekai & Fantasy",
"favorite": true,
"status": "Completed",
"note": "Nation-building focus over battles. Also reading light novels (21/23 volumes).",
"image": "/images/anime/that-time-i-got-reincarnated-as-a-slime.jpg"
},
{
"title": "Overlord",
"category": "Isekai & Fantasy",
"status": "Completed",
"note": "Protagonist becomes actual overlord. Political intrigue and power fantasy. Also reading light novels (16/18 volumes)."
},
{
"title": "Re:Zero",
"category": "Isekai & Fantasy",
"status": "Completed",
"note": "Time-loop psychological thriller. Subaru's growth is compelling. Also reading light novels (26-28/44+ volumes)."
},
{
"title": "Ascendance of a Bookworm",
"category": "Isekai & Fantasy",
"status": "Completed",
"note": "Unique isekai with commerce and innovation focus rather than combat."
},
{
"title": "Solo Leveling",
"category": "Isekai & Fantasy",
"status": "Completed",
"note": "Korean webnovel turned anime/manhwa. Protagonist gains power through system. Consumed in all formats."
},
{
"title": "Log Horizon",
"category": "Isekai & Fantasy",
"status": "Completed",
"note": "Thousands trapped in MMORPG become reality. Focus on politics and society building."
},
{
"title": "No Game No Life",
"category": "Isekai & Fantasy",
"status": "Completed",
"note": "Everything decided by games. Vibrant colors, clever strategies."
},
{
"title": "Yona of the Dawn",
"category": "Isekai & Fantasy",
"status": "Completed",
"note": "Princess exiled, gathers allies. Character-driven fantasy romance."
},
{
"title": "Kakegurui",
"category": "Isekai & Fantasy",
"status": "Completed",
"note": "Gambling school where status determined by gambling. Psychological, dark humor."
},
{
"title": "Rising of the Shield Hero",
"category": "Isekai & Fantasy",
"status": "Completed",
"note": "Summoned as Shield Hero, faces betrayal. Darker isekai."
},
{
"title": "How a Realist Hero Rebuilt the Kingdom",
"category": "Isekai & Fantasy",
"status": "Completed",
"note": "Isekai focusing on kingdom management and economics."
},
{
"title": "The Strongest Sage with the Weakest Crest",
"category": "Isekai & Fantasy",
"status": "Completed",
"note": "Reincarnated with low stats, technique overcomes limits."
},
{
"title": "Plunderer",
"category": "Isekai & Fantasy",
"status": "Completed",
"note": "Post-apocalyptic fantasy with unique power system."
},
{
"title": "A Returner's Magic Should Be Special",
"category": "Isekai & Fantasy",
"status": "Completed",
"note": "Magical academy with regression trope."
},
{
"title": "Knights and Magic",
"category": "Isekai & Fantasy",
"status": "Completed",
"note": "Mecha fantasy."
},
{
"title": "The Unwanted Undead Adventurer",
"category": "Isekai & Fantasy",
"status": "Completed",
"note": "Supporting character becomes main. Short, sweet, complete."
},
{
"title": "Black Summoner",
"category": "Isekai & Fantasy",
"status": "Completed",
"note": "Dungeon fantasy with overpowered protagonist."
},
{
"title": "The Irregular at Magic High School",
"category": "Isekai & Fantasy",
"status": "Completed",
"note": "Magic school where student has uncommon power."
},
{
"title": "Fire Force",
"category": "Battle & Action",
"status": "Completed",
"note": "Firefighters fighting flames from human combustion."
},
{
"title": "Psycho-Pass",
"category": "Battle & Action",
"status": "Completed",
"note": "Cyberpunk dystopia where potential for crime measured. Philosophical, dark."
},
{
"title": "Parasyte",
"category": "Battle & Action",
"status": "Completed",
"note": "Alien parasites taking over human bodies. Body horror, philosophy."
},
{
"title": "World Trigger",
"category": "Battle & Action",
"status": "Completed",
"note": "Invaders and defense force using \"triggers.\" Tactical, strategic."
},
{
"title": "Aldnoah.Zero",
"category": "Battle & Action",
"status": "Completed",
"note": "Mecha war between Earth and Vers. Political, tactical."
},
{
"title": "Blood Blockade Battlefront",
"category": "Battle & Action",
"status": "Completed",
"note": "Supernatural city with unusual powers. Stylish, action-packed."
},
{
"title": "Hellsing",
"category": "Battle & Action",
"status": "Completed",
"note": "Vampire hunters fighting vampires. Over-the-top, violent."
},
{
"title": "Goblin Slayer",
"category": "Battle & Action",
"status": "Completed",
"note": "Dark fantasy adventurer whose focus is killing goblins. Trauma-informed."
},
{
"title": "Berserk of Gluttony",
"category": "Battle & Action",
"status": "Completed",
"note": "Fantasy with unique power system involving eating."
},
{
"title": "Blue Exorcist",
"category": "Battle & Action",
"status": "Completed",
"note": "Protagonist is son of Satan but becomes exorcist."
},
{
"title": "Fate/Stay Night: Unlimited Blade Works",
"category": "Battle & Action",
"status": "Completed",
"note": "Visual novel adaptation with Holy Grail War."
},
{
"title": "Fate/Zero",
"category": "Battle & Action",
"status": "Completed",
"note": "Prequel exploring previous Holy Grail War. Complex, philosophical."
},
{
"title": "The God of High School",
"category": "Battle & Action",
"status": "Completed",
"note": "Tournament fighting. Watched in anime and webtoon."
},
{
"title": "Noblesse",
"category": "Battle & Action",
"status": "Completed",
"note": "Vampire nobility in modern setting. Watched in anime and webtoon."
},
{
"title": "Tower of God",
"category": "Battle & Action",
"status": "Completed",
"note": "Climbing mysterious tower. Watched in anime and manhwa."
},
{
"title": "Vinland Saga",
"category": "Battle & Action",
"status": "Completed",
"note": "Vikings, war, pacifism, redemption. Thorfinn's journey is complex and mature."
},
{
"title": "Magi: The Labyrinth of Magic",
"category": "Isekai & Fantasy",
"favorite": true,
"status": "Completed",
"note": "Adventure fantasy inspired by the Arabian Nights. Dungeons, djinn, and a sprawling world that grows more political as it goes.",
"image": "/images/anime/magi-the-labyrinth-of-magic.jpg"
},
{
"title": "Witch Hat Atelier",
"category": "Isekai & Fantasy",
"status": "Completed",
"note": "Beautifully drawn fantasy about a girl who discovers magic is drawn, not born. The art alone is worth it."
},
{
"title": "Erased",
"category": "Dark & Psychological",
"status": "Completed",
"note": "Time travel to prevent murders. Emotional, compelling."
},
{
"title": "Death Parade",
"category": "Dark & Psychological",
"status": "Completed",
"note": "Afterlife bar where games determine souls. Emotional, philosophical."
},
{
"title": "So I'm a Spider, So What?",
"category": "Dark & Psychological",
"status": "Completed",
"note": "Isekai from spider perspective. Clever, funny, surprisingly deep."
},
{
"title": "Tokyo Ghoul",
"category": "Dark & Psychological",
"status": "Completed",
"note": "Half-ghoul navigating two worlds. Dark, violent, explores prejudice."
},
{
"title": "Bungo Stray Dogs",
"category": "Dark & Psychological",
"status": "Completed",
"note": "Detectives with powers based on literary figures."
},
{
"title": "86",
"category": "Dark & Psychological",
"status": "Completed",
"note": "Discriminated soldiers fighting war. Mature, powerful, heartbreaking."
},
{
"title": "Akame ga Kill!",
"category": "Dark & Psychological",
"status": "Completed",
"note": "Dark fantasy rebellion with high casualties."
},
{
"title": "Tanya the Evil",
"category": "Dark & Psychological",
"status": "Completed",
"note": "Salaryman reincarnated as magical girl officer in WWI. (Also known as Youjo Senki.)"
},
{
"title": "Your Lie in April",
"category": "Character & Romance",
"status": "Completed",
"note": "Music and romance. Emotional, beautiful, devastating."
},
{
"title": "Spy x Family",
"category": "Character & Romance",
"status": "Caught up",
"note": "Spy, assassin, telepath form fake family. Charming comedy with heart."
},
{
"title": "Ancient Magus Bride",
"category": "Character & Romance",
"status": "Completed",
"note": "Mature apprentice learning from ancient mage. Beautiful world-building."
},
{
"title": "To Your Eternity",
"category": "Unique & Specialized",
"status": "Completed",
"note": "Immortal being learning humanity across time. Beautiful, devastating."
},
{
"title": "Ranking of Kings",
"category": "Unique & Specialized",
"status": "Completed",
"note": "Fantasy kingdom with deaf prince protagonist."
},
{
"title": "Dr. Stone",
"category": "Unique & Specialized",
"status": "Completed",
"note": "Stone petrification, scientific reconstruction, kingdom building."
},
{
"title": "The Promised Neverland",
"category": "Unique & Specialized",
"status": "Completed",
"note": "Children's orphanage hiding dark secret. First arc excellent."
},
{
"title": "Food Wars",
"category": "Unique & Specialized",
"status": "Completed",
"note": "Cooking battles with elaborate descriptions and fanservice."
},
{
"title": "Wise Man's Grandchild",
"category": "Unique & Specialized",
"status": "Completed",
"note": "Isekai with powerful but socially inept protagonist."
},
{
"title": "Reincarnated as the World's Strongest Exorcist",
"category": "Unique & Specialized",
"status": "Completed",
"note": "Fantasy exorcism."
},
{
"title": "Noragami",
"category": "Unique & Specialized",
"status": "Completed",
"note": "Minor god doing odd jobs. Character-driven fantasy."
},
{
"title": "Lord of Mysteries",
"category": "Unique & Specialized",
"status": "Completed",
"note": "Cthulhu-esque mystery, power system."
},
{
"title": "Fruits Basket",
"category": "Unique & Specialized",
"status": "Completed",
"note": "Sohma family curse, emotional, heartwarming."
},
{
"title": "Kuroko's Basketball",
"category": "Unique & Specialized",
"status": "Completed",
"note": "Sports anime with shadow player and light player partnership."
},
{
"title": "Haikyu!!",
"category": "Unique & Specialized",
"status": "Completed",
"note": "Volleyball anime with incredible team dynamics. Character work elevates beyond sports."
},
{
"title": "Digimon Adventure Tri",
"category": "Unique & Specialized",
"status": "Completed",
"note": "Digimon reboot/remix."
},
{
"title": "Zatch Bell",
"category": "Unique & Specialized",
"status": "Completed",
"note": "Battle mamodo with spell books. (Also known as Zak Bell.)"
},
{
"title": "Konosuba",
"category": "Unique & Specialized",
"status": "Completed",
"note": "Isekai comedy with dysfunctional party."
},
{
"title": "Guilty Crown",
"category": "Unique & Specialized",
"status": "Completed",
"note": "Apocalypse, music, power systems."
},
{
"title": "Record of Grancrest War",
"category": "Unique & Specialized",
"status": "Completed",
"note": "Fantasy war and unions."
},
{
"title": "SerVamp",
"category": "Unique & Specialized",
"status": "Completed",
"note": "Vampires and servants."
},
{
"title": "Prince of Tennis",
"category": "Unique & Specialized",
"status": "Completed",
"note": "Sports anime."
},
{
"title": "MAR",
"category": "Unique & Specialized",
"status": "Completed",
"note": "Adventure fantasy."
},
{
"title": "Tokyo Revengers",
"category": "Unique & Specialized",
"status": "Completed",
"note": "Time gang time travel. Watched anime, finished manga as well."
},
{
"title": "Darling in the FranXX",
"category": "Unique & Specialized",
"status": "Completed",
"note": "Mecha fantasy romance."
},
{
"title": "SK8 the Infinity",
"category": "Unique & Specialized",
"status": "Completed",
"note": "Skateboard racing anime."
},
{
"title": "Soul Eater",
"category": "Unique & Specialized",
"status": "Completed",
"note": "Weapon/meister academy."
},
{
"title": "Wistoria: Wand and Sword",
"category": "Unique & Specialized",
"status": "Completed",
"note": "Magic academy anime."
},
{
"title": "Reign of the Seven Spellblades",
"category": "Unique & Specialized",
"status": "Completed",
"note": "Magic academy."
},
{
"title": "The Unaware Atelier Meister",
"category": "Unique & Specialized",
"status": "Completed",
"note": "Isekai atelier fantasy."
},
{
"title": "Kabaneri of the Iron Fortress",
"category": "Unique & Specialized",
"status": "Completed",
"note": "Zombies with trains."
},
{
"title": "Classroom of the Elite",
"category": "Unique & Specialized",
"status": "Completed",
"note": "Elite school mystery with hidden genius protagonist."
},
{
"title": "Charlotte",
"category": "Unique & Specialized",
"status": "Completed",
"note": "Students with powers."
},
{
"title": "Sword Art Online",
"category": "Unique & Specialized",
"status": "Completed",
"note": "All seasons and movies."
},
{
"title": "The King's Avatar",
"category": "Unique & Specialized",
"status": "Completed",
"note": "Pro gamer returns. Watched fully, read light novel, seen all animation and live-action shows. Completed all formats."
},
{
"title": "Fairy Tail",
"category": "Unique & Specialized",
"status": "Completed",
"note": "Guild-based fantasy."
},
{
"title": "Bofuri",
"category": "Unique & Specialized",
"status": "Completed",
"note": "Defensive build in VRMMO."
},
{
"title": "Kaiju No. 8",
"category": "Unique & Specialized",
"status": "Completed",
"note": "Kaiju defense forces."
},
{
"title": "Welcome to Demon School! Iruma-Kun",
"category": "Unique & Specialized",
"status": "Completed",
"note": "Demon school fantasy."
},
{
"title": "JoJo's Bizarre Adventure",
"category": "Unique & Specialized",
"status": "Completed",
"note": "Up to Steel Ball Run."
},
{
"title": "Black Butler",
"category": "Unique & Specialized",
"status": "Completed",
"note": "Demon butler."
},
{
"title": "Frieren: Beyond Journey's End",
"category": "Unique & Specialized",
"status": "Completed",
"note": "Post-adventure elf fantasy."
},
{
"title": "One Piece",
"favorite": true,
"category": "Long-Running Ongoing",
"status": "Caught up",
"note": "Caught up through current episodes (1,100+ episodes and ongoing).",
"image": "/images/anime/one-piece.jpg"
}
]
+788
View File
@@ -0,0 +1,788 @@
[
{
"title": "The Princess Bride",
"category": "Standalone",
"year": 1987,
"favorite": true,
"blurb": "Fantasy adventure that defines \"quotable.\"",
"image": "/images/movies/the-princess-bride.jpg"
},
{
"title": "Contact",
"category": "Standalone",
"year": 1997,
"favorite": true,
"blurb": "Sci-fi that asks the big questions.",
"image": "/images/movies/contact.jpg"
},
{
"title": "Howl's Moving Castle",
"category": "Ghibli",
"year": 2004,
"favorite": true,
"blurb": "Ghibli magic and Miyazaki's imagination.",
"image": "/images/movies/howls-moving-castle.jpg"
},
{
"title": "The Matrix",
"category": "The Matrix",
"year": 1999,
"favorite": true,
"blurb": "Red pill/blue pill, bullet time, philosophical questions that still resonate.",
"image": "/images/movies/the-matrix.jpg"
},
{
"title": "The Matrix Reloaded",
"category": "The Matrix",
"year": 2003
},
{
"title": "The Matrix Revolutions",
"category": "The Matrix",
"year": 2003
},
{
"title": "The Matrix Resurrections",
"category": "The Matrix",
"year": 2021
},
{
"title": "Kiki's Delivery Service",
"category": "Ghibli",
"year": 1989
},
{
"title": "Spirited Away",
"category": "Ghibli",
"year": 2001
},
{
"title": "My Neighbor Totoro",
"category": "Ghibli",
"year": 1988
},
{
"title": "Porco Rosso",
"category": "Ghibli",
"year": 1992
},
{
"title": "Iron Man",
"category": "Marvel Cinematic Universe",
"year": 2008
},
{
"title": "The Incredible Hulk",
"category": "Marvel Cinematic Universe",
"year": 2008
},
{
"title": "Iron Man 2",
"category": "Marvel Cinematic Universe",
"year": 2010
},
{
"title": "Thor",
"category": "Marvel Cinematic Universe",
"year": 2011
},
{
"title": "Captain America: The First Avenger",
"category": "Marvel Cinematic Universe",
"year": 2011
},
{
"title": "The Avengers",
"category": "Marvel Cinematic Universe",
"year": 2012
},
{
"title": "Iron Man 3",
"category": "Marvel Cinematic Universe",
"year": 2013
},
{
"title": "Thor: The Dark World",
"category": "Marvel Cinematic Universe",
"year": 2013
},
{
"title": "Captain America: The Winter Soldier",
"category": "Marvel Cinematic Universe",
"year": 2014
},
{
"title": "Guardians of the Galaxy",
"category": "Marvel Cinematic Universe",
"year": 2014
},
{
"title": "Avengers: Age of Ultron",
"category": "Marvel Cinematic Universe",
"year": 2015
},
{
"title": "Ant-Man",
"category": "Marvel Cinematic Universe",
"year": 2015
},
{
"title": "Captain America: Civil War",
"category": "Marvel Cinematic Universe",
"year": 2016
},
{
"title": "Doctor Strange",
"category": "Marvel Cinematic Universe",
"year": 2016
},
{
"title": "Spider-Man: Homecoming",
"category": "Marvel Cinematic Universe",
"year": 2017
},
{
"title": "Thor: Ragnarok",
"category": "Marvel Cinematic Universe",
"year": 2017
},
{
"title": "Black Panther",
"category": "Marvel Cinematic Universe",
"year": 2018
},
{
"title": "Avengers: Infinity War",
"category": "Marvel Cinematic Universe",
"year": 2018,
"favorite": true,
"blurb": "The MCU at its operatic peak.",
"image": "/images/movies/avengers-infinity-war.jpg"
},
{
"title": "Ant-Man and the Wasp",
"category": "Marvel Cinematic Universe",
"year": 2018
},
{
"title": "Captain Marvel",
"category": "Marvel Cinematic Universe",
"year": 2019
},
{
"title": "Avengers: Endgame",
"category": "Marvel Cinematic Universe",
"year": 2019,
"favorite": true,
"blurb": "The decade-long payoff.",
"image": "/images/movies/avengers-endgame.jpg"
},
{
"title": "Spider-Man: Far From Home",
"category": "Marvel Cinematic Universe",
"year": 2019
},
{
"title": "Black Widow",
"category": "Marvel Cinematic Universe",
"year": 2021
},
{
"title": "Shang-Chi and the Legend of the Ten Rings",
"category": "Marvel Cinematic Universe",
"year": 2021
},
{
"title": "Eternals",
"category": "Marvel Cinematic Universe",
"year": 2021
},
{
"title": "Spider-Man: No Way Home",
"category": "Marvel Cinematic Universe",
"year": 2021
},
{
"title": "Doctor Strange in the Multiverse of Madness",
"category": "Marvel Cinematic Universe",
"year": 2022
},
{
"title": "Thor: Love and Thunder",
"category": "Marvel Cinematic Universe",
"year": 2022
},
{
"title": "Black Panther: Wakanda Forever",
"category": "Marvel Cinematic Universe",
"year": 2022
},
{
"title": "Ant-Man and the Wasp: Quantumania",
"category": "Marvel Cinematic Universe",
"year": 2023
},
{
"title": "Guardians of the Galaxy Vol. 3",
"category": "Marvel Cinematic Universe",
"year": 2023
},
{
"title": "The Marvels",
"category": "Marvel Cinematic Universe",
"year": 2023
},
{
"title": "Deadpool & Wolverine",
"category": "Marvel Cinematic Universe",
"year": 2024
},
{
"title": "Captain America: Brave New World",
"category": "Marvel Cinematic Universe",
"year": 2025
},
{
"title": "Thunderbolts*",
"category": "Marvel Cinematic Universe",
"year": 2025
},
{
"title": "The Fantastic Four: First Steps",
"category": "Marvel Cinematic Universe",
"year": 2025
},
{
"title": "Avengers: Doomsday",
"category": "Marvel Cinematic Universe",
"year": 2026
},
{
"title": "Avatar",
"category": "Avatar",
"year": 2009
},
{
"title": "Avatar: The Way of Water",
"category": "Avatar",
"year": 2022
},
{
"title": "Pacific Rim",
"category": "Pacific Rim",
"year": 2013
},
{
"title": "Pacific Rim: Uprising",
"category": "Pacific Rim",
"year": 2018
},
{
"title": "Knives Out",
"category": "Knives Out",
"year": 2019
},
{
"title": "Glass Onion: A Knives Out Mystery",
"category": "Knives Out",
"year": 2022
},
{
"title": "Wake Up Dead Man: A Knives Out Mystery",
"category": "Knives Out",
"year": 2025
},
{
"title": "The Fast and the Furious",
"category": "Fast & Furious",
"year": 2001
},
{
"title": "2 Fast 2 Furious",
"category": "Fast & Furious",
"year": 2003
},
{
"title": "The Fast and the Furious: Tokyo Drift",
"category": "Fast & Furious",
"year": 2006
},
{
"title": "Fast & Furious",
"category": "Fast & Furious",
"year": 2009
},
{
"title": "Fast Five",
"category": "Fast & Furious",
"year": 2011
},
{
"title": "Fast & Furious 6",
"category": "Fast & Furious",
"year": 2013
},
{
"title": "Furious 7",
"category": "Fast & Furious",
"year": 2015
},
{
"title": "The Fate of the Furious",
"category": "Fast & Furious",
"year": 2017
},
{
"title": "F9: The Fast Saga",
"category": "Fast & Furious",
"year": 2021
},
{
"title": "Fast X",
"category": "Fast & Furious",
"year": 2023
},
{
"title": "Hobbs & Shaw",
"category": "Fast & Furious",
"year": 2019
},
{
"title": "Star Wars: Episode IV - A New Hope",
"category": "Star Wars",
"year": 1977
},
{
"title": "Star Wars: Episode V - The Empire Strikes Back",
"favorite": true,
"category": "Star Wars",
"year": 1980,
"image": "/images/movies/star-wars-episode-v-the-empire-strikes-back.jpg"
},
{
"title": "Star Wars: Episode VI - Return of the Jedi",
"category": "Star Wars",
"year": 1983
},
{
"title": "Star Wars: Episode I - The Phantom Menace",
"category": "Star Wars",
"year": 1999
},
{
"title": "Star Wars: Episode II - Attack of the Clones",
"category": "Star Wars",
"year": 2002
},
{
"title": "Star Wars: Episode III - Revenge of the Sith",
"category": "Star Wars",
"year": 2005
},
{
"title": "Star Wars: Episode VII - The Force Awakens",
"category": "Star Wars",
"year": 2015
},
{
"title": "Rogue One: A Star Wars Story",
"category": "Star Wars",
"year": 2016
},
{
"title": "Star Wars: Episode VIII - The Last Jedi",
"category": "Star Wars",
"year": 2017
},
{
"title": "Solo: A Star Wars Story",
"category": "Star Wars",
"year": 2018
},
{
"title": "Star Wars: Episode IX - The Rise of Skywalker",
"category": "Star Wars",
"year": 2019
},
{
"title": "Saw",
"category": "Saw",
"year": 2004
},
{
"title": "Saw II",
"category": "Saw",
"year": 2005
},
{
"title": "Saw III",
"category": "Saw",
"year": 2006
},
{
"title": "Saw IV",
"category": "Saw",
"year": 2007
},
{
"title": "Saw V",
"category": "Saw",
"year": 2008
},
{
"title": "Saw VI",
"category": "Saw",
"year": 2009
},
{
"title": "Saw 3D: The Final Chapter",
"category": "Saw",
"year": 2010
},
{
"title": "Jigsaw",
"category": "Saw",
"year": 2017
},
{
"title": "Spiral: From the Book of Saw",
"category": "Saw",
"year": 2021
},
{
"title": "Saw X",
"category": "Saw",
"year": 2023
},
{
"title": "Mission: Impossible",
"category": "Mission: Impossible",
"year": 1996
},
{
"title": "Mission: Impossible 2",
"category": "Mission: Impossible",
"year": 2000
},
{
"title": "Mission: Impossible III",
"category": "Mission: Impossible",
"year": 2006
},
{
"title": "Mission: Impossible - Ghost Protocol",
"category": "Mission: Impossible",
"year": 2011
},
{
"title": "Mission: Impossible - Rogue Nation",
"category": "Mission: Impossible",
"year": 2015
},
{
"title": "Mission: Impossible - Fallout",
"category": "Mission: Impossible",
"year": 2018
},
{
"title": "Mission: Impossible - Dead Reckoning",
"category": "Mission: Impossible",
"year": 2023
},
{
"title": "Mission: Impossible - The Final Reckoning",
"category": "Mission: Impossible",
"year": 2025
},
{
"title": "Jurassic Park",
"favorite": true,
"category": "Jurassic Park / World",
"year": 1993,
"image": "/images/movies/jurassic-park.jpg"
},
{
"title": "The Lost World: Jurassic Park",
"category": "Jurassic Park / World",
"year": 1997
},
{
"title": "Jurassic Park III",
"category": "Jurassic Park / World",
"year": 2001
},
{
"title": "Jurassic World",
"category": "Jurassic Park / World",
"year": 2015
},
{
"title": "Jurassic World: Fallen Kingdom",
"category": "Jurassic Park / World",
"year": 2018
},
{
"title": "Jurassic World Dominion",
"category": "Jurassic Park / World",
"year": 2022
},
{
"title": "Transformers",
"category": "Transformers",
"favorite": true,
"year": 2007,
"image": "/images/movies/transformers.jpg"
},
{
"title": "Transformers: Revenge of the Fallen",
"category": "Transformers",
"year": 2009
},
{
"title": "Transformers: Dark of the Moon",
"category": "Transformers",
"year": 2011
},
{
"title": "Transformers: Age of Extinction",
"category": "Transformers",
"year": 2014
},
{
"title": "Transformers: The Last Knight",
"category": "Transformers",
"year": 2017
},
{
"title": "Bumblebee",
"category": "Transformers",
"year": 2018
},
{
"title": "Transformers: Rise of the Beasts",
"category": "Transformers",
"year": 2023
},
{
"title": "Spider-Man",
"category": "Spider-Man (All Versions)",
"year": 2002
},
{
"title": "Spider-Man 2",
"category": "Spider-Man (All Versions)",
"year": 2004
},
{
"title": "Spider-Man 3",
"category": "Spider-Man (All Versions)",
"year": 2007
},
{
"title": "The Amazing Spider-Man",
"category": "Spider-Man (All Versions)",
"year": 2012
},
{
"title": "The Amazing Spider-Man 2",
"category": "Spider-Man (All Versions)",
"year": 2014
},
{
"title": "Spider-Man: Into the Spider-Verse",
"category": "Spider-Man (All Versions)",
"year": 2018
},
{
"title": "Spider-Man: Across the Spider-Verse",
"category": "Spider-Man (All Versions)",
"year": 2023
},
{
"title": "Batman Begins",
"category": "The Dark Knight Trilogy",
"year": 2005
},
{
"title": "The Dark Knight",
"category": "The Dark Knight Trilogy",
"year": 2008
},
{
"title": "The Dark Knight Rises",
"category": "The Dark Knight Trilogy",
"year": 2012
},
{
"title": "X-Men",
"category": "X-Men / Fox Marvel",
"year": 2000
},
{
"title": "X2: X-Men United",
"category": "X-Men / Fox Marvel",
"year": 2003
},
{
"title": "X-Men: The Last Stand",
"category": "X-Men / Fox Marvel",
"year": 2006
},
{
"title": "X-Men Origins: Wolverine",
"category": "X-Men / Fox Marvel",
"year": 2009
},
{
"title": "X-Men: First Class",
"category": "X-Men / Fox Marvel",
"year": 2011
},
{
"title": "The Wolverine",
"category": "X-Men / Fox Marvel",
"year": 2013
},
{
"title": "X-Men: Days of Future Past",
"category": "X-Men / Fox Marvel",
"year": 2014
},
{
"title": "Deadpool",
"category": "X-Men / Fox Marvel",
"year": 2016
},
{
"title": "X-Men: Apocalypse",
"category": "X-Men / Fox Marvel",
"year": 2016
},
{
"title": "Logan",
"category": "X-Men / Fox Marvel",
"year": 2017
},
{
"title": "Deadpool 2",
"category": "X-Men / Fox Marvel",
"year": 2018
},
{
"title": "X-Men: Dark Phoenix",
"category": "X-Men / Fox Marvel",
"year": 2019
},
{
"title": "The New Mutants",
"category": "X-Men / Fox Marvel",
"year": 2020
},
{
"title": "Raiders of the Lost Ark",
"category": "Indiana Jones",
"year": 1981
},
{
"title": "Indiana Jones and the Temple of Doom",
"category": "Indiana Jones",
"year": 1984
},
{
"title": "Indiana Jones and the Last Crusade",
"category": "Indiana Jones",
"year": 1989
},
{
"title": "Indiana Jones and the Kingdom of the Crystal Skull",
"category": "Indiana Jones",
"year": 2008
},
{
"title": "Indiana Jones and the Dial of Destiny",
"category": "Indiana Jones",
"year": 2023
},
{
"title": "Godzilla",
"category": "MonsterVerse",
"year": 2014
},
{
"title": "Kong: Skull Island",
"category": "MonsterVerse",
"year": 2017
},
{
"title": "Godzilla: King of the Monsters",
"category": "MonsterVerse",
"year": 2019
},
{
"title": "Godzilla vs. Kong",
"category": "MonsterVerse",
"year": 2021
},
{
"title": "Godzilla x Kong: The New Empire",
"category": "MonsterVerse",
"year": 2024
},
{
"title": "The Bourne Identity",
"category": "Bourne",
"year": 2002
},
{
"title": "The Bourne Supremacy",
"category": "Bourne",
"year": 2004
},
{
"title": "The Bourne Ultimatum",
"category": "Bourne",
"year": 2007
},
{
"title": "The Bourne Legacy",
"category": "Bourne",
"year": 2012
},
{
"title": "Jason Bourne",
"category": "Bourne",
"year": 2016
},
{
"title": "Ocean's Eleven",
"favorite": true,
"category": "Ocean's",
"year": 2001,
"image": "/images/movies/oceans-eleven.jpg"
},
{
"title": "Ocean's Twelve",
"category": "Ocean's",
"year": 2004
},
{
"title": "Ocean's Thirteen",
"category": "Ocean's",
"year": 2007
},
{
"title": "Ocean's 8",
"category": "Ocean's",
"year": 2018
},
{
"title": "Venom",
"category": "Venom",
"year": 2018
},
{
"title": "Venom: Let There Be Carnage",
"category": "Venom",
"year": 2021
}
]
+609
View File
@@ -0,0 +1,609 @@
[
{
"title": "Breaking Bad",
"favorite": true,
"blurb": "The character study of a decade. Walter White's transformation is devastating. Meticulously crafted, transcendent storytelling. One of TV's greatest dramas.",
"image": "/images/tv/breaking-bad.jpg"
},
{
"title": "Brooklyn Nine-Nine",
"favorite": true,
"blurb": "Found family comedy with the perfect ensemble. Andre Braugher's deadpan Captain Holt is legendary.",
"image": "/images/tv/brooklyn-nine-nine.jpg"
},
{
"title": "Parks and Recreation",
"favorite": true,
"blurb": "The show about people who care about their community. Leslie Knope is everything. Optimistic government comedy.",
"image": "/images/tv/parks-and-recreation.jpg"
},
{
"title": "The Bear",
"favorite": true,
"blurb": "Grief and anxiety in a kitchen. Frenetic, anxiety-inducing, emotionally devastating and incredibly human.",
"image": "/images/tv/the-bear.jpg"
},
{
"title": "WandaVision",
"favorite": true,
"blurb": "Multiverse introduction through sitcom pastiche. Brilliant.",
"image": "/images/tv/wandavision.jpg"
},
{
"title": "Suits",
"favorite": true,
"blurb": "Sharp dialogue, workplace politics, the Harvey/Mike dynamic, Louis Litt's incredible character growth.",
"image": "/images/tv/suits.jpg"
},
{
"title": "30 Rock",
"blurb": "Fast-paced industry satire. Tina Fey's meta comedy about SNL and network TV. Sharp, absurd, culturally relevant.",
"image": "/images/tv/30-rock.jpg"
},
{
"title": "House",
"blurb": "Brilliant but misanthropic diagnostician solves impossible cases. Hugh Laurie's performance is iconic. A favorite.",
"image": "/images/tv/house.jpg"
},
{
"title": "Arcane",
"favorite": true,
"blurb": "One of the best animated series ever made. Stunning visuals, emotional storytelling, transcends its video game source.",
"image": "/images/tv/arcane.jpg"
},
{
"title": "The Wire",
"blurb": "The definitive Baltimore drama. Institution-level storytelling that redefined what TV could do.",
"image": "/images/tv/the-wire.jpg"
},
{
"title": "The Sopranos",
"blurb": "The mob drama that started the golden age of prestige TV. Tony Soprano is one of TV's great characters.",
"image": "/images/tv/the-sopranos.jpg"
},
{
"title": "Succession",
"blurb": "Corporate power dynamics and family dysfunction. Brutal, funny, devastating.",
"image": "/images/tv/succession.jpg"
},
{
"title": "The Crown",
"blurb": "British royalty drama across decades. Impeccable production, character-driven.",
"image": "/images/tv/the-crown.jpg"
},
{
"title": "The Queen's Gambit",
"blurb": "Chess prodigy story that became a cultural phenomenon. Beautiful, satisfying.",
"image": "/images/tv/the-queens-gambit.jpg"
},
{
"title": "Ozark",
"blurb": "Financial planner launders money for cartel. Tense, complex, character-driven.",
"image": "/images/tv/ozark.jpg"
},
{
"title": "Peaky Blinders",
"blurb": "Birmingham gangsters in post-WWI England. Style, violence, Thomas Shelby.",
"image": "/images/tv/peaky-blinders.jpg"
},
{
"title": "Breaking Bad",
"category": "Prestige Drama",
"status": "Completed"
},
{
"title": "The Wire",
"category": "Prestige Drama",
"status": "Completed"
},
{
"title": "The Sopranos",
"category": "Prestige Drama",
"status": "Completed"
},
{
"title": "Succession",
"category": "Prestige Drama",
"status": "Completed"
},
{
"title": "The Crown",
"category": "Prestige Drama",
"status": "Completed"
},
{
"title": "Game of Thrones",
"favorite": true,
"category": "Prestige Drama",
"status": "Completed",
"image": "/images/tv/game-of-thrones.jpg"
},
{
"title": "House of the Dragon",
"category": "Prestige Drama",
"status": "Watching",
"note": "Watching Season 2"
},
{
"title": "The Last of Us",
"category": "Prestige Drama",
"status": "Completed"
},
{
"title": "Westworld",
"category": "Prestige Drama",
"status": "Completed"
},
{
"title": "Big Little Lies",
"category": "Prestige Drama",
"status": "Completed"
},
{
"title": "True Detective",
"category": "Prestige Drama",
"status": "Completed"
},
{
"title": "House of Cards",
"category": "Prestige Drama",
"status": "Completed"
},
{
"title": "Ozark",
"category": "Prestige Drama",
"status": "Completed"
},
{
"title": "The Queen's Gambit",
"category": "Prestige Drama",
"status": "Completed"
},
{
"title": "Peaky Blinders",
"category": "Prestige Drama",
"status": "Completed"
},
{
"title": "Mindhunter",
"category": "Prestige Drama",
"status": "Completed"
},
{
"title": "Stranger Things",
"category": "Streaming Originals",
"status": "Completed"
},
{
"title": "Dark",
"category": "Streaming Originals",
"status": "Completed"
},
{
"title": "You",
"category": "Streaming Originals",
"status": "Completed"
},
{
"title": "The OA",
"category": "Streaming Originals",
"status": "Completed"
},
{
"title": "Russian Doll",
"category": "Streaming Originals",
"status": "Completed"
},
{
"title": "Warrior Nun",
"category": "Streaming Originals",
"status": "Completed"
},
{
"title": "Black Mirror",
"category": "Streaming Originals",
"status": "Completed"
},
{
"title": "Altered Carbon",
"category": "Streaming Originals",
"status": "Completed"
},
{
"title": "Alice in Borderland",
"category": "Streaming Originals",
"status": "Completed"
},
{
"title": "Lupin",
"category": "Streaming Originals",
"status": "Completed"
},
{
"title": "Wednesday",
"category": "Streaming Originals",
"status": "Completed"
},
{
"title": "Shadow and Bone",
"category": "Streaming Originals",
"status": "Completed"
},
{
"title": "Fate: The Winx Saga",
"category": "Streaming Originals",
"status": "Completed"
},
{
"title": "The Order",
"category": "Streaming Originals",
"status": "Completed"
},
{
"title": "Cobra Kai",
"favorite": true,
"category": "Streaming Originals",
"status": "Completed",
"image": "/images/tv/cobra-kai.jpg"
},
{
"title": "Squid Game",
"category": "Streaming Originals",
"status": "Completed"
},
{
"title": "The Boys",
"category": "Streaming Originals",
"status": "Completed"
},
{
"title": "Invincible",
"favorite": true,
"category": "Streaming Originals",
"status": "Completed",
"image": "/images/tv/invincible.jpg"
},
{
"title": "Severance",
"category": "Streaming Originals",
"status": "Completed"
},
{
"title": "The Office (US)",
"category": "Workplace & Sitcom Comedies",
"status": "Completed"
},
{
"title": "Parks and Recreation",
"category": "Workplace & Sitcom Comedies",
"status": "Completed"
},
{
"title": "30 Rock",
"category": "Workplace & Sitcom Comedies",
"status": "Completed"
},
{
"title": "Brooklyn Nine-Nine",
"category": "Workplace & Sitcom Comedies",
"status": "Completed"
},
{
"title": "Friends",
"category": "Workplace & Sitcom Comedies",
"status": "Completed"
},
{
"title": "Community",
"category": "Workplace & Sitcom Comedies",
"status": "Completed"
},
{
"title": "Arrested Development",
"category": "Workplace & Sitcom Comedies",
"status": "Completed"
},
{
"title": "Veep",
"category": "Workplace & Sitcom Comedies",
"status": "Completed"
},
{
"title": "Curb Your Enthusiasm",
"category": "Workplace & Sitcom Comedies",
"status": "Completed"
},
{
"title": "Silicon Valley",
"category": "Workplace & Sitcom Comedies",
"status": "Completed"
},
{
"title": "The Big Bang Theory",
"category": "Workplace & Sitcom Comedies",
"status": "Completed"
},
{
"title": "Modern Family",
"category": "Workplace & Sitcom Comedies",
"status": "Completed"
},
{
"title": "Abbott Elementary",
"category": "Workplace & Sitcom Comedies",
"status": "Completed"
},
{
"title": "Never Have I Ever",
"category": "Workplace & Sitcom Comedies",
"status": "Completed"
},
{
"title": "Sex Education",
"category": "Workplace & Sitcom Comedies",
"status": "Completed"
},
{
"title": "BoJack Horseman",
"category": "Workplace & Sitcom Comedies",
"status": "Completed"
},
{
"title": "House",
"category": "Procedural & Long-Running",
"status": "Completed"
},
{
"title": "The West Wing",
"category": "Procedural & Long-Running",
"status": "Completed"
},
{
"title": "The Closer",
"category": "Procedural & Long-Running",
"status": "Completed"
},
{
"title": "Doctor Who",
"category": "Procedural & Long-Running",
"status": "Completed"
},
{
"title": "NCIS",
"category": "Procedural & Long-Running",
"status": "Completed"
},
{
"title": "Criminal Minds",
"category": "Procedural & Long-Running",
"status": "Completed"
},
{
"title": "Law & Order: SVU",
"category": "Procedural & Long-Running",
"status": "Completed"
},
{
"title": "Rizzoli & Isles",
"category": "Procedural & Long-Running",
"status": "Completed"
},
{
"title": "Bodyguard",
"category": "Procedural & Long-Running",
"status": "Completed"
},
{
"title": "Designated Survivor",
"category": "Procedural & Long-Running",
"status": "Completed"
},
{
"title": "The Recruit",
"category": "Procedural & Long-Running",
"status": "Completed"
},
{
"title": "The Night Agent",
"category": "Procedural & Long-Running",
"status": "Completed"
},
{
"title": "The Lincoln Lawyer",
"category": "Procedural & Long-Running",
"status": "Watching"
},
{
"title": "A Man on the Inside",
"category": "Procedural & Long-Running",
"status": "Completed"
},
{
"title": "Daredevil",
"favorite": true,
"category": "Marvel Universe",
"status": "Completed",
"image": "/images/tv/daredevil.jpg"
},
{
"title": "Jessica Jones",
"category": "Marvel Universe",
"status": "Completed"
},
{
"title": "Luke Cage",
"category": "Marvel Universe",
"status": "Completed"
},
{
"title": "Iron Fist",
"category": "Marvel Universe",
"status": "Completed"
},
{
"title": "The Punisher",
"category": "Marvel Universe",
"status": "Completed"
},
{
"title": "The Defenders",
"category": "Marvel Universe",
"status": "Completed"
},
{
"title": "WandaVision",
"category": "Marvel Universe",
"status": "Completed"
},
{
"title": "Loki",
"category": "Marvel Universe",
"status": "Completed"
},
{
"title": "What If...?",
"category": "Marvel Universe",
"status": "Completed"
},
{
"title": "Hawkeye",
"category": "Marvel Universe",
"status": "Completed"
},
{
"title": "Moon Knight",
"category": "Marvel Universe",
"status": "Completed"
},
{
"title": "Ms. Marvel",
"category": "Marvel Universe",
"status": "Completed"
},
{
"title": "She-Hulk",
"category": "Marvel Universe",
"status": "Completed"
},
{
"title": "Secret Invasion",
"category": "Marvel Universe",
"status": "Completed"
},
{
"title": "Wonder Man",
"category": "Marvel Universe",
"status": "Completed"
},
{
"title": "Agatha All Along",
"category": "Marvel Universe",
"status": "Completed"
},
{
"title": "The Flash",
"category": "Marvel Universe",
"status": "Completed"
},
{
"title": "Supergirl",
"category": "Marvel Universe",
"status": "Completed"
},
{
"title": "Arrow",
"category": "Marvel Universe",
"status": "Completed"
},
{
"title": "Legends of Tomorrow",
"category": "Marvel Universe",
"status": "Completed"
},
{
"title": "The Mandalorian",
"favorite": true,
"category": "Star Wars Universe",
"status": "Completed",
"image": "/images/tv/the-mandalorian.jpg"
},
{
"title": "The Book of Boba Fett",
"category": "Star Wars Universe",
"status": "Completed"
},
{
"title": "Ahsoka",
"category": "Star Wars Universe",
"status": "Completed"
},
{
"title": "Obi-Wan Kenobi",
"category": "Star Wars Universe",
"status": "Completed"
},
{
"title": "Andor",
"category": "Star Wars Universe",
"status": "Completed"
},
{
"title": "Arcane",
"category": "Animation (Non-Anime)",
"status": "Completed"
},
{
"title": "Castlevania",
"favorite": true,
"category": "Animation (Non-Anime)",
"status": "Completed",
"image": "/images/tv/castlevania.jpg"
},
{
"title": "Blood of Zeus",
"category": "Animation (Non-Anime)",
"status": "Completed"
},
{
"title": "Rick and Morty",
"category": "Animation (Non-Anime)",
"status": "Completed"
},
{
"title": "Archer",
"category": "Animation (Non-Anime)",
"status": "Completed"
},
{
"title": "Futurama",
"category": "Animation (Non-Anime)",
"status": "Completed"
},
{
"title": "Cyberpunk: Edgerunners",
"category": "Animation (Non-Anime)",
"status": "Completed"
},
{
"title": "The Great British Baking Show",
"favorite": true,
"category": "Reality & Competition",
"status": "Completed",
"image": "/images/tv/the-great-british-baking-show.jpg"
},
{
"title": "Hell's Kitchen",
"category": "Reality & Competition",
"status": "Completed"
},
{
"title": "MasterChef",
"category": "Reality & Competition",
"status": "Completed"
},
{
"title": "Kitchen Nightmares",
"category": "Reality & Competition",
"status": "Completed"
}
]
+167
View File
@@ -0,0 +1,167 @@
[
{
"title": "Brandon Sanderson — Cosmere",
"category": "Books",
"status": "Complete",
"favorite": true,
"blurb": "Complete. Every novel, every era. The gold standard for me.",
"image": "/images/reading/brandon-sanderson-cosmere.jpg"
},
{
"title": "Frank Herbert — Dune",
"category": "Books",
"status": "Complete",
"favorite": true,
"blurb": "Books 1-3 complete.",
"image": "/images/reading/frank-herbert-dune.jpg"
},
{
"title": "J.R.R. Tolkien — The Lord of the Rings",
"category": "Books",
"status": "Listening",
"favorite": true,
"blurb": "Rereading by listening to the Andy Serkis audiobooks.",
"image": "/images/reading/j-r-r-tolkien-the-lord-of-the-rings.jpg"
},
{
"title": "Douglas Preston & Lincoln Child — Pendergast",
"category": "Books",
"favorite": true,
"status": "Rereading",
"blurb": "Currently rereading. Up to Cabinet of Curiosities.",
"image": "/images/reading/douglas-preston-lincoln-child-pendergast.jpg"
},
{
"title": "Sarah J. Maas — Throne of Glass",
"category": "Books",
"status": "Reading",
"blurb": "Currently tandem reading Heir of Fire (Book 3) and Tower of Dawn (Book 4)."
},
{
"title": "Emily Rodda — Deltora Quest",
"category": "Books",
"favorite": true,
"status": "Reread-pending",
"blurb": "Planning to listen to audiobooks to reread.",
"image": "/images/reading/emily-rodda-deltora-quest.jpg"
},
{
"title": "Fonda Lee — Green Bone Saga",
"category": "Books",
"status": "Next",
"blurb": "Next series to read."
},
{
"title": "The Hobbit",
"category": "Books",
"status": "Complete"
},
{
"title": "That Time I Got Reincarnated as a Slime",
"category": "Light Novels",
"status": "Reading",
"note": "Read up to Volume 13 before, planning to finish now that it is complete. 21/23 volumes."
},
{
"title": "Overlord",
"category": "Light Novels",
"status": "Reading",
"note": "16/18 volumes."
},
{
"title": "Re:Zero",
"category": "Light Novels",
"status": "Reading",
"note": "26-28/44+ volumes."
},
{
"title": "The King's Avatar",
"category": "Light Novels",
"favorite": true,
"status": "Complete",
"blurb": "A pro gamer gets forced out of his team and climbs back to the top from scratch. I've consumed it in every format: novel, animation, live-action. The esports scene is immaculately detailed.",
"note": "Read fully. Also seen all animation and live-action shows.",
"image": "/images/reading/the-king-s-avatar.jpg"
},
{
"title": "Solo Leveling",
"category": "Webnovels",
"favorite": true,
"status": "Complete",
"blurb": "The weakest hunter gets a system and refuses to waste it. The 'level up' loop is pure dopamine and the manhwa art is gorgeous. Gobbled it up in every format.",
"note": "Korean webnovel turned manhwa. Consumed in all formats.",
"image": "/images/reading/solo-leveling.jpg"
},
{
"title": "The God of High School",
"category": "Webtoons",
"status": "Complete",
"note": "Also watched the anime."
},
{
"title": "Noblesse",
"category": "Webtoons",
"status": "Complete",
"note": "Also watched the anime."
},
{
"title": "Tower of God",
"category": "Webtoons",
"status": "Complete",
"note": "Also watched the anime."
},
{
"title": "Tokyo Revengers",
"category": "Manga & Manhwa",
"status": "Complete",
"note": "Finished the manga. Also watched the anime."
},
{
"title": "Wistoria: Wand and Sword",
"category": "Manga & Manhwa",
"status": "New",
"note": "Recently added to the reading list. After finishing season 2."
},
{
"title": "Omniscient Reader's Viewpoint",
"category": "Webnovels",
"status": "Complete",
"favorite": true,
"blurb": "The sole reader of a dying webnovel watches it become reality, and he's the only one who knows how the story ends. It's about stories, friendship, and what you'd sacrifice to keep a narrative alive. One of the best things I've read, full stop.",
"image": "/images/reading/omniscient-reader-s-viewpoint.jpg"
},
{
"title": "Overgeared",
"category": "Webnovels",
"status": "Complete",
"favorite": true,
"image": "/images/reading/overgeared.jpg",
"blurb": "Started rough and became an obsession. Grid is the most frustratingly lovable lead; watching the 'worst legendary class' snowball into something absurd never gets old. The crafting and base-building scratch my optimization itch hard."
},
{
"title": "Desolate Era (I Eat Tomatoes)",
"category": "Webnovels",
"status": "Complete",
"favorite": true,
"image": "/images/reading/desolate-era.jpg",
"blurb": "Classic xianxia cultivation done right. Ji Ning's rebirth into a brutal cultivation world hooked me immediately. The power progression actually feels earned, and the world just keeps opening up."
},
{
"title": "Lord Xue Ying (I Eat Tomatoes)",
"category": "Webnovels",
"status": "Complete",
"note": "Same author as Desolate Era. Spear-wielding prodigy in a cultivation world."
},
{
"title": "Stellar Transformation (I Eat Tomatoes)",
"category": "Webnovels",
"status": "Complete",
"note": "Same universe as Coiling Dragon. Cultivation through stellar tiers."
},
{
"title": "Coiling Dragon (I Eat Tomatoes)",
"category": "Webnovels",
"status": "Complete",
"note": "The breakout hit. Linley's rise through a cultivation world. Where I started with I Eat Tomatoes."
}
]
+158
View File
@@ -0,0 +1,158 @@
/**
* media.ts
* ---------------------------------------------------------------------------
* Shared types and helpers for the media viewing-history feature. Three media
* kinds (anime, tv, movies) plus reading live as JSON files in `src/data/`
* (and `src/data/media/`), following the `boardgames.json` precedent.
*
* Each item uses one flexible shape: a `favorite` flag is the single source of
* truth for what shows in the top "Favorites" grid (no duplicate list to keep
* in sync), and `category` drives the grouping of the full library below.
*
* The JSON is the source of truth — these helpers just read it, filter, and
* sort. Run the matching `scripts/fetch-*-art.mjs` tool to populate the
* optional `image` field for favorites; the site serves local files from
* `public/images/`, so no external API is hit at runtime.
*/
import moviesData from '@/data/media/movies.json';
import tvData from '@/data/media/tv.json';
import animeData from '@/data/media/anime.json';
import readingData from '@/data/reading.json';
/** A single viewing-history or reading entry. Most fields optional. */
export interface MediaItem {
/** Display title, e.g. "Breaking Bad" or "The Matrix". */
title: string;
/** True → shown in the top "Favorites" grid as a rich card. */
favorite?: boolean;
/** Grouping label for the full library, e.g. "Prestige Drama". */
category?: string;
/** Consumption state pill, e.g. "Completed", "Watching", "Caught up". */
status?: 'Completed' | 'Watching' | 'Caught up' | string;
/** Release year (movies primarily). */
year?: number;
/** Human-readable span for a franchise, e.g. "All 4 films". */
count?: string;
/** Franchise constituent titles (movies franchises). */
films?: string[];
/** Long-form why-I-love-it copy shown on favorite cards. */
blurb?: string;
/** Short detail shown next to a full-list pill, e.g. "21/23 volumes". */
note?: string;
/** Local public path to poster/cover art, filled by the fetcher scripts. */
image?: string;
}
/** The four kinds backed by JSON data. */
export type MediaKind = 'anime' | 'tv' | 'movies' | 'reading';
const dataByKind: Record<MediaKind, MediaItem[]> = {
anime: animeData as MediaItem[],
tv: tvData as MediaItem[],
movies: moviesData as MediaItem[],
reading: readingData as MediaItem[],
};
/** Per-page metadata so each `.astro` page stays thin. */
export interface MediaKindMeta {
kind: MediaKind;
/** mdi icon for the hero mark + hub card. */
icon: string;
/** Page heading. */
title: string;
/** Italic pull-quote tagline. */
tagline: string;
/** One-paragraph overview rendered under the hero. */
overview: string;
/** Hero banner image (public path) — vibe-matched stock; swap freely. */
heroImage?: string;
/** Alt text for the hero image. */
heroImageAlt?: string;
}
export const mediaKinds: Record<MediaKind, MediaKindMeta> = {
anime: {
kind: 'anime',
icon: 'animation-play',
title: 'Anime',
tagline: 'Subbed, dubbed, and somehow still behind.',
overview:
"I don't dabble in anime. I live here. 150+ completed series and counting, and this isn't a side shelf next to my live-action watching. Shonen, isekai, dark psychological, whatever Frieren is. If it's mattered in the medium over the last couple decades, I've probably seen it. The backlog is its own final boss at this point.",
heroImage: '/images/anime-hero.jpg',
heroImageAlt: 'A cosplayer in a colorful anime-inspired costume at a convention',
},
tv: {
kind: 'tv',
icon: 'television-classic',
title: 'TV Shows',
tagline: 'My rewatch rotation has a rewatch rotation.',
overview:
"I watch TV like it's my job. 200+ completed series across decades and every platform that's mattered: the HBO prestige-drama run, the NBC comedy golden age, the streaming-original explosion, and the full Marvel and Star Wars rosters. If it was the show everyone was talking about, I was watching it. My comfort-rewatch rotation never stops growing.",
heroImage: '/images/tv-hero.jpg',
heroImageAlt: 'A cozy living room with a couch facing a TV',
},
movies: {
kind: 'movies',
icon: 'movie-open',
title: 'Movies',
tagline: 'I stayed for the post-credits scene.',
overview:
"When I love a franchise, I commit all the way: every film, every spinoff, every post-credits scene. 160+ films across 17+ franchises. But the real favorites are the ones I'll stop on any time they cross my path. The Princess Bride's quotability, The Matrix bending my brain, the MCU at its decade-long peak. Those are the comfort rewatches.",
heroImage: '/images/movies-hero.jpg',
heroImageAlt: 'Empty red seats in a vintage movie theater',
},
reading: {
kind: 'reading',
icon: 'book-open-page-variant',
title: 'Reading',
tagline: 'Perpetually three books ahead of my to-read list.',
overview:
"I read like it's a competitive sport. Fantasy is my native language. Brandon Sanderson's Cosmere is fully consumed and I am eagerly awaiting the next era. Frank Herbert's Dune trilogy shaped how I think about worldbuilding. Douglas Preston & Lincoln Child's Pendergast saga showed me that a well-crafted narrative can keep me engaged outside of a Tolkien-esque fantasy world. And I'm currently working through Sarah J. Maas's Throne of Glass series. Beyond novels, I read light novels, comic books, manhwa, and manga. If a story exists in any format, I'm probably consuming it in some capacity.",
heroImage: '/images/reading-nook.jpg',
heroImageAlt: 'A cozy reading nook with shelves of books',
},
};
/**
* Favorites for a kind: items flagged `favorite: true`, sorted alphabetically
* by title (stable, predictable order across renders).
*/
export function getFavorites(kind: MediaKind): MediaItem[] {
return dataByKind[kind]
.filter((item) => item.favorite)
.sort((a, b) => a.title.localeCompare(b.title));
}
export interface MediaGroup {
category: string;
items: MediaItem[];
}
/**
* Full library for a kind, grouped by `category` in order of first appearance
* (so the JSON's authored ordering is preserved). Items without a category are
* omitted — favorites-without-category belong only to the Favorites grid.
*/
export function getGrouped(kind: MediaKind): MediaGroup[] {
const groups: MediaGroup[] = [];
const indexByCategory = new Map<string, number>();
for (const item of dataByKind[kind]) {
if (!item.category) continue;
let idx = indexByCategory.get(item.category);
if (idx === undefined) {
idx = groups.length;
indexByCategory.set(item.category, idx);
groups.push({ category: item.category, items: [] });
}
groups[idx].items.push(item);
}
return groups;
}
/** Total count of entries for a kind (favorites + full library, deduped by title). */
export function getCount(kind: MediaKind): number {
const titles = new Set<string>();
for (const item of dataByKind[kind]) titles.add(item.title.toLowerCase());
return titles.size;
}
+118
View File
@@ -0,0 +1,118 @@
---
/**
* anime.astro — Anime
* ---------------------------------------------------------------------------
* The primary pillar: 150+ completed series. Favorites render as poster cards
* up top (populated by scripts/fetch-anime-art.mjs); the full library is
* grouped by category below. Follows the canonical interest-page template.
*/
import BaseLayout from "@layouts/BaseLayout.astro";
import Reveal from "@components/motion/Reveal.astro";
import InterestHero from "@components/layout/InterestHero.astro";
import MediaCard from "@components/cards/MediaCard.astro";
import CategoryList from "@components/media/CategoryList.astro";
import SectionHeader from "@components/layout/SectionHeader.astro";
import RelatedInterests from "@components/layout/RelatedInterests.astro";
import Placeholder from "@components/layout/Placeholder.astro";
import { getFavorites, getGrouped, mediaKinds, getCount } from "@lib/media";
const meta = mediaKinds.anime;
const favorites = getFavorites("anime");
const groups = getGrouped("anime");
const count = getCount("anime");
const related = [
{
href: "/interests/tv",
title: "TV Shows",
icon: "television-classic",
blurb: "When I step away from animation, the live-action backlog is waiting.",
},
{
href: "/interests/reading",
title: "Reading",
icon: "book-open-page-variant",
blurb: "Half of these have light novels and manga I'm reading too.",
},
];
---
<BaseLayout
surface="interest"
title={meta.title}
description={`${count}+ completed anime series. Shonen, isekai, dark psychological. The primary pillar of my watchlist, not a side shelf.`}
>
<InterestHero
icon={meta.icon}
title={meta.title}
tagline={meta.tagline}
image={meta.heroImage}
imageAlt={meta.heroImageAlt}
/>
<div class="container-prose flex flex-col gap-20 py-12 sm:py-16">
{/* Overview */}
<section
class="flex flex-col gap-5 text-lg leading-relaxed"
style="color: var(--color-text-muted);"
>
<Reveal><p>{meta.overview}</p></Reveal>
</section>
{/* Favorites */}
<section class="flex flex-col gap-6">
<Reveal
><SectionHeader
kicker="The rewatch pile"
icon="star"
title="Favorites"
/></Reveal
>
{
favorites.length > 0 ? (
<div class="grid gap-5 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
{favorites.map((item, i) => (
<Reveal delay={i * 70}>
<MediaCard
kind="anime"
title={item.title}
image={item.image}
status={item.status}
blurb={item.blurb}
/>
</Reveal>
))}
</div>
) : (
<Placeholder
label="Anime favorites"
hint='Mark items with "favorite": true in src/data/media/anime.json.'
/>
)
}
</section>
{/* Full library */}
<section class="flex flex-col gap-6">
<Reveal
><SectionHeader
kicker={`${count}+ and counting`}
icon="format-list-bulleted"
title="The full watch history"
/></Reveal
>
<Reveal delay={80}>
{groups.length > 0 ? (
<CategoryList groups={groups} pillIcon="play-circle" />
) : (
<Placeholder
label="Anime library"
hint="Add categorized items to src/data/media/anime.json."
/>
)}
</Reveal>
</section>
<RelatedInterests items={related} />
</div>
</BaseLayout>
+32 -2
View File
@@ -38,13 +38,43 @@ const interests = [
{
href: "/interests/reading",
icon: "book-open-page-variant",
title: "Reading & Media",
title: "Reading",
tagline: "Perpetually three books ahead of my to-read list.",
blurb:
"The Cosmere, Dune, the Pendergast saga, and Throne of Glass. Plus light novels, manga, manhwa, anime, and film.",
"The Cosmere, Dune, the Pendergast saga, and Throne of Glass. Plus light novels, manga, manhwa, and webtoons.",
image: "/images/fantasy-books.jpg",
alt: "Fantasy bookshelf",
},
{
href: "/interests/anime",
icon: "animation-play",
title: "Anime",
tagline: "Subbed, dubbed, and somehow still behind.",
blurb:
"150+ completed series. The shonen classics, the modern hits, the isekai rabbit hole, and Frieren living rent-free in my head.",
image: "/images/anime-hero.jpg",
alt: "A cosplayer in a colorful anime-inspired costume at a convention",
},
{
href: "/interests/tv",
icon: "television-classic",
title: "TV Shows",
tagline: "My rewatch rotation has a rewatch rotation.",
blurb:
"200+ series across every prestige drama, NBC comedy, streaming original, and the full Marvel and Star Wars rosters.",
image: "/images/tv-hero.jpg",
alt: "A cozy living room with a couch facing a TV",
},
{
href: "/interests/movies",
icon: "movie-open",
title: "Movies",
tagline: "I stayed for the post-credits scene.",
blurb:
"160+ films across 17+ franchises. When I love a series I commit all the way, and The Princess Bride is always within reach.",
image: "/images/movies-hero.jpg",
alt: "Empty red seats in a vintage movie theater",
},
{
href: "/interests/making",
icon: "palette",
+119
View File
@@ -0,0 +1,119 @@
---
/**
* movies.astro — Movies
* ---------------------------------------------------------------------------
* 160+ films across 17+ franchises. Favorite films render as poster cards;
* the full watch history below lists every film grouped by franchise (plus a
* Standalone group for single films). Posters populated by
* scripts/fetch-tmdb-art.mjs.
*/
import BaseLayout from "@layouts/BaseLayout.astro";
import Reveal from "@components/motion/Reveal.astro";
import InterestHero from "@components/layout/InterestHero.astro";
import MediaCard from "@components/cards/MediaCard.astro";
import CategoryList from "@components/media/CategoryList.astro";
import SectionHeader from "@components/layout/SectionHeader.astro";
import RelatedInterests from "@components/layout/RelatedInterests.astro";
import Placeholder from "@components/layout/Placeholder.astro";
import { getFavorites, getGrouped, mediaKinds, getCount } from "@lib/media";
const meta = mediaKinds.movies;
const favorites = getFavorites("movies");
const groups = getGrouped("movies");
const count = getCount("movies");
const related = [
{
href: "/interests/tv",
title: "TV Shows",
icon: "television-classic",
blurb: "The MCU and Star Wars sagas continue here.",
},
{
href: "/interests/anime",
title: "Anime",
icon: "animation-play",
blurb: "The animated half of my watchlist.",
},
];
---
<BaseLayout
surface="interest"
title={meta.title}
description={`${count}+ films across 17+ franchises. When I love a series I commit all the way, and The Princess Bride is always within reach.`}
>
<InterestHero
icon={meta.icon}
title={meta.title}
tagline={meta.tagline}
image={meta.heroImage}
imageAlt={meta.heroImageAlt}
/>
<div class="container-prose flex flex-col gap-20 py-12 sm:py-16">
{/* Overview */}
<section
class="flex flex-col gap-5 text-lg leading-relaxed"
style="color: var(--color-text-muted);"
>
<Reveal><p>{meta.overview}</p></Reveal>
</section>
{/* Favorites */}
<section class="flex flex-col gap-6">
<Reveal
><SectionHeader
kicker="Stop on these any time"
icon="star"
title="Favorites"
/></Reveal
>
{
favorites.length > 0 ? (
<div class="grid gap-5 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
{favorites.map((item, i) => (
<Reveal delay={i * 70}>
<MediaCard
kind="movies"
title={item.title}
image={item.image}
year={item.year}
blurb={item.blurb}
/>
</Reveal>
))}
</div>
) : (
<Placeholder
label="Movie favorites"
hint='Mark items with "favorite": true in src/data/media/movies.json.'
/>
)
}
</section>
{/* Full franchise watch history */}
<section class="flex flex-col gap-6">
<Reveal
><SectionHeader
kicker={`${count}+ films across ${groups.length} franchises`}
icon="filmstrip"
title="By franchise"
/></Reveal
>
<Reveal delay={80}>
{groups.length > 0 ? (
<CategoryList groups={groups} pillIcon="movie-open" />
) : (
<Placeholder
label="Movie library"
hint="Add categorized films to src/data/media/movies.json."
/>
)}
</Reveal>
</section>
<RelatedInterests items={related} />
</div>
</BaseLayout>
+62 -163
View File
@@ -1,92 +1,33 @@
---
/**
* reading.astro — Reading & Media
* reading.astro — Reading
* ---------------------------------------------------------------------------
* Per-interest template with the spec's reading highlights table (status pills
* mapped from spec emoji statuses), anime favorites, and movies/TV note.
* Books, comics, light novels, webtoons, and manga/manhwa. Favorites render as
* cover cards up top (populated by scripts/fetch-book-covers.mjs); the full
* library is grouped by category below. Follows the canonical interest-page
* template. The anime and movies/TV content that used to live here now has its
* own pages under /interests/anime, /tv, and /movies.
*/
import BaseLayout from "@layouts/BaseLayout.astro";
import Reveal from "@components/motion/Reveal.astro";
import InterestHero from "@components/layout/InterestHero.astro";
import InterestCard from "@components/cards/InterestCard.astro";
import MediaCard from "@components/cards/MediaCard.astro";
import CategoryList from "@components/media/CategoryList.astro";
import SectionHeader from "@components/layout/SectionHeader.astro";
import RelatedInterests from "@components/layout/RelatedInterests.astro";
import Icon from "@components/Icon.astro";
import Placeholder from "@components/layout/Placeholder.astro";
import { getFavorites, getGrouped, mediaKinds } from "@lib/media";
// Spec reading highlights, with status mapped from the spec's emoji statuses.
const reading = [
{
icon: "check-decagram",
title: "Brandon Sanderson — Cosmere",
status: "Complete",
body: "Complete. Every novel, every era. The gold standard for me.",
},
{
icon: "check-decagram",
title: "Frank Herbert — Dune",
status: "Complete",
body: "Books 1-3 complete.",
},
{
icon: "bookmark",
title: "Emily Rodda — Deltora Quest",
status: "Reread-pending",
body: "Planning to listen to audiobooks to reread.",
},
{
icon: "volume-high",
title: "J.R.R. Tolkein — The Lord of the Rings",
status: "Listening",
body: "Rereading by listening to the Andy Serkis audiobooks.",
},
{
icon: "autorenew",
title: "Douglas Preston & Lincoln Child — Pendergast",
status: "Rereading",
body: "Currently rereading. Up to Cabinet of Curiosities.",
},
{
icon: "autorenew",
title: "Sarah J. Maas — Throne of Glass",
status: "Reading",
body: "Currently Tandem reading Heir of Fire (Book 3) and Tower of Dawn (Book 4).",
},
{
icon: "bookmark",
title: "Fonda Lee — Green Bone Saga",
status: "Next",
body: "Next Series to Read.",
},
{
icon: "bookmark",
title: "That Time I Got Reincarnated as a Slime",
status: "New",
body: "Read up to Volume 13 before, planning to finish now that it is complete.",
},
{
icon: "new-box",
title: "Wistoria: Wand and Sword",
status: "New",
body: "Recently added to the reading list. After finishing season 2",
},
];
const anime = [
"One Piece",
"Bleach",
"Gundam (all series)",
"Code Geass",
"Magi",
"That Time I Got Reincarnated as a Slime",
"Overlord",
];
const meta = mediaKinds.reading;
const favorites = getFavorites("reading");
const groups = getGrouped("reading");
const related = [
{
href: "/interests/ttrpgs",
title: "TTRPGs & Tabletop",
icon: "dice-multiple",
blurb: "Worldbuilding inspiration flows both ways.",
href: "/interests/anime",
title: "Anime",
icon: "animation-play",
blurb: "A lot of these light novels and manga have adaptations I've watched.",
},
{
href: "/interests/making",
@@ -99,15 +40,15 @@ const related = [
<BaseLayout
surface="interest"
title="Reading & Media"
description="The Cosmere, Dune, the Pendergast saga, Throne of Glass. Plus light novels, manga, manhwa, anime, and film. If a story exists, I am probably consuming it."
title={meta.title}
description="The Cosmere, Dune, the Pendergast saga, Throne of Glass. Plus light novels, manga, manhwa, and webtoons. If a story exists, I am probably consuming it."
>
<InterestHero
icon="book-open-page-variant"
title="Reading & Media"
tagline="Perpetually three books ahead of my to-read list."
image="/images/reading-nook.jpg"
imageAlt="A cozy reading nook with shelves of books"
icon={meta.icon}
title={meta.title}
tagline={meta.tagline}
image={meta.heroImage}
imageAlt={meta.heroImageAlt}
/>
<div class="container-prose flex flex-col gap-20 py-12 sm:py-16">
@@ -116,102 +57,60 @@ const related = [
class="flex flex-col gap-5 text-lg leading-relaxed"
style="color: var(--color-text-muted);"
>
<Reveal
><p>
I read like it's a competitive sport. Fantasy is my native language,
Brandon Sanderson's Cosmere is fully consumed and I am eagerly
awaiting the next era. Frank Herbert's Dune trilogy shaped how I think
about worldbuilding. Douglas Preston & Lincoln Child's Pendergast saga
showed me that a well-crafted narrative can keep me engaged outside of
a Tolkein-esque fantasy world. And I'm currently working through Sarah
J. Maas's Throne of Glass series.
</p></Reveal
>
<Reveal delay={80}
><p>
Beyond novels, I read light novels, comic books, manhwa, and manga. I
watch anime and film extensively. If a story exists in any format, I'm
probably consuming it in some capacity.
</p></Reveal
>
<Reveal><p>{meta.overview}</p></Reveal>
</section>
{/* Reading highlights */}
{/* Favorites */}
<section class="flex flex-col gap-6">
<Reveal
><SectionHeader
kicker="On the shelf"
icon="bookshelf"
title="Reading highlights"
title="Favorites"
/></Reveal
>
<div class="grid gap-5 sm:grid-cols-2 lg:grid-cols-3">
{
reading.map((r, i) => (
<Reveal delay={i * 70}>
<InterestCard icon={r.icon} title={r.title} status={r.status}>
{r.body}
</InterestCard>
</Reveal>
))
}
</div>
{
favorites.length > 0 ? (
<div class="grid gap-5 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
{favorites.map((item, i) => (
<Reveal delay={i * 70}>
<MediaCard
kind="reading"
title={item.title}
image={item.image}
status={item.status}
blurb={item.blurb}
/>
</Reveal>
))}
</div>
) : (
<Placeholder
label="Reading favorites"
hint='Mark items with "favorite": true in src/data/reading.json.'
/>
)
}
</section>
{/* Anime */}
<section class="flex flex-col gap-5">
{/* Full library */}
<section class="flex flex-col gap-6">
<Reveal
><SectionHeader
kicker="Animated"
icon="television-classic"
title="Anime favorites"
kicker="The rest of the shelf"
icon="format-list-bulleted"
title="What I'm reading"
/></Reveal
>
<Reveal delay={80}>
<ul class="flex flex-wrap gap-3">
{
anime.map((a) => (
<li class="pill text-sm">
<Icon name="play-circle" class="h-3.5 w-3.5" />
{a}
</li>
))
}
</ul>
</Reveal>
</section>
{/* Movies & TV */}
<section class="flex flex-col gap-5">
<Reveal
><SectionHeader
kicker="Beyond the page"
icon="movie-open"
title="Movies & TV"
/></Reveal
>
<Reveal delay={80}>
<div class="grid gap-5 sm:grid-cols-[1fr_18rem]">
<blockquote
class="border-l-2 pl-5 text-lg leading-relaxed"
style="border-color: var(--color-secondary); color: var(--color-text-muted);"
>
Watches extensively across genres. Particularly drawn to sci-fi,
fantasy, and animated film.
</blockquote>
<figure
class="relative overflow-hidden rounded-xl border"
style="border-color: color-mix(in srgb, var(--color-accent) 35%, transparent);"
>
<img
src="/images/fantasy-books.jpg"
alt="Fantasy and media collection"
loading="lazy"
decoding="async"
class="aspect-[4/3] w-full object-cover"
/>
</figure>
</div>
{groups.length > 0 ? (
<CategoryList groups={groups} pillIcon="book-open-page-variant" />
) : (
<Placeholder
label="Reading library"
hint="Add categorized items to src/data/reading.json."
/>
)}
</Reveal>
</section>
+118
View File
@@ -0,0 +1,118 @@
---
/**
* tv.astro — TV Shows
* ---------------------------------------------------------------------------
* 200+ completed series across prestige drama, streaming originals, workplace
* comedy, procedural, Marvel/Star Wars universes, animation, and reality.
* Favorites as poster cards; full library grouped by category below.
*/
import BaseLayout from "@layouts/BaseLayout.astro";
import Reveal from "@components/motion/Reveal.astro";
import InterestHero from "@components/layout/InterestHero.astro";
import MediaCard from "@components/cards/MediaCard.astro";
import CategoryList from "@components/media/CategoryList.astro";
import SectionHeader from "@components/layout/SectionHeader.astro";
import RelatedInterests from "@components/layout/RelatedInterests.astro";
import Placeholder from "@components/layout/Placeholder.astro";
import { getFavorites, getGrouped, mediaKinds, getCount } from "@lib/media";
const meta = mediaKinds.tv;
const favorites = getFavorites("tv");
const groups = getGrouped("tv");
const count = getCount("tv");
const related = [
{
href: "/interests/anime",
title: "Anime",
icon: "animation-play",
blurb: "The other half of my watchlist, honestly.",
},
{
href: "/interests/movies",
title: "Movies",
icon: "movie-open",
blurb: "The MCU, the Matrix, and everything between.",
},
];
---
<BaseLayout
surface="interest"
title={meta.title}
description={`${count}+ completed series across prestige drama, workplace comedy, streaming originals, and the full Marvel and Star Wars rosters.`}
>
<InterestHero
icon={meta.icon}
title={meta.title}
tagline={meta.tagline}
image={meta.heroImage}
imageAlt={meta.heroImageAlt}
/>
<div class="container-prose flex flex-col gap-20 py-12 sm:py-16">
{/* Overview */}
<section
class="flex flex-col gap-5 text-lg leading-relaxed"
style="color: var(--color-text-muted);"
>
<Reveal><p>{meta.overview}</p></Reveal>
</section>
{/* Favorites */}
<section class="flex flex-col gap-6">
<Reveal
><SectionHeader
kicker="On heavy rotation"
icon="star"
title="Favorites"
/></Reveal
>
{
favorites.length > 0 ? (
<div class="grid gap-5 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
{favorites.map((item, i) => (
<Reveal delay={i * 70}>
<MediaCard
kind="tv"
title={item.title}
image={item.image}
status={item.status}
blurb={item.blurb}
/>
</Reveal>
))}
</div>
) : (
<Placeholder
label="TV favorites"
hint='Mark items with "favorite": true in src/data/media/tv.json.'
/>
)
}
</section>
{/* Full library */}
<section class="flex flex-col gap-6">
<Reveal
><SectionHeader
kicker={`${count}+ series`}
icon="format-list-bulleted"
title="Everything I've finished"
/></Reveal
>
<Reveal delay={80}>
{groups.length > 0 ? (
<CategoryList groups={groups} pillIcon="television-classic" />
) : (
<Placeholder
label="TV library"
hint="Add categorized items to src/data/media/tv.json."
/>
)}
</Reveal>
</section>
<RelatedInterests items={related} />
</div>
</BaseLayout>