| Route | File | Collection | Template |
|---|---|---|---|
/ | index.astro | All 5 | BaseLayout + HeroSlider + SectionCarousel x6 |
/404 | 404.astro | — | BaseLayout (noIndex) |
/rss.xml | rss.xml.ts | All 5 | RSS endpoint (top 50, always excludes drafts) |
/{type}/ | {type}/index.astro | One | CategoryIndex with type-specific accent color |
/{year}/tank-talk/{slug}/ | [year]/tank-talk/[...slug].astro | tank-talk | WineryArticleTemplate |
/{year}/one-voice/{slug}/ | [year]/one-voice/[...slug].astro | one-voice | WineryArticleTemplate |
/{year}/raise-a-glass/{slug}/ | [year]/raise-a-glass/[...slug].astro | raise-a-glass | Inline template |
/{year}/uncorked/{slug}/ | [year]/uncorked/[...slug].astro | uncorked | Inline template |
/{year}/operation-storyteller/{slug}/ | [year]/operation-storyteller/[...slug].astro | operation-storyteller | Inline template |
/{year}/hot-off-the-vine/{slug}/ | [year]/hot-off-the-vine/[...slug].astro | hot-off-the-vine | Thin MDX wrapper |
All 6 follow the same pattern:
getCollection(type) with environment-aware draft filteringCategoryIndex componentEntity field per type: tank-talk/one-voice -> winery, uncorked -> venue, raise-a-glass -> company, hot-off-the-vine -> brand, operation-storyteller -> none
Accent colors (oklch): Tank Talk 0.55 0.02 75, One Voice 0.55 0.12 110, Uncorked 0.55 0.15 160, Raise a Glass 0.65 0.16 55, HOTV 0.48 0.16 10, Op Storyteller 0.55 0.15 290
All use [year]/collection/[...slug].astro:
getStaticPaths() generates routes from collection^\d{4}$import.meta.env.PROD ? !data.draft : truerender(entry)Tank Talk + One Voice -> WineryArticleTemplate (shared in src/components/article/templates/). Full interview layout: FeatureCover, ProfileCard, IntroSection, InterviewTimeline with slotted ProductShowcase and PhotoSliders, Featured Wines grid.
Raise a Glass + Uncorked -> Inline templates in page files. Same interview components (InterviewTimeline, ProfileCard, IntroSection) plus FindOurWinesAt. FeatureCover hero, career highlights (raise-a-glass only).
Operation Storyteller -> Inline template. Prose-first layout with author bio, category pill (6 color-coded categories), reading time, series info, related interviews, optional ProductShowcase.
Hot Off The Vine -> Thin MDX wrapper. BaseLayout + SmoothScroll + body background from themeColors.background + <Content />. All visual composition lives in the MDX file.
1. Page template: import.meta.glob("/src/content/{type}/**/assets/photo-slider-*/*.{jpg,...}")
2. resolvePhotoSliders() maps string paths -> ImageMetadata
3. Returns Record<sectionNumber, ResolvedSlider> passed to template/timeline
Glob must use static string literal per Vite constraint.
getCollection() x 5 (parallel) -> filter drafts -> toIndexArticle()
-> getHeroArticles(): 3 real + 4 placeholders = 7 hero cards
-> getSectionArticles(): per-section arrays padded with gradient placeholders
JsonLd component on all article pagesdata-pagefind-body on article content, data-pagefind-filter="type:...", data-pagefind-meta for title/description/thumbnailgetImage() at 200x200 webp, quality 60