# Шрифтовой пак и пресеты субтитров

61 семейство, 159 статических начертаний, 24 МБ. Каталог:
`assets/fonts/font_catalog.json`. Состояние установки: `assets/fonts/font_manifest.json`.

```bash
python scripts/install_fonts.py --tier all      # core + pro + fx
python scripts/install_fonts.py --tier pro      # без громких и экспериментальных
python scripts/install_fonts.py --only sans,condensed
python scripts/font_audit.py --measure-check
```

## Уровни

| Уровень | Что там | Сколько |
|---|---|---|
| `core` | Обязательный минимум: субтитры, заголовки, данные, рукописный акцент | 13 |
| `pro` | Жанровая типографика | 36 |
| `fx` | Громкие и экспериментальные лица, только как акцент | 12 |

## Группы

| Группа | Для чего | Примеры |
|---|---|---|
| `sans` | Субтитры, интерфейс, лоуэр-третьи | Montserrat, Inter, Onest, Golos Text, Manrope |
| `condensed` | Плотная речь, спорт, длинные слова | Oswald, Sofia Sans Condensed, Alumni Sans |
| `display` | Хуки, титульные карточки | Unbounded, Russo One, Rubik Mono One, Prata, Oi |
| `tech` | IT, гаджеты, наука | Exo 2, Play, Geologica, Tektur, Handjet |
| `serif` | Интервью, цитаты, премиум | Playfair Display, Cormorant Garamond, PT Serif |
| `mono` | Код, терминал, данные | JetBrains Mono, Fira Code, IBM Plex Mono |
| `script` | Личное обращение, заметка | Caveat, Bad Script, Marck Script, Neucha |
| `fx` | Глитч, пиксель, граффити | Rubik Glitch, Press Start 2P, Rubik Spray Paint |

## Почему пак ставится, а не берётся из системы

Три причины, каждая проверена рендером:

1. **libass не читает подпапки `fontsdir`.** Поэтому кроме папок по семействам
   собирается плоский пак `assets/fonts/all/` — именно его нужно передавать.

2. **Вариативные шрифты Google по умолчанию тонкие.** `Montserrat[wght].ttf` сообщает
   `name ID 1 = "Montserrat Thin"`, а libass ищет именно по `name ID 1`. Стиль
   `Fontname: Montserrat` не находит ничего и падает на Arial. Поэтому
   `install_fonts.py` инстанцирует статические начертания с корректными RIBBI-именами.

3. **Вес нужен настоящий, а не синтетический.** Fake-bold по тонкому контуру выглядит
   именно как fake-bold.

Без `fonttools` пак всё равно ставится, но в нём останутся вариативные файлы, и скрипт
скажет об этом прямо, перечислив, какие лица нельзя прожигать.

## Вес и имя семейства

Инстанцирование даёт стандартную схему Google:

| Вес | ASS Fontname | Bold в стиле |
|---|---|---|
| 400 | `Montserrat` | 0 |
| 700 | `Montserrat` | −1 |
| 600 | `Montserrat SemiBold` | 0 |
| 900 | `Montserrat Black` | 0 |

`font_manifest.json` хранит фактические имена в поле `ass_names`, и движок субтитров
берёт их оттуда — не угадывает.

Regular и Bold делят один `name ID 1`, поэтому при **измерении** текста файл выбирается
ещё и по весу: измерить Regular, а нарисовать Bold — это ошибка около 6% ширины.

## Аудит

`scripts/font_audit.py` делает три независимые проверки:

* **глифы** — кириллица, латиница, цифры и обязательная пунктуация на месте;
* **лицензия** — файл лицензии лежит рядом с каждым источником;
* **libass** — решающая. Рендерит пробный кадр тем же фильтром `ass`, что и прожиг, и
  читает решения libass из её лога `fontselect:`. Подстановка системного шрифта — это
  падение проверки.

`--measure-check` дополнительно сравнивает предсказанную ширину строки с настоящими
чернилами. На паке: средняя ошибка 0.64%, максимум 1.6%.

Пунктуация разделена по последствиям: без `. , ! ? : ; - ( ) % " ' / « »` субтитр
выпускать нельзя, а отсутствие `– — … № ₽ € °` стоит только визуальной
однородности — libass подставит отдельный глиф, и это можно обойти, написав «...»
вместо «…».

Исключён: **Pixelify Sans** — в апстриме отсутствуют У+041E (О) и У+041F (П).

## 31 пресет субтитров

### Шесть дизайнерских — плашки и подсветка

Эти шесть используют то, чего в старом наборе не было: скруглённую подложку,
построенную кубическими безье и подогнанную под **измеренную** ширину строк.
Плашка снимает нужду в толстой обводке, поэтому буквы остаются чистыми, а не
обведёнными по контуру.

| Пресет | Шрифт | Режим | Когда |
|---|---|---|---|
| `signature_plate` | Golos Text Black | karaoke | **Базовый.** Тёмная скруглённая плашка, тонкая светлая кромка. Читается на любом фоне |
| `pill_highlight` | Onest Black | box | Вся фраза видна, произносимое слово в жёлтой пилюле. Самый читаемый динамичный вариант |
| `broadcast_clean` | Inter Bold | karaoke | Без плашки и без прыжков. Интервью, эксперт, длинные видео |
| `neo_brutal` | Unbounded Black | karaoke | Светлая карточка с чёрным контуром и цветной чертой. Тезисы, короткие удары |
| `soft_glass` | Manrope ExtraBold | karaoke | Светлая пилюля, тёмный текст. Лайфстайл, бьюти, продукт |
| `accent_underline` | Geologica Bold | karaoke | Без плашки, но с фирменной чертой под блоком |

Прозрачность плашки задаётся `opacity` и разворачивается в `\1a`, а не в альфу
`\1c` — см. `CAPTION_ENGINE.md`, пункт 5.

### Двадцать пять базовых

| Пресет | Шрифт | Режим | Когда |
|---|---|---|---|
| `clean_editorial` | Montserrat Bold | karaoke | Прежний вариант по умолчанию, любой фон |
| `clean_center` | Montserrat SemiBold | karaoke | Низ кадра занят действием |
| `hormozi_pop` | Montserrat Black | word_pop | Максимальная удерживающая динамика |
| `yellow_highlight` | Montserrat Black | box | Только 1–2 ударные фразы |
| `karaoke_box_white` | Inter Bold | box | Деловая подача, продукт |
| `reference_neon` | Oswald Bold | karaoke | Тёплая тёмная картинка, вечер |
| `moto_speed` | Tektur Bold | karaoke | Скорость, техника, гонки |
| `sport_condensed` | Sofia Sans Condensed Black | word_pop | Матч, тренировка |
| `energy_stack` | Alumni Sans Black | word_pop | Плакатная подача в три строки |
| `tiktok_one_word` | Unbounded Bold | one_word | Максимальный ритм |
| `bold_statement` | Russo One | karaoke | Авто, стройка, оборудование |
| `premium_display` | Playfair Display | plain | Короткие утверждения, высокий чек |
| `luxury_serif` | Cormorant Garamond | plain | Мода, парфюм, интерьер |
| `cinema_bottom` | Inter SemiBold | plain | Длинные ролики, документальный тон |
| `soft_documentary` | PT Serif | karaoke | История, архив, разбор |
| `podcast_calm` | Onest SemiBold | karaoke | Разговорный формат |
| `news_lower` | Roboto Condensed | box | Информационный тон, факты |
| `tech_mono` | JetBrains Mono | karaoke | Код, терминал, данные |
| `data_callout` | Bitter Bold | word_pop | Финансы, статистика |
| `minimal_thin` | Jost | plain | Архитектура, дизайн |
| `handwritten_note` | Caveat Bold | karaoke | Личное обращение |
| `street_graffiti` | Dela Gothic One | word_pop | Музыка, скейт, рэп |
| `glitch_alert` | Exo 2 Black | word_pop | Ошибка, конфликт, взлом |
| `retro_pixel` | Press Start 2P | karaoke | Игры, 8-бит |
| `kids_bubble` | Comfortaa Bold | word_pop | Дети, еда, забота |

## Правило выбора

1. Измерь самый длинный настоящий блок.
2. Он должен уложиться в 86% ширины кадра и максимум две строки.
3. Держи одно семейство через весь ролик; второе дисплейное лицо допустимо только на
   титульной карточке.
4. Блок обязан висеть не меньше двух секунд — столько нужно, чтобы его успели
   прочитать. Это ограничивает `max_words` сверху сильнее, чем ширина кадра.

## Почему базовым стал пресет с плашкой

Обводка — компромисс: она делает текст читаемым на пёстром фоне ценой того, что
каждая буква обрастает контуром и теряет рисунок. На кириллице это заметнее, чем
на латинице: у кириллических знаков больше вертикалей на ту же ширину, и обводка
0.06 кегля начинает их слеплять.

Плашка решает ту же задачу без этой цены: фон под текстом контролируется, поэтому
обводка не нужна вообще и шрифт виден таким, каким его нарисовали. Плюс это
единственный способ дать субтитрам собственный дизайн, а не только цвет.

Скруглённый угол — не украшение. Прямой угол на плашке под текстом — самый
заметный признак того, что подложку сгенерировал скрипт, а не поставил дизайнер.
4. Смотри контрольные кадры: самая длинная строка, первая фраза, выделенная фраза,
   последняя фраза.
5. Если текст перекрывает лицо, машину или интерфейс — перемещай всю зону титров, а не
   сжимай одну строку.

Выбирать глазами:

```bash
python scripts/preview_caption_styles.py --out-dir caption_previews \
  --canvas reels --background frame.png --text "реальная фраза"
```

Контактный лист рисует зелёную рамку безопасной зоны платформы. Пресет, хороший сам по
себе, может засунуть вторую строку под интерфейс TikTok — видно только на этой рамке.

## Цвета в пресетах

Пишутся как `#RRGGBB` с отдельной прозрачностью и конвертируются в ASS `&HAABBGGRR`
один раз, потому что ручная запись байт-перевёрнутого BGR — это то, как пресеты
получают неправильный акцентный цвет.
