Pure presentation component for rendering MSP case study cards in three density variants: `default` (vertical detail), `sm` (compact horizontal for chat-inline), and `portrait` (rail/strip layout via `EntityPortraitCard`). ## Key Components ### `CaseStudyCard` The primary export. Renders a case study in one of three layouts based on the `size` prop. Contains no click logic — callers supply `href` directly and wrap with their own anchor. Image resolution follows the fallback chain: `study.featured_image` → `placeholderUrl` → `bg-ods-bg` background. ### `CaseStudyCardSkeleton` Loading placeholder that mirrors the real card's layout zones for each density. The `default` and `portrait` variants share the same skeleton shape (media aspect ratio → 72px title zone → 60px person footer). ### Props (`CaseStudyCardProps`) | Prop | Type | Description | |---|---|---| | `study` | `CaseStudy` | Case study data object | | `href` | `string` | Resolved detail URL (caller-supplied) | | `target` | `'_blank'` | Optional new-tab behavior | | `placeholderUrl` | `string \| null` | OG placeholder when `featured_image` is absent | | `size` | `'default' \| 'sm' \| 'portrait'` | Card density variant | | `showTypeBadge` | `boolean` | Portrait only — shows "Case Study" chip in mixed-type rails | ## Usage Example ```typescript import { CaseStudyCard, CaseStudyCardSkeleton } from './case-study-card' import { useOgPlaceholderUrl } from '../hooks/use-og-placeholder-url' function CaseStudyRail({ studies, isLoading }) { const placeholderUrl = useOgPlaceholderUrl({ aspect: 'wide' }) if (isLoading) { return } return studies.map((study) => ( )) } ``` **Source:** [`case-study-card.tsx`](https://github.com/flamingo-stack/openframe-oss-lib/blob/main/case-study-card.tsx)