Presentation-only card component for programs (podcasts, webinars, events) supporting three density modes: `default`, `sm` (compact), and `portrait` (vertical rail). ## Key Components ### Exports - **`ProgramCard`** — Generic card component accepting any `BaseProgramItem` subtype; delegates to density-specific render branches - **`ProgramCardSkeleton`** — Animated loading placeholder matching `default` or `sm` layout - **`ProgramCardProps`** — Interface defining all card inputs ### Internal Utilities - **`formatUtc(date, fmt)`** — UTC-pinned `date-fns` formatter that prevents React #418 hydration mismatches between server (UTC) and client (local) timezones - **`getHosts(hosts)`** — Normalizes `ProgramHost[]` or raw JSON string into `{ name, avatar }[]` - **`MediaGallery`** — Horizontally scrollable thumbnail strip with `ImageGalleryModal` lightbox on click ### Density Modes | Size | Layout | Use Case | |---|---|---| | `default` | Wide horizontal with full detail | Archive/listing pages | | `sm` | Compact horizontal row | Chat-inline cards | | `portrait` | Vertical via `EntityPortraitCard` | Mixed-content rails | ## Usage Example ```typescript import { ProgramCard, ProgramCardSkeleton } from './program-card' import { podcastConfig } from '../config/podcast-config' // Loading state if (isLoading) return // Default density // Portrait for mixed-content rail ``` > The card writes no click logic — callers wrap or pass `href` directly. Use `target="_blank"` with `rel="noopener noreferrer"` for cross-platform or embed contexts.