/* ==========================================================================
   Rockyt — design system
   Один файл, без препроцессоров и сборки. Обе темы из одних переменных.
   Порядок: токены → сброс → типографика → шапка → слой → компоненты → утилиты
   ========================================================================== */

/* --- 0. Шрифты -------------------------------------------------------- */
/* Локальные подмножества, без внешних CDN. Блок взят из fonts/font-face.css
   как есть, пути переписаны относительно css/app.css.

   Оба шрифта вариативные: один файл на всю ось насыщенности, поэтому любой
   font-weight из 200-800 (Manrope) или 100-800 (JetBrains Mono) работает без
   дополнительных файлов.

   JetBrains Mono — только latin: он используется для машинных значений
   (коды языков, ID видео и каналов), которые всегда ASCII.

   Manrope — latin + latin-ext + cyrillic + greek + vietnamese: переведённые
   заголовки в таблице проверки рендерятся без подмены шрифта на акцентах
   польского, чешского, турецкого, русского, греческого, вьетнамского.
   Деванагари, арабица, CJK, тайский, бенгальский, тамильский и телугу в
   Manrope отсутствуют — эти языки уходят в системный стек, и это ожидаемо.

   ВАЖНО: подмножество latin-ext обязательно для интерфейса на русском тоже —
   в нём лежит длинное тире и типографские кавычки, которыми набраны тексты. */

/* Ready-to-use @font-face block — paths assume this file sits next to
   css/app.css, i.e. fonts/*.woff2 alongside css/app.css (adjust the
   relative path if you place it elsewhere).

   Both are variable fonts: ONE file covers the whole weight axis, so any
   font-weight from 200–800 (Manrope) or 100–800 (JetBrains Mono) just works
   — no extra files needed per weight.

   JetBrains Mono ships latin-only on purpose: per DESIGN.md it is used only
   for machine values (language codes, video/channel IDs), which are always
   ASCII. If that rule ever changes, more subsets need fetching first.

   Manrope ships latin + latin-ext + cyrillic + greek + vietnamese: the
   product's 47 target languages include Polish/Czech/Turkish/Romanian/etc.
   (latin-ext), Russian/Ukrainian/Bulgarian/Serbian (cyrillic), Greek, and
   Vietnamese — translated titles in the review table can render in Manrope
   without a mid-word fallback to a system font on their accented letters.
   Devanagari/Arabic/CJK/Thai/Bengali/Tamil/Telugu are NOT in Manrope at all
   (no Latin-based font covers them) — those languages fall back to the
   system font stack automatically, which is correct and expected. */

@font-face {
  font-family: "Manrope";
  font-style: normal;
  font-weight: 200 800;
  font-display: swap;
  src: url("../fonts/Manrope-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191,
    U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Manrope";
  font-style: normal;
  font-weight: 200 800;
  font-display: swap;
  src: url("../fonts/Manrope-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7,
    U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F,
    U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F,
    U+A720-A7FF;
}
@font-face {
  font-family: "Manrope";
  font-style: normal;
  font-weight: 200 800;
  font-display: swap;
  src: url("../fonts/Manrope-cyrillic.woff2") format("woff2");
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: "Manrope";
  font-style: normal;
  font-weight: 200 800;
  font-display: swap;
  src: url("../fonts/Manrope-greek.woff2") format("woff2");
  unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1,
    U+03A3-03FF;
}
@font-face {
  font-family: "Manrope";
  font-style: normal;
  font-weight: 200 800;
  font-display: swap;
  src: url("../fonts/Manrope-vietnamese.woff2") format("woff2");
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169,
    U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323,
    U+0329, U+1EA0-1EF9, U+20AB;
}

@font-face {
  font-family: "JetBrains Mono";
  font-style: normal;
  font-weight: 100 800;
  font-display: swap;
  src: url("../fonts/JetBrainsMono-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6,
    U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122,
    U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* --- 1. Токены -------------------------------------------------------- */

:root {
  /* Поверхности и текст (светлая тема — основная) */
  --bg:            #FAFAF9;
  --bg-sunken:     #F4F4F1;
  --surface:       #FFFFFF;
  --surface-2:     #FCFCFB;
  --surface-inset: #F6F6F4;
  --ink:           #0F1012;
  --ink-2:         #43474C;
  --ink-3:         #55585D;
  --ink-muted:     #6B6E73;   /* минимальный серый: 4.9:1 на --bg */
  --border:        #EAEAE7;
  --border-strong: #E1E1DE;
  --border-hover:  #C9C9C5;

  /* Акцент */
  --accent:        #5B2BFF;
  --accent-hover:  #4A21D6;
  --accent-soft:   #F0EBFF;
  --accent-border: #DDD3FF;
  --accent-ink:    #FFFFFF;
  --accent-ring:   rgba(91, 43, 255, .14);
  --accent-shadow: rgba(91, 43, 255, .30);

  /* Сигнальный (данные, тёмные поверхности) */
  --signal:        #C6F24E;
  --signal-ink:    #0F1012;
  --signal-dark:   #5A7A16;   /* тот же смысл, читаемый на светлом */

  /* Статусы: фон / текст / граница */
  --ok-bg:      #EAF8F1;  --ok-fg:      #0A7C4D;  --ok-bd:      #CDEDDD;  --ok:      #0FA968;
  /* Фон кнопки публикации — отдельно от --ok: на нём лежит текст, поэтому
     нужен тёмный оттенок (белый на #00795F = 5.4:1). Подтон холодный: тёплый
     зелёный рядом с электрическим фиолетовым читается из другой палитры, а
     холодный рифмуется с ним глубиной. Наведение темнее, не светлее —
     иначе контраст падал бы в момент нажатия. */
  --publish:       #00795F;
  --publish-hover: #00614C;
  --publish-ink:   #FFFFFF;
  --warn-bg:    #FDF5E4;  --warn-fg:    #96620A;  --warn-bd:    #F3E3BE;  --warn:    #E0940B;
  --danger-bg:  #FDF0F0;  --danger-fg:  #B7343A;  --danger-bd:  #F6D5D6;  --danger:  #E5484D;
  --danger-ink: #C6353A;  --danger-line:#F4CFD0;  --danger-wash:#FDF3F3;

  /* Графики. Сейчас полосы данных берут тот же фиолетовый, что и кнопки —
     открытый вопрос: цвет действия и цвет данных совпадают. Варианты решения
     описаны в HANDOFF.md, раздел «Открытый вопрос: цвет данных». */
  --chart-bar:      #5B2BFF;
  --chart-bar-new:  #C6F24E;
  --chart-line:     #5B2BFF;
  --chart-point:    #5B2BFF;
  --chart-band:     rgba(198, 242, 78, .16);
  --chart-grid:     #EAEAE7;
  --chart-axis:     #6B6E73;
  --chart-country:  #43474C;
  --chart-delta:    #0A7C4D;
  --chart-marker:   #5B2BFF;
  --chart-track:    #F0F0EE;

  /* Тёмная панель внутри светлой темы (блок «Что дал перевод») */
  --panel:        #0F1012;
  --panel-2:      #16181C;
  --panel-3:      #1A1D22;
  --panel-border: #232529;
  --panel-ink:    #FAFAF9;
  --panel-ink-2:  #E8E9EA;
  --panel-muted:  #A6AAB0;
  /* Ошибка на панели. Отдельная пара, потому что панель тёмная и в светлой
     теме тоже: обычный --danger-bg там дал бы светло-розовую плашку. Как и
     остальные panel-токены, значения одни на обе темы. */
  --panel-danger-bg: #3D1A1C;
  --panel-danger-fg: #FF8E92;

  /* Шкала отступов */
  --s-1: 4px;  --s-2: 8px;   --s-3: 12px;  --s-4: 16px;
  --s-5: 20px; --s-6: 24px;  --s-8: 32px;  --s-10: 40px;
  --s-12: 48px; --s-16: 64px;

  /* Радиусы */
  --r-sm: 8px; --r: 11px; --r-lg: 16px; --r-xl: 20px; --r-pill: 999px;

  /* Типографика */
  --font: "Manrope", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
          "Helvetica Neue", Arial, sans-serif;
  --font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, "SF Mono",
               Menlo, Consolas, monospace;

  /* Тени */
  --shadow-1: 0 1px 2px rgba(15, 16, 18, .04);
  --shadow-2: 0 4px 18px rgba(15, 16, 18, .06);
  --shadow-3: 0 12px 40px rgba(15, 16, 18, .07);
  --shadow-accent: 0 1px 2px var(--accent-shadow);
  --shadow-accent-hover: 0 4px 14px rgba(91, 43, 255, .32);

  /* Слой */
  --container: 1180px;
  --container-narrow: 460px;
  --header-h: 60px;
}

/* Тёмная тема. Следует системе; переключатель ставит data-theme на <html>. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg:            #0B0C0E;
    --bg-sunken:     #08090A;
    --surface:       #121418;
    --surface-2:     #16181C;
    --surface-inset: #1A1D22;
    --ink:           #F2F3F5;
    --ink-2:         #C6CACF;
    --ink-3:         #B2B6BC;
    --ink-muted:     #A6AAB0;
    --border:        #232529;
    --border-strong: #2A2E35;
    --border-hover:  #454B54;

    --accent:        #7C55FF;
    --accent-hover:  #9377FF;
    --accent-soft:   #1B1633;
    --accent-border: #2E2452;
    --accent-ink:    #FFFFFF;
    --accent-ring:   rgba(124, 85, 255, .22);
    --accent-shadow: rgba(0, 0, 0, .4);

    --signal-dark:   #C6F24E;

    --ok-bg:      #0F2318;  --ok-fg:      #62D89B;  --ok-bd:      #1B3D28;  --ok:      #3BD07A;
  /* В тёмной теме кнопка светлая, а текст на ней чернильный */
  --publish:       #2FD09B;
  --publish-hover: #4ADCAD;
  --publish-ink:   #04241B;
    --warn-bg:    #241C08;  --warn-fg:    #E6B25C;  --warn-bd:    #3E3113;  --warn:    #F5B544;
    --danger-bg:  #1C0F0C;  --danger-fg:  #FF8E92;  --danger-bd:  #3D1A14;  --danger:  #FF6166;
    --danger-ink: #FF8E92;  --danger-line:#4A1E17;  --danger-wash:#1C0F0C;

    --chart-bar:      #7C55FF;
    --chart-bar-new:  #C6F24E;
    --chart-line:     #7C55FF;
    --chart-point:    #7C55FF;
    --chart-band:     rgba(198, 242, 78, .10);
    --chart-grid:     #232529;
    --chart-axis:     #A6AAB0;
    --chart-country:  #C6CACF;
    --chart-delta:    #62D89B;
    --chart-marker:   #7C55FF;
    --chart-track:    #232529;

    --panel:        #16181C;
    --panel-2:      #1A1D22;
    --panel-3:      #1F2228;
    --panel-border: #2A2E35;

    --shadow-1: 0 1px 2px rgba(0, 0, 0, .3);
    --shadow-2: 0 4px 18px rgba(0, 0, 0, .35);
    --shadow-3: 0 12px 40px rgba(0, 0, 0, .45);
  }
}

:root[data-theme="dark"] {
  --bg:            #0B0C0E;
  --bg-sunken:     #08090A;
  --surface:       #121418;
  --surface-2:     #16181C;
  --surface-inset: #1A1D22;
  --ink:           #F2F3F5;
  --ink-2:         #C6CACF;
  --ink-3:         #B2B6BC;
  --ink-muted:     #A6AAB0;
  --border:        #232529;
  --border-strong: #2A2E35;
  --border-hover:  #454B54;

  --accent:        #7C55FF;
  --accent-hover:  #9377FF;
  --accent-soft:   #1B1633;
  --accent-border: #2E2452;
  --accent-ink:    #FFFFFF;
  --accent-ring:   rgba(124, 85, 255, .22);
  --accent-shadow: rgba(0, 0, 0, .4);

  --signal-dark:   #C6F24E;

  --ok-bg:      #0F2318;  --ok-fg:      #62D89B;  --ok-bd:      #1B3D28;  --ok:      #3BD07A;
  /* В тёмной теме кнопка светлая, а текст на ней чернильный */
  --publish:       #2FD09B;
  --publish-hover: #4ADCAD;
  --publish-ink:   #04241B;
  --warn-bg:    #241C08;  --warn-fg:    #E6B25C;  --warn-bd:    #3E3113;  --warn:    #F5B544;
  --danger-bg:  #1C0F0C;  --danger-fg:  #FF8E92;  --danger-bd:  #3D1A14;  --danger:  #FF6166;
  --danger-ink: #FF8E92;  --danger-line:#4A1E17;  --danger-wash:#1C0F0C;

  --chart-bar:      #7C55FF;
  --chart-bar-new:  #C6F24E;
  --chart-line:     #7C55FF;
  --chart-point:    #7C55FF;
  --chart-band:     rgba(198, 242, 78, .10);
  --chart-grid:     #232529;
  --chart-axis:     #A6AAB0;
  --chart-country:  #C6CACF;
  --chart-delta:    #62D89B;
  --chart-marker:   #7C55FF;
  --chart-track:    #232529;

  --panel:        #16181C;
  --panel-2:      #1A1D22;
  --panel-3:      #1F2228;
  --panel-border: #2A2E35;

  --shadow-1: 0 1px 2px rgba(0, 0, 0, .3);
  --shadow-2: 0 4px 18px rgba(0, 0, 0, .35);
  --shadow-3: 0 12px 40px rgba(0, 0, 0, .45);
}

/* --- 2. Сброс --------------------------------------------------------- */

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font);
  font-size: 15px;
  line-height: 1.6;
  font-feature-settings: "tnum" 1;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

img, svg { max-width: 100%; }
img { height: auto; }

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 4px;
}

::selection { background: var(--accent-soft); color: var(--ink); }

/* --- 3. Типографика --------------------------------------------------- */

h1, h2, h3 { margin: 0; letter-spacing: -.03em; text-wrap: pretty; }
h1 { font-size: 28px; font-weight: 800; line-height: 1.15; }
h2 { font-size: 22px; font-weight: 700; line-height: 1.2; letter-spacing: -.025em; }
h3 { font-size: 16px; font-weight: 700; line-height: 1.3; letter-spacing: -.02em; }
p  { margin: 0; }

a { color: var(--accent); text-decoration: none; }
a:hover { color: var(--accent-hover); text-decoration: underline; }

code {
  font-family: var(--font-mono);
  font-size: .92em;
  color: var(--ink-2);
}

strong { font-weight: 700; }

.text-body   { font-size: 15px; line-height: 1.6; color: var(--ink); }
.muted       { font-size: 13px; line-height: 1.55; color: var(--ink-muted); }
.hint        { font-size: 12px; line-height: 1.5; color: var(--ink-muted); font-weight: 400; }
.tabular     { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
.break-all   { word-break: break-all; }
.warn {
  font-size: 14px;
  line-height: 1.55;
  color: var(--ink);
  overflow-wrap: anywhere;
}
/* Постоянное предупреждение на странице (в отличие от разового баннера):
   тонкая линейка слева, чтобы отличалось от обычного текста. */
p.warn {
  padding-left: var(--s-3);
  border-left: 2px solid var(--warn);
}

/* --- 4. Шапка --------------------------------------------------------- */

.app-header {
  position: sticky;
  top: 0;
  z-index: 30;
  background: color-mix(in srgb, var(--bg) 86%, transparent);
  backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--border);
}

.app-header-inner {
  max-width: var(--container);
  margin: 0 auto;
  padding: 0 var(--s-4);
  height: var(--header-h);
  display: flex;
  align-items: center;
  gap: var(--s-3);
}

/* Логотип: rock + плашка yt + точка. Плашка живёт на .brand-yt. */
.brand {
  display: flex;
  align-items: center;
  flex: none;
  font-size: 17px;
  font-weight: 800;
  letter-spacing: -.03em;
  line-height: 1;
  color: var(--ink);
}
.brand:hover { color: var(--ink); text-decoration: none; }
.brand-yt {
  display: inline-flex;
  align-items: center;
  background: var(--signal);
  color: var(--signal-ink);
  border-radius: 5px;
  padding: .16em .2em .2em;
  margin-left: .08em;
  letter-spacing: -.02em;
}
.brand-dot { color: var(--accent); margin-left: .06em; }
.brand-tld { font-weight: 600; color: var(--ink-3); }

.spacer { flex: 1; min-width: 0; }

.app-nav {
  display: flex;
  align-items: center;
  gap: var(--s-2);
}
/* :not(.btn) — иначе цвет ссылки навигации перебивает собственный цвет
   кнопки, отрендеренной как <a> внутри шапки (лендинг). */
.app-nav a:not(.btn) {
  flex: none;
  font-size: 14px;
  font-weight: 600;
  color: var(--ink-3);
  padding: 7px 10px;
  border-radius: var(--r-sm);
}
.app-nav a:not(.btn):hover { color: var(--ink); text-decoration: none; }
.app-nav a:not(.btn)[aria-current="page"] {
  color: var(--ink);
  background: var(--surface-inset);
}
.app-nav a.btn { flex: none; color: var(--accent-ink); }
.app-nav a.btn-secondary { color: var(--ink); }

.theme-toggle, .lang-toggle {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  height: 36px;
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-sm);
  color: var(--ink-2);
  cursor: pointer;
  font-family: inherit;
  transition: border-color .14s;
}
.theme-toggle { width: 36px; padding: 0; }
.theme-toggle:hover, .lang-toggle:hover { border-color: var(--border-hover); }
.theme-toggle svg { width: 17px; height: 17px; }
.lang-toggle { gap: 6px; padding: 0 10px; font-size: 13px; font-weight: 600; }

/* Переключатель языка интерфейса. На <details>, чтобы работал без JS: закрытие
   по клику вне и по Esc добавлено четырьмя строками скрипта, но и без них
   меню открывается и закрывается по самой кнопке.

   Флаг эмодзи здесь оправдан вопреки общему правилу: это единственное место,
   где эмодзи несёт данные, а не украшение: флаг узнаётся быстрее названия,
   особенно когда человек ищет свой язык в списке на чужом ему языке. */
.lang-switch { position: relative; flex: none; }
.lang-switch > summary { padding: 0; list-style: none; }
.lang-switch > summary::-webkit-details-marker { display: none; }
.lang-switch > summary::before { content: none; }
.lang-switch .lang-toggle { pointer-events: none; }
.lang-switch[open] .lang-toggle { border-color: var(--border-hover); }
.lang-caret { font-size: 9px; color: var(--ink-muted); }

.lang-menu {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  z-index: 40;
  width: max-content;
  min-width: 160px;
  max-width: min(280px, calc(100vw - 40px));
  max-height: 330px;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 6px;
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-3);
  display: flex;
  flex-direction: column;
  gap: 1px;
}
.lang-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 10px;
  border-radius: var(--r-sm);
  font-size: 14px;
  font-weight: 500;
  color: var(--ink-2);
  text-decoration: none;
  white-space: nowrap;
}
.lang-item:hover { background: var(--surface-inset); color: var(--ink); text-decoration: none; }
.lang-item[aria-current="true"] { background: var(--accent-soft); color: var(--accent-hover); font-weight: 600; }
.lang-flag { width: 16px; height: 12px; flex: none; border-radius: 2px; object-fit: cover; }
.lang-native { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }

/* Меню шире доступного места — на десктопе right:0 от самой кнопки
   достаточно (места вокруг много), но на телефоне кнопка переключателя не
   у самого края экрана (правее ещё theme-toggle), и якорь «от кнопки»
   утаскивал широкое меню за левый край экрана. position:fixed относительно
   вьюпорта с одинаковыми отступами с обеих сторон работает независимо от
   того, где именно на панели сидит кнопка. */
@media (max-width: 400px) {
  .lang-menu {
    position: fixed;
    top: calc(var(--header-h) + 6px);
    left: 20px;
    right: 20px;
    width: auto;
    max-width: none;
  }
}

/* Иконка темы: показываем ту, что предлагает переключение */
.icon-sun { display: none; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .icon-moon { display: none; }
  :root:not([data-theme="light"]) .icon-sun  { display: block; }
}
:root[data-theme="dark"] .icon-moon { display: none; }
:root[data-theme="dark"] .icon-sun  { display: block; }
:root[data-theme="light"] .icon-moon { display: block; }
:root[data-theme="light"] .icon-sun  { display: none; }

/* Меню пользователя. Выход из аккаунта — не «настройки», а базовая
   потребность, поэтому живёт в шапке независимо от того, появятся ли настройки.
   На том же <details>, что и переключатель языка: работает без JS. */
.user-menu { position: relative; flex: none; }
.user-menu > summary { padding: 0; list-style: none; cursor: pointer; }
.user-menu > summary::-webkit-details-marker { display: none; }
.user-menu > summary::before { content: none; }

.user-avatar {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border-radius: var(--r-pill);
  background: var(--ink);
  color: var(--signal);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: .02em;
  border: 1px solid transparent;
}
.user-menu[open] .user-avatar { border-color: var(--border-hover); }

.user-dropdown {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  z-index: 40;
  width: 244px;
  padding: 6px;
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-3);
  display: flex;
  flex-direction: column;
  gap: 1px;
}
/* Почта — не пункт меню, а напоминание, под каким аккаунтом вы вошли:
   у человека бывает несколько аккаунтов и несколько каналов. */
.user-email {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 9px 10px 11px;
  border-bottom: 1px solid var(--border);
  margin-bottom: 5px;
}
.user-email-label {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ink-muted);
}
.user-email-value {
  font-size: 13px;
  font-weight: 600;
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
}
.user-item {
  display: block;
  padding: 9px 10px;
  border-radius: var(--r-sm);
  font-size: 14px;
  font-weight: 500;
  color: var(--ink-2);
  text-decoration: none;
}
.user-item:hover { background: var(--surface-inset); color: var(--ink); text-decoration: none; }
.user-item.is-exit { color: var(--danger-ink); }
.user-item.is-exit:hover { background: var(--danger-wash); color: var(--danger-ink); }
.user-form { margin: 0; }
.user-form button {
  width: 100%;
  text-align: left;
  font-family: inherit;
  border: 0;
  background: none;
  cursor: pointer;
}

@media (max-width: 400px) {
  .user-dropdown { width: calc(100vw - 40px); }
}

/* Гамбургер: на узких экранах в шапке не помещаются все элементы разом
   (переключатель языка + тема + вход/регистрация или аккаунт), поэтому
   всё, кроме логотипа и языка, уходит сюда. Скрыт на десктопе (display:none
   ниже, снимается в мобильном брейкпоинте) — на широком экране те же
   действия уже видны напрямую через .nav-desktop-only. */
.mobile-menu { position: relative; flex: none; display: none; }
.mobile-menu > summary { padding: 0; list-style: none; cursor: pointer; }
.mobile-menu > summary::-webkit-details-marker { display: none; }
.mobile-menu > summary::before { content: none; }
.mobile-menu-toggle {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-sm);
  color: var(--ink-2);
  cursor: pointer;
  transition: border-color .14s;
}
.mobile-menu[open] .mobile-menu-toggle { border-color: var(--border-hover); }
.mobile-menu-toggle svg { width: 18px; height: 18px; }
.mobile-menu-panel {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  z-index: 40;
  width: 244px;
  padding: 6px;
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-3);
  display: flex;
  flex-direction: column;
  gap: 1px;
}
@media (max-width: 400px) {
  .mobile-menu-panel {
    position: fixed;
    top: calc(var(--header-h) + 6px);
    left: 20px;
    right: 20px;
    width: auto;
  }
}

/* --- 5. Слой ---------------------------------------------------------- */

.container {
  max-width: var(--container);
  margin: 0 auto;
  padding-left: var(--s-4);
  padding-right: var(--s-4);
}
.container-narrow { max-width: var(--container-narrow); }

.page { flex: 1 0 auto; width: 100%; padding-top: var(--s-6); padding-bottom: var(--s-16); }

.stack    { display: flex; flex-direction: column; gap: var(--s-4); }
.stack-2  { display: flex; flex-direction: column; gap: var(--s-2); }
.stack-6  { display: flex; flex-direction: column; gap: var(--s-5); }

.row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-2);
}
.row-between { justify-content: space-between; }

.grid-auto {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: var(--s-4);
}

.section { display: flex; flex-direction: column; gap: var(--s-4); }
.section-title { font-size: 15px; font-weight: 700; letter-spacing: -.01em; }
.section-subtitle { font-size: 14px; line-height: 1.6; color: var(--ink-muted); max-width: 68ch; }

.breadcrumb {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-2);
  font-size: 13px;
  color: var(--ink-muted);
}
.breadcrumb a { color: var(--ink-muted); }
.breadcrumb .sep { color: var(--border-hover); }

/* --- 6. Кнопки -------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  font-family: inherit;
  font-size: 14px;
  font-weight: 600;
  line-height: 1.2;
  padding: 12px 20px;
  border-radius: var(--r);
  border: 0;
  background: var(--accent);
  color: var(--accent-ink);
  cursor: pointer;
  white-space: nowrap;
  box-shadow: var(--shadow-accent);
  transition: transform .14s, box-shadow .14s, background .14s, border-color .14s;
}
.btn:hover {
  color: var(--accent-ink);
  text-decoration: none;
  transform: translateY(-1px);
  box-shadow: var(--shadow-accent-hover);
}
.btn:active { transform: none; }

.btn-secondary {
  background: var(--surface);
  color: var(--ink);
  border: 1px solid var(--border-strong);
  box-shadow: none;
}
.btn-secondary:hover {
  color: var(--ink);
  background: var(--surface-2);
  border-color: var(--border-hover);
  transform: none;
  box-shadow: none;
}

.btn-ghost {
  background: transparent;
  color: var(--ink-3);
  box-shadow: none;
}
.btn-ghost:hover {
  color: var(--ink);
  background: var(--surface-inset);
  transform: none;
  box-shadow: none;
}

.btn-danger {
  background: var(--surface);
  color: var(--danger-ink);
  border: 1px solid var(--danger-line);
  box-shadow: none;
}
.btn-danger:hover {
  color: var(--danger-ink);
  background: var(--danger-wash);
  transform: none;
  box-shadow: none;
}

/* Подтверждающее действие: публикация в YouTube. Отличается от фиолетовой
   кнопки не «важностью», а типом — фиолетовая запускает работу внутри сервиса,
   зелёная отправляет результат наружу, зрителям. Цвет не новый: --ok уже
   означает «опубликовано» в плашках и полосе полного покрытия. */
.btn-publish {
  background: var(--publish);
  color: var(--publish-ink);
  box-shadow: 0 1px 2px rgba(0, 121, 95, .3);
}
.btn-publish:hover {
  background: var(--publish-hover);
  color: var(--publish-ink);
  box-shadow: 0 4px 14px rgba(0, 121, 95, .32);
}

.btn-sm { font-size: 13px; padding: 9px 15px; border-radius: var(--r-sm); }
.btn-block { width: 100%; }

/* На узком экране кнопка перестаёт быть островом и занимает строку */
@media (max-width: 480px) {
  .btn-responsive { width: 100%; }
}

/* --- 7. Карточки ------------------------------------------------------ */

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-xl);
  padding: var(--s-5);
  box-shadow: var(--shadow-1);
}
.card-flush { padding: 0; overflow: hidden; }

.card-section {
  margin-top: var(--s-4);
  padding-top: var(--s-4);
  border-top: 1px solid var(--border);
}

/* Тёмная панель: ядро продукта, «Что дал перевод» */
.panel {
  background: var(--panel);
  color: var(--panel-ink);
  border-radius: var(--r-xl);
  padding: var(--s-6);
  display: flex;
  flex-direction: column;
  gap: var(--s-5);
}
.panel .muted, .panel .hint { color: var(--panel-muted); }
.panel-divider { padding-top: var(--s-4); border-top: 1px solid var(--panel-border); }

/* Крупное доказательство: одно число, вокруг него — контекст */
.proof-head {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-8);
  align-items: flex-end;
}
.proof-label {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--panel-muted);
}
.proof-num {
  font-size: 52px;
  font-weight: 800;
  letter-spacing: -.04em;
  line-height: 1;
  color: var(--signal);
}
.proof-stats { display: flex; flex-wrap: wrap; gap: var(--s-6); padding-bottom: var(--s-1); }
.proof-stat { display: flex; flex-direction: column; gap: var(--s-1); }
.proof-stat-label { font-size: 12px; color: var(--panel-muted); }
.proof-stat-value { font-size: 22px; font-weight: 700; }
.proof-stat-value.is-after { color: var(--signal); }
.proof-line { font-size: 15px; line-height: 1.6; color: var(--ink-2); }

/* Абзац с основанием расчёта. Убирать нельзя: без него цифры выглядят
   точнее, чем они есть. */
.proof-basis {
  font-size: 13px;
  line-height: 1.65;
  color: var(--ink-2);
  max-width: 80ch;
}
.proof-basis-box {
  background: var(--bg-sunken);
  border-radius: var(--r-lg);
  padding: var(--s-5);
  display: flex;
  flex-direction: column;
  gap: var(--s-3);
}

/* --- 8. Формы --------------------------------------------------------- */

.field { display: flex; flex-direction: column; gap: 7px; }

/* Один вход в системе, где текст набирается по одной цифре за раз — крупнее
   и вразрядку, чтобы шесть символов читались как код, а не как обычное
   поле. */
.field-code { font-size: 20px; letter-spacing: .4em; text-align: center; }

.field label, .label {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

input[type="text"], input[type="email"], input[type="password"],
input[type="search"], input[type="number"], textarea, select {
  font-family: inherit;
  font-size: 14px;
  line-height: 1.5;
  padding: 11px 13px;
  border-radius: var(--r);
  border: 1px solid var(--border-strong);
  background: var(--surface-2);
  color: var(--ink);
  outline: none;
  width: 100%;
  transition: border-color .14s, box-shadow .14s, background .14s;
}
input::placeholder, textarea::placeholder { color: var(--ink-muted); opacity: 1; }
input:focus, textarea:focus, select:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-ring);
  background: var(--surface);
}
textarea { resize: vertical; }

.field-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(148px, 1fr));
  gap: var(--s-3);
}

.checkbox-row { display: flex; align-items: flex-start; gap: var(--s-3); }
.checkbox-row input[type="checkbox"] {
  width: 17px; height: 17px;
  accent-color: var(--accent);
  cursor: pointer;
  flex: none;
  margin: 2px 0 0;
}
.checkbox-row label {
  font-size: 14px;
  font-weight: 600;
  line-height: 1.4;
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink);
  cursor: pointer;
}

/* Счётчик символов — функциональный элемент: лимиты YouTube 100 / 5000 */
.counter { font-family: var(--font-mono); font-size: 12px; font-weight: 500; }
.len-ok   { color: var(--ok-fg); }
.len-over { color: var(--danger-fg); font-weight: 600; }

.form-inline { display: inline-flex; margin: 0; }

/* Раскрывающийся блок */
details > summary {
  font-size: 14px;
  font-weight: 600;
  color: var(--ink-2);
  cursor: pointer;
  padding: var(--s-2) 0;
  list-style: none;
  display: flex;
  align-items: center;
  gap: var(--s-2);
}
details > summary::-webkit-details-marker { display: none; }
details > summary::before {
  content: "";
  width: 7px; height: 7px;
  border-right: 2px solid var(--ink-muted);
  border-bottom: 2px solid var(--ink-muted);
  transform: rotate(-45deg);
  transition: transform .16s;
  flex: none;
}
details[open] > summary::before { transform: rotate(45deg); }
details > summary:hover { color: var(--ink); }

/* --- 8b. Ошибки в формах ---------------------------------------------- */
/* Ошибка живёт рядом с полем, а не только сверху страницы: человек видит
   её там, где исправляет. Красная рамка дублируется текстом и иконкой —
   цвет никогда не единственный носитель смысла. */

.field-error {
  display: block;
  position: relative;
  padding-left: 20px;
  font-size: 12px;
  line-height: 1.5;
  font-weight: 500;
  color: var(--danger-fg);
}
.field-error a { color: inherit; text-decoration: underline; }
.field-error::before {
  content: "!";
  position: absolute;
  left: 0;
  top: 2px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 14px;
  height: 14px;
  border-radius: var(--r-pill);
  background: var(--danger-fg);
  color: var(--surface);
  font-size: 10px;
  font-weight: 800;
  line-height: 1;
}

input[aria-invalid="true"], textarea[aria-invalid="true"], select[aria-invalid="true"] {
  border-color: var(--danger-bd);
  background: var(--danger-wash);
}
input[aria-invalid="true"]:focus, textarea[aria-invalid="true"]:focus {
  border-color: var(--danger);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--danger) 18%, transparent);
}

/* Поле, заполненное верно после ошибки — тихое подтверждение */
.field-ok {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  font-weight: 500;
  color: var(--ok-fg);
}

/* Блок ошибки уровня формы: заголовок + что делать дальше */
/* Заголовок баннера — своя строка во всех вариантах, иначе следующее
   предложение прилипает к нему без пробела. */
.flash strong { display: block; margin-bottom: 2px; }
.flash a { color: inherit; text-decoration: underline; }

/* Неактивная кнопка: пока форма не готова к отправке */
.btn:disabled, .btn[aria-disabled="true"] {
  background: var(--surface-inset);
  color: var(--ink-muted);
  border-color: var(--border);
  box-shadow: none;
  cursor: not-allowed;
  transform: none;
}
.btn:disabled:hover, .btn[aria-disabled="true"]:hover {
  transform: none;
  box-shadow: none;
  background: var(--surface-inset);
  color: var(--ink-muted);
}

/* --- 9. Плашки и флеши ------------------------------------------------ */

/* У каждой плашки есть слово: статус никогда не передаётся одним цветом. */
.tag {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  font-weight: 600;
  line-height: 1.4;
  padding: 3px 9px;
  border-radius: var(--r-pill);
  white-space: nowrap;
  background: var(--surface-inset);
  color: var(--ink-3);
  border: 1px solid var(--border);
}
.tag-ok       { background: var(--ok-bg);     color: var(--ok-fg);     border-color: var(--ok-bd); }
.tag-new      { background: var(--accent-soft); color: var(--accent-hover); border-color: var(--accent-border); }
.tag-draft    { background: var(--warn-bg);   color: var(--warn-fg);   border-color: var(--warn-bd); }
.tag-error    { background: var(--danger-bg); color: var(--danger-fg); border-color: var(--danger-bd); }
.tag-existing { background: var(--surface-inset); color: var(--ink-3); border-color: var(--border); }

/* Карточка/строка видео в списке: то же «оригинал разошёлся с YouTube»,
   что жёлтый "!" на вкладках карточки видео (.tab-badge.is-warn) — здесь
   как круглая иконка-глазок рядом с тегами покрытия языков, а не текстовый
   .tag: слово тут — в title-тултипе, а не в теле бейджа, тот же контракт,
   что уже принят для .tab-badge на этой же странице деталей видео. */
.attn-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--warn-bg);
  color: var(--warn-fg);
  border: 1px solid var(--warn-bd);
  font-size: 11px;
  font-weight: 700;
  line-height: 1;
  cursor: default;
}

/* Значок-бейдж результата операции (напр. итог OAuth) — тот же цветовой
   контракт, что .tag-ok/.tag-error, но как круглая иконка, а не текстовый чип. */
.result-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  border-radius: var(--r-lg);
  flex: none;
}
.result-icon.is-ok    { background: var(--ok-bg);     color: var(--ok-fg); }
.result-icon.is-error { background: var(--danger-bg); color: var(--danger-fg); }

.flash {
  display: flex;
  flex-direction: column;
  gap: var(--s-1);
  padding: 14px 16px;
  border-radius: var(--r-lg);
  font-size: 14px;
  line-height: 1.55;
  border: 1px solid var(--border);
  background: var(--surface-inset);
  color: var(--ink);
  /* В текст ошибки прилетают сырые ответы API с длинными токенами
     (JSON, URL) — без этого баннер растягивает страницу по горизонтали. */
  overflow-wrap: anywhere;
}
.flash code {
  color: inherit;
  background: color-mix(in srgb, currentColor 10%, transparent);
  padding: 1px 4px;
  border-radius: 4px;
}
.flash-ok, .flash-success { background: var(--ok-bg); border-color: var(--ok-bd); color: var(--ok-fg); }
.flash-warn  { background: var(--warn-bg);   border-color: var(--warn-bd);   color: var(--warn-fg); }
.flash-error { background: var(--danger-bg); border-color: var(--danger-bd); color: var(--danger-fg); }
.flash-info  { background: var(--accent-soft); border-color: var(--accent-border); color: var(--accent-hover); }

/* --- 10. Полоса покрытия и прогресс ----------------------------------- */

.meter { display: flex; flex-direction: column; gap: var(--s-2); max-width: 460px; }
.meter-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-2);
}
.meter-value { font-size: 13px; font-weight: 600; color: var(--ink-2); }
.meter-track {
  height: 8px;
  border-radius: var(--r-pill);
  background: var(--chart-track);
  overflow: hidden;
}
.meter-fill {
  height: 100%;
  border-radius: var(--r-pill);
  background: var(--chart-bar);
  transform-origin: left;
  animation: rk-grow .7s cubic-bezier(.2, .8, .2, 1);
}
.meter-fill.is-full { background: var(--ok); }
.meter-fill.is-empty { background: var(--border-hover); }

.progress {
  height: 10px;
  border-radius: var(--r-pill);
  background: var(--panel-border);
  overflow: hidden;
}
.progress-bar {
  height: 100%;
  width: 0;
  border-radius: var(--r-pill);
  background: var(--signal);
  transition: width .5s cubic-bezier(.2, .8, .2, 1);
}

@keyframes rk-grow { from { transform: scaleX(0); } to { transform: scaleX(1); } }
@keyframes rk-pulse { 0%, 100% { opacity: .35; } 50% { opacity: 1; } }

.dot-live {
  width: 8px; height: 8px;
  border-radius: var(--r-pill);
  background: var(--signal);
  flex: none;
  animation: rk-pulse 1.4s ease-in-out infinite;
}

/* Загрузка файла. Нативный <input type="file"> рисуется движком браузера и
   не поддаётся оформлению — поэтому сам инпут прячется, а видимой целью
   становится <label>, стилизованная как вторичная кнопка. Инпут остаётся в
   потоке (не display:none), иначе он перестаёт получать фокус с клавиатуры. */
.file-field { display: flex; flex-direction: column; gap: var(--s-2); }
.file-field input[type="file"] {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}
/* Сама зона — тоже <label>, поэтому наследовала бы мелкий капс от
   `.field label`; регистр, трекинг и размер здесь возвращаются к обычному
   тексту намеренно. */
.field .file-drop, .file-drop {
  display: inline-flex;
  align-items: center;
  gap: var(--s-3);
  align-self: flex-start;
  max-width: 100%;
  padding: 10px 16px;
  border: 1px dashed var(--border-strong);
  border-radius: var(--r);
  background: var(--surface-2);
  color: var(--ink-2);
  font-size: 14px;
  font-weight: 500;
  letter-spacing: normal;
  text-transform: none;
  cursor: pointer;
  transition: border-color .14s, background .14s, color .14s;
}
.file-drop:hover { border-color: var(--accent); color: var(--ink); }
.file-field input[type="file"]:focus-visible + .file-drop {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.file-drop svg { width: 18px; height: 18px; flex: none; }
.file-drop-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.file-field.has-file .file-drop {
  border-style: solid;
  border-color: var(--accent);
  color: var(--ink);
}

/* Сброс выбранного файла. Живёт в потоке рядом с зоной выбора, а не поверх
   неё: наложение попало бы внутрь <label>, и клик по крестику открывал бы
   диалог выбора файла вместо отмены. */
.file-row { display: flex; align-items: center; gap: var(--s-2); flex-wrap: wrap; }
.file-clear {
  display: none;
  align-items: center;
  justify-content: center;
  width: 32px; height: 32px;
  flex: none;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--ink-muted);
  cursor: pointer;
  transition: color .14s, border-color .14s, background .14s;
}
.file-clear:hover {
  color: var(--danger-ink);
  border-color: var(--danger-line);
  background: var(--danger-wash);
}
.file-clear svg { width: 15px; height: 15px; }
.file-field.has-file .file-clear { display: inline-flex; }

/* Кнопка, ожидающая ответа сервера. Некоторые действия идут через ИИ и
   занимают 10-20 секунд — без обратной связи страница выглядит мёртвой, и
   человек жмёт кнопку повторно. Ставится скриптом в base.html по атрибуту
   data-busy на форме.
   Намеренно НЕ используется атрибут disabled: он бы отбросил name/value
   нажатой кнопки из отправляемой формы (у нас так выбирается вариант
   генерации обложки), поэтому клики глушатся через pointer-events. */
.btn.is-busy {
  pointer-events: none;
  opacity: .75;
  box-shadow: none;
}
.btn.is-busy .dot-live {
  background: currentColor;
}

/* Чипы языков в процессе перевода */
.lang-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.lang-chip {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 500;
  padding: 4px 8px;
  border-radius: 7px;
  background: var(--panel-3);
  color: var(--panel-muted);
}
.lang-chip.is-done    { background: var(--panel-border); color: var(--signal); }
.lang-chip.is-current { background: var(--signal); color: var(--signal-ink); }
.lang-chip.is-error   { background: var(--panel-danger-bg); color: var(--panel-danger-fg); }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
  }
}

/* --- 11. Таблицы ------------------------------------------------------ */

.table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }

.table, .table-compact {
  width: 100%;
  border-collapse: collapse;
  font-size: 14px;
}
.table th, .table-compact th {
  text-align: left;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--ink-muted);
  padding: 10px 12px;
  white-space: nowrap;
}
.table td, .table-compact td {
  padding: 12px;
  border-top: 1px solid var(--border);
  vertical-align: top;
}
.table-compact { min-width: 480px; }
.table-compact td { padding: 10px 12px; }
.table td:first-child, .table th:first-child,
.table-compact td:first-child, .table-compact th:first-child { padding-left: var(--s-5); }
.table td:last-child, .table th:last-child,
.table-compact td:last-child, .table-compact th:last-child { padding-right: var(--s-5); }
.table .num, .table-compact .num { text-align: right; font-family: var(--font-mono); }
.table .delta-up, .table-compact .delta-up {
  color: var(--ok-fg); font-family: var(--font-mono); font-weight: 500;
}
.table .delta-down, .table-compact .delta-down {
  color: var(--danger-fg); font-family: var(--font-mono); font-weight: 500;
}

.table-compact td.error-cell {
  color: var(--danger-fg);
  overflow-wrap: anywhere;
}

.preview-text {
  font-size: 14px;
  line-height: 1.5;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* --- 12. Языки: список + редактор ------------------------------------- */
/* Замена таблице на 47 строк: слева выбор, справа один язык целиком. */

.split {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-4);
  align-items: flex-start;
}
.split-list   { flex: 1 1 300px; min-width: 260px; }
.split-detail {
  flex: 1.6 1 380px;
  min-width: 280px;
  /* .split-detail is jumped to via a same-page `#detail` anchor link
     (see .lang-row hrefs) — without this, the sticky .app-header (60px)
     covers the top of the card right after the jump, so it looks like
     nothing happened. Below --header-h on narrow viewports too, since
     .split stacks the detail card under the full language list there and
     the anchor jump is the only way a user finds it. */
  scroll-margin-top: calc(var(--header-h) + var(--s-4));
}

.lang-list { max-height: 520px; overflow-y: auto; overscroll-behavior: contain; }

/* На 47 строках плоский список нечитаем: группы по состоянию ставят наверх то,
   что требует действия, а заголовок группы липнет к верху при прокрутке, чтобы
   не терялся контекст. Порядок групп = порядок срочности. */
.lang-group {
  position: sticky;
  top: 0;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-2);
  padding: 8px 16px;
  background: var(--surface-inset);
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ink-muted);
}
.lang-group:first-child { border-top: 0; }
.lang-group-count { font-family: var(--font-mono); letter-spacing: 0; }
.lang-row {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  padding: 12px 16px;
  border: 0;
  border-left: 3px solid transparent;
  background: var(--surface);
  cursor: pointer;
  font-family: inherit;
  text-align: left;
  transition: background .14s;
}
.lang-row:hover { background: var(--surface-2); }
.lang-row.is-selected { border-left-color: var(--accent); background: var(--accent-soft); }
.lang-row-name { font-size: 14px; font-weight: 600; color: var(--ink); }
.lang-row-code { font-family: var(--font-mono); font-size: 12px; color: var(--ink-muted); }
.lang-row-title {
  font-size: 12px;
  color: var(--ink-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 210px;
}

/* --- 13. Вкладки ------------------------------------------------------ */
/* Карточка видео разнесена по вкладкам: экран готов к росту функционала. */

.tabs {
  display: flex;
  gap: 2px;
  border-bottom: 1px solid var(--border);
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}
.tab {
  display: flex;
  align-items: center;
  gap: 7px;
  font-family: inherit;
  font-size: 14px;
  font-weight: 600;
  padding: 11px 14px;
  border: 0;
  border-bottom: 2px solid transparent;
  background: none;
  color: var(--ink-muted);
  cursor: pointer;
  white-space: nowrap;
  text-decoration: none;
}
.tab:hover { color: var(--ink); text-decoration: none; }
.tab[aria-selected="true"] { border-bottom-color: var(--accent); color: var(--ink); }
.tab-badge {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 500;
  padding: 2px 6px;
  border-radius: 6px;
  background: var(--surface-inset);
  color: var(--ink-muted);
}
.tab[aria-selected="true"] .tab-badge { background: var(--accent-soft); color: var(--accent-hover); }
/* Разошедшийся с YouTube оригинал — источник видео поменяли не через сервис
   (в Студии) либо ретайтлом, а переводы остались от старого текста. Жёлтый,
   не нейтральный, — это не «здесь что-то есть», а «здесь надо среагировать». */
.tab-badge.is-warn,
.tab[aria-selected="true"] .tab-badge.is-warn {
  background: var(--warn-bg);
  color: var(--warn-fg);
}

/* --- 14. Идентичность видео и канала ---------------------------------- */

.identity {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: var(--s-5);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-xl);
  padding: var(--s-5);
  box-shadow: var(--shadow-1);
}
.identity-thumb {
  flex: 0 1 196px;
  min-width: 140px;
  aspect-ratio: 16 / 9;
  border-radius: var(--r-lg);
  background: linear-gradient(135deg, #1D2025, #3A3D42);
  object-fit: cover;
}
.identity-body {
  flex: 1 1 300px;
  min-width: 240px;
  display: flex;
  flex-direction: column;
  gap: var(--s-3);
}
.identity-title { font-size: 22px; font-weight: 700; line-height: 1.28; letter-spacing: -.02em; }
.identity-actions { display: flex; flex-direction: column; gap: var(--s-2); align-self: center; }

.channel-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-3) var(--s-4);
  padding: var(--s-4) var(--s-5);
  background: var(--surface);
}
.channel-row + .channel-row { border-top: 1px solid var(--border); }
.channel-id { flex: 1 1 190px; min-width: 150px; display: flex; flex-direction: column; gap: 5px; }
.channel-name { font-size: 15px; font-weight: 700; letter-spacing: -.01em; }
.avatar, .avatar-placeholder {
  width: 42px; height: 42px;
  border-radius: var(--r-pill);
  flex: none;
  object-fit: cover;
}
.avatar-placeholder {
  background: repeating-linear-gradient(45deg,
    var(--surface-inset), var(--surface-inset) 6px,
    var(--border) 6px, var(--border) 12px);
}

/* --- 15. Список видео ------------------------------------------------- */

.toolbar {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-3);
  align-items: center;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: 14px var(--s-4);
  box-shadow: var(--shadow-1);
}
.toolbar input[type="search"], .toolbar input[type="text"] { flex: 1 1 220px; min-width: 180px; width: auto; }

.segmented {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  background: var(--bg-sunken);
  padding: 3px;
  border-radius: var(--r-sm);
}
.segmented > * {
  display: flex;
  align-items: center;
  gap: 7px;
  font-family: inherit;
  font-size: 13px;
  font-weight: 600;
  padding: 7px 12px;
  border: 0;
  border-radius: 8px;
  background: transparent;
  color: var(--ink-3);
  cursor: pointer;
  white-space: nowrap;
  text-decoration: none;
}
.segmented > [aria-pressed="true"], .segmented > .is-active {
  background: var(--surface);
  color: var(--ink);
  box-shadow: var(--shadow-1);
}
.segmented .count { font-family: var(--font-mono); font-size: 11px; font-weight: 500; color: var(--ink-muted); }

.video-list {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-xl);
  overflow: hidden;
  box-shadow: var(--shadow-1);
}
.video-list-head {
  display: flex;
  align-items: center;
  gap: var(--s-4);
  padding: 11px var(--s-5);
  border-bottom: 1px solid var(--border);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--ink-muted);
}
.video-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-3) var(--s-4);
  padding: 13px var(--s-5);
  background: var(--surface);
}
.video-row + .video-row { border-top: 1px solid var(--border); }
.video-row-thumb {
  width: 78px; height: 44px;
  border-radius: var(--r-sm);
  flex: none;
  background: linear-gradient(135deg, #1D2025, #3A3D42);
  object-fit: cover;
}
.col-title    { flex: 1 1 220px; min-width: 170px; display: flex; flex-direction: column; gap: 5px; }
.col-date     { flex: 0 1 112px; min-width: 96px; font-family: var(--font-mono); font-size: 13px; color: var(--ink-3); }
.col-coverage { flex: 0 1 168px; min-width: 120px; display: flex; flex-direction: column; gap: 6px; }
.col-coverage .meter-track { height: 6px; }

.video-title {
  font-size: 14px;
  font-weight: 600;
  color: var(--ink);
  line-height: 1.35;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
a.video-title:hover, .video-card:hover .video-title { color: var(--ink); }

/* Шапка колонок бессмысленна, когда строки схлопнулись в карточки */
@media (max-width: 640px) {
  .video-list-head { display: none; }
}

.video-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(258px, 1fr));
  gap: var(--s-4);
}
.video-card {
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  overflow: hidden;
  box-shadow: var(--shadow-1);
  color: var(--ink);
  transition: transform .14s, box-shadow .14s;
}
.video-card:hover {
  text-decoration: none;
  transform: translateY(-2px);
  box-shadow: 0 8px 22px rgba(15, 16, 18, .08);
}
.video-card img,
.video-card-thumb {
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  background: linear-gradient(135deg, #1D2025, #3A3D42);
  display: block;
  flex: none;
}
.video-card-body {
  padding: 14px;
  display: flex;
  flex-direction: column;
  gap: 11px;
  flex: 1;
}

/* Пагинация. У канала бывают сотни видео, поэтому «Показать ещё» не подходит:
   без номеров нельзя вернуться на то место, где закончил, и нельзя сказать
   коллеге «смотри на третьей странице». Слева всегда счётчик диапазона —
   он отвечает на вопрос «сколько всего», не заставляя листать до конца. */
.pagination {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  padding-top: var(--s-2);
}
.pagination-count { font-size: 13px; color: var(--ink-muted); }
.pagination-pages { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; }

.page-link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 36px;
  height: 36px;
  padding: 0 10px;
  border-radius: var(--r-sm);
  border: 1px solid transparent;
  background: transparent;
  font-family: var(--font-mono);
  font-size: 13px;
  font-weight: 500;
  color: var(--ink-2);
  text-decoration: none;
}
.page-link:hover {
  background: var(--surface-inset);
  color: var(--ink);
  text-decoration: none;
}
.page-link[aria-current="page"] {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
  font-weight: 600;
}
.page-link[aria-current="page"]:hover { background: var(--accent); color: var(--accent-ink); }

/* Стрелки: подписаны словом на широком экране, значком на узком */
.page-link.is-nav {
  font-family: var(--font);
  font-weight: 600;
  border-color: var(--border-strong);
  background: var(--surface);
  color: var(--ink);
}
.page-link.is-nav:hover { border-color: var(--border-hover); background: var(--surface-2); }
.page-link.is-disabled {
  color: var(--border-hover);
  border-color: var(--border);
  background: var(--surface);
  pointer-events: none;
}

/* Пропуск страниц — не кнопка, поэтому не притворяется ею */
.page-gap {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 24px;
  height: 36px;
  color: var(--ink-muted);
  font-size: 13px;
  user-select: none;
}

/* Размер страницы стоит рядом с номерами: его меняют в том же контексте */
.page-size {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  font-size: 13px;
  color: var(--ink-muted);
}
.page-size select {
  width: auto;
  padding: 7px 10px;
  font-size: 13px;
  border-radius: var(--r-sm);
}

@media (max-width: 560px) {
  .pagination { justify-content: center; }
  .page-nav-word { display: none; }
}
@media (min-width: 561px) {
  .page-nav-icon { display: none; }
}

/* --- 15b. История видео ----------------------------------------------- */
/* Хронология одного видео. Дата — заголовок группы, внутри только время:
   повторять «11 мая» у каждой строки значит превращать хронологию в таблицу.
   Плашка типа события стоит в колонке фиксированной ширины — иначе текст
   начинался бы на разном x в каждой строке (разброс до 29px) и колонки,
   ради которой плашку вынесли вперёд, не получалось бы. */

.history-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  padding: var(--s-5) var(--s-5) var(--s-4);
  border-bottom: 1px solid var(--border);
}

.history {
  display: flex;
  flex-direction: column;
  /* Свой скроллер обязателен: без него position:sticky у .history-day
     не работает — скроллпортом становится overflow:hidden самой карточки,
     а он никогда не прокручивается. Тот же приём у .lang-list. */
  max-height: 560px;
  overflow-y: auto;
  overscroll-behavior: contain;
}

.history-day {
  position: sticky;
  top: 0;
  z-index: 1;
  padding: 8px var(--s-5);
  background: var(--surface-inset);
  border-bottom: 1px solid var(--border);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ink-muted);
}
.history-day:not(:first-child) { border-top: 1px solid var(--border); }

.history-item {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: var(--s-2) var(--s-3);
  padding: 13px var(--s-5);
  background: var(--surface);
}
.history-item + .history-item { border-top: 1px solid var(--border); }

.history-time {
  flex: 0 0 44px;
  font-size: 12px;
  color: var(--ink-muted);
  padding-top: 2px;
}
.history-kind {
  flex: 0 0 104px;
  display: flex;
  padding-top: 1px;
}
.history-body {
  flex: 1 1 260px;
  min-width: 220px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.history-text { font-size: 14px; font-weight: 600; line-height: 1.45; }
.history-body .muted { font-size: 13px; line-height: 1.55; }

/* Событие «записано на YouTube» помечается тем же зелёным, что и кнопки
   записи — плашка и кнопка означают одно и то же действие. */
.tag-publish {
  background: var(--ok-bg);
  color: var(--publish);
  border-color: var(--ok-bd);
}

@media (max-width: 560px) {
  .history-time, .history-kind { flex: 0 0 auto; }
}

/* --- 15c. Настройки аккаунта ------------------------------------------ */
/* Правило страницы: карточка = действие. Всё, что человек только читает
   (почта, тариф, счётчики), карточкой не оборачивается — иначе четыре
   одинаковых блока подряд, и непонятно, где вообще можно что-то сделать.
   Читаемое подаётся строками-определениями, единственное действие —
   смена пароля — единственная карточка на странице. */

.settings { display: flex; flex-direction: column; gap: var(--s-8); max-width: 680px; }
.settings-block { display: flex; flex-direction: column; gap: var(--s-3); }
.settings-head { display: flex; flex-direction: column; gap: 3px; }
.settings-title {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

/* Строка-определение. Значение справа, на узком экране уходит под подпись —
   поэтому не таблица: у таблицы на 375px схлопывается вся сетка. */
.def-row {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-2) var(--s-4);
  padding: 13px 0;
  border-bottom: 1px solid var(--border);
}
.def-row:first-of-type { border-top: 1px solid var(--border); }
.def-key { font-size: 14px; color: var(--ink-2); }
.def-val { font-size: 14px; font-weight: 600; color: var(--ink); text-align: right; }
.def-val.is-empty { font-weight: 400; color: var(--ink-muted); }

/* Почта показывается текстом, а НЕ disabled-полем: серое поле ввода читается
   как сломанная форма — человек пытается в него печатать. Раз менять нельзя,
   это не поле. */
.def-email { font-family: var(--font-mono); font-size: 14px; font-weight: 500; }

/* Заглушка. Отделена от рабочих блоков явно: пока за тарифом и балансом нет
   системы, показывать их как работающие — врать пользователю. */
.stub {
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: var(--s-4) var(--s-5) var(--s-5);
  display: flex;
  flex-direction: column;
  gap: var(--s-2);
}
.stub .def-row { border-color: var(--border); }
.stub .def-row:last-of-type { border-bottom: 0; padding-bottom: 0; }

/* --- 15d. Вкладка «Обложка» -------------------------------------------- */
/* Экран, где каждое нажатие стоит денег. Отсюда два правила, которых нет на
   других вкладках: цена всегда внутри кнопки (а не подписью рядом), и всё
   дороже разбора требует подтверждения — один клик ничего не запускает. */

/* Цена внутри кнопки. Дешёвая набрана на фоне самой кнопки и почти не
   заметна; дорогая — чернильный чип с лаймовым числом, тот же контраст, что
   у логотипа. Это единственный носитель разницы «8 против 30». */
.price {
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 500;
  padding: 3px 8px;
  border-radius: 7px;
  white-space: nowrap;
}
.price-cheap { background: rgba(255, 255, 255, .18); color: inherit; }
.price-quiet { background: var(--surface-inset); color: var(--ink-2); }
.price-dear  { background: var(--ink); color: var(--signal); }
.btn .price { margin-left: 2px; }

/* Полоса «Было и стало». Первое, что видно на вкладке: текущая обложка и
   рядом все сгенерированные. Прокрутка по горизонтали, кадры 16:9 —
   на мобильном ведёт себя так же, отдельного шаблона не нужно. */
.tn-strip {
  display: flex;
  gap: var(--s-4);
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  padding-bottom: var(--s-1);
}
.tn-slot {
  flex: none;
  width: 288px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.tn-slot.is-wide { width: 320px; }
.tn-frame {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  border-radius: 14px;
  overflow: hidden;
  border: 1px solid var(--border);
  background: var(--surface-inset);
}
.tn-frame.is-empty { border-style: dashed; border-color: var(--border-hover); background: var(--surface-2); }
.tn-frame img { width: 100%; height: 100%; object-fit: cover; display: block; }
.tn-frame-stub { position: absolute; inset: 0; background: linear-gradient(135deg, #1D2025, #33373E); }

/* Подпись кадра. «Скачать» намеренно НЕ здесь, а в строке действий ниже:
   теги разной длины переносили кнопку на вторую строку у одних кадров и
   оставляли в первой у других, и одинаковые действия вставали на разной
   высоте. Строка действий у всех кадров одна и та же. */
.tn-caption {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2);
  align-items: center;
  justify-content: space-between;
  min-height: 26px;
}
.tn-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2);
  align-items: center;
  padding-top: 9px;
  border-top: 1px solid var(--border);
}
.tn-actions .btn { flex: none; }
.tn-actions .btn-use { flex: 1 1 auto; }
.btn-use.is-on { border-color: var(--signal); background: #F4FBE2; color: var(--ink); }

/* Отметка «эту я поставил». Без неё список из шести картинок по 30 токенов
   превращается в свалку: слот «Сейчас на YouTube» не обновляется никогда,
   потому что сервис не умеет заливать обложку. */
.tn-used {
  position: absolute;
  top: 10px;
  left: 10px;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 11px;
  font-weight: 700;
  padding: 5px 10px;
  border-radius: var(--r-pill);
  background: var(--signal);
  color: var(--signal-ink);
}

/* Ожидание 80 секунд происходит НА МЕСТЕ будущей картинки, а не под кнопкой:
   человек смотрит туда, где появится результат. Автообновление обязательно —
   без него это снова читается как поломка. */
.tn-run {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: 10px;
  padding: 14px;
  background: var(--panel-2);
  overflow: hidden;
}
.tn-run > * { position: relative; }
.tn-run::before {
  content: "";
  position: absolute;
  top: 0; bottom: 0; left: 0;
  width: 34%;
  background: linear-gradient(90deg, transparent, rgba(198, 242, 78, .14), transparent);
  animation: tn-sweep 2.2s cubic-bezier(.4, 0, .2, 1) infinite;
}
.tn-run-label { display: flex; align-items: center; gap: var(--s-2); font-size: 13px; font-weight: 600; color: var(--panel-ink); }
.tn-run-track { height: 6px; border-radius: var(--r-pill); background: var(--panel-border); overflow: hidden; }
.tn-run-bar { height: 100%; border-radius: var(--r-pill); background: var(--signal); transition: width .9s linear; }
.tn-run-time { display: flex; justify-content: space-between; font-family: var(--font-mono); font-size: 11px; color: var(--panel-muted); }
@keyframes tn-sweep { 0% { transform: translateX(-100%) } 100% { transform: translateX(280%) } }

/* Неудача занимает кадр целиком. Текст здесь ограничен по высоте самим
   кадром (16:9), поэтому в плитку идёт только причина одной фразой, а совет
   живёт под кадром в .tn-fail-hint, где высота ничем не зажата.
   overflow-y — страховка: ни одно будущее сообщение не должно обрезаться
   на середине фразы, даже если окажется длиннее, чем я рассчитывал. */
.tn-fail {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--s-2);
  padding: var(--s-4);
  padding-right: 34px;   /* место под крестик, чтобы текст под него не заезжал */
  background: var(--danger-wash);
  overflow-y: auto;
  overscroll-behavior: contain;
}
.tn-fail-title { font-size: 13px; color: var(--danger-fg); }
.tn-fail-reason { font-size: 12px; line-height: 1.5; color: var(--danger-fg); }
.tn-fail-hint { font-size: 12px; line-height: 1.5; color: var(--ink-muted); margin: 0; }

/* Крестик поверх кадра: убрать прочитанную ошибку. Отдельно от «Запустить
   заново» — то действие платное, и промахнуться по нему нельзя. */
.tn-dismiss-form { position: absolute; top: 6px; right: 6px; margin: 0; }
.tn-dismiss {
  width: 24px;
  height: 24px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 17px;
  line-height: 1;
  color: var(--danger-fg);
  background: transparent;
  border: 0;
  border-radius: var(--r-sm);
  cursor: pointer;
  opacity: .55;
  transition: opacity .15s ease, background-color .15s ease;
}
.tn-dismiss:hover { opacity: 1; background: var(--danger-line); }
.tn-dismiss:focus-visible { opacity: 1; outline: 2px solid var(--danger-fg); outline-offset: 1px; }
.tn-empty {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 18px;
  font-size: 12px;
  line-height: 1.5;
  color: var(--ink-muted);
  text-align: center;
}

/* Карточка варианта: свой текст, своя кнопка, своя цена, своё ожидание.
   Кнопка живёт внутри рекомендации, к которой относится. */
.tn-var { display: flex; flex-direction: column; }

/* Концепция, выбранная нейросетью. Отличается рамкой, а не заливкой: карточки
   стоят в ряд, и заливка одной из трёх читалась бы как «остальные неактивны»,
   тогда как выбрать можно любую. */
.tn-var-best { border-color: var(--accent); }

/* Слепая проверка под готовым кадром. Намеренно тихая: это не ошибка и не
   успех, а второе мнение. Кричать красным тут не о чем — картинка уже
   оплачена и годна, речь только о том, всё ли в ней читается. */
.tn-check {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 9px 0 2px;
  font-size: 12px;
  line-height: 1.5;
  color: var(--ink-3);
}
.tn-check-row { display: flex; gap: var(--s-2); align-items: baseline; }
.tn-check-mark {
  flex: none;
  width: 14px;
  font-weight: 700;
  text-align: center;
  color: var(--ink-muted);
}
.tn-check-mark.is-ok { color: var(--ok-fg); }
.tn-check-mark.is-bad { color: var(--danger-fg); }

/* Строка «ключ — значение» внутри концепции. Ключ узкий и постоянной ширины,
   чтобы три карточки читались как таблица, а не как три разных текста. */
.tn-concept-row { display: flex; gap: var(--s-3); align-items: baseline; }
.tn-concept-key {
  flex: 0 0 84px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--ink-muted);
}
/* Текст, который реально окажется на картинке, — показан так, как он там и
   выглядит: крупно и капсом. Это главное, что человек сравнивает между идеями. */
.tn-concept-text {
  font-weight: 800;
  font-size: 15px;
  letter-spacing: .02em;
  text-transform: uppercase;
  color: var(--ink);
}
.tn-var-head { padding: var(--s-5) var(--s-5) var(--s-4); display: flex; flex-direction: column; gap: var(--s-3); }
.tn-var-num {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  border-radius: var(--r-pill);
  padding: 4px 10px;
}
.tn-var-num.is-1,
.tn-var-num.is-2,
.tn-var-num.is-3 {
  color: var(--ink-3);
  background: var(--surface-inset);
  border: 1px solid var(--border);
}
.tn-points { padding: 0 var(--s-5); display: flex; flex-direction: column; }
.tn-point { display: flex; gap: var(--s-3); padding: 12px 0; border-top: 1px solid var(--border); align-items: flex-start; }
.tn-point-n { font-family: var(--font-mono); font-size: 11px; font-weight: 500; color: var(--ink-muted); flex: none; width: 16px; padding-top: 2px; }

/* Подвал карточки — там же, где подтверждение. Подсветка означает
   «сейчас у вас спрашивают про деньги». */
.tn-foot { padding: var(--s-4) var(--s-5) var(--s-5); border-top: 1px solid var(--border); background: var(--surface-2); }
.tn-foot.is-asking { background: #F7F5FF; transition: background .28s; }

/* Подтверждение раскрывается на месте, без перезагрузки: страница
   перерисовывалась целиком, человек терял позицию и не понимал, что вообще
   произошло. Ссылка ?ask= остаётся рабочей (и без JS сервер отдаст открытое
   состояние), но при живом JS переход отменяется и панель разворачивается.
   Через grid-template-rows, а не height/max-height: высоту содержимого знать
   не нужно, и анимируется одно свойство сетки, а не раскладка каждого кадра. */
.tn-ask {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows .28s cubic-bezier(.2, .8, .2, 1);
}
/* Размер задаёт только CSS: JS переключает класс и больше ни во что не лезет.
   Если движок не умеет интерполировать fr, панель раскроется мгновенно —
   это верно, просто без плавности. */
.tn-ask.is-open { grid-template-rows: 1fr; }
/* Без min-height: он обнулил бы минимальный вклад содержимого в размер строки,
   и `1fr` схлопнулся бы в ноль — раскрытия не было бы вовсе. */
.tn-ask-inner { overflow: hidden; }
.tn-ask-trigger[hidden] { display: none; }

@media (prefers-reduced-motion: reduce) {
  .tn-ask { transition: none; }
}

/* Фото стало частью решения о списании, а не отдельным блоком: прокручивать
   вниз, чтобы приложить фото к доработке, больше не нужно. */
.tn-photo-pick {
  display: flex;
  gap: 11px;
  align-items: center;
  padding: 11px 13px;
  border-radius: var(--r);
  border: 1px dashed var(--border-hover);
  background: var(--surface);
  cursor: pointer;
}
.tn-photo-pick:hover { border-color: var(--accent); background: var(--surface-2); }
.tn-photo-pick input[type="file"] { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
.tn-photo-plus {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px; height: 30px;
  border-radius: 9px;
  background: var(--accent-soft);
  color: var(--accent-hover);
  flex: none;
  font-size: 15px;
  font-weight: 700;
}
.tn-photo-has {
  display: flex;
  gap: 11px;
  align-items: center;
  padding: 11px 13px;
  border-radius: var(--r);
  border: 1px solid var(--accent-border);
  background: #F7F5FF;
}
.tn-photo-thumb { width: 44px; height: 32px; border-radius: 7px; flex: none; object-fit: cover; background: var(--surface-inset); }

.tn-meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-4);
  align-items: center;
  padding: var(--s-4) 18px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
}

/* Предупреждение о повторном разборе. Разбор всегда смотрит на обложку в
   YouTube: пока новая не залита, 8 токенов вернут те же претензии. Скрывать
   можно только когда человек отметил, что поставил новую. */
.tn-stale {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-3);
  align-items: flex-start;
  padding: var(--s-4) 18px;
  background: var(--warn-bg);
  border: 1px solid var(--warn-bd);
  border-radius: var(--r-lg);
  color: var(--warn-fg);
}

/* Подтверждение платного действия. Без него человек не понимает, произошло
   ли списание — и это была жалоба номер один. */
.tn-toast {
  position: fixed;
  left: 50%;
  bottom: 26px;
  transform: translateX(-50%);
  z-index: 40;
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 13px 18px;
  border-radius: 14px;
  background: var(--panel);
  color: var(--panel-ink);
  box-shadow: var(--shadow-3);
  max-width: min(560px, calc(100vw - 40px));
  font-size: 13px;
  line-height: 1.5;
}
.tn-toast-close {
  flex: none;
  width: 26px;
  height: 26px;
  margin-left: var(--s-2);
  border: 0;
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--panel-muted);
  font-family: inherit;
  font-size: 15px;
  line-height: 1;
  cursor: pointer;
}
.tn-toast-close:hover { background: var(--panel-border); color: var(--panel-ink); }

.tn-toast-ok {
  width: 22px; height: 22px;
  border-radius: var(--r-pill);
  background: var(--signal);
  color: var(--signal-ink);
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  font-size: 13px;
  font-weight: 800;
}

@media (max-width: 640px) {
  .tn-slot, .tn-slot.is-wide { width: 260px; }
}


/* --- 16. Графики ------------------------------------------------------ */
/* Геометрия приходит с сервера; здесь только цвета и типографика.
   Столбцы «после − до» рисуются одним рядом с подписью справа. */

/* Обвязка спарклайна. Подписи осей ЖИВУТ В HTML, а не в SVG: при
   viewBox="0 0 640 200" и width:100% на карточке 327px коэффициент 0.51,
   то есть font-size="12" внутри SVG превратился бы в 6px. В HTML они не
   масштабируются никогда. Отсюда и размеры ниже — колонка Y ровно 40px,
   этого хватает на пятизначное число моноширинным 11px. */
.chart-frame { display: flex; align-items: stretch; gap: var(--s-2); }
.chart-y {
  flex: 0 0 40px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  text-align: right;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--chart-axis);
  padding: 1px 0;
}
.chart-x {
  display: flex;
  justify-content: space-between;
  padding-left: calc(40px + var(--s-2));
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--chart-axis);
}
.chart-legend {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-4);
  font-size: 12px;
  color: var(--ink-muted);
}
.chart-legend span { display: flex; align-items: center; gap: 6px; }
.chart-swatch { width: 10px; height: 10px; border-radius: 3px; flex: none; }
/* Прозрачная зона наведения на всю высоту графика: поймать курсором
   <circle r="2.5"> почти невозможно, а вертикальная полоса — легко. */
.chart-hit { fill: transparent; }
.chart-hit:hover { fill: var(--surface-inset); fill-opacity: .5; }

.chart { width: 100%; overflow-x: auto; }
.chart svg { display: block; }
.chart-bar        { fill: var(--chart-bar); }
.chart-bar-new    { fill: var(--chart-bar-new); }
.chart-line       { fill: none; stroke: var(--chart-line); stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
.chart-point      { fill: var(--chart-point); }
.chart-band       { fill: var(--chart-band); }
.chart-grid       { stroke: var(--chart-grid); stroke-width: 1; }
.chart-axis       { fill: var(--chart-axis); }
.chart-country    { fill: var(--chart-country); }
.chart-delta      { fill: var(--chart-delta); font-family: var(--font-mono); }
.chart-marker     { stroke: var(--chart-marker); stroke-width: 1.5; stroke-dasharray: 3 3; }
.chart-marker-label { fill: var(--chart-marker); font-weight: 600; }

.stat-label {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

/* Горизонтальный бар на чистом CSS — для рядов, где сервер отдаёт числа */
.bar-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-3);
}
.bar-row-label { flex: 0 0 96px; font-size: 13px; color: var(--ink-2); text-align: right; }
.bar-row-track {
  position: relative;
  flex: 1 1 160px;
  min-width: 120px;
  height: 26px;
  border-radius: 6px;
  background: var(--chart-track);
  overflow: hidden;
}
.bar-row-fill {
  height: 100%;
  border-radius: 6px;
  background: var(--chart-bar);
  transform-origin: left;
  animation: rk-grow .6s cubic-bezier(.2, .8, .2, 1);
}
/* Один цвет на все полосы: полоса измеряет величину роста, а «новый рынок» —
   это статус, и он передаётся словом внутри полосы, а не вторым оттенком. */

/* Подпись внутри полосы. Текст набран двумя одинаковыми слоями: нижний
   тёмный, верхний белый и обрезан по ширине заливки. Каждая буква попадает
   в свой контраст, поэтому фраза читается целиком даже когда полоса короче
   надписи — и подпись никогда не уезжает на вторую строку. */
.bar-row-inline,
.bar-row-inline-over {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  padding-left: 10px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .01em;
  white-space: nowrap;
  pointer-events: none;
}
.bar-row-inline { color: var(--ink-2); }
.bar-row-inline-over { color: #FFFFFF; }

/* Обёртка верхнего слоя: её width равна width заливки — задаётся инлайном
   из того же процента, что и .bar-row-fill. */
.bar-row-clip {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  overflow: hidden;
  pointer-events: none;
  transform-origin: left;
  animation: rk-grow .6s cubic-bezier(.2, .8, .2, 1);
}

/* Блик по заливке новых рынков. Два элемента, и это принципиально: обёртка
   только обрезает (её width равна ширине заливки), а ездит вложенный слой.
   Если анимировать саму обёртку, overflow ей не поможет — он режет детей, а
   градиент лежит в её собственном фоне, поэтому блик уезжал бы за полосу и
   мерцал по пустой части дорожки, как скелетон загрузки.
   Двигается только transform — слой композитится, перерисовки нет. Глушится
   общим правилом prefers-reduced-motion в конце файла. */
.bar-row-sheen {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  overflow: hidden;
  pointer-events: none;
}
.bar-row-sheen::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .4) 45%, transparent);
  animation: rk-sheen 3.4s cubic-bezier(.4, 0, .2, 1) infinite;
}
@keyframes rk-sheen {
  0%   { transform: translateX(-100%); }
  60%, 100% { transform: translateX(320%); }
}

.bar-row-delta {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: baseline;
  gap: 4px;
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 600;
  color: var(--ok-fg);
}
/* Точка отсчёта нового рынка: ноль приглушён, чтобы читался как «было
   столько», а не как часть суммы роста. */
.bar-row-from { color: var(--ink-muted); font-weight: 400; }
.bar-row-arrow { color: var(--border-hover); font-weight: 400; }


/* --- 17. Пустые состояния --------------------------------------------- */
/* Человек видит их первыми: они объясняют, что появится и когда. */

.empty {
  display: flex;
  flex-direction: column;
  gap: var(--s-5);
  padding: var(--s-8) var(--s-6);
}
.empty-title { font-size: 20px; font-weight: 700; letter-spacing: -.025em; }
.empty p { max-width: 60ch; }
.empty-dashed {
  background: var(--surface);
  border: 1px dashed var(--border-hover);
  border-radius: var(--r-lg);
}
.empty-preview { display: flex; flex-wrap: wrap; gap: var(--s-6); opacity: .4; pointer-events: none; }

.steps { display: flex; flex-direction: column; border-top: 1px solid var(--border); }
.step {
  display: flex;
  gap: var(--s-4);
  padding: var(--s-4) 0;
  align-items: flex-start;
  border-bottom: 1px solid var(--border);
}
.step-num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px; height: 26px;
  border-radius: var(--r-sm);
  background: var(--panel);
  color: var(--signal);
  font-size: 12px;
  font-weight: 800;
  flex: none;
}
.step-body { display: flex; flex-direction: column; gap: 3px; }
.step-title { font-size: 14px; font-weight: 600; }

/* --- 17b. Выбор языков ------------------------------------------------- */
/* Заменяет поле «коды через запятую». Человек не обязан помнить, что бразильский
   португальский — это pt-BR: он выбирает языки по названию, коды уходят в тот же
   force_relang скрытым полем. Нативный <dialog> — фокус, Esc и подложка
   бесплатно, без библиотек. */

.modal {
  width: min(720px, calc(100vw - 32px));
  max-height: min(86vh, 760px);
  padding: 0;
  border: 1px solid var(--border);
  border-radius: var(--r-xl);
  background: var(--surface);
  color: var(--ink);
  box-shadow: var(--shadow-3);
  overflow: hidden;
}
.modal::backdrop { background: rgba(15, 16, 18, .48); backdrop-filter: blur(2px); }
.modal-form { display: flex; flex-direction: column; max-height: inherit; }

.modal-head {
  display: flex;
  align-items: flex-start;
  gap: var(--s-4);
  padding: var(--s-5) var(--s-5) var(--s-4);
  border-bottom: 1px solid var(--border);
}
.modal-title { font-size: 19px; font-weight: 700; letter-spacing: -.025em; }
.modal-close {
  flex: none;
  width: 32px;
  height: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--border-strong);
  border-radius: var(--r-sm);
  background: var(--surface);
  color: var(--ink-3);
  font-size: 15px;
  line-height: 1;
  cursor: pointer;
  font-family: inherit;
}
.modal-close:hover { border-color: var(--border-hover); color: var(--ink); }

.modal-body {
  flex: 1 1 auto;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: var(--s-4) var(--s-5) var(--s-5);
  display: flex;
  flex-direction: column;
  gap: var(--s-4);
}
.modal-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-4) var(--s-5);
  border-top: 1px solid var(--border);
  background: var(--surface-2);
}

/* Зона выбранных: всегда сверху, чтобы решение было видно целиком без
   прокрутки, и с рамкой — она отделяет «что произойдёт» от «что доступно». */
.chip-zone {
  display: flex;
  flex-wrap: wrap;
  gap: 7px;
  align-content: flex-start;
  min-height: 78px;
  padding: 12px;
  border: 1px dashed var(--border-hover);
  border-radius: var(--r-lg);
  background: var(--surface-2);
}
.chip-zone.is-filled { border-style: solid; border-color: var(--accent-border); background: var(--accent-soft); }
.chip-zone-empty { align-self: center; font-size: 13px; color: var(--ink-muted); }

.chip {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-family: inherit;
  font-size: 13px;
  font-weight: 600;
  line-height: 1.2;
  padding: 8px 12px;
  border: 1px solid var(--border-strong);
  border-radius: var(--r-pill);
  background: var(--surface);
  color: var(--ink-2);
  cursor: pointer;
  white-space: nowrap;
  transition: border-color .12s, background .12s, color .12s;
}
.chip:hover { border-color: var(--border-hover); color: var(--ink); }
.chip-code { font-family: var(--font-mono); font-size: 11px; font-weight: 400; color: var(--ink-muted); }

/* Выбранный чип: акцентная заливка и крестик. Клик по всему чипу тоже снимает
   выбор — крестик здесь подсказка, а не единственная цель. */
.chip.is-selected {
  border-color: transparent;
  background: var(--accent);
  color: var(--accent-ink);
  padding-right: 8px;
}
.chip.is-selected:hover { background: var(--accent-hover); color: var(--accent-ink); }
.chip.is-selected .chip-code { color: rgba(255, 255, 255, .88); }
/* Крестик — только у выбранного: удалять можно лишь то, что уже выбрано.
   В общем списке чип добавляют, а не удаляют, и крестик там читался бы как
   «убрать язык из списка вообще». */
.chip-x { display: none; }
.chip.is-selected .chip-x {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  border-radius: var(--r-pill);
  background: rgba(255, 255, 255, .2);
  font-size: 12px;
  line-height: 1;
}
.chip.is-selected:hover .chip-x { background: rgba(255, 255, 255, .32); }

/* Уже опубликованный язык: выбрать можно, но видно, что это перевод заново. */
.chip-done .chip-name::after {
  content: "·";
  margin: 0 5px;
  color: var(--border-hover);
}
.chip-done .chip-note { font-size: 11px; font-weight: 400; color: var(--ok-fg); }
.chip.is-selected .chip-note { color: #FFFFFF; }

/* В тёмной теме --accent светлее, поэтому заливку выбранного чипа берём на
   тон глубже — белый текст на ней уходит с границы AA. */
:root[data-theme="dark"] .chip.is-selected { background: #5B2BFF; }
:root[data-theme="dark"] .chip.is-selected:hover { background: #6B3FFF; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .chip.is-selected { background: #5B2BFF; }
  :root:not([data-theme="light"]) .chip.is-selected:hover { background: #6B3FFF; }
}

.chip-hidden { display: none; }
/* Режим «недостающие» прячет уже переведённые языки совсем: в этом сценарии
   выбирают из того, чего ещё нет, и опубликованный язык здесь — шум.
   Отдельный класс от .chip-hidden, чтобы поиск не показал скрытое режимом. */
.chip-off { display: none; }

/* --- 17c. Страница 404 -------------------------------------------------- */
/* Идея страницы: сервис переводит одну мысль на много языков — здесь он делает
   то же самое с «страница не найдена». Цифры 404 при этом не меняются: они
   одинаковы на любом языке, и это единственная шутка, которую страница себе
   позволяет. Смена языка — не украшение: она показывает продукт в работе
   ровно там, где человеку всё равно нечего делать. */

.nf { display: flex; align-items: center; min-height: calc(100vh - var(--header-h) - var(--s-16)); }
.nf-body {
  display: flex;
  flex-direction: column;
  gap: var(--s-5);
  max-width: 620px;
}

.nf-code {
  position: relative;
  align-self: flex-start;
  font-family: var(--font);
  font-size: clamp(84px, 17vw, 150px);
  font-weight: 800;
  letter-spacing: -.05em;
  line-height: .9;
  color: var(--ink);
  padding: 0 .06em .04em;
}
/* Лаймовая черта под цифрами — отсылка к плашке логотипа, но без наложения
   на текст: в логотипе на лайме всегда лежат чернила, а здесь цвет цифр
   задаёт тема. Слой лежит ниже базовой линии, поэтому обе темы читаются. */
.nf-code::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: .1em;
  border-radius: 3px;
  background: var(--signal);
}

/* Строка живой смены. Высота фиксирована, иначе текст под ней дёргается на
   каждой фразе — арабский и японский заметно разной высоты. */
.nf-live {
  display: flex;
  align-items: baseline;
  gap: var(--s-3);
  min-height: 34px;
}
.nf-lang {
  flex: none;
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 500;
  padding: 4px 8px;
  border-radius: 7px;
  background: var(--accent-soft);
  color: var(--accent-hover);
  transition: opacity .2s;
}
.nf-phrase {
  font-size: 21px;
  font-weight: 700;
  letter-spacing: -.02em;
  line-height: 1.3;
  color: var(--ink-2);
  transition: opacity .2s;
}

.nf-title { font-size: 26px; font-weight: 800; letter-spacing: -.03em; line-height: 1.2; }
.nf-lead { font-size: 15px; line-height: 1.6; color: var(--ink-3); max-width: 52ch; }
.nf-foot { padding-top: var(--s-2); }

@media (max-width: 560px) {
  .nf { min-height: 0; padding-top: var(--s-8); }
  .nf-title { font-size: 21px; }
  .nf-phrase { font-size: 18px; }
}

/* --- 18. Утилиты ------------------------------------------------------ */

.hidden, [hidden] { display: none !important; }
.nowrap { white-space: nowrap; }
.grow { flex: 1; }
.self-start { align-self: flex-start; }
.max-60 { max-width: 60ch; }

@media (max-width: 640px) {
  h1 { font-size: 24px; }
  .identity-title { font-size: 19px; }
  .proof-num { font-size: 40px; }
  .app-nav a { padding: 7px 8px; }
  /* Флаг текущего языка в закрытой кнопке-переключателе — на узких экранах
     шапка и так плотная (Войти/Начать + переключатель + тема); код языка
     (RU/EN/...) сам по себе достаточно узнаваем. Флаги в открытом списке
     языков остаются — там места хватает, и они помогают искать свой язык
     среди остальных. */
  .lang-toggle .lang-flag { display: none; }
  /* Гамбургер вместо разложенных по шапке действий — см. .mobile-menu
     выше: та же логика, что уже применялась к флагу, только для целых
     кнопок (тема, вход/регистрация, аккаунт), которые вместе с языком не
     помещаются в одну строку на телефоне. */
  .nav-desktop-only { display: none !important; }
  .mobile-menu { display: block; }
}
