/* ============================================================================
   EFFISCORE — TV BOARD
   Сцена: телевизор в цеху, взгляд с 3–5 метров, 10–20 секунд на паузе.
   Человек читает ДВЕ вещи: своё имя и своё место. Всё остальное — опора.

   Главное отличие от прежней версии: размер шрифта считается ОТ ВЫСОТЫ СТРОКИ
   (--rh), а не от vh отдельно от неё. Раньше строки росли вместе с экраном, а
   шрифт упирался в потолок clamp(), и на 4K рейтинг превращался в стопку
   цветных плит с мелкими подписями. Теперь пропорция «строка : имя : балл»
   одна и та же на 1440×900 и на 3840×2160.
   ============================================================================ */

.tvx {
  flex: 1; min-height: 0; display: flex; flex-direction: column;
  padding: var(--tv-pad); gap: var(--tv-pad);
  /* Нижня межа була 10px і починала діяти при висоті вікна нижче 909px —
     тобто рамка переставала стискатись рівно там, де місця найменше. У форматі
     «Чотири ліги» цей відступ входить у вертикаль П'ЯТЬ разів (рамка згори й
     знизу, просвіт до шапки й до підвалу, плюс просвіт між двома рядами зон),
     тому 1080p при зумі 150 % (вікно 720px) віддавав рамці 40px замість 32 і
     давав заповнення 84 % при нормі 85–95. Вище 909px нічого не змінюється:
     там межа й так не діяла (11.08.2026, BUG-030). */
  --tv-pad: clamp(8px, 1.1vh, 26px);
  --rg: clamp(3px, 0.42vh, 10px);           /* просвет между строками */
}

/* ---------------------------------------------------------------- шапка --- */
/* Шапка не имеет права красть площадь у рейтинга: всё, что можно нажать,
   живёт в Manager. На стене нажимать некому — здесь только состояние. */
.tvx-head {
  flex: none; display: flex; align-items: center;
  gap: clamp(12px, 1.6vw, 40px);
  padding: 0 clamp(4px, 0.5vw, 14px);
  height: clamp(34px, 4.4vh, 96px);
}
.tvx-brand {
  font-size: clamp(17px, 2.5vh, 54px); font-weight: 800;
  letter-spacing: 0.02em; line-height: 1; white-space: nowrap;
}
.tvx-brand b { font-weight: 800; color: var(--c-pick); }
.tvx-brand i {
  font-style: normal; font-weight: 600; color: var(--c-text-3);
  font-size: 0.42em; letter-spacing: var(--ls-caps); text-transform: uppercase;
  margin-left: 0.6em; vertical-align: 0.35em;
}
.tvx-meta {
  margin-left: auto; display: flex; align-items: center;
  gap: clamp(10px, 1.3vw, 32px);
  font-size: clamp(11px, 1.5vh, 30px); color: var(--c-text-2);
  white-space: nowrap; font-variant-numeric: tabular-nums;
}
.tvx-wh {
  font-weight: 800; letter-spacing: 0.04em; color: var(--c-text);
  padding: 0.18em 0.7em; border-radius: var(--r-pill);
  background: var(--c-surface-2); box-shadow: inset 0 0 0 1px var(--c-line);
}
.tvx-live {
  display: inline-flex; align-items: center; gap: 0.5em; font-weight: 700;
  letter-spacing: var(--ls-caps);
}
.tvx-live .d { width: 0.55em; height: 0.55em; border-radius: 50%; background: currentColor; }
.tvx-live.LIVE { color: var(--c-up); }
.tvx-live.LIVE .d { animation: tvx-pulse 2s var(--ease-in-out) infinite; }
.tvx-live.FINAL { color: var(--c-info); }
.tvx-live.STALE { color: var(--c-warn); }
.tvx-live.ERROR, .tvx-live.NO_DATA { color: var(--c-down); }
.tvx-live.PROCESSING { color: var(--processing); }
@keyframes tvx-pulse { 0%, 100% { opacity: 1 } 50% { opacity: 0.3 } }

/* ------------------------------------------------------------ сетка зон --- */
/* minmax(0, 1fr), а не 1fr. `1fr` это `minmax(auto, 1fr)`, и столбец имеет
   право раздуться под содержимое: при одном человеке в лиге строка становится
   высотой 167 px, её элементы фиксированного размера перестают влезать в
   половину экрана, и ПРАВЫЙ столбец уезжает за край монитора целиком. */
.tvx-grid {
  flex: 1; min-height: 0;
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--tv-pad);
  /* Один grid-шаблон для `.row` і `.zone-colhead` (05.08.2026) — тому підпис
     стовпця і число під ним завжди рівно одне під одним, без JS-розрахунку
     зсуву. Порядок: місце · аватар · ім'я(гнучке) · рух · Score · основний
     обсяг · кількість · вага. Перші чотири — фіксовані/гнучкі, останні
     чотири — фіксована частка --rh, тому цифри не гуляють шириною від рядка
     до рядка.
     ВАЖЛИВО: оголошено САМЕ тут, а не на `.tvx` — --rh ставиться інлайном
     через JS на #tvx-grid, і custom property, що посилається на var(--rh) на
     ПРЕДКОВІ (де --rh ще не існує), обчислюється як invalid ОДИН РАЗ і саме
     це (порожнє) значення успадковують усі нащадки — навіть ті, де --rh уже
     визначено. Так --row-gt мовчки ставав порожнім, і рядок розвалювався на
     звичайний потік (ім'я розтягувалось на всю ширину, ранг тікав управо). */
  /* 05.08.2026 (2): три опорні цифри (обсяг/кількість/вага) розширені під
     крупніший шрифт — власник прямо попросив зробити їх виразнішими поруч
     зі Score, а не лишати «другим голосом». pr/it 1.05→1.15, wt 1.35→1.55. */
  --row-gt: 2.6ch calc(var(--rh) * 0.62) minmax(0, 1fr) calc(var(--rh) * 0.95)
            calc(var(--rh) * 1.55) calc(var(--rh) * 1.15) calc(var(--rh) * 1.15) calc(var(--rh) * 1.55);
}
.tvx-col { display: flex; flex-direction: column; gap: var(--tv-pad); min-height: 0; }

/* Зона занимает долю колонки по числу людей, а не половину поровну.
   Причина в данных: за 30 дней MULTI это 1–5 человек, а STAPLER — 6–19.
   Равные квадранты означали бы четверть экрана пустоты под MULTI и
   выдавленный хвост STAPLER одновременно. flex-grow ставит js. */
.zone {
  display: flex; flex-direction: column; min-height: 0; min-width: 0;
  background: var(--c-surface-1); border-radius: var(--r-3);
  padding: var(--s-1); overflow: hidden;
  --accent: var(--c-pick);
}
.zone.pack { --accent: var(--c-pack); }

.zone-h {
  flex: none; display: flex; align-items: baseline;
  gap: clamp(6px, 0.8vw, 18px);
  padding: calc(var(--rh) * 0.16) calc(var(--rh) * 0.26) calc(var(--rh) * 0.06);
}

/* Заголовки чотирьох числових стовпців — ОДИН раз на зону, рівно над
   значеннями (05.08.2026). grid-template-columns тут — АБСОЛЮТНО той самий
   рядок, що й у `.row` нижче: звідси й гарантія, що підпис і число завжди
   стоять одне під одним, без окремого рахунку зсуву в JS. Перші чотири
   позиції шаблону (rank/avatar/name/move) лишаються порожніми — підписи
   виставлені в конкретні grid-column, а не по порядку дітей. */
.zone-colhead {
  flex: none; display: grid;
  grid-template-columns: var(--row-gt);
  column-gap: calc(var(--rh) * 0.20);
  padding: 0 calc(var(--rh) * 0.24) calc(var(--rh) * 0.10);
  /* 05.08.2026 (2): підпис виразніше — власник попросив, щоб «ЛОК/ЗАМ,
     К-ТЬ, ВАГА» не губились поруч зі SCORE. Було 0.16·rh кольору c-text-3
     (найтьмяніший рівень) — тепер 0.20·rh і c-text-2 (той самий рівень, що
     й «Темп · Внесок» під іменем, але більший розміром). */
  font-size: max(12px, calc(var(--rh) * 0.20));
  font-weight: 800; letter-spacing: var(--ls-caps); text-transform: uppercase;
  color: var(--c-text-2);
}
.zone-colhead span { text-align: right; line-height: 1; white-space: nowrap; overflow: hidden; }
.zone-colhead .ch-sc { grid-column: 5; }
.zone-colhead .ch-pr { grid-column: 6; }
.zone-colhead .ch-it { grid-column: 7; }
.zone-colhead .ch-wt { grid-column: 8; }
/* SINGLE: одна посилка = одне замовлення, «Кількість» один-в-один дублює
   «Замовлень» (05.08.2026, прохання власника). Ховаємо дублікат-колонку і
   розтягуємо замовлення на звільнене місце — тільки для цієї ліги, MULTI й
   пікерські ліги лишаються при чотирьох різних числах. */
.zone[data-zone="single"] .ch-it, .zone[data-zone="single"] .row .n-it { display: none; }
.zone[data-zone="single"] .ch-pr, .zone[data-zone="single"] .row .n-pr { grid-column: 6 / 8; }
/* Підписи стовпців лишаються навіть на найдрібнішому рядку (05.08.2026) —
   саме вони прибирають здогадки «що це за цифра», а це головна скарга, яку
   виправляє ФАЗА 8. Жертвуємо декором (аватар/підрядок/бейджі), не цифрами. */
.zone-t {
  font-size: calc(var(--rh) * 0.36); font-weight: 800;
  letter-spacing: var(--ls-caps); color: var(--accent); line-height: 1;
}
.zone-n { font-size: max(12px, calc(var(--rh) * 0.27)); font-weight: 700; color: var(--c-text-3); line-height: 1; }
/* «4–9 / 17» — появляется только когда зона листается. Без него человек в
   середине списка решит, что его вообще нет на экране.
   05.08.2026: `.zone-u` (повне слово одиниці виміру в кутку шапки) прибрано
   — тепер цю роль виконує `.zone-colhead` точно над потрібною колонкою.
   Толкати вправо тепер має сам `.zone-pg`, тому маржа переїхала сюди. */
.zone-pg {
  margin-left: auto; font-size: max(12px, calc(var(--rh) * 0.25)); font-weight: 700;
  color: var(--c-text-3); letter-spacing: 0.04em; font-variant-numeric: tabular-nums;
  line-height: 1;
}
/* justify-content: center нужен только в вырожденном случае — когда зона
   рассчитана на две строки, а человек в лиге один. При обычном заполнении
   высота подогнана точно, и центрирование ничего не меняет. */
.zone-b {
  flex: 1; min-height: 0; display: flex; flex-direction: column;
  /* ФАЗА 9 (05.08.2026): було `center` — коли в лізі людей менше, ніж блок
     розрахований вмістити (бо сусідня ліга з тим самим "поверхом" потребує
     більше), рядки збивались в один шматок посередині блоку замість того,
     щоб зайняти всю виділену висоту. `space-between` розтягує проміжки МІЖ
     рядками на всю висоту (перший рядок — біля верху блоку, останній — біля
     низу), а коли рядків рівно стільки, скільки блок і так розрахований
     показати, зайвого місця нема і поведінка не відрізняється від старої. */
  justify-content: space-between; gap: var(--rg); overflow: hidden;
}
/* BUG-031, друга половина (11.08.2026). Рівномірний розтяг має межу: коли
   вільного місця стільки, що просвіт між рядками стає більшим за сам рядок,
   ліга перестає читатись як один список і розсипається на окремі смуги.
   Перша версія фіксу «Двох ліг» саме це й зробила — заповнення 72 % → 83 %,
   а вигляд гірший. Тому JS (`fitDuo`, board.js) міряє майбутній просвіт і при
   перевищенні ~45 % висоти рядка ставить цей клас: рядки притискаються вгору,
   низ панелі лишається порожнім. Панель при цьому все одно на всю висоту
   стовпця, тож голої підкладки екрана, як було до фіксу, не видно. */
.zone-b.packed { justify-content: flex-start; }
.zone-empty {
  flex: 1; display: grid; place-items: center; color: var(--c-text-3);
  font-size: calc(var(--rh) * 0.30); text-align: center; padding: var(--s-4);
}

/* --------------------------------------------------------------- строка --- */
.row {
  position: relative; flex: none; height: var(--rh);
  display: grid; grid-template-columns: var(--row-gt);
  align-items: center; column-gap: calc(var(--rh) * 0.20);
  padding: 0 calc(var(--rh) * 0.24);
  border-radius: var(--r-2); background: var(--c-surface-2);
  isolation: isolate; overflow: hidden;
  transition: transform var(--t-rank) var(--ease-out),
              opacity var(--t-base) var(--ease-out);
}
/* Заливка = доля от лидера по ТОЙ ЖЕ метрике, что определяет место.
   transform, а не width: ширина дёргает раскладку на каждом обновлении. */
/* Дорожка заливки заканчивается ДО блоку цифр — тепер це ЧОТИРИ колонки
   (Score/обсяг/кількість/вага), а не дві, тому й num-zone ширший, ніж був. */
.row { --num-zone: calc(var(--rh) * 6.3); }
.row .bar {
  position: absolute; inset: 0 var(--num-zone) 0 0; z-index: -1; transform-origin: left center;
  background: color-mix(in oklab, var(--accent) 13%, transparent);
  border-right: 2px solid color-mix(in oklab, var(--accent) 55%, transparent);
  transition: transform var(--t-slow) var(--ease-out);
}
/* ФАЗА 8 (05.08.2026): топ-3 навмисно виразніше, ніж каже загальне правило
   «без дешевого ігрового вигляду» в цьому файлі вище. Власник попросив
   переступити його саме тут: змагальність трьох перших місць мотивує людей
   на підлозі, це перевірено на практиці, а не здогад дизайну. Раніше тройку
   відзначали лише кольором рейки й цифри — на відстані 3–5 метрів це
   губилось серед двадцяти інших рядків. Тепер — насиченіший фон, товща
   рейка з м'яким світінням і медальйон замість голого числа місця. */
.row .rail {
  position: absolute; left: 0; top: 0; bottom: 0; width: calc(var(--rh) * 0.09);
  min-width: 3px; border-radius: var(--r-2) 0 0 var(--r-2); background: transparent;
}
.row.p1 .rail { background: var(--c-gold); width: calc(var(--rh) * 0.18); }
.row.p2 .rail { background: var(--c-silver); width: calc(var(--rh) * 0.16); }
.row.p3 .rail { background: var(--c-bronze); width: calc(var(--rh) * 0.16); }
.row.p1 {
  background: color-mix(in oklab, var(--c-gold) 22%, var(--c-surface-2));
  box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--c-gold) 45%, transparent),
              0 0 calc(var(--rh) * 0.28) color-mix(in oklab, var(--c-gold) 25%, transparent);
}
.row.p2 {
  background: color-mix(in oklab, var(--c-silver) 16%, var(--c-surface-2));
  box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--c-silver) 38%, transparent);
}
.row.p3 {
  background: color-mix(in oklab, var(--c-bronze) 16%, var(--c-surface-2));
  box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--c-bronze) 38%, transparent);
}

/* Явний grid-column на КОЖНІЙ клітинці рядка (05.08.2026) — критично, коли
   поруч є `display:none` (аватар/числа ховаються на дрібних рядках, дивись
   data-size нижче). Без явного номера сітка розставляє видимі елементи
   ПІДРЯД у перші вільні колонки: сховали аватар — ім'я з'їжджає в колонку
   аватара (22px замість 370), «рух» — у колонку імені, Score — у колонку
   «руху». Рядок розвалювався саме так, коли рядок ставав компактним. */
.row .rk { grid-column: 1; }
.row .av { grid-column: 2; }
.row .who { grid-column: 3; }
.row .mv { grid-column: 4; }
.row .sc { grid-column: 5; }
.row .n-pr { grid-column: 6; }
.row .n-it { grid-column: 7; }
.row .n-wt { grid-column: 8; }

.row .rk {
  flex: none; min-width: 2.1ch; text-align: right; line-height: 1;
  font-size: calc(var(--rh) * 0.34); font-weight: 800;
  color: var(--c-text-3); font-variant-numeric: tabular-nums;
}
/* Медальйон: заповнене коло кольору металу з темною цифрою всередині — той
   самий прийом, що й у аватара, тож він не виглядає чужорідним. Розмір в
   `em` (від власного font-size .rk), а не в rh: так він гарантовано влазить
   у виділену під ранг колонку (2.6ch) на будь-якій висоті рядка. */
.row.p1 .rk, .row.p2 .rk, .row.p3 .rk {
  display: grid; place-items: center; margin-left: auto;
  width: 1.5em; height: 1.5em; border-radius: 50%;
  color: oklch(0.18 0.02 258); text-shadow: none;
}
.row.p1 .rk { background: linear-gradient(150deg, oklch(0.90 0.15 97), oklch(0.74 0.16 62)); }
.row.p2 .rk { background: linear-gradient(150deg, oklch(0.90 0.01 258), oklch(0.74 0.02 258)); }
.row.p3 .rk { background: linear-gradient(150deg, oklch(0.78 0.09 55), oklch(0.60 0.10 42)); }
.row .rk.none { font-size: calc(var(--rh) * 0.26); color: var(--c-text-3); opacity: .55; }
/* Чемпіон дня: бал трохи більший, ніж у решти — саме він відповідає на
   питання «хто сьогодні перший» першим поглядом. */
.row.p1 .sc { font-size: calc(var(--rh) * 0.56); }

.row .av {
  flex: none; width: calc(var(--rh) * 0.62); height: calc(var(--rh) * 0.62);
  border-radius: 50%; display: grid; place-items: center;
  font-size: calc(var(--rh) * 0.26); font-weight: 800; color: oklch(0.18 0.02 258);
  outline: 1px solid oklch(1 0 0 / .10); outline-offset: -1px;
}
.row .who { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; justify-content: center; gap: calc(var(--rh) * 0.04); }
/* Метки живут ВПЛОТНУЮ к имени. Пока они лежали отдельным элементом строки,
   flex отбрасывал их к правому краю свободного места, и «B15» повисало
   посреди пустоты — читалось как ещё одна цифра результата, а не как
   уточнение к человеку. */
.row .nml { display: flex; align-items: center; min-width: 0; gap: calc(var(--rh) * 0.14); }
.row .nm {
  font-size: calc(var(--rh) * 0.38); font-weight: 700; line-height: 1.05;
  letter-spacing: var(--ls-head); white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
}
.row.p1 .nm { font-weight: 800; }
.row .sub {
  font-size: calc(var(--rh) * 0.24); color: var(--c-text-2); line-height: 1;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  font-variant-numeric: tabular-nums;
}
.zone[data-size="s"] .sub { display: none; }
.zone[data-size="s"] .av { display: none; }
.zone[data-size="s"] .nm { font-size: calc(var(--rh) * 0.42); }

/* Балл — самая крупная цифра строки: именно он решает место. */
.row .sc {
  text-align: right; line-height: 1;
  font-size: calc(var(--rh) * 0.50); font-weight: 800;
  color: var(--accent); font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
}
.row .sc.dash { color: var(--c-text-3); font-weight: 700; }
/* Обсяг/кількість/вага — три опорні цифри, другий голос після Score, але
   не пошепки. 05.08.2026 (2): власник попросив зробити їх виразнішими —
   було 0.28·rh / вага 600 / колір c-text-2 (той самий, що й дрібні підписи);
   стало 0.38·rh / вага 700 / яскравість на рівні імені — так вони читаються
   нарівні з іменем, не тільки зі Score.
   05.08.2026 (3): три різні кольори (--c-num-pr/it/wt, tokens.css) — щоб
   колонки не зливались в одну сіру масу цифр і легше знаходились очима
   під своїм заголовком, навіть коли підпис зверху не в фокусі погляду. */
.row .n-pr, .row .n-it, .row .n-wt {
  text-align: right; line-height: 1;
  font-size: max(13px, calc(var(--rh) * 0.38)); font-weight: 700;
  font-variant-numeric: tabular-nums; overflow: hidden; text-overflow: clip;
}
.row .n-pr { color: var(--c-num-pr); }
.row .n-it { color: var(--c-num-it); }
.row .n-wt { color: var(--c-num-wt); }
.zone-colhead .ch-pr { color: var(--c-num-pr); }
.zone-colhead .ch-it { color: var(--c-num-it); }
.zone-colhead .ch-wt { color: var(--c-num-wt); }
.zone[data-size="m"] .sub, .zone[data-size="s"] .sub { display: none; }

/* Движение: стрелка + число + подпись для скринридера. Не только цвет —
   красный и зелёный неразличимы примерно у каждого двенадцатого мужчины,
   а в цеху их большинство. */
.row .mv {
  flex: none; display: inline-flex; align-items: center; gap: 0.15em;
  min-width: calc(var(--rh) * 0.95); justify-content: flex-end;
  /* Мінімум 12px (BUG-019): при дрібному рядку стрілка ▲▼ падала до 10,6px,
     а це єдиний носій інформації «людина обігнала сусіда» — на стіні цеху з
     п'яти метрів такий розмір не існує. Прозорий «·» у тому ж елементі
     розміру не потребує, але й не заважає: він тільки тримає місце в сітці. */
  font-size: max(12px, calc(var(--rh) * 0.28)); font-weight: 800; line-height: 1;
  font-variant-numeric: tabular-nums;
}
.row .mv.up { color: var(--c-up); }
.row .mv.down { color: var(--c-down); }
.row .mv.flat { color: transparent; }
.row .mv .ar { font-size: 0.85em; }

/* Метки контекста. Порог читаемости — 12px: то, что мельче, с пяти метров
   не существует, поэтому на маленькой строке метки просто не рисуются. */
.row .mks { flex: none; display: inline-flex; gap: calc(var(--rh) * 0.08); }
.row .mk {
  font-size: max(12px, calc(var(--rh) * 0.22)); font-weight: 800; line-height: 1.5;
  padding: 0 0.5em; border-radius: var(--r-pill);
  box-shadow: inset 0 0 0 1px currentColor;
}
.row .mk.f { color: var(--c-gold); }
.row .mk.mix { color: var(--c-info); }
.row .mk.rp { color: var(--c-text-3); }
/* Універсал і АМР (05.08.2026) — ті самі кольори, що й у Manager
   (`.tag.dual`, `.league-pill.robot` у style.css), щоб «⇄» і «АМР» читались
   однаково на обох екранах, а не як два різних значки. */
.row .mk.dual { color: #38bdf8; }
.row .mk.amr { color: #a78bfa; }
.zone[data-size="s"] .mks, .zone[data-size="m"] .mks { display: none; }

/* Поднявшийся подсвечивается на пару секунд — иначе стрелка ▲ теряется
   среди двадцати строк, и подъём замечает только тот, кто смотрел в упор. */
.row.moved-up { animation: tvx-lift 2.4s var(--ease-out) 1; }
@keyframes tvx-lift {
  0%   { box-shadow: inset 0 0 0 0 transparent; }
  12%  { box-shadow: inset 0 0 0 2px color-mix(in oklab, var(--c-up) 70%, transparent); }
  100% { box-shadow: inset 0 0 0 0 transparent; }
}

/* Смена страницы внутри зоны: короткий сдвиг вверх + проявление.
   Только transform и opacity — оба идут мимо layout и paint. */
/* Граница между закреплённой тройкой и листающимся хвостом. Без неё подпись
   «4–16 / 18» противоречит экрану: человек видит строки с первой, а написано
   «с четвёртой». Черта показывает, где кончается неподвижная часть. */
/* Черта рисуется В СУЩЕСТВУЮЩЕМ просвете между строками (overflow: visible на
   строке для этого не нужен — псевдоэлемент внутри её высоты). Отдельный
   margin-top добавил бы зоне высоты, а она посчитана до пикселя, и лишние
   пять пикселей срезали бы последнюю строку. */
.zone.paging .row.tail-first::before {
  content: ""; position: absolute; inset: 0 0 auto 0; z-index: 1;
  border-top: 1px solid var(--c-text-3);
}
.row.pg-in { animation: tvx-pgin var(--t-base) var(--ease-out) backwards; }
@keyframes tvx-pgin { from { opacity: 0; transform: translateY(10%); } }

/* ------------------------------------------------- инструменты по наведению */
/* Функции не удалены — они спрятаны от стены. На телевизоре мышь не двигается,
   значит полоса не появится никогда; за ноутбуком она возвращается от первого
   движения. Пустое место читалось бы как «функцию убрали», а постоянная панель
   кнопок отнимала бы у рейтинга полосу в 40 px на каждом экране. */
.tvx-tools {
  display: flex; align-items: center; gap: var(--s-2);
  opacity: 0; pointer-events: none;
  transition: opacity var(--t-base) var(--ease-out);
}
.tvx.has-pointer .tvx-tools,
.tvx-tools:focus-within { opacity: 1; pointer-events: auto; }
.tvx-ctl {
  background: var(--c-surface-2); color: var(--c-text-2);
  border: var(--b-1); border-radius: var(--r-2);
  padding: 6px 10px; font: inherit; font-size: var(--fs-13); font-weight: 600;
  min-height: 32px; cursor: pointer; color-scheme: dark;
  transition: color var(--t-fast) var(--ease-out), border-color var(--t-fast) var(--ease-out);
}
.tvx-ctl:hover { color: var(--c-text); border-color: var(--c-pick); }
.tvx-ctl:active { transform: scale(0.96); }
.tvx-ctl[hidden] { display: none; }
.tvx-ctl.seg { display: inline-flex; gap: 0; padding: 0; overflow: hidden; }
.tvx-ctl.seg:empty { display: none; }
.tvx-ctl.seg button {
  background: transparent; border: 0; border-right: var(--b-hair);
  color: var(--c-text-2); font: inherit; font-size: var(--fs-13); font-weight: 700;
  padding: 6px 11px; min-height: 32px; cursor: pointer;
}
.tvx-ctl.seg button:last-child { border-right: 0; }
.tvx-ctl.seg button.on { background: var(--c-pick); color: oklch(0.18 0.02 258); }

.tvx-fail {
  grid-column: 1 / -1; display: grid; place-items: center; text-align: center;
  color: var(--c-warn); font-size: clamp(14px, 2vh, 34px);
  background: var(--c-surface-1); border-radius: var(--r-3); padding: var(--s-6);
}

/* Доступно скринридеру, не занимает пикселей. */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* --------------------------------------------------------------- подвал --- */
.tvx-foot {
  flex: none; display: flex; align-items: center;
  gap: clamp(10px, 1.4vw, 34px); padding: 0 clamp(4px, 0.5vw, 14px);
  height: clamp(16px, 2.2vh, 46px);
  font-size: clamp(10px, 1.35vh, 26px); color: var(--c-text-3);
  white-space: nowrap; overflow: hidden;
}
.tvx-foot .k { font-weight: 700; letter-spacing: var(--ls-caps); text-transform: uppercase; }
.tvx-foot .sp { margin-left: auto; }
.tvx-foot em { font-style: normal; color: var(--c-text-2); }

/* --------------------------------------------------- «одна ліга» на экран - */
/* 05.08.2026: renderFocus() будує РІВНО одну `.tvx-col` — вона одночасно й
   перша, й остання дитина. Правило `:last-child { display:none }` було
   написане під старіший варіант із двома колонками і ховало ЄДИНУ наявну —
   екран лишався порожнім, хоча дані прийшли й розмітка була правильна
   (перевірено Playwright: DOM містив зону з рядками, видимих пікселів — нуль). */
.tvx-grid.solo { grid-template-columns: 1fr; }

/* ================================================== MANAGER: правки поверх ==
   Полный редизайн таблицы — отдельная задача; здесь исправлено то, что мешало
   читать её как таблицу рейтинга. */

/* Score — главная колонка: по ней теперь стоит место. Раньше он был набран
   тем же кеглем, что и «Пік-листи», и глазами приходилось искать вывод среди
   исходных данных. */
.grid th.hero { color: var(--c-text); }
.grid td.hero {
  font-weight: 800; font-size: var(--fs-16); color: var(--c-text);
  font-variant-numeric: tabular-nums;
}
.grid tr.picker td.hero { color: var(--c-pick); }
.grid tr.sorter td.hero { color: var(--c-pack); }

/* Шапка таблицы: 11.5px в верхнем регистре на приглушённом цвете — это ниже
   порога и по кеглю, и по контрасту одновременно. */
.grid th { font-size: var(--fs-12); color: var(--c-text-2); }

/* Разделитель лиги при сортировке по месту: четыре лестницы должны читаться
   как четыре, а не как одна со сбитой нумерацией. */
.grid tr.lg-first td { border-top: 2px solid var(--c-line); }

.seg button.warn { color: var(--c-warn); }

/* Отладочный режим QA: рисует границу зоны и подпись с --rh.
   Включается ?debug=1 — на телевизоре не появляется никогда. */
body.tvx-debug .zone { outline: 1px dashed color-mix(in oklab, var(--c-pick) 50%, transparent); }
body.tvx-debug .zone-b::after {
  content: attr(data-dbg); position: absolute; right: 4px; bottom: 2px;
  font-size: 11px; color: var(--c-pick); background: var(--c-bg); padding: 0 4px;
}
body.tvx-debug .zone-b { position: relative; }
