惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

GbyAI
GbyAI
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
P
Proofpoint News Feed
L
Lohrmann on Cybersecurity
S
Secure Thoughts
Attack and Defense Labs
Attack and Defense Labs
人人都是产品经理
人人都是产品经理
Stack Overflow Blog
Stack Overflow Blog
W
WeLiveSecurity
O
OpenAI News
SecWiki News
SecWiki News
博客园 - Franky
NISL@THU
NISL@THU
Microsoft Azure Blog
Microsoft Azure Blog
T
Tor Project blog
Microsoft Security Blog
Microsoft Security Blog
aimingoo的专栏
aimingoo的专栏
Security Latest
Security Latest
H
Hacker News: Front Page
Google Online Security Blog
Google Online Security Blog
P
Privacy & Cybersecurity Law Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
D
Darknet – Hacking Tools, Hacker News & Cyber Security
月光博客
月光博客
李成银的技术随笔
Spread Privacy
Spread Privacy
F
Full Disclosure
F
Fortinet All Blogs
T
The Exploit Database - CXSecurity.com
Vercel News
Vercel News
AWS News Blog
AWS News Blog
WordPress大学
WordPress大学
IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog
IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog
V
Visual Studio Blog
J
Java Code Geeks
博客园 - 三生石上(FineUI控件)
G
Google Developers Blog
云风的 BLOG
云风的 BLOG
博客园 - 司徒正美
Engineering at Meta
Engineering at Meta
Last Week in AI
Last Week in AI
P
Palo Alto Networks Blog
宝玉的分享
宝玉的分享
T
True Tiger Recordings
N
News and Events Feed by Topic
酷 壳 – CoolShell
酷 壳 – CoolShell
Cisco Talos Blog
Cisco Talos Blog
N
News | PayPal Newsroom
S
SegmentFault 最新的问题
Jina AI
Jina AI

Все публикации подряд на Хабре

Архитектура монорепозитория для параллельного исполнения торговых стратегий Вам продают ИИ. Покупать нужно не его Матрица компетенций джедая: как снизить Bus Factor на проекте Production начинается там, где заканчивается вайбкодинг От фич и каскадов к генеративной модели: как мы переосмыслили рекомендации с помощью ARGUS Отвечай, как топовый специалист: как службе поддержки решать настоящие, а не озвученные проблемы клиентов Новые IT-специалисты эпохи AI: как зарубежные и российские компании относятся к vibe-coders, low-coders и zerocoders Локальная система проверки персонала: как мы автоматизировали скрининг соискателей без передачи ПДн наружу Разрабатывали решение для автоматизации, а получили универсальный продукт «Мультиплексор для Лабораторных измерений» Подготовка и сдача экзамена PMP в мае 2026 года Время закрывать доски. Ваш SaaS таск-трекер — это просто слой лака над базой данных Как мы проектировали multi-agent feedback для обучения рисованию Что такое Gemma 4: обзор новой LLM от Google CyBOK. Глава 3. Законы и регуляторные нормы. Часть 8 LLM-инференс на фотонах? Препарируем передовые технологии, представленные в апреле Агенты выходят на работу (часть 3) Нехватка CUDA-памяти при обучении с GRPO: как перестать гадать и начать считать Окей, Lamoda, что надеть на вечеринку? Как обучить LLM навыкам ИИ-стилиста ArchiMate 4: Отказ от слоёв и унификация метамодели Дальнейшая судьба SFP-Master Игровой ПК или PlayStation 5: что выгоднее в 2026 году Flipper One — нам нужна ваша помощь Как мы построили корпоративную LLM-платформу: архитектура, грабли и выводы Устранить нельзя оставить — разбираем ситуацию с уязвимостями в российской виртуализации Bitrix и Laravel: веб-хуки, ERP и все-все-все (часть 5) Поиск секрета популярности лучших репозиториев GitHub за всё время существования платформы Сэкономили на облаке под 1С: ДО — заложили бюджет на штраф. Разбираем 152-ФЗ при работе с 1С Компьютерное зрение: что получается, когда у вас не идеальная лаборатория, а дождь, снег и подвижный манипулятор Параметризация в JUnit 5 и Allure Report Мне 15, и я собираю AI-стартап для недвижки: как я победил GPU, баги PyTorch и очередь в визовый центр Стратегия «Голубого океана»: как системный аналитик влияет на продукт Проектируем с нуля калькулятор на FPGA. Часть 3: Практические численные методы От видимости сети до кибербезопасности: главный миф о сетевой телеметрии, который мешает раскрыть потенциал NetFlow Как интегрировать ТСД с любой конфигурацией «1С: Предприятия»? Человеческие головы, сандалии и лягушки: стегоконтейнеры за тысячи лет до первого компьютера GigaIDE Pro для разработки на Django Как добиться непостоянного момента? Книга: «Kubernetes. Полное руководство по развертыванию и управлению Kubernetes в облачных и локальных средах. 2-е изд.» Почему IT-специалисты остаются: что работает на удержание в 2026 году Соединение деталей 3D-печатных изделий… Простое ли дело? Yamaha RGX121Z RM — современный суперстрат с японским вайбом второй половины 1980-х Как я написал плагин для WooCommerce под Yandex YCP или как купить в 1 клик из Алисы Креативное программирование: визуализация звука Сложно читать IT литературу на кривом русском? Есть решение — книжный ревью (рефакторинг) История о том, как человечество наняло очень странного сотрудника Как мы в отделе документации создали LLM агента для автоматизированного перевода с английского на другие языки Почему e-ink до сих пор не убил LCD, хотя должен был Как оплачивать нейросети и остальное недоступное в РФ в 2026: 9 способов с ценами и рисками, где можно влететь Решение проблем в управлении: почему мидл-менеджеры справляются с кризисами эффективнее топов Сколько телефонов и планшетов продали партнёры: единое хранилище данных для бренда электроники Google Fellow, студент Нанкина и создатель TikTok: кто сделал Seedream и Seedance. Досье SpeShu.AI В прорывном эксперименте из первых в мире полностью искусственных яиц вылупились птенцы Разворачиваем облачный ТОиР на заводе за две недели Vivaldi 8.0 — Унифицированная свобода выбора Как мы с нуля реализовали двустороннее доверие «лес–лес» с Microsoft Active Directory Хакер спас мир и сел в тюрьму: Невероятная история Маркуса Хатчинса и червя WannaCry Построение корпоративной архитектуры в ИТ-проектах, используя методологию TOGAF Пайплайн не должен хранить секрет: безопасное хранение и доставка секретов для CI/CD с Deckhouse Code и Stronghold ОГЭ информатика. 16 задание на Python Asus, MSI и Gigabyte урезают производство материнских плат. Что происходит на рынке Claudex: как я подружил Claude Code с ChatGPT/Codex OAuth без OpenAI API key Как измерить скорость интернета? Почему выгорают не слабые, а ваши Версионирование таблиц репозитория метаданных Sigla Vision Графическая утилита PostgreSQL mini Profiler (в помощь экспертам по технологическим вопросам 1С и не только им) Шахматные программы IV. Термины и методы Почему Я.Директ не приводит премиальных клиентов и что с этим делать – продали элитных туров на 600 млн Реестр отечественного ПО: как бизнесу выбрать решение среди 30 000 записей и не ошибиться Глаза не видят, а код пишется: как я настраиваю и программирую 100+ модулей в умном доме Архитектура AI-сервисов: почему монолит убивает latency и GPU Процессы: чего до сих пор не хватало обычным BPM (Часть 2) Книжный салон — дополнительные книги от издательства «БХВ». Предзаказ Как продакту довести фичу до прода без PMBOK и PRINCE2 Оргмодель, процессы и агенты (Часть 1) Probe-сеть из 10 регионов: что я не учёл про AS-разнесённость Как автоматизировать повторную обработку сообщений из архива в DATAREON Platform Arguments to Config — простая и мощная библиотека для парсинга аргументов в CLI-приложении на C# Как я обучил GPT с нуля на русском языке — и что из этого получилось Миллион алых нод: о выборе баз данных для хранения больших объёмов Билеты, баги и БДСМ: хроники тревел-стартапа От vSphere к VCD: как мы построили хранилище образов и нативный CSI для Kubernetes Фолдинг белка на ноутбуке. De novo дизайн KRAS G12D (Switch II) ингибитора. Докинг, валидация в AlfaFold Server и PyMOL Тебя уволят, и ничего не сломается. Возможно, станет даже лучше ИИ от Anthropic вскрыл банки G20, Цукерберг уволил 8000 человек за один день, а мы это пропустили Один за всех: как я в одиночку тащу фуллстек-проект, который незаметно разросся до соцсети Реакционная лженаука. Как СССР осудил кибернетику — и чем это аукнулось для ИИ Лёгкий мониторинг Proxmox-кластера: Pulse вместо большого Zabbix-стека RAG для тех, кто разочаровался: почему retrieval ломается и как это починить Три уровня субъективной реальности: почему непонимание в командах заложено биологически Дирижёр вместо конвейера: как AI ломает классический pipeline разработки Dart 3.12 — что нового в Dart? Четыре реакции — четыре тела. Можно ли измерить тип личности по сердцебиению? Flutter 3.44 — Что нового во Flutter? Найм инженеров в 2026: ботлнек — это не рынок, это вы Тонкие контроллеры и модели. Использование паттернов проектирования в Rails-приложении Тезис о расширенном разуме Сумасшедшая история Т9: Стартапы, дельфины и буддизм [Перевод] Открыл ли китайский компьютер «Цзючжан 4.0» эру квантового превосходства? Что такое DWH (КХД) и как работает корпоративное хранилище данных Как я создал сервис по написанию формальных документов
Чтобы не выглядело как пет-проект»: как я в одиночку сделал премиальный интерфейс кино-сервиса (с кодом)
vibemuvik · 2026-05-21 · via Все публикации подряд на Хабре

Чтобы не выглядело как пет-проект»: как я в одиночку сделал премиальный интерфейс кино-сервиса (с кодом)

Время на прочтение8 мин

Охват и читатели168

Кейс

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

Сразу дисклеймер: я не дизайнер. Всё нажито методом «смотрю на референсы (Letterboxd, Mubi, KinoPoisk HD) и пытаюсь повторить ощущение». Оказалось, премиальность — это не про дорогие шрифты, а про несколько повторяющихся приёмов. Разберём пять.

1. Акцентный цвет из постера фильма — фича, которая дороже всего «продаёт»

Самое заметное решение. Раньше у меня на всех страницах был один статичный фиолетовый акцент — и это выглядело дёшево и одинаково. Идея: пусть каждая страница фильма подсвечивается доминантным цветом его постера. Заходишь на мрачный нуар — интерфейс уходит в холодный синий, открываешь комедию — тёплый янтарь. Страница будто «сделана под этот фильм».

Делается без всяких ML, прямо в браузере через canvas: рисуем постер в крошечный буфер 32×48, усредняем цвета (выкидывая чёрные рамки и серость), переводим в HSL и принудительно «насыщаем», потому что постеры часто тусклые. Результат кладём в CSS-переменную — и весь интерфейс подхватывает её.

function applyPosterAccent(posterUrl: string) {  const img = new Image();  img.crossOrigin = "anonymous";  img.onload = () => {    const canvas = document.createElement("canvas");    const w = (canvas.width = 32), h = (canvas.height = 48);    const ctx = canvas.getContext("2d");    if (!ctx) return;    ctx.drawImage(img, 0, 0, w, h);    const { data } = ctx.getImageData(0, 0, w, h);
    let r = 0, g = 0, b = 0, n = 0;    for (let i = 0; i < data.length; i += 4) {      const R = data[i], G = data[i + 1], B = data[i + 2];      const max = Math.max(R, G, B), min = Math.min(R, G, B);      if (max < 30 || min > 230) continue; // чёрные рамки / выбеленные пиксели      if (max - min < 25) continue;          // серое — в акцент не годится      r += R; g += G; b += B; n++;    }    if (!n) return;
    const { h: hue, s, l } = rgbToHsl(r / n, g / n, b / n);    // постеры часто тусклые — принудительно делаем цвет «сочным»    const sat = Math.min(0.85, Math.max(0.45, s * 1.6 + 0.15));    const lit = Math.min(0.62, Math.max(0.52, l));
    document.documentElement.style.setProperty(      "--movie-accent", `hsl(${hue | 0} ${(sat * 100) | 0}% ${(lit * 100) | 0}%)`    );    document.documentElement.style.setProperty(      "--movie-accent-soft", `hsl(${hue | 0} ${(sat * 100) | 0}% ${(lit * 100) | 0}% / .18)`    );  };  img.src = posterUrl;}

const img = new Image();

img.crossOrigin = "anonymous";

img.onload = () => {

const canvas = document.createElement("canvas");

const w = (canvas.width = 32), h = (canvas.height = 48);

const ctx = canvas.getContext("2d");

if (!ctx) return;

ctx.drawImage(img, 0, 0, w, h);

const { data } = ctx.getImageData(0, 0, w, h);

let r = 0, g = 0, b = 0, n = 0;

for (let i = 0; i < data.length; i += 4) {

const R = data[i], G = data[i + 1], B = data[i + 2];

const max = Math.max(R, G, B), min = Math.min(R, G, B);

if (max < 30 || min > 230) continue; // чёрные рамки / выбеленные пиксели

if (max - min < 25) continue; // серое — в акцент не годится

r += R; g += G; b += B; n++;

}

if (!n) return;

const { h: hue, s, l } = rgbToHsl(r / n, g / n, b / n);

// постеры часто тусклые — принудительно делаем цвет «сочным»

const sat = Math.min(0.85, Math.max(0.45, s * 1.6 + 0.15));

const lit = Math.min(0.62, Math.max(0.52, l));

document.documentElement.style.setProperty(

"--movie-accent", hsl(${hue | 0} ${(sat 100) | 0}% ${(lit 100) | 0}%)

);

document.documentElement.style.setProperty(

"--movie-accent-soft", hsl(${hue | 0} ${(sat 100) | 0}% ${(lit 100) | 0}% / .18)

);

};

img.src = posterUrl;

}

rgbToHsl — обычная конверсия RGB→HSL, она в любом сниппете в сети. Важны два момента: фильтрация «мусорных» пикселей (без неё на чёрных постерах акцент получается грязно-серым) и буст насыщенности (s * 1.6 + 0.15) — без него половина фильмов давала бы блёклый акцент.

Стоит это копейки по производительности (даунскейл до 32×48 — это меньше 1500 пикселей), а ощущение даёт именно «премиальное»: интерфейс реагирует на контент.

2. Glassmorphism через одну переменную, а не 100 захардкоженных цветов

Второй приём — стекло. Но не «прилепил blur и забыл», а так, чтобы всё было завязано на тот же --movie-accent. Тогда стеклянные карточки, свечения и обводки автоматически окрашиваются под фильм, и это выглядит цельно.

.glass-card {  background: linear-gradient(180deg,      var(--movie-accent-soft, rgba(255,255,255,.06)),      rgba(255,255,255,.02));  backdrop-filter: blur(18px) saturate(1.2);  border: 1px solid rgba(255,255,255,.08);  border-radius: 18px;  box-shadow: 0 24px 60px -20px var(--movie-accent-soft);}

background: linear-gradient(180deg,

var(--movie-accent-soft, rgba(255,255,255,.06)),

rgba(255,255,255,.02));

backdrop-filter: blur(18px) saturate(1.2);

border: 1px solid rgba(255,255,255,.08);

border-radius: 18px;

box-shadow: 0 24px 60px -20px var(--movie-accent-soft);

}

Главный урок: премиальность = единый источник правды для темы. Когда цвета разбросаны по компонентам, всё рассыпается на «почти одинаковые» оттенки и выглядит самодельно. Когда есть 5–6 CSS-переменных (--bg--glass--accent--accent-soft--text--dim) — всё дышит в унисон, и тему можно крутить в одну строку.

Отдельная боль с SSR: стеклянные слои на тёмном фоне дают мерзкий «мигающий» FOUC, если стили приедут позже разметки. Лечится инлайном критических переменных прямо в <head> на сервере — тогда первый кадр уже правильный.

3. Анимированная «кинолента» сверху — мелочь, которая считывается как «дорого»

Под шапкой у меня едет горизонтальная лента постеров — то, что подсознательно ассоциируется со стриминг-сервисами. Сама по себе она простая (горизонтальный скролл + стрелки), но есть нюанс UX, который отличает «ленту» от «дёшево прибитого ряда картинок»: стрелки должны гаснуть в крайних положениях.

const updateArrows = () => {  const el = stripRef.current;  if (!el) return;  setCanLeft(el.scrollLeft > 4);  setCanRight(el.scrollLeft + el.clientWidth < el.scrollWidth - 4);};// вешаем на el.addEventListener("scroll", updateArrows, { passive: true })// + ResizeObserver, чтобы пересчитывать при ресайзе

const el = stripRef.current;

if (!el) return;

setCanLeft(el.scrollLeft > 4);

setCanRight(el.scrollLeft + el.clientWidth < el.scrollWidth - 4);

};

// вешаем на el.addEventListener("scroll", updateArrows, { passive: true })

// + ResizeObserver, чтобы пересчитывать при ресайзе

Плюс первые 4 постера грузим loading="eager" (они выше fold и влияют на LCP), остальные — lazy. Звучит банально, но именно из таких мелочей складывается ощущение, что «всё плавно и продумано».

4. Web Push без Firebase — на чистом VAPID

Хватит про фронт. Пуш-уведомления я сделал на нативном Web Push, без FCM и сторонних сервисов — не хотелось вендор-лока ради пет-проекта. На фронте регистрируем service worker и подписываемся:

const reg = await navigator.serviceWorker.register("/sw.js");const sub = await reg.pushManager.subscribe({  userVisibleOnly: true,  applicationServerKey: urlB64ToUint8Array(VAPID_PUBLIC_KEY),});await fetch("/api/push/subscribe", {  method: "POST",  headers: { "Content-Type": "application/json" },  body: JSON.stringify(sub),});

const sub = await reg.pushManager.subscribe({

userVisibleOnly: true,

applicationServerKey: urlB64ToUint8Array(VAPID_PUBLIC_KEY),

});

await fetch("/api/push/subscribe", {

method: "POST",

headers: { "Content-Type": "application/json" },

body: JSON.stringify(sub),

});

Подписку (endpoint + ключи p256dh/auth) храню в PostgreSQL, по одной на устройство. Отправка на бэке (FastAPI) — через pywebpush:

from pywebpush import webpush, WebPushException
def send_push(sub, title, body, url="/"):    try:        webpush(            subscription_info={                "endpoint": sub.endpoint,                "keys": {"p256dh": sub.p256dh, "auth": sub.auth},            },            data=json.dumps({"title": title, "body": body, "url": url}),            vapid_private_key=VAPID_PRIVATE,            vapid_claims={"sub": "mailto:admin@example.com"},            ttl=3600,        )    except WebPushException as e:        # 404/410 = подписка протухла → удаляем из базы, чтобы не копился мусор        if e.response is not None and e.response.status_code in (404, 410):            delete_subscription(sub.id)

def send_push(sub, title, body, url="/"):

try:

webpush(

subscription_info={

"endpoint": sub.endpoint,

"keys": {"p256dh": sub.p256dh, "auth": sub.auth},

},

data=json.dumps({"title": title, "body": body, "url": url}),

vapid_private_key=VAPID_PRIVATE,

vapid_claims={"sub": "mailto:admin@example.com"},

ttl=3600,

)

except WebPushException as e:

# 404/410 = подписка протухла → удаляем из базы, чтобы не копился мусор

if e.response is not None and e.response.status_code in (404, 410):

delete_subscription(sub.id)

Грабли, на которых стоит сэкономить вам нервы: на iOS пуши прилетают только если сайт установлен как PWA (Safari 16.4+) — это ограничение Apple, не ваше. И обязательно чистите мёртвые подписки по 404/410, иначе таблица распухает «фантомными» устройствами.

5. Один честный рейтинг из голосов и рецензий (и баг, который я этим закрыл)

Бэкенд-история, которую Хабр любит: «было криво — стало красиво». У меня есть два способа оценить фильм — звёздный голос («оценить») и оценка внутри рецензии. Сначала они жили раздельно: один обработчик считал рейтинг из таблицы голосов, другой — из рецензий, и оба писали в одно и то же поле. Итог — рейтинг скакал в зависимости от того, что произошло последним. Классическая «неразбериха».

Починил одним SQL — единый рейтинг как среднее по уникальным пользователям, где у каждого берётся его звёздный голос, а если голоса нет — оценка из одобренной рецензии:

WITH per_user AS (    SELECT user_id, rating FROM movie_votes WHERE movie_id = :mid    UNION ALL    SELECT user_id, rating FROM reviews      WHERE movie_id = :mid        AND status = 'approved'        AND user_id NOT IN (SELECT user_id FROM movie_votes WHERE movie_id = :mid))SELECT COALESCE(AVG(rating), 0), COUNT(*) FROM per_user;

SELECT user_id, rating FROM movie_votes WHERE movie_id = :mid

UNION ALL

SELECT user_id, rating FROM reviews

WHERE movie_id = :mid

AND status = 'approved'

AND user_id NOT IN (SELECT user_id FROM movie_votes WHERE movie_id = :mid)

)

SELECT COALESCE(AVG(rating), 0), COUNT(*) FROM per_user;

NOT IN (...голоса...) — это и есть дедупликация: проголосовал и написал рецензию — учтём один раз, голос приоритетнее. Эту функцию я зову из всех мест, где рейтинг пересчитывается (голос, создание/правка/удаление рецензии). Один источник правды — и поле перестало «дышать».

Бонус: возрастной гейт, который не убивает SEO

Раз уж про кино — нужен был блок 18+ на «взрослых» фильмах. Тут легко выстрелить себе в ногу: если рисовать гейт на сервере и прятать под ним контент, поисковик увидит заглушку вместо страницы и выкинет её из индекса. Поэтому гейт у меня — строго клиентский оверлей поверх уже отрендеренного контента:

"use client";export default function AgeGate({ ageRating, mpaa }: Props) {  const [confirmed, setConfirmed] = useState(false);  useEffect(() => {    setConfirmed(localStorage.getItem("vm_age18_ok") === "1");  }, []);  if (!is18Plus(ageRating, mpaa) || confirmed) return null;  return <div className="age-overlay">/* … */</div>;}

export default function AgeGate({ ageRating, mpaa }: Props) {

const [confirmed, setConfirmed] = useState(false);

useEffect(() => {

setConfirmed(localStorage.getItem("vm_age18_ok") === "1");

}, []);

if (!is18Plus(ageRating, mpaa) || confirmed) return null;

return <div className="age-overlay">/* … */</div>;

}

Поисковый робот получает полный серверный HTML со всем контентом фильма; гейта в этой HTML нет (он дорисовывается в браузере по localStorage), редиректов и noindex тоже нет. Для живого человека контент перекрыт, для индексации — как будто гейта не существует. Возрастные проверки, к слову, прямо исключены из «штрафа за навязчивые баннеры» у Google.

Что в итоге

Премиальность в одиночку — это не про бюджет, а про несколько дисциплинированных приёмов: реакция интерфейса на контент (цвет из постера), единая тема через переменные, внимание к микро-UX (гаснущие стрелки, приоритет загрузки), и аккуратные «скучные» бэкенд-решения, которые не подводят. Ничего из этого не требует команды — только насмотренности и готовности переделать раз пять.

Если интересно потыкать живьём, на чём всё это крутится в бою — проект открыт: vibemuvik.ru. Не зову регистрироваться, просто если по ходу статьи стало любопытно, как «цвет из постера» и стекло выглядят вместе — можно зайти и посмотреть на странице любого фильма.

А вам интересно: какими приёмами вы «удешевляете» или, наоборот, «удорожаете» интерфейс малой кровью? И где, по-вашему, граница между «премиально» и «перегружено эффектами»? Особенно любопытно от тех, кто тоже тащит фронт в одиночку — поделитесь своими находками в комментариях.

и какой стал

было

и стало