/* ============================================================================
   MIRAGE — дизайн-система сервісу.

   ДЖЕРЕЛО: `~/Desktop/rachel-broll-test/ПРОТОТИП-навігації.html`, розділ
   <style>. Правила перенесені ДОСЛІВНО, а не переписані з памʼяті: прототип
   доводили замірами в браузері (кеглі, радіуси, контраст, переповнення), і
   будь-яке «майже те саме» тут означало б втрату цих замірів.

   ЩО ЗМІНЕНО ПРИ ПЕРЕНЕСЕННІ — рівно три речі:
     1. Прототип був одним файлом із хеш-роутером, тож layout-правила окремих
        екранів висіли на `#v-settings`, `#v-usage` тощо. У продукті кожен
        екран — окрема сторінка, тому це класи на <body>:
            #v-settings   -> .pg-settings            (GET /settings)
            #v-psettings  -> .pg-project-settings    (GET /project/{id}/settings)
            #v-create     -> .pg-project-new         (GET /projects/new)
            #v-job-run    -> .pg-job-run             (задача в роботі)
            #v-usage      -> .pg-usage               (GET /project/{id}/usage)
     2. Прибрано `.view` / `.view.on` — перемикач екранів усередині одного
        файлу. Появу сторінки дає `.page` (та сама анімація riseIn).
     3. Додано глобальний блок prefers-reduced-motion — правило 12 з
        `MIRAGE-ТЗ-на-реалізацію.md` §5 («вимикає ВСІ анімації»). У прототипі
        він накривав лише чек-ліст генерації.

   ЯК ПІДКЛЮЧАЄТЬСЯ. Цей файл іде в <head> ПЕРШИМ і несе весь вигляд сервісу.
   Сторінковий <style> (якщо він взагалі потрібен) — після нього, і тоді
   локальне правило свідомо перекриває спільне. Це протилежно до старого
   vidrush.css, який стояв останнім і накривав шість різних сторінок згори;
   там це було латкою, тут — основою.

   ЩО НЕ МОЖНА ЗАГУБИТИ ПРИ ПРАВКАХ (MIRAGE-ТЗ §5):
     - одна ширина 1120px на весь сервіс, хедер має ту саму внутрішню сітку;
     - наведення НЕ рухає елемент — змінюється лише світло (тло, тінь, рамка);
     - жодних емодзі й текстових гліфів у розмітці: у Manrope їх немає, лише SVG;
     - кегль зі шкали 11/12/13/14/16/18/24/32 (+H1 clamp 30-44), півкроків немає;
     - радіус зі шкали 6/8/12/16 + коло;
     - затухання й статуси — КОЛЬОРОМ, не прозорістю: підлога контрасту 4.5:1;
     - кольори мають значення: синій — дія і вибір, зелений — готово,
       бурштин — увага, червоний — руйнівне, сірий — система працює сама,
       нейтральний світлий (--motion) — анімація кадру (властивість, не статус).
   ========================================================================== */

  /* ====== токени — зняті з vidrush.css / index.html (v3) ====== */
  :root {
    --bg:#181818; --panel:#1f1f1f; --panel-2:#232323; --border:#4a4a4e;
    --text:#eeeeee; --muted:#a3a3a3; --accent:#2563eb; --accent-hover:#3b74f0;
    --motion:#eeeeee;   /* анімація кадру: нейтральна — усі кольори з сенсом зайняті статусами й дією */
    --ok:#30d158; --warn:#ff9f0a; --bad:#ff7a70;
    --panel-glass: linear-gradient(135deg, rgba(255,255,255,.07) 0%, rgba(255,255,255,0) 55%), rgba(26,26,28,.30);
    --glass-border: rgba(255,255,255,.16);
    --glass-shadow: 0 8px 32px rgba(0,0,0,.35), inset 0 1px 0 rgba(255,255,255,.09);
    --glass-blur: blur(26px) saturate(190%);
    /* поверхня картки: напівпрозора, ледь світліша зверху; без темного провалу й «дзеркала» */
    --vr-card: linear-gradient(180deg, rgba(255,255,255,.055) 0%, rgba(255,255,255,.015) 100%);
    --vr-card-bg: rgba(44,44,48,.40);
    /* Готове відео тихіше за роботу. Орієнтир — не картка, а КОНТЕЙНЕР блоку
       «In progress» (--panel-glass, .30): те, що вже зроблено, не має важити
       більше за те, що робиться просто зараз, і мусить від нього ВІДРІЗНЯТИСЬ.
       Ішли двома кроками: .40 -> .20 користувач визнав недостатнім, бо різниця
       з контейнером лишалась замалою; тепер .10, тобто вчетверо менше за
       картку і втричі за контейнер. Нижче опускати нікуди — рамка лишиться
       єдиним, що тримає рядок. */
    --vr-row: linear-gradient(180deg, rgba(255,255,255,.016) 0%, rgba(255,255,255,.004) 100%);
    --vr-row-bg: rgba(44,44,48,.10);
    --vr-hair: rgba(255,255,255,.09);
    --vr-tight: -0.04em;
    --vr-ease: cubic-bezier(.22,.61,.36,1);
    --measure: 1120px;
    --header-h: 56px;
  }
  /* Інтерфейс темний, і системні віджети мають про це знати. Без цього рядка
     браузер малює їх у СВІТЛІЙ схемі: плеєр озвучки виходив білою смугою
     посеред темної панелі (знайдено 23.08 на екрані затвердження озвучки).
     Стосується не лише його — так само поводяться смуги прокрутки, контроли
     відео й системні поля. Одне оголошення замість латки на кожен віджет. */
  :root { color-scheme: dark; }
  * { box-sizing: border-box; }
  html { scrollbar-gutter:stable; }
  html, body { margin:0; }
  [hidden] { display:none !important; }
  /* одна поверхня для всіх карток: напівпрозора, з легким розмиттям фону.
     .video-card сюди НЕ входить: рядок готового відео мусить бути тихішим за
     все, що в роботі (§рішення користувача 23.08) — своя поверхня нижче. */
  .project-card, .job-card-compact, .tile, .voice-card {
    background-color: var(--vr-card-bg); background-image: var(--vr-card);
    backdrop-filter: blur(14px) saturate(140%); -webkit-backdrop-filter: blur(14px) saturate(140%);
  }
  body {
    background: var(--bg); color: var(--text);
    font-family: 'Manrope', -apple-system, BlinkMacSystemFont, sans-serif;
    min-height: 100vh; padding-bottom: 40px;
  }
  a { color: inherit; }
  body::before { content:''; position:fixed; inset:0; z-index:-1; pointer-events:none;
    background: radial-gradient(120% 90% at 50% 0%, transparent 40%, rgba(0,0,0,.55) 100%); }
  body::after { content:''; position:fixed; inset:0; z-index:-1; pointer-events:none; opacity:.035;
    background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='140' height='140'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/></filter><rect width='140' height='140' filter='url(%23n)'/></svg>"); }
  .bg-blobs { position:fixed; inset:0; z-index:-2; overflow:hidden; pointer-events:none; }
  .bg-blob { position:absolute; border-radius:50%; filter:blur(96px); opacity:.38; }
  .bg-blob1 { width:520px; height:520px; left:-160px; top:-140px; background:radial-gradient(circle,#ff8a3d 0%,rgba(255,138,61,0) 70%); animation:bgFloat1 38s ease-in-out infinite; }
  .bg-blob2 { width:560px; height:560px; right:-180px; top:-100px; background:radial-gradient(circle,#2563eb 0%,rgba(37,99,235,0) 70%); animation:bgFloat2 46s ease-in-out infinite; }
  .bg-blob3 { width:460px; height:460px; left:30%; top:60vh; background:radial-gradient(circle,#a855f7 0%,rgba(168,85,247,0) 70%); animation:bgFloat3 34s ease-in-out infinite; }
  @keyframes bgFloat1 { 0%,100%{transform:translate(0,0)} 50%{transform:translate(70px,50px)} }
  @keyframes bgFloat2 { 0%,100%{transform:translate(0,0)} 50%{transform:translate(-60px,70px)} }
  @keyframes bgFloat3 { 0%,100%{transform:translate(0,0) scale(1)} 50%{transform:translate(40px,-50px) scale(1.12)} }

  /* ====== СПІЛЬНИЙ ХЕДЕР — новий ====== */
  /* Смуга хедера — на всю ширину вікна, а її вміст — по тій самій сітці, що й сторінка:
     лого, заголовок сторінки й картки стоять на одній вертикалі. */
  .app-header {
    position: sticky; top: 0; z-index: 100;
    height: var(--header-h);
    display: flex; justify-content: center;
    background: rgba(24,24,24,.72);
    border-bottom: 1px solid rgba(255,255,255,.07);
    backdrop-filter: blur(18px) saturate(160%); -webkit-backdrop-filter: blur(18px) saturate(160%);
  }
  .header-in { width:100%; max-width:var(--measure); padding:0 28px; display:flex; align-items:center; gap:18px; }
  /* лого Mirage — самий знак, без напису */
  .brand { display:flex; align-items:center; text-decoration:none; flex:0 0 auto; color:#fff; }
  /* 20px, а не 15: старий знак був суцільною плашкою й читався за будь-якого
     розміру, а хвиля складається з трьох штрихів із просвітами — на 15px вони
     злипались у пляму. Хедер 55px, тож 20px у нього лягає без тісноти. */
  .brand-mark { height:20px; width:auto; display:block; overflow:visible; }
  /* Крихти — частина сторінки, над її заголовком (конвенція Polaris/Primer/Material).
     Показують увесь шлях; поточна сторінка — остання ланка, без посилання. */
  .crumbs { display:flex; align-items:center; gap:8px; min-width:0; margin:0 0 10px;
    font-size:13px; color:var(--muted); }
  .crumbs:empty { display:none; }
  .crumbs a { display:inline-flex; align-items:center; gap:8px; text-decoration:none; color:var(--muted); white-space:nowrap; }
  .crumbs a:hover { color:var(--text); }
  .crumbs .sep { opacity:.45; }
  .crumbs .cur { white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
  .crumbs .crumb-full { display:flex; align-items:center; gap:8px; min-width:0; }
  /* Згорнутий варіант існує завжди, але показується лише у вузькому вікні —
     див. медіазапит нижче й коментар до M.crumbs. */
  .crumbs .crumb-up { display:none; }
  .crumbs .crumb-up svg { width:16px; height:16px; flex-shrink:0; margin-right:-2px; }
  .hdr-right { margin-left:auto; display:flex; align-items:center; gap:4px; }
  .hdr-link {
    display:inline-flex; align-items:center; gap:7px; height:34px; padding:0 12px; border-radius:8px;
    font-size:13px; font-weight:600; color:var(--muted); text-decoration:none;
    transition: background .15s, color .15s;
  }
  .hdr-link:hover { color:var(--text); background:rgba(255,255,255,.06); }
  .hdr-link.active { color:var(--text); background:rgba(255,255,255,.08); }
  .hdr-link svg { width:16px; height:16px; opacity:.85; }

  /* ====== сторінка ====== */
  /* ширина: рамка сторінки — за розділом (сервісні списки/форми вузькі по центру, усе всередині проєкту — широке),
     а форма всередині широкої рамки обмежена колонкою .col */
  .page { max-width: var(--measure); margin: 0 auto; padding: 32px 28px 40px; }
  /* Налаштування — на всю ширину. Поле займає весь простір, а стан ключа
     («Saved», «Rejected») стоїть праворуч від нього, а не під ним: місце там і так порожнє. */
  .pg-settings .key-field, .pg-project-settings .key-field, .pg-project-new .key-field { display:grid; grid-template-columns:minmax(0,1fr) 260px; gap:0 20px; align-items:start; }
  .pg-settings .key-field > label, .pg-project-settings .key-field > label, .pg-project-new .key-field > label { grid-column:1 / -1; }
  .pg-settings .key-field .key-state, .pg-project-settings .key-field .key-state, .pg-project-new .key-field .key-state { margin-top:0; height:40px; }
  .pg-settings .help, .pg-project-settings .help { max-width:72ch; }
  .pg-project-settings .row-between { max-width:none; }
  @media (max-width:820px) {
    .pg-settings .key-field, .pg-project-settings .key-field, .pg-project-new .key-field { grid-template-columns:1fr; }
    .pg-settings .key-field .key-state, .pg-project-settings .key-field .key-state, .pg-project-new .key-field .key-state { margin-top:8px; height:auto; }
  }
  /* заголовок і головна дія — по вертикальному центру одного рядка */
  .page-head { display:flex; align-items:center; justify-content:space-between; gap:16px; margin:0 0 24px; flex-wrap:wrap; }
  /* Блок заголовка мусить СТИСКАТИСЬ, а не міряти себе довжиною назви.
     §07.09: у задачі «DA1: Former Food Inspector Exposes 10 US Ground Beef
     Brands That Don't Use 100% BEEF» flex-basis:auto дорівнював ширині всього
     речення, воно забирало рядок цілком, і «Cancel task» перескакував під
     чіп статусу. Базис 320px — поріг, нижче якого дії переносяться під
     заголовок (телефон); вище вони лишаються праворуч, а назва переноситься
     по словах усередині свого блоку. min-width:0 — щоб довге слово без
     пробілів не розпирало блок за межі рядка. */
  .page-head > div:first-child { flex:1 1 320px; min-width:0; }
  h1 { font-size: clamp(30px, 3.6vw, 44px); font-weight:800; letter-spacing:var(--vr-tight); line-height:1.12; margin:0; }
  .head-actions { display:flex; align-items:center; gap:10px; flex-shrink:0; }
  /* Тихий рядок під заголовком сторінки: «Managed by …». Кегль 13 і колір
     --muted — та сама пара, що в підписах під полями форм; це запис, а не
     дія, і сперечатись із H1 він не мусить. */
  .head-note { font-size:13px; color:var(--muted); margin-top:6px; }
  .head-actions .link-quiet { height:40px; }

  /* контроли: одна висота 40px для кнопок і полів; small = 34px (як посилання хедера); радіус 8 */
  button, .btn {
    display:inline-flex; align-items:center; gap:8px; justify-content:center;
    height:40px; padding:0 16px; background:var(--accent); color:#fff; border:none; border-radius:8px;
    font-size:14px; font-weight:600; cursor:pointer; text-decoration:none; font-family:inherit; line-height:1; white-space:nowrap;
    transition: box-shadow .2s var(--vr-ease), background-color .2s, color .2s, filter .12s;
  }
  /* Наведення НЕ рухає елемент: змінюємо лише світло — тло, тінь, рамку. */
  button:hover, .btn:hover { background:var(--accent-hover); box-shadow:0 6px 18px rgba(0,0,0,.34); }
  button:active, .btn:active { filter:brightness(.94); }
  .btn.secondary, button.secondary { background:#3a3a3c; }
  .btn.secondary:hover, button.secondary:hover { background:#48484a; }
  /* дія на одному обʼєкті: колір дії є, заливки немає — не сперечається з головною кнопкою сторінки */
  .btn.tinted, button.tinted { background:rgba(37,99,235,.16); color:#cfe0ff; box-shadow:inset 0 0 0 1px rgba(126,166,255,.55); }
  .btn.tinted:hover, button.tinted:hover { background:rgba(37,99,235,.26); color:#fff; box-shadow:inset 0 0 0 1px rgba(126,166,255,.75); }
  .btn.ghost, button.ghost { background:transparent; box-shadow:inset 0 0 0 1px var(--border); color:var(--muted); }
  .btn.ghost:hover, button.ghost:hover { background:rgba(255,255,255,.05); color:var(--text); box-shadow:inset 0 0 0 1px #3d3d42; }
  .btn.danger, button.danger { background:#5a1f22; color:#ffb3ad; }
  .btn.danger:hover, button.danger:hover { background:#73282c; }
  .btn.small, button.small { height:34px; padding:0 12px; font-size:13px; }
  .btn svg, button svg { width:16px; height:16px; flex-shrink:0; }
  /* Фокус — контуром, а не тінню: тінь перезаписували локальні правила (.seg, .icon-btn, .ava),
     і частина контролів лишалася без жодної позначки. Контраст контуру ≈5.8:1. */
  :where(button, .btn, a, input, textarea, select, [tabindex], [role="switch"]):focus-visible {
    outline:2px solid #7ea6ff; outline-offset:2px; box-shadow:none; }
  button:disabled, button:disabled:hover { opacity:.45; cursor:not-allowed; box-shadow:none; background:var(--accent); filter:none; }
  /* іконкова кнопка без рамки: 32px, іконка 16 */
  .icon-btn { width:32px; height:32px; padding:0; background:transparent; border:none; color:var(--muted); border-radius:8px; box-shadow:none; }
  .icon-btn:hover { background:rgba(255,255,255,.06); color:var(--text); box-shadow:none; }
  .icon-btn svg { width:16px; height:16px; }
  /* іконка статусу: 13px, зсув на 1px вниз — центр іконки на оптичному центрі малих літер, а не на центрі рядка */
  .ico { width:13px; height:13px; flex-shrink:0; margin-top:1px; }

  section {
    background: var(--panel-glass); border:1px solid var(--glass-border); border-radius:16px;
    padding:24px; margin-bottom:16px;
    backdrop-filter: var(--glass-blur); -webkit-backdrop-filter: var(--glass-blur); box-shadow: var(--glass-shadow);
  }
  section > h2 {
    font-size:12px; text-transform:uppercase; letter-spacing:.12em; color:var(--muted); font-weight:700;
    margin:0 0 16px; display:flex; align-items:center; justify-content:space-between; gap:10px;
  }
  /* суцільний острівець під заголовком секції (заголовок — назовні, на краю сторінки) */
  .panel { background:var(--panel-glass); border:1px solid var(--glass-border); border-radius:16px; padding:24px;
    backdrop-filter:var(--glass-blur); -webkit-backdrop-filter:var(--glass-blur); box-shadow:var(--glass-shadow); }
  .panel > .job-card-compact:last-child { margin-bottom:0; }
  .panel > #projList > .project-card:last-child { margin-bottom:0; }
  /* вкладки проєкту */
  .tabs { display:flex; gap:2px; border-bottom:1px solid rgba(255,255,255,.08); margin:0 0 24px; }
  .tab { height:40px; padding:0 12px; display:inline-flex; align-items:center; font-size:13px; font-weight:600; color:var(--muted);
    text-decoration:none; border-bottom:2px solid transparent; margin-bottom:-1px; transition:color .15s; }
  .tab:hover { color:var(--text); }
  .tab.active { color:var(--text); border-bottom-color:var(--accent); }
  /* «IN PROGRESS · 3»: назва, крапка й число — окремі елементи з точними проміжками; розрядка капсу на них не діє */
  section > h2 > span:first-child { display:inline-flex; align-items:baseline; gap:7px; }
  section > h2 .cnt { font-weight:600; letter-spacing:0; font-variant-numeric:tabular-nums; opacity:.6; }
  section > h2 .cnt::before { content:'·'; margin-right:7px; }
  section.flat { background:none; border:none; box-shadow:none; backdrop-filter:none; padding:0; margin-bottom:32px; }
  section.flat > h2 { margin-bottom:16px; }
  /* Заголовок-розкривач. Єдиний на сервіс — архів (§запит користувача 23.08).
     Кнопка мусить виглядати рівно як заголовок поруч, тож збиває з себе весь
     вигляд кнопки: тло, висоту, поля й власний шрифт. `font:inherit` не несе
     ні розрядки, ні капсу — їх додаємо окремо. */
  .h2-toggle {
    height:auto; padding:0; border-radius:0; background:none; box-shadow:none;
    font:inherit; letter-spacing:inherit; text-transform:inherit; color:inherit; gap:7px;
  }
  .h2-toggle:hover { background:none; box-shadow:none; color:var(--text); }
  .h2-toggle .chev { width:13px; height:13px; color:var(--muted); transition:transform .18s var(--vr-ease); }
  .h2-toggle[aria-expanded="true"] .chev { transform:rotate(180deg); }
  .h2-toggle .cnt { letter-spacing:0; }
  /* між роботою в процесі і каталогом готового — крок більший, ніж усередині розділу */
  #projContent > section.flat:first-of-type { margin-bottom:40px; }
  section > .job-card-compact:last-child { margin-bottom:0; }

  /* типографічна шкала форм: підпис 13/600 → пояснення 13/400 сіре (у тому ж рядку) → під полем: help 12/400 сіре, статус 12/600 кольоровий.
     Половинних розмірів (12.5 / 13.5) у формах немає. Відступ під контролом — завжди 6px. */
  label { display:block; font-size:13px; font-weight:600; margin-bottom:8px; color:var(--text); }
  label .lbl-desc { font-weight:400; color:var(--muted); }
  /* input[type=password] додано 2026-08-23: поле другого пароля на екрані
     витрат лишалось системним — вузька світла коробочка посеред панелі. */
  input[type=text], input[type=password], input[type=number], textarea, select {
    width:100%; height:40px; background:#1e1e20; border:1px solid var(--border); border-radius:8px;
    color:var(--text); padding:0 12px; font-size:14px; font-family:inherit; line-height:1;
  }
  select { appearance:none; -webkit-appearance:none; padding-right:36px; cursor:pointer;
    background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23a3a3a3' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
    background-repeat:no-repeat; background-position:right 12px center; }
  textarea { height:auto; padding:10px 12px; line-height:1.5; }
  input:focus, textarea:focus, select:focus { outline:none; border-color:var(--accent); box-shadow:0 0 0 3px rgba(37,99,235,.18); }
  textarea { min-height:110px; resize:vertical; }
  .field { margin-bottom:16px; }
  .mt-md { margin-top:16px; }
  .row-between { display:flex; align-items:center; justify-content:space-between; gap:24px; }
  .row-between .help { margin:0; }
  .mt-lg { margin-top:24px; }
  .field.last, .key-field.last, section > .last:last-child { margin-bottom:0; }
  .help { color:var(--muted); font-size:12px; margin-top:6px; line-height:1.45; }
  .row { display:flex; gap:10px; align-items:center; }
  .key-input { letter-spacing:.02em; }
  .row:has(.eye) { position:relative; }
  .row:has(.eye) input { padding-right:42px; }
  /* create project */
  .key-field { margin-bottom:16px; }
  .key-state { font-size:12px; font-weight:500; line-height:16px; margin-top:8px; min-height:16px; color:var(--muted); display:flex; align-items:center; gap:6px; }
  .key-state.ok { color:var(--ok); }
  .key-state.bad { color:var(--bad); }
  .key-state.checking { color:#9dbcff; }
  /* Крутилка — один компонент на сервіс. Форму й обертання задає .spin,
     розмір і колір — місце вживання. */
  .spin { border-radius:50%; flex-shrink:0; animation:spin .8s linear infinite; }
  .key-state .spin { width:10px; height:10px; border:1.5px solid rgba(157,188,255,.35); border-top-color:#9dbcff; }
  @keyframes spin { to { transform:rotate(360deg); } }
  .key-field.ok input { border-color:rgba(48,209,88,.45); }
  .key-field.bad input { border-color:rgba(255,122,112,.55); }
  #copyBlock, #newBlock { margin-top:16px; }
  .seg { display:inline-flex; padding:3px; gap:2px; background:rgba(0,0,0,.25); border:1px solid var(--border); border-radius:8px; }
  .seg button { height:32px; background:transparent; color:var(--muted); padding:0 12px; font-size:13px; border-radius:8px; box-shadow:none; }
  .seg button:hover { background:rgba(255,255,255,.05); color:var(--text); box-shadow:none; }
  .seg button.on { background:rgba(255,255,255,.1); color:#fff; }
  .eye { position:absolute; right:4px; top:50%; transform:translateY(-50%); }
  .eye:hover { transform:translateY(-50%); }
  /* дії секції — у самій секції, праворуч під її вмістом */
  .sec-foot { display:flex; align-items:center; justify-content:flex-end; gap:12px; margin-top:20px;
    padding-top:20px; border-top:1px solid rgba(255,255,255,.07); }
  .form-foot { display:flex; align-items:center; justify-content:flex-end; gap:12px; margin-top:24px; }
  .foot-hint { margin-right:auto; font-size:12px; font-weight:500; color:var(--bad); }

  /* проєкти: картка — div, клікабельна через розтягнуте посилання назви; кнопки праворуч живуть поверх */
  .project-card {
    position:relative; display:flex; align-items:center; justify-content:space-between; gap:14px;
    background-image: var(--vr-card); border:1px solid var(--vr-hair); border-radius:12px;
    padding:16px; margin-bottom:8px; color:inherit;
    transition: box-shadow .2s var(--vr-ease), border-color .2s var(--vr-ease);
  }
  .project-link { color:inherit; text-decoration:none; }
  .project-link::after { content:''; position:absolute; inset:0; border-radius:inherit; }
  .project-link:focus-visible { outline:none; }
  .project-card:has(.project-link:focus-visible) { box-shadow:0 0 0 3px rgba(37,99,235,.35); }
  .project-card:hover { box-shadow:0 10px 26px rgba(0,0,0,.36); border-color:rgba(255,255,255,.2); }
  .project-name { font-size:16px; font-weight:700; letter-spacing:-.02em; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  .project-main { min-width:0; display:flex; flex-direction:column; gap:3px; }
  .project-side { position:relative; z-index:1; display:flex; align-items:center; gap:8px; flex-shrink:0; }
  .project-meta { font-size:12px; color:var(--muted); font-variant-numeric:tabular-nums; }
  /* власний випадний список — у стилі сервісу, не системний */
  .dd { position:relative; }
  .dd-trigger { width:100%; height:40px; padding:0 12px; background:#1e1e20; color:var(--text); box-shadow:inset 0 0 0 1px var(--border);
    border-radius:8px; font-size:14px; font-weight:400; justify-content:space-between; text-align:left; }
  .dd-trigger:hover { background:#1e1e20; box-shadow:inset 0 0 0 1px #3d3d42; }
  .dd.open .dd-trigger, .dd-trigger:focus-visible { box-shadow:inset 0 0 0 1px var(--accent), 0 0 0 3px rgba(37,99,235,.18); }
  .dd-trigger .chev { width:16px; height:16px; color:var(--muted); transition:transform .18s var(--vr-ease); }
  .dd.open .dd-trigger .chev { transform:rotate(180deg); }
  .dd-menu { position:absolute; left:0; right:0; top:calc(100% + 6px); z-index:30; margin:0; padding:6px; list-style:none;
    background:linear-gradient(135deg, rgba(255,255,255,.06) 0%, rgba(255,255,255,0) 55%), rgba(28,28,30,.97);
    border:1px solid var(--glass-border); border-radius:8px; box-shadow:0 12px 32px rgba(0,0,0,.45);
    backdrop-filter:blur(18px) saturate(160%); -webkit-backdrop-filter:blur(18px) saturate(160%); display:none; }
  .dd.open .dd-menu { display:block; animation:riseIn .14s var(--vr-ease) both; }
  .dd-menu li { display:flex; align-items:center; justify-content:space-between; gap:10px; height:36px; padding:0 10px; border-radius:6px;
    font-size:14px; font-weight:400; color:var(--text); cursor:pointer; }
  .dd-menu li:hover { background:rgba(255,255,255,.06); }
  .dd-menu li svg { width:14px; height:14px; visibility:hidden; color:#9dbcff; }
  .dd-menu li[aria-selected="true"] svg { visibility:visible; }
  /* один бейдж статусу на весь сервіс: .chip і .job-status — той самий компонент */
  .chip, .job-status { display:inline-flex; align-items:center; gap:6px; font-size:11px; font-weight:600; padding:3px 9px; border-radius:999px; white-space:nowrap; border:1px solid currentColor; text-decoration:none; }
  .chip::before, .job-status::before { content:''; width:5px; height:5px; border-radius:50%; background:currentColor; }
  .chip-warn { color:#ffc770; background:rgba(255,199,112,.09); border-color:rgba(255,199,112,.32); }
  .chip-info { color:#7ec1ff; background:rgba(126,193,255,.09); border-color:rgba(126,193,255,.32); }
  .chip-bad  { color:#ff7a70; background:rgba(255,122,112,.09); border-color:rgba(255,122,112,.32); cursor:pointer; }
  .chip-bad:hover { background:rgba(255,122,112,.18); }
  /* архівовані проєкти: згорнутий рядок під списком */
  .archived-row { margin-top:16px; padding-top:16px; border-top:1px solid rgba(255,255,255,.07); }
  .link-btn { height:auto; padding:0; background:none; color:var(--muted); font-size:12px; font-weight:600; box-shadow:none; }
  .link-btn:hover { background:none; color:var(--text); box-shadow:none; }
  #archList { margin-top:12px; }
  .project-card.archived { opacity:.75; }
  .project-card.archived:hover { opacity:1; }

  /* картка задачі (компактна) — як у index.html */
  .job-card-compact {
    display:grid; grid-template-columns:minmax(0,1fr) auto; align-items:center; column-gap:12px; row-gap:8px;
    background-image:var(--vr-card); border:1px solid var(--vr-hair); border-radius:12px;
    padding:16px; margin-bottom:8px; text-decoration:none; color:inherit;
    transition: box-shadow .2s var(--vr-ease), border-color .2s var(--vr-ease);
  }
  .job-card-compact:hover { box-shadow:0 10px 26px rgba(0,0,0,.36); border-color:rgba(255,255,255,.18); }
  .jcc-title { font-size:14px; font-weight:700; letter-spacing:-.02em; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  .jcc-msg { font-size:12px; color:var(--muted); grid-column:1/-1; }
  /* Помилка збійної задачі в списку. Два рядки максимум: текст із пайплайна
     буває довгий (повний шлях до файлу), а картка в списку — не місце для
     повного вердикту, він на екрані задачі. Колір той самий, що в бейджа
     Failed, щоб рядок читався як його продовження, а не як окрема тривога. */
  .jcc-err { color:var(--bad); display:-webkit-box; -webkit-line-clamp:2;
    -webkit-box-orient:vertical; overflow:hidden; }
  .job-progress-row { grid-column:1/-1; display:flex; align-items:center; gap:10px; }
  .job-progress-bar { flex:1; height:4px; border-radius:999px; background:rgba(255,255,255,.06); overflow:hidden; }
  .job-progress-fill { height:100%; border-radius:999px; background:var(--accent); }
  .job-progress-pct { font-size:12px; font-weight:500; color:var(--muted); font-variant-numeric:tabular-nums; min-width:32px; text-align:right; }
  /* бейдж усередині рядка таблиці — дрібніший за самостійний */
  .chip-sm { font-size:10px; padding:1px 7px; vertical-align:middle; margin-left:8px; }
  .chip-sm::before { width:4px; height:4px; }
  .chip-draft { color:var(--warn); background:rgba(255,159,10,.09); border-color:rgba(255,159,10,.38); }
  .chip-draft::before { display:none; }
  /* порожній стан проєкту */
  .empty-state { display:flex; flex-direction:column; align-items:center; text-align:center; padding:56px 28px; gap:6px; }
  .empty-title { font-size:18px; font-weight:700; letter-spacing:-.02em; }
  .empty-sub { font-size:13px; color:var(--muted); margin-bottom:14px; }
  /* Кольори статусів = що вони вимагають від людини:
     синій — потрібна твоя дія, сірий — система працює сама, зелений — готово, червоний — впало. */
  .job-status-awaiting_approval,
  .job-status-awaiting_prompt_review,
  .job-status-awaiting_broll_approval { color:#7ea6ff; background:rgba(37,99,235,.14); border-color:rgba(126,166,255,.45); }

  /* поверхня рядка відео — та сама картка */
  .video-card { display:block; text-decoration:none; color:inherit;
    background-image:var(--vr-card); border:1px solid var(--vr-hair); border-radius:16px; overflow:hidden;
    transition: box-shadow .2s var(--vr-ease), border-color .2s var(--vr-ease); }
  /* список замість сітки */
  .videos-list { display:flex; flex-direction:column; gap:8px; }
  /* порядок колонок: обʼєкт → коли зроблено → скільки триває → скільки коштувало */
  /* ВІДСТУПИ: праве поле 24px — стільки ж, скільки внутрішнє поле панелі.
     Було 14px, потім 16px, і користувач обидва рази сказав, що остання цифра
     тиснеться до краю. 16 дорівнювало проміжку між колонками — а саме тому й
     не працювало: край картки читається як межа, і поле біля нього мусить
     бути більшим за проміжок між сусідніми числами, інакше вартість зорово
     вивалюється з таблиці. Ліве поле лишається 8px: там мініатюра, і вона
     навмисно підходить до краю ближче, ніж текст.

     ЯК ШАПКА ТРИМАЄТЬСЯ НА ОДНІЙ ВЕРТИКАЛІ З ЧИСЛАМИ. Стояло 8/14 у рядка
     проти 0/15 у шапки, і це виглядало як помилка, але нею не було: зайвий
     піксель праворуч компенсував рамку, якої в шапки немає (box-sizing
     border-box, рамка зʼїдає піксель усередині). Компенсація руками ламається
     від будь-якої зміни відступу — варто було поставити рівні 16/16, і
     стовпці розʼїхались на той самий піксель, тільки в інший бік (заміряно
     getBoundingClientRect: 1061 проти 1060).

     Тому тепер шапка НЕСЕ ТУ САМУ РАМКУ, просто прозору. Геометрія в обох
     однакова за побудовою, і рахувати пікселі більше не треба. На Usage
     рамки немає в обох (див. нижче) — там теж збігається. */
  .video-card { background-color:var(--vr-row-bg); background-image:var(--vr-row);
    backdrop-filter:blur(14px) saturate(140%); -webkit-backdrop-filter:blur(14px) saturate(140%); }
  /* ПРОМІЖОК МІЖ КОЛОНКАМИ 16px -> 24px (§прохання користувача 23.08). Ширини
     колонок і праве поле не мінялись, тож вартість лишилась рівно там, де
     стояла, — а решта чисел роз'їхалась вліво тим більше, чим лівіше стоїть:
     $/min на 8px, Length на 16, Created на 24. Саме цього й просили: «останню
     не чіпай, всі інші трошки ліворуч».
     Проміжок один на весь рядок, тож і мініатюра з назвою розійшлись на ті
     самі 24 — рядок лишається однією сіткою, а не двома різними. */
  .video-row { display:grid; grid-template-columns:96px minmax(0,1fr) 110px 64px 72px 72px; align-items:center; gap:24px;
    padding:8px 24px 8px 8px; border-radius:12px; border:1px solid rgba(255,255,255,.06); text-decoration:none; color:inherit;
    transition: box-shadow .2s var(--vr-ease), border-color .2s var(--vr-ease); }
  /* Тінь на наведення легша за карткову: рядок і в спокої тихіший, а різкий
     підйом повертав би йому ту саму вагу, яку щойно прибрали. */
  .video-row:hover { border-color:rgba(255,255,255,.16); box-shadow:0 6px 16px rgba(0,0,0,.26); }
  .video-row .thumb { position:relative; aspect-ratio:16/9; border-radius:8px; overflow:hidden; background:#0e0e10; }
  .video-row .thumb .ph { position:absolute; inset:0; }
  .video-row .t { font-size:14px; font-weight:700; letter-spacing:-.015em; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
  .video-row .m { font-size:11px; color:var(--muted); font-variant-numeric:tabular-nums; white-space:nowrap; text-align:right; }
  .video-row.head { padding:0 24px 8px 8px; border-color:transparent; background:none; backdrop-filter:none; font-size:11px; font-weight:700;
    letter-spacing:.08em; text-transform:uppercase; color:var(--muted); }
  .video-row.head:hover { transform:none; box-shadow:none; }
  .video-row.head .m { font-size:11px; font-weight:700; }
  .video-row.head .hl { grid-column:1 / 3; }
  /* Вартість — те число, заради якого в таблицю й дивляться, тож воно єдине
     виходить із приглушеного сірого. :not(.head), бо в шапці «COST» має
     лишитись таким самим підписом, як сусідні. */
  .video-row:not(.head) .m.cost { color:var(--text); font-weight:700; }

  /* майстер створення відео */
  /* ── Новий відеозапис: кроки ліворуч, кошторис і запуск праворуч ───────── */
  .wz-grid { display:grid; grid-template-columns:minmax(0,1fr) 300px; gap:16px; align-items:start; }
  @media (max-width:960px) { .wz-grid { grid-template-columns:1fr; } }
  .wz-side section { position:sticky; top:76px; margin-bottom:0; }
  .stepper-bar { display:flex; align-items:center; gap:12px; }
  .wz-bar-grid { margin-bottom:16px; }
  .stepper { display:flex; gap:8px; }
  .step-arrows { margin-left:auto; display:flex; gap:6px; }
  .step-arrow { width:36px; height:36px; border-radius:8px; box-shadow:inset 0 0 0 1px var(--border); }
  .step-arrow:hover { background:rgba(255,255,255,.09); box-shadow:inset 0 0 0 1px #5c5c62; }
  .step-arrow:disabled { opacity:.35; box-shadow:inset 0 0 0 1px var(--border); background:none; }
  .step-pill { height:36px; padding:0 14px; gap:9px; border-radius:8px; font-size:13px; font-weight:600;
    background:rgba(255,255,255,.05); color:var(--muted); box-shadow:inset 0 0 0 1px var(--border); }
  .step-pill:hover { background:rgba(255,255,255,.09); color:var(--text); box-shadow:inset 0 0 0 1px #5c5c62; }
  .step-pill .sp-n { display:flex; align-items:center; justify-content:center; width:19px; height:19px; border-radius:5px;
    background:rgba(255,255,255,.1); font-size:11px; font-weight:700; }
  .step-pill.on { background:rgba(37,99,235,.16); color:#fff; box-shadow:inset 0 0 0 1px rgba(126,166,255,.5); }
  .step-pill.on .sp-n { background:var(--accent); color:#fff; }
  .step-pill.done .sp-n { background:rgba(48,209,88,.2); color:#30d158; }
  /* Крапка на кроці, де чогось бракує (§15.09). Чого саме — у title пігулки
     і в рядку під кнопкою; тут лише «сюди варто повернутись». Колір
     застережливий, не аварійний: нічого не зламано, просто не заповнено. */
  .step-pill.warn { color:var(--text); }
  .step-pill.warn::after { content:''; width:6px; height:6px; margin-left:-3px;
    border-radius:50%; background:var(--warn); flex:0 0 auto; }

  /* Поле, якого бракує. Ставиться лише після дотику або після натиску на
     неактивну кнопку — свіжа форма не червона. */
  .need-bad > label { color:var(--bad); }
  .need-bad input[type=text], .need-bad textarea { border-color:rgba(255,122,112,.55); }
  .need-bad .dd-trigger { box-shadow:inset 0 0 0 1px rgba(255,122,112,.55); }
  /* Сітка аватарів — не поле, рамки в неї нема. outline замість border:
     він не займає місця й нічого не зсуває. */
  .need-bad .avatar-grid { outline:1px solid rgba(255,122,112,.55); outline-offset:8px; border-radius:2px; }
  /* однакова висота кроків — Back/Next не стрибають при перемиканні */
  .wz-stage { min-height:496px; }
  .wz-stage > section { margin-bottom:0; }
  .grid-num { display:grid; grid-template-columns:minmax(0,1fr) 160px; gap:0 14px; }
  @media (max-width:560px) { .grid-num { grid-template-columns:1fr; } }
  .wz-foot-row { display:flex; align-items:center; justify-content:space-between; gap:12px; margin-top:8px; }
  .wz-count { font-size:12px; color:var(--muted); font-variant-numeric:tabular-nums; }
  /* поле сценарію: підказка всередині порожнього поля, своя смуга прокрутки */
  .drop { position:relative; }
  /* display:block обов'язковий. Як inline-block textarea лишає під собою
     базову лінію рядка (заміряно: контейнер 225px проти поля 220px), і картка
     файлу, що стоїть по inset:0 контейнера, вилазила на ті 5px нижче за рамку
     поля — вона й виглядала «нерівною». */
  #wz-script { display:block; min-height:220px; max-width:none; resize:none; scrollbar-width:thin; scrollbar-color:rgba(255,255,255,.22) transparent; }
  #wz-script::-webkit-scrollbar { width:10px; }
  #wz-script::-webkit-scrollbar-track { background:transparent; }
  #wz-script::-webkit-scrollbar-thumb { background:rgba(255,255,255,.18); border-radius:999px; border:3px solid transparent; background-clip:content-box; }
  #wz-script::-webkit-scrollbar-thumb:hover { background:rgba(255,255,255,.3); background-clip:content-box; }
  .drop-empty { position:absolute; inset:0; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:8px;
    pointer-events:none; color:var(--muted); font-size:13px; text-align:center; padding:20px; }
  .drop-empty svg { width:26px; height:26px; opacity:.7; }
  .drop-empty small { font-size:11px; opacity:.7; }
  .drop-empty .link-quiet { pointer-events:auto; height:auto; color:#9dbcff; }
  .drop-empty.gone { display:none; }
  .drop-hint { position:absolute; inset:0; display:none; align-items:center; justify-content:center; border-radius:8px;
    background:rgba(37,99,235,.14); box-shadow:inset 0 0 0 2px var(--accent); color:#cfe0ff; font-size:14px; font-weight:600; }
  .drop.over .drop-hint { display:flex; }
  /* Дві підказки одночасно накладались одна на одну — під час перетягування
     лишається тільки «Drop the file here». */
  .drop.over .drop-empty { visibility:hidden; }
  /* прикріплений файл: поле не лишається порожнім, у ньому стоїть картка файлу */
  .drop-file { position:absolute; inset:0; display:flex; flex-direction:column; align-items:center; justify-content:center;
    gap:10px; padding:20px; text-align:center; border-radius:8px; background:rgba(37,99,235,.07);
    box-shadow:inset 0 0 0 1px rgba(37,99,235,.35); }
  .drop-file.gone { display:none; }
  .drop-file svg { width:30px; height:30px; color:#9dbcff; }
  .drop-file-n { font-size:14px; font-weight:600; word-break:break-all; max-width:100%; }
  .drop-file-w { font-size:12px; color:var(--muted); font-variant-numeric:tabular-nums; }
  .drop-file .link-quiet { height:auto; color:#9dbcff; margin-top:2px; }
  /* «More» — рідко вживані параметри; навмисно не схоже на поле вводу */
  /* розкриття рідко вживаних параметрів: рядок на власній поверхні, не схожий ні на поле, ні на список */
  .more { width:100%; justify-content:flex-start; gap:10px; height:44px; margin-top:24px; padding:0 14px; border-radius:8px;
    background:rgba(255,255,255,.04); color:var(--text); font-size:13px; font-weight:600; box-shadow:inset 0 0 0 1px var(--border); }
  .more:hover { background:rgba(255,255,255,.07); box-shadow:inset 0 0 0 1px #5c5c62; }
  .more[aria-expanded="true"] { border-radius:8px 8px 0 0; background:rgba(255,255,255,.05); }
  .more .chev { margin-left:auto; width:16px; height:16px; color:var(--muted); transition:transform .18s var(--vr-ease); }
  .more[aria-expanded="true"] .chev { transform:rotate(180deg); }
  .more-body { padding:20px 14px 4px; background:rgba(255,255,255,.02);
    box-shadow:inset 1px 0 0 var(--border), inset -1px 0 0 var(--border), inset 0 -1px 0 var(--border);
    border-radius:0 0 8px 8px; }
  /* заголовок-рядок усередині кроку (тумблер аватара) */
  .panel-head { display:flex; align-items:center; justify-content:space-between; gap:16px; margin-bottom:16px; }
  .panel-head-t { font-size:14px; font-weight:600; }
  .warn-d { color:#e8c48a; }
  /* панель кошторису */
  /* пари «назва — значення»: зведення, деталі, підсумки */
  .kv { display:grid; grid-template-columns:auto minmax(0,1fr); gap:10px 16px; font-size:13px; font-variant-numeric:tabular-nums; }
  .kv > span:nth-child(odd) { color:var(--muted); white-space:nowrap; }
  .kv > span:nth-child(even) { text-align:right; white-space:nowrap; }
  /* три показники кошторису: головний зверху, два допоміжні в ряд */
  .est-num { font-size:24px; font-weight:800; letter-spacing:-.03em; font-variant-numeric:tabular-nums; line-height:1.1; }
  .est-num.sm { font-size:16px; font-weight:700; }
  .est-cap { font-size:11px; font-weight:500; letter-spacing:.06em; text-transform:uppercase; color:rgba(163,163,163,.75); margin-top:6px; }
  .est-pair { display:grid; grid-template-columns:1fr 1fr; gap:16px; margin-top:16px; padding-top:16px;
    border-top:1px solid rgba(255,255,255,.07); }
  .wz-run { width:100%; height:44px; margin-top:24px; }
  /* Поки сервер відповідає. Заміряно на стенді 23.08: до відповіді /generate
     ходить у ElevenLabs (1.3с) і, коли аватар увімкнено, у каталог HeyGen
     (11.4с на холодному кеші, 0 з кешу). Доти кнопка просто гасла до 45%
     непрозорості — і ці секунди читались як «не натиснулось».
     Синій тут за змістом системи: синє = робота йде. */
  .wz-run.busy, .wz-run.busy:disabled, .wz-run.busy:disabled:hover {
    opacity:1; background:var(--accent); color:#fff; cursor:progress; }
  .wz-run .spin { width:15px; height:15px; border:2px solid rgba(255,255,255,.45); border-top-color:#fff; }
  .est-note { font-size:11px; color:var(--muted); text-align:center; margin-top:8px; white-space:nowrap; }
  /* Перелік того, чого бракує, у 300px колонку одним рядком не влазить. */
  .est-note.needs { white-space:normal; line-height:1.6; }
  /* Пункт переліку — кнопка, що везе на своє поле. Виглядає як посилання. */
  .need-link { display:inline; height:auto; padding:0; background:none; box-shadow:none;
    color:#9dbcff; font:inherit; text-decoration:underline; text-underline-offset:2px; }
  .need-link:hover { background:none; box-shadow:none; color:#c3d7ff; }
  /* Неактивна кнопка запуску: вигляд вимкненої (як button:disabled), але клік
     живий — він відводить до першого незаповненого поля. Тому й курсор
     звичайний, а не not-allowed: натиснути тут є сенс. */
  .wz-run[aria-disabled="true"], .wz-run[aria-disabled="true"]:hover {
    opacity:.45; box-shadow:none; background:var(--accent); filter:none; }
  /* повзунки */
  .sliders { display:grid; gap:20px; }
  /* Повзунки (07.09.2026, прохання користувача): тонша лінія, без синього —
     щоб панель налаштувань голосу не кричала. Заповнена частина — приглушений
     сірий, бігунок — темний диск із тонкою обвідкою, що світлішає під курсором. */
  .slider label { display:flex; align-items:center; justify-content:space-between; font-size:13px; font-weight:500; margin-bottom:10px; }
  .slider label b { font-weight:500; font-size:13px; color:var(--muted); font-variant-numeric:tabular-nums; }
  .slider input[type=range] { display:block; width:100%; height:2px; margin:6px 0; -webkit-appearance:none; appearance:none; border-radius:999px; outline:none; cursor:pointer;
    background:linear-gradient(90deg, #9a9a9f var(--p,50%), rgba(255,255,255,.12) var(--p,50%)); }
  .slider input[type=range]::-webkit-slider-thumb { -webkit-appearance:none; width:14px; height:14px; border-radius:999px;
    background:var(--panel); border:1.5px solid #9a9a9f; cursor:pointer; box-shadow:none; transition:border-color .15s, background .15s; }
  .slider input[type=range]:hover::-webkit-slider-thumb, .slider input[type=range]:focus-visible::-webkit-slider-thumb { border-color:var(--text); }
  .slider input[type=range]:active::-webkit-slider-thumb { background:var(--text); border-color:var(--text); }
  .slider input[type=range]::-moz-range-track { height:2px; background:transparent; }
  .slider input[type=range]::-moz-range-thumb { width:14px; height:14px; border-radius:999px; background:var(--panel); border:1.5px solid #9a9a9f; }
  /* тумблер speaker boost у панелі голосу — тим самим кеглем, що підписи повзунків */
  #voiceSet .toggle-row { margin-top:6px; }
  #voiceSet .toggle-row .t { font-size:13px; font-weight:500; }
  .grid2 { display:grid; grid-template-columns:1fr 1fr; gap:0 16px; }
  @media (max-width:640px) { .grid2 { grid-template-columns:1fr; } }
  /* пошук і «додати» всередині випадного списку */
  .dd-search-row { padding:2px 2px 8px; }
  .dd-q { height:32px; font-size:13px; }
  .dd-opts { margin:0; padding:0; list-style:none; max-height:230px; overflow:auto; }
  .dd-add { width:100%; justify-content:flex-start; gap:8px; height:36px; margin-top:6px; padding:0 10px; border-radius:6px;
    background:none; color:#9dbcff; font-size:13px; font-weight:600; box-shadow:none; }
  .dd-add:hover { background:rgba(37,99,235,.14); color:#cfe0ff; box-shadow:none; }
  .dd-add svg { width:15px; height:15px; }
  .dd-opts li.off { opacity:.5; pointer-events:none; }
  /* §стейл-голоси: обрати можна (раптом каталог збрехав — перевірка fail-open),
     але видно, що на поточному акаунті його немає */
  .dd-opts li.stale { opacity:.62; }
  .dd-note { font-size:11px; font-weight:600; color:#e0a3a3; margin-left:6px; white-space:nowrap; }
  /* Назва тягнеться, решта тулиться праворуч. */
  .dd-lbl { flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  /* Кошик у рядку списку. Правило .dd-menu li svg ховає ВСІ іконки, крім
     вибраної, — тому видимість повертаємо явно, а показ-на-наведення робимо
     через opacity, не через visibility. На мертвому записі видно завжди. */
  .dd-del { flex:none; width:22px; height:22px; padding:0; border:none; border-radius:6px; cursor:pointer;
    display:flex; align-items:center; justify-content:center; background:none; color:#8b8f9a;
    opacity:0; transition:opacity .12s, background .12s, color .12s; }
  .dd-del svg { visibility:visible; width:13px; height:13px; color:inherit; }
  .dd-menu li:hover .dd-del, .dd-del:focus-visible, .dd-opts li.stale .dd-del { opacity:1; }
  .dd-del:hover { background:#5a1f22; color:#ffb3ad; }
  /* аватари */
  .avatar-grid { display:grid; grid-template-columns:repeat(auto-fill, minmax(132px,1fr)); gap:12px; }
  .ava, .ava-add { display:block; width:100%; height:auto; aspect-ratio:1/1; position:relative; padding:0;
    border-radius:12px; overflow:hidden; background:#0e0e10; box-shadow:inset 0 0 0 1px var(--border); }
  .ava .ph { position:absolute; inset:0; }
  /* Прев'ю HeyGen — велике фото (у заміряному випадку 2752×1536). Без цього
     правила <img class="ph"> лягав у плитку СВОЇМ розміром від лівого верхнього
     кута, і в 150-піксельному квадраті було видно шматок стіни замість обличчя.
     object-position по вертикалі працює лише для портретних кадрів — у
     горизонтальних висота й так заповнена; 25% там нічого не змінює. */
  .ava img.ph { width:100%; height:100%; object-fit:cover; object-position:50% 25%; }
  .ava .ava-n { position:absolute; left:0; right:0; bottom:0; padding:24px 12px 12px; font-size:12px; font-weight:600;
    line-height:1.3; text-align:left; background:linear-gradient(transparent, rgba(0,0,0,.85)); }
  .ava { transition:box-shadow .15s, opacity .15s; }
  .ava[aria-checked="true"] { box-shadow:inset 0 0 0 2px var(--accent), 0 0 0 3px rgba(37,99,235,.32); }
  .ava[aria-checked="true"] img.ph { filter:saturate(1.06) brightness(1.04); }
  .ava[aria-checked="true"] .ava-n { background:linear-gradient(transparent, rgba(29,78,216,.92)); color:#fff; }
  /* поки один вибраний — решта пригашені, щоб вибір читався з відстані */
  .avatar-grid.has-pick .ava:not([aria-checked="true"]):not(.ava-dead):not(:hover) { opacity:.5; }
  /* квадратний чекбокс — один компонент на весь сервіс: плитки аватарів і кадри на затвердження */
  .pick-box { width:22px; height:22px; padding:0; border:none; border-radius:6px; display:flex; align-items:center; justify-content:center;
    background:rgba(10,12,18,.5); box-shadow:inset 0 0 0 1.5px rgba(255,255,255,.55); color:transparent; }
  .pick-box svg { width:13px; height:13px; }
  .pick-box:hover { box-shadow:inset 0 0 0 1.5px rgba(255,255,255,.8); background:rgba(10,12,18,.5); }
  .ava .pick-box { position:absolute; top:8px; right:8px; }
  .ava[aria-checked="true"] .pick-box, .pick-box[aria-checked="true"] { background:var(--accent); color:#fff; box-shadow:none; }
  .pick-box[aria-checked="true"]:hover { background:var(--accent); box-shadow:none; }
  /* Клітинка аватара: плитка + кошик поруч. Обгортка потрібна тому, що плитка
     сама по собі <button>, а вкладений <button> — невалідна розмітка. */
  .ava-cell { position:relative; }
  .ava-del { position:absolute; top:8px; left:8px; width:22px; height:22px; padding:0; border:none;
    border-radius:6px; display:flex; align-items:center; justify-content:center; cursor:pointer;
    background:rgba(10,12,18,.55); color:#e8eaf0; opacity:0; transition:opacity .15s, background .15s; }
  .ava-del svg { width:13px; height:13px; }
  .ava-del:hover { background:#5a1f22; color:#ffb3ad; }
  /* Показуємо на наведення — щоб кошик не змагався за увагу з вибором. АЛЕ на
     мертвому записі він видимий завжди: саме його там і шукають, а вибрати
     таку плитку однаково не можна. */
  .ava-cell:hover .ava-del, .ava-del:focus-visible, .ava-cell-dead .ava-del { opacity:1; }
  .ava-add { display:flex; flex-direction:column; align-items:center; justify-content:center; gap:8px;
    background:rgba(37,99,235,.05); box-shadow:inset 0 0 0 1px rgba(37,99,235,.45); color:#9dbcff; font-size:12px; font-weight:600; }
  .ava-add:hover { background:rgba(37,99,235,.12); box-shadow:inset 0 0 0 1px var(--accent); color:#cfe0ff; }
  .ava-add svg { width:22px; height:22px; }
  /* Архів — надгробок, а не картка (§запит користувача 23.08: «максимально
     непримітною»). Порожня рамка без заливки й розмиття, без жодного підпису:
     скільки коштувала зупинена задача й чому вона стала, видно на її сторінці
     та у вкладці Usage — у списку це шум. */
  .job-card-compact.archived {
    background-color:transparent; background-image:none;
    backdrop-filter:none; -webkit-backdrop-filter:none;
    border-color:rgba(255,255,255,.07); padding:11px 16px; opacity:.6;
  }
  .job-card-compact.archived .jcc-title { font-weight:600; color:var(--muted); }
  .job-card-compact.archived:hover { opacity:1; box-shadow:none; border-color:rgba(255,255,255,.16); }
  /* Плитка «New video» в кінці блоку «In progress»: не картка задачі, а
     заклик. Пунктир — щоб з першого погляду читалась як місце під нову
     роботу, а не як задача, що вже є.

     КНОПКИ ВСЕРЕДИНІ НЕМАЄ, і це рішення користувача 23.08: «кнопка наче
     занадто багато уваги на себе притягує». Лишився самий підпис, а клікає
     вся плитка — пунктирна рамка і є та кнопка. Тому колір тексту й колір
     рамки світлішають РАЗОМ на наведення: без цього плитка на вигляд не
     реагувала б на курсор, і не було б видно, що вона взагалі клікабельна.

     ВИСОТА — 99px, стільки ж, скільки картка задачі в роботі (заміряно в
     браузері: заголовок + смуга прогресу + рядок під нею + padding 16px).
     Своєї висоти в плитки бути не може: вона стоїть у тому самому стовпчику,
     що й картки, і на 68px помітно провалювалась. Число cosmetic-only —
     якщо в картці колись зміняться рядки, його треба переміряти, нічого при
     цьому не зламається. */
  .job-cta {
    display:flex; align-items:center; justify-content:center; min-height:99px;
    border:1px dashed rgba(255,255,255,.13); border-radius:12px;
    padding:16px; margin-bottom:8px; text-decoration:none; color:var(--muted);
    transition:border-color .2s var(--vr-ease), color .2s var(--vr-ease);
  }
  .job-cta:last-child { margin-bottom:0; }
  /* Та сама плитка на списку ПРОЄКТІВ. Висота інша, бо сусід інший: картка
     проєкту — 76px (заміряно в браузері: padding 16+16, назва 16/700 і рядок
     мети під нею), а не 99px, як картка задачі в роботі зі смугою прогресу.
     Число cosmetic-only — якщо картка проєкту колись підросте, переміряти.
     §запит користувача 26.08: синьої кнопки «New project» більше немає, дію
     несе сама плитка — так само, як «New video» всередині проєкту. */
  .proj-cta { min-height:76px; }
  .job-cta:hover { border-color:rgba(255,255,255,.26); color:var(--text); }
  .cta-label { display:inline-flex; align-items:center; gap:8px; font-size:13px; font-weight:600; }
  .cta-label svg { width:16px; height:16px; flex-shrink:0; }

  .toggle-row { display:flex; align-items:center; justify-content:space-between; padding:14px 0; border-bottom:1px solid rgba(255,255,255,.06); gap:24px; }
  .toggle-row:last-of-type { border-bottom:none; padding-bottom:0; }
  .toggle-row .t { font-size:14px; font-weight:600; }
  .toggle-row .d { font-size:12px; color:var(--muted); margin-top:3px; line-height:1.45; max-width:52ch; }
  .toggle-switch { position:relative; width:38px; height:22px; background:#6b6b70; border-radius:999px; flex-shrink:0; cursor:pointer; }
  .toggle-switch.on { background:var(--accent); }
  .toggle-switch .knob { position:absolute; top:2px; left:2px; width:18px; height:18px; background:#fff; border-radius:999px; transition:left .15s; }
  .toggle-switch.on .knob { left:18px; }
  #avaBody.off { opacity:.45; pointer-events:none; }

  /* задача */
  .player { aspect-ratio:16/9; border-radius:16px; background:#0b0b0c; position:relative; overflow:hidden; border:1px solid rgba(255,255,255,.08); }
  .player .ph { position:absolute; inset:0; opacity:.9; }
  .player .ctl { position:absolute; left:0; right:0; bottom:0; padding:12px 14px; display:flex; align-items:center; gap:10px; background:linear-gradient(transparent, rgba(0,0,0,.7)); font-size:12px; font-variant-numeric:tabular-nums; }
  .player .ctl .bar { flex:1; height:4px; background:rgba(255,255,255,.25); border-radius:999px; overflow:hidden; }
  .player .ctl .bar i { display:block; width:23%; height:100%; background:#fff; }
  .player .bigplay { position:absolute; inset:0; display:flex; align-items:center; justify-content:center; }
  .player .bigplay span { width:64px; height:64px; border-radius:50%; background:rgba(255,255,255,.92); display:flex; align-items:center; justify-content:center; }
  .player .bigplay svg { width:24px; height:24px; fill:#111; margin-left:4px; }
  /* сторінка відео */
  .h1-video { font-size:clamp(24px, 2.6vw, 32px); }
  /* екран генерації: шапка з відсотком, під лінією — чек-ліст етапів.
     Увесь екран має вміщатись без гортання (ноутбук 1440×700). */
  .h1-video { font-size:clamp(24px, 2.6vw, 32px); }
  .pg-job-run .page { padding-bottom:24px; }
  .pg-job-run .page-foot { margin-top:24px; }
  body.pg-job-run { padding-bottom:0; }
  .gen-head { display:flex; align-items:center; gap:28px; }
  .gen-pct { flex:0 0 auto; }
  .gen-bar { flex:1; height:6px; position:relative; overflow:hidden; }
  .gen-bar .job-progress-fill { transition:width .6s var(--vr-ease); }
  /* світлий бігунок проходить усю лінію від краю до краю — ознака, що робота триває */
  .gen-bar::after { content:''; position:absolute; inset:0; border-radius:inherit; pointer-events:none;
    background:linear-gradient(90deg, transparent, rgba(255,255,255,.30), transparent);
    background-size:38% 100%; background-repeat:no-repeat; animation:barSheen 2.8s linear infinite; }
  @keyframes barSheen { from { background-position:-38% 0; } to { background-position:138% 0; } }
  /* кроків завжди стільки сам• (WIN у скрипті), тож висота стала сама собою;
     поле зверху і знизу однакове — список не притискається до краю острівця */
  .gen-panel .timeline { margin-top:20px; padding:20px 0; border-top:1px solid rgba(255,255,255,.07); }

  .timeline { list-style:none; margin:0; padding:0; position:relative; }
  .timeline li { position:relative; display:flex; align-items:center; gap:14px; min-height:44px; }
  .timeline .dot { position:relative; z-index:1; width:18px; height:18px; flex:0 0 18px; border-radius:50%; display:flex; align-items:center; justify-content:center; }
  .timeline .dot svg { width:11px; height:11px; }
  /* Лінія йде від краю одного кружка до краю наступного — торкається обох, але не заходить під них
     (інакше просвічує крізь напівпрозорі кружки затухлих кроків). Зазор = радіус: пройдений і поточний 9, майбутній 3. */
  .timeline li::before { content:''; position:absolute; left:8.5px; top:calc(50% + var(--gs, 9px)); bottom:calc(-50% + var(--ge, 9px)); width:1px; background:rgba(255,255,255,.12); }
  .timeline li:last-child::before { display:none; }
  /* Пройдений шлях — суцільна синя лінія, торкається кружків. */
  .timeline li.done::before { background:var(--accent); width:2px; left:8px; --gs:9px; --ge:9px; }
  /* Ще не пройдений шлях — пунктир, і він НЕ торкається кружків: розрив показує, що туди ще не дійшли. */
  .timeline li.cur::before { --gs:15px; --ge:8px; background:none; border-left:1px dashed rgba(255,255,255,.16); width:0; }
  .timeline li.todo::before { --gs:8px; --ge:8px; background:none; border-left:1px dashed rgba(255,255,255,.16); width:0; }
  .timeline li.done .dot { background:var(--accent); color:#fff; }
  .timeline li.done .label { color:var(--muted); font-size:13px; }
  /* Затухання за відстанню від поточного кроку. Прозорість накладається лише на кружки й лінію —
     текст гасне КОЛЬОРОМ із підлогою 4.5:1 (WCAG AA), інакше давні кроки читалися на 1.9:1. */
  .timeline li.done[data-d="1"] .dot, .timeline li.todo[data-d="1"] .dot { opacity:.9; }
  .timeline li.done[data-d="2"] .dot, .timeline li.todo[data-d="2"] .dot { opacity:.6; }
  .timeline li.done[data-d="3"] .dot, .timeline li.todo[data-d="3"] .dot,
  .timeline li.done[data-d="4"] .dot, .timeline li.todo[data-d="4"] .dot { opacity:.36; }
  .timeline li.done[data-d="1"] .label, .timeline li.todo[data-d="1"] .label { color:#a3a3a3; }
  .timeline li.done[data-d="2"] .label, .timeline li.todo[data-d="2"] .label { color:#8e8e8e; }
  .timeline li.done[data-d="3"] .label, .timeline li.todo[data-d="3"] .label,
  .timeline li.done[data-d="4"] .label, .timeline li.todo[data-d="4"] .label { color:#7d7d7d; }
  /* лінія згасає разом зі своїм кружком — інакше кроки тьмяні, а зв'язки між ними яскраві */
  .timeline li[data-d="1"]::before { opacity:.9; }
  .timeline li[data-d="2"]::before { opacity:.6; }
  .timeline li[data-d="3"]::before, .timeline li[data-d="4"]::before { opacity:.36; }
  .timeline li.cur .dot { background:var(--accent); box-shadow:0 0 0 5px rgba(37,99,235,.18), 0 0 20px rgba(37,99,235,.45);
    animation:dotPulse 1.9s var(--vr-ease) infinite; }
  /* пульс іде хвилею назовні — видно, що крок саме зараз працює */
  @keyframes dotPulse {
    0%   { box-shadow:0 0 0 4px rgba(37,99,235,.34), 0 0 18px rgba(37,99,235,.45); }
    70%  { box-shadow:0 0 0 14px rgba(37,99,235,0),  0 0 22px rgba(37,99,235,.5); }
    100% { box-shadow:0 0 0 4px rgba(37,99,235,0),   0 0 18px rgba(37,99,235,.45); }
  }
  /* 8px, а не 7: у контейнері 18px відступ від краю виходив 5.5px, і на
     дробовому масштабі браузер округляв боки по-різному — цятка з'їжджала на
     піксель угору-ліворуч, що при збільшенні добре видно (§скарга користувача
     23.08). Парне число ділиться націло: рівно по 5px з кожного боку. */
  .timeline li.cur .dot::after { content:''; width:8px; height:8px; border-radius:50%; background:#fff; }
  .timeline li.cur .label { color:#7ea6ff; font-size:16px; font-weight:700; letter-spacing:-.02em; }
  /* деталь кроку — поруч із назвою, не під нею: рядок лишається однієї висоти з рештою */
  .timeline li.cur > div { display:flex; align-items:baseline; gap:10px; flex-wrap:wrap; }
  .timeline li.cur .sub { font-size:12px; color:var(--muted); font-weight:500; letter-spacing:0; }
  .timeline li.todo .dot { width:6px; height:6px; flex-basis:6px; margin:0 6px; background:rgba(255,255,255,.22); }
  .timeline li.todo .label { color:#a3a3a3; font-size:13px; }
  /* перехід на наступний крок: список підповзає знизу вгору, а лінія щойно пройденого
     доростає з пунктиру в суцільну синю */
  .timeline.advance li { animation:stepShift .55s cubic-bezier(.22,.61,.36,1) both; }
  .timeline.advance li:nth-child(2) { animation-delay:.03s; }
  .timeline.advance li:nth-child(3) { animation-delay:.06s; }
  .timeline.advance li:nth-child(4) { animation-delay:.09s; }
  .timeline.advance li:nth-child(5) { animation-delay:.12s; }
  @keyframes stepShift { from { transform:translateY(7px); opacity:.55; } to { transform:none; opacity:1; } }
  .timeline li.done.just::before { transform-origin:top; animation:lineFill .5s var(--vr-ease) both; }
  @keyframes lineFill { from { transform:scaleY(0); } to { transform:scaleY(1); } }
  .timeline li.done.just .dot { animation:dotPop .4s var(--vr-ease) both; }
  @keyframes dotPop { 0% { transform:scale(.4); } 60% { transform:scale(1.12); } 100% { transform:none; } }
  @media (prefers-reduced-motion: reduce) {
    .timeline li.cur .dot, .timeline.advance li, .timeline li.done.just::before,
    .timeline li.done.just .dot, .gen-bar .job-progress-fill::after { animation:none; }
  }
  /* нотатка живе під острівцем, не в ньому */
  .gen-note { display:flex; gap:10px; align-items:flex-start; margin-top:16px; max-width:460px; color:var(--muted); font-size:13px; line-height:1.5; }
  .gen-note svg { width:16px; height:16px; flex-shrink:0; margin-top:2px; }
  .two-col { display:grid; grid-template-columns:minmax(0, 1fr) 300px; gap:16px; align-items:stretch; }
  .two-col > section { margin-bottom:0; display:flex; flex-direction:column; }
  /* підсумок відео — простий список «назва — значення», один рядок на факт;
     під тонкою лінією в тому ж острівці — розбивка, скільки що коштувало */
  .done-info .kv { gap:14px 16px; }
  /* острівець тягнеться на висоту плеєра, тож розбивка притиснута до низу */
  .sec-part { margin-top:18px; padding-top:18px; border-top:1px solid rgba(255,255,255,.07); }
  .done-info .sec-part { margin-top:auto; }
  @media (max-width: 900px) { .two-col { grid-template-columns:1fr; } }
  .h2-note { letter-spacing:0; text-transform:none; font-weight:500; font-size:12px; }
  /* §рядок про вимкнений рух (2026-08-25). Кліп, якому зняли рух, зникає зі
     списку — слот більше не анімований. Цей рядок і є єдиний слід дії, тому
     він стоїть над списком, а не десь у підвалі. */
  .drop-note { margin:0 0 12px; font-size:13px; color:var(--muted); }
  /* §кліп із вимкненим рухом (2026-08-25). Картка лишається на місці, але
     читається як виключена з роботи: приглушена й без акценту. Не ховаємо —
     інакше дія не мала б сліду, а повернути її не було б звідки. */
  /* Кнопка в роботі: підпис змінився, але вона має ще й ВИГЛЯДАТИ зайнятою,
     інакше сірий текст читається просто як «вимкнено». */
  button.busy, .btn.busy { opacity:.75; cursor:progress; }
  /* Екран очікування: підложка + панель із крутилкою. Поверх усього, зокрема
     поверх діалогу підтвердження — саме з нього ми сюди й потрапляємо. */
  .wait { position:fixed; inset:0; z-index:80; display:flex; align-items:center; justify-content:center;
    background:rgba(8,9,12,.62); backdrop-filter:blur(2px); animation:fadeIn .12s ease both; }
  .wait[hidden] { display:none; }
  .wait-panel { display:flex; align-items:center; gap:14px; padding:20px 24px; border-radius:14px;
    background:var(--panel, #14161c); box-shadow:0 18px 48px rgba(0,0,0,.5), inset 0 0 0 1px var(--border);
    max-width:min(420px, calc(100vw - 32px)); animation:riseIn .16s var(--vr-ease) both; }
  .wait-spin { width:22px; height:22px; border:2.5px solid rgba(157,188,255,.28); border-top-color:#9dbcff; }
  .wait-title { font-size:15px; font-weight:600; color:var(--text); }
  .wait-note { font-size:13px; color:var(--muted); margin-top:3px; }
  /* Під оверлеєм нічого не натискається й не ловить фокус із клавіатури. */
  body.waiting .page, body.waiting .app-header, body.waiting .modal { pointer-events:none; }
  /* Дія в заголовку секції: дрібна, праворуч, не конкурує з самим заголовком. */
  .frames-section h2 { display:flex; align-items:center; gap:12px; }
  .h2-act { margin-left:auto; font-size:12px; padding:6px 12px; }
  .h2-act[aria-pressed="true"] { color:var(--text); box-shadow:inset 0 0 0 1px var(--accent-hover); }
  /* Кліп, якому рух вимкнено. §скарга користувача 25.08: «не всю картку треба
     затемняти, а саме оце вікно, де плеєр. І може робити його червоним краще,
     так буде видніше. А оцю праву частину не потрібно».
     Доти стояло opacity:.45 на ВЕСЬ рядок — тьмянів і промт, і кнопки, з якими
     людина ще працює (зокрема та сама, якою рішення повертають назад). */
  .frame-row.dropped .frame-media { filter:grayscale(.9) brightness(.55); }
  .frame-row.dropped .frame-media::after { content:'Kept still'; position:absolute; inset:0;
    display:flex; align-items:center; justify-content:center;
    background:rgba(122,26,32,.42); box-shadow:inset 0 0 0 2px #d0555c;
    color:#ffd9d6; font-size:13px; font-weight:700; letter-spacing:.04em;
    text-transform:uppercase; text-shadow:0 1px 3px rgba(0,0,0,.6); pointer-events:none; }
  /* Перезнімання — стан важливіший; свій напис у нього вже є. Вага селекторів
     однакова, тож без :not() виграв би той, що нижче у файлі. */
  .frame-row.dropped.regenerating .frame-media::after { content:'Regenerating…'; }
  /* Кнопка повернення руху — теж червона: вона про той самий стан. */
  .frame-row.dropped .drop-one { color:#ffb3ad; box-shadow:inset 0 0 0 1px rgba(208,85,92,.55); }
  /* Дія всередині текстового рядка: підкреслене слово, а не кнопка-острівець —
     вона другорядна й не має конкурувати з панеллю вибору. */
  .linkish { background:none; border:0; box-shadow:none; padding:0; font:inherit;
             color:var(--accent-hover); text-decoration:underline; text-underline-offset:2px;
             cursor:pointer; }
  .linkish:hover { color:var(--text); }
  .linkish:disabled { color:var(--muted); text-decoration:none; cursor:default; }
  /* рядок даних під назвою відео: крапка-роздільник із однаковим повітрям з обох боків */
  .meta-row { display:flex; align-items:center; gap:8px; margin-top:8px; font-size:13px; color:var(--muted); font-variant-numeric:tabular-nums; }
  .meta-row span + span::before { content:'·'; margin-right:8px; opacity:.5; }
  .page-foot { display:flex; justify-content:flex-start; margin-top:32px; padding-top:20px; border-top:1px solid rgba(255,255,255,.07); }
  .quiet-danger { color:var(--muted); }
  .quiet-danger:hover { color:var(--bad); box-shadow:inset 0 0 0 1px rgba(255,122,112,.4); }
  /* Кадри на затвердження — рядками, як у самому сервісі: картинка ліворуч, праворуч слова озвучки
     (проти них і судять кадр) та промт дрібним. Виділення — акцентом сайту, синім. */
  .frames-section { max-width:none; }
  .frames-list { display:flex; flex-direction:column; gap:12px; }
  .frame-row { display:grid; grid-template-columns:minmax(0,52%) minmax(0,1fr); align-items:stretch;
    border:1px solid var(--vr-hair); border-radius:16px; overflow:hidden;
    background-color:var(--vr-card-bg); background-image:var(--vr-card);
    backdrop-filter:blur(14px) saturate(140%); -webkit-backdrop-filter:blur(14px) saturate(140%);
    transition:border-color .18s var(--vr-ease), box-shadow .18s var(--vr-ease); }
  .frame-row.selected { border-color:var(--accent); box-shadow:0 0 0 1px var(--accent), 0 10px 26px rgba(37,99,235,.16); }
  /* це <button>, тож скидаємо глобальні розміри кнопки: кадр має бути на всю комірку */
  .frame-media { display:block; width:100%; height:auto; position:relative; aspect-ratio:16/9;
    background:#0e0e10; cursor:zoom-in; border:none; padding:0; border-radius:0; }
  .frame-media:hover { box-shadow:inset 0 0 0 1px rgba(255,255,255,.16); background:#0e0e10; }
  .frame-media .ph { position:absolute; inset:0; }
  /* Кадр 1920×1080 лягає в рамку 16:9 цілком: нічого не обрізається ні згори, ні з боків. */
  .frame-media img, .lb-stage img { position:absolute; inset:0; width:100%; height:100%; object-fit:contain; background:#0e0e10; }
  .frame-media .zoom { position:absolute; right:12px; bottom:12px; width:32px; height:32px; border-radius:8px;
    background:rgba(0,0,0,.55); display:flex; align-items:center; justify-content:center; opacity:0; transition:opacity .15s; }
  .frame-media .zoom svg { width:15px; height:15px; }
  .frame-media:hover .zoom { opacity:1; }
  .frame-side { min-width:0; padding:16px; display:flex; flex-direction:column; gap:12px; }
  .frame-head { display:flex; align-items:center; gap:8px; }
  .frame-check { margin-left:auto; }
  /* Слова озвучки — головний текст рядка: цитата з синьою лінійкою збоку. */
  .frame-said { font-size:14px; line-height:1.5; color:var(--text); padding-left:12px; border-left:2px solid rgba(37,99,235,.6); }
  /* Промт — довідка «чому вийшло саме це»: дрібний сірий блок на власному тлі, без підпису. */
  .frame-prompt { background:rgba(255,255,255,.045); border-radius:8px; padding:10px 12px; }
  .frame-prompt p { margin:0; font-size:13px; line-height:1.45; color:var(--muted); }
  .frame-actions { margin-top:auto; display:flex; align-items:center; justify-content:space-between; gap:12px; padding-top:2px; }
  /* Анімація кадру — властивість кадру, а не дія і не статус, тому вона НЕ синя:
     тиха позначка на самому кадрі + так само тихий перемикач у діях, обидва — самим значком. */
  .anim-badge { display:none; position:absolute; left:12px; bottom:12px; align-items:center; justify-content:center;
    width:30px; height:30px; border-radius:8px; background:var(--motion); color:#181818; box-shadow:0 4px 14px rgba(0,0,0,.35); }
  .anim-badge svg { width:17px; height:17px; }
  /* Кліп грається просто в картці, СИСТЕМНИМ плеєром (§правка користувача
     23.08). Тут стояв мальований: кнопка по центру, смужка позиції знизу,
     напис із тривалістю в кутку. Судять рух не з одного перегляду — його
     відмотують і спиняють, а цього мальований не вміє; тривалість і позицію
     показує сам плеєр, тож .clip-dur/.clip-line/.clip-play пішли разом із ним. */
  .clip-media { cursor:default; }
  .clip-media .clip-gone { position:absolute; inset:0; display:flex; align-items:center;
    justify-content:center; background:#0e0e10; }
  .frame-row.animated .anim-badge, .lb-stage.animated .anim-badge { display:inline-flex; }
  .anim-toggle { display:inline-flex; align-items:center; justify-content:center; gap:6px; padding:0 12px; border-radius:8px;
    font-size:13px; font-weight:600; background:none; color:var(--muted); box-shadow:inset 0 0 0 1px var(--border); }
  .anim-toggle svg { width:17px; height:17px; }
  .anim-toggle:hover { background:rgba(255,255,255,.06); color:var(--text); box-shadow:inset 0 0 0 1px #5a5a5f; }
  .anim-toggle[aria-pressed="true"] { background:rgba(255,255,255,.18); color:#fff; box-shadow:inset 0 0 0 1px rgba(255,255,255,.45); }
  .anim-toggle[aria-pressed="true"]:hover { background:rgba(255,255,255,.26); color:#fff; box-shadow:inset 0 0 0 1px rgba(255,255,255,.6); }
  .anim-toggle:disabled { opacity:.35; cursor:not-allowed; }
  .frame-actions .btn, .frame-actions button { height:34px; padding:0 12px; font-size:13px; }
  /* тихе текстове посилання: дія рідкісна й не мусить сперечатися з основною кнопкою */
  .link-quiet { display:inline-flex; align-items:center; height:34px; padding:0 2px; background:none; box-shadow:none; color:var(--muted); font-weight:400; font-size:13px; }
  .link-quiet:hover { background:none; color:var(--text); box-shadow:none; text-decoration:underline; text-underline-offset:3px; }
  /* Діалог підтвердження — для дій, які не відкотиш: скасування задачі, затвердження всіх кадрів. */
  .modal { position:fixed; inset:0; z-index:500; display:flex; align-items:center; justify-content:center; padding:24px;
    background:rgba(8,8,10,.72); backdrop-filter:blur(8px); -webkit-backdrop-filter:blur(8px); animation:fadeIn .14s ease-out; }
  .modal-panel { width:min(440px,100%); padding:24px; border-radius:16px;
    background:var(--panel-glass); border:1px solid var(--glass-border); box-shadow:0 24px 60px rgba(0,0,0,.55);
    backdrop-filter:var(--glass-blur); -webkit-backdrop-filter:var(--glass-blur); animation:riseIn .18s var(--vr-ease) both; }
  .modal-panel h3 { margin:0 0 8px; font-size:16px; font-weight:700; letter-spacing:-.02em; }
  .modal-panel p { margin:0; font-size:13px; line-height:1.55; color:var(--muted); }
  .modal-field { margin:16px 0 0; }
  .modal-actions { display:flex; justify-content:flex-end; gap:12px; margin-top:24px; }
  /* Панель вибору — плаваюче вікно по центру внизу; зʼявляється лише коли щось обрано.
     Головна дія (Approve) живе вгорі праворуч, руйнівна (Cancel job) — унизу сторінки: конвенція SaaS. */
  .sel-bar { position:fixed; left:50%; bottom:24px; transform:translateX(-50%); z-index:300;
    display:flex; align-items:center; gap:12px; padding:12px; border-radius:16px;
    background:rgba(22,22,25,.94); border:1px solid var(--glass-border); box-shadow:0 16px 40px rgba(0,0,0,.5);
    backdrop-filter:blur(18px) saturate(160%); -webkit-backdrop-filter:blur(18px) saturate(160%);
    animation:selUp .18s var(--vr-ease) both; }
  @keyframes selUp { from { opacity:0; transform:translate(-50%, 10px) } to { opacity:1; transform:translate(-50%, 0) } }
  .sel-chip { display:inline-flex; align-items:center; height:40px; padding:0 14px; border-radius:8px;
    background:rgba(37,99,235,.14); border:1px solid rgba(126,166,255,.4); color:#cfe0ff;
    font-size:13px; font-weight:600; font-variant-numeric:tabular-nums; white-space:nowrap; }
  /* Промотка вгору — над плаваючою панеллю, у правому куті. */
  .to-top { position:fixed; right:24px; bottom:24px; z-index:290; width:40px; height:40px; padding:0; border-radius:12px;
    background:rgba(22,22,25,.92); color:var(--text); border:1px solid var(--glass-border); box-shadow:0 10px 26px rgba(0,0,0,.4);
    backdrop-filter:blur(14px); -webkit-backdrop-filter:blur(14px); }
  .to-top:hover { background:rgba(34,34,38,.96); color:#fff; }
  .to-top svg { width:18px; height:18px; }
  /* Перегенерація: кадр розмивається і показує статус; після завершення лишається позначеним «Updated»,
     щоб серед десятків кадрів одразу було видно, які саме перезняли. */
  /* Кадр під час перегенерації. §правка 23.08: додано `img` — доти правило
     ловило лише заглушку `.ph`, а справжня картинка (у розмітці це просто
     <img> без класу) не темніла ЗОВСІМ; єдиним натяком лишалась напівпрозора
     плашка «Regenerating…». Заразом яскравість опущена з .65 до .3: людина
     має бачити, що цей кадр уже не той, з іншого кінця столу. */
  .frame-row.regenerating .frame-media .ph, .frame-row.regenerating .frame-media img,
  .lb-stage.regenerating .ph, .lb-stage.regenerating img {
    filter:blur(10px) saturate(.4) brightness(.3); transition:filter .25s; }
  .frame-row.regenerating .frame-media::after, .lb-stage.regenerating::after { content:'Regenerating…'; position:absolute; inset:0;
    display:flex; align-items:center; justify-content:center;
    font-size:13px; font-weight:600; letter-spacing:.02em; color:#dce8ff; background:rgba(10,12,18,.55); }
  .frame-row.regenerating { border-color:rgba(126,166,255,.5); }
  .frame-row.fresh { border-color:rgba(48,209,88,.8); box-shadow:0 0 0 2px rgba(48,209,88,.45);
    animation:freshPulse 2.2s var(--vr-ease) infinite; }
  @keyframes freshPulse {
    0%, 100% { box-shadow:0 0 0 2px rgba(48,209,88,.45); }
    50%      { box-shadow:0 0 0 2px rgba(48,209,88,.9), 0 0 0 8px rgba(48,209,88,.14); }
  }
  .fresh-badge { display:none; align-items:center; gap:5px; font-size:11px; font-weight:700; letter-spacing:.06em;
    text-transform:uppercase; color:#30d158; border:1px solid rgba(48,209,88,.4); background:rgba(48,209,88,.1);
    border-radius:999px; padding:2px 8px; }
  .frame-row.fresh .fresh-badge { display:inline-flex; }
  /* §2026-08-23. Слот, чий опис не пройшов авто-валідацію. Доти екран рахував
     їх (`autoFailed`) і НЕ показував ніде — а після того, як валідатор
     перестав спиняти продакшен, це єдине місце, де про них узагалі можна
     дізнатись. Бурштин, а не червоне: кадр не зіпсований, просто його опис
     ніхто не звірив — дивитись треба на саму картинку. */
  .unchecked-badge { display:inline-flex; align-items:center; font-size:11px; font-weight:700;
    letter-spacing:.06em; text-transform:uppercase; color:var(--warn);
    border:1px solid rgba(255,159,10,.38); background:rgba(255,159,10,.09);
    border-radius:999px; padding:2px 8px; }
  /* ---- Повноекранний перегляд кадру ---- */
  .lb { position:fixed; inset:0; z-index:400; display:flex; align-items:center; justify-content:center;
    padding:24px 80px; background:rgba(10,10,12,.95);
    backdrop-filter:blur(12px); -webkit-backdrop-filter:blur(12px); animation:fadeIn .16s ease-out; }
  @keyframes fadeIn { from { opacity:0 } to { opacity:1 } }
  /* ширину диктує сам кадр — підписи й кнопки стоять рівно по його краях */
  .lb-inner { width:min(1180px, 100%, calc(64vh * 16 / 9)); display:flex; flex-direction:column; gap:14px; }
  .lb-top { display:flex; align-items:center; gap:10px; }
  .lb-count { font-size:12px; color:var(--muted); font-variant-numeric:tabular-nums; }
  .lb-stagewrap { position:relative; }
  /* Сцена — сама кнопка вибору: клік по кадру позначає його на перегенерацію. */
  .lb-stage { display:block; width:100%; height:auto; position:relative; aspect-ratio:16/9;
    border-radius:16px; overflow:hidden; background:#0e0e10; padding:0; cursor:pointer; box-shadow:none; }
  /* Кадр іде — стара картинка притухає, поверх неї крутилка. Без цього видно
     ПОПЕРЕДНІЙ кадр, і ззовні це читається як «зависло» (§скарга 25.08). */
  .lb-stage.lb-loading:not(.regenerating) img { opacity:.35; filter:blur(1px); transition:opacity .12s; }
  /* :not(.regenerating) — інакше ця крутилка перебила б напис «Regenerating…»
     нижче: у нього така сама вага (два класи + ::after), і виграє той, що
     стоїть пізніше у файлі. Перезнімання — стан важливіший за завантаження. */
  .lb-stage.lb-loading:not(.regenerating)::after { content:''; position:absolute; top:50%; left:50%;
    width:34px; height:34px; margin:-17px 0 0 -17px; border-radius:50%;
    border:3px solid rgba(255,255,255,.25); border-top-color:#fff;
    animation:spin .8s linear infinite; pointer-events:none; z-index:3; }
  .lb-stage:hover { box-shadow:0 0 0 1px rgba(255,255,255,.28); background:#0e0e10; }
  .lb-stage .ph { position:absolute; inset:0; transition:filter .18s; }
  .lb-pick { position:absolute; top:12px; right:12px; width:26px; height:26px; border-radius:6px;
    background:rgba(10,12,18,.5); box-shadow:inset 0 0 0 1.5px rgba(255,255,255,.5); color:transparent; }
  .lb-pick svg { width:15px; height:15px; }
  .lb-stage[aria-pressed="true"] { box-shadow:0 0 0 2px var(--accent); }
  .lb-stage[aria-pressed="true"] .ph { filter:brightness(1.06) saturate(1.05); }
  .lb-stage[aria-pressed="true"] .lb-pick { background:var(--accent); color:#fff; box-shadow:none; }
  /* Стрілки — біля самої картинки, а не по краях екрана. */
  .lb-nav { position:absolute; top:50%; transform:translateY(-50%); width:44px; height:44px; padding:0; border-radius:50%;
    background:rgba(255,255,255,.08); color:#fff; box-shadow:none; }
  .lb-nav:hover { background:rgba(255,255,255,.18); box-shadow:none; }
  .lb-nav svg { width:20px; height:20px; }
  .lb-prev { left:-58px; } .lb-next { right:-58px; }
  /* Хрестик — поза картинкою, у куті екрана, як і стрілки поза кадром. */
  .lb-close { position:fixed; top:20px; right:20px; width:40px; height:40px; padding:0; border-radius:8px;
    background:rgba(255,255,255,.08); color:#fff; box-shadow:none; }
  .lb-close:hover { background:rgba(255,255,255,.18); box-shadow:none; }
  .lb-close svg { width:17px; height:17px; }
  .lb-bottom { display:flex; align-items:flex-end; gap:24px; }
  .lb-text { min-width:0; flex:1; display:flex; flex-direction:column; gap:8px; }
  .lb-said { font-size:14px; line-height:1.5; padding-left:12px; border-left:2px solid rgba(37,99,235,.6); }
  .lb-prompt { font-size:13px; line-height:1.45; color:var(--muted); max-width:820px; padding-left:14px; }
  .lb-acts { flex:0 0 auto; display:flex; align-items:center; gap:12px; }
  .lb-acts .anim-toggle { height:34px; box-shadow:inset 0 0 0 1px rgba(255,255,255,.28); }
  .lb-acts .anim-toggle[aria-pressed="true"] { box-shadow:inset 0 0 0 1px rgba(255,255,255,.55); }
  .pick-box { display:flex; align-items:center; justify-content:center;
    transition:background .15s, color .15s, box-shadow .15s; }
  .broll-idx { border:1px solid rgba(255,255,255,.22); color:rgba(255,255,255,.7); border-radius:999px; padding:2px 8px; font-size:11px; font-weight:700; font-variant-numeric:tabular-nums; }

  /* ── Usage: одна головна цифра, розбивка смугою, відео блоками ───────── */
  /* між підсумком витрат і таблицею — той самий крок, що на сторінці проєкту */
  .pg-usage section:not(.flat), .pg-spend section { margin-bottom:40px; }
  /* Сторінка загальних витрат користується тими самими смугами, що й витрати
     проєкту: ті самі гроші, той самий компонент. */
  .pg-spend .stat-row, .pg-spend .spend-rows {
    margin-top:16px; padding-top:16px; border-top:1px solid rgba(255,255,255,.07); }
  .pg-spend .usage-head { margin-bottom:0; }
  /* Назва проєкту в стовпчику довша за назву сервісу — даємо їй місця. */
  .pg-spend #projectRows .sr-name { }
  @media (min-width:701px) { .pg-spend #projectRows .spend-row { grid-template-columns:260px minmax(0,1fr) 78px 44px; } }
  /* Картка витрат — три смуги, розділені лініями (§правка користувача 23.08):
     підсумок → три показники → розбивка по сервісах. Розділяти треба саме ці
     три блоки: рядок за рядком лінії дробили розбивку на вісім однакових
     шматків, тобто додавали шуму замість структури. Відтінок той самий, що
     розділяє секції в решті інтерфейсу. */
  .usage-head { display:flex; align-items:flex-start; justify-content:space-between; gap:24px; }
  .pg-usage .stat-row, .pg-usage .spend-rows {
    margin-top:16px; padding-top:16px; border-top:1px solid rgba(255,255,255,.07); }
  .usage-period { display:flex; align-items:center; gap:12px; flex-wrap:wrap; justify-content:flex-end; }
  .dd-inline { display:inline-block; min-width:150px; }
  .dd-inline .dd-trigger { height:34px; font-size:13px; }
  .est-num.xl { font-size:32px; }
  .stat-row { display:flex; gap:32px; }
  /* власний календар діапазону */
  .rangepick { position:relative; }
  .range-trigger { width:auto; min-width:190px; height:34px; font-size:13px; gap:12px; }
  .cal-pop { position:absolute; right:0; top:calc(100% + 8px); z-index:40; width:272px; padding:14px;
    background:rgba(26,26,30,.98); border:1px solid var(--glass-border); border-radius:12px; box-shadow:0 16px 40px rgba(0,0,0,.55);
    backdrop-filter:var(--glass-blur); -webkit-backdrop-filter:var(--glass-blur); animation:riseIn .16s var(--vr-ease) both; }
  .cal-head { display:flex; align-items:center; justify-content:space-between; margin-bottom:12px; }
  .cal-title { font-size:13px; font-weight:700; }
  .cal-week, .cal-grid { display:grid; grid-template-columns:repeat(7, 1fr); gap:2px; }
  .cal-week span { text-align:center; font-size:11px; font-weight:600; color:var(--muted); padding-bottom:6px; }
  .cal-day { height:32px; padding:0; border-radius:6px; background:none; box-shadow:none; color:var(--text);
    font-size:12px; font-weight:500; font-variant-numeric:tabular-nums; }
  .cal-day:hover { background:rgba(255,255,255,.08); box-shadow:none; }
  .cal-day.out { color:rgba(255,255,255,.25); }
  .cal-day.in { background:rgba(37,99,235,.16); border-radius:0; }
  .cal-day.edge { background:var(--accent); color:#fff; }
  .cal-day.edge.start { border-radius:6px 0 0 6px; } .cal-day.edge.end { border-radius:0 6px 6px 0; }
  .cal-day.edge.only { border-radius:6px; }
  .cal-foot { display:flex; align-items:center; justify-content:space-between; gap:12px; margin-top:12px;
    padding-top:12px; border-top:1px solid rgba(255,255,255,.07); }
  .cal-sel { font-size:12px; color:var(--muted); font-variant-numeric:tabular-nums; }
  /* розбивка витрат — стовпчиками по сервісах: видно і суму, і співвідношення */
  .spend-rows { display:grid; gap:12px; }
  .spend-row { display:grid; grid-template-columns:150px minmax(0,1fr) 78px 44px; align-items:center; gap:16px;
    font-size:13px; font-variant-numeric:tabular-nums; }
  .sr-name { color:var(--muted); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  .sr-track { height:8px; border-radius:999px; background:rgba(255,255,255,.06); overflow:hidden; }
  .sr-track i { display:block; height:100%; border-radius:999px; }
  .sr-val { text-align:right; font-weight:600; }
  .sr-pct { text-align:right; color:var(--muted); }
  @media (max-width:700px) { .spend-row { grid-template-columns:110px minmax(0,1fr) 66px 40px; gap:10px; } }
  /* у витратах та сама таблиця, тільки без прев'ю: тут важать числа, а не кадр */
  .video-row.no-thumb { grid-template-columns:minmax(0,1fr) 110px 64px 72px 72px; padding:12px 24px; }
  .video-row.no-thumb.head { padding:0 24px 8px 24px; }
  .video-row.no-thumb.head .hl { grid-column:1; }
  /* settings */  /* settings */
  .danger-zone { border-color:rgba(255,122,112,.28); }
  .danger-zone > h2 { color:#ffb3ad; }
  .orphan-list { font-size:13px; color:var(--muted); padding:12px; background:rgba(0,0,0,.18); border-radius:8px; margin-top:12px; }
  .orphan-list b { color:var(--text); }

  /* voice design */

  @keyframes riseIn { from{opacity:0; transform:translateY(8px)} to{opacity:1; transform:none} }
  .ph-1 { background: linear-gradient(135deg,#3b2f1a,#8a5a2b 45%,#d9a066); }
  .ph-2 { background: linear-gradient(135deg,#1c2a3a,#2f5d7a 50%,#8bb8c9); }
  .ph-3 { background: linear-gradient(135deg,#2a1d1d,#6b3a2f 50%,#c98c6e); }
  .ph-4 { background: linear-gradient(135deg,#1e2a1e,#3f6b3a 50%,#a7c98c); }
  .ph-5 { background: linear-gradient(135deg,#2a2030,#5a3f6e 50%,#c3a2d6); }
  .ph-6 { background: linear-gradient(135deg,#30281c,#7a6a3f 50%,#d6c58c); }
  .ph-7 { background: linear-gradient(135deg,#1f2b2b,#3f6b6b 50%,#8cc9c4); }
  .ph-8 { background: linear-gradient(135deg,#2b1f1f,#6b3f3f 50%,#c98c8c); }
  .ph::after { content:''; position:absolute; inset:0; background:radial-gradient(80% 70% at 30% 30%, rgba(255,255,255,.18), transparent 60%), radial-gradient(60% 60% at 80% 80%, rgba(0,0,0,.35), transparent 60%); }
  @media (max-width: 720px) {
    .header-in { padding:0 16px; gap:10px; }
    /* Крихти НЕ ховаємо (раніше тут стояло display:none — і піднятись на
       рівень вище ставало нічим). Повний шлях згортається до посилання на
       батька; воно й читабельне, і не тисне на 720px. */
    .crumbs .crumb-full { display:none; }
    .crumbs .crumb-up { display:inline-flex; align-items:center; gap:6px; }
    .page { padding:24px 16px; }
  }
  /* ── Поява сторінки ────────────────────────────────────────────────────
     У прототипі кожен перехід роутера програвав riseIn на новому екрані.
     Тут переходи серверні, тож те саме дає сама сторінка. */
  .page { animation: riseIn .22s var(--vr-ease) both; }

  /* ── prefers-reduced-motion ────────────────────────────────────────────
     Правило 12 з ТЗ §5: вимикає ВСІ анімації, не лише чек-ліст. Плавні
     переходи кольору/тіні лишаються — вони не рухають нічого на екрані
     й саме на них тримається правило «наведення не рухає елемент».
     Фонові блоби — це рух великих плям, тож вони теж зупиняються. */
  @media (prefers-reduced-motion: reduce) {
    *, *::before, *::after { animation-duration: .001ms !important; animation-iteration-count: 1 !important; }
    .bg-blob { animation: none !important; }
    /* Завмерле кільце — це обрубок дуги, а не «йде робота». Лишається підпис. */
    .spin { display:none !important; }
    .page { animation: none !important; }
  }

  /* ── Справжні прев'ю замість плиток-заглушок ──────────────────────────
     У прототипі кадр у таблиці малювався кольоровим прямокутником (.ph) —
     там не було чого показувати. У продукті це файл із
     /api/jobs/{id}/thumbnail, і йому потрібне правило, якого в прототипі
     не існувало. cover, а не contain: 96px завширшки — це впізнавання
     кадру, а не його розглядання. */
  .video-row .thumb img { position:absolute; inset:0; width:100%; height:100%;
    object-fit:cover; display:block; }

  /* ── Картка задачі в списку проєкту ───────────────────────────────────
     Рух у смузі прогресу — той самий блиск, що на екрані генерації
     (.gen-bar нижче): світла смужка проходить лінію від краю до краю.
     Це єдина ознака, що робота ЙДЕ ПРОСТО ЗАРАЗ, — відсоток може стояти
     хвилинами на довгому кроці, і без руху картка виглядає замерзлою.

     Клас ставиться ЛИШЕ задачі в статусі running. На паузі затвердження і
     на впалій задачі та сама анімація брехала б, що щось відбувається. */
  .job-progress-bar.live { position: relative; }
  .job-progress-bar.live::after {
    content: ''; position: absolute; inset: 0; border-radius: inherit; pointer-events: none;
    background: linear-gradient(90deg, transparent, rgba(255,255,255,.30), transparent);
    background-size: 38% 100%; background-repeat: no-repeat;
    animation: barSheen 2.8s linear infinite;
  }

  /* Відступ над дією в картці — 8px ПОНАД сітковий, разом 16.
     Геометрично 8px тут уже було, і на око це читалось як помилка: смуга
     прогресу заввишки 4px висить у рядку 17px, тож під нею лишається ще
     ~6.5px порожнечі, і просвіт «смуга -> текст» бачиться як ~14.5px.
     Кнопка ж має власне тло, її верх — це і є видимий край. Рівні числа
     давали нерівні просвіти; тепер рівні просвіти. */
  .jcc-action { margin-top: 8px; }

  /* ── Стани задачі в списку (§рішення користувача 2026-08-22) ──────────
     СИНІЙ — робота йде просто зараз. Це акцентний синій сервісу, той самий,
     що на кнопках і смузі прогресу: усе синє на екрані означає «живе».
     Раніше цей відтінок стояв на «awaiting review», але там він конкурував
     із кнопкою дії того ж кольору всередині тієї ж картки.

     ФІОЛЕТОВИЙ — задача чекає на людину. Свій колір потрібен саме тут:
     синій зайнятий роботою, червоний — падінням, зелений означає «готово»,
     а бурштин уже носить чернетка. Відтінок не випадковий — це той самий
     #a855f7, що у фоновій плямі, тож в інтерфейсі він не чужий. */
  .chip-run { color:#7ea6ff; background:rgba(37,99,235,.14); border-color:rgba(126,166,255,.45); }
  .chip-review { color:#d8b4fe; background:rgba(168,85,247,.14); border-color:rgba(192,132,252,.45); }

  /* Кадру ще немає на диску: не згенерувався, або його щойно прибрали перед
     перезніманням. У прототипі такого стану не існує — там усі кадри завжди
     на місці. Без підпису лишався б темний прямокутник, за яким не відрізнити
     «ще не готово» від «картинка не завантажилась». */
  .frame-media .media-note { position:absolute; inset:0; display:flex; align-items:center;
    justify-content:center; padding:16px; text-align:center;
    font-size:12px; font-weight:500; color:var(--muted); }

  /* Рядок фактів під назвою може стати довгим (крок, кількість кадрів,
     хронометраж, анімовані, квота перегенерацій). Без переносу він або
     виштовхував би головну дію, або вилазив за сторінку. */
  .meta-row { flex-wrap: wrap; row-gap: 4px; }

  /* ── Вимкнена кнопка ГАСНЕ, а не перефарбовується ─────────────────────
     Правило `button:disabled { background:var(--accent); box-shadow:none }`
     вище писалось під головну кнопку — вона й так синя, — але накриває ВСІ.
     Через це тихий обведений .anim-toggle у вимкненому стані ставав
     суцільним синім прямокутником без рамки, тобто ВАЖЧИМ за сусідню
     активну кнопку. Те саме робилось із червоною «Mark as failed» на
     сторінці налаштувань.

     Тут кожен варіант повертає собі СВІЙ базовий вигляд. Друга половина
     кожного селектора (:disabled:hover) навмисно лишається: вимкнений
     контрол не має реагувати на наведення взагалі. */
  button.ghost:disabled, button.ghost:disabled:hover,
  button.anim-toggle:disabled, button.anim-toggle:disabled:hover {
    background: none; box-shadow: inset 0 0 0 1px var(--border);
  }
  button.tinted:disabled, button.tinted:disabled:hover {
    background: rgba(37,99,235,.16); box-shadow: inset 0 0 0 1px rgba(126,166,255,.55);
  }
  button.secondary:disabled, button.secondary:disabled:hover { background: #3a3a3c; }
  button.danger:disabled, button.danger:disabled:hover { background: #5a1f22; }
  /* День календаря — той самий випадок, спійманий удруге (23.08): майбутні
     числа перетворювались на суцільні сині плитки, ще й помітніші за обрані. */
  button.cal-day:disabled, button.cal-day:disabled:hover { background: none; }

  /* ── Дії в лайтбоксі: обидві кнопки однакової висоти ──────────────────
     У прототипі `.lb-acts .anim-toggle` жорстко виставлений у 34px, а сусідній
     `Regenerate` лишається на типовій висоті контрола — 40px. Поруч це видно
     як дві різні кнопки, і саме на цьому спіймав користувач.

     Вирівнюємо на 40, а не на 34: у лайтбоксі всі інші контроли повнорозмірні
     (стрілки 44, хрестик 40), а 34 — це «small», розмір для щільних рядків
     списку. Тут щільності немає, тут одна велика картинка й дві дії під нею. */
  .lb-acts .anim-toggle { height: 40px; }

  /* Плеєр — справжній <video>, а не мальований прямокутник із прототипу.
     Власні контроли довелось би писати з нуля заради того, що браузер уже
     вміє; лишаємо його рамку й пропорції. */
  video.player { display:block; width:100%; height:auto; object-fit:contain; background:#0b0b0c; }

  /* ── Details на готовому відео: відстані по шкалі ритму ───────────────
     У прототипі тут стояли 14 / 18 / 6 — жодне не зі шкали 8/12/16/24, і на
     екрані це читалось як один довгий список з одинадцяти рядків, у якого
     десь посередині є тонка лінія. Насправді груп дві й вони різні за
     природою: зведення (скільки коштувало, скільки триває) і розбивка
     (з чого ця сума складається).

     12 усередині групи, 24 між групами — різниця вдвічі, і групи читаються
     як групи. Примітка про невідомі суми дістала 16 замість 6: доти вона
     злипалась з останнім рядком розбивки й виглядала як ще один її рядок. */
  .done-info .kv { gap: 12px 16px; }
  /* Лінія стоїть ПОСЕРЕДИНІ просвіту між групами: однаково зверху й знизу.
     Прототип робив це через 18/18; тут 16/16 — те саме, але зі шкали. */
  .done-info .sec-part { margin-top: 16px; padding-top: 16px; }

  /* Розбивка НЕ притискається до низу острівця (у прототипі тут стояло
     margin-top:auto). Задум був у тому, щоб острівець, розтягнутий на висоту
     плеєра, не мав порожнього хвоста внизу. Але коли сервісів мало — коротке
     відео без аватара дає їх чотири, — між зведенням і розбивкою зяяло понад
     100px, і список розривався навпіл посеред читання. Порожнеча в кінці
     острівця нікому не заважає; діра в середині списку заважає.
     Конкретне значення відступу — у правилі вище. */
  .done-info .sec-part { margin-top: 16px; }

  /* Аватар, якого немає на активному акаунті HeyGen. У прототипі такого стану
     немає — там усі плитки робочі. У продукті він реальний: запис від
     попереднього акаунта виглядав у списку живим, а задача з ним впала б аж
     на платному кроці. Гасне, але лишається видимим — щоб було зрозуміло,
     звідки він і чому не обирається. */
  .ava-dead { opacity:.4; cursor:not-allowed; }
  .ava-dead .ph { filter:grayscale(1); }

  /* Запобіжник до MIRAGE.keyDetail: навіть скорочений вердикт може не влізти
     у два рядки на вузькій колонці. Рядок стану тримає висоту рядів ключів
     вирівняною — розʼїхатись він не має права, повний текст є в title. */
  .key-state { overflow: hidden; }

  /* Вимкнений день у календарі — майбутнє. Гасне, а не перефарбовується
     (правило 15 ТЗ); фон йому повертає блок «Вимкнена кнопка ГАСНЕ» вище. */
  .cal-day:disabled { color:rgba(255,255,255,.28); cursor:not-allowed; }
  /* Порожній період на екрані витрат: рядок замість стовпчика з нулями. */
  .empty-note { font-size:13px; color:var(--muted); }

  /* ── Витрати: рядки відео БЕЗ власної поверхні ────────────────────────
     На вкладці Videos рядок — самостійна картка, і це правильно: там він
     головний обʼєкт сторінки. На витратах він продовження тієї самої панелі,
     що й розбивка вище, і власна поверхня робила його чужорідним —
     напівпрозора картка всередині напівпрозорої панелі дає подвійне скло.
     Лишається рядок таблиці з тонкою лінією зверху. */
  .pg-usage .videos-list { gap:0; }
  .pg-usage .video-row {
    background:none; background-image:none; backdrop-filter:none; -webkit-backdrop-filter:none;
    border:none; border-top:1px solid rgba(255,255,255,.07); border-radius:0;
    padding:10px 0; }
  .pg-usage .video-row:hover { background:rgba(255,255,255,.035); box-shadow:none; }
  /* Шапка стовпців — не рядок даних: без лінії й без реакції на наведення.
     Праве поле 15px там компенсувало внутрішній відступ картки; картки більше
     немає, тож без обнулення останній стовпець шапки не збігався б із числами. */
  .pg-usage .video-row.head, .pg-usage .video-row.no-thumb.head {
    border-top:none; padding:0 0 8px; }
  .pg-usage .video-row.head:hover { background:none; }

  /* ── Кліпи на екрані затвердження анімацій ────────────────────────────
     Смужка прогресу йде за реальним часом відео (mirage.js слухає timeupdate),
     а не за CSS-анімацією фіксованої довжини — у прототипі кліпа не існувало,
     тож там смужку «програвала» анімація на var(--len). З живим файлом вони
     розходяться на першій же буферизації. */
  .clip-media video { position:absolute; inset:0; width:100%; height:100%; object-fit:cover;
    display:block; background:#0e0e10; }
  /* Лупа не має лізти під смугу системних контролів — вона там і не потрібна:
     на час перегляду її ховаємо, доки курсор не піде з плеєра. */
  .clip-media .zoom { bottom:auto; top:10px; }
  /* Кліп на весь екран: системні контроли, тож стадія не поводиться як кнопка */
  .lb-stage video { max-width:100%; max-height:100%; display:block; border-radius:12px; background:#0e0e10; }
  .lb-stage:has(video:not([hidden])) { cursor:default; }

  /* Текст помилки зупиненої задачі — під чек-лістом, у тій самій панелі.
     Не в діалозі: діалог зникає, а це рядок, який людина цитує, коли питає,
     що сталось. Моноширинний, бо там технічні назви й шляхи. */
  .gen-error { margin-top:20px; padding-top:20px; border-top:1px solid rgba(255,255,255,.07);
    font-size:13px; line-height:1.55; color:var(--bad); overflow-wrap:anywhere; }

  /* ── Затвердження озвучки ─────────────────────────────────────────────
     Плеєр виглядає як ПОЛЕ інтерфейсу, а не як чужа вставка. Системний
     контрол малює власну панель зі своїм заокругленням (пігулка ~27px), і
     всередині картки з радіусом 16 це читалось як дві різні речі одна в
     одній. Радіус, тло й рамку зводимо до тих самих, що в полів вводу;
     заокруглення внутрішньої панелі задається окремо — без нього браузер
     лишає свою пігулку поверх нашої рамки. */
  audio.player { width:100%; display:block; height:44px; border-radius:8px;
    background:#1e1e20; box-shadow:inset 0 0 0 1px var(--border); }
  audio.player::-webkit-media-controls-enclosure,
  audio.player::-webkit-media-controls-panel { background:#1e1e20; border-radius:8px; }
  /* Сценарій — на всю ширину панелі (§правка користувача 23.08). Висота
     обмежена: тисяча слів на семихвилинне відео витіснила б плеєр за екран, а
     гортати текст усередині рамки звичніше, ніж усю сторінку. */
  .script-box { max-height:340px; overflow-y:auto; padding-right:8px;
    font-size:14px; line-height:1.65; color:var(--text); white-space:pre-wrap; }

  /* ── Екран входу ──────────────────────────────────────────────────────
     Єдина сторінка сервісу БЕЗ хедера: доки людина не ввійшла, ні знак-
     посилання, ні навігація їй нікуди не ведуть. Тому картка стоїть просто
     посеред вікна, а знак Mirage переїжджає з хедера всередину неї — це
     єдине місце, де він більший за свої 20px.

     Ширина 340px, а не --measure: поле вводу одне, і розтягувати його на
     1120px нема за чим. Кегль, радіус, висота контролів і поверхня картки —
     ті самі, що всюди (.panel, .field, button), нічого власного тут не
     заводиться. */
  body.pg-login { display:grid; place-items:center; min-height:100vh; padding:24px; }
  /* min(), а не рівно 340: на 375px екрані картка ширша за поле сітки, і
     grid ставив би її від лівого краю — 24px зліва проти 11 справа. */
  .login-card { width:min(340px, 100%); }
  .login-card .brand-mark { height:26px; margin-bottom:24px; }
  .login-card button { width:100%; }
  /* Рядок статусу тримає висоту постійно, навіть порожній: інакше поява
     помилки штовхала б картку вгору, а за правилом дизайну стан міняє
     світло, а не розкладку. 12/600 кольоровий — шкала статусів під полем. */
  .login-status { min-height:17px; margin:12px 0 0; font-size:12px; font-weight:600;
    color:var(--bad); line-height:1.4; }
