# Subtitle Preset Grid Redesign - Design Document

**Date:** 2026-04-06  
**Scope:** Redesign preset preview cards in Caption Settings step to match uploaded video aspect ratio with modern visual refresh

---

## Overview

Redesign the subtitle preset selection grid to:
1. Display preset previews with the **same aspect ratio as the uploaded video**
2. Apply a **modern visual refresh** consistent with the app's design language
3. Show **style characteristics** (font, colors) as subtle hints
4. Maintain **responsive layout** across screen sizes

---

## Core Functionality

### Dynamic Aspect Ratio

**Data Flow:**
1. Fetch video metadata via `GET /api/media/mediafiles/{media_file_id}/` using `primaryFileId` from WizardContext
2. Extract `width` and `height` from `MediaFileRead` response
3. Calculate aspect ratio: `width / height`
4. Apply as CSS `aspect-ratio` to preset cards via inline style or CSS variable
5. Handle loading state while fetching metadata
6. Fallback to 16:9 if no video is uploaded or API error occurs

**Implementation Notes:**
- Store aspect ratio in WizardContext alongside other video metadata
- Update ratio when `primaryFileId` changes
- Cards use container queries for responsive sizing

---

## Visual Design (5 Pillars Applied)

### 1. Typography with Character
- Keep existing font system (consistent with app)
- Style name: `font-weight: 500`, `font-size: 14px`
- Characteristic labels: `font-size: 12px`, muted color (`--gray-10`)

### 2. Committed Color & Theme
- Uses **Catppuccin Mocha** palette matching the project:
  - Canvas: `--bg-canvas: #11111b`
  - Cards: `--bg-default: #1e1e2e`
  - Surfaces: `--bg-surface: #313244`
  - Borders: `--border-default: #45475a`, `--border-subtle: #313244`
  - Text: `--text-primary: #cdd6f4`, `--text-secondary: #bac2de`, `--text-tertiary: #9399b2`
- Selected state: purple accent (`--purple-400: #cba6f7`) with glow shadow
- Card hover: border transitions to purple accent
- System badge: purple-100 background with purple-400 text
- Checkmark indicator on selected card (top-right corner)

### 3. Purposeful Motion
- Cards fade in with staggered animation (50ms delay per card)
- Smooth border-color transition on hover (150ms ease)
- Selection change: immediate border color change
- Loading skeleton: shimmer animation

### 4. Brave Spatial Composition
- CSS Grid with `auto-fill` and `minmax(200px, 1fr)`
- Consistent 16px gap between cards
- Cards maintain video aspect ratio without stretching
- Responsive: more columns on wide screens, fewer on narrow

### 5. Atmosphere & Depth
- Card background: subtle gradient overlay for depth
- Selected card: elevated with `box-shadow` + accent glow
- Dark preview background (`#0c0a1a`) preserved from existing StylePreview
- Rounded corners: `border-radius: 12px`

---

## Component Structure

### PresetCard

```
┌──────────────────────────────────────┐
│                                      │
│     [StylePreview Component]         │  ← Dynamic aspect-ratio
│     "Пример субтитров"               │     based on video
│                                      │
├──────────────────────────────────────┤
│ Style Name              [Системный]  │  ← Footer
│ Lobster · Yellow accent              │  ← Characteristics (subtle)
└──────────────────────────────────────┘
```

**Props:**
- `preset: CaptionPresetRead`
- `isSelected: boolean`
- `aspectRatio: number` (width/height, e.g., 1.777 for 16:9)
- `onSelect: () => void`
- `onEdit: () => void`
- `onDelete: () => void`

### StylePreview Updates

**New Props:**
- `aspectRatio?: number` - overrides default 9/16

**Behavior:**
- Uses passed `aspectRatio` for container sizing
- Falls back to 9/16 if not provided
- Maintains all existing text styling logic

### PresetGrid Updates

**New Behavior:**
- Fetches video metadata via `useVideoMetadata()` hook
- Passes `aspectRatio` to all PresetCard children
- Shows skeleton loading state while fetching
- Responsive grid layout

---

## Style Characteristics Display

Each card footer shows:
- **Font family** (e.g., "Lobster", "Inter") - extracted from `preset.style_config.text.font_family`
- **Accent color** - small color dot + name if distinct from default
- Hidden on cards narrower than 180px (responsive)

**Format:**
```
{font_family} · {accent_color_name}
```

Example: `Lobster · Желтый` or `Inter · Неоновый`

---

## Loading State

**Skeleton Card:**
- Same aspect ratio as target (default 16:9 while loading)
- Shimmer animation on preview area
- Gray placeholder for text
- 4-6 skeleton cards shown while loading

---

## Responsive Behavior

| Screen Width | Grid Columns | Card Min Width |
|--------------|--------------|----------------|
| < 480px      | 2            | 140px          |
| 480-768px    | 3            | 160px          |
| 768-1200px   | 4            | 180px          |
| > 1200px     | 5-6          | 200px          |

---

## API Integration

### New Hook: `useVideoMetadata`

```typescript
function useVideoMetadata(fileId: string | null) {
  return api.useQuery(
    "get",
    "/api/media/mediafiles/{media_file_id}/",
    { params: { path: { media_file_id: fileId ?? "" } } },
    { enabled: !!fileId }
  )
}
```

### Aspect Ratio Calculation

```typescript
const aspectRatio = useMemo(() => {
  if (!mediaFile?.width || !mediaFile?.height) return 16 / 9
  return mediaFile.width / mediaFile.height
}, [mediaFile])
```

---

## Edge Cases

1. **No video uploaded:** Fall back to 16:9 aspect ratio
2. **Video metadata unavailable:** Show error toast, fall back to 16:9
3. **Very wide video (>21:9):** Cap max card width to prevent overflow
4. **Very tall video (9:16+):** Limit max height, allow scrolling if needed
5. **No presets:** Show empty state with "Создать пресет" card only

---

## Files Modified

1. `src/features/project/CaptionSettingsStep/PresetGrid.tsx` - Grid logic, aspect ratio distribution
2. `src/features/project/CaptionSettingsStep/PresetGrid.module.scss` - Grid styles, responsive layout
3. `src/features/project/CaptionSettingsStep/StylePreview.tsx` - Accept aspect ratio prop
4. `src/features/project/CaptionSettingsStep/StylePreview.module.scss` - Dynamic sizing
5. `src/features/project/CaptionSettingsStep/useVideoMetadata.ts` - New hook (or inline in PresetGrid)

---

## Acceptance Criteria

- [ ] Preset cards display with uploaded video's aspect ratio
- [ ] Grid is responsive and works on mobile/desktop
- [ ] Loading state shows skeleton cards
- [ ] Style characteristics (font, color) visible on cards
- [ ] Selected state clearly visible with accent border
- [ ] Hover effects smooth and purposeful
- [ ] Fallback to 16:9 when no video available
- [ ] All existing functionality preserved (select, edit, delete, create)
