Shared portrait-density card anatomy used by all entity card types (`size="portrait"`), encoding media zone, title zone, and footer zone layout exactly once to prevent per-card drift. ## Key Components ### Interfaces - **`EntityPortraitPerson`** — Author/person data: `name`, optional `avatarUrl`, `subtitle`, and `iconOverlayUrl` (small round overlay on avatar bottom-right, e.g. MSP company icon) - **`EntityPortraitCardProps`** — Full card props: `href`, `typeLabel` (content-type chip), `imageUrl`, `placeholderUrl`, `title`, `titleClassName` (typography override per entity), `person`, and `className` ### Component - **`EntityPortraitCard`** — Renders three fixed anatomical zones inside a hover-styled `Card`: - **Media zone** (`aspect-[1200/630]`): Wide covers fill `object-cover`; non-wide covers (e.g. square podcast art) render `object-contain` on an edge-sampled background color via `useImageEdgeColor` - **Title zone** (`h-[72px]`): Optional `StatusBadge` eyebrow chip + 2- or 3-line clamped title with per-entity `titleClassName` - **Footer zone** (`h-[60px]`): Avatar (or initial fallback) + name + subtitle, with optional `iconOverlayUrl` overlay ### Constants - **`MIN_WIDE_RATIO`** (`1.3`) — Aspect ratio threshold distinguishing wide OG-style covers from square/portrait artwork ## Usage Example ```typescript import { EntityPortraitCard } from './entity-portrait-card' ``` ## Notes - The `typeLabel` prop should be omitted on single-type rails (all cards are the same content type) and passed only on mixed-type rails - Cover image resolution follows the shared `useCoverImageFallback` chain: real image → `placeholderUrl` → hidden - The footer zone renders an empty fixed-height box even when `person` is `null`, preserving baseline alignment across a rail