<!-- source-hash: 35eb2dca49e5c1311632535a7cffdf8b -->
Pixel-perfect loading skeleton for `ProductReleaseCard` that prevents layout shift by mirroring the loaded card's exact dimensions in both `lg` and `sm` variants.

## Key Components

### `ProductReleaseCardSkeleton`

A client-side animated pulse skeleton component with two size branches:

| Branch | Matches | Structure |
|--------|---------|-----------|
| `lg` | `ProductReleaseCard size="lg"` | Hero image + version pill + title + summary + changelog strip + 4-cell metadata grid |
| `sm` | `ProductReleaseCard size="sm"` | Thumbnail + title row + date row + summary row |

### `ProductReleaseCardSkeletonProps`

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `className` | `string` | — | Additional CSS classes |
| `size` | `'lg' \| 'sm'` | `'lg'` | Must match the target card's `size` prop to prevent layout shift on resolve |

## Usage Example

```typescript
import { ProductReleaseCardSkeleton } from './product-release-card-skeleton'

// Full release card placeholder (e.g. in ReleasesList grid)
function ReleasesListLoading() {
  return (
    <div className="grid grid-cols-1 gap-4">
      {Array.from({ length: 5 }).map((_, i) => (
        <ProductReleaseCardSkeleton key={i} size="lg" />
      ))}
    </div>
  )
}

// Compact inline placeholder (e.g. inside a chat message)
function ChatReleaseLoading() {
  return <ProductReleaseCardSkeleton size="sm" />
}
```

> **Important:** Always pass the same `size` value as the `ProductReleaseCard` it replaces. Mismatched sizes cause measurable layout shift when the loaded card resolves, since placeholder heights are calibrated to match each variant's exact rendered metrics.