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)