/* Копия panel/public/app/css/app.css kiselevlab (через прототип vibe-1, 30.09.2026) — основа вёрстки.
   Не править: тема демо-стенда и новые блоки — в theme.css. */
/* Дизайн-система KISELEV GROUP. Источник — design/prototype/index.html
 * (утверждённый прототип дизайн-чата). Правки вносить синхронно с ним:
 * прототип остаётся эталоном внешнего вида, этот файл — рабочая копия. */

/* =============================================================
 * Учебный портал КГ — прототип оболочки (меню + панели + роли).
 * Дизайн-код: токены и сайдбар/топбар — dev.kiselevgroup.com (dev2-skin),
 * раскладка контента, чаты, конспекты, хаб программы — vibe-portal.
 * ============================================================= */

/* ── Токены KISELEV GROUP (warm): бежевый #F6F4EE · чернила #0E1014 ·
      оранжевый #EE5A1F. Источник — redesign-dev2-package/files/tokens.css.
      Живой dev-портал сейчас на «mineral neutrals» — сюда НЕ берём. ── */
:root {
  --background: 44 33% 95%;   --foreground: 220 18% 7%;
  --card: 0 0% 100%;          --card-foreground: 220 18% 7%;
  --popover: 0 0% 100%;       --popover-foreground: 220 18% 7%;
  --primary: 17 86% 53%;      --primary-foreground: 0 0% 100%;
  --secondary: 44 28% 90%;    --secondary-foreground: 220 18% 7%;
  --muted: 44 28% 90%;        --muted-foreground: 219 12% 34%;
  --accent: 22 80% 92%;       --accent-foreground: 17 86% 45%;
  --destructive: 15 90% 41%;  --destructive-foreground: 0 0% 100%;
  --border: 44 23% 87%;       --input: 44 23% 87%;  --ring: 17 86% 53%;
  --smui-red: 15 90% 41%; --smui-orange: 17 86% 53%; --smui-yellow: 35 87% 42%;
  --smui-green: 154 63% 33%; --smui-purple: 236 48% 58%;
  --smui-surface-0: 44 33% 95%; --smui-surface-1: 0 0% 100%;
  --smui-surface-2: 44 28% 90%; --smui-surface-3: 44 22% 84%;
  --smui-border-hover: 219 12% 45%;
  /* Семантика плашек: выведена из warm-палитры так, чтобы текст читался
     на бежевом фоне и на белой карточке (в архиве этих токенов ещё нет). */
  --kg-ui-blue: 213 45% 40%; --kg-ui-mint: 154 63% 30%; --kg-ui-yellow: 35 87% 34%;
  --kg-ui-orange: 17 86% 43%; --kg-ui-red: 15 90% 41%; --kg-ui-purple: 236 48% 48%;
  --kg-ui-steel: 213 30% 42%; --kg-ui-teal: 176 60% 28%;
  --kg-ui-fill: .12; --kg-ui-fill-hover: .21;
  --kg-side-bg: 220 18% 7%;
  --font-sans: 'Onest', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  --font-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
  --kg-navbar-h: 60px; --kg-side-w: 224px; --kg-side-cw: 62px; --kg-ctrl-h: 32px;
  /* Высота админской полосы над кабинетом. У ученика полосы нет, но
     переменная нужна всегда: от неё считают смещения правила
     `body.has-adminbar`. */
  --kg-adminbar-h: 34px;
  --left-chat-w: 420px; --right-chat-w: 380px;
  color-scheme: light;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --background: 38 13% 7%;   --foreground: 45 35% 93%;
    --card: 40 18% 10%;        --card-foreground: 45 35% 93%;
    --popover: 40 18% 10%;     --popover-foreground: 45 35% 93%;
    --primary: 18 88% 56%;     --primary-foreground: 30 30% 8%;
    --secondary: 38 20% 13%;   --secondary-foreground: 45 35% 93%;
    --muted: 38 20% 13%;       --muted-foreground: 42 12% 66%;
    --accent: 30 22% 14%;      --accent-foreground: 18 88% 56%;
    --destructive: 11 80% 59%; --destructive-foreground: 45 35% 95%;
    --border: 39 17% 16%;      --input: 39 17% 16%; --ring: 18 88% 56%;
    --smui-red: 11 80% 59%; --smui-orange: 18 88% 56%; --smui-yellow: 40 71% 55%;
    --smui-green: 152 47% 52%; --smui-purple: 240 60% 74%;
    --smui-surface-0: 38 13% 7%; --smui-surface-1: 40 18% 10%;
    --smui-surface-2: 38 20% 13%; --smui-surface-3: 36 16% 17%;
    --smui-border-hover: 40 12% 30%;
    --kg-ui-blue: 211 70% 64%; --kg-ui-mint: 152 47% 55%; --kg-ui-yellow: 40 80% 60%;
    --kg-ui-orange: 18 88% 60%; --kg-ui-red: 11 80% 62%; --kg-ui-purple: 240 60% 74%;
    --kg-ui-steel: 210 34% 63%; --kg-ui-teal: 174 55% 50%;
    --kg-ui-fill: .16; --kg-ui-fill-hover: .25;
    --kg-side-bg: 38 22% 5%;
    color-scheme: dark;
  }
}
:root[data-theme="dark"] {
  --background: 38 13% 7%;   --foreground: 45 35% 93%;
  --card: 40 18% 10%;        --card-foreground: 45 35% 93%;
  --popover: 40 18% 10%;     --popover-foreground: 45 35% 93%;
  --primary: 18 88% 56%;     --primary-foreground: 30 30% 8%;
  --secondary: 38 20% 13%;   --secondary-foreground: 45 35% 93%;
  --muted: 38 20% 13%;       --muted-foreground: 42 12% 66%;
  --accent: 30 22% 14%;      --accent-foreground: 18 88% 56%;
  --destructive: 11 80% 59%; --destructive-foreground: 45 35% 95%;
  --border: 39 17% 16%;      --input: 39 17% 16%; --ring: 18 88% 56%;
  --smui-red: 11 80% 59%; --smui-orange: 18 88% 56%; --smui-yellow: 40 71% 55%;
  --smui-green: 152 47% 52%; --smui-purple: 240 60% 74%;
  --smui-surface-0: 38 13% 7%; --smui-surface-1: 40 18% 10%;
  --smui-surface-2: 38 20% 13%; --smui-surface-3: 36 16% 17%;
  --smui-border-hover: 40 12% 30%;
  --kg-ui-blue: 211 70% 64%; --kg-ui-mint: 152 47% 55%; --kg-ui-yellow: 40 80% 60%;
  --kg-ui-orange: 18 88% 60%; --kg-ui-red: 11 80% 62%; --kg-ui-purple: 240 60% 74%;
  --kg-ui-steel: 210 34% 63%; --kg-ui-teal: 174 55% 50%;
  --kg-ui-fill: .16; --kg-ui-fill-hover: .25;
  --kg-side-bg: 38 22% 5%;
  color-scheme: dark;
}

/* ── База ── */
*, *::before, *::after { box-sizing: border-box; }
html, body { height: 100%; }
body {
  margin: 0; background: hsl(var(--background)); color: hsl(var(--foreground));
  font-family: var(--font-sans); font-size: 13px; line-height: 1.45;
  -webkit-font-smoothing: antialiased; overflow: hidden;
  padding-left: var(--kg-side-w);
}
body.kg-side-collapsed { padding-left: var(--kg-side-cw); }
body.side-anim { transition: padding-left .22s ease; }
/* Ссылок в интерфейсе много, и почти все они — кнопки, строки списков и
   карточки: подчёркивание там только шумит. Подчёркиваем осознанно —
   ссылки внутри текста статьи и юридических документов (см. ниже). */
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; cursor: pointer; }
input, textarea, select { font: inherit; color: inherit; }

/* Своя стрелка у выпадающих списков вместо браузерной (правка владельца
   14.09). Родная стрелка у каждого движка своя: в Chrome она мелкая и серая,
   в Firefox другой формы, а на тёмной теме местами вообще чёрная на чёрном.
   Рисуем шеврон фоном — он берёт цвет темы и одинаков везде. Место под него
   резервируем padding-right, иначе длинное значение наезжает на стрелку. */
select {
  -webkit-appearance: none; -moz-appearance: none; appearance: none;
  padding-right: 30px;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8' fill='none' stroke='%23808894' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M1 1.5 6 6.5 11 1.5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 11px center;
  background-size: 11px 7px;
}
select:disabled { opacity: .55; }
/* Сама раскрытая часть списка рисуется системой — ей задаём хотя бы фон и цвет,
   иначе в тёмной теме у некоторых движков она остаётся светлой. */
select option { background: hsl(var(--card)); color: hsl(var(--foreground)); }
svg { display: block; }
:focus-visible { outline: 2px solid hsl(var(--ring)); outline-offset: 2px; }
.mono { font-family: var(--font-mono); }
.num { font-variant-numeric: tabular-nums; }
.muted { color: hsl(var(--muted-foreground)); }

/* ── Сайдбар (dev2-skin, 1:1) ── */
.kg-sidebar {
  position: fixed; left: 0; top: 0; bottom: 0; width: var(--kg-side-w); z-index: 45;
  background: hsl(var(--kg-side-bg)); color: rgba(255,255,255,.85);
  display: flex; flex-direction: column; padding: 14px 12px; gap: 2px; overflow: hidden;
  border-right: 1px solid hsl(var(--border)); font-family: var(--font-sans);
}
body.side-anim .kg-sidebar { transition: width .22s ease; }
body.kg-side-collapsed .kg-sidebar { width: var(--kg-side-cw); }
.kgs-brand { display: flex; align-items: center; gap: 10px; padding: 4px 6px 16px; text-decoration: none; white-space: nowrap; }
/* Знак ОДНОГО размера везде: квадрат 36, скругление 10 — и в кабинете, и на
   сайте, и на страницах оплаты. Было 32 (правка владельца 15.09: «логотип в
   app KL и черточка очень мелкие»). Размер букв внутри отдельно НЕ задаётся,
   см. правило `.kgs-mark svg` сразу ниже — там же причина. */
.kgs-mark { width: 36px; height: 36px; flex-shrink: 0; color: #fff;
  background: linear-gradient(140deg, hsl(var(--primary)) 0%, hsl(var(--smui-red)) 100%);
  display: grid; place-items: center; font-family: var(--font-mono); font-weight: 800; font-size: 12px; border-radius: 10px; }
/* ⚠️ Рисунок занимает ВЕСЬ квадрат, и это не небрежность. Поля у знака уже
   нарисованы ВНУТРИ картинки: контур «KL» занимает 410 единиц из 512 по
   ширине (80%) и 301 по высоте. Пока svg вставляли размером «23 из 36», поля
   складывались дважды и знак в шапке выходил заметно мельче того же знака в
   иконке вкладки, где viewBox и есть весь квадрат (правка владельца 15.09:
   «KL и черточка очень мелкие, хотя в других местах правильно»). Размер
   квадрата — ЕДИНСТВЕННОЕ число: атрибуты width/height у svg в разметке
   остаются только как запасной вид без стилей.
   ⚠️ Знак сменён 21.09 — новые «KL» вместо прежних «KL» с чертой под буквами.
   Он ШИРЕ квадрата (410 × 301), поэтому поля по бокам и сверху разные: так и
   задумано. Точка из присланной владельцем картинки убрана его же решением —
   она почти ничего не весит и перекашивала знак влево при равных полях. */
.kgs-mark svg { width: 100%; height: 100%; display: block; }
.kgs-brandtext { display: flex; flex-direction: column; line-height: 1; }
.kgs-brandtext b { color: #fff; font-size: 16px; font-weight: 800; letter-spacing: .04em; }
.kgs-brandtext i { color: rgba(255,255,255,.4); font-size: 8px; font-style: normal; letter-spacing: .01em; text-transform: uppercase; margin-top: 3px; }
/* `min-height: 0` здесь обязателен, а не для красоты: по умолчанию элемент
   флекс-колонки не сжимается меньше своего содержимого, поэтому длинный
   список разделов растягивал меню, `overflow-y: auto` не включался, а лишнее
   срезал `overflow: hidden` у самого сайдбара — прокрутки не было, и вместе с
   хвостом списка уезжали блок персонала и карточка пользователя. */
.kgs-nav { display: flex; flex-direction: column; gap: 2px; min-height: 0; flex: 0 1 auto;
  overflow-y: auto; overflow-x: hidden; scrollbar-width: none; }
.kgs-nav::-webkit-scrollbar { display: none; }
.kgs-grp { font-family: var(--font-mono); font-size: 9.5px; color: rgba(255,255,255,.4);
  padding: 16px 8px 6px; letter-spacing: .07em; text-transform: uppercase; white-space: nowrap; }
.kgs-link { display: flex; align-items: center; gap: 11px; padding: 9px 9px; border-radius: 9px; width: 100%;
  font-size: 13px; color: rgba(255,255,255,.82); text-decoration: none; white-space: nowrap;
  transition: background .12s; position: relative; background: none; border: 0; text-align: left; }
.kgs-link:hover { background: rgba(255,255,255,.06); }
.kgs-link.kgs-active { background: rgba(255,255,255,.10); color: #fff; font-weight: 600; }
.kgs-link.kgs-active::before { content: ""; position: absolute; left: -12px; top: 8px; bottom: 8px; width: 3px; border-radius: 0 3px 3px 0; background: hsl(var(--primary)); }
.kgs-link svg { flex-shrink: 0; opacity: .72; }
.kgs-link.kgs-active svg { opacity: 1; }
.kgs-link > span:not(.kgs-badge) { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.kgs-badge { margin-left: auto; flex-shrink: 0; white-space: nowrap; min-width: 18px; text-align: center;
  padding: 1px 6px; border-radius: 20px; background: hsl(var(--primary)); color: hsl(var(--primary-foreground));
  font-size: 10.5px; font-weight: 800; font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
.kgs-badge.is-soft { background: rgba(255,255,255,.12); color: rgba(255,255,255,.75); font-weight: 600; }
/* «Чаты» в сайдбаре — КНОПКА (см. navItems() в shell.js), у неё нет своего
   адреса, поэтому подсветку «открыто сейчас» решает класс на body, а не
   `.kgs-active`: тот же приём, что раньше стоял на кнопке в верхней панели. */
body.has-right-chat .kgs-link.is-chat { background: rgba(255,255,255,.10); color: #fff; }
.kgs-spacer { flex: 1; }
.kgs-foot { border-top: 1px solid rgba(255,255,255,.09); padding-top: 10px; margin-top: 8px; flex-shrink: 0; }
.kgs-user { display: flex; align-items: center; gap: 10px; padding: 6px; white-space: nowrap; }
.kgs-av { width: 30px; height: 30px; flex-shrink: 0; border-radius: 50%;
  background: linear-gradient(135deg, hsl(var(--primary)), hsl(15 90% 41%));
  display: grid; place-items: center; color: #fff; font-weight: 700; font-size: 11px; font-family: var(--font-mono); }
/* Себя в подвале меню тоже можно нажать — это второй вход в свою карточку и
   ЕДИНСТВЕННЫЙ на телефоне: там плашка с именем из шапки скрыта (см. медиа-
   запрос ниже), а меню выдвигается. */
.kgs-mecard { display: flex; align-items: center; gap: 10px; min-width: 0; flex: 1;
  color: inherit; text-decoration: none; border-radius: 9px; }
/* Силуэт вместо кружка с инициалами — и только в СВЁРНУТОМ меню: в развёрнутом
   рядом стоит имя, и значок был бы лишним, а в рельсе без него вход в свою
   карточку пропал бы вовсе (кружок оттуда убран правкой владельца 15.09). */
.kgs-me-ic { display: none; color: rgba(255,255,255,.6); }
body.kg-side-collapsed .kgs-me-ic { display: flex; }
body.kg-side-collapsed .kgs-mecard { justify-content: center; flex: none; }
.kgs-mecard:hover .kgs-uname { color: hsl(var(--primary)); }
.kgs-unamebox { display: flex; flex-direction: column; min-width: 0; line-height: 1.15; }
.kgs-uname { color: #fff; font-size: 12.5px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; }
.kgs-urole { color: rgba(255,255,255,.45); font-size: 9.5px; font-family: var(--font-mono); letter-spacing: .06em; text-transform: uppercase; }
.kgs-logout { margin-left: auto; flex-shrink: 0; color: rgba(255,255,255,.5); display: grid; place-items: center; cursor: pointer; background: none; border: 0; padding: 4px; }
.kgs-logout:hover { color: #fff; }
body.kg-side-collapsed .kgs-brandtext, body.kg-side-collapsed .kgs-link span, body.kg-side-collapsed .kgs-grp,
body.kg-side-collapsed .kgs-unamebox, body.kg-side-collapsed .kgs-logout, body.kg-side-collapsed .kgs-badge { display: none; }
body.kg-side-collapsed .kgs-link { justify-content: center; }
body.kg-side-collapsed .kgs-user { justify-content: center; }
body.kg-side-collapsed .kgs-brand { justify-content: center; padding-left: 0; padding-right: 0; }
body.kg-side-collapsed .kgs-link.kgs-active::before { display: none; }

/* Стрелка сворачивания меню — внутри renderSidebar() (правка владельца
   24.09, три захода в тот же день). Развёрнуто и свёрнуто устроены РАЗНО, и
   это не забытая унификация, а обратный вывод из двух неудачных попыток:
   (1) кружок НА ГРАНИЦЕ сайдбара — половина на тёмном, половина на светлом,
   да ещё с рамкой, вышел шов; (2) своя строка ПОД логотипом в ОБОИХ
   состояниях — развёрнуто владелец назвал «ужасно»: там она смотрелась
   лишней плашкой на всю ширину там, где раньше стояла компактно рядом с
   именем. Развёрнутому виду вернули то, что было ДО правок (компактно,
   справа от «KISELEV LAB», без рамки и кружка — эту часть владелец одобрил
   ещё во втором заходе). Абсолютная позиция внутри `.kg-sidebar`
   (position:fixed — уже готовый контекст) не отнимает у строки бренда
   высоту и не считает пиксели от ширины сайдбара: `right` меряется от его
   же правого края, поэтому его не нужно пересчитывать под кг-side-w/cw.
   Свёрнутому рельсу (62px, там едва хватает места одному знаку KL) такая
   позиция рядом с лого не подходит — вот там и остаётся строка под ним. */
.kgs-toggle {
  position: absolute; top: 36px; right: 8px; transform: translateY(-50%);
  width: 22px; height: 22px; padding: 0; border-radius: 6px; flex-shrink: 0;
  display: grid; place-items: center;
  background: none; border: 0; color: rgba(255,255,255,.4);
  transition: color .15s, background .15s;
}
.kgs-toggle:hover { color: #fff; background: rgba(255,255,255,.08); }
.kgs-toggle svg { transition: transform .18s ease; }
body.kg-side-collapsed .kgs-toggle {
  position: static; top: auto; right: auto; transform: none;
  width: 100%; margin: 2px 0 8px; display: flex; justify-content: center;
}
/* Свёрнуто — стрелка смотрит вправо (разворачивать), развёрнуто — влево
   (свернуть): значок говорит о ДЕЙСТВИИ по клику, не о текущем состоянии. */
body.kg-side-collapsed .kgs-toggle svg { transform: rotate(180deg); }

/* ── Топбар (dev2-skin) ── */
.navbar {
  height: var(--kg-navbar-h); padding: 0 18px 0 14px; gap: 10px; display: flex; align-items: center;
  background: hsl(var(--card)); border-bottom: 1px solid hsl(var(--border));
  overflow-x: auto; scrollbar-width: none; position: relative; z-index: 40;
}
.navbar::-webkit-scrollbar { display: none; }
.navbar-spacer { flex: 1; }
/* Лимиты подписки Claude (только у админа) — ДВЕ строки в одной плашке
   (правка владельца 10.09). Высота, скругление, рамка и фон ровно как у
   кнопки темы рядом: одной строкой плашка была вдвое шире соседей, да ещё
   с круглыми краями, и правый угол шапки выглядел собранным из трёх разных
   стилей. Моноширинный шрифт с табличными цифрами — чтобы плашка не
   дёргалась по ширине, когда 9% станет 10%. */
/* ⚠️ Плашка ужата примерно на треть (правка владельца 15.09). Ширину съедали
   четыре колонки фиксированного размера; ужимались все разом, потому что
   строка обязана оставаться сеткой — иначе «5ч» и «7д» перестают вставать
   друг под другом. Заодно «сколько осталось» печатается одной единицей
   («3ч» вместо «3ч48м», см. leftLabel в shell.js): в плашке смотрят
   «скоро или нет», точное время сброса лежит в подсказке. */
.lim { display: flex; align-items: center; gap: 6px; flex-shrink: 0; cursor: pointer;
  height: var(--kg-ctrl-h); padding: 0 8px; border-radius: 9px;
  border: 1px solid hsl(var(--border)); background: hsl(var(--card));
  font-family: var(--font-mono); font-size: 8.5px; line-height: 1;
  font-variant-numeric: tabular-nums; color: hsl(var(--muted-foreground));
  white-space: nowrap; transition: background .15s, border-color .15s; }
.lim:hover { background: hsl(var(--background)); border-color: hsl(var(--muted-foreground)); }
.lim-mark { display: flex; color: hsl(var(--primary)); }
/* Знак OpenAI монохромный (правка владельца 18.09). Не `#000`, а токен текста:
   на тёмной теме чёрное на чёрном не видно вовсе. Правило `.lim.is-off
   .lim-mark` весит больше и по-прежнему гасит знак у неподключённого аккаунта. */
.lim-mark.is-mono { color: hsl(var(--foreground)); }
.lim-rows { display: grid; gap: 4px; }
.lim-r { display: flex; align-items: center; gap: 4px; }
/* Колонки фиксированной ширины: иначе полоски и проценты двух строк не
   встают друг под другом и плашка выглядит рассыпанной. По той же причине
   фиксирована дорожка — полоска нужна «на глаз», прыгающая ширина мешает. */
.lim-k { width: 10px; opacity: .75; }
.lim b { width: 21px; text-align: right; font-weight: 600; color: hsl(var(--foreground)); }
/* Сколько осталось до обнуления: смотрят на плашку как раз ради пары
   «сколько выбрано и сколько ещё терпеть». */
.lim em { width: 17px; text-align: right; font-style: normal; opacity: .75; }
.lim-t { width: 14px; height: 4px; border-radius: 999px; overflow: hidden;
  background: hsl(var(--muted-foreground) / .22); }
.lim-t i { display: block; height: 100%; border-radius: 999px; background: hsl(var(--kg-ui-mint)); }
.lim-t i.is-warm { background: hsl(var(--kg-ui-yellow)); }
.lim-t i.is-hot { background: hsl(var(--destructive)); }
.lim.is-warm { border-color: hsl(var(--kg-ui-yellow) / .5); }
.lim.is-hot { border-color: hsl(var(--destructive) / .55); }
/* Показывать нечего — плашка остаётся НА МЕСТЕ и той же формы, но тихой:
   у Codex аккаунт ещё не подключён, и его место в шапке занято заранее.
   Убирать плашку до подключения нельзя — иначе ряд кнопок в шапке будет
   перестраиваться в тот день, когда аккаунт появится. */
.lim.is-off { opacity: .72; }
.lim.is-off b { color: hsl(var(--muted-foreground)); }
.lim.is-off .lim-mark { color: hsl(var(--muted-foreground)); }
/* Обновить не вышло (у Anthropic жёсткий лимит частоты) — показываем ПОСЛЕДНИЕ
   цифры, но приглушённо: видно, что они не сию секунду, а сколько им лет и
   почему — в подсказке. Прочерки вместо чисел здесь были бы хуже: человек
   читает их как «всё сломалось», хотя данные есть. */
.lim.is-stale .lim-mark, .lim.is-stale .lim-rows { opacity: .5; }
.btn-icon {
  width: var(--kg-ctrl-h); height: var(--kg-ctrl-h); border-radius: 9px; flex-shrink: 0;
  border: 1px solid hsl(var(--border)); background: hsl(var(--card)); color: hsl(var(--muted-foreground));
  display: inline-flex; align-items: center; justify-content: center; position: relative;
  transition: background .15s, border-color .15s, color .15s;
}
.btn-icon:hover { background: hsl(var(--background)); border-color: hsl(var(--muted-foreground)); color: hsl(var(--foreground)); }
.btn-icon.nav-active { border-color: hsl(var(--primary)); color: hsl(var(--primary)); background: hsl(var(--primary) / 0.08); }
.btn-icon .dot { position: absolute; top: 5px; right: 5px; width: 7px; height: 7px; border-radius: 50%; background: hsl(var(--primary)); border: 1.5px solid hsl(var(--card)); }
/* Бургер — ТОЛЬКО на телефоне (правка владельца 24.09). На широком экране
   меню сворачивает стрелка под лого (.kgs-toggle в сайдбаре), а этой кнопке в
   шапке стало нечего делать — только съедать место, которого там и так не хватало.
   На телефоне сайдбар выезжает ПОВЕРХ контента и до открытия не виден вовсе:
   без кнопки здесь открыть меню было бы нечем (см. @media 640px ниже).
   ⚠️ Правило ОБЯЗАНО стоять ПОСЛЕ `.btn-icon` — у обоих селекторов
   одинаковая специфичность (один класс), и `.btn-icon` тоже задаёт `display`
   (inline-flex); при равной специфичности побеждает последнее по тексту
   правило, а не по «логике». Поставленное раньше `.btn-icon`, оно молча
   проигрывало и бургер оставался виден на десктопе — поймано headless-
   браузером (Playwright), в разметке и по чтению кода это не видно вовсе. */
.kgs-burger { display: none; }
.nav-crumb { display: flex; align-items: baseline; gap: 10px; white-space: nowrap; min-width: 0; }
.nav-crumb b { font-size: 13.5px; font-weight: 700; letter-spacing: -.01em; }
.nav-crumb i { font-style: normal; font-family: var(--font-mono); font-size: 10.5px; color: hsl(var(--muted-foreground)); letter-spacing: .02em; }
/* Пилюли статуса — общая форма dev-портала */
.pill {
  height: var(--kg-ctrl-h); border-radius: 9px; flex-shrink: 0; box-sizing: border-box; cursor: pointer;
  background: hsl(var(--background)); border: 1px solid hsl(var(--border));
  display: inline-flex; align-items: center; gap: 7px; padding: 0 10px; color: hsl(var(--muted-foreground));
  transition: border-color .15s;
}
.pill:hover { border-color: hsl(var(--smui-border-hover)); }
.pill-l { font-family: var(--font-mono); font-size: 8.5px; font-weight: 600; letter-spacing: .05em; text-transform: uppercase; }
.pill-v { font-family: var(--font-mono); font-size: 11.5px; font-weight: 700; font-variant-numeric: tabular-nums; color: hsl(var(--foreground)); }
.pill-dot { width: 7px; height: 7px; border-radius: 50%; background: hsl(var(--kg-ui-mint)); box-shadow: 0 0 0 3px hsl(var(--kg-ui-mint) / .18); }
.pill.is-good { border-color: hsl(var(--kg-ui-mint) / .5); } .pill.is-good .pill-v { color: hsl(var(--kg-ui-mint)); }
.pill.is-warn { border-color: hsl(var(--kg-ui-yellow) / .6); } .pill.is-warn .pill-v { color: hsl(var(--kg-ui-yellow)); } .pill.is-warn .pill-dot { background: hsl(var(--kg-ui-yellow)); box-shadow: 0 0 0 3px hsl(var(--kg-ui-yellow) / .18); }
.pill.is-idle .pill-dot { background: hsl(var(--muted-foreground)); box-shadow: none; }
.pill-bar { width: 44px; height: 5px; border-radius: 999px; background: hsl(var(--secondary)); overflow: hidden; }
.pill-bar i { display: block; height: 100%; width: 38%; background: hsl(var(--kg-ui-mint)); border-radius: 999px; }
.btn-assist {
  height: var(--kg-ctrl-h); border-radius: 9px; flex-shrink: 0; padding: 0 12px 0 10px;
  border: 1px solid hsl(var(--primary) / .45); background: hsl(var(--primary) / .07); color: hsl(var(--primary));
  display: inline-flex; align-items: center; gap: 7px; font-weight: 600; font-size: 12.5px;
}
.btn-assist:hover, .btn-assist.nav-active { background: hsl(var(--primary)); color: hsl(var(--primary-foreground)); border-color: hsl(var(--primary)); }
/* Имя в шапке — такая же плашка, как кнопка темы: та же высота, то же
   скругление, та же рамка (правка владельца 10.09). Роль отсюда убрана — она
   в подсказке: администратор и так знает, кто он, а вторая строка растила
   плашку и ломала ряд. Не кнопка и не ссылка: нажимать тут нечего, поэтому
   и наведения нет. */
/* Плашка с именем — ссылка на свою карточку (/app/profile): нажать на себя
   и увидеть свои данные ожидаемее, чем искать раздел в меню. */
/* ⚠️ Кружка с инициалами здесь больше НЕТ (правка владельца 15.09), и отсюда
   симметричные отступы: раньше слева было 3 px под аватар, и без него плашка
   выглядела бы сдвинутой. Ширина теперь ровно по имени. */
.tb-user { display: flex; align-items: center; flex-shrink: 0;
  height: var(--kg-ctrl-h); padding: 0 12px; border-radius: 9px;
  border: 1px solid hsl(var(--border)); background: hsl(var(--card));
  color: inherit; text-decoration: none; }
.tb-user:hover { border-color: hsl(var(--muted-foreground)); }
.tb-user b { font-size: 12px; font-weight: 600; }

/* ── Просмотр глазами ученика (17.09.2026) ─────────────────────────────────
   Администратор должен видеть КАЖДУЮ секунду, что кабинет сейчас чужой:
   плашка имени становится оранжевой и подписывается «под учеником», а по краю
   окна идёт тонкая рамка того же цвета. Без такого сигнала чужой кабинет
   неотличим от своего — а это ровно тот случай, когда человек думает, что
   правит своё. */
.tb-user.is-imp { flex-direction: column; align-items: flex-start; justify-content: center;
  gap: 0; height: auto; padding: 4px 11px; cursor: pointer;
  border-color: hsl(var(--primary) / .6); background: hsl(var(--primary) / .1); }
.tb-user.is-imp i { font-style: normal; font-family: var(--font-mono); font-size: 8.5px;
  font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
  color: hsl(var(--primary)); line-height: 1.3; }
.tb-user.is-imp b { color: hsl(var(--foreground)); line-height: 1.25; }
.tb-user.is-imp:hover { background: hsl(var(--primary) / .18); }
/* Рамка окна. `pointer-events: none` обязателен — иначе слой поверх страницы
   съедает все клики. */
body.is-impersonating::after { content: ''; position: fixed; inset: 0; z-index: 90;
  pointer-events: none; border: 2px solid hsl(var(--primary) / .55); border-radius: 3px; }

/* ── Админская полоса над кабинетом (правка владельца 28.09) ──────────────
   Тонкая строка поверх ВСЕГО, во всю ширину экрана — включая колонку меню:
   разделы портала не принадлежат ни программе, ни ученику, и место им над
   кабинетом, а не внутри него.
   ⚠️ Полоса `fixed`, поэтому её высоту обязаны знать все, кто считал от
   верха окна: сайдбар (`top`), рабочая область (`height: 100vh - …`) и обе
   выдвижные панели (`top`). Правила ниже — под `body.has-adminbar`, то есть
   у ученика раскладка остаётся прежней до пикселя.
   ⚠️ Класс `.on` — текущий раздел; подсветка идёт по адресу, включая
   вложенные (`/app/users/42`), см. renderAdminBar. */
.adminbar {
  position: fixed; top: 0; left: 0; right: 0; z-index: 60;
  height: var(--kg-adminbar-h); display: flex; align-items: center; gap: 4px;
  padding: 0 14px; background: hsl(var(--kg-side-bg)); color: rgba(255,255,255,.72);
  font-family: var(--font-sans); font-size: 11.5px;
  border-bottom: 1px solid rgba(255,255,255,.08);
  overflow-x: auto; scrollbar-width: none;
}
/* 🔴 БЕЗ ЭТОЙ СТРОКИ ПОЛОСА ВИДНА УЧЕНИКУ. `display:flex` выше перебивает
   атрибут `hidden`, которым её гасит shell.js, — и у всех, кроме админа,
   наверху оставалась пустая чёрная полоса, накрывавшая шапку (поймано
   владельцем на снимке в тот же день). Ровно та же ловушка, что уже стоила
   блока «это подписка с автоматическим продлением» на оформлении, и она же
   записана в правилах проекта: «display:flex поверх hidden». */
.adminbar[hidden] { display: none !important; }
.adminbar::-webkit-scrollbar { display: none; }
.adminbar .ab-links { display: flex; align-items: center; gap: 2px; flex: 1; min-width: 0; }
.ab-link, .ab-admin {
  display: inline-flex; align-items: center; gap: 6px; flex-shrink: 0;
  height: 22px; padding: 0 9px; border-radius: 7px; text-decoration: none;
  color: rgba(255,255,255,.72); font-weight: 600; letter-spacing: .01em;
  transition: background .13s, color .13s;
}
.ab-link svg, .ab-admin svg { opacity: .8; }
.ab-link:hover { background: rgba(255,255,255,.1); color: #fff; }
.ab-link.on { background: rgba(255,255,255,.14); color: #fff; }
.ab-link.on svg { opacity: 1; }
/* «Админ» — вход в управление целиком, поэтому фирменного цвета, а не серый
   как разделы: он не сосед им по списку, а дверь над ним. */
.ab-admin { background: hsl(var(--primary) / .18); color: hsl(var(--primary));
            border: 1px solid hsl(var(--primary) / .4); }
.ab-admin:hover, .ab-admin.on { background: hsl(var(--primary));
            color: hsl(var(--primary-foreground)); border-color: hsl(var(--primary)); }
.ab-admin svg { opacity: 1; }

body.has-adminbar .kg-sidebar { top: var(--kg-adminbar-h); }
body.has-adminbar > .navbar { margin-top: var(--kg-adminbar-h); }
body.has-adminbar .main-layout,
body.has-adminbar .dashboard { height: calc(100vh - var(--kg-navbar-h) - var(--kg-adminbar-h)); }
body.has-adminbar .drawer,
body.has-adminbar .form-drawer { top: calc(var(--kg-navbar-h) + var(--kg-adminbar-h)); }
/* До входа кабинета нет вовсе — нет и полосы (класс снимается в shell.js,
   но гостевая часть рисуется раньше первого renderTopbar). */
body.guest .adminbar { display: none; }
@media (max-width: 900px) {
  /* На телефоне подписи не влезают семь раз — остаются значки, а «Админ»
     держит подпись: это единственная кнопка, которую ищут глазами. */
  .ab-link span { display: none; }
  .ab-link { padding: 0 7px; }
}

/* Кнопка «Админ» между темой и именем (правка владельца 15.09): вход в
   панель администратора. Форма — как у соседей по ряду (высота, скругление,
   рамка), цвет — фирменный: это единственная кнопка в шапке, которая ведёт
   не «к себе», а в управление порталом.
   ⚠️ С 28.09 в шапке её НЕТ — она переехала в админскую полосу (.ab-admin).
   Стиль оставлен: разметка `.btn-admin` может понадобиться снова. */
.btn-admin { display: inline-flex; align-items: center; gap: 6px; flex-shrink: 0;
  height: var(--kg-ctrl-h); padding: 0 11px; border-radius: 9px;
  border: 1px solid hsl(var(--primary) / .45); background: hsl(var(--primary) / .07);
  color: hsl(var(--primary)); text-decoration: none;
  font-size: 11.5px; font-weight: 700; letter-spacing: .02em;
  transition: background .15s, border-color .15s, color .15s; }
.btn-admin:hover, .btn-admin.on { background: hsl(var(--primary));
  color: hsl(var(--primary-foreground)); border-color: hsl(var(--primary)); }

/* Своя карточка: строки «ключ — значение — кнопка». Значение растягивается,
   кнопка прижата вправо; на телефоне строка разворачивается в столбик. */
.pf-rows { margin-top: 18px; border-top: 1px solid hsl(var(--border)); }
.pf-row { display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 11px 0; border-bottom: 1px solid hsl(var(--border) / .5); }
.pf-k { min-width: 88px; font-family: var(--font-mono); font-size: 10.5px; font-weight: 600;
  letter-spacing: .06em; text-transform: uppercase; color: hsl(var(--muted-foreground)); }
.pf-v { flex: 1; min-width: 160px; font-size: 13px; word-break: break-word; }
/* Раскладка карточки — плашками в два столбца (правка владельца 17.09: одна
   колонка «от и до» смотрелась пусто). Слева данные, справа стопка коротких
   плашек, оплаты — таблицей на всю ширину. На узком экране всё в столбик. */
.pf-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; }
/* ⚠️ align-items СТРЕТЧИТ короткий столбец под высоту длинного (правка
   владельца 18.09) — раньше стоял align-items:start, и под коротким столбцом
   (в 2-3 карточки) оставалось пустое место фона ниже последней карточки,
   независимо от того, есть ли в нём необязательный блок (например, «Вход
   через Битрикс24») или нет. Лишнюю высоту забирает ПОСЛЕДНЯЯ карточка
   столбца (`.pf-side > :last-child`), а не пустота под ней — так место видно
   как «карточка попросторнее», а не как дыра в вёрстке. */
.pf-side { display: flex; flex-direction: column; gap: 16px; min-width: 0; }
.pf-side > :last-child { flex: 1 1 auto; }
.pf-main { min-width: 0; }
.pf-wide { grid-column: 1 / -1; }
.pf-side .hw-row { padding: 4px 0; }
.pf-side .form-hint { margin-top: 10px; }
@media (max-width: 900px) { .pf-grid { grid-template-columns: 1fr; } }

/* ── Раскладка: контент | правая панель помощника (vibe-portal) ── */
.main-layout {
  display: grid; grid-template-columns: 0 1fr 0; height: calc(100vh - var(--kg-navbar-h)); overflow: hidden;
  transition: grid-template-columns .15s;
}
body.has-right-chat .main-layout { grid-template-columns: 0 1fr var(--right-chat-w); }
body.has-left-chat .main-layout { grid-template-columns: var(--left-chat-w) 1fr 0; }
body.has-left-chat.has-right-chat .main-layout { grid-template-columns: var(--left-chat-w) 1fr var(--right-chat-w); }
.chat-pane-left { grid-column: 1; grid-row: 1; overflow: hidden; border-right: 1px solid hsl(var(--border)); }
.chat-pane-right { grid-column: 3; grid-row: 1; overflow: hidden; border-left: 1px solid hsl(var(--border)); }
.dashboard { grid-column: 2; grid-row: 1; min-width: 0; overflow-y: auto; height: 100%; }
.page { padding: 22px 26px 60px; max-width: 1360px; }
.view[hidden] { display: none !important; }

/* Чат-панели (структура vibe-portal, скин KG) */
.chat-pane { background: hsl(var(--card)); display: flex; flex-direction: column; height: 100%; min-width: 0; }
.chat-head { display: flex; align-items: center; gap: 10px; padding: 12px 14px; border-bottom: 1px solid hsl(var(--border)); }
.chat-head b { font-size: 12.5px; font-weight: 700; }
.chat-head i { font-style: normal; font-family: var(--font-mono); font-size: 9.5px; color: hsl(var(--muted-foreground)); letter-spacing: .06em; text-transform: uppercase; }
.chat-msgs { flex: 1; overflow-y: auto; padding: 14px; display: flex; flex-direction: column; gap: 10px; }
.bubble { max-width: 92%; padding: 9px 12px; border-radius: 12px; font-size: 12.5px; line-height: 1.5; }
.bubble.u { align-self: flex-end; background: hsl(var(--primary) / .12); border: 1px solid hsl(var(--primary) / .3); }
.bubble.a { align-self: flex-start; background: hsl(var(--background)); border: 1px solid hsl(var(--border)); }
.bubble .who { display: block; font-family: var(--font-mono); font-size: 9px; letter-spacing: .08em; text-transform: uppercase; color: hsl(var(--muted-foreground)); margin-bottom: 4px; }
.chat-form { padding: 12px 14px; border-top: 1px solid hsl(var(--border)); display: flex; gap: 8px; align-items: flex-end; }
.chat-form textarea { flex: 1; resize: none; height: 64px; border-radius: 10px; border: 1px solid hsl(var(--input)); background: hsl(var(--background)); padding: 9px 11px; font-size: 12.5px; }
.chat-send { height: 34px; padding: 0 12px; border-radius: 10px; border: 1px solid transparent; background: hsl(var(--primary)); color: hsl(var(--primary-foreground)); font-weight: 700; }
.pc-seg { display: inline-flex; border: 1px solid hsl(var(--border)); border-radius: 9px; overflow: hidden; margin-left: auto; }
.pc-seg button { height: 24px; padding: 0 9px; background: hsl(var(--background)); border: 0; font-family: var(--font-mono); font-size: 9.5px; font-weight: 700; letter-spacing: .06em; color: hsl(var(--muted-foreground)); }
.pc-seg button.on { background: hsl(var(--primary)); color: hsl(var(--primary-foreground)); }

/* ── Шапка раздела (dev2-skin .section-head) ── */
.section-head { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; margin-bottom: 18px; }
.section-head-left { display: flex; align-items: baseline; gap: 10px; min-width: 0; }
.section-label { font-family: var(--font-mono); font-size: 13px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; }
.section-path { font-family: var(--font-mono); font-size: 11px; color: hsl(var(--muted-foreground)); }
.project-search {
  flex: 1; min-width: 220px; max-width: 620px; height: var(--kg-ctrl-h); border-radius: 9px; background: hsl(var(--background));
  border: 1px solid hsl(var(--border)); padding: 0 12px; gap: 8px; display: flex; align-items: center; color: hsl(var(--muted-foreground));
}
.project-search:focus-within { border-color: hsl(var(--primary)); }
.project-search input { flex: 1; border: none; background: none; outline: none; font-family: var(--font-sans); font-size: 12.5px; color: hsl(var(--foreground)); }
.dashboard-actions { display: flex; align-items: center; gap: 8px; margin-left: auto; }
/* Панель фильтров списка учеников (21.09). Обычный перенос по строкам, а не
   прокрутка вбок: фильтров десять, и спрятанный за краем экрана фильтр
   читается как несуществующий. Ширину задаёт сам селект — у .form-input она
   100%, и без этого каждый занял бы свою строку. */
.u-filters { display: flex; flex-wrap: wrap; gap: 8px; margin: -6px 0 16px; }
.u-filters > .form-input, .u-filters .kg-select { width: auto; min-width: 0; }
.u-filters .kg-select-btn { max-width: 230px; }
.u-filters .kg-select-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.btn { height: var(--kg-ctrl-h); padding: 0 14px; border-radius: 9px; font-family: var(--font-sans); font-weight: 600; font-size: 12.5px;
  display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; border: 1px solid hsl(var(--border));
  background: hsl(var(--card)); color: hsl(var(--foreground)); transition: transform .12s, filter .15s, background .15s, border-color .15s; }
.btn:hover { background: hsl(var(--background)); }
.btn:active { transform: translateY(1px); }
.btn-primary { background: hsl(var(--primary)); color: hsl(var(--primary-foreground)); border-color: transparent; }
.btn-primary:hover { background: hsl(var(--primary)); filter: brightness(1.06); }
.btn-ghost { background: hsl(var(--card)); }
.btn-danger { color: hsl(var(--destructive)); border-color: hsl(var(--destructive) / .45); }
.btn-danger:hover { background: hsl(var(--destructive) / .08); }
.btn-sm { height: 26px; padding: 0 10px; font-size: 11.5px; border-radius: 7px; }
.btn-filter { text-transform: uppercase; font-family: var(--font-mono); font-size: 10.5px; letter-spacing: .05em; font-weight: 700; color: hsl(var(--muted-foreground)); }
.btn-filter.is-on { color: hsl(var(--primary)); border-color: hsl(var(--primary) / .5); background: hsl(var(--primary) / .07); }
/* ── Выпадашка в нашем стиле (см. enhanceSelect в ui.js) ── */
/* Нативный select остаётся в DOM как источник значения, но не показывается:
   его меню рисует операционная система, чужим шрифтом и цветом. */
.kg-select-native { display: none !important; }
.kg-select { position: relative; display: inline-flex; }
.kg-select-btn { display: inline-flex; align-items: center; justify-content: space-between;
  gap: 8px; width: 100%; white-space: nowrap; }
.kg-select-btn svg { flex-shrink: 0; opacity: .6; transition: transform .15s; }
.kg-select.open .kg-select-btn svg { transform: rotate(180deg); }
.kg-select.open .kg-select-btn { border-color: hsl(var(--primary) / .55); color: hsl(var(--primary)); }
.kg-select-menu {
  display: none; position: absolute; top: calc(100% + 6px); right: 0; z-index: 60;
  min-width: 100%; max-height: 260px; overflow-y: auto; padding: 5px;
  background: hsl(var(--popover)); border: 1px solid hsl(var(--border));
  border-radius: 11px; box-shadow: 0 18px 40px -18px hsl(220 18% 7% / .45);
}
.kg-select.open .kg-select-menu { display: block; }
.kg-select-item {
  display: block; width: 100%; text-align: left; white-space: nowrap;
  padding: 7px 10px; border: 0; border-radius: 7px; background: none;
  font-family: var(--font-mono); font-size: 11.5px; color: hsl(var(--foreground));
}
.kg-select-item:hover { background: hsl(var(--secondary)); }
.kg-select-item.on { background: hsl(var(--primary) / .12); color: hsl(var(--primary)); font-weight: 600; }

/* Выпадашка ВНУТРИ ФОРМЫ (22.09): «чем вы занимаетесь» в регистрации, на
   оформлении заказа, в анкете кабинета и в карточке ученика.
   ⚠️ По умолчанию .kg-select — фильтр в шапке таблицы: inline-flex и пункты
   без переноса. В форме та же разметка обязана вести себя как обычное поле,
   иначе длинная подпись («Штатный интегратор — внедряю Битрикс24…») уезжает
   за край экрана на телефоне. Видно только на снимке 390 px. */
.kg-select.is-field { display: flex; width: 100%; }
.kg-select.is-field .kg-select-btn { height: auto; min-height: var(--kg-ctrl-h);
  padding: 9px 12px; white-space: normal; text-align: left; line-height: 1.35; }
.kg-select.is-field .kg-select-label { white-space: normal; }
.kg-select.is-field .kg-select-label.is-empty { color: hsl(var(--muted-foreground)); }
.kg-select.is-field .kg-select-menu { left: 0; right: 0; min-width: 0; max-height: 360px; }  /* ← весь список без прокрутки: семь вариантов, и последний из них —
       «не хочу отвечать», спрятать его за прокруткой значило бы прятать
       единственный необязывающий ответ */
.kg-select.is-field .kg-select-item { white-space: normal; font-family: var(--font-sans);
  font-size: 12.5px; line-height: 1.35; padding: 9px 10px; }
/* 🔴 ВНУТРИ МОДАЛЬНОГО ОКНА меню встаёт В ПОТОК, а не поверх содержимого.
   У `.modal` стоит `overflow: auto` (стили окна инлайновые, их ставит ui.js),
   и абсолютное меню обрезается рамкой окна: из семи вариантов видно два, а
   остальные просто отрезаны. Видно ТОЛЬКО на снимке экрана — ни ошибки, ни
   предупреждения. В потоке окно само прокручивается к списку. */
/* ⚠️ И сама обёртка становится КОЛОНКОЙ: .kg-select — это flex-строка, и
   меню, вставшее в поток, иначе оказывается ВТОРЫМ элементом ряда, то есть
   сбоку от кнопки. Тоже видно только на снимке. */
.modal .kg-select.is-field { flex-direction: column; align-items: stretch; }
.modal .kg-select.is-field .kg-select-menu { position: static; margin-top: 6px;
  max-height: 260px; box-shadow: none; }

/* ── Бейджи (плашка с тонированной подложкой, dev-портал) ── */
.badge { --c: var(--kg-ui-steel); display: inline-flex; align-items: center; gap: 5px; height: 22px; padding: 0 8px; border-radius: 7px;
  font-family: var(--font-mono); font-size: 9.5px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; white-space: nowrap;
  color: hsl(var(--c)); background: color-mix(in srgb, hsl(var(--c)) calc(var(--kg-ui-fill) * 100%), hsl(var(--card))); border: 1px solid hsl(var(--c) / .52); }
.badge.b-mint { --c: var(--kg-ui-mint); } .badge.b-blue { --c: var(--kg-ui-blue); } .badge.b-yellow { --c: var(--kg-ui-yellow); }
.badge.b-orange { --c: var(--kg-ui-orange); } .badge.b-red { --c: var(--kg-ui-red); } .badge.b-purple { --c: var(--kg-ui-purple); }
.badge.b-teal { --c: var(--kg-ui-teal); } .badge.b-steel { --c: var(--kg-ui-steel); }
.badge.b-muted { --c: var(--muted-foreground); background: transparent; }
.badge.b-solid { background: hsl(var(--c)); color: hsl(var(--card)); border-color: transparent; }

/* ── Карточки, таблицы ── */
.card { background: hsl(var(--card)); border: 1px solid hsl(var(--border)); border-radius: 16px; }
.card-h { display: flex; align-items: center; gap: 10px; padding: 14px 18px; border-bottom: 1px solid hsl(var(--border)); }
.card-h b { font-size: 13px; font-weight: 700; }
.card-h .sub { font-family: var(--font-mono); font-size: 10.5px; color: hsl(var(--muted-foreground)); }
.card-h .right { margin-left: auto; display: flex; gap: 8px; align-items: center; }
.card-b { padding: 16px 18px; }
.tbl-wrap { border: 1px solid hsl(var(--border)); border-radius: 16px; overflow: hidden; background: hsl(var(--card)); }
.tbl-scroll { overflow-x: auto; }
.tbl { width: 100%; border-collapse: collapse; }
.tbl th { padding: 12px 18px; text-align: left; font-family: var(--font-mono); font-size: 10.5px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase;
  color: hsl(var(--muted-foreground)); background: hsl(var(--background)); border-bottom: 1px solid hsl(var(--border)); white-space: nowrap; }
.tbl td { padding: 11px 18px; border-bottom: 1px solid hsl(var(--border) / .5); vertical-align: middle; }
.tbl tbody tr:last-child td { border-bottom: none; }
.tbl tbody tr:hover { background: hsl(var(--secondary) / .45); }
.tbl-name { font-family: var(--font-mono); font-size: 13px; font-weight: 600; }
.tbl-meta { font-family: var(--font-mono); font-size: 12px; color: hsl(var(--muted-foreground)); font-variant-numeric: tabular-nums; }
/* Вторая строка в ячейке: подробность под названием (текст уведомления в
   карточке человека). Отдельным классом, а не <i>, — курсив здесь читается
   хуже, а перенос нужен. */
.tbl-sub { display: block; font-size: 11.5px; line-height: 1.4; color: hsl(var(--muted-foreground)); margin-top: 2px; }
.tbl-actions { display: inline-flex; gap: 6px; justify-content: flex-end; }
/* Подвал длинного списка: кнопка «Показать ещё» по центру, под ней сколько
   показано. По центру, а не у края, потому что нажимают её подряд много раз —
   курсор не должен гулять по ширине экрана. */
.tbl-more { display: flex; flex-direction: column; align-items: center; gap: 6px; margin-top: 14px; }
.tbl-more .muted { font-family: var(--font-mono); font-size: 11px; }
.td-r { text-align: right; }
.tbl-btn { height: 30px; min-width: 30px; border-radius: 8px; padding: 0 9px; font-family: var(--font-mono); font-size: 10.5px; font-weight: 600;
  border: 1px solid hsl(var(--border)); background: transparent; color: hsl(var(--muted-foreground)); display: inline-flex; align-items: center; justify-content: center; gap: 4px; }
.tbl-btn:hover { border-color: hsl(var(--muted-foreground)); color: hsl(var(--foreground)); background: hsl(var(--background)); }
.tbl-btn.tbl-claude { color: hsl(var(--primary)); border-color: hsl(var(--primary) / .4); }
.tbl-btn.tbl-claude:hover { background: hsl(var(--primary)); color: hsl(var(--primary-foreground)); border-color: hsl(var(--primary)); }
.tbl-btn.tbl-danger { color: hsl(var(--destructive)); border-color: hsl(var(--destructive) / .4); }
.tbl-btn.tbl-danger:hover { background: hsl(var(--destructive) / .1); }
.avatar-sm { width: 26px; height: 26px; border-radius: 50%; display: inline-grid; place-items: center; font-family: var(--font-mono); font-size: 10px; font-weight: 700;
  color: #fff; background: linear-gradient(135deg, hsl(var(--kg-ui-steel)), hsl(var(--kg-ui-blue))); flex-shrink: 0; }
.user-cell { display: flex; align-items: center; gap: 10px; }
.user-cell b { font-weight: 600; font-size: 13px; display: block; }
.user-cell i { font-style: normal; font-family: var(--font-mono); font-size: 10.5px; color: hsl(var(--muted-foreground)); }
.chips { display: flex; flex-wrap: wrap; gap: 5px; }
/* Части урока: те же плашки, но кликабельные — текущая подсвечена мятным. */
#vparts .badge { cursor: pointer; text-transform: none; letter-spacing: .02em; font-size: 10.5px; height: 26px; }
#vparts .badge:hover { filter: brightness(1.12); }
.progress { height: 6px; border-radius: 999px; background: hsl(var(--secondary)); overflow: hidden; }
.progress i { display: block; height: 100%; background: hsl(var(--primary)); border-radius: 999px; }

/* ── Главная ── */
.grid-2 { display: grid; grid-template-columns: 1.4fr 1fr; gap: 16px; align-items: start; }
/* ⚠️ Ровные половины — для РАВНОЗНАЧНЫХ карточек (правка владельца 15.09
   «сделай графики одинаковой ширины»). Обычный .grid-2 намеренно кривой,
   1.4fr на 1fr: он собран под пару «главное слева, второстепенное справа».
   Два графика за один и тот же период равнозначны, и разная ширина колонок
   молча врёт — столбики одного месяца выходят разной толщины, и глаз читает
   это как разницу в данных. */
.grid-even { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 16px; align-items: start; }
.grid-3 { display: grid; grid-template-columns: repeat(3, minmax(0,1fr)); gap: 14px; }
/* Карточка-приоритет — паттерн mark-портала: тёплый градиент, оранжевый
   кикер с точкой, действия чипами внизу. Единственное место с заливкой. */
.prios { display: grid; grid-template-columns: repeat(auto-fit, minmax(330px, 1fr)); gap: 16px; margin-bottom: 16px; }
.prio { display: flex; flex-direction: column; gap: 9px; padding: 18px 20px; border-radius: 16px;
  border: 1px solid hsl(var(--primary) / .30);
  background: linear-gradient(135deg, hsl(var(--accent)) 0%, hsl(var(--card)) 62%); }
.prio .kicker { display: flex; align-items: center; gap: 7px; font-family: var(--font-mono); font-size: 9.5px; font-weight: 700;
  letter-spacing: .12em; text-transform: uppercase; color: hsl(var(--primary)); }
.prio .kicker::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: hsl(var(--primary)); flex-shrink: 0; }
.prio h2 { margin: 0; font-size: 19px; font-weight: 800; letter-spacing: -.015em; line-height: 1.25; text-wrap: balance; }
.prio .sub { margin: 0; color: hsl(var(--muted-foreground)); font-size: 12.5px; line-height: 1.5; }
.prio .acts { display: flex; flex-wrap: wrap; gap: 8px; margin-top: auto; padding-top: 5px; }
.prio .btn-ghost { background: hsl(var(--card) / .7); }
.prog-row { display: flex; align-items: center; gap: 14px; padding: 12px 18px; border-bottom: 1px solid hsl(var(--border) / .6); }
.prog-row:last-child { border-bottom: 0; }
.prog-ic { width: 38px; height: 38px; border-radius: 10px; display: grid; place-items: center; font-family: var(--font-mono); font-weight: 800; font-size: 11px; flex-shrink: 0;
  background: hsl(var(--c) / var(--kg-ui-fill)); color: hsl(var(--c)); border: 1px solid hsl(var(--c) / .4); --c: var(--kg-ui-steel); }
.prog-row .t { flex: 1; min-width: 0; }
.prog-row .t b { display: block; font-size: 13.5px; font-weight: 700; }
.prog-row .t i { font-style: normal; font-family: var(--font-mono); font-size: 10.5px; color: hsl(var(--muted-foreground)); }
/* Архивная программа (снята с продажи, доступ остаётся) — приглушена, но
   не спрятана: курс открывается тем же кликом. Разделитель над блоком
   объясняет, почему она стоит отдельно, — плашка «архив» одна этого не
   говорит. */
.row-sep { padding: 9px 18px 7px; border-top: 1px solid hsl(var(--border));
  background: hsl(var(--secondary) / .45);
  font-family: var(--font-mono); font-size: 10px; letter-spacing: .04em;
  color: hsl(var(--muted-foreground)); }
.prog-row.is-arch .prog-ic { opacity: .8; }
.prog-row.is-arch .t b { font-weight: 600; color: hsl(var(--muted-foreground)); }
.prog-row.is-arch:hover .t b { color: hsl(var(--foreground)); }
.prog-row .pr { width: 120px; }
.prog-row .pr .progress { margin-top: 5px; }
.prog-row .pr i { font-style: normal; font-family: var(--font-mono); font-size: 10.5px; color: hsl(var(--muted-foreground)); }
.hw-row { display: grid; grid-template-columns: 1fr auto auto; gap: 12px; align-items: center; padding: 11px 18px; border-bottom: 1px solid hsl(var(--border) / .6); }
.hw-row:last-child { border-bottom: 0; }
.hw-row b { font-size: 13px; font-weight: 600; display: block; }
.hw-row i { font-style: normal; font-family: var(--font-mono); font-size: 10.5px; color: hsl(var(--muted-foreground)); }
.hw-due { font-family: var(--font-mono); font-size: 11px; font-variant-numeric: tabular-nums; color: hsl(var(--muted-foreground)); white-space: nowrap; }
.hw-due.soon { color: hsl(var(--kg-ui-orange)); font-weight: 700; }
.feed { padding: 6px 18px 12px; }
.feed-it { display: grid; grid-template-columns: 58px 1fr; gap: 10px; padding: 8px 0; border-bottom: 1px dashed hsl(var(--border)); font-size: 12.5px; }
.feed-it:last-child { border-bottom: 0; }
.feed-it i { font-style: normal; font-family: var(--font-mono); font-size: 10.5px; color: hsl(var(--muted-foreground)); padding-top: 2px; }

/* ── Программы (каталог) ── */
.pcards { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 14px; }
.pcard { display: flex; flex-direction: column; gap: 12px; padding: 18px; border-radius: 16px; background: hsl(var(--card)); border: 1px solid hsl(var(--border)); position: relative; }
.pcard.is-locked { background: hsl(var(--card) / .55); }
.pcard.is-locked .pcard-title, .pcard.is-locked .pcard-desc { opacity: .75; }
.pcard-top { display: flex; align-items: center; gap: 10px; }
.pcard-top .prog-ic { width: 42px; height: 42px; font-size: 12px; }
.pcard-title { font-size: 15px; font-weight: 800; letter-spacing: -.01em; line-height: 1.2; }
.pcard-kind { font-family: var(--font-mono); font-size: 10px; color: hsl(var(--muted-foreground)); letter-spacing: .05em; text-transform: uppercase; margin-top: 3px; }
.pcard-desc { color: hsl(var(--muted-foreground)); font-size: 12.5px; line-height: 1.5; margin: 0; }
.pcard-meta { display: flex; gap: 14px; font-family: var(--font-mono); font-size: 10.5px; color: hsl(var(--muted-foreground)); font-variant-numeric: tabular-nums; }
.pcard-foot { display: flex; align-items: center; gap: 8px; margin-top: auto; padding-top: 6px; }
.pcard-foot .price { margin-left: auto; font-family: var(--font-mono); font-size: 12px; font-weight: 700; font-variant-numeric: tabular-nums; }
.pcard .progress { margin-top: -4px; }

/* ── Программа: хаб (kb-layout vibe-portal) ── */
.kb-layout { display: grid; grid-template-columns: 234px minmax(0,1fr); gap: 20px; align-items: start; }
/* Липкая колонка обязана СВОЙ список и прокручивать: у «Профессии» с блоками
   и у PRO модулей больше, чем влезает в экран, а `overflow: hidden` без
   ограничения высоты просто обрезал хвост списка — нижние модули были
   недостижимы ни колесом, ни клавишами. Формула та же, что у `.reader-nav`. */
.kb-side { border: 1px solid hsl(var(--border)); background: hsl(var(--card)); border-radius: 14px; position: sticky; top: 0;
  max-height: calc(100vh - var(--kg-navbar-h) - 78px); overflow-y: auto; overscroll-behavior: contain; }
.kb-group-h { padding: 12px 13px 6px; font-family: var(--font-mono); font-size: 9.5px; letter-spacing: .12em; text-transform: uppercase; color: hsl(var(--muted-foreground)); }
.kb-group-h:not(:first-child) { border-top: 1px solid hsl(var(--border)); margin-top: 4px; }
.kb-item { display: flex; align-items: center; gap: 9px; padding: 8px 13px; cursor: pointer; font-size: 12.5px; color: hsl(var(--muted-foreground)); border-left: 2px solid transparent; width: 100%; background: none; border-top: 0; border-right: 0; border-bottom: 0; text-align: left; }
.kb-item:hover { background: hsl(var(--secondary) / .6); }
.kb-item.active { color: hsl(var(--foreground)); border-left-color: hsl(var(--primary)); background: hsl(var(--accent)); font-weight: 600; }
.kb-item .n { margin-left: auto; font-family: var(--font-mono); font-size: 10.5px; opacity: .6; font-variant-numeric: tabular-nums; }
.kb-item .st { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; border: 1.5px solid hsl(var(--border)); }
.kb-item .st.done { background: hsl(var(--kg-ui-mint)); border-color: hsl(var(--kg-ui-mint)); }
.kb-item .st.cur { border-color: hsl(var(--primary)); background: hsl(var(--primary) / .35); }
.kb-item.lesson { padding-left: 24px; font-size: 12px; }
.kb-mod { display: flex; align-items: center; gap: 9px; padding: 9px 13px 5px; font-size: 12.5px; font-weight: 700; }
.kb-mod i { font-style: normal; font-family: var(--font-mono); font-size: 10px; color: hsl(var(--muted-foreground)); margin-left: auto; }
.kb-foot { padding: 12px 13px; border-top: 1px solid hsl(var(--border)); }
.kb-foot .progress { margin-top: 6px; }
.kb-foot i { font-style: normal; font-family: var(--font-mono); font-size: 10.5px; color: hsl(var(--muted-foreground)); }

/* Урок */
.lesson-head { display: flex; align-items: flex-start; gap: 14px; margin-bottom: 14px; }
.lesson-head .kicker { font-family: var(--font-mono); font-size: 10px; letter-spacing: .1em; text-transform: uppercase; color: hsl(var(--muted-foreground)); margin-bottom: 6px; }
.lesson-head h1 { margin: 0; font-size: 22px; font-weight: 800; letter-spacing: -.015em; text-wrap: balance; }
.lesson-head .acts { margin-left: auto; display: flex; gap: 8px; flex-shrink: 0; }
.video { aspect-ratio: 16 / 9; border-radius: 16px; background: hsl(220 18% 9%); position: relative; overflow: hidden; border: 1px solid hsl(var(--border)); display: grid; place-items: center; color: rgba(255,255,255,.85); }
/* Части урока идут стопкой, а не переключателем: у «корпоративного портала»
   их девять, и чипы под одним плеером читались как оглавление к чему-то
   другому — было не видно, что это и есть весь урок (правка владельца 10.09). */
.vpart { margin: 18px 0; }
.vpart-h { display: flex; align-items: baseline; gap: 10px; margin: 0 2px 8px; }
.vpart-h b { font-size: 13.5px; font-weight: 600; }
.vpart-h i { font-family: var(--font-mono); font-size: 11px; font-style: normal;
  color: hsl(var(--muted-foreground)); }
.video .play { width: 64px; height: 64px; border-radius: 50%; background: hsl(var(--primary)); display: grid; place-items: center; box-shadow: 0 12px 30px -8px hsl(var(--primary) / .6); }
.video .vmeta { position: absolute; left: 16px; bottom: 14px; display: flex; gap: 10px; align-items: center; font-family: var(--font-mono); font-size: 10.5px; letter-spacing: .05em; }
.video .vprov { position: absolute; right: 14px; top: 12px; font-family: var(--font-mono); font-size: 9.5px; letter-spacing: .1em; text-transform: uppercase; opacity: .6; }
.video .vbar { position: absolute; left: 0; right: 0; bottom: 0; height: 4px; background: rgba(255,255,255,.15); }
.video .vbar i { display: block; height: 100%; width: 34%; background: hsl(var(--primary)); }
.tabs { display: flex; gap: 2px; border-bottom: 1px solid hsl(var(--border)); margin: 20px 0 18px; }
.tab { padding: 9px 12px; background: none; border: 0; border-bottom: 2px solid transparent; margin-bottom: -1px; font-weight: 600; font-size: 12.5px; color: hsl(var(--muted-foreground)); display: inline-flex; gap: 7px; align-items: center; }
.tab .n { font-family: var(--font-mono); font-size: 10px; padding: 1px 6px; border-radius: 6px; background: hsl(var(--secondary)); }
.tab.on { color: hsl(var(--foreground)); border-bottom-color: hsl(var(--primary)); }
.lesson-note { max-width: 70ch; }
.lesson-note > p { font-size: 13.5px; line-height: 1.66; margin: 0 0 14px; }
.lesson-note em { font-style: normal; color: hsl(var(--primary)); }
.note-h2 { display: flex; align-items: center; gap: 10px; font-family: var(--font-mono); font-size: 14px; font-weight: 700; margin: 28px 0 12px; padding-bottom: 9px; border-bottom: 1px solid hsl(var(--border)); }
.note-h2-no { width: 22px; height: 22px; display: inline-flex; align-items: center; justify-content: center; font-size: 11px; border-radius: 6px; background: hsl(var(--primary) / .14); color: hsl(var(--primary)); border: 1px solid hsl(var(--primary) / .4); }
.note-callout { display: flex; gap: 12px; margin: 16px 0; padding: 13px 15px; border-radius: 12px; background: hsl(var(--card)); border: 1px solid hsl(var(--border)); border-left-width: 3px; }
.note-callout.is-key { border-left-color: hsl(var(--primary)); } .note-callout.is-key .lbl { color: hsl(var(--primary)); }
.note-callout.is-tip { border-left-color: hsl(var(--kg-ui-mint)); } .note-callout.is-tip .lbl { color: hsl(var(--kg-ui-mint)); }
.note-callout .lbl { display: block; font-family: var(--font-mono); font-size: 9.5px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; margin-bottom: 4px; }
.note-callout .body { font-size: 12.5px; line-height: 1.6; }
.note-steps { margin: 12px 0; padding: 0; list-style: none; counter-reset: st; }
.note-steps li { position: relative; counter-increment: st; padding: 7px 0 7px 38px; font-size: 13px; line-height: 1.55; border-bottom: 1px solid hsl(var(--border)); }
.note-steps li:last-child { border-bottom: 0; }
.note-steps li::before { content: counter(st); position: absolute; left: 0; top: 7px; width: 21px; height: 21px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center;
  font-family: var(--font-mono); font-size: 10.5px; font-weight: 700; background: hsl(var(--secondary)); color: hsl(var(--muted-foreground)); border: 1px solid hsl(var(--border)); }
.kbd { font-family: var(--font-mono); font-size: 11.5px; padding: 1px 6px; border-radius: 5px; background: hsl(var(--secondary)); border: 1px solid hsl(var(--border)); white-space: nowrap; }
.mat-list { display: grid; gap: 8px; max-width: 720px; }
/* База знаний: карточки в несколько колонок, сгруппированные по разделам. */
.mat-grid { max-width: none; grid-template-columns: repeat(auto-fill, minmax(330px, 1fr)); }
.mat-group { font-family: var(--font-mono); font-size: 10.5px; letter-spacing: .1em; text-transform: uppercase;
  color: hsl(var(--muted-foreground)); margin: 20px 0 8px; }
.mat-group:first-child { margin-top: 0; }
.mat { display: flex; align-items: center; gap: 12px; padding: 11px 14px; border-radius: 12px; background: hsl(var(--card)); border: 1px solid hsl(var(--border)); }
.mat.is-locked { opacity: .55; }
a.mat { text-decoration: none; color: inherit; }
a.mat:hover { border-color: hsl(var(--primary) / .5); background: hsl(var(--accent)); }
a.mat:hover .act { border-color: hsl(var(--primary) / .5); color: hsl(var(--primary)); }
/* Длинное описание не должно распирать карточку — обрезаем строкой. */
.mat .t { min-width: 0; }
.mat .t i { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mat .ic { width: 34px; height: 34px; border-radius: 9px; display: grid; place-items: center; background: hsl(var(--secondary)); color: hsl(var(--muted-foreground)); flex-shrink: 0; }
.mat b { display: block; font-size: 13px; font-weight: 600; }
.mat i { font-style: normal; font-family: var(--font-mono); font-size: 10.5px; color: hsl(var(--muted-foreground)); }
.mat .act { margin-left: auto; }
.hw-box { max-width: 760px; }
.hw-status { display: flex; align-items: center; gap: 10px; padding: 12px 16px; border-radius: 12px; background: hsl(var(--kg-ui-yellow) / .1); border: 1px solid hsl(var(--kg-ui-yellow) / .45); margin-bottom: 14px; font-size: 12.5px; }
.form-label { display: block; font-family: var(--font-mono); font-size: 10.5px; letter-spacing: .08em; text-transform: uppercase; color: hsl(var(--muted-foreground)); margin: 14px 0 6px; }
.form-input, .form-area, .form-select { width: 100%; border-radius: 9px; border: 1px solid hsl(var(--input)); background: hsl(var(--background)); padding: 0 11px; height: 36px; font-size: 13px; }
.form-area { height: auto; min-height: 96px; padding: 9px 11px; resize: vertical; line-height: 1.5; }
.form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.form-hint { font-size: 11.5px; color: hsl(var(--muted-foreground)); margin-top: 6px; }
.seg { display: inline-flex; border: 1px solid hsl(var(--border)); border-radius: 9px; overflow: hidden; background: hsl(var(--background)); }
.seg button { height: 30px; padding: 0 12px; border: 0; background: transparent; font-weight: 600; font-size: 12px; color: hsl(var(--muted-foreground)); }
.seg button.on { background: hsl(var(--card)); color: hsl(var(--foreground)); box-shadow: inset 0 0 0 1px hsl(var(--border)); }

/* ── Админ: контент ── */
.editor-layout { display: grid; grid-template-columns: 300px minmax(0,1fr); gap: 20px; align-items: start; }
.tree { padding: 6px 0; }
.tree-row { display: flex; align-items: center; gap: 8px; padding: 6px 12px; font-size: 12.5px; cursor: pointer; border-radius: 6px; margin: 0 6px;
  /* Строки дерева не выделяются текстом. Иначе выделение, случайно
     оставшееся от двойного клика, перехватывает нажатие: браузер тащит
     ВЫДЕЛЕННЫЙ ТЕКСТ, а не строку, и перетаскивание молча не работает. */
  -webkit-user-select: none; user-select: none; }
.tree-row:hover { background: hsl(var(--secondary) / .6); }
.tree-row.on { background: hsl(var(--accent)); font-weight: 600; }
.tree-row.l1 { padding-left: 28px; } .tree-row.l2 { padding-left: 46px; font-size: 12px; color: hsl(var(--muted-foreground)); }
.tree-row.l2.on { color: hsl(var(--foreground)); }
.tree-row .st { width: 7px; height: 7px; border-radius: 50%; flex-shrink: 0; }
.st.pub { background: hsl(var(--kg-ui-mint)); } .st.draft { background: hsl(var(--kg-ui-yellow)); }
.tree-row .cnt { margin-left: auto; font-family: var(--font-mono); font-size: 10px; opacity: .6; }
/* Перетаскивание модулей и уроков. Курсор «grab» — единственная подсказка,
   что строку можно двигать: значок-ручка сдвинул бы отступы уровней.
   Признак — `data-drag`, а НЕ атрибут `draggable`: перетаскивание сделано на
   событиях указателя (см. wireDnd в views-content.js), потому что HTML5
   drag-and-drop у владельца обрывался в самом браузере. */
.tree-row[data-drag]:hover { cursor: grab; }
.tree-row.dragging { opacity: .4; }
.tree-row.drop-b { box-shadow: inset 0 2px 0 hsl(var(--primary)); }
.tree-row.drop-a { box-shadow: inset 0 -2px 0 hsl(var(--primary)); }
/* Урок брошен на чужой модуль — он переедет внутрь: подсвечиваем весь модуль
   целиком, а не край строки. Полоска сверху или снизу читалась бы как
   «встанет рядом», а урок встаёт ВНУТРЬ. */
.tree-row.drop-in {
  box-shadow: inset 0 0 0 2px hsl(var(--primary));
  background: hsl(var(--primary) / .08);
}
.editor-actions { display: flex; gap: 8px; align-items: center; padding: 14px 18px; border-top: 1px solid hsl(var(--border)); background: hsl(var(--background) / .5); border-radius: 0 0 16px 16px; }
.blocks { display: grid; gap: 10px; }
.block { display: flex; align-items: center; gap: 10px; padding: 10px 12px; border-radius: 10px; border: 1px dashed hsl(var(--border)); background: hsl(var(--background)); font-size: 12.5px; }
.block .drag { color: hsl(var(--muted-foreground)); cursor: grab; }
.block .kind { font-family: var(--font-mono); font-size: 9.5px; letter-spacing: .08em; text-transform: uppercase; color: hsl(var(--muted-foreground)); width: 74px; flex-shrink: 0; }

/* ── Заметки архитектора (правый drawer) ── */
.drawer { position: fixed; top: var(--kg-navbar-h); right: 0; bottom: 0; width: 440px; max-width: 100vw; z-index: 60; background: hsl(var(--card));
  border-left: 1px solid hsl(var(--border)); border-radius: 16px 0 0 16px; box-shadow: -20px 0 50px -30px hsl(0 0% 0% / .5); display: flex; flex-direction: column;
  transform: translateX(105%); transition: transform .22s ease; }
.drawer.open { transform: none; }
.drawer-h { display: flex; align-items: center; gap: 10px; padding: 14px 18px; border-bottom: 1px solid hsl(var(--border)); }
.drawer-h b { font-size: 13px; }
.drawer-b { overflow-y: auto; padding: 6px 18px 30px; font-size: 12.5px; line-height: 1.55; }
.drawer-b h4 { font-family: var(--font-mono); font-size: 10.5px; letter-spacing: .1em; text-transform: uppercase; color: hsl(var(--primary)); margin: 20px 0 8px; }
.drawer-b ul { margin: 0; padding-left: 18px; } .drawer-b li { margin: 4px 0; }
.drawer-b code { font-family: var(--font-mono); font-size: 11px; background: hsl(var(--secondary)); padding: 1px 5px; border-radius: 4px; }
.ent { border: 1px solid hsl(var(--border)); border-radius: 10px; padding: 10px 12px; margin: 8px 0; background: hsl(var(--background)); }
.ent b { font-family: var(--font-mono); font-size: 11.5px; }
.ent i { display: block; font-style: normal; color: hsl(var(--muted-foreground)); font-size: 11.5px; margin-top: 3px; }

/* ── Панель управления прототипом ── */
.proto { position: fixed; left: 50%; bottom: 14px; transform: translateX(-50%); z-index: 70; display: flex; align-items: center; gap: 8px; padding: 8px 10px 8px 12px;
  border-radius: 14px; background: hsl(var(--foreground)); color: hsl(var(--background)); box-shadow: 0 16px 40px -18px hsl(0 0% 0% / .7); white-space: nowrap; max-width: calc(100vw - 40px); overflow-x: auto; scrollbar-width: none; }
.proto::-webkit-scrollbar { display: none; }
.proto .lbl { font-family: var(--font-mono); font-size: 9px; font-weight: 800; letter-spacing: .14em; color: hsl(var(--primary)); margin-right: 4px; }
.proto .sep { width: 1px; height: 20px; background: hsl(var(--background) / .2); margin: 0 4px; }
.proto .grp { display: inline-flex; align-items: center; gap: 4px; }
.proto .gl { font-family: var(--font-mono); font-size: 9.5px; letter-spacing: .06em; text-transform: uppercase; opacity: .55; margin-right: 4px; }
.proto button { height: 26px; padding: 0 10px; border-radius: 8px; border: 1px solid hsl(var(--background) / .25); background: transparent; color: hsl(var(--background)); font-size: 11.5px; font-weight: 600; }
.proto button.on { background: hsl(var(--primary)); border-color: hsl(var(--primary)); color: #fff; }
.proto button:hover:not(.on) { background: hsl(var(--background) / .12); }
.proto .ent-btn { padding: 0 8px; font-family: var(--font-mono); font-size: 10.5px; }

.empty { padding: 34px; text-align: center; border: 1px dashed hsl(var(--border)); border-radius: 14px; color: hsl(var(--muted-foreground)); font-size: 12.5px; }
.kpis { display: grid; grid-template-columns: repeat(4, minmax(0,1fr)); gap: 12px; margin-bottom: 18px; }
.kpi { padding: 14px 16px; border-radius: 14px; background: hsl(var(--card)); border: 1px solid hsl(var(--border)); }
.kpi .v { font-family: var(--font-mono); font-size: 24px; font-weight: 700; letter-spacing: -.02em; font-variant-numeric: tabular-nums; line-height: 1.1; }
.kpi .l { font-family: var(--font-mono); font-size: 10px; letter-spacing: .08em; text-transform: uppercase; color: hsl(var(--muted-foreground)); margin-top: 6px; }
.kpi .d { font-size: 11.5px; margin-top: 4px; color: hsl(var(--muted-foreground)); }
/* Зелёным — только то, что действительно рост: прибавка людей и выручка.
   Подпись периода тем же цветом читалась бы как хорошая новость ни о чём. */
.kpi .d.is-up { color: hsl(var(--kg-ui-mint)); }
.kpis.is-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }

/* ── Столбики по месяцам (панель администратора) ────────────────────────────
   Рисуются разметкой: шесть значений не стоят чужой библиотеки в критическом
   пути кабинета. Высота столбика — процент от максимума, остальное делает CSS.
   Одна величина на график — поэтому легенды нет: её называет заголовок
   карточки, а двух шкал на одной картинке не бывает вовсе. */
.bars { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr);
  gap: 10px; align-items: end; height: 190px; }
/* ⚠️ Только внутри `.bars`: голый `.bar` цеплял полоску прогресса в шапке
   программы (`.phero-st .bar`) — flex с центровкой ставил её заливку
   посередине дорожки (поймано владельцем 28.09). */
.bars > .bar { display: flex; flex-direction: column; align-items: center; justify-content: flex-end;
  height: 100%; min-width: 0; gap: 6px; }
/* Значение над столбиком: столбиков шесть, и смотрят на них ради чисел.
   На графике с десятками точек так делать нельзя — там подписывают выборочно. */
.bar .bv { font-family: var(--font-mono); font-size: 10.5px; font-weight: 700;
  font-variant-numeric: tabular-nums; color: hsl(var(--foreground));
  white-space: nowrap; }
.bar.is-zero .bv { color: hsl(var(--muted-foreground) / .7); font-weight: 500; }
/* Дорожка задаёт общую высоту, столбик растёт от базовой линии вверх. */
.bar .bt { flex: 1; width: 100%; max-width: 46px; display: flex; align-items: flex-end;
  border-bottom: 1px solid hsl(var(--border)); }
.bar .bt i { display: block; width: 100%; border-radius: 4px 4px 0 0;
  background: hsl(var(--c) / .85); transition: height .2s; }
.bar:hover .bt i { background: hsl(var(--c)); }
.bar .bl { font-family: var(--font-mono); font-size: 10px; letter-spacing: .04em;
  color: hsl(var(--muted-foreground)); }

/* Входы в разделы админки — с 24.09 (правка владельца «Админ слева уберём,
   перенесём в кнопку») у admin в левом меню своего блока больше нет вовсе,
   и это единственный путь к разделам, не считая кнопки «Админ» в шапке.
   ⚠️ Здесь ДВАЖДЫ стояла раскладка «фиксированные три ряда, лишнее — вбок
   горизонтальной прокруткой»: сначала на `@media` по ширине ЭКРАНА, потом на
   `@container` по ширине КАРТОЧКИ — и оба раза владелец ловил её обрезающей
   разделы (24.09, дважды подряд, со скриншотами). Причина в самой идее:
   пяти столбцам нужно ровно 1140 px, а порог включения стоял ниже — и в
   промежутке (включилось, но пяти столбцов ещё не влезло) край обрезался.
   Список только растёт, и у растущего списка это будет промежутком ПОЧТИ
   ЛЮБОЙ ширины. Поэтому раскладка — просто обёртывающаяся сетка, без
   спецрежима: сколько столбцов помещается, столько и есть, лишнее уходит
   вниз новой строкой. На широком экране столбцов и так набирается 5-6, а
   три ряда получаются сами — без горизонтальной прокрутки и без обрезанного
   края ни на одной ширине. */
.adm-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 10px; }
/* Название в одну строку без пояснения (правка владельца 24.09) — значит и
   плитка компактная, одной строки высотой, а не карточка с иконкой под неё
   подогнанной. Иконка уменьшена вместе с плиткой, а не оставлена прежнего
   размера — иначе она бы одна держала прежнюю высоту. */
.adm-tile { display: flex; align-items: center; gap: 9px; padding: 9px 12px;
  border: 1px solid hsl(var(--border)); border-radius: 10px; background: hsl(var(--background));
  color: inherit; text-decoration: none; transition: border-color .15s, background .15s; }
.adm-tile:hover { border-color: hsl(var(--primary) / .55); background: hsl(var(--card)); }
.adm-ic { width: 26px; height: 26px; flex-shrink: 0; border-radius: 8px; display: grid;
  place-items: center; color: hsl(var(--primary)); background: hsl(var(--primary) / .1); }
.adm-tile b { font-size: 13px; font-weight: 650; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* ⚠️ Модификатор специфичнее общего правила, поэтому мобильные раскладки
   .kpis его НЕ перебьют — колонки надо снимать здесь же, иначе на телефоне
   останутся три колонки по 90 px. */
@media (max-width: 1100px) { .kpis.is-3 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 640px) {
  .bars { height: 150px; gap: 5px; }
  .bar .bv { font-size: 9px; }
  .bar .bl { font-size: 9px; }
}

.review-layout { display: grid; grid-template-columns: minmax(0,1.1fr) minmax(0,.9fr); gap: 16px; align-items: start; }
.subm { padding: 14px 16px; border-radius: 12px; background: hsl(var(--background)); border: 1px solid hsl(var(--border)); font-size: 12.5px; line-height: 1.55; }
.subm .who { font-family: var(--font-mono); font-size: 10px; letter-spacing: .08em; text-transform: uppercase; color: hsl(var(--muted-foreground)); margin-bottom: 6px; }

/* ══════════ ПУБЛИЧНАЯ ЧАСТЬ (до входа) ══════════ */
body.guest { padding-left: 0; overflow-y: auto; }
body.guest .kg-sidebar, body.guest > .navbar, body.guest .main-layout, body.guest .drawer { display: none; }
#public { display: none; }
body.guest #public { display: block; }
.pub-head { position: sticky; top: 0; z-index: 30; height: 62px; display: flex; align-items: center; gap: 22px;
  padding: 0 26px; background: hsl(var(--card)); border-bottom: 1px solid hsl(var(--border)); }
.pub-brand { display: flex; align-items: center; gap: 10px; text-decoration: none; flex-shrink: 0; }
/* ⚠️ Размеры логотипа в гостевой шапке живут в `marketing.css`, блок «ШАПКА
   САЙТА», а не здесь: гостевая часть всегда идёт под классом `.mkt`, и
   тамошнее правило всё равно перебьёт это по весу селектора. Эти две строки
   остаются только как запасной вид без marketing.css. */
.pub-brand b { font-size: 14px; font-weight: 700; display: block; line-height: 1.1; }
.pub-brand i { font-style: normal; font-size: 9.5px; font-family: var(--font-mono); letter-spacing: .06em; text-transform: uppercase; color: hsl(var(--muted-foreground)); }
.pub-nav { display: flex; gap: 4px; margin-left: 10px; }
.pub-nav button { background: none; border: 0; padding: 8px 10px; border-radius: 8px; font-size: 13px; font-weight: 500; color: hsl(var(--muted-foreground)); }
.pub-nav button:hover { background: hsl(var(--secondary)); color: hsl(var(--foreground)); }
.pub-nav button.on { color: hsl(var(--foreground)); font-weight: 600; }
.pub-wrap { max-width: 1080px; margin: 0 auto; padding: 34px 26px 70px; }
.pub-hero { display: grid; grid-template-columns: 1.25fr .95fr; gap: 26px; align-items: center; padding: 30px 32px; border-radius: 20px;
  border: 1px solid hsl(var(--primary) / .28); background: linear-gradient(135deg, hsl(var(--accent)) 0%, hsl(var(--card)) 64%); margin-bottom: 28px; }
.pub-hero .kicker { display: flex; align-items: center; gap: 7px; font-family: var(--font-mono); font-size: 10px; font-weight: 700;
  letter-spacing: .12em; text-transform: uppercase; color: hsl(var(--primary)); margin-bottom: 12px; }
.pub-hero .kicker::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: hsl(var(--primary)); }
.pub-hero h1 { margin: 0 0 12px; font-size: 32px; line-height: 1.15; font-weight: 800; letter-spacing: -.025em; text-wrap: balance; }
.pub-hero p { margin: 0 0 18px; color: hsl(var(--muted-foreground)); font-size: 14px; line-height: 1.6; max-width: 52ch; }
.pub-hero .acts { display: flex; gap: 10px; flex-wrap: wrap; }
.pub-hero .btn { height: 38px; padding: 0 18px; font-size: 13px; }
.pub-facts { display: grid; gap: 10px; }
.pub-fact { display: flex; align-items: baseline; gap: 12px; padding: 11px 14px; border-radius: 12px; background: hsl(var(--card) / .75); border: 1px solid hsl(var(--border)); }
.pub-fact b { font-family: var(--font-mono); font-size: 18px; font-weight: 700; font-variant-numeric: tabular-nums; min-width: 72px; }
.pub-fact span { font-size: 12.5px; color: hsl(var(--muted-foreground)); line-height: 1.4; }
.pub-h2 { display: flex; align-items: baseline; gap: 12px; margin: 34px 0 14px; }
.pub-h2 h2 { margin: 0; font-size: 19px; font-weight: 800; letter-spacing: -.015em; }
.pub-h2 span { font-size: 12.5px; color: hsl(var(--muted-foreground)); }
.pub-h2 a { margin-left: auto; font-size: 12.5px; color: hsl(var(--primary)); text-decoration: none; }
.pub-foot { border-top: 1px solid hsl(var(--border)); background: hsl(var(--card)); padding: 26px; }
.pub-foot-in { max-width: 1080px; margin: 0 auto; display: grid; grid-template-columns: 1.2fr 1fr 1fr; gap: 24px; }
.pub-foot h4 { margin: 0 0 10px; font-family: var(--font-mono); font-size: 9.5px; letter-spacing: .1em; text-transform: uppercase; color: hsl(var(--muted-foreground)); font-weight: 700; }
.pub-foot a { display: block; padding: 3px 0; font-size: 12.5px; color: hsl(var(--foreground)); text-decoration: none; }
.pub-foot a:hover { color: hsl(var(--primary)); }
.pub-foot .req { font-size: 11.5px; color: hsl(var(--muted-foreground)); line-height: 1.65; }
.pub-foot .req b { display: block; color: hsl(var(--foreground)); font-size: 12.5px; margin-bottom: 4px; }
.cookie-bar { position: fixed; left: 18px; right: 18px; bottom: 76px; z-index: 65; max-width: 780px; margin: 0 auto;
  display: flex; align-items: center; gap: 14px; padding: 13px 16px; border-radius: 14px;
  background: hsl(var(--card)); border: 1px solid hsl(var(--border)); box-shadow: 0 18px 44px -22px hsl(0 0% 0% / .55); font-size: 12.5px; }
.cookie-bar a { color: hsl(var(--primary)); }
body:not(.guest) .cookie-bar, .cookie-bar[hidden] { display: none; }
/* Плашка висит НАД содержимым: пока её не закрыли, низ страницы ею накрыт.
   На оформлении заказа под ней оказывались галочки согласий. Класс ставит
   wireShell() в views-public.js, снимает — кнопка «Понятно». */
body.guest.has-cookiebar { padding-bottom: 104px; }

/* Форма входа и регистрации */
.auth-card { max-width: 460px; margin: 0 auto; padding: 26px 28px 24px; border-radius: 18px; background: hsl(var(--card)); border: 1px solid hsl(var(--border)); }
.auth-card h1 { margin: 0 0 6px; font-size: 22px; font-weight: 800; letter-spacing: -.02em; }
.auth-card .lead { margin: 0 0 18px; color: hsl(var(--muted-foreground)); font-size: 12.5px; line-height: 1.55; }
.auth-card .btn { width: 100%; justify-content: center; height: 40px; margin-top: 18px; }
/* Разделитель между входом по паролю и входом через Битрикс24. Нужен именно
   он, а не отступ: две кнопки подряд читаются как «сначала одна, потом
   другая», а это два РАВНОЗНАЧНЫХ способа войти. */
.auth-or { display: flex; align-items: center; gap: 10px; margin-top: 18px;
  font-family: var(--font-mono); font-size: 10.5px; text-transform: uppercase;
  letter-spacing: .08em; color: hsl(var(--muted-foreground)); }
.auth-or::before, .auth-or::after { content: ''; flex: 1; height: 1px; background: hsl(var(--border)); }
/* Кнопка сразу под разделителем — отступ уже дал он (у .auth-card .btn он 18px). */
.auth-or + .btn { margin-top: 12px; }
.auth-alt { margin-top: 14px; text-align: center; font-size: 12.5px; color: hsl(var(--muted-foreground)); }
.auth-alt button { background: none; border: 0; color: hsl(var(--primary)); font-weight: 600; padding: 0; }
.consent { display: flex; gap: 9px; align-items: flex-start; margin-top: 12px; font-size: 12px; line-height: 1.5; color: hsl(var(--muted-foreground)); }
.consent input { margin: 2px 0 0; width: 15px; height: 15px; accent-color: hsl(var(--primary)); flex-shrink: 0; }
.consent a { color: hsl(var(--primary)); }

/* Юридический документ */
/* Ссылка внутри текста должна выглядеть ссылкой — здесь подчёркивание к месту. */
#legal-body a, .article .art-body p > a, .article .art-body li > a,
.article .art-body td > a {
  color: hsl(var(--primary)); text-decoration: underline; text-underline-offset: 2px;
}
.legal-layout { display: grid; grid-template-columns: 250px minmax(0,1fr); gap: 26px; align-items: start; }
.legal-toc { position: sticky; top: 78px; border: 1px solid hsl(var(--border)); border-radius: 14px; background: hsl(var(--card));
  max-height: calc(100vh - var(--kg-navbar-h) - 96px); overflow-y: auto; overscroll-behavior: contain; }
.legal-toc .h { padding: 12px 14px 8px; font-family: var(--font-mono); font-size: 9.5px; letter-spacing: .1em; text-transform: uppercase; color: hsl(var(--muted-foreground)); }
.legal-toc button { display: block; width: 100%; text-align: left; padding: 7px 14px; background: none; border: 0; border-left: 2px solid transparent; font-size: 12.5px; color: hsl(var(--muted-foreground)); }
.legal-toc button:hover { background: hsl(var(--secondary) / .6); }
.legal-toc button.on { color: hsl(var(--foreground)); border-left-color: hsl(var(--primary)); background: hsl(var(--accent)); font-weight: 600; }
.doc { max-width: 72ch; }
.doc .meta { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; margin-bottom: 14px; }
.doc h1 { margin: 0 0 8px; font-size: 26px; font-weight: 800; letter-spacing: -.02em; text-wrap: balance; }
.doc h2 { margin: 26px 0 10px; font-size: 15px; font-weight: 700; padding-bottom: 7px; border-bottom: 1px solid hsl(var(--border)); }
.doc p { margin: 0 0 12px; font-size: 13.5px; line-height: 1.7; }
.doc .req-box { margin-top: 24px; padding: 14px 16px; border-radius: 12px; background: hsl(var(--secondary) / .55); border: 1px solid hsl(var(--border)); font-size: 12.5px; line-height: 1.7; }

/* ══════════ ПРОГРАММА: ОБЗОР И ТАРИФЫ ══════════ */
.prog-head { display: grid; grid-template-columns: 1fr auto; gap: 20px; align-items: start; padding: 20px 22px; border-radius: 16px;
  border: 1px solid hsl(var(--border)); background: hsl(var(--card)); margin-bottom: 18px; }
.prog-head h1 { margin: 4px 0 8px; font-size: 22px; font-weight: 800; letter-spacing: -.02em; }
.prog-head .lead { margin: 0; color: hsl(var(--muted-foreground)); font-size: 13px; line-height: 1.55; max-width: 62ch; }
.prog-head .side { text-align: right; display: grid; gap: 8px; justify-items: end; }
.tariffs { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 12px; margin-bottom: 18px; }
.tariff { padding: 14px 16px; border-radius: 14px; border: 1px solid hsl(var(--border)); background: hsl(var(--card)); display: flex; flex-direction: column; gap: 7px; }
.tariff.is-mine { border-color: hsl(var(--primary)); background: linear-gradient(150deg, hsl(var(--accent)) 0%, hsl(var(--card)) 70%); }
.tariff .t { font-size: 13.5px; font-weight: 700; display: flex; align-items: center; gap: 7px; }
.tariff .p { font-family: var(--font-mono); font-size: 17px; font-weight: 700; font-variant-numeric: tabular-nums; }
.tariff .d { font-size: 11.5px; color: hsl(var(--muted-foreground)); line-height: 1.45; }
.tariff .btn { margin-top: auto; }
.mod-list { display: grid; gap: 8px; }
.mod { display: grid; grid-template-columns: 34px 1fr auto; gap: 12px; align-items: center; padding: 12px 15px; border-radius: 12px;
  background: hsl(var(--card)); border: 1px solid hsl(var(--border)); }
.mod.is-locked { background: hsl(var(--card) / .5); }
.mod.is-locked .mod-t b { color: hsl(var(--muted-foreground)); }
.mod-no { width: 34px; height: 34px; border-radius: 9px; display: grid; place-items: center; font-family: var(--font-mono); font-size: 12px; font-weight: 700;
  background: hsl(var(--secondary)); color: hsl(var(--muted-foreground)); }
.mod.is-done .mod-no { background: hsl(var(--kg-ui-mint) / .16); color: hsl(var(--kg-ui-mint)); }
.mod.is-cur .mod-no { background: hsl(var(--primary) / .16); color: hsl(var(--primary)); border: 1px solid hsl(var(--primary) / .45); }
.mod-t b { display: block; font-size: 13.5px; font-weight: 650; }
.mod-t i { font-style: normal; font-family: var(--font-mono); font-size: 10.5px; color: hsl(var(--muted-foreground)); }
.mod-r { display: flex; align-items: center; gap: 10px; }
.mod-r .progress { width: 90px; }
/* Строка модуля — ссылка целиком, поэтому «кнопка» внутри неё это span. Он не
   ловит клик сам (pointer-events: none), иначе на нём не работал бы курсор-рука
   и «открыть в новой вкладке» средней кнопкой. Наведение подсвечивает всю
   строку вместе с кнопкой — так видно, что нажимается вся строка. */
a.mod .mod-r .btn { pointer-events: none; }
a.mod:hover { border-color: hsl(var(--primary) / .5); }
a.mod:hover .mod-r .btn { border-color: hsl(var(--primary) / .5); color: hsl(var(--primary)); }

/* Оценка урока — на портале ученики оценивают каждый урок (4.65/5 по 104 оценкам) */
.rate { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-top: 26px; padding: 14px 16px; border-radius: 14px;
  background: hsl(var(--secondary) / .5); border: 1px solid hsl(var(--border)); }
.rate b { font-size: 13px; }
.stars { display: flex; gap: 3px; }
.stars .star { cursor: pointer; font-size: 19px; line-height: 1; padding: 2px;
  color: hsl(var(--border)); transition: color .12s; }
.stars .star.on { color: hsl(var(--primary)); }
.stars:hover .star { color: hsl(var(--primary) / .5); }
.stars button { background: none; border: 0; padding: 2px; font-size: 19px; line-height: 1; color: hsl(var(--border)); }
.stars button.on { color: hsl(var(--primary)); }
.rate .avg { margin-left: auto; font-family: var(--font-mono); font-size: 11.5px; color: hsl(var(--muted-foreground)); font-variant-numeric: tabular-nums; }

/* ══════════ АДМИН-ФОРМЫ (drawer) И ЗАГРУЗКА ВИДЕО ══════════ */
.form-drawer { position: fixed; top: var(--kg-navbar-h); right: 0; bottom: 0; width: 460px; max-width: 100vw; z-index: 62;
  background: hsl(var(--card)); border-left: 1px solid hsl(var(--border)); border-radius: 16px 0 0 16px;
  box-shadow: -22px 0 54px -34px hsl(0 0% 0% / .6); display: flex; flex-direction: column; transform: translateX(105%); transition: transform .22s ease; }
.form-drawer.open { transform: none; }
.fd-h { display: flex; align-items: center; gap: 10px; padding: 14px 18px; border-bottom: 1px solid hsl(var(--border)); }
.fd-h b { font-size: 13.5px; }
.fd-b { flex: 1; overflow-y: auto; padding: 4px 18px 20px; }
.fd-f { display: flex; gap: 8px; align-items: center; padding: 14px 18px; border-top: 1px solid hsl(var(--border)); background: hsl(var(--background) / .5); }
.pick-list { display: grid; gap: 6px; margin-top: 6px; }
.pick { display: flex; align-items: center; gap: 10px; padding: 9px 12px; border-radius: 10px; border: 1px solid hsl(var(--border)); background: hsl(var(--background)); font-size: 12.5px; }
.pick.on { border-color: hsl(var(--primary)); background: hsl(var(--primary) / .07); }
.pick input { accent-color: hsl(var(--primary)); width: 15px; height: 15px; }
.pick .price { margin-left: auto; font-family: var(--font-mono); font-size: 11.5px; color: hsl(var(--muted-foreground)); }
.uploader { border: 1px dashed hsl(var(--border)); border-radius: 12px; padding: 18px; text-align: center; background: hsl(var(--background)); }
.uploader .big { font-size: 13px; font-weight: 600; margin-bottom: 4px; }
.up-file { display: grid; grid-template-columns: 34px 1fr auto; gap: 12px; align-items: center; margin-top: 12px; padding: 11px 13px; border-radius: 11px; background: hsl(var(--card)); border: 1px solid hsl(var(--border)); }
.up-file .progress { margin-top: 6px; }
.up-file b { font-size: 12.5px; }
.up-file i { font-style: normal; font-family: var(--font-mono); font-size: 10.5px; color: hsl(var(--muted-foreground)); }

/* Пустые состояния — режим прототипа */
body:not(.empty-mode) .when-empty { display: none !important; }
body.empty-mode .when-full { display: none !important; }
.empty-state { display: grid; justify-items: center; gap: 10px; padding: 46px 30px; text-align: center;
  border: 1px dashed hsl(var(--border)); border-radius: 16px; background: hsl(var(--card) / .5); }
.empty-state .ic { width: 46px; height: 46px; border-radius: 13px; display: grid; place-items: center; background: hsl(var(--secondary)); color: hsl(var(--muted-foreground)); }
.empty-state b { font-size: 15px; font-weight: 700; }
.empty-state p { margin: 0; max-width: 46ch; font-size: 12.5px; line-height: 1.6; color: hsl(var(--muted-foreground)); }

@media (max-width: 1100px) { .grid-2, .grid-even, .review-layout { grid-template-columns: 1fr; } .kpis { grid-template-columns: repeat(2, 1fr); } .editor-layout { grid-template-columns: 1fr; }
  .pub-hero { grid-template-columns: 1fr; } .legal-layout { grid-template-columns: 1fr; } .legal-toc { position: static; } .pub-foot-in { grid-template-columns: 1fr 1fr; } }
@media (max-width: 900px) {
  /* Плашка лимитов уходит раньше имени: на узком экране ширину в шапке
     занимают кнопки программ, а лимиты — вещь справочная и только для
     админа. Порог тот же, что был у прежней широкой пилюли. */
  .lim { display: none; }
  /* Одна колонка: контент обязан жить в ней, иначе grid-column:2 из десктопного
     правила создаёт неявную вторую колонку и страница уезжает за экран. */
  .main-layout { grid-template-columns: 1fr !important; }
  .dashboard { grid-column: 1 !important; }
  /* Чат-панели на узком экране разворачиваются поверх страницы, но ТОЛЬКО когда
     открыты. Без этого fixed игнорирует нулевую ширину колонки, и закрытая
     панель молча накрывает собой весь экран. */
  .chat-pane-left, .chat-pane-right { display: none; }
  body.has-left-chat .chat-pane-left,
  body.has-right-chat .chat-pane-right {
    display: block; position: fixed; inset: var(--kg-navbar-h) 0 0 0; z-index: 50; grid-column: 1 !important;
  }
  /* Колонка встала над контентом и больше не липкая — ограничивать её высоту
     незачем, пусть список идёт целиком (на телефоне ниже он снова свернётся). */
  .kb-layout { grid-template-columns: 1fr; }
  .kb-side { position: static; max-height: none; overflow: visible; }
}
/* ══════════ МОБИЛЬНАЯ РАЗМЕТКА (≤640px) ══════════
   Сайдбар выезжает поверх контента с затемнением, топбар оставляет только
   бургер, название раздела и два действия. Таблицы прячут второстепенные
   колонки (.hide-sm расставляет JS по data-hide) и скроллятся вбок сами. */
.side-scrim { display: none; position: fixed; inset: 0; z-index: 44; background: hsl(220 18% 7% / .5); backdrop-filter: blur(1px); }
@media (max-width: 640px) {
  body { padding-left: 0 !important; }
  .kg-sidebar { transform: translateX(-100%); width: min(300px, 86vw); box-shadow: 0 0 40px hsl(0 0% 0% / .4); }
  body.side-open .kg-sidebar { transform: none; }
  body.side-open .side-scrim { display: block; }
  body.kg-side-collapsed .kg-sidebar { width: min(300px, 86vw); }
  body.kg-side-collapsed .kgs-brandtext, body.kg-side-collapsed .kgs-link span,
  body.kg-side-collapsed .kgs-grp, body.kg-side-collapsed .kgs-unamebox,
  body.kg-side-collapsed .kgs-logout, body.kg-side-collapsed .kgs-badge { display: revert; }
  body.kg-side-collapsed .kgs-link, body.kg-side-collapsed .kgs-user, body.kg-side-collapsed .kgs-brand { justify-content: flex-start; }
  /* Бургер возвращается — на телефоне сайдбар выезжает ПОВЕРХ и до открытия
     не виден вовсе, стрелке под лого (.kgs-toggle) открыть его нечем — она
     тоже спрятана здесь же (внутри самого сайдбара, поэтому видна лишь
     когда он выехал, — прятать её отдельно незачем, но раз он уже открыт,
     сворачивать там больше нечего смыслового: та же кнопка `.kgs-burger`
     его и закроет). */
  .kgs-toggle { display: none; }

  .navbar { padding: 0 12px; gap: 8px; }
  /* На телефоне в шапке остаются только бургер и программы: имя и лимиты
     подписки — не то, ради чего стоит отнимать ширину у навигации. */
  .nav-crumb i, .tb-user, .pill, .lim { display: none; }
  .nav-crumb b { font-size: 14px; }
  .btn-assist span, .btn-assist { font-size: 0; }
  .btn-assist { width: var(--kg-ctrl-h); padding: 0; justify-content: center; }
  .btn-assist svg { width: 15px; height: 15px; }

  .page { padding: 16px 14px 96px; }
  .section-head { gap: 10px; margin-bottom: 14px; }
  .section-head-left { flex-direction: column; align-items: flex-start; gap: 2px; }
  .project-search { order: 3; width: 100%; max-width: none; flex-basis: 100%; }
  .dashboard-actions { margin-left: 0; width: 100%; overflow-x: auto; scrollbar-width: none; padding-bottom: 2px; }
  .dashboard-actions::-webkit-scrollbar { display: none; }

  .grid-2, .grid-3, .review-layout, .editor-layout, .kb-layout, .prios, .tariffs, .pcards, .mat-list { grid-template-columns: 1fr !important; }
  .kpis { grid-template-columns: repeat(2, minmax(0,1fr)); gap: 10px; }
  .kpi .v { font-size: 20px; }
  .prio h2, .prog-head h1 { font-size: 17px; }
  .prog-head { grid-template-columns: 1fr; gap: 14px; }
  .prog-head .side { text-align: left; justify-items: start; }
  .kb-side { max-height: 260px; overflow-y: auto; }
  .lesson-head { flex-direction: column; gap: 10px; }
  .lesson-head .acts { margin-left: 0; width: 100%; }
  .lesson-head .acts .btn { flex: 1; justify-content: center; }
  .lesson-head h1 { font-size: 19px; }
  .tabs { overflow-x: auto; scrollbar-width: none; }
  .tabs::-webkit-scrollbar { display: none; }
  .tab { white-space: nowrap; }
  .video .play { width: 52px; height: 52px; }
  .rate { flex-wrap: wrap; }
  .rate .avg { margin-left: 0; width: 100%; }

  .hide-sm { display: none !important; }
  .tbl th, .tbl td { padding: 9px 12px; }
  .tbl-name { font-size: 12.5px; }
  .tbl-btn { height: 28px; min-width: 28px; padding: 0 7px; }
  .mod { grid-template-columns: 30px 1fr; row-gap: 8px; }
  .mod-r { grid-column: 1 / -1; justify-content: flex-start; flex-wrap: wrap; }
  .mod-r .progress { width: 70px; }
  .hw-row { grid-template-columns: 1fr auto; row-gap: 6px; }
  .hw-row .hw-due { grid-column: 1 / -1; }
  .prog-row { flex-wrap: wrap; }
  .prog-row .pr { width: 100%; }

  .form-drawer, .drawer { top: 0; width: 100vw; border-radius: 0; border-left: 0; }
  .fd-b, .drawer-b { padding-left: 14px; padding-right: 14px; }
  .chat-pane-left, .chat-pane-right { border: 0; }

  .pub-head { height: 56px; padding: 0 14px; gap: 10px; }
  .pub-nav { display: none; }
  .pub-brand i { display: none; }
  .pub-head .btn { height: 30px; padding: 0 11px; font-size: 12px; }
  .pub-wrap { padding: 22px 14px 90px; }
  .pub-hero { padding: 22px 18px; gap: 18px; }
  .pub-hero h1 { font-size: 24px; }
  .pub-foot-in { grid-template-columns: 1fr; gap: 18px; }
  .auth-card { padding: 20px 18px; }
  .auth-card .form-row, .form-row { grid-template-columns: 1fr; }
  .cookie-bar { flex-wrap: wrap; bottom: 84px; left: 10px; right: 10px; }
  .cookie-bar .btn { margin-left: 0 !important; }
  .legal-toc { max-height: 210px; overflow-y: auto; }
  .doc h1 { font-size: 21px; }

  .proto { bottom: 8px; left: 8px; right: 8px; transform: none; border-radius: 12px; padding: 7px 8px; gap: 6px; }
  .proto .lbl, .proto .gl { display: none; }
  .proto button { height: 24px; padding: 0 8px; font-size: 11px; }
}
@media (prefers-reduced-motion: reduce) { * { transition: none !important; } }

/* ══════════ НАЧАЛО: меню (программы сверху, разделы слева) ══════════ */
/* Эталон — design/prototype/menu.html (собирается build-menu.py).
   Верхняя панель отвечает на вопрос «какая программа», левое меню — «какой
   раздел этой программы». Раньше оба вопроса тянуло одно левое меню. */

.progbar { display: flex; align-items: center; gap: 3px; min-width: 0; overflow-x: auto;
  scrollbar-width: none; }
.progbar::-webkit-scrollbar { display: none; }
.progtab { height: var(--kg-ctrl-h); padding: 0 11px; border-radius: 9px; flex-shrink: 0;
  display: inline-flex; align-items: center; gap: 8px; white-space: nowrap;
  font-size: 12.5px; font-weight: 600; color: hsl(var(--muted-foreground));
  border: 1px solid transparent; background: none; transition: background .14s, color .14s; }
.progtab:hover { background: hsl(var(--secondary)); color: hsl(var(--foreground)); }
/* Точек у названий программ больше нет (правка владельца 14.09). Цвет
   программы никуда не делся: выбранная вкладка красится им целиком —
   заливкой и рамкой, а это заметнее кружка. У невыбранных цвет и не нужен:
   они и так приглушены. Кружок в левом меню (.kgs-prog .d) оставлен — в
   свёрнутом меню он единственное, что показывает текущую программу. */
/* ⚠️ Рамка выбранной вкладки — .75, а не .5 (правка владельца 15.09: «боковые
   стороны смотрятся тоньше горизонтальных»). Замер показал, что рамка ВЕЗДЕ
   одна и та же — 1 px, и «чернил» поперёк грани поровну (305 на снимке при
   DPR 1 и сверху, и слева). Разное — длина ПРЯМОГО участка: при высоте 32 px
   и радиусе 9 px на вертикальную сторону остаётся 14 px прямой линии против
   85 px на горизонтальной, то есть больше половины стороны — сглаженная дуга.
   Полупрозрачная линия на таком коротком участке и «тает». Радиус не трогаем:
   9 px общий у всей верхней панели (.btn-icon, .lim, .tb-user) — и у них рамка
   НЕПРОЗРАЧНАЯ, отсюда и разница с соседями. Поднимаем плотность до их уровня. */
.progtab.on { background: hsl(var(--c) / var(--kg-ui-fill)); border-color: hsl(var(--c) / .75);
  color: hsl(var(--foreground)); }
.progtab.locked { color: hsl(var(--muted-foreground) / .75); }
.progtab.locked svg { opacity: .5; }
.prog-sep { width: 1px; height: 20px; flex-shrink: 0; background: hsl(var(--border)); margin: 0 5px; }
.progtab.is-all { font-family: var(--font-mono); font-size: 10.5px; letter-spacing: .05em;
  text-transform: uppercase; }

/* Программа над меню: меню принадлежит одной программе, и это должно
   читаться, не поднимая глаз к верхней панели. */
.kgs-prog { display: flex; align-items: center; gap: 9px; padding: 8px 9px; margin: 2px 0 8px;
  border-radius: 10px; background: rgba(255,255,255,.06); white-space: nowrap; }
.kgs-prog:hover { background: rgba(255,255,255,.10); }
.kgs-prog .d { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; background: hsl(var(--c));
  --c: var(--kg-ui-steel); box-shadow: 0 0 0 3px hsl(var(--c) / .22); }
.kgs-prog-t { display: flex; flex-direction: column; line-height: 1.2; min-width: 0; }
.kgs-prog-t b { color: #fff; font-size: 12.5px; font-weight: 650; overflow: hidden; text-overflow: ellipsis; }
.kgs-prog-t i { font-style: normal; font-family: var(--font-mono); font-size: 9px; letter-spacing: .07em;
  text-transform: uppercase; color: rgba(255,255,255,.42); margin-top: 2px; }
body.kg-side-collapsed .kgs-prog-t { display: none; }
body.kg-side-collapsed .kgs-prog { justify-content: center; }

/* Разделы персонала — отдельным блоком внизу меню, вне прокручиваемого списка
   разделов программы: место у них одно и то же в любой программе. */
.kgs-admin { flex-shrink: 0; display: flex; flex-direction: column; gap: 2px;
  border-top: 1px solid rgba(255,255,255,.09); padding-top: 6px; margin-top: 8px;
  min-height: 0; }
.kgs-admin .kgs-grp { padding-top: 4px; }
/* ⚠️ Прокрутка — у СПИСКА, а не у всего блока (правка владельца 15.09):
   заголовок-кнопка обязан оставаться на месте, иначе свернуть блок можно
   будет только домотав список до верха. Потолок высоты обязателен вместе с
   `min-height: 0` — без него элемент флекс-колонки не сжимается меньше
   содержимого и `overflow` не включается вовсе (на этом уже горели .kgs-nav
   и .reader-side). */
.kgs-admin-b { display: flex; flex-direction: column; gap: 2px;
  max-height: 42vh; overflow-y: auto; scrollbar-width: none; min-height: 0; }
.kgs-admin-b::-webkit-scrollbar { display: none; }
/* Заголовок блока — кнопка сворачивания. Выглядит как подпись группы
   (тот же кегль, моношрифт, разрядка), но нажимается: подпись, которая
   вдруг стала кнопкой, не должна кричать об этом. */
.kgs-admin-h { display: flex; align-items: center; gap: 6px; width: 100%;
  background: none; border: 0; cursor: pointer; padding: 6px 10px 5px;
  color: rgba(255,255,255,.42); font-family: var(--font-mono); font-size: 9.5px;
  font-weight: 700; letter-spacing: .08em; text-transform: uppercase; text-align: left; }
.kgs-admin-h:hover { color: rgba(255,255,255,.75); }
.kgs-admin-h span { flex: 1; min-width: 0; }
.kgs-admin-h svg { flex-shrink: 0; transition: transform .18s; }
.kgs-admin.is-folded .kgs-admin-h svg { transform: rotate(-90deg); }
.kgs-admin.is-folded .kgs-admin-b { display: none; }
/* В свёрнутом меню (рельс 62 px) подписи нет — остаётся один шеврон по центру,
   и он по-прежнему сворачивает блок. */
body.kg-side-collapsed .kgs-admin-h { justify-content: center; padding-left: 0; padding-right: 0; }
body.kg-side-collapsed .kgs-admin-h span { display: none; }
/* «Аккаунт» не сворачивается (правка владельца 24.09) — заголовок там простая
   подпись (`<div>`, не `<button>`), но наследует вид `.kgs-admin-h`; снимаем
   только курсор и ховер, которые говорили «это нажимается». */
.kgs-acc .kgs-admin-h { cursor: default; }
.kgs-acc .kgs-admin-h:hover { color: rgba(255,255,255,.42); }

/* Приём материалов: зона перетаскивания папки. */
.drop { margin-top: 4px; padding: 26px 22px; text-align: center; border-radius: 16px;
  border: 2px dashed hsl(var(--border)); background: hsl(var(--card));
  transition: border-color .15s, background .15s; }
.drop.over { border-color: hsl(var(--primary)); background: hsl(var(--primary) / .06); }
.drop b { display: block; font-size: 14px; margin-bottom: 6px; }
.drop p { margin: 0 auto; max-width: 62ch; font-size: 12.5px; line-height: 1.55;
  color: hsl(var(--muted-foreground)); }

/* Заголовок экрана: в шапке его место заняли программы. */
.page-head { display: flex; align-items: flex-start; gap: 14px; flex-wrap: wrap;
  padding: 20px 26px 0; max-width: 1360px; }
.page-head:empty { display: none; }
/* Если заголовок есть, у контента свой отступ сверху уже не нужен целиком. */
.page-head:not(:empty) + .page { padding-top: 16px; }
.page-head h1 { margin: 0; font-size: 21px; font-weight: 700; letter-spacing: -.02em; }
.page-head .sub { margin: 5px 0 0; font-size: 12.5px; color: hsl(var(--muted-foreground)); max-width: 70ch; }
.page-head-t { min-width: 0; }
.page-head-a { margin-left: auto; display: flex; gap: 8px; align-items: center; flex-shrink: 0; }
.page-crumb { font-family: var(--font-mono); font-size: 10.5px; letter-spacing: .06em;
  text-transform: uppercase; color: hsl(var(--muted-foreground)); margin-bottom: 6px; }

/* Программа заведена, но её содержимое ещё не перенесено. */
.soon-plate { max-width: 620px; margin: 30px auto; padding: 30px 28px; text-align: center;
  border-radius: 18px; background: hsl(var(--card)); border: 1px dashed hsl(var(--border)); }
.soon-ic { width: 46px; height: 46px; border-radius: 14px; margin: 0 auto 14px; display: grid;
  place-items: center; background: hsl(var(--primary) / .12); color: hsl(var(--primary)); }
.soon-plate b { display: block; font-size: 16px; margin-bottom: 8px; }
.soon-plate p { margin: 0; font-size: 13px; line-height: 1.6; color: hsl(var(--muted-foreground)); }

@media (max-width: 640px) {
  .page-head { padding: 14px 14px 0; }
  .page-head h1 { font-size: 18px; }
  .progtab { padding: 0 9px; font-size: 12px; }
}
/* ══════════ КОНЕЦ: меню ══════════ */

/* ══════ НАЧАЛО: статьи и читалка (перенесено из прототипа) ══════ */
/* Стили конспектов перенесены из panel/public/css/style.css: контент
   размечен классами старой панели, и без них схемы (SVG) показываются
   чёрными прямоугольниками. Все правила заскоуплены под .article —
   на остальной портал они не влияют. Эталон — design/prototype/articles.html
   (пересобрать: python3 design/prototype/build-articles.py). */
.article .btn-primary {
  background: hsl(var(--primary));
  color: hsl(var(--primary-foreground));
  border-color: hsl(var(--primary));
}
.article .btn-primary:hover {
  background: hsl(var(--primary));
  color: hsl(var(--primary-foreground));
  opacity: .85;
  border-color: hsl(var(--primary));
}
.article .course-modules {
  display: flex; flex-direction: column; gap: 10px;
  max-width: 760px; margin: 0 auto;
}
.article .course-module {
  display: grid; grid-template-columns: 60px 1fr auto;
  align-items: stretch;
  background: hsl(var(--card));
  border: 1px solid hsl(var(--border));
  text-decoration: none;
  transition: border-color .15s;
}
.article .course-module:hover { border-color: hsl(var(--primary)); }
.article .course-module--recommended {
  border-color: hsl(var(--smui-green) / 0.5);
}
.article .course-module--recommended:hover { border-color: hsl(var(--smui-green)); }
.article .course-module--recommended .course-module-no {
  background: hsl(var(--smui-green) / 0.15);
  color: hsl(var(--smui-green));
  border-right-color: hsl(var(--smui-green) / 0.3);
}
.article .course-module--recommended .course-module-cta {
  color: hsl(var(--smui-green));
}
.article .course-module-no {
  display: flex; align-items: center; justify-content: center;
  font-family: var(--font-mono); font-size: 13px; font-weight: 700;
  background: hsl(var(--primary) / 0.10);
  color: hsl(var(--primary));
  letter-spacing: .04em;
  border-right: 1px solid hsl(var(--border));
}
.article .course-module-body { padding: 14px 18px; display: flex; flex-direction: column; gap: 4px; }
.article .course-module-title {
  font-family: var(--font-mono); font-size: 13px; font-weight: 700;
  color: hsl(var(--foreground));
}
.article .course-module-desc {
  font-size: 12px; line-height: 1.5;
  color: hsl(var(--muted-foreground));
}
.article .course-module-cta {
  display: flex; align-items: center;
  padding: 0 18px;
  font-family: var(--font-mono); font-size: 11px; font-weight: 600;
  letter-spacing: .04em;
  color: hsl(var(--primary));
  border-left: 1px solid hsl(var(--border));
}
.article .course-module--no-link {
  cursor: default;
}
.article .course-module--no-link:hover { border-color: hsl(var(--border)); }
.article .course-module--no-link .course-module-cta {
  color: hsl(var(--muted-foreground));
  font-weight: 400;
}
.article .skills-scope-diagram {
  display: grid;
  grid-template-columns: 1fr 48px 1fr;
  gap: 0;
  align-items: center;
  max-width: 720px;
  margin: 12px 0 0;
}
.article .skills-scope-card {
  border: 1px solid hsl(var(--border));
  background: hsl(var(--smui-surface-1));
  padding: 14px 16px;
  display: flex; flex-direction: column; gap: 10px;
}
.article .skills-scope-global { border-color: hsl(var(--primary) / 0.4); }
.article .skills-scope-project { border-color: hsl(var(--smui-green) / 0.5); }
.article .skills-scope-badge {
  font-family: var(--font-mono); font-size: 10px; font-weight: 700;
  letter-spacing: .08em; color: hsl(var(--muted-foreground));
}
.article .skills-scope-path {
  font-family: var(--font-mono); font-size: 14px; font-weight: 700;
  color: hsl(var(--foreground));
}
.article .skills-scope-tree pre {
  margin: 0; font-size: 11px; line-height: 1.6;
  color: hsl(var(--muted-foreground));
  background: hsl(var(--smui-surface-0)); padding: 10px 12px;
  border: 1px solid hsl(var(--border));
  overflow-x: auto;
}
.article .skills-scope-props {
  margin: 0; padding: 0; list-style: none;
  display: flex; flex-direction: column; gap: 3px;
}
.article .skills-scope-props li {
  font-size: 11px; color: hsl(var(--muted-foreground)); line-height: 1.4;
}
.article .skills-scope-props li strong { color: hsl(var(--foreground)); }
.article .skills-scope-note {
  font-size: 11px; color: hsl(var(--muted-foreground));
  border-top: 1px solid hsl(var(--border)); padding-top: 8px;
  font-style: italic;
}
.article .skills-scope-note--highlight {
  color: hsl(var(--smui-green));
  border-top-color: hsl(var(--smui-green) / 0.3);
}
.article .skills-scope-arrow {
  display: flex; flex-direction: column; align-items: center;
  gap: 6px; padding: 0 4px;
}
.article .skills-scope-arrow-line {
  width: 100%; height: 1px; background: hsl(var(--border));
  position: relative;
}
.article .skills-scope-arrow-line::after {
  content: '→'; position: absolute; right: -6px; top: -9px;
  font-size: 14px; color: hsl(var(--muted-foreground));
}
.article .skills-scope-arrow-label {
  font-size: 9px; font-family: var(--font-mono);
  color: hsl(var(--muted-foreground)); text-align: center;
  line-height: 1.4; letter-spacing: .03em; text-transform: uppercase;
}
@media (max-width: 640px) {
.article .skills-scope-diagram {
    grid-template-columns: 1fr;
  }
.article .skills-scope-arrow { flex-direction: row; padding: 8px 0; }
.article .skills-scope-arrow-line { width: 40px; }
.article .skills-scope-arrow-line::after { content: '↓'; right: auto; left: 50%; top: -9px; }
}
.article .sca-flow {
  display: grid;
  grid-template-columns: 52px 1fr 52px 1fr;
  gap: 6px 0;
  align-items: center;
  max-width: 680px;
  margin: 0 0 20px;
  background: hsl(var(--smui-surface-0));
  border: 1px solid hsl(var(--border));
  padding: 14px 16px;
}
.article .sca-flow-actor {
  display: flex; flex-direction: column; align-items: center; gap: 2px;
}
.article .sca-flow-actor-icon { font-size: 20px; line-height: 1; }
.article .sca-flow-actor-label {
  font-family: var(--font-mono); font-size: 9px; font-weight: 700;
  color: hsl(var(--muted-foreground)); letter-spacing: .06em; text-transform: uppercase;
}
.article .sca-flow-msg {
  display: flex; align-items: center; gap: 8px;
  padding: 4px 8px;
  font-size: 11px; font-family: var(--font-mono);
  color: hsl(var(--muted-foreground));
}
.article .sca-flow-msg code { font-size: 11px; }
.article .sca-flow-msg-arrow { opacity: .4; white-space: nowrap; }
.article .sca-flow-msg--cmd { border-left: 2px solid hsl(var(--primary) / .5); }
.article .sca-flow-msg--skill { border-left: 2px solid hsl(var(--smui-green) / .5); }
.article .sca-flow-msg--agent { border-left: 2px solid hsl(39 80% 55% / .5); }
.article .sca-flow-result {
  grid-column: 4;
  font-size: 11px; line-height: 1.5;
  color: hsl(var(--muted-foreground));
  padding: 4px 8px;
  border-left: 1px dashed hsl(var(--border));
}
.article .sca-flow-result em { font-style: normal; color: hsl(var(--foreground)); }
.article .sca-flow-tag {
  display: inline-block;
  font-family: var(--font-mono); font-size: 9px; font-weight: 700;
  letter-spacing: .08em; padding: 1px 5px;
  margin-right: 6px; vertical-align: middle;
}
.article .sca-flow-result--cmd   .sca-flow-tag { background: hsl(var(--primary) / .15);   color: hsl(var(--primary)); }
.article .sca-flow-result--skill .sca-flow-tag { background: hsl(var(--smui-green) / .15); color: hsl(var(--smui-green)); }
.article .sca-flow-result--agent .sca-flow-tag { background: hsl(39 80% 55% / .15);        color: hsl(39 80% 60%); }
.article .sca-cards {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px;
  max-width: 760px; margin: 0;
}
.article .sca-card {
  border: 1px solid hsl(var(--border));
  background: hsl(var(--smui-surface-1));
  padding: 14px; display: flex; flex-direction: column; gap: 8px;
}
.article .sca-card--cmd { border-top: 2px solid hsl(var(--primary)); }
.article .sca-card--skill { border-top: 2px solid hsl(var(--smui-green)); }
.article .sca-card--agent { border-top: 2px solid hsl(39 80% 55%); }
.article .sca-card-header {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
}
.article .sca-card-icon { font-size: 16px; }
.article .sca-card-name {
  font-family: var(--font-mono); font-size: 11px; font-weight: 700;
  letter-spacing: .08em; color: hsl(var(--foreground));
}
.article .sca-card-path {
  font-size: 10px; color: hsl(var(--muted-foreground));
  background: hsl(var(--smui-surface-0)); padding: 1px 5px;
  border: 1px solid hsl(var(--border));
}
.article .sca-card-who {
  font-size: 11px; color: hsl(var(--foreground));
  background: hsl(var(--smui-surface-0)); padding: 6px 8px;
  border-left: 2px solid hsl(var(--border));
}
.article .sca-card--cmd   .sca-card-who { border-color: hsl(var(--primary)); }
.article .sca-card--skill .sca-card-who { border-color: hsl(var(--smui-green)); }
.article .sca-card--agent .sca-card-who { border-color: hsl(39 80% 55%); }
.article .sca-card-who code { font-size: 10px; }
.article .sca-card-what {
  font-size: 11px; line-height: 1.6; color: hsl(var(--muted-foreground));
}
.article .sca-card-when {
  font-size: 10px; line-height: 1.5; color: hsl(var(--muted-foreground));
  border-top: 1px solid hsl(var(--border)); padding-top: 7px;
  font-style: italic;
}
@media (max-width: 640px) {
.article .sca-cards { grid-template-columns: 1fr; }
.article .sca-flow { grid-template-columns: 44px 1fr 44px; }
.article .sca-flow-result { grid-column: 1 / -1; border-left: none; border-top: 1px dashed hsl(var(--border)); }
}
.article .page-meta {
  font-family: var(--font-mono); font-size: 10px; font-weight: 600;
  letter-spacing: .18em; text-transform: uppercase;
  color: hsl(var(--muted-foreground));
  margin-bottom: 12px;
}
.article .page-h1 {
  font-family: var(--font-mono);
  font-size: 32px; font-weight: 700;
  letter-spacing: -.01em;
  color: hsl(var(--foreground));
  margin-bottom: 14px;
}
.article .page-h1 em { font-style: normal; color: hsl(var(--primary)); }
.article .page-lead {
  font-size: 14px; line-height: 1.6;
  color: hsl(var(--muted-foreground));
  margin-bottom: 24px;
  max-width: 64ch;
}
.article .anchor-chips {
  display: flex; gap: 6px; flex-wrap: wrap;
  margin-bottom: 32px;
}
.article .anchor-chip {
  display: inline-flex; gap: 6px; align-items: center;
  padding: 4px 11px;
  background: hsl(var(--card));
  border: 1px solid hsl(var(--border));
  color: hsl(var(--muted-foreground));
  font-family: var(--font-mono); font-size: 10px; font-weight: 500;
  letter-spacing: .04em;
  text-decoration: none;
  transition: color .15s, border-color .15s;
}
.article .anchor-chip::before {
  content: '●'; font-size: 7px;
  color: hsl(var(--primary)); opacity: .7;
}
.article .anchor-chip:hover {
  color: hsl(var(--foreground));
  border-color: hsl(var(--primary) / 0.45);
}
.article .page-sect {
  background: hsl(var(--card));
  border-left: 3px solid hsl(var(--primary));
  border-top: 1px solid hsl(var(--border));
  border-right: 1px solid hsl(var(--border));
  border-bottom: 1px solid hsl(var(--border));
  padding: 18px 22px;
  margin-bottom: 14px;
  scroll-margin-top: 56px;
}
.article .page-sect-no {
  font-family: var(--font-mono); font-size: 11px; font-weight: 700;
  letter-spacing: .12em; text-transform: uppercase;
  color: hsl(var(--foreground));
  margin-bottom: 8px;
}
.article .page-sect p, .article .page-list li {
  font-size: 13px; line-height: 1.6;
  color: hsl(var(--foreground));
}
.article .page-sect p { margin-bottom: 8px; }
.article .page-list { padding-left: 22px; }
.article .page-list li { margin: 4px 0; }
.article .page-list li::marker { color: hsl(var(--primary)); }
.article .page-table {
  width: 100%; border-collapse: collapse;
  font-family: var(--font-mono); font-size: 12px;
}
.article .page-table thead th {
  text-align: left;
  font-weight: 600; font-size: 10px;
  letter-spacing: .08em; text-transform: uppercase;
  color: hsl(var(--muted-foreground));
  padding: 7px 12px;
  border-bottom: 1px solid hsl(var(--border));
}
.article .page-table tbody td {
  padding: 8px 12px;
  border-bottom: 1px solid hsl(var(--border));
  color: hsl(var(--foreground));
  vertical-align: top;
}
.article .page-table tbody tr:last-child td { border-bottom: none; }
.article .page-table td:first-child { white-space: nowrap; width: 1%; }
.article .page-faq {
  background: hsl(var(--card));
  border: 1px solid hsl(var(--border));
  margin-bottom: 8px;
}
.article .page-faq summary {
  padding: 11px 16px;
  cursor: pointer;
  font-family: var(--font-mono); font-size: 12px; font-weight: 600;
  color: hsl(var(--foreground));
  list-style: none;
}
.article .page-faq summary::-webkit-details-marker { display: none; }
.article .page-faq summary::before {
  content: '+'; margin-right: 10px; color: hsl(var(--primary)); font-weight: 700;
}
.article .page-faq[open] summary::before { content: '−'; }
.article .page-faq p {
  padding: 0 16px 14px;
  font-size: 12.5px; line-height: 1.6;
  color: hsl(var(--muted-foreground));
}
.article .link-tiles {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 12px;
  margin-bottom: 18px;
}
.article .link-tiles-primary { margin-bottom: 28px; }
.article .link-tile {
  display: flex; flex-direction: column; gap: 4px;
  padding: 18px 20px;
  background: hsl(var(--card));
  border: 1px solid hsl(var(--border));
  text-decoration: none;
  transition: border-color .15s, background .15s;
}
.article .link-tile:hover {
  border-color: hsl(var(--primary));
  background: hsl(var(--primary) / 0.06);
}
.article .link-tile-label {
  font-family: var(--font-mono); font-size: 9.5px; font-weight: 700;
  letter-spacing: .16em; text-transform: uppercase;
  color: hsl(var(--primary));
}
.article .link-tile-title {
  font-family: var(--font-mono); font-size: 16px; font-weight: 700;
  letter-spacing: -.01em;
  color: hsl(var(--foreground));
  margin-top: 4px;
}
.article .link-tile-desc {
  font-size: 12px; line-height: 1.5;
  color: hsl(var(--muted-foreground));
  margin-top: 6px;
}
.article .link-tile-alt .link-tile-label { color: hsl(var(--muted-foreground)); }
/* Плитка-ДЕЙСТВИЕ («Создать свой dev»): та же карточка, но это <button>, и
   браузер даёт ему свои шрифт, рамку и центровку — их надо снять, иначе
   соседние плитки-ссылки и она выглядят как из разных наборов. Красится
   акцентом: это главное действие статьи, а не ещё одна ссылка рядом. */
.article .link-tile-btn {
  font: inherit; text-align: left; cursor: pointer; width: 100%;
  border-color: hsl(var(--primary));
  background: hsl(var(--primary) / 0.06);
}
.article .link-tile-btn:hover { background: hsl(var(--primary) / 0.12); }
.article .link-tile-btn[disabled] { opacity: .6; cursor: default; }
.article .page-h2 {
  font-family: var(--font-mono);
  font-size: 13px; font-weight: 700;
  letter-spacing: .08em; text-transform: uppercase;
  color: hsl(var(--foreground));
  margin: 28px 0 14px;
  padding-bottom: 8px;
  border-bottom: 1px solid hsl(var(--border));
}
.article .page-list a, .article .page-lead a, .article .course-placeholder-desc a {
  color: hsl(190 80% 60%);  /* мягкий cyan, на тёмном фоне читается лучше чем primary */
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-thickness: 1px;
}
.article .page-list a:hover, .article .page-lead a:hover, .article .course-placeholder-desc a:hover {
  color: hsl(190 90% 70%);
  text-decoration-thickness: 2px;
}
.article html:not(.dark) .page-list a, .article html:not(.dark) .page-lead a, .article html:not(.dark) .course-placeholder-desc a {
  color: hsl(210 80% 45%);
}
.article html:not(.dark) .page-list a:hover, .article html:not(.dark) .page-lead a:hover, .article html:not(.dark) .course-placeholder-desc a:hover {
  color: hsl(210 90% 35%);
}
.article .dl-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 16px;
  margin-top: 24px;
}
.article .dl-card {
  border: 1px solid hsl(var(--border));
  border-radius: 12px;
  background: hsl(var(--card));
  padding: 18px;
  display: flex;
  flex-direction: column;
}
.article .dl-card-head {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 12px;
}
.article .dl-card-icon { font-size: 26px; line-height: 1; }
.article .dl-card-title {
  font-size: 15px;
  font-weight: 700;
  color: hsl(var(--foreground));
}
.article .dl-card-sub {
  font-family: var(--font-mono);
  font-size: 11px;
  color: hsl(var(--muted-foreground));
  margin-top: 2px;
}
.article .dl-card-desc {
  font-size: 13px;
  line-height: 1.55;
  color: hsl(var(--muted-foreground));
  margin-bottom: 16px;
  flex: 1;
}
.article .dl-btn-row { display: flex; gap: 8px; flex-wrap: wrap; }
.article .dl-btn { text-decoration: none; text-align: center; }
.article .dl-btn-sec {
  text-decoration: none;
  text-align: center;
  border: 1px solid hsl(var(--border));
  color: hsl(var(--muted-foreground));
}
.article .dl-btn-sec:hover { color: hsl(var(--foreground)); border-color: hsl(var(--foreground) / .4); }
.article .lesson-note { max-width: 70ch; margin-top: 4px; }
.article .lesson-note > p { font-size: 13.5px; line-height: 1.66; color: hsl(var(--foreground)); margin: 0 0 14px; }
.article .lesson-note > p.muted { color: hsl(var(--muted-foreground)); }
.article .lesson-note strong { color: hsl(var(--foreground)); font-weight: 600; }
.article .lesson-note em { font-style: normal; color: hsl(var(--primary)); }
.article .note-toc { display: flex; flex-wrap: wrap; gap: 6px; margin: 0 0 26px; }
.article .note-toc-chip {
  font-family: var(--font-mono); font-size: 10px; font-weight: 600;
  letter-spacing: .04em; text-transform: uppercase;
  padding: 4px 10px;
  background: hsl(var(--card)); border: 1px solid hsl(var(--border));
  color: hsl(var(--muted-foreground));
}
.article .note-h2 {
  display: flex; align-items: center; gap: 10px;
  font-family: var(--font-mono); font-size: 14px; font-weight: 700;
  letter-spacing: .02em; color: hsl(var(--foreground));
  margin: 34px 0 14px; padding-bottom: 9px;
  border-bottom: 1px solid hsl(var(--border));
}
.article .note-h2-no {
  flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px;
  font-size: 11px; font-weight: 700;
  background: hsl(var(--primary) / .14); color: hsl(var(--primary));
  border: 1px solid hsl(var(--primary) / .4);
}
.article .lesson-note ul { margin: 0 0 16px; padding: 0; list-style: none; }
.article .lesson-note ul li {
  position: relative; padding: 3px 0 3px 20px;
  font-size: 13px; line-height: 1.6; color: hsl(var(--foreground));
}
.article .lesson-note ul li::before {
  content: '▸'; position: absolute; left: 2px; top: 3px;
  color: hsl(var(--primary)); font-size: 11px;
}
.article .kbd {
  font-family: var(--font-mono); font-size: 11.5px;
  padding: 1px 6px; margin: 0 1px;
  background: hsl(var(--secondary)); color: hsl(var(--foreground));
  border: 1px solid hsl(var(--border));
  white-space: nowrap;
}
.article .note-callout {
  display: flex; gap: 12px; align-items: flex-start;
  margin: 18px 0; padding: 13px 15px;
  background: hsl(var(--card)); border: 1px solid hsl(var(--border));
  border-left-width: 3px;
}
.article .note-callout-ic { flex: none; font-size: 16px; line-height: 1.4; }
.article .note-callout-body { font-size: 12.5px; line-height: 1.6; color: hsl(var(--foreground)); }
.article .note-callout-label {
  display: block; font-family: var(--font-mono); font-size: 9.5px; font-weight: 700;
  letter-spacing: .14em; text-transform: uppercase; margin-bottom: 4px;
}
.article .note-callout.is-key { border-left-color: hsl(var(--primary)); }
.article .note-callout.is-key  .note-callout-label { color: hsl(var(--primary)); }
.article .note-callout.is-tip { border-left-color: hsl(var(--smui-green)); }
.article .note-callout.is-tip  .note-callout-label { color: hsl(var(--smui-green)); }
.article .note-callout.is-warn { border-left-color: hsl(var(--smui-orange)); }
.article .note-callout.is-warn .note-callout-label { color: hsl(var(--smui-orange)); }
.article .note-quote {
  margin: 16px 0; padding: 12px 15px;
  background: hsl(var(--background)); border: 1px dashed hsl(var(--border));
  font-family: var(--font-mono); font-size: 12px; line-height: 1.6;
  color: hsl(var(--foreground));
}
.article .note-quote-label {
  display: block; font-size: 9.5px; font-weight: 700; letter-spacing: .14em;
  text-transform: uppercase; color: hsl(var(--muted-foreground)); margin-bottom: 6px;
}
.article .note-quote pre {
  margin: 0; font: inherit; color: inherit;
  white-space: pre; overflow-x: auto;
}
.article .note-steps { margin: 16px 0; padding: 0; list-style: none; counter-reset: st; }
.article .note-steps li {
  position: relative; counter-increment: st;
  padding: 7px 0 7px 38px; min-height: 26px;
  font-size: 13px; line-height: 1.55; color: hsl(var(--foreground));
  border-bottom: 1px solid hsl(var(--border));
}
.article .note-steps li:last-child { border-bottom: none; }
.article .note-steps li::before {
  content: counter(st); position: absolute; left: 0; top: 7px;
  width: 21px; height: 21px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  font-family: var(--font-mono); font-size: 10.5px; font-weight: 700;
  background: hsl(var(--secondary)); color: hsl(var(--muted-foreground));
  border: 1px solid hsl(var(--border));
}
.article .note-cmp-wrap { overflow-x: auto; margin: 18px 0; border: 1px solid hsl(var(--border)); }
.article .note-cmp { width: 100%; border-collapse: collapse; font-size: 12px; }
.article .note-cmp th, .article .note-cmp td {
  padding: 9px 12px; text-align: left; vertical-align: top;
  border-bottom: 1px solid hsl(var(--border));
}
.article .note-cmp thead th {
  font-family: var(--font-mono); font-size: 10px; font-weight: 700;
  letter-spacing: .06em; text-transform: uppercase;
  color: hsl(var(--muted-foreground)); background: hsl(var(--background));
}
.article .note-cmp tbody tr:last-child td { border-bottom: none; }
.article .note-cmp tbody td:first-child { font-weight: 600; color: hsl(var(--foreground)); }
/* Раньше здесь стоял white-space: nowrap — из-за него длинная подпись
   первого столбца («Claude Code, сервер, по шаблону») растягивала таблицу
   шире колонки статьи, и на экране появлялась своя полоса прокрутки.
   Замерено на 26 конспектах «Вайбкодинга»: широкими были 17 из 26. */
.article .note-cmp td { color: hsl(var(--muted-foreground)); }
/* ⚠️ Имя СВОЁ (note-pick), а не .pick: глобальный .pick (подбор тарифа в
   админке) — это display:flex с рамкой, фоном и padding, и он бил по ячейке
   таблицы конспекта, превращая её во флекс-бокс. Специфичность тут не спасает:
   правило ниже задаёт только цвет, а display/border/background приходили от
   .pick и оставались. */
.article .note-cmp .note-pick { color: hsl(var(--smui-green)); font-weight: 600; }
.article .note-cmp .nope { color: hsl(var(--smui-red)); }
.article .note-fig {
  margin: 22px 0; padding: 18px;
  background: hsl(var(--background)); border: 1px solid hsl(var(--border));
}
.article .note-fig svg { width: 100%; height: auto; display: block; }
.article .note-fig-cap {
  margin-top: 12px; font-family: var(--font-mono); font-size: 10.5px;
  letter-spacing: .03em; color: hsl(var(--muted-foreground)); text-align: center;
}
/* ⚠️ На телефоне схему НЕ сжимаем, а листаем вбок. Холст у всех схем шириной
   700, и при ширине колонки ~340 px он ужимался вдвое: подпись в 12 px
   превращалась в 6 — не читает никто (снимок 390 px, 28.09.2026). Ширину
   держим близкой к натуральной, лишнее уезжает под горизонтальную прокрутку —
   тем же приёмом, что у таблиц сравнения (.note-cmp-wrap).
   Разметку схем это НЕ меняет: прокручивается сам блок .note-fig, поэтому
   приём работает и для конспектов, написанных раньше. Подпись прилипает к
   левому краю (position: sticky), иначе она уезжала бы вместе с рисунком.
   ⚠️ Правило обязано стоять ПОСЛЕ .note-fig-cap: медиазапрос не добавляет
   специфичности, и при обратном порядке text-align: center из базового
   правила перебивал бы left. */
@media (max-width: 640px) {
  .article .note-fig { overflow-x: auto; padding: 14px; }
  .article .note-fig svg { min-width: 620px; }
  .article .note-fig-cap {
    position: sticky; left: 0; text-align: left; margin-top: 10px;
  }
}
.article .note-fig svg text { font-family: var(--font-mono); fill: hsl(var(--foreground)); }
.article .note-fig svg .t-mut { fill: hsl(var(--muted-foreground)); }
.article .note-fig svg .t-pri { fill: hsl(var(--primary)); }
.article .note-fig svg .t-grn { fill: hsl(var(--smui-green)); }
.article .note-fig svg .t-org { fill: hsl(var(--smui-orange)); }
.article .note-fig svg .t-red { fill: hsl(var(--smui-red)); }
.article .note-fig svg .box { fill: hsl(var(--card)); stroke: hsl(var(--border)); stroke-width: 1.2; }
.article .note-fig svg .box-pri { fill: hsl(var(--primary) / .12); stroke: hsl(var(--primary)); stroke-width: 1.4; }
.article .note-fig svg .box-grn { fill: hsl(var(--smui-green) / .12); stroke: hsl(var(--smui-green)); stroke-width: 1.4; }
.article .note-fig svg .box-org { fill: hsl(var(--smui-orange) / .12); stroke: hsl(var(--smui-orange)); stroke-width: 1.4; }
.article .note-fig svg .box-dash { fill: none; stroke: hsl(var(--border)); stroke-width: 1.2; stroke-dasharray: 4 4; }
.article .note-fig svg .ln { stroke: hsl(var(--muted-foreground)); stroke-width: 1.4; fill: none; }
.article .note-fig svg .ln-pri { stroke: hsl(var(--primary)); stroke-width: 1.6; fill: none; }
.article .note-fig svg .ln-dash { stroke: hsl(var(--smui-red)); stroke-width: 1.6; fill: none; stroke-dasharray: 5 4; }
.article .note-fig svg .fill-mut { fill: hsl(var(--muted-foreground)); }
.article .note-fig svg .fill-pri { fill: hsl(var(--primary)); }
.article .note-fig svg .fill-bg { fill: hsl(var(--background)); }
.article .note-fig svg .fill-track { fill: hsl(var(--secondary)); }
.article .lesson-note .note-end {
  margin-top: 26px; padding-top: 14px; border-top: 1px solid hsl(var(--border));
  font-size: 12px; color: hsl(var(--muted-foreground));
}

/* Новое: те же блоки, но в текущем языке дизайна (скругления, воздух). */
.article .note-fig, .article .note-callout, .article .note-quote,
.article .note-cmp-wrap, .article .link-tile, .article .dl-card, .article .sca-card,
.article .page-faq, .article .skills-scope-card, .article .anchor-chip,
.article .sca-flow, .article .note-toc-chip { border-radius: 12px; }
/* Схема «команда vs скилл vs агент» — одна плашка, а не россыпь строк. */
.article .sca-flow { padding: 16px 18px; }
/* Блок кода и терминала: заливка совпадала с фоном страницы — получалась
   рамка вокруг пустоты. Даём фон; пунктир при заливке лишний.
   */
.article .note-quote {
  background: hsl(var(--secondary)); border: 1px solid hsl(var(--border));
}
.article .note-quote-label { color: hsl(var(--primary)); }
.article .note-fig { background: hsl(var(--card)); }


/* «Уровни установки»: две колонки по 300px в читалке не помещались — дерево
   каталогов обрезалось, а подпись на стрелке ломалась на три строки. Ставим
   карточки друг под друга, стрелку — между ними (вариант из мобильных правил
   исходных стилей). */
.article .skills-scope-diagram { grid-template-columns: 1fr; gap: 10px; align-items: stretch; }
.article .skills-scope-arrow { flex-direction: row; justify-content: center; gap: 10px; padding: 4px 0; }
.article .skills-scope-arrow-line { width: 46px; }
.article .skills-scope-arrow-line::after { content: '↓'; right: auto; left: 50%; top: -9px; }
.article .skills-scope-arrow-label { white-space: nowrap; }
.article .skills-scope-card { border-radius: 12px; }
.article .skills-scope-tree pre { border-radius: 10px; background: hsl(var(--secondary)); }

/* Схема «команда vs скилл vs агент»: каждая строка — один сценарий. Заливка
   по цвету механизма связывает реплику с результатом, иначе схема читается
   как набор отдельных фраз. */
.article .sca-flow { gap: 10px 8px; }
.article .sca-flow-msg, .article .sca-flow-result {
  padding: 8px 11px; border-radius: 9px; background: hsl(var(--secondary) / .7);
}
.article .sca-flow-msg--cmd, .article .sca-flow-result--cmd { background: hsl(var(--primary) / .09); }
.article .sca-flow-msg--skill, .article .sca-flow-result--skill { background: hsl(var(--smui-green) / .10); }
.article .sca-flow-msg--agent, .article .sca-flow-result--agent { background: hsl(39 80% 55% / .13); }
.article .sca-flow-msg { justify-content: space-between; }
.article .sca-flow-result { align-self: stretch; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }

/* Три карточки «команда/скилл/агент» в колонке 700px были бы по 220px —
   текста в них много. Ставим в столбик с воздухом. */
.article .sca-cards { grid-template-columns: 1fr; gap: 12px; }
/* Раздел статьи — не карточка. Раньше каждый раздел был плашкой с рамкой и
   оранжевой полосой: на длинной статье это читается тяжело. Оставляем
   заголовок с тонкой линией и воздух между разделами — градация та же,
   а страница дышит. */
.article .page-sect { background: none; border: 0; padding: 0; margin: 30px 0 0; }
.article .page-sect + .page-sect { margin-top: 30px; }
.article .page-sect-no {
  font-family: var(--font-mono); font-size: 12px; font-weight: 700; letter-spacing: .09em;
  text-transform: uppercase; color: hsl(var(--foreground));
  margin: 0 0 12px; padding-bottom: 8px; border-bottom: 1px solid hsl(var(--border));
}
.article .page-sect p, .article .page-sect li { font-size: 13.5px; line-height: 1.72; }
.article .page-sect p { margin: 0 0 10px; max-width: 66ch; }
.article .lesson-note { max-width: none; }
.article .lesson-note > p { font-size: 13.5px; line-height: 1.72; }
.article .note-h2, .article .page-h2 { scroll-margin-top: 20px; }
.article .page-sect { scroll-margin-top: 20px; }
.article img, .article svg { max-width: 100%; }
.article .page-table, .article .note-cmp { display: table; width: 100%; }
.article .note-cmp-wrap, .article .page-table-wrap { overflow-x: auto; }


/* Портал занят: ответ идёт дольше четверти секунды. Полоска поверх страницы
   вместо стирания экрана — содержимое остаётся на месте, ничего не мигает.
   Стирание ради крутилки и было тем, что люди называли «перезагрузкой». */
body.is-busy::after {
  content: ""; position: fixed; left: 0; right: 0; top: 0; height: 2px; z-index: 80;
  background: linear-gradient(90deg, transparent, hsl(var(--primary)), transparent);
  background-size: 40% 100%; background-repeat: no-repeat;
  animation: kg-busy 1.1s linear infinite;
}
@keyframes kg-busy { from { background-position: -40% 0; } to { background-position: 140% 0; } }
@media (prefers-reduced-motion: reduce) {
  body.is-busy::after { animation: none; background-size: 100% 100%; opacity: .5; }
}

/* ═══ Читалка статей — предмет этого прототипа ═══════════════════════════ */
.reader { display: grid; grid-template-columns: 252px minmax(0, 1fr) 212px; gap: 26px; align-items: start; }
/* Экран модуля — без оглавления: колонок две. */
.reader.is-2col { grid-template-columns: 252px minmax(0, 1fr); }

/* Колонка 1 — все статьи раздела. Видно всегда: попав в статью, ты не теряешь
   ни списка соседних, ни своего места в нём. */
.reader-nav {
  position: sticky; top: 0; background: hsl(var(--card));
  border: 1px solid hsl(var(--border)); border-radius: 14px;
  max-height: calc(100vh - var(--kg-navbar-h) - 78px); overflow-y: auto; padding-bottom: 8px;
}
.rn-search { position: sticky; top: 0; z-index: 2; padding: 10px; background: hsl(var(--card));
  border-bottom: 1px solid hsl(var(--border)); }
.rn-search input { width: 100%; height: 30px; padding: 0 10px; border-radius: 8px;
  border: 1px solid hsl(var(--input)); background: hsl(var(--background));
  font-size: 12px; outline: none; }
.rn-search input:focus { border-color: hsl(var(--primary)); }
/* Два уровня: раздел → модуль → статья. Разделы разделены полосой и отступом,
   модули сворачиваются — иначе шесть модулей курса превращают меню в простыню. */
.rn-sec { display: flex; align-items: center; gap: 8px; padding: 13px 12px 8px;
  font-family: var(--font-mono); font-size: 9.5px; font-weight: 700; letter-spacing: .13em;
  text-transform: uppercase; color: hsl(var(--foreground));
  background: hsl(var(--secondary) / .4); }
.rn-sec.is-split { margin-top: 12px; border-top: 1px solid hsl(var(--border)); padding-top: 14px; }
.rn-sec .ic { font-size: 12px; }
.rn-modbtn { display: flex; align-items: center; gap: 8px; width: 100%; padding: 7px 12px;
  background: none; border: 0; text-align: left; text-decoration: none;
  font-size: 12.5px; font-weight: 600;
  line-height: 1.3; color: hsl(var(--foreground)); }
.rn-modbtn:hover { background: hsl(var(--secondary) / .55); }
.rn-modbtn .t { min-width: 0; }
.rn-modbtn .n { margin-left: auto; flex: none; font-family: var(--font-mono); font-size: 10px;
  color: hsl(var(--muted-foreground)); }
.rn-modbtn .chev { flex: none; opacity: .5; transition: transform .15s; }
.rn-mod.open .rn-modbtn .chev { transform: rotate(90deg); }
.rn-list { display: none; padding: 2px 0 8px; }
.rn-mod.open .rn-list { display: block; }
/* База знаний: раздел — заголовок, а не сворачивающаяся кнопка (правка
   владельца 24.09). Разделов три-четыре, статей внутри единицы — прятать их
   за стрелкой не от чего. Список раскрыт ВСЕГДА и ровно этим правилом, а не
   классом `open`: его снимают markNav и сброс поиска. */
.rn-modh { display: flex; align-items: center; padding: 12px 12px 3px;
  font-family: var(--font-mono); font-size: 9.5px; font-weight: 700; letter-spacing: .1em;
  text-transform: uppercase; color: hsl(var(--muted-foreground)); }
.rn-mod.is-flat .rn-list { display: block; }
.rn-mod.is-flat + .rn-mod.is-flat .rn-modh { margin-top: 4px; }
.rn-item { display: flex; align-items: center; gap: 9px; width: 100%; padding: 6px 12px 6px 20px;
  background: none; border: 0; border-left: 2px solid transparent; text-align: left;
  text-decoration: none; font-size: 12.5px; line-height: 1.35; color: hsl(var(--muted-foreground)); }
/* Точки состояния у названий убраны (правка владельца 14.09): пустой кружок
   у каждого урока — шум. Отметка «пройдено» осталась, но галочкой и только
   там, где есть что отмечать: пустого места она не занимает.
   Сам элемент .st из разметки убран, правило оставлено на случай старой
   вкладки, открытой до выкладки. */
.rn-item .st { display: none; }
.rn-item.is-done .t::after { content: '✓'; margin-left: 6px; font-size: 11px;
  color: hsl(var(--kg-ui-mint)); font-weight: 700; }
.rn-item .ic { flex: none; font-size: 12px; width: 15px; text-align: center; opacity: .8; }
.rn-item .t { min-width: 0; }
.rn-item .n { margin-left: auto; flex: none; font-family: var(--font-mono); font-size: 9.5px;
  letter-spacing: .06em; opacity: .6; }
.rn-item:hover:not(.is-soon) { background: hsl(var(--secondary) / .55); color: hsl(var(--foreground)); }
.rn-item.on { background: hsl(var(--accent)); color: hsl(var(--foreground));
  font-weight: 600; border-left-color: hsl(var(--primary)); }
.rn-item.is-hub { color: hsl(var(--foreground)); }
.rn-item.is-soon { opacity: .45; cursor: default; }
.rn-empty { padding: 14px 12px; font-size: 12px; color: hsl(var(--muted-foreground)); }
/* ⚠️ Поиск по разделу прячет строки атрибутом `hidden`, а `display: flex`
   у `.rn-item` его перебивает — правило класса сильнее `[hidden]` из стилей
   браузера, и найденное соседствовало с ненайденным. Та же ловушка, что с
   `.less`, `#segFlow` и `.seg`. */
.rn-item[hidden], .rn-mod[hidden] { display: none !important; }

/* Колонка 2 — сама статья. Колонка держит ширину строки ~72 символа и стоит
   ПО ЦЕНТРУ: сейчас текст прижат к левому краю широкого экрана. */
.reader-main { min-width: 0; display: flex; justify-content: center; }
/* Колонка статьи фиксированной ширины и по центру. Проза уже (66ch — комфортная
   длина строки), схемы, таблицы и карточки занимают всю ширину колонки: так
   картинки читаются, а текст не растягивается на весь экран. */
.article { width: 100%; max-width: 700px; }
.art-body > p, .art-body > ul, .art-body > ol, .art-lead { max-width: 66ch; }
.art-head { padding-bottom: 18px; margin-bottom: 6px; border-bottom: 1px solid hsl(var(--border)); }
.art-kicker { font-family: var(--font-mono); font-size: 9.5px; font-weight: 600;
  letter-spacing: .18em; text-transform: uppercase; color: hsl(var(--muted-foreground)); }
.art-title { font-family: var(--font-mono); font-size: 27px; font-weight: 700;
  letter-spacing: -.02em; line-height: 1.18; margin: 12px 0 12px; text-wrap: balance; }
.art-title em { font-style: normal; color: hsl(var(--primary)); }
.art-lead { font-size: 14px; line-height: 1.62; color: hsl(var(--muted-foreground));
  max-width: 62ch; margin: 0; }
.art-meta { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; margin-top: 18px;
  font-family: var(--font-mono); font-size: 10.5px; letter-spacing: .04em;
  color: hsl(var(--muted-foreground)); }
.art-meta b { color: hsl(var(--foreground)); font-variant-numeric: tabular-nums; }
.art-meta .btn { margin-left: auto; }
.art-done.is-done { border-color: hsl(var(--kg-ui-mint) / .5); color: hsl(var(--kg-ui-mint)); }
.art-body { max-width: none; }
.art-nav { display: flex; gap: 10px; margin-top: 26px; padding-top: 18px;
  border-top: 1px solid hsl(var(--border)); }
.art-nav .btn { max-width: 46%; overflow: hidden; }
.art-nav .btn span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Оглавление внутри статьи — на средних экранах, вместо правой колонки. */
.art-toc { display: none; margin: 18px 0 4px; border: 1px solid hsl(var(--border));
  border-radius: 12px; background: hsl(var(--card)); padding: 10px 14px; }
.art-toc[hidden] { display: none !important; }
.art-toc summary { font-family: var(--font-mono); font-size: 10.5px; letter-spacing: .1em;
  text-transform: uppercase; color: hsl(var(--muted-foreground)); cursor: pointer; }
.art-toc-list { margin-top: 10px; display: grid; gap: 4px; }

/* Колонка 3 — где я в статье. Скроллспай подсвечивает текущий раздел. */
.reader-side { position: sticky; top: 0; padding-top: 2px;
  max-height: calc(100vh - var(--kg-navbar-h) - 78px); overflow-y: auto; overscroll-behavior: contain; }
.outline-h { font-family: var(--font-mono); font-size: 9.5px; letter-spacing: .12em;
  text-transform: uppercase; color: hsl(var(--muted-foreground)); margin-bottom: 10px; padding-left: 11px; }
.outline-i { display: block; width: 100%; text-align: left; text-decoration: none;
  padding: 5px 6px 5px 11px; background: none; border: 0; border-left: 2px solid hsl(var(--border));
  font-size: 11.5px; line-height: 1.4; color: hsl(var(--muted-foreground)); }
.outline-i:hover { color: hsl(var(--foreground)); }
.outline-i.on { color: hsl(var(--foreground)); font-weight: 600; border-left-color: hsl(var(--primary)); }
.outline-foot { margin-top: 14px; padding: 12px 0 0 11px; border-top: 1px solid hsl(var(--border));
  font-family: var(--font-mono); font-size: 10px; color: hsl(var(--muted-foreground)); }
.outline-foot .progress { margin-top: 6px; }

/* Хаб модуля */
.hub-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 10px; margin: 20px 0 8px; }
.hub-card { display: flex; gap: 12px; padding: 13px 15px; border-radius: 12px;
  background: hsl(var(--card)); border: 1px solid hsl(var(--border)); }
.hub-card .ic { flex: none; width: 30px; height: 30px; border-radius: 9px; display: grid;
  place-items: center; background: hsl(var(--secondary)); }
.hub-card b { display: block; font-family: var(--font-mono); font-size: 10.5px; font-weight: 700;
  letter-spacing: .1em; text-transform: uppercase; margin-bottom: 5px; }
.hub-card p { margin: 0; font-size: 12.5px; line-height: 1.55; color: hsl(var(--muted-foreground)); }
.hub-card.is-meet .ic { background: hsl(var(--kg-ui-yellow) / .16); color: hsl(var(--kg-ui-yellow)); }
.hub-h { margin: 26px 0 10px; font-family: var(--font-mono); font-size: 10.5px; font-weight: 700;
  letter-spacing: .12em; text-transform: uppercase; color: hsl(var(--muted-foreground));
  padding-bottom: 8px; border-bottom: 1px solid hsl(var(--border)); }
.hub-lessons { display: grid; gap: 6px; }
.hub-lesson { display: flex; align-items: center; gap: 14px; width: 100%; text-align: left;
  padding: 11px 15px; border-radius: 11px; background: hsl(var(--card));
  border: 1px solid hsl(var(--border)); font-size: 13px; color: hsl(var(--foreground)); }
.hub-lesson i { flex: none; font-style: normal; font-family: var(--font-mono); font-size: 11px;
  color: hsl(var(--primary)); width: 14px; }
.hub-lesson span { min-width: 0; }
.hub-lesson em { margin-left: auto; font-style: normal; font-family: var(--font-mono);
  font-size: 10px; letter-spacing: .06em; color: hsl(var(--muted-foreground)); white-space: nowrap; }
.hub-lesson:hover { border-color: hsl(var(--primary) / .5); }
.hub-lesson.is-soon { opacity: .55; }

@media (max-width: 1260px) {
  .reader, .reader.is-2col { grid-template-columns: 236px minmax(0, 1fr); }
  .reader-side { display: none; }
  .art-toc { display: block; }
}
@media (max-width: 900px) {
  .reader, .reader.is-2col { grid-template-columns: 1fr; }
  .reader-nav { position: static; max-height: 320px; }
  .article { max-width: none; }
  .art-title { font-size: 22px; }
}

/* Сравнительная таблица на телефоне: строка становится карточкой, а название
   столбца — подписью над значением. Включается только для ячеек с data-th:
   без подписей получился бы столбик значений, из которого не понять, что есть что,
   поэтому старые таблицы (база знаний) продолжают прокручиваться вбок. */
@media (max-width: 560px) {
  .article .note-cmp-wrap:has(td[data-th]) { overflow-x: visible; border: none; }
  .article .note-cmp:has(td[data-th]) { font-size: 12.5px; }
  .article .note-cmp:has(td[data-th]) thead { display: none; }
  .article .note-cmp:has(td[data-th]) tbody tr {
    display: block; margin-bottom: 10px; padding: 10px 12px;
    border: 1px solid hsl(var(--border)); border-radius: 12px;
  }
  /* Блоками становятся ВСЕ ячейки такой таблицы: в строке может быть ячейка
     без подписи (пустой заголовок столбца), и она иначе осталась бы ячейкой
     таблицы посреди карточки. */
  .article .note-cmp:has(td[data-th]) tbody td { display: block; padding: 6px 0; border-bottom: none; }
  .article .note-cmp:has(td[data-th]) tbody td + td { border-top: 1px dashed hsl(var(--border)); }
  .article .note-cmp td[data-th]::before {
    content: attr(data-th); display: block; margin-bottom: 2px;
    font-family: var(--font-mono); font-size: 9.5px; font-weight: 700;
    letter-spacing: .06em; text-transform: uppercase; color: hsl(var(--muted-foreground));
  }
  .article .note-cmp tbody td[data-th]:first-child::before { content: attr(data-th); }
}

/* ══════ КОНЕЦ: статьи и читалка ══════ */

/* Сортируемые заголовки таблиц: стрелка показывает текущее направление.
   Активный столбец подсвечен, чтобы было видно, по чему идёт сортировка. */
.tbl th.th-sort { cursor: pointer; user-select: none; white-space: nowrap; }
.tbl th.th-sort:hover { color: var(--kg-text); }
.tbl th.th-sort.on { color: var(--kg-text); font-weight: 600; }

/* ══════ НАЧАЛО: шаблоны приложений и промпты ══════
   Витрина «9 шаблонов приложений» и «DEV портал»: карточка с превью,
   страница шаблона с живым демо в рамке и текстом промпта.
   Демо живут отдельными страницами (/demos/<приложение>/) и своим стилем
   (demos/_kit/kit.css) — здесь только витрина портала. */

.tpl-lead { max-width: 760px; margin: -2px 0 20px; font-size: 13.5px; line-height: 1.6;
  color: hsl(var(--muted-foreground)); }

.tpl-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 16px; }
.tpl { display: flex; flex-direction: column; background: hsl(var(--card));
  border: 1px solid hsl(var(--border)); border-radius: 16px; overflow: hidden;
  transition: border-color .15s, transform .15s; }
.tpl:hover { border-color: hsl(var(--primary) / .55); transform: translateY(-2px); }
/* Ссылка занимает превью и текст, а кнопки стоят рядом с ней: кнопка внутри
   ссылки — недопустимая разметка, и клавиатурой она не нажимается. */
.tpl-link { display: flex; flex-direction: column; flex: 1; min-height: 0;
  text-decoration: none; color: inherit; }
.tpl-link:focus-visible { outline: 2px solid hsl(var(--ring)); outline-offset: -2px; }

/* Превью: миниатюра экрана приложения. Рисуется примитивами с настоящими
   подписями и числами, а не серыми полосками, — карточка должна читаться как
   уменьшенный экран, а не как заглушка. Картинкой не делаем: она не живёт в
   двух темах и весит. */
.tpl-shot { --c: var(--kg-ui-orange); height: 150px; padding: 11px; overflow: hidden;
  background: hsl(var(--secondary) / .5); border-bottom: 1px solid hsl(var(--border));
  display: flex; flex-direction: column; gap: 6px; }
.tpl-shot .s-row { display: flex; gap: 6px; align-items: stretch; min-height: 0; }
.tpl-shot .s-col { display: flex; flex-direction: column; gap: 6px; min-width: 0; flex: 1; }
.tpl-shot .s-p { background: hsl(var(--card)); border: 1px solid hsl(var(--border));
  border-radius: 6px; padding: 6px 7px; min-width: 0; flex: 1;
  display: flex; flex-direction: column; gap: 3px; justify-content: center; }
.tpl-shot .s-t { font-family: var(--font-mono); font-size: 7px; letter-spacing: .06em;
  text-transform: uppercase; color: hsl(var(--muted-foreground)); white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis; }
.tpl-shot .s-v { font-size: 13px; font-weight: 700; letter-spacing: -.02em; line-height: 1;
  font-variant-numeric: tabular-nums; white-space: nowrap; }
.tpl-shot .s-s { font-size: 9px; color: hsl(var(--muted-foreground)); white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis; }
.tpl-shot .s-s b { color: hsl(var(--foreground)); font-weight: 600; }
.tpl-shot .s-up { color: hsl(var(--kg-ui-mint)); }
.tpl-shot .s-dn { color: hsl(var(--destructive)); }
.tpl-shot .s-hi { color: hsl(var(--c)); }
.tpl-shot .s-bars { display: flex; align-items: flex-end; gap: 3px; flex: 1; min-height: 26px; }
.tpl-shot .s-bars > i { flex: 1; border-radius: 2px 2px 0 0; background: hsl(var(--c) / .38); }
.tpl-shot .s-bars > i.on { background: hsl(var(--c)); }
.tpl-shot .s-line { display: flex; align-items: center; gap: 5px; font-size: 9px; min-width: 0; }
.tpl-shot .s-line > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tpl-shot .s-line .nm { flex: 1; }
.tpl-shot .s-av { width: 13px; height: 13px; border-radius: 50%; flex-shrink: 0;
  background: hsl(var(--c) / .18); color: hsl(var(--c)); font-size: 6px; font-weight: 700;
  display: grid; place-items: center; font-family: var(--font-mono); }
.tpl-shot .s-bg { display: inline-block; padding: 0 4px; border-radius: 4px; font-size: 7px;
  font-family: var(--font-mono); font-weight: 600; flex-shrink: 0;
  background: hsl(var(--c) / .16); color: hsl(var(--c)); }
.tpl-shot .s-bg.g { background: hsl(var(--muted-foreground) / .14); color: hsl(var(--muted-foreground)); }
.tpl-shot .s-bg.ok { background: hsl(var(--kg-ui-mint) / .16); color: hsl(var(--kg-ui-mint)); }
.tpl-shot .s-tabs { display: flex; gap: 8px; border-bottom: 1px solid hsl(var(--border));
  padding-bottom: 4px; font-size: 8px; color: hsl(var(--muted-foreground)); }
.tpl-shot .s-tabs b { color: hsl(var(--c)); font-weight: 700;
  border-bottom: 1.5px solid hsl(var(--c)); padding-bottom: 4px; margin-bottom: -5px; }
.tpl-shot .s-kan { display: flex; gap: 5px; flex: 1; min-height: 0; }
.tpl-shot .s-kan > div { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 4px; }
.tpl-shot .s-kc { background: hsl(var(--card)); border: 1px solid hsl(var(--border));
  border-radius: 4px; height: 15px; }
.tpl-shot .s-kc.a { border-top: 2px solid hsl(var(--c)); }
.tpl-shot .s-kc.b { border-top: 2px solid hsl(var(--kg-ui-mint)); }
.tpl-shot .s-grid { display: grid; gap: 2px; flex: 1; }
.tpl-shot .s-grid i { border-radius: 2px; background: hsl(var(--c) / .2); }
.tpl-shot .s-grid i.hot { background: hsl(var(--destructive) / .42); }
.tpl-shot .s-grid i.warm { background: hsl(var(--kg-ui-yellow) / .48); }
.tpl-shot .s-grid i.cool { background: hsl(var(--kg-ui-mint) / .42); }
.tpl-shot .s-grid i.off { background: hsl(var(--muted-foreground) / .13); }

.tpl-b { padding: 14px 16px 12px; flex: 1; display: flex; flex-direction: column; gap: 7px; }
.tpl-b h3 { margin: 0; font-size: 14.5px; font-weight: 700; letter-spacing: -.01em; }
.tpl-b p { margin: 0; font-size: 12.5px; line-height: 1.55; color: hsl(var(--muted-foreground)); }
.tpl-meta { display: flex; flex-wrap: wrap; gap: 5px; margin-top: auto; padding-top: 4px; }
.tpl-meta span { font-family: var(--font-mono); font-size: 9.5px; letter-spacing: .04em;
  text-transform: uppercase; color: hsl(var(--muted-foreground));
  border: 1px solid hsl(var(--border)); border-radius: 6px; padding: 2px 6px; }
.tpl-f { display: flex; align-items: center; gap: 8px; padding: 10px 16px;
  border-top: 1px solid hsl(var(--border)); background: hsl(var(--secondary) / .35); }
.tpl-tag { font-family: var(--font-mono); font-size: 10px; font-weight: 600; letter-spacing: .05em;
  text-transform: uppercase; color: hsl(var(--c, var(--kg-ui-orange))); margin-right: auto; }
.tpl-f .btn { flex-shrink: 0; }

/* ── Страница одного шаблона ── */
.tpl-top { display: grid; grid-template-columns: minmax(0,1.35fr) minmax(0,.85fr); gap: 18px;
  align-items: start; margin-bottom: 20px; }
.tpl-facts { display: grid; gap: 9px; }
.tpl-fact { display: grid; grid-template-columns: 118px minmax(0,1fr); gap: 12px; align-items: baseline;
  font-size: 12.5px; line-height: 1.5; }
.tpl-fact b { font-family: var(--font-mono); font-size: 9.5px; font-weight: 600; letter-spacing: .06em;
  text-transform: uppercase; color: hsl(var(--muted-foreground)); }
.tpl-screens { margin: 0; padding: 0; list-style: none; display: grid; gap: 7px; }
.tpl-screens li { display: grid; grid-template-columns: 16px minmax(0,1fr); gap: 8px;
  font-size: 12.5px; line-height: 1.45; }
.tpl-screens svg { color: hsl(var(--kg-ui-mint)); margin-top: 2px; }

/* Рамка живого демо: окно браузера с адресом. Та же метафора, что на витрине
   приложений КГ, — сразу видно, что внутри настоящая страница, а не картинка. */
.demo-frame { border: 1px solid hsl(var(--border)); border-radius: 16px; overflow: hidden;
  background: hsl(var(--card)); }
.demo-bar { display: flex; align-items: center; gap: 10px; padding: 9px 12px;
  background: hsl(220 18% 12%); color: rgba(255,255,255,.72); }
.demo-dots { display: flex; gap: 5px; flex-shrink: 0; }
.demo-dots i { width: 9px; height: 9px; border-radius: 50%; background: rgba(255,255,255,.22); }
.demo-url { flex: 1; min-width: 0; font-family: var(--font-mono); font-size: 10.5px;
  background: rgba(255,255,255,.08); border-radius: 7px; padding: 4px 9px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.demo-bar .badge { flex-shrink: 0; }
.demo-bar .btn { height: 24px; padding: 0 9px; font-size: 11px; border-radius: 7px;
  background: rgba(255,255,255,.10); border-color: rgba(255,255,255,.18); color: #fff; }
.demo-bar .btn:hover { background: rgba(255,255,255,.18); }
.demo-frame iframe { display: block; width: 100%; height: 680px; border: 0; background: hsl(var(--background));
  transition: height .18s ease; }   /* высоту уточняет fitDemo() по содержимому */

.tpl-src { margin-top: 20px; }
.tpl-src pre { margin: 0; max-height: 460px; overflow: auto; padding: 16px 18px;
  font-family: var(--font-mono); font-size: 11.5px; line-height: 1.62; white-space: pre-wrap;
  word-break: break-word; color: hsl(var(--foreground)); }
/* Промпт — размеченным документом, а не сырым markdown (правка владельца
   24.09). Разбор — mdDoc() в views-prompts.js. Сам текст для агента (блоки
   ``` в файле) — .pr-code: читается как текст, разделы капсом подсвечены. */
.pr-doc { padding: 20px 24px 24px; max-width: 860px; font-size: 13.5px; line-height: 1.65; }
.pr-doc > :first-child { margin-top: 0; }
.pr-doc p { margin: 0 0 12px; }
.pr-doc h2, .pr-doc h3, .pr-doc h4, .pr-doc h5 { text-wrap: balance; line-height: 1.3; }
.pr-doc .pr-h1 { font-size: 20px; font-weight: 700; margin: 28px 0 10px; }
.pr-doc .pr-h2 { font-size: 16px; font-weight: 700; margin: 22px 0 8px; }
.pr-doc .pr-h3, .pr-doc .pr-h4 { font-family: var(--font-mono); font-size: 11px; font-weight: 700;
  letter-spacing: .08em; text-transform: uppercase; color: hsl(var(--primary)); margin: 20px 0 8px; }
.pr-doc hr { border: 0; border-top: 1px solid hsl(var(--border)); margin: 22px 0; }
.pr-doc ul, .pr-doc ol { margin: 0 0 12px; padding-left: 22px; }
.pr-doc li { margin: 3px 0; }
.pr-doc li::marker { color: hsl(var(--primary)); }
.pr-doc code { font-family: var(--font-mono); font-size: 12px; background: hsl(var(--secondary));
  border: 1px solid hsl(var(--border)); border-radius: 5px; padding: 0 5px; }
.pr-doc blockquote { margin: 0 0 12px; padding: 10px 14px; border-radius: 10px;
  background: hsl(var(--accent)); color: hsl(var(--foreground)); font-size: 13px; }
.pr-tbl { overflow-x: auto; margin: 0 0 14px; border: 1px solid hsl(var(--border)); border-radius: 10px; }
.pr-tbl table { border-collapse: collapse; width: 100%; font-size: 12.5px; }
.pr-tbl th { text-align: left; font-family: var(--font-mono); font-size: 10px; font-weight: 600; letter-spacing: .08em;
  text-transform: uppercase; color: hsl(var(--muted-foreground)); background: hsl(var(--secondary) / .6);
  padding: 8px 12px; border-bottom: 1px solid hsl(var(--border)); }
.pr-tbl td { padding: 8px 12px; border-bottom: 1px solid hsl(var(--border)); vertical-align: top; }
.pr-tbl tr:last-child td { border-bottom: 0; }
.pr-code { margin: 0 0 14px; padding: 14px 16px; border-radius: 12px; border: 1px solid hsl(var(--primary) / .3);
  background: hsl(var(--primary) / .04); font-size: 13px; line-height: 1.6; overflow-x: auto; }
.pr-code-l { display: block; white-space: pre-wrap; word-break: break-word; }
.pr-code-l.is-mono { font-family: var(--font-mono); font-size: 11.5px; white-space: pre; }
.pr-code-h { display: block; margin: 12px 0 4px; font-family: var(--font-mono); font-size: 11px; font-weight: 700;
  letter-spacing: .08em; color: hsl(var(--primary)); }
.pr-code-h:first-child { margin-top: 0; }
@media (max-width: 640px) { .pr-doc { padding: 16px; } }

@media (max-width: 1100px) { .tpl-top { grid-template-columns: 1fr; } }
@media (max-width: 760px) {
  .tpl-grid { grid-template-columns: 1fr; }
  .demo-frame iframe { height: 560px; }   /* до замера; дальше — по содержимому */
  .tpl-fact { grid-template-columns: 1fr; gap: 2px; }
}
/* ══════ КОНЕЦ: шаблоны приложений и промпты ══════ */


/* ═══ НАЧАЛО: живые встречи (раздел /app/meetings, 14.09) ═══════════════════
   Раздел общий для портала: встреча бывает сразу для нескольких программ,
   поэтому она не принадлежит курсу и цветом кодируется по программе.
   Цвет берётся из program.json (поле color) — см. progColor в shell.js. */
.cal { border: 1px solid hsl(var(--border)); border-radius: 14px; background: hsl(var(--card)); overflow: hidden; }
.cal-h { display: flex; align-items: center; gap: 10px; padding: 10px 12px; border-bottom: 1px solid hsl(var(--border)); }
.cal-h b { font-size: 14px; font-weight: 700; min-width: 168px; }
.cal-nav { display: flex; gap: 6px; margin-left: auto; }
.cal-grid { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); }
.cal-dow { font-family: var(--font-mono); font-size: 10px; letter-spacing: .08em; text-transform: uppercase;
  color: hsl(var(--muted-foreground)); padding: 7px 8px; border-bottom: 1px solid hsl(var(--border));
  background: hsl(var(--secondary) / .45); }
.cal-day { min-height: 104px; padding: 6px 6px 8px; display: flex; flex-direction: column; gap: 4px;
  border-right: 1px solid hsl(var(--border)); border-bottom: 1px solid hsl(var(--border)); }
.cal-day:nth-child(7n) { border-right: 0; }
.cal-day.out { background: hsl(var(--secondary) / .35); }
.cal-day.out .cal-n { opacity: .35; }
.cal-day.wknd { background: hsl(var(--secondary) / .25); }
.cal-n { font-family: var(--font-mono); font-size: 11px; color: hsl(var(--muted-foreground));
  width: 20px; height: 20px; display: grid; place-items: center; border-radius: 50%; }
.cal-day.today .cal-n { background: hsl(var(--primary)); color: #fff; font-weight: 700; }
.cal-ev { display: block; font-size: 10.5px; line-height: 1.3; padding: 4px 7px; border-radius: 6px;
  text-decoration: none; background: hsl(var(--c) / .14); color: hsl(var(--c));
  border-left: 3px solid hsl(var(--c)); overflow: hidden; }
.cal-ev:hover { background: hsl(var(--c) / .24); }
.cal-ev b { font-family: var(--font-mono); font-weight: 700; margin-right: 4px; }
/* Карточка в календаре — две строки: время с названием и ведущий под ними
   (правка владельца 14.09). Обе строки в одну линию с многоточием: в ячейку
   дня шире не влезет, а перенос сделал бы высоту ячеек разной. */
.cal-ev .ev-t, .cal-ev .ev-w { display: block; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis; }
.cal-ev .ev-w { font-size: 9.5px; opacity: .72; margin-top: 1px; }
/* Прошедшая встреча гасится: календарь про то, что впереди, а записи всё
   равно лежат списком ниже. */
.cal-ev.past { background: hsl(var(--muted-foreground) / .1); color: hsl(var(--muted-foreground));
  border-left-color: hsl(var(--muted-foreground) / .5); }
.cal-leg { display: flex; flex-wrap: wrap; align-items: center; gap: 14px; margin: 0 2px 12px; }
.cal-leg span { display: inline-flex; align-items: center; gap: 6px; font-size: 12px;
  color: hsl(var(--muted-foreground)); }
.cal-leg i { width: 10px; height: 10px; border-radius: 3px; background: hsl(var(--c)); }
.cal-leg i.is-past { background: hsl(var(--muted-foreground) / .5); }

.meet { display: grid; grid-template-columns: 132px minmax(0, 1fr) auto auto; gap: 14px;
  align-items: center; padding: 7px 14px; border-top: 1px solid hsl(var(--border));
  text-decoration: none; color: inherit; }
.meet:first-child { border-top: 0; }
.meet:hover { background: hsl(var(--secondary) / .4); }
/* Та же приглушённая строка в общем разделе встреч. */
.meet.is-locked { opacity: .58; }
.meet.is-locked:hover { opacity: .72; background: transparent; }
/* Дата — ДВЕ строки в одной колонке: «29 сен, вт» и время под ней. Колонка
   фиксированной ширины, чтобы названия встреч начинались на одной линии, и
   достаточно широкая, чтобы ни дата, ни «время уточним» не переносились. */
.meet-when { font-family: var(--font-mono); line-height: 1.25;
  border-right: 1px solid hsl(var(--border)); padding-right: 12px; }
.meet-when b { display: block; font-size: 13px; font-weight: 700; white-space: nowrap; }
.meet-when i { display: block; font-style: normal; font-size: 11px;
  color: hsl(var(--muted-foreground)); white-space: nowrap; }
.meet-t b { display: block; font-size: 13px; font-weight: 600; }
.meet-t i { display: block; font-style: normal; font-size: 11.5px;
  color: hsl(var(--muted-foreground)); margin-top: 1px; }
/* Программа — отдельной колонкой у кнопок (правка владельца 14.09): в строке
   с названием плашка съедала место у самого названия. */
.meet-progs { display: flex; flex-wrap: wrap; gap: 4px; justify-content: flex-end; }
.meet-prog { display: inline-block; font-family: var(--font-mono); font-size: 10px; letter-spacing: .06em;
  text-transform: uppercase; color: hsl(var(--c)); background: hsl(var(--c) / .12);
  border-radius: 5px; padding: 2px 6px; white-space: nowrap; }
.meet-act { display: flex; align-items: center; gap: 8px; }
.meet.is-live { background: hsl(var(--primary) / .06); }
.live-dot { width: 7px; height: 7px; border-radius: 50%; background: hsl(var(--destructive)); flex: none; }

/* Блок «как попасть» на странице встречи: до начала — время и кнопка,
   во время — та же кнопка с пометкой. После встречи блока нет вовсе. */
.meet-join { display: flex; align-items: center; gap: 14px; margin: 16px 0;
  padding: 14px 16px; border: 1px solid hsl(var(--border)); border-radius: 14px;
  background: hsl(var(--secondary) / .45); }
.meet-join b { display: block; font-size: 14px; }
.meet-join i { display: block; font-style: normal; font-size: 12px; color: hsl(var(--muted-foreground)); margin-top: 2px; }
.meet-join .btn { margin-left: auto; }
.meet-join.is-live { border-color: hsl(var(--primary) / .5); background: hsl(var(--primary) / .07); }

@media (max-width: 900px) {
  .cal-day { min-height: 66px; }
  .cal-ev { font-size: 9.5px; }
  .meet { grid-template-columns: 116px minmax(0, 1fr); row-gap: 6px; }
  .meet-progs { justify-content: flex-start; grid-column: 2; }
  .meet-act { grid-column: 2; }
}
/* ═══ КОНЕЦ: живые встречи ══════════════════════════════════════════════════ */

/* ═══ НАЧАЛО: почта ═════════════════════════════════════════════════════════
   Раздел «Почта» — ящик портала в админке (views-mail.js).
   Две колонки: список писем и само письмо. На узком экране колонка письма
   закрывает список целиком (класс .is-open), а не жмётся рядом с ним: на
   телефоне 320-пиксельный список и письмо одновременно нечитаемы оба.      */

.mail {
  display: grid; grid-template-columns: minmax(280px, 380px) minmax(0, 1fr);
  gap: 18px; align-items: start;
}

/* Липкая колонка обязана иметь потолок высоты И min-height: 0 — без них низ
   длинного списка недостижим, а overflow на флекс-элементе не включается
   вовсе (на этом уже погорели .kgs-nav, .kb-side и .reader-side).          */
.mail-list {
  position: sticky; top: 12px; max-height: calc(100vh - 96px);
  min-height: 0; display: flex; flex-direction: column;
  background: hsl(var(--card)); border: 1px solid hsl(var(--border));
  border-radius: 12px; overflow: hidden;
}
.mail-rows { overflow-y: auto; min-height: 0; flex: 1; }
.mail-pad { padding: 16px; overflow-y: auto; min-height: 0; }

/* Переключатель ЯЩИКОВ стоит НАД папками и выглядит иначе — двумя карточками
   с адресом, а не рядом вкладок. Два одинаковых ряда вкладок подряд читались
   бы как один уровень, и «Входящие» второго ряда выглядели бы папкой внутри
   ящика из первого. Появляется только когда ящиков больше одного. */
.mail-boxes {
  display: flex; gap: 6px; padding: 8px 8px 0; flex: 0 0 auto;
  background: hsl(var(--muted) / .4);
}
.mail-box {
  flex: 1 1 0; min-width: 0; padding: 7px 10px; border-radius: 9px;
  border: 1px solid hsl(var(--border)); background: hsl(var(--background));
  text-decoration: none; color: inherit; display: block; position: relative;
}
.mail-box:hover { border-color: hsl(var(--primary) / .5); }
.mail-box.is-on { background: hsl(var(--card)); border-color: hsl(var(--primary));
  box-shadow: 0 1px 3px rgba(0,0,0,.09); }
.mail-box b {
  display: block; font-size: 12.5px; font-weight: 600; line-height: 1.2;
  color: hsl(var(--muted-foreground));
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.mail-box.is-on b { color: hsl(var(--foreground)); }
/* Адрес — моноширинным и мельче: его читают глазами по буквам, а не как текст. */
.mail-box i {
  display: block; font-style: normal; font-family: var(--font-mono, ui-monospace, monospace);
  font-size: 10.5px; line-height: 1.3; margin-top: 2px; color: hsl(var(--muted-foreground));
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.mail-box u {
  position: absolute; top: 6px; right: 8px; text-decoration: none;
  font-size: 11px; font-weight: 700; color: hsl(var(--kg-ui-yellow, 38 92% 45%));
}

.mail-tabs {
  display: flex; gap: 2px; padding: 6px; flex: 0 0 auto; justify-content: space-between;
  border-bottom: 1px solid hsl(var(--border)); background: hsl(var(--muted) / .4);
}
/* Ширина по содержимому, а не поровну: на четыре равные доли колонки
   «Отправленные» не помещается (нужно 110px, доля даёт 90), а «Спам» столько
   и не просит. */
.mail-tab {
  flex: 0 1 auto; text-align: center; padding: 7px 6px; border-radius: 7px;
  font-size: 12.5px; font-weight: 600; color: hsl(var(--muted-foreground));
  text-decoration: none; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.mail-tab:hover { background: hsl(var(--background)); color: hsl(var(--foreground)); }
.mail-tab.is-on { background: hsl(var(--card)); color: hsl(var(--foreground));
  box-shadow: 0 1px 3px rgba(0,0,0,.09); }
.mail-tab i { font-style: normal; opacity: .55; margin-left: 5px; font-weight: 500; }

/* Строка списка: кто · когда сверху, тема под ними. Тема длиннее всего,
   поэтому ей отдана вся ширина второй строкой, а не колонка рядом с датой. */
.mail-row {
  display: grid; grid-template-columns: minmax(0, 1fr) auto auto;
  gap: 2px 8px; padding: 10px 14px; text-decoration: none; color: inherit;
  border-bottom: 1px solid hsl(var(--border) / .6); cursor: pointer;
}
.mail-row:hover { background: hsl(var(--muted) / .5); }
.mail-row.is-open { background: hsl(var(--primary) / .09);
  box-shadow: inset 3px 0 0 hsl(var(--primary)); }
.mail-from {
  font-size: 13.5px; font-weight: 500; color: hsl(var(--foreground));
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.mail-when {
  grid-column: 2 / 4; font-size: 11.5px; color: hsl(var(--muted-foreground));
  white-space: nowrap; font-variant-numeric: tabular-nums;
}
.mail-subj {
  grid-column: 1 / 3; font-size: 12.5px; color: hsl(var(--muted-foreground));
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.mail-clip { grid-column: 3; align-self: center; color: hsl(var(--muted-foreground)); opacity: .7; }
.mail-ans { display: inline-flex; vertical-align: -2px; margin-right: 4px; opacity: .75; }
/* Непрочитанное — жирным и с точкой слева: цветом одним отличать нельзя,
   тема и так приглушена. */
.mail-row.is-new .mail-from { font-weight: 700; }
.mail-row.is-new .mail-subj { color: hsl(var(--foreground)); font-weight: 500; }
.mail-row.is-new .mail-from::before {
  content: ""; display: inline-block; width: 6px; height: 6px; border-radius: 50%;
  background: hsl(var(--primary)); margin-right: 7px; vertical-align: 1px;
}

.mail-msg {
  background: hsl(var(--card)); border: 1px solid hsl(var(--border));
  border-radius: 12px; min-height: 320px; overflow: hidden;
}
.mail-none {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 10px; min-height: 320px; color: hsl(var(--muted-foreground));
}
.mail-none p { margin: 0; font-size: 13.5px; }

.mail-head { padding: 18px 20px 0; }
.mail-head-top { display: flex; align-items: flex-start; gap: 12px; }
.mail-head h2 { margin: 0; font-size: 19px; line-height: 1.35; flex: 1; min-width: 0;
  overflow-wrap: anywhere; }
.mail-close { flex: 0 0 auto; }
.mail-meta { margin: 12px 0 0; display: grid; gap: 3px; font-size: 13px; }
.mail-meta > div { display: flex; gap: 8px; overflow-wrap: anywhere; }
.mail-meta span {
  flex: 0 0 52px; color: hsl(var(--muted-foreground)); font-size: 12px;
  text-transform: lowercase;
}
.mail-acts { display: flex; flex-wrap: wrap; gap: 8px; margin: 16px 0 0;
  padding-bottom: 14px; border-bottom: 1px solid hsl(var(--border)); }

.mail-imgbar {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  margin: 12px 0 0; padding: 10px 12px; border-radius: 9px;
  background: hsl(var(--muted) / .6); font-size: 12.5px;
  color: hsl(var(--muted-foreground));
}

/* Рамка письма: своих полей и рамки у неё нет — письмо должно читаться как
   часть страницы. Высоту ставит скрипт по содержимому. */
.mail-body { padding: 18px 20px; }
.mail-body.is-html {
  margin: 0 20px 18px; padding: 0; border-radius: 10px; overflow: hidden;
  border: 1px solid hsl(var(--border)); background: #fff;
}
.mail-body iframe {
  display: block; width: 100%; border: 0; min-height: 80px; background: transparent;
}

.mail-att { padding: 0 20px 18px; display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.mail-att b { flex: 0 0 100%; font-size: 12px; color: hsl(var(--muted-foreground));
  text-transform: uppercase; letter-spacing: .04em; margin-bottom: 2px; }
.mail-file {
  display: inline-flex; align-items: center; gap: 7px; max-width: 100%;
  padding: 7px 12px; border: 1px solid hsl(var(--border)); border-radius: 8px;
  background: hsl(var(--background)); text-decoration: none; color: inherit; font-size: 13px;
}
.mail-file:hover { border-color: hsl(var(--primary)); color: hsl(var(--primary)); }
.mail-file span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mail-file i { font-style: normal; color: hsl(var(--muted-foreground)); font-size: 11.5px; }

.mail-reply { margin: 0 20px 20px; padding: 14px; border-radius: 10px;
  border: 1px solid hsl(var(--border)); background: hsl(var(--muted) / .35); }
.mail-reply-h { display: flex; align-items: center; justify-content: space-between;
  margin-bottom: 10px; }
.mail-text { width: 100%; margin-top: 12px; resize: vertical; min-height: 150px;
  font-family: var(--font-sans); line-height: 1.55; }
.mail-reply-f { display: flex; align-items: center; gap: 10px; margin-top: 12px; flex-wrap: wrap; }
.mail-reply-f .muted { font-size: 12.5px; }

@media (max-width: 900px) {
  .mail { grid-template-columns: minmax(0, 1fr); }
  .mail-list { position: static; max-height: none; }
  .mail-msg { display: none; }
  /* Письмо открыто — список уходит: рядом им на телефоне не поместиться. */
  .mail.is-open .mail-list { display: none; }
  .mail.is-open .mail-msg { display: block; }
}
/* ═══ КОНЕЦ: почта ══════════════════════════════════════════════════════════ */

/* ═══ НАЧАЛО: водяной знак и полный экран у записи (15.09.2026) ═══════════════
   Знак лежит ПОВЕРХ плеера и появляется раз в две минуты на 12 секунд, меняя
   угол. Он не защищает от скачивания — он делает утёкшую копию опознаваемой.
   Стили держат три правила, каждое из которых важно:
     • pointer-events:none — знак не перехватывает клик по плееру, иначе он
       мешал бы перемотке ровно те секунды, что виден;
     • полупрозрачность и мелкий шрифт — просьба владельца «чтобы не мешало
       просмотру контента»;
     • собственная тень под текстом: знак ложится и на светлый кадр, и на
       тёмный, и без тени на светлом фоне он читаться перестаёт.            */
/* Ширина подобрана по РЕНДЕРУ, а не на глаз: в читалке видео стоит в средней
   колонке и на ноутбуке это ~490 px, где 46% давали 225 px и знак разъезжался
   на пять строк — высокий блок в углу мешает больше, чем широкий и низкий. */
.vwm {
  position: absolute; z-index: 3; max-width: min(64%, 460px);
  font-size: 8.5px; line-height: 1.5; font-weight: 500;
  color: rgba(255,255,255,.58);
  text-shadow: 0 1px 3px rgba(0,0,0,.85), 0 0 12px rgba(0,0,0,.6);
  pointer-events: none; user-select: none;
  transition: opacity .6s ease; opacity: 1;
}
.vwm[hidden] { display: none; }
.vwm-tl { top: 12px; left: 14px; }
/* Отступ справа больше, чем у остальных углов: там стоит наша кнопка полного
   экрана, и без зазора первая строка знака уезжала бы под неё. */
.vwm-tr { top: 12px; right: 52px; text-align: right; }
/* Снизу отступ больше: там панель управления плеера, и знак не должен на неё
   наезжать — иначе он и мешает, и сам нечитаем. */
.vwm-br { bottom: 54px; right: 14px; text-align: right; }
.vwm-bl { bottom: 54px; left: 14px; }

/* Родная кнопка «во весь экран» убирается ДВАЖДЫ: атрибутом controlsList и
   здесь, через теневой DOM плеера. Одного атрибута мало — в живой сборке
   Chrome кнопка на панели осталась (видно на снимке проверки), а она
   разворачивает сам <video>, рядом с которым знак не виден. В Firefox ни то,
   ни другое не действует: там остаётся родная кнопка, и полный экран будет
   без знака — это известный предел, знак в этом случае держится на остальных
   путях (окно, наша кнопка, съёмка с телефона). */
.video video::-webkit-media-controls-fullscreen-button { display: none !important; }

/* Своя кнопка «во весь экран»: разворачивает КОНТЕЙНЕР вместе со знаком. */
.vfs {
  position: absolute; z-index: 4; right: 10px; top: 10px;
  width: 30px; height: 30px; border-radius: 8px;
  border: 1px solid rgba(255,255,255,.22); background: rgba(0,0,0,.42);
  color: rgba(255,255,255,.8); font-size: 13px; line-height: 1;
  display: grid; place-items: center; cursor: pointer;
  /* Видна ВСЕГДА, а не по наведению: родную кнопку мы убрали, и на планшете
     с телефоном наведения нет вовсе — иначе полный экран стал бы недоступен. */
  opacity: .45; transition: opacity .18s ease;
}
.video:hover .vfs, .vfs:focus-visible { opacity: 1; }
.vfs:hover { background: rgba(0,0,0,.65); color: #fff; }
/* ═══ КОНЕЦ: водяной знак и полный экран у записи ════════════════════════════ */

/* ═══ НАЧАЛО: открепляемый плеер (15.09.2026) ════════════════════════════════
   Промежуточный размер между «мелко в колонке» и «весь экран» (просьба
   владельца). Родной «картинка в картинке» НЕ используется намеренно: он
   показывает только кадры, без нашей разметки, то есть без водяного знака.
   Здесь переносится/остаётся сам контейнер, и знак едет вместе с ним.       */
.video.is-float {
  position: fixed; right: 24px; bottom: 24px; z-index: 60;
  width: min(620px, 52vw); height: auto; aspect-ratio: 16 / 9;
  /* Тянется за правый нижний угол. overflow нужен самому resize, поэтому
     перекрываем hidden из базового .video. */
  resize: both; overflow: auto; min-width: 280px; min-height: 160px;
  box-shadow: 0 24px 60px -12px rgba(0,0,0,.65);
  border-color: rgba(255,255,255,.18);
  cursor: grab;
}
.video.is-float:active { cursor: grabbing; }
/* Место, откуда плеер уехал: без заглушки колонка урока схлопывается, а
   человек не понимает, куда делось видео. */
.video.vdetached {
  display: grid; place-items: center; cursor: default;
  font-size: 12px; color: rgba(255,255,255,.55);
}
.vpip {
  position: absolute; z-index: 4; right: 46px; top: 10px;
  width: 30px; height: 30px; border-radius: 8px;
  border: 1px solid rgba(255,255,255,.22); background: rgba(0,0,0,.42);
  color: rgba(255,255,255,.8); font-size: 13px; line-height: 1;
  display: grid; place-items: center; cursor: pointer;
  opacity: .45; transition: opacity .18s ease;
}
.video:hover .vpip, .vpip:focus-visible { opacity: 1; }
.vpip:hover { background: rgba(0,0,0,.65); color: #fff; }
/* Знак в правом верхнем углу отступает теперь от ДВУХ кнопок, а не одной. */
.vwm-tr { right: 88px; }
/* ═══ КОНЕЦ: открепляемый плеер ══════════════════════════════════════════════ */

/* Плеер, уехавший в отдельное окно. Отдельные правила нужны потому, что в
   окно копируются ВСЕ стили кабинета — включая отступ body под левое меню,
   которого там нет. Из-за него кадр уезжал вправо на ширину меню и оставлял
   неровные чёрные поля (замер: окно 1440×900, кадр 1216×684, сдвиг 224 px).
   `position: fixed; inset: 0` делает плеер независимым от того, что творится
   с body, а снятая пропорция позволяет ему занять окно любой формы. */
.video.is-pip {
  position: fixed; inset: 0;
  width: 100%; height: 100%; max-width: none; margin: 0;
  aspect-ratio: auto; border: 0; border-radius: 0;
}
/* Кадр вписывается целиком и центруется: поля остаются только если форма окна
   не совпала с формой записи, и тогда они РАВНЫЕ с двух сторон. */
.video.is-pip video { width: 100%; height: 100%; object-fit: contain; }

/* =============================================================================
 * НАЧАЛО: главная программы, «Мои программы» и абонемент (15.09.2026)
 * Переделано по прототипу владельца. Три правила, на которых всё стоит:
 *   1. цвет программы живёт ГРАДИЕНТОМ (полосок-подчёркиваний больше нет) и
 *      повторяется внутри программы — по нему видно, куда попал;
 *   2. приветственное видео крупное и закрывается крестиком — посмотрели
 *      раз-другой, и оно не занимает первый экран;
 *   3. у абонемента главное не модули, а встречи: они идут лентой.
 * ============================================================================= */

/* Двухколоночный экран: содержимое и правая колонка. Та же сетка на всех трёх
   экранах — иначе они разъедутся при первой же правке.
   ⚠️ align-items убран (правка владельца 18.09) — со «start» короткая колонка
   заканчивалась выше длинной, и ниже её последней карточки оставалось пустое
   место фона. По умолчанию grid растягивает обе колонки под высоту более
   длинной, а лишнюю высоту у короткой забирает её ПОСЛЕДНЯЯ карточка
   (`.hm-col > :last-child`), а не пустота под ней — тот же приём, что и на
   «Моей карточке» (`.pf-side`). */
.hm { display: grid; grid-template-columns: minmax(0, 1fr) 316px; gap: 16px; }
.hm-col { display: flex; flex-direction: column; gap: 14px; min-width: 0; }
.hm-col > :last-child { flex: 1 1 auto; }
/* Сетка программ отвязана от правой колонки (правка владельца 28.09): если
   сетка последняя в колонке (архива нет), правило выше тянуло её под высоту
   правой колонки, и ряды карточек растягивались на полэкрана. Ряды — по
   содержимому, лишнее место остаётся под сеткой. */
.hm-col > .pgrid { align-content: start; }
@media (max-width: 1100px) { .hm { grid-template-columns: 1fr; } }

.hm-next { display: flex; gap: 13px; align-items: center; }
/* Выровненная по соседу слева карточка (alignHome в views-student.js):
   лишняя высота уходит в тело, а у «Продолжить» оно по центру. */
.card.is-fit { display: flex; flex-direction: column; }
.card.is-fit > .hm-next { flex: 1 1 auto; }
.hm-next-k { font-family: var(--font-mono); font-size: 10.5px; color: hsl(var(--muted-foreground)); }
.hm-next b { display: block; font-size: 13.5px; margin: 2px 0 8px; }

/* Кольцо прогресса — один conic-gradient. Ради одного числа тянуть библиотеку
   графиков незачем. */
.ring { display: grid; place-items: center; width: 62px; height: 62px; border-radius: 50%; flex-shrink: 0;
  background: conic-gradient(hsl(var(--c, var(--primary))) calc(var(--p) * 1%),
              hsl(var(--muted-foreground) / .18) 0); }
.ring i { display: grid; place-items: center; width: 48px; height: 48px; border-radius: 50%;
  background: hsl(var(--card)); font-family: var(--font-mono); font-size: 12.5px;
  font-weight: 700; font-style: normal; }

/* ── Приветственное видео ──────────────────────────────────────────────────
   Крестик прячет блок целиком, а не сворачивает: вёрстка не едет, потому что
   блок — обычный элемент колонки, следующий просто поднимается. Вернуть можно
   кнопкой «Вводное видео» в шапке программы. */
.hello { position: relative; overflow: hidden; border-radius: 16px;
  border: 1px solid hsl(var(--border)); }
.hello-x { position: absolute; top: 10px; right: 10px; z-index: 3; width: 30px; height: 30px;
  border-radius: 9px; border: 1px solid rgba(255,255,255,.25); background: rgba(10,10,12,.55);
  color: #fff; cursor: pointer; display: grid; place-items: center; }
.hello-x:hover { background: rgba(10,10,12,.85); }
.hello-pic { position: relative; display: grid; place-items: center; aspect-ratio: 16/6;
  background: radial-gradient(120% 120% at 28% 18%, #32363F 0%, #14161B 72%); }
.hello-pic::after { content: ''; position: absolute; inset: 0;
  background: linear-gradient(180deg, transparent 42%, rgba(0,0,0,.76) 100%); }
.hello-play { position: relative; z-index: 2; width: 66px; height: 66px; border-radius: 50%;
  background: rgba(255,255,255,.95); color: #14161B; display: grid; place-items: center;
  box-shadow: 0 8px 30px rgba(0,0,0,.4); }
.hello-play svg { fill: currentColor; stroke: none; margin-left: 3px; }
.hello-cap { position: absolute; left: 20px; right: 20px; bottom: 16px; z-index: 2; color: #fff; }
.hello-cap b { display: block; font-size: clamp(15px, 2.2vw, 20px); font-weight: 750; margin: 7px 0 3px; }
.hello-cap i { font-style: normal; font-size: 12.5px; color: rgba(255,255,255,.75);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.hello-dur { position: absolute; right: 14px; bottom: 14px; z-index: 2; font-family: var(--font-mono);
  font-size: 11px; color: #fff; background: rgba(0,0,0,.55); padding: 4px 7px; border-radius: 6px; }
/* Ролик играет ПРЯМО В ПЛАШКЕ (правка владельца 21.09: «без переброса в режим
   просмотра уроков»). Плашка — баннер 16:6, запись — 16:9: не переключи мы
   пропорцию, от кадра осталась бы горизонтальная щель. Кнопка «свернуть» у
   играющего ролика не нужна — крестик плашки на месте и никуда не уезжает. */
button.hello-pic { width: 100%; border: 0; padding: 0; cursor: pointer; font: inherit; }
.hello.is-playing .hello-pic,
.hello.is-playing .hello-vid { aspect-ratio: 16/9; }
.hello-vid { display: block; width: 100%; aspect-ratio: 16/9; background: #000; }
.badge.b-soft { background: rgba(255,255,255,.18); color: #fff; }
.badge.b-orange { background: hsl(var(--primary) / .14); color: hsl(var(--primary)); }

/* ── Шапка программы ──────────────────────────────────────────────────────── */
/* ⚠️ Середина градиента — СМЕСЬ цвета с тёмным, а не цвет с прозрачностью
   (правка 17.09: «не хватает темноты и контрастности, в прототипе красивее»).
   `hsl(... / .55)` подмешивает то, что лежит ПОД плашкой, то есть светлый фон
   страницы: середина выцветала до пастели, и весь градиент выглядел размытым.
   `color-mix` с #14161B даёт ту же линию, что в прототипе, — от чистого цвета
   программы к почти чёрному, и не зависит от фона под собой. */
.phero { position: relative; overflow: hidden; border-radius: 16px; padding: 22px 24px; color: #fff;
  background: linear-gradient(135deg, hsl(var(--c, var(--primary))) 0%,
              color-mix(in srgb, hsl(var(--c, var(--primary))) 45%, #14161B) 52%, #14161B 100%); }
.phero-k { font-family: var(--font-mono); font-size: 11px; letter-spacing: .1em;
  text-transform: uppercase; color: rgba(255,255,255,.62); }
.phero h2 { margin: 6px 0 8px; font-size: clamp(20px, 3vw, 27px); font-weight: 800; letter-spacing: -.02em; }
.phero p { margin: 0 0 15px; color: rgba(255,255,255,.78); font-size: 13.5px; max-width: 64ch; }
.phero-acts { display: flex; gap: 8px; flex-wrap: wrap; }
.phero-acts .btn { background: rgba(255,255,255,.14); border-color: rgba(255,255,255,.28); color: #fff; }
.phero-acts .btn:hover { background: rgba(255,255,255,.22); }
.phero-acts .btn-primary { background: #fff; border-color: #fff; color: #14161B; }
.phero-acts .btn-primary:hover { background: rgba(255,255,255,.88); }
/* ⚠️ У каждого пункта СВОЯ ширина, и число с подписью — ПО ЦЕНТРУ этой
   ширины (правка владельца 21.09: первая попытка — просто `min-width` без
   центровки — «смотрится плохо»). Причина: у левого края короткого пункта
   («6») пусто до конца его 96 px, а у длинного («8,4 ч») пусто почти нет —
   расстояние ДО следующего пункта визуально то 22 px, то все 90+.
   Центровка убирает это: текст стоит в середине своей колонки, и зазоры
   между колонками читаются одинаковыми независимо от длины числа и подписи.
   `tabular-nums` держит одну и ту же ширину у цифр «6» и «26». Сам набор
   пунктов у программ РАЗНЫЙ (у «Бесплатно» нет домашних заданий и встреч) —
   это не чинится, каждая программа показывает только то, что у неё есть. */
.phero-stat { display: flex; gap: 22px; flex-wrap: wrap; margin-top: 18px; padding-top: 15px;
  border-top: 1px solid rgba(255,255,255,.18); }
.phero-stat > div { min-width: 96px; text-align: center; }
.phero-stat b { display: block; font-family: var(--font-mono); font-size: 17px; font-weight: 700;
  font-variant-numeric: tabular-nums; }
.phero-stat span { font-size: 11px; color: rgba(255,255,255,.62); }

/* ── Шапка программы v2 (правка владельца 24.09) ─────────────────────────────
   Прогресс и доступ (у витрины — цена) — плашкой в шапке справа, строки цифр
   нет. Плашка 360 px: строка «осталось 69 дн · продлевается абонементом»
   обязана уместиться в одну строку (правка владельца). */
.phero-v2 { display: grid; grid-template-columns: minmax(0, 1fr) 360px; gap: 18px 26px; align-items: center; padding: 20px 22px; }
.phero-v2 .phero-main { min-width: 0; }
.phero-v2 p { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; margin-bottom: 14px; }
.phero-st { border-radius: 13px; background: rgba(255,255,255,.1); box-shadow: inset 0 0 0 1px rgba(255,255,255,.16);
  padding: 13px 14px; display: grid; gap: 11px; min-width: 0; }
.phero-st .top { display: grid; grid-template-columns: 52px minmax(0, 1fr); gap: 12px; align-items: center; }
.phero-st .top b { display: block; font-size: 14px; font-weight: 700; }
.phero-st .top span { font-size: 12px; color: rgba(255,255,255,.7); }
.phero-ring { position: relative; width: 52px; height: 52px; display: block; }
.phero-ring svg { width: 52px; height: 52px; transform: rotate(-90deg); display: block; }
/* Селектор с `.phero-st .top` — иначе `.phero-st .top b` (display:block, 14px) перебивал центровку. */
.phero-st .top .phero-ring b { position: absolute; inset: 0; display: grid; place-items: center; font: 700 12px var(--font-mono); }
.phero-st .bar { height: 4px; border-radius: 4px; background: rgba(255,255,255,.18); overflow: hidden; }
.phero-st .bar i { display: block; height: 100%; background: #fff; border-radius: 4px; }
.phero-st .acc { display: grid; grid-template-columns: 26px minmax(0, 1fr); gap: 10px; align-items: center;
  border-top: 1px solid rgba(255,255,255,.16); padding-top: 10px; }
.phero-st .acc > i { width: 26px; height: 26px; border-radius: 7px; background: rgba(255,255,255,.14); display: grid; place-items: center; font-style: normal; }
.phero-st .acc b { display: block; font-size: 12.5px; font-weight: 700; }
.phero-st .acc span { display: block; font-size: 11.5px; color: rgba(255,255,255,.7); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.phero-st .acc.warn b { color: #FFD9A0; }
.phero-st .lbl { font: 600 10px var(--font-mono); letter-spacing: .1em; text-transform: uppercase; color: rgba(255,255,255,.6); }
.phero-st .price { font: 800 24px var(--font-mono); letter-spacing: -.01em; }
.phero-st .price small { display: block; font: 500 12px var(--font-sans); color: rgba(255,255,255,.7); letter-spacing: 0; margin-top: 2px; }
.phero-st p { margin: 0; font-size: 12px; color: rgba(255,255,255,.75); display: block; -webkit-line-clamp: unset; }
@media (max-width: 1100px) { .phero-v2 { grid-template-columns: 1fr; } }
/* Домашки на главной — строка задания: модуль · суть · статус · кнопка. */
.hw-row.hw-task { grid-template-columns: 30px minmax(0, 1fr) auto auto; }
.hw-task .hw-m { font: 700 11px var(--font-mono); color: hsl(var(--primary)); }
.hw-task b { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
@media (max-width: 600px) { .hw-row.hw-task { grid-template-columns: 26px minmax(0, 1fr) auto; } .hw-task .btn { display: none; } }

/* ── Карточки программ ────────────────────────────────────────────────────── */
/* ⚠️ Карточка целиком в цвете программы — тот же градиент, что у шапки внутри
   (правка владельца 17.09: «карточки более акцентны по цветам, как плашки
   главные внутри»). Раньше цвет был лёгким оттенком в углу белой карточки, и
   «Мои программы» читались как четыре одинаковых белых прямоугольника.
   Некупленная — того же цвета, но приглушённого (`--top`): она не выпадает из
   ряда, а отступает в нём на шаг. */
.pgrid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; }
@media (max-width: 720px) { .pgrid { grid-template-columns: 1fr; } }
.pgc { position: relative; border: 1px solid transparent; border-radius: 14px; overflow: hidden;
  color: #fff; --top: hsl(var(--c, var(--primary)));
  background: linear-gradient(145deg, var(--top) 0%,
              color-mix(in srgb, var(--top) 40%, #14161B) 58%, #14161B 100%); }
.pgc-in { padding: 16px; display: flex; flex-direction: column; gap: 10px; height: 100%; }
.pgc-top { display: flex; align-items: flex-start; gap: 11px; }
/* ⚠️ Символ внутри плашки крупный (правка владельца 15.09): в 17 px он терялся
   в 40-пиксельном квадрате и читался как точка. */
.pgc-ic { width: 42px; height: 42px; border-radius: 12px; flex-shrink: 0; display: grid;
  place-items: center; color: #fff;
  background: rgba(255,255,255,.16); border: 1px solid rgba(255,255,255,.22); }
.pgc h3 { margin: 0; font-size: 15.5px; font-weight: 750; line-height: 1.25; color: #fff; }
.pgc-who { font-family: var(--font-mono); font-size: 11px; color: rgba(255,255,255,.66); margin-top: 3px; }
.pgc-next { font-size: 12.5px; color: rgba(255,255,255,.78); }
.pgc-next b { color: #fff; font-weight: 600; }
.pgc-meta { font-family: var(--font-mono); font-size: 11px; color: rgba(255,255,255,.62); }
.pgc-foot { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: auto; padding-top: 4px; }
/* Кнопки на цветном — те же, что в шапке программы: белая главная, стеклянная
   вторая. Иначе на карточке стояла бы кнопка в цветах светлой темы. */
.pgc .btn { background: rgba(255,255,255,.14); border-color: rgba(255,255,255,.28); color: #fff; }
.pgc .btn:hover { background: rgba(255,255,255,.22); }
.pgc .btn-primary { background: #fff; border-color: #fff; color: #14161B; }
.pgc .btn-primary:hover { background: rgba(255,255,255,.88); filter: none; }
/* ⚠️ «Купить» — всегда ОРАНЖЕВАЯ (правка владельца 17.09: «наша оранжевая
   кнопка Купить»), в том числе на цветной карточке, где остальные кнопки
   белые и стеклянные. На карточке самого оранжевого курса её отделяет от
   фона светлая обводка — без неё она там сливается. */
.btn-buy, .pgc .btn-buy { background: hsl(var(--primary)); border-color: hsl(var(--primary));
  color: hsl(var(--primary-foreground)); }
.pgc .btn-buy { box-shadow: 0 0 0 1px rgba(255,255,255,.3); }
.btn-buy:hover, .pgc .btn-buy:hover { background: hsl(var(--primary)); filter: brightness(1.08); }
.pgc .progress { background: rgba(255,255,255,.22); }
.pgc .progress i { background: #fff; }
/* Статус «не куплено» — в правом верхнем углу карточки (правка владельца).
   ⚠️ Флаг вынут из потока, поэтому заголовку нужен отступ ПОД него: у
   «Профессии: Аналитик Битрикс24» длинная строка иначе уезжает прямо под
   плашку (поймано снимком 1440 px). */
.pgc-flag { position: absolute; top: 11px; right: 11px; z-index: 2;
  font-family: var(--font-mono); font-size: 9px; font-weight: 700; letter-spacing: .07em;
  text-transform: uppercase; padding: 3px 7px; border-radius: 7px;
  background: rgba(0,0,0,.3); border: 1px solid rgba(255,255,255,.24); color: rgba(255,255,255,.86); }
.pgc.is-locked .pgc-top { padding-right: 84px; }
/* Некупленная — СВЕТЛАЯ и в цвете программы, а не тёмная (правка владельца
   22.09, четвёртый заход: «ты убрал цвета — сделал тёмными — а надо было их
   ПРИСВЕТЛИТЬ, а цвета оставить»). Тёмный нейтральный вариант шагом раньше
   был неверным направлением — владелец просил ровно противоположное. Взят
   тот же приём, что уже используется в приложении для лёгкого цветного фона
   (`.badge`, `.progtab.on`, `.cal-ev` — везде `hsl(var(--c) / .14)` фоном и
   `hsl(var(--c))` текстом): бледная плашка того же цвета вместо тёмного
   градиента. Текст и кнопки поэтому перекрашены — белый текст на светлом фоне
   был бы нечитаем. */
.pgc.is-locked { background: hsl(var(--c, var(--primary)) / .14);
  border-color: hsl(var(--c, var(--primary)) / .3); color: hsl(var(--c, var(--primary))); }
.pgc.is-locked .pgc-ic { background: hsl(var(--c, var(--primary)) / .18);
  border-color: hsl(var(--c, var(--primary)) / .32); color: hsl(var(--c, var(--primary))); }
.pgc.is-locked h3 { color: hsl(var(--c, var(--primary))); }
.pgc.is-locked .pgc-who { color: hsl(var(--c, var(--primary)) / .68); }
.pgc.is-locked .pgc-next { color: hsl(var(--c, var(--primary)) / .82); }
.pgc.is-locked .pgc-next b { color: hsl(var(--c, var(--primary))); }
.pgc.is-locked .pgc-meta { color: hsl(var(--c, var(--primary)) / .64); }
.pgc.is-locked .btn:not(.btn-buy) { background: hsl(var(--c, var(--primary)) / .12);
  border-color: hsl(var(--c, var(--primary)) / .4); color: hsl(var(--c, var(--primary))); }
.pgc.is-locked .btn:not(.btn-buy):hover { background: hsl(var(--c, var(--primary)) / .2); }
.pgc.is-locked .pgc-flag { background: hsl(var(--c, var(--primary)) / .85);
  border-color: transparent; color: #fff; }
/* Закончившийся доступ — не то же, что «не куплено»: карточка остаётся в цвете
   программы (человек её покупал), а плашка и дата подсвечены янтарным, чтобы
   срок читался с первого взгляда. Правка владельца 18.09. */
.pgc.is-ended { --top: color-mix(in srgb, hsl(var(--c, var(--primary))) 74%, #4A3A22); }
.pgc.is-ended .pgc-top { padding-right: 96px; }
.pgc.is-ended .pgc-flag { background: rgba(255,176,32,.22); border-color: rgba(255,196,92,.55);
  color: #FFD79A; }
.pgc-ended { color: #FFD79A !important; font-weight: 600; }
.arch-row.is-ended .arch-t i { color: hsl(var(--kg-ui-yellow)); }

/* ── Абонемент: ключевая карта ────────────────────────────────────────────── */
/* flex-колонка — чтобы при `min-height` от alignHome содержимое стояло по
   центру, а не прижималось к верху. */
.subhero { position: relative; overflow: hidden; border-radius: 18px; padding: 20px 22px; color: #fff;
  display: flex; flex-direction: column; justify-content: center;
  border: 1px solid rgba(201,164,94,.45);
  background: linear-gradient(135deg, #2A2520 0%, #423729 38%, #6B5330 68%, #2A2520 100%); }
.subhero::after { content: ''; position: absolute; right: -60px; top: -80px; width: 260px; height: 260px;
  border-radius: 50%; background: radial-gradient(circle, rgba(212,173,98,.26) 0%, transparent 70%); }
.subhero-in { position: relative; z-index: 2; display: flex; gap: 20px; flex-wrap: wrap; align-items: center; }
.subhero-l { flex: 1; min-width: 260px; }
.subhero-k { font-family: var(--font-mono); font-size: 10.5px; letter-spacing: .14em;
  text-transform: uppercase; color: rgba(255,255,255,.62); }
.subhero h2 { margin: 6px 0; font-size: 21px; font-weight: 800; letter-spacing: -.02em; }
.subhero p { margin: 0 0 13px; font-size: 13px; color: rgba(255,255,255,.76); max-width: 58ch; }
.subhero-acts { display: flex; gap: 8px; flex-wrap: wrap; }
.subhero-acts .btn { background: rgba(255,255,255,.14); border-color: rgba(255,255,255,.3); color: #fff; }
.subhero-acts .btn-primary { background: linear-gradient(135deg, #E8C88B, #B98F3E);
  border-color: transparent; color: #241D14; }
/* Не подключён — та же «присветлить, цвет оставить» логика, что у карточек
   программ (правка владельца 22.09: «и абонемент тоже»). Золотой градиент
   геро раньше стоял ВСЕГДА, тёмный и бронзовый что при активной, что при
   неактивной подписке — «не подключён» отличался только надписью в статус-
   карте справа. Теперь сама карта светлеет и берёт цвет из токена жёлтого
   (program.json: "color": "yellow"), тем же приёмом, что и `.pgc.is-locked`. */
.subhero.is-off { border-color: hsl(var(--kg-ui-yellow) / .32);
  background: hsl(var(--kg-ui-yellow) / .14); color: hsl(var(--kg-ui-yellow)); }
.subhero.is-off::after { display: none; }
.subhero.is-off .subhero-k { color: hsl(var(--kg-ui-yellow) / .7); }
.subhero.is-off p { color: hsl(var(--kg-ui-yellow) / .78); }
.subhero.is-off .subhero-acts .btn:not(.btn-primary) { background: hsl(var(--kg-ui-yellow) / .12);
  border-color: hsl(var(--kg-ui-yellow) / .4); color: hsl(var(--kg-ui-yellow)); }
.subhero.is-off .subhero-acts .btn-primary { background: hsl(var(--kg-ui-yellow));
  color: #fff; }
/* Карта статуса. Металлический блик даёт ощущение уровня, а подпись рядом
   честно говорит, чем один уровень отличается от другого. */
.statuscard { width: 210px; flex-shrink: 0; border-radius: 14px; padding: 14px; color: #221B12;
  background: linear-gradient(140deg, #C9A45E 0%, #8C6C34 45%, #E4CB96 70%, #7A5C2C 100%);
  box-shadow: 0 10px 26px rgba(0,0,0,.26); }
.statuscard .lv { font-family: var(--font-mono); font-size: 10.5px; letter-spacing: .12em;
  text-transform: uppercase; opacity: .78; }
.statuscard b { display: block; font-size: 19px; font-weight: 800; margin: 3px 0 9px; }
.statuscard .stat-line { height: 5px; border-radius: 999px; background: rgba(34,27,18,.25); overflow: hidden; }
.statuscard .stat-line i { display: block; height: 100%; background: #221B12; border-radius: 999px; }
.statuscard .nx { font-size: 11px; margin-top: 7px; font-weight: 600; }
/* Абонемента нет — карта тихая: обещать «латунь» тому, у кого подписки нет,
   нельзя. */
.statuscard.is-off { background: hsl(var(--secondary)); color: hsl(var(--muted-foreground));
  box-shadow: none; border: 1px dashed hsl(var(--border)); }
.statuscard.is-off b { color: hsl(var(--foreground)); }
/* Главная абонемента: в карте ещё и сроки подписки — строкой под полосой,
   тем же рисунком, что «Доступ до…» в шапке программы. */
.statuscard.has-terms { width: 290px; }
.statuscard .acc { display: grid; grid-template-columns: 26px minmax(0, 1fr); gap: 10px; align-items: center;
  margin-top: 12px; padding-top: 11px; border-top: 1px solid rgba(34,27,18,.2); }
.statuscard.has-terms > .acc:first-child { margin-top: 0; padding-top: 0; border-top: 0; }
.statuscard .acc > i { width: 26px; height: 26px; border-radius: 7px; background: rgba(34,27,18,.12);
  display: grid; place-items: center; font-style: normal; }
.statuscard .acc b { font-size: 12.5px; font-weight: 700; margin: 0; }
.statuscard .acc span { display: block; font-size: 11.5px; opacity: .8; }
@media (max-width: 560px) { .statuscard, .statuscard.has-terms { width: 100%; } }

/* ── Новые уроки по абонементу (18.09) ────────────────────────────────────── */
/* Строка урока, открывшегося подпиской. Это НЕ карточка модуля: здесь важен не
   прогресс, а факт «вышло и уже ваше», поэтому строка плоская и с датой. */
.nu { display: flex; gap: 12px; align-items: center; padding: 12px 16px;
  border-bottom: 1px solid hsl(var(--border)); color: inherit; }
.nu:last-child { border-bottom: 0; }
.nu:hover { background: hsl(var(--secondary) / .5); }
.nu .ic { width: 32px; height: 32px; border-radius: 9px; flex-shrink: 0; display: grid;
  place-items: center; background: hsl(var(--kg-ui-mint) / .15); color: hsl(var(--kg-ui-mint)); }
.nu .t { min-width: 0; flex: 1; }
.nu .t b { display: block; font-size: 13px; font-weight: 700; line-height: 1.3; }
.nu .t span { font-family: var(--font-mono); font-size: 11px;
  color: hsl(var(--muted-foreground)); }
.nu .r { flex-shrink: 0; display: flex; gap: 8px; align-items: center; }
/* Пояснение к правилу накопления. Стоит ВНУТРИ карточки, а не подписью в
   шапке: правило длиннее строки, и в шапке оно ломало бы её на две. */
.nunote { display: flex; gap: 9px; padding: 11px 16px; font-size: 12px; line-height: 1.5;
  background: hsl(var(--kg-ui-mint) / .10); border-bottom: 1px solid hsl(var(--border)); }
.nunote svg { flex-shrink: 0; margin-top: 2px; color: hsl(var(--kg-ui-mint)); }

/* ── Лестница статусов ────────────────────────────────────────────────────── */
/* Металлы уровней. Недостижимые приглушены, СЛЕДУЮЩИЙ — в полную силу: он и
   есть то, ради чего человек остаётся ещё на три месяца. */
.kbr .ic.lv-brass    { background: linear-gradient(140deg, #D9C39A, #B99C63); color: #241D14; }
.kbr .ic.lv-bronze   { background: linear-gradient(140deg, #D8A87A, #A9713C); color: #241D14; }
.kbr .ic.lv-silver   { background: linear-gradient(140deg, #D7DCE2, #9AA6B4); color: #1B2026; }
.kbr .ic.lv-gold     { background: linear-gradient(140deg, #F0D48A, #C39A2E); color: #241D14; }
.kbr .ic.lv-platinum { background: linear-gradient(140deg, #E8ECEF, #A9B4BD 55%, #7E8891); color: #1B2026; }
.kbr .ic[class*="lv-"] { opacity: .5; font-weight: 700; }
.kbr .ic[class*="lv-"].is-now, .kbr.is-next .ic[class*="lv-"] { opacity: 1; }
.kbr.is-next { background: hsl(var(--secondary) / .55); }
.kbr .lock { margin-left: auto; color: hsl(var(--muted-foreground)); flex-shrink: 0; }

/* Оплата от компании: плашка «пока нельзя» внизу карточки подписки. */
.invoice { display: flex; gap: 10px; align-items: flex-start; padding: 12px 16px;
  background: hsl(var(--kg-ui-yellow) / .12); border-top: 1px solid hsl(var(--border));
  border-radius: 0 0 13px 13px; }
.invoice svg { flex-shrink: 0; margin-top: 2px; color: hsl(var(--kg-ui-yellow)); }
.invoice b { display: block; font-size: 12.5px; }
.invoice > span > span { font-size: 11.5px; color: hsl(var(--muted-foreground)); }

/* ── Модули карточками ────────────────────────────────────────────────────── */
.modgrid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; padding: 14px 16px; }
@media (max-width: 720px) { .modgrid { grid-template-columns: 1fr; } }
.modc { display: flex; flex-direction: column; gap: 8px; padding: 14px; border-radius: 12px;
  border: 1px solid hsl(var(--border)); color: inherit; text-decoration: none;
  background: linear-gradient(160deg, hsl(var(--c, var(--kg-ui-steel)) / .09) 0%, hsl(var(--card)) 62%);
  transition: border-color .15s, transform .12s; }
.modc:hover { border-color: hsl(var(--c, var(--primary)) / .55); }
.modc-top { display: flex; align-items: center; gap: 8px; }
.modc-no { font-family: var(--font-mono); font-size: 10px; font-weight: 700; letter-spacing: .05em;
  padding: 3px 7px; border-radius: 6px;
  background: hsl(var(--c, var(--kg-ui-steel)) / .16); color: hsl(var(--c, var(--kg-ui-steel))); }
.modc > b { font-size: 14px; font-weight: 700; line-height: 1.3; }
.modc-meta { font-family: var(--font-mono); font-size: 11px; color: hsl(var(--muted-foreground)); }
.modc-foot { display: flex; align-items: center; gap: 10px; margin-top: auto; }
.modc-foot .progress { flex: 1; }
.modc-pct { font-family: var(--font-mono); font-size: 11px; color: hsl(var(--muted-foreground)); }
.modc.is-done { border-color: hsl(var(--kg-ui-mint) / .4); }
.modc.is-locked { opacity: .72; }
/* Без правой колонки (главная «Аналитика PRO», `.hm.is-lean`) модули идут во
   всю ширину тремя столбцами. */
.hm.is-lean { grid-template-columns: minmax(0, 1fr); }
.modgrid.is-3col { grid-template-columns: repeat(3, minmax(0, 1fr)); }
@media (max-width: 1100px) { .modgrid.is-3col { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 720px)  { .modgrid.is-3col { grid-template-columns: 1fr; } }
/* Мега-урок: одна тема, много видео — плотнее цвет программы и список видео
   внутри карточки (оттого и выше соседних). */
.modc.is-mega { border-color: hsl(var(--c, var(--kg-ui-steel)) / .45);
  background: linear-gradient(160deg, hsl(var(--c, var(--kg-ui-steel)) / .2) 0%, hsl(var(--card)) 78%); }
.modc.is-mega.is-done { border-color: hsl(var(--kg-ui-mint) / .5); }
.modc.is-mega > b { font-size: 16px; }
.modc-mega { font-family: var(--font-mono); font-size: 10px; font-weight: 700; letter-spacing: .05em;
  text-transform: uppercase; padding: 3px 7px; border-radius: 6px;
  background: hsl(var(--c, var(--kg-ui-steel))); color: #fff; }
.modc-parts { display: flex; flex-direction: column; gap: 5px; padding: 8px 0 6px;
  border-top: 1px dashed hsl(var(--c, var(--kg-ui-steel)) / .3); }
.modc-parts > span { display: flex; align-items: center; gap: 7px; min-width: 0;
  font-size: 12px; color: hsl(var(--foreground) / .82); }
.modc-parts > span > svg { flex-shrink: 0; color: hsl(var(--c, var(--kg-ui-steel))); }
.modc-parts > span.is-done > svg { color: hsl(var(--kg-ui-mint)); }
.modc-parts i { font-style: normal; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.blockline { display: flex; align-items: center; gap: 10px; padding: 12px 16px 0; }
.blockline b { font-size: 12.5px; font-weight: 700; }
.blockline span { font-family: var(--font-mono); font-size: 11px; color: hsl(var(--muted-foreground)); }
.blockline::after { content: ''; flex: 1; height: 1px; background: hsl(var(--border)); }
.card-h .more { margin-left: auto; font-size: 12px; color: hsl(var(--primary)); font-weight: 600; }

/* ── Инструменты и сервисы ────────────────────────────────────────────────── */
/* ⚠️ Сетка по умолчанию — ПОДГОНКА ПО ШИРИНЕ, и менять её на жёсткие колонки
   нельзя: те же плитки стоят в УЗКОЙ правой колонке абонемента («Сервисы»,
   «Мои приложения», «База знаний»). Там три колонки дают по 145 px на плитку,
   значок с текстом рядом уже не помещаются, и карточки наезжают друг на друга
   (поймано владельцем 18.09 — в коде этого не видно, видно только на экране).
   Жёсткая сетка живёт модификатором `is-3col` и включается там, где колонка
   широкая, — на главной программы. */
.toolgrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: 12px; padding: 14px 16px; }
/* ⚠️ ТРИ КОЛОНКИ ЖЁСТКО, а не `auto-fit` (правка владельца 18.09: «сделай все
   карточки покрупнее и единым размером — получится сетка 3×2»). При подгонке
   ширину карточки задавало то, сколько их поместилось: на широком экране
   вставало четыре, пятая уезжала в свой ряд одна и выглядела вдвое шире
   соседок. Шесть плиток на три колонки дают ровно два ряда.
   ⚠️ `grid-auto-rows: 1fr` равняет и РЯДЫ. Без него подпись в две строки
   («ваши приложения и публикация») делала свой ряд выше соседнего, и «единым
   размером» не выходило: карточки равны только внутри ряда. */
.toolgrid.is-3col { grid-template-columns: repeat(3, minmax(0, 1fr));
  grid-auto-rows: 1fr; }
.toolc { display: flex; gap: 13px; align-items: flex-start; padding: 16px 17px; border-radius: 13px;
  border: 1px solid hsl(var(--border)); color: inherit; text-decoration: none;
  background: linear-gradient(160deg, hsl(var(--c, var(--primary)) / .08) 0%, hsl(var(--card)) 60%); }
.toolc:hover { border-color: hsl(var(--c, var(--primary)) / .55); }
.toolc .ic { width: 40px; height: 40px; border-radius: 11px; flex-shrink: 0; display: grid;
  place-items: center; background: hsl(var(--c, var(--primary)) / .15); color: hsl(var(--c, var(--primary))); }
.toolc b { display: block; font-size: 14px; font-weight: 700; margin-bottom: 3px; }
.toolc i { font-style: normal; font-size: 12px; color: hsl(var(--muted-foreground)); line-height: 1.45; }
.toolc.is-live .ic { background: hsl(var(--kg-ui-mint) / .15); color: hsl(var(--kg-ui-mint)); }
/* Колонка страницы узкая (у программы слева ещё меню, справа прогресс), и три
   плитки в ней перестают читаться раньше, чем кончается экран: подпись
   начинает рассыпаться по одному слову. Считаем по ОКНУ — своей контейнерной
   меры у этой колонки нет, а ширина у неё от окна и зависит. */
@media (max-width: 1180px) { .toolgrid.is-3col { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 720px)  { .toolgrid.is-3col { grid-template-columns: 1fr; grid-auto-rows: auto; } }

/* Строка списка в правой колонке (материалы, статусы, доступ). */
.kbr { display: flex; gap: 10px; padding: 11px 16px; align-items: center; color: inherit;
  text-decoration: none; border-bottom: 1px solid hsl(var(--border)); }
.kbr:last-child { border-bottom: 0; }
.kbr:hover b { color: hsl(var(--primary)); }
.kbr.is-plain:hover b { color: inherit; }
.kbr .ic { width: 30px; height: 30px; border-radius: 9px; display: grid; place-items: center;
  flex-shrink: 0; background: hsl(var(--kg-ui-steel) / .14); color: hsl(var(--kg-ui-steel));
  font-family: var(--font-mono); font-size: 12px; font-weight: 700; }
.kbr .ic.is-ok { background: hsl(var(--kg-ui-mint) / .15); color: hsl(var(--kg-ui-mint)); }
.kbr .ic.is-mut { background: hsl(var(--muted-foreground) / .14); color: hsl(var(--muted-foreground)); }
.kbr b { display: block; font-size: 13px; font-weight: 650; }
.kbr i { font-style: normal; font-size: 11.5px; color: hsl(var(--muted-foreground)); }

/* Строка встречи в правой колонке. */
/* Строка встречи — сетка в два ряда (правка владельца 28.09): название во всю
   ширину В ОДНУ СТРОКУ (длинное обрезается многоточием), под ним время и на
   его уровне кнопка «Подключиться». Ссылка `.meet-main` и `.t` — `contents`:
   их дети встают прямо в сетку, а кликабельность ссылки остаётся. */
.meet-row { display: grid; grid-template-columns: 40px minmax(0, 1fr) auto; column-gap: 10px; row-gap: 4px;
  align-items: center; padding: 11px 16px; color: inherit; text-decoration: none;
  border-bottom: 1px solid hsl(var(--border)); }
.meet-row .meet-main, .meet-row .t { display: contents; color: inherit; text-decoration: none; }
.meet-row:last-child { border-bottom: 0; }
.meet-row .meet-main:hover .t b { color: hsl(var(--primary)); }
.meet-row .d { grid-row: 1 / span 2; text-align: center; font-family: var(--font-mono); }
.meet-row .d b { display: block; font-size: 15px; font-weight: 700; line-height: 1.1; }
.meet-row .d i { font-style: normal; font-size: 10px; color: hsl(var(--muted-foreground)); text-transform: uppercase; }
.meet-row .t b { grid-column: 2 / 4; grid-row: 1; min-width: 0; font-size: 13px; font-weight: 650;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.meet-row .t i { grid-column: 2; grid-row: 2; font-style: normal; font-size: 11.5px; color: hsl(var(--muted-foreground)); }
.meet-row .meet-go { grid-column: 3; grid-row: 2; }

/* ── Архив ────────────────────────────────────────────────────────────────── */
.arch-list { padding: 6px 0; }
.arch-row { display: flex; align-items: center; gap: 12px; padding: 11px 16px; color: inherit;
  text-decoration: none; border-bottom: 1px solid hsl(var(--border) / .6); }
.arch-row:last-child { border-bottom: 0; }
.arch-ic { width: 38px; height: 38px; border-radius: 11px; flex-shrink: 0; display: grid;
  place-items: center; color: #fff;
  background: linear-gradient(140deg, hsl(var(--muted-foreground)), hsl(var(--muted-foreground) / .6)); }
.arch-t { flex: 1; min-width: 0; }
.arch-t b { display: block; font-size: 13.5px; font-weight: 650; color: hsl(var(--muted-foreground)); }
.arch-row:hover .arch-t b { color: hsl(var(--foreground)); }
.arch-t i { font-style: normal; font-family: var(--font-mono); font-size: 11px;
  color: hsl(var(--muted-foreground)); }
.arch-pr { font-family: var(--font-mono); font-size: 11px; color: hsl(var(--muted-foreground)); }

/* ── Лента встреч абонемента ──────────────────────────────────────────────── */
/* ⚠️ Классы .feed и .seg в этом файле уже заняты — здесь свои имена. */
.mseg { display: inline-flex; gap: 4px; padding: 4px; border-radius: 10px; margin-left: auto;
  background: hsl(var(--background)); border: 1px solid hsl(var(--border)); }
.mseg button { font: inherit; font-size: 11.5px; font-weight: 600; color: hsl(var(--muted-foreground));
  background: none; border: 0; border-radius: 7px; padding: 4px 9px; cursor: pointer; }
.mseg button.on { background: hsl(var(--card)); color: hsl(var(--foreground)); }
.mfeed { padding: 4px 16px 10px; }
.fitem { display: grid; grid-template-columns: 54px minmax(0, 1fr); gap: 12px; padding: 12px 0;
  border-bottom: 1px solid hsl(var(--border)); }
.fitem:last-child { border-bottom: 0; }
.fd { text-align: center; font-family: var(--font-mono); padding-top: 2px; }
.fd b { display: block; font-size: 17px; font-weight: 700; line-height: 1.05; }
.fd span { font-size: 10px; color: hsl(var(--muted-foreground)); text-transform: uppercase; }
.ft > b { display: block; font-size: 14px; font-weight: 700; margin-bottom: 3px; }
.fmeta { display: block; font-size: 11.5px; color: hsl(var(--muted-foreground)); }
.facts { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin-top: 8px; }
.fitem.is-past .fd b { color: hsl(var(--muted-foreground)); }
/* Встреча, к которой доступа нет: строка остаётся на месте, но приглушена
   (правка владельца 18.09). Не `display:none` — расписание это витрина
   абонемента, и пустой раздел не продаёт ничего. Название читается, курсор
   не обещает перехода. */
.fitem.is-locked { opacity: .58; }
.fitem.is-locked:hover { opacity: .72; }

/* ── План обновлений (два продающих блока абонемента) ─────────────────────── */
.uplan { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (max-width: 720px) { .uplan { grid-template-columns: 1fr; } }
.uplan > div { padding: 15px 16px; }
.uplan > div + div { border-left: 1px solid hsl(var(--border)); }
@media (max-width: 720px) { .uplan > div + div { border-left: 0; border-top: 1px solid hsl(var(--border)); } }
.uplan h4 { margin: 0 0 10px; font-size: 12.5px; font-weight: 700; }
.upl { display: flex; gap: 9px; padding: 8px 0; border-bottom: 1px dashed hsl(var(--border)); font-size: 12.5px; }
.upl:last-child { border-bottom: 0; }
.upl .dot { width: 17px; height: 17px; border-radius: 50%; flex-shrink: 0; margin-top: 2px;
  display: grid; place-items: center; font-size: 10px;
  background: hsl(var(--kg-ui-mint) / .16); color: hsl(var(--kg-ui-mint)); }
.upl.is-soon .dot { background: hsl(var(--muted-foreground) / .15); color: hsl(var(--muted-foreground)); }
.upl b { font-weight: 650; }
.upl span { display: block; color: hsl(var(--muted-foreground)); font-size: 11.5px; }

/* ── Главная абонемента после 28.09 ───────────────────────────────────────── */
/* «Обновления уроков»: слева 2/3 — что вышло, справа 1/3 — «В плане». */
.upd2 { display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); }
.upd2 h4 { display: flex; align-items: baseline; gap: 8px; margin: 0; padding: 14px 16px 10px;
  font-size: 12.5px; font-weight: 700; }
.upd2 h4 span { font-family: var(--font-mono); font-size: 11px; font-weight: 400; color: hsl(var(--muted-foreground)); }
.upd2-l { min-width: 0; }
.upd2-l > .upl, .upd2-l > p { margin-left: 16px; margin-right: 16px; }
.upd2-l > .upl:last-child { margin-bottom: 8px; }
.upd2-l .nunote { border-top: 1px solid hsl(var(--border)); }
/* Вторая строка урока (программа · модуль · длина · дата) — в одну строку:
   в 2/3 ширины она переносилась и удваивала высоту строки. */
.upd2-l .nu { padding-top: 9px; padding-bottom: 9px; }
.upd2-l .nu .t span { display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.upd2-r { border-left: 1px solid hsl(var(--border)); padding-bottom: 12px;
  background: hsl(var(--secondary) / .35); }
.upd-plan-i { position: relative; margin: 0 16px; padding: 8px 0 8px 16px; border-bottom: 1px dashed hsl(var(--border)); }
.upd-plan-i:last-child { border-bottom: 0; }
.upd-plan-i::before { content: ''; position: absolute; left: 0; top: 14px; width: 7px; height: 7px; border-radius: 50%;
  border: 2px solid hsl(var(--primary)); }
.upd-plan-i b { display: block; font-size: 13px; font-weight: 650; line-height: 1.35; }
.upd-plan-i span { display: block; margin-top: 1px; font-family: var(--font-mono); font-size: 11px; color: hsl(var(--muted-foreground)); }
@media (max-width: 720px) {
  .upd2 { grid-template-columns: 1fr; }
  .upd2-l .nu .badge { display: none; }
  .upd2-r { border-left: 0; border-top: 1px solid hsl(var(--border)); }
}
/* «Встречи» компактно: строка ~46 px — дата, название с «время · ведущий»,
   кнопка. Вся строка — ссылка, кнопка внутри неё — span. */
.mfeed { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }
.mfeed > div { min-width: 0; padding-bottom: 6px; }
.mfeed > div + div { border-left: 1px solid hsl(var(--border)); }
.mfeed h4 { margin: 0; padding: 12px 16px 6px; font-size: 12.5px; font-weight: 700; }
.mfeed > div > p { margin-left: 16px; margin-right: 16px; }
.mline { display: grid; grid-template-columns: 34px minmax(0, 1fr) auto; gap: 10px; align-items: center;
  padding: 6px 16px; color: inherit; text-decoration: none; }
.mline + .mline { border-top: 1px dashed hsl(var(--border)); }
a.mline:hover { background: hsl(var(--secondary) / .5); }
a.mline:hover .ml-t b { color: hsl(var(--primary)); }
.ml-d { display: flex; flex-direction: column; align-items: center; line-height: 1.1;
  font-family: var(--font-mono); font-size: 9.5px; text-transform: uppercase; color: hsl(var(--muted-foreground)); }
.ml-d b { font-size: 15px; font-weight: 700; color: hsl(var(--foreground)); }
.mline.is-past .ml-d b { color: hsl(var(--muted-foreground)); }
.ml-t { min-width: 0; }
.ml-t b, .ml-t i { display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ml-t b { font-size: 13px; font-weight: 650; }
.ml-t i { font-style: normal; font-size: 11px; color: hsl(var(--muted-foreground)); }
.mline.is-locked { opacity: .58; }
@media (max-width: 720px) {
  .mfeed { grid-template-columns: 1fr; }
  .mfeed > div + div { border-left: 0; border-top: 1px solid hsl(var(--border)); }
}

/* ⚠️ На телефоне кнопка «играть» стоит ВЫШЕ подписи, а не по центру кадра:
   по центру она попадала ровно на заголовок ролика, и название читалось
   сквозь белый круг (поймано на снимке 390 px). */
@media (max-width: 640px) {
  .hello-pic { aspect-ratio: 16/10; }
  .hello-play { width: 54px; height: 54px; margin-bottom: 76px; }
  .hello-play svg { width: 20px; height: 20px; }
  .hello-cap i { -webkit-line-clamp: 2; }
}

/* Планшет: правая колонка уходит вниз, карточки программ — в одну колонку
   только на телефоне (на планшете две помещаются). */
@media (max-width: 900px) {
  .phero { padding: 18px 18px; }
  .subhero { padding: 18px; }
  .hello-cap { left: 14px; right: 14px; bottom: 12px; }
}

/* ═══ Витрина: программа, которую человек не купил (17.09.2026) ═════════════
   Правка владельца: «пусть всё открывается, но бледнее и не читается — видно,
   что есть, но не прочитать». Читаемость закрывает СЕРВЕР: в этом режиме он
   не отдаёт ни текста конспекта, ни адреса записи, ни ссылок. Здесь только
   вид: приглушённый состав и честные заглушки на месте содержимого.
   ⚠️ Размывать настоящий текст фильтром НЕЛЬЗЯ — он остался бы в разметке
   целиком и читался бы через панель разработчика. Поэтому полосы. */
.hm.is-preview .modc,
.hm.is-preview .card.is-preview .kbr,
.hm.is-preview .card.is-preview .toolc.is-off { opacity: .74; }
.hm.is-preview .modc:hover { opacity: .9; }
.modgrid.is-preview .modc { border-style: dashed; }
.modc-lock { margin-left: auto; color: hsl(var(--muted-foreground)); display: inline-flex; }
.kbr.is-off { cursor: default; }
.kbr.is-off .ic { color: hsl(var(--muted-foreground)); }
/* Закрытая плитка инструментов: тот же размер и та же подпись, что у живой, —
   меняется только то, что она не ведёт никуда. Пунктир и приглушённый значок
   берём у закрытого модуля: два разных языка «это закрыто» на одном экране
   читались бы как два разных состояния. ⚠️ Рамку на наведении гасим явно —
   без этого `.toolc:hover` подсвечивает её цветом программы и плитка
   обещает клик, которого не будет. */
.toolc.is-off { cursor: default; border-style: dashed; }
.toolc.is-off:hover { border-color: hsl(var(--border)); }
.toolc.is-off .ic { background: hsl(var(--muted-foreground) / .12);
  color: hsl(var(--muted-foreground)); }

/* Карточка покупки на месте прогресса и полоса над списком модулей. */
.buycard .buy-price { font-family: var(--font-mono); font-size: 22px; font-weight: 700; }
.buyline { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-bottom: 14px;
  padding: 12px 14px; border-radius: 12px; font-size: 12.5px;
  border: 1px solid hsl(var(--primary) / .35); background: hsl(var(--primary) / .07); }
.buyline span { flex: 1; min-width: 200px; color: hsl(var(--muted-foreground)); }
.buyline.is-wide { margin: 18px 0; }

/* Место записи: тот же прямоугольник 16:9, что у плеера, но с замком. */
.lockvideo { display: grid; place-items: center; align-content: center; gap: 3px;
  background: radial-gradient(120% 120% at 30% 20%, #32363F 0%, #14161B 74%); color: #fff; }
.lockvideo .lv-ic { opacity: .7; }
.lockvideo b { font-size: 14px; font-weight: 700; }
.lockvideo i { font-style: normal; font-size: 12px; color: rgba(255,255,255,.6); }

.lockhead { display: flex; align-items: baseline; gap: 10px; margin: 20px 0 10px; }
.lockhead b { font-size: 15px; font-weight: 700; }
.lockhead span { font-family: var(--font-mono); font-size: 11px; color: hsl(var(--muted-foreground)); }
/* Скелет конспекта: столько полос, сколько знаков в тексте, — короткая
   шпаргалка и разбор на десять страниц должны выглядеть по-разному. */
.skel { display: flex; flex-direction: column; gap: 9px; padding: 2px 0 4px; }
.skel span { display: block; height: 10px; border-radius: 999px;
  background: linear-gradient(90deg, hsl(var(--muted-foreground) / .19), hsl(var(--muted-foreground) / .09)); }
.skel span:nth-child(4n) { margin-bottom: 8px; }

.locklist { display: flex; flex-direction: column; gap: 8px; margin-top: 16px; }
.lockrow { display: flex; align-items: center; gap: 11px; padding: 11px 13px; border-radius: 11px;
  border: 1px dashed hsl(var(--border)); background: hsl(var(--card)); }
.lockrow .ic { width: 30px; height: 30px; border-radius: 9px; display: grid; place-items: center;
  flex-shrink: 0; background: hsl(var(--secondary)); color: hsl(var(--muted-foreground)); }
.lockrow .t { flex: 1; min-width: 0; }
.lockrow .t b { display: block; font-size: 13px; font-weight: 650; }
.lockrow .t i { font-style: normal; font-size: 11.5px; color: hsl(var(--muted-foreground)); }
/* КОНЕЦ: главная программы, «Мои программы» и абонемент */

/* =============================================================================
 * НАЧАЛО: футер кабинета, кнопка «Чаты» и панель чатов (16.09.2026)
 * ============================================================================= */

/* Футер прижат к низу области контента: на коротком экране он не подтягивается
   к последней карточке. ⚠️ Он НЕ на всю ширину окна: слева фиксированное меню
   высотой в экран, и полоса под ним перекрыла бы имя и кнопку выхода. */
.dashboard { display: flex; flex-direction: column; }
.dashboard > .page { flex: 1 0 auto; }
.app-foot { flex-shrink: 0; margin-top: 18px; padding: 12px 26px;
  border-top: 1px solid hsl(var(--border)); background: hsl(var(--card));
  display: flex; gap: 16px; flex-wrap: wrap; align-items: center;
  font-size: 11.5px; color: hsl(var(--muted-foreground)); }
.af-req { flex: 1; min-width: 260px; }
.af-req b { color: hsl(var(--foreground)); font-weight: 650; }
.af-sep { opacity: .45; margin: 0 6px; }
.af-req .mono { font-family: var(--font-mono); font-size: 11px; }
.af-docs { display: flex; gap: 14px; flex-wrap: wrap; }
.af-docs a:hover, .af-req a:hover { color: hsl(var(--primary)); }
@media (max-width: 640px) { .app-foot { padding: 12px 16px; } }

/* ── Панель чатов ────────────────────────────────────────────────────────── */
.chat-pane-right { background: hsl(var(--card)); display: flex; flex-direction: column; }
/* ⚠️ `display: flex` ПЕРЕБИВАЕТ атрибут `hidden` — та самая ловушка, из-за
   которой закрытая панель чатов накрывала собой весь экран на телефоне
   (найдено снимком 390 px, 17.09). Правило про узкий экран стоит ВЫШЕ в файле
   и при равной силе проигрывает этой строке, поэтому чиним у источника: у
   скрытой панели display снимается явно, на любой ширине. */
.chat-pane-right[hidden], .chat-pane-left[hidden] { display: none !important; }
.cm-head { display: flex; align-items: center; gap: 8px; padding: 11px 12px;
  border-bottom: 1px solid hsl(var(--border)); flex-shrink: 0; }
.cm-head b { font-size: 13.5px; font-weight: 700; flex: 1; }
.btn-icon-sm { width: 28px; height: 28px; }
.cm-tabs { display: flex; gap: 5px; padding: 9px 12px; overflow-x: auto; flex-shrink: 0;
  border-bottom: 1px solid hsl(var(--border)); scrollbar-width: none; }
.cm-tabs::-webkit-scrollbar { display: none; }
/* ⚠️ Радиус — 9px, ТОТ ЖЕ, что у `.btn` и у вкладок программ (`.progtab`), а
   не 999px (правка владельца 21.09: «сделай как все кнопки — квадратные с
   закруглением, сейчас овалы»). Три вкладки — «Абонемент», «Поддержка»,
   «Куратор» — были единственным местом кабинета, где кнопка была полной
   таблеткой; остальные элементы такой формы (счётчики, полоски прогресса)
   ею и остаются намеренно — это индикаторы, а не кнопки. */
.cm-tab { font: inherit; font-size: 11.5px; font-weight: 600; cursor: pointer; white-space: nowrap;
  color: hsl(var(--muted-foreground)); background: hsl(var(--background));
  border: 1px solid hsl(var(--border)); border-radius: 9px; padding: 5px 10px;
  display: inline-flex; align-items: center; gap: 6px; }
.cm-tab:hover { color: hsl(var(--foreground)); }
.cm-tab.on { background: hsl(var(--foreground)); color: hsl(var(--card));
  border-color: hsl(var(--foreground)); }
.cm-tab .n { font-family: var(--font-mono); font-size: 9.5px; font-weight: 700;
  background: hsl(var(--primary)); color: #fff; padding: 0 5px; border-radius: 20px; }
.cm-about { padding: 8px 12px; font-size: 11.5px; line-height: 1.5;
  color: hsl(var(--muted-foreground));
  background: hsl(var(--background)); border-bottom: 1px solid hsl(var(--border)); flex-shrink: 0; }
/* Где ещё живёт эта переписка. У «Подключите Телеграм» / «Открыть бота» —
   по-прежнему ОДНОЙ строкой со ссылкой в конце фразы: короткий текст, и
   ссылка там не рвётся. */
.cm-note { display: block; margin-top: 4px; }
.cm-note a { color: hsl(var(--primary)); font-weight: 600; }
/* ⚠️ А вот «Открыть в Телеграме» (уже привязанный Телеграм) — ЭТО ОТМЕНА
   решения чуть выше по истории правок этого же дня. Тогда кнопку убрали
   ровно отсюда — «Открыть в Телеграме» как ссылка-хвост фразы переносилась
   по словам («Открыть в» / «Телеграме» разными строками) и владелец назвал
   это криво. `.cm-tgbtn` — текст отдельной строкой, кнопка во всю ширину
   колонки, текст в ней центрирован — `justify-content` в `.btn` это уже
   умеет, здесь только сама раскладка блока. */
.cm-tgbtn { display: block; margin-top: 8px; }
.cm-tgbtn span { display: block; margin-bottom: 6px; }
.cm-tgbtn .btn { width: 100%; justify-content: center; }

/* ── Вкладка группового чата: не переписка, а вход в Телеграм ───────────────
   🔴 Ленты и поля ввода здесь НЕТ: сам чат живёт в Телеграме, и предлагать
   писать в портал значило бы предлагать писать в пустоту. */
.cm-gate { flex: 1; overflow-y: auto; display: flex; align-items: center;
  justify-content: center; padding: 20px 16px; min-height: 0; }
.chat-pane-right.is-gate .cm-msgs,
.chat-pane-right.is-gate .cm-foot { display: none; }
.cm-hero { text-align: center; max-width: 280px; }
.cm-hero-i { width: 46px; height: 46px; margin: 0 auto 12px; border-radius: 14px;
  display: grid; place-items: center; color: hsl(var(--primary));
  background: hsl(var(--primary) / .1); }
.cm-hero-t { display: block; font-size: 14px; font-weight: 700; line-height: 1.35;
  margin-bottom: 7px; word-break: break-word; }
.cm-hero-p { margin: 0 0 14px; font-size: 12.5px; line-height: 1.55;
  color: hsl(var(--muted-foreground)); }
/* Кнопка СВОЕЙ строкой и во всю колонку: в узкой панели кнопка, втиснутая в
   поток текста, читается как опечатка вёрстки. */
.cm-hero-b { display: flex; width: 100%; justify-content: center; }
.cm-hero-n a { color: hsl(var(--primary)); font-weight: 600; }
.cm-hero-n { margin: 12px 0 0; font-size: 11px; line-height: 1.5;
  color: hsl(var(--muted-foreground)); opacity: .85; }

/* Пустая переписка: не «здесь тихо», а подсказка, о чём писать. */
.cm-empty { margin: auto 0; text-align: center; padding: 18px 6px; }
.cm-empty b { display: block; font-size: 13px; margin-bottom: 5px; }
.cm-empty span { font-size: 12px; line-height: 1.5; color: hsl(var(--muted-foreground)); }
.cm-msgs { flex: 1; overflow-y: auto; padding: 14px 12px; display: flex;
  flex-direction: column; gap: 10px; min-height: 0; }
.cm { max-width: 88%; align-self: flex-start; padding: 8px 11px; border-radius: 12px;
  border-bottom-left-radius: 4px; background: hsl(var(--background));
  border: 1px solid hsl(var(--border)); font-size: 12.5px; line-height: 1.45; }
.cm.is-mine { align-self: flex-end; background: hsl(var(--primary)); color: #fff;
  border-color: transparent; border-bottom-left-radius: 12px; border-bottom-right-radius: 4px; }
.cm-who { display: block; font-size: 10.5px; font-weight: 700; margin-bottom: 3px;
  color: hsl(var(--muted-foreground)); }
.cm.is-staff .cm-who { color: hsl(var(--primary)); }
.cm-body { white-space: pre-wrap; word-break: break-word; }
.cm-time { display: block; font-family: var(--font-mono); font-size: 9.5px;
  font-style: normal; opacity: .6; margin-top: 4px; }
.cm-day { align-self: center; font-family: var(--font-mono); font-size: 10px;
  color: hsl(var(--muted-foreground)); background: hsl(var(--background));
  border: 1px solid hsl(var(--border)); padding: 2px 9px; border-radius: 20px; }
/* ── Поле ввода ─────────────────────────────────────────────────────────────
   Правка владельца 21.09: «кнопку отправить сделаем одним размером с окном».
   Поле занимает всю ширину СВОЕЙ строкой, кнопка — под ним: в колонке 320 px
   кнопка сбоку отжимала поле до половины, и длинное сообщение приходилось
   писать в щель. Скрепка слева от кнопки, шириной под себя. */
.cm-foot { border-top: 1px solid hsl(var(--border)); padding: 10px 12px; flex-shrink: 0;
  display: flex; flex-direction: column; gap: 8px; }
.cm-foot textarea { width: 100%; resize: none; min-height: 40px; max-height: 140px;
  padding: 10px 11px; border-radius: 10px; border: 1px solid hsl(var(--input));
  background: hsl(var(--background)); color: inherit; font: inherit; font-size: 12.5px; }
.cm-foot textarea:focus { outline: none; border-color: hsl(var(--primary) / .6); }
.cm-send-row { display: flex; gap: 8px; }
.cm-clip { flex: 0 0 auto; width: 40px; padding: 0; display: grid; place-items: center;
  border: 1px solid hsl(var(--input)); color: hsl(var(--muted-foreground)); }
.cm-clip:hover { color: hsl(var(--primary)); border-color: hsl(var(--primary) / .5); }
.cm-send { flex: 1; justify-content: center; }

/* Выбранные, но ещё не отправленные файлы. */
.cm-pend { display: flex; flex-wrap: wrap; gap: 6px; }
.cm-pend-i { display: inline-flex; align-items: center; gap: 5px; max-width: 100%;
  padding: 3px 4px 3px 5px; border-radius: 8px; font-size: 11px;
  background: hsl(var(--background)); border: 1px solid hsl(var(--border)); }
.cm-pend-i img { width: 22px; height: 22px; object-fit: cover; border-radius: 4px; }
.cm-pend-i i { font-style: normal; color: hsl(var(--muted-foreground)); }
.cm-pend-i button { border: 0; background: none; cursor: pointer; padding: 2px;
  color: hsl(var(--muted-foreground)); display: grid; place-items: center; }
.cm-pend-i button:hover { color: hsl(var(--destructive)); }
/* Перетаскивание файла на панель. */
.chat-pane-right.is-drop { outline: 2px dashed hsl(var(--primary) / .6); outline-offset: -6px; }

/* Вложения внутри сообщения: картинка видна сразу, файл — строкой. */
.cm-att { display: flex; flex-direction: column; gap: 6px; margin-top: 6px; }
.cm-img { display: block; border-radius: 8px; overflow: hidden; line-height: 0; }
.cm-img img { width: 100%; max-height: 220px; object-fit: cover; display: block; }
.cm-file { display: flex; align-items: center; gap: 7px; padding: 6px 8px; border-radius: 8px;
  background: hsl(var(--card) / .5); border: 1px solid hsl(var(--border)); font-size: 11.5px; }
.cm.is-mine .cm-file { background: rgba(255, 255, 255, .16); border-color: transparent; color: #fff; }
.cm-file span { flex: 1; word-break: break-all; }
.cm-file i { font-style: normal; font-size: 10px; opacity: .7; white-space: nowrap; }
/* На телефоне панель занимает экран целиком: 340 px рядом с контентом там
   не помещаются вовсе. */
@media (max-width: 720px) {
  body.has-right-chat .main-layout { grid-template-columns: 0 0 100%; }
}
/* КОНЕЦ: футер кабинета и чаты */

/* ─── НАЧАЛО: «Приглашай и зарабатывай» (18.09.2026) ──────────────────────────
   Экран /app/referral и раздел выплат /app/payouts. Классы свои, с приставкой
   `ref-`: в кабинете уже есть `.card`, `.badge` и `.tbl` со своими правилами,
   и общие имена вроде `.two` однажды столкнулись бы с ними. */

/* ⚠️ ТРИ ПЛИТКИ ОДНИМ РЯДОМ, а не карточки одна под другой (правка владельца
   24.09). «5% деньгами» и «10% бонусами» — это одна сумма на выбор, а не два
   начисления; друг под другом они читаются как «мне положено и то, и другое».
   Ссылка стоит с ними в ряду по той же причине, по которой она вообще на
   экране: за ней сюда и заходят.
   ⚠️ Высоту ряду задаёт самая длинная плитка, а кнопка у каждой прижата к
   низу (`margin-top: auto` у `.btn`) — без этого кнопки встают на трёх разных
   уровнях и ряд рассыпается на три случайные карточки. */
.ref-top { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 14px; }
.ref-tile { display: flex; flex-direction: column; }
.ref-tile .card-b { display: flex; flex-direction: column; flex: 1; padding: 15px 16px 16px; }
.ref-tile .form-input { font-size: 11.5px; }
.ref-tile p { margin: 9px 0 14px; font-size: 11.5px; line-height: 1.55;
              color: hsl(var(--muted-foreground)); }
.ref-tile p b { color: hsl(var(--foreground)); }
.ref-tile .btn { margin-top: auto; width: 100%; }
.ref-tile .ref-have { color: hsl(var(--kg-ui-yellow)); }
.ref-tile .ref-minus { color: hsl(var(--destructive)); }
.ref-sum { font-family: var(--font-mono); font-size: 30px; font-weight: 800;
           letter-spacing: -.02em; margin: 6px 0 2px; }
.ref-sum.is-brass { color: hsl(var(--kg-ui-yellow)); }
/* Плитки узкие сами по себе: ниже ~980 px три штуки становятся колонками по
   240 px, где заголовок «Или удвоить бонусами» уже переносится по слогам. */
@media (max-width: 980px) { .ref-top { grid-template-columns: 1fr; } }

/* ── Материалы для соцсетей ────────────────────────────────────────────────
   ⚠️ ПРЕВЬЮ ДЕРЖИТ ПРОПОРЦИЮ СВОЕЙ КАРТИНКИ (`aspect-ratio: var(--ar)` из
   разметки), поэтому карточки в ряду разной высоты — и выровнены по верху.
   Одинаковая рамка на все три формата прятала бы ровно то, ради чего человек
   сюда и смотрит: квадрат это, вертикаль или горизонталь. */
.promo-tabs { display: flex; gap: 8px; flex-wrap: wrap; }
.promo-head { display: flex; gap: 12px; align-items: center; flex-wrap: wrap;
              margin-bottom: 14px; }
.promo-head .promo-url { font-size: 11px; margin-top: 3px; word-break: break-all; }
.promo-head .btn { margin-left: auto; flex-shrink: 0; }
.promo-pics { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
              gap: 12px; align-items: start; }
.promo-pic { border: 1px solid hsl(var(--border)); border-radius: 12px; overflow: hidden;
             background: hsl(var(--card)); display: flex; flex-direction: column; }
.promo-pic .shot { display: block; width: 100%; height: auto; aspect-ratio: var(--ar, 1);
                   object-fit: cover; background: #0E1013;
                   border-bottom: 1px solid hsl(var(--border)); }
.promo-pic figcaption { padding: 10px 12px 12px; display: flex; flex-direction: column; gap: 2px; }
.promo-pic figcaption b { font-size: 12.5px; }
.promo-pic figcaption span { font-size: 10.5px; }
.promo-pic .row { display: flex; gap: 6px; margin-top: 9px; }
.promo-pic .row .btn { flex: 1; padding: 0 8px; }
/* Тексты — ТРЕМЯ СТОЛБИКАМИ (правка владельца 24.09), по столбику на вариант
   поста: строками они читались как один длинный список, из которого не видно,
   что это три РАЗНЫХ текста на выбор. Кнопка у каждого прижата к низу — ряд
   держится на одной линии, как и плитки наверху. */
.promo-texts { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
               gap: 10px; margin-top: 14px; }
.promo-text { display: flex; flex-direction: column; gap: 12px;
              border: 1px solid hsl(var(--border)); border-radius: 12px; padding: 13px 14px; }
.promo-text p { margin: 0; font-size: 12.5px; line-height: 1.55; }
.promo-text .btn { margin-top: auto; align-self: flex-start; flex-shrink: 0; }
.promo-note { margin: 14px 0 0; font-size: 11.5px; line-height: 1.55; }
@media (max-width: 1100px) { .promo-texts { grid-template-columns: 1fr; } }
@media (max-width: 860px) {
  .promo-pics { grid-template-columns: 1fr; }
  .promo-head .btn { margin-left: 0; }
  .promo-text .btn { align-self: stretch; }
}

/* Кнопка «удвоить» — латунью: тем же материалом, что уровни абонемента.
   Она делает другое, чем соседняя «вывести», и выглядеть обязана иначе,
   иначе две кнопки рядом читаются как одна операция с двумя суммами.
   ⚠️ `.btn-brass` — ОДИН класс (специфичность 0,1,0), а у `.btn:hover`
   специфичность выше (0,2,0: класс + псевдокласс) — без своего `:hover`
   при наведении побеждал background от `.btn:hover`, градиент пропадал и
   кнопка на секунду становилась плоским пятном не в тон рамке (владелец
   увидел это как «лишние линии, градиент криво встаёт»). Заодно `border:
   none` вместо `border-color: transparent` — прозрачная рамка поверх
   скруглённого градиента местами даёт шов на стыке скругления и заливки. */
.btn-brass { background: linear-gradient(135deg, #E8C88B, #B98F3E);
             border: none; color: #241D14; }
.btn-brass:hover { background: linear-gradient(135deg, #E8C88B, #B98F3E); filter: brightness(1.06); }
.btn-brass[disabled] { filter: grayscale(.7); }
/* КОНЕЦ: «Приглашай и зарабатывай» */

/* ═══ НАЧАЛО: экран «Модули» — дорожки модулей ══════════════════════════════
   Переделан 24.09 по правке владельца: прежняя лента из широких строк
   (900 px на строку, кнопка «Открыть» в 800 px от названия) повторяла тот же
   список уроков, что и так стоит слева в читалке, и растягивалась на 3153 px
   у «Вайбкодинга» и ~4500 px у «Аналитика». Теперь модуль — колонка со своим
   списком уроков, колонок две в ряд: 1001 px и 1606 px соответственно.

   Решения, которые легко случайно отменить:
   • Уроки видны СРАЗУ, без раскрытия. Свёрнутый список вернул бы ровно то
     дублирование с читалкой, ради которого экран и переделывали.
   • Порядок ЗМЕЙКОЙ (1-2 / 3-4), а не CSS-колонками (1-4 слева, 5-8 справа) —
     решение владельца: курс читают слева направо. Плата — модули разной длины
     оставляют пустоту в ряду; по его же решению карточки тянутся по высоте
     ряда (align-items: stretch), то есть пустота живёт ВНУТРИ карточки, а не
     дырой между ними.
   • Кнопки «Продолжить» в карточках нет: шесть одинаковых кнопок подряд не
     выделяют ничего. Следующий урок помечен в самом списке словом «дальше».  */
.progpage { --c: var(--kg-ui-steel); }
.mroad { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px;
  align-items: stretch; margin-bottom: 16px; }
.mcol { display: flex; flex-direction: column; background: hsl(var(--card));
  border: 1px solid hsl(var(--border)); border-radius: 12px; overflow: hidden; }
.mcol.is-soon { opacity: .62; }
.mcol-h { display: grid; grid-template-columns: 34px minmax(0, 1fr) auto; gap: 11px;
  align-items: start; padding: 13px 14px 11px; }
/* Плашки — своей колонкой: их бывает две сразу (состояние модуля и дата
   встречи), и по отдельности вторая уезжала в следующий ряд сетки. */
.mcol-tags { display: flex; flex-direction: column; align-items: flex-end; gap: 4px; }
.mcol-no { width: 34px; height: 34px; border-radius: 10px; display: grid; place-items: center;
  font-family: var(--font-mono); font-weight: 700; font-size: 14px;
  color: hsl(var(--c)); background: hsl(var(--c) / .13); }
.mcol.is-done .mcol-no { color: hsl(var(--kg-ui-mint)); background: hsl(var(--kg-ui-mint) / .13); }
.mcol-t { min-width: 0; }
.mcol-t b { display: block; font-size: 14px; font-weight: 600; line-height: 1.3; text-wrap: balance; }
.mcol-t b a { color: inherit; text-decoration: none; }
.mcol-t b a:hover { color: hsl(var(--c)); }
.mcol-t i { display: block; margin-top: 3px; font-style: normal; font-size: 11.5px;
  font-family: var(--font-mono); color: hsl(var(--muted-foreground)); }
.mcol-p { padding: 0 14px 10px; }
.mcol-p .progress { background: hsl(var(--muted)); }
.mcol-p .progress i { background: hsl(var(--c)); }
.mcol.is-done .mcol-p .progress i { background: hsl(var(--kg-ui-mint)); }

/* Список уроков. Вся строка — ссылка: кнопка «Открыть» у правого края в
   800 px от названия была главной претензией к прежнему экрану. */
.mls { padding: 4px; border-top: 1px solid hsl(var(--border)); flex: 1 1 auto; }
.mls-r { display: grid; grid-template-columns: 18px minmax(0, 1fr) auto; gap: 9px;
  align-items: center; padding: 6px 9px; border-radius: 8px; font-size: 13px;
  color: hsl(var(--foreground)); text-decoration: none; }
a.mls-r:hover { background: hsl(var(--muted)); }
.mls-r.is-off { opacity: .55; cursor: default; }
.mls-s { display: grid; place-items: center; color: hsl(var(--muted-foreground)); }
.mls-r.is-done .mls-s { color: hsl(var(--kg-ui-mint)); }
.mls-ok { font-size: 13px; font-weight: 700; font-style: normal; }
.mls-t { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mls-d, .mls-next { font-style: normal; font-family: var(--font-mono); font-size: 11px;
  font-variant-numeric: tabular-nums; color: hsl(var(--muted-foreground)); }
.mls-r.is-next { background: hsl(var(--c) / .10); font-weight: 600; }
.mls-r.is-next .mls-s, .mls-r.is-next .mls-next { color: hsl(var(--c)); }
.mcol-note { padding: 8px 14px 11px; border-top: 1px solid hsl(var(--border));
  font-size: 11.5px; color: hsl(var(--muted-foreground)); }

/* Полоса «Продолжить»: единственное главное действие экрана. */
.mresume { display: flex; align-items: center; gap: 14px; margin-bottom: 14px;
  padding: 13px 16px; border-radius: 14px; text-decoration: none; color: inherit;
  border: 1px solid hsl(var(--border));
  background: linear-gradient(135deg, hsl(var(--c) / .16), hsl(var(--card)) 62%); }
.mresume:hover { border-color: hsl(var(--c) / .5); }
.mresume-p { flex: 0 0 auto; width: 44px; height: 44px; border-radius: 12px;
  display: grid; place-items: center; background: hsl(var(--c)); color: #fff; }
.mresume-t { min-width: 0; flex: 1 1 auto; }
.mresume-t i { display: block; font-style: normal; font-size: 10.5px; font-weight: 700;
  letter-spacing: .07em; text-transform: uppercase; color: hsl(var(--muted-foreground)); }
.mresume-t b { display: block; font-size: 15px; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
.mresume-t .muted { font-size: 12px; }
.mresume-r { display: flex; align-items: center; gap: 12px; flex: 0 0 auto; }
.mresume-r .ring { width: 46px; height: 46px; }
.mresume-r .ring i { width: 36px; height: 36px; font-size: 11px; }

@media (max-width: 980px) {
  .mroad { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 560px) {
  .mresume { flex-wrap: wrap; }
  .mresume-r { width: 100%; justify-content: space-between; }
}
/* ═══ КОНЕЦ: экран «Модули» ════════════════════════════════════════════════ */

/* ═══ «Свой dev-портал»: проект, промпт, методичка (правка владельца 24.09) ═══
   Одна страница вместо двух («Свой dev-портал» + «DEV портал»): сверху проект
   svoy-dev с кнопками, под ним промпт целиком и три его части, ниже методичка
   в форме старой страницы «Свой Vibe» — нумерованные разделы, варианты
   «сами / через Claude» карточками рядом, код с кнопкой «Копировать».
   Прототип согласован владельцем (артефакт «Прототип «Свой dev-портал»»). */
.dp-proj { border: 1.5px solid hsl(var(--primary) / .45); border-radius: 16px; padding: 20px 22px;
  background: linear-gradient(180deg, hsl(var(--primary) / .08), transparent 70%), hsl(var(--card));
  display: grid; gap: 14px; margin: 18px 0 0; scroll-margin-top: 80px; }
.dp-proj-h { display: flex; gap: 14px; align-items: flex-start; }
.dp-ic { width: 44px; height: 44px; flex-shrink: 0; border-radius: 12px; display: grid; place-items: center;
  background: hsl(var(--primary)); color: hsl(var(--primary-foreground)); }
.dp-lbl { font-family: var(--font-mono); font-size: 10px; font-weight: 600; letter-spacing: .1em;
  text-transform: uppercase; color: hsl(var(--primary)); }
.dp-t { font-size: 18px; font-weight: 700; margin: 3px 0 5px; line-height: 1.25; }
.dp-d { margin: 0; font-size: 13px; line-height: 1.6; color: hsl(var(--muted-foreground)); }
.dp-steps { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; margin: 0; padding: 0; list-style: none; }
.dp-steps li { background: hsl(var(--card)); border: 1px solid hsl(var(--border)); border-radius: 10px;
  padding: 9px 11px; font-size: 12px; line-height: 1.45; }
.dp-steps b { display: block; font-family: var(--font-mono); font-size: 9.5px; letter-spacing: .1em;
  color: hsl(var(--primary)); margin-bottom: 2px; }
.dp-files { font-size: 12px; color: hsl(var(--muted-foreground)); line-height: 1.8; }
.dp-files code { font-family: var(--font-mono); font-size: 11px; color: hsl(var(--foreground));
  background: hsl(var(--secondary)); padding: 1px 6px; border-radius: 5px; }
.dp-files a { color: hsl(var(--primary)); }
.dp-foot { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 10px;
  border-top: 1px solid hsl(var(--border)); padding-top: 14px; }
.dp-acts { display: flex; flex-wrap: wrap; gap: 8px; }
.dp-acts .btn { display: inline-flex; align-items: center; gap: 7px; text-decoration: none; }
.dp-st { font-size: 11.5px; color: hsl(var(--muted-foreground)); display: inline-flex; align-items: center; gap: 6px; }
.dp-dot { width: 7px; height: 7px; border-radius: 50%; background: hsl(var(--smui-green)); }
.dp-dot.off { background: hsl(var(--border)); }
@media (max-width: 560px) { .dp-steps { grid-template-columns: 1fr; } .dp-proj { padding: 16px; } }

.dp-sep { display: flex; align-items: center; gap: 12px; margin: 26px 0 12px; font-family: var(--font-mono);
  font-size: 10px; font-weight: 600; letter-spacing: .12em; text-transform: uppercase; color: hsl(var(--muted-foreground)); }
.dp-sep::after { content: ""; flex: 1; height: 1px; background: hsl(var(--border)); }
.dp-prompt { border: 1px solid hsl(var(--border)); background: hsl(var(--card)); border-radius: 14px; padding: 16px 18px;
  display: flex; gap: 14px; align-items: center; flex-wrap: wrap; }
.dp-prompt .dp-ic { width: 40px; height: 40px; border-radius: 11px; background: hsl(var(--secondary)); color: hsl(var(--foreground)); }
.dp-prompt-b { flex: 1 1 260px; min-width: 0; }
.dp-prompt-b b { display: block; font-size: 15px; }
.dp-prompt-b p { margin: 3px 0 0; font-size: 12.5px; line-height: 1.55; color: hsl(var(--muted-foreground)); }
.dp-lock { display: flex; gap: 10px; align-items: flex-start; margin-top: 10px; font-size: 12.5px; line-height: 1.55;
  color: hsl(var(--muted-foreground)); border: 1px dashed hsl(var(--border)); border-radius: 10px; padding: 10px 12px; }
.dp-parts { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; margin-top: 10px; }
.dp-part { border: 1px solid hsl(var(--border)); border-top: 3px solid hsl(var(--c)); background: hsl(var(--card));
  border-radius: 12px; padding: 13px 14px; display: flex; flex-direction: column; gap: 5px; min-width: 0; }
.dp-part-tag { font-family: var(--font-mono); font-size: 9.5px; font-weight: 600; letter-spacing: .08em;
  text-transform: uppercase; color: hsl(var(--c)); }
.dp-part b { font-size: 13.5px; }
.dp-part p { margin: 0; flex: 1; font-size: 12px; line-height: 1.5; color: hsl(var(--muted-foreground)); }
.dp-part .dp-acts { margin-top: 6px; gap: 6px; }
.dp-badge { display: inline-flex; align-items: center; gap: 4px; font-family: var(--font-mono); font-size: 10px;
  letter-spacing: .04em; text-transform: uppercase; padding: 3px 8px; border-radius: 6px;
  background: hsl(var(--secondary)); color: hsl(var(--muted-foreground)); border: 1px solid hsl(var(--border)); }
@media (max-width: 720px) { .dp-parts { grid-template-columns: 1fr; } }

/* Методичка — форма старой страницы «Свой Vibe». Разметка приходит из
   content/articles/svoy-dev-portal.html через чистку (sanitize-notes.js):
   кнопок и data-атрибутов там быть не может, поэтому «Копировать» у блоков
   кода дорисовывает views-article.js. */
.article .as-sect { background: hsl(var(--card)); border: 1px solid hsl(var(--border)); border-radius: 14px;
  padding: 18px 20px; margin: 0 0 14px; scroll-margin-top: 80px; }
.article .as-no { font-family: var(--font-mono); font-size: 11px; font-weight: 700; letter-spacing: .08em;
  color: hsl(var(--primary)); margin: 0 0 10px; }
.article .as-sect p { font-size: 13.5px; line-height: 1.62; margin: 0 0 10px; }
.article .as-sect p:last-child { margin-bottom: 0; }
.article .as-sect > .as-tbl + p, .article .as-sect .as-mt { margin-top: 14px; }
.article .as-tbl { overflow-x: auto; }
.article .as-tbl table { border-collapse: collapse; width: 100%; font-size: 13px; }
.article .as-tbl td { padding: 8px 10px; border-bottom: 1px solid hsl(var(--border)); vertical-align: top; }
.article .as-tbl td:first-child { width: 32%; font-weight: 600; }
.article .as-tbl.is-ph td:first-child { width: 54px; font-family: var(--font-mono); color: hsl(var(--primary)); }
.article .as-tbl tr:last-child td { border-bottom: 0; }
.article .as-var2, .article .as-prov { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; margin: 12px 0 0; }
.article .as-var { border: 1px solid hsl(var(--border)); border-radius: 10px; padding: 14px; min-width: 0;
  display: flex; flex-direction: column; gap: 8px; }
.article .as-var.is-b { border-color: hsl(var(--primary) / .45); background: hsl(var(--primary) / .05); }
.article .as-var-l { font-family: var(--font-mono); font-size: 10.5px; font-weight: 700; letter-spacing: .07em;
  color: hsl(var(--muted-foreground)); }
.article .as-var.is-b .as-var-l { color: hsl(var(--primary)); }
/* Кнопка — у НИЖНЕГО края карточки: карточки варианта в ряду одной высоты,
   и кнопки стоят на одной линии, сколько бы строк ни было в тексте. */
.article .as-var > .btn:last-child { margin-top: auto; }
.article .as-var p { font-size: 12.5px; line-height: 1.55; margin: 0; }
.article .as-sub { font-size: 11.5px; color: hsl(var(--muted-foreground)); }
.article .as-prov > div { border: 1px solid hsl(var(--border)); border-radius: 10px; padding: 13px;
  display: flex; flex-direction: column; gap: 4px; }
.article .as-prov b { font-size: 13px; }
.article .as-prov p { font-size: 12px; color: hsl(var(--muted-foreground)); margin: 0 0 6px; flex: 1; }
.article .as-sect .btn { display: inline-flex; align-items: center; align-self: flex-start;
  text-decoration: none; color: hsl(var(--foreground)); }
.article .as-sect .btn-primary { color: hsl(var(--primary-foreground)); }
.article .as-code { background: hsl(var(--secondary)); border: 1px solid hsl(var(--border)); border-radius: 9px; min-width: 0; }
.article .as-code-h { display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 7px 8px 0 13px; min-height: 26px; font-family: var(--font-mono); font-size: 9.5px; letter-spacing: .08em;
  text-transform: uppercase; color: hsl(var(--muted-foreground)); }
.article .as-code pre { margin: 0; padding: 10px 13px 12px; font-family: var(--font-mono); font-size: 11.5px;
  line-height: 1.6; overflow-x: auto; white-space: pre; background: none; border: 0; }
.article .as-code.is-wrap pre { white-space: pre-wrap; }
.article .as-warn { border: 1px solid hsl(var(--smui-yellow) / .4); background: hsl(var(--smui-yellow) / .08);
  border-radius: 10px; padding: 10px 13px; font-size: 12.5px; line-height: 1.55; margin-top: 12px; }
.article .as-warn-l { display: block; font-family: var(--font-mono); font-size: 9.5px; font-weight: 700;
  letter-spacing: .08em; text-transform: uppercase; color: hsl(var(--smui-yellow)); margin-bottom: 3px; }
@media (max-width: 640px) {
  .article .as-var2, .article .as-prov { grid-template-columns: 1fr; }
  .article .as-sect { padding: 16px; }
}

/* «Проекты»: svoy-dev закреплён сверху и выделен (правка владельца 24.09). */
.dp-pin { border: 1.5px solid hsl(var(--primary) / .45); border-radius: 16px; padding: 18px 20px; margin-bottom: 18px;
  background: linear-gradient(90deg, hsl(var(--primary) / .08), transparent 80%), hsl(var(--card));
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto; gap: 16px; align-items: center; }
.dp-pin .dp-ic { width: 52px; height: 52px; border-radius: 14px; }
.dp-pin-t { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.dp-pin-t b { font-size: 17px; }
.dp-pin p { margin: 4px 0 0; font-size: 12.5px; line-height: 1.55; color: hsl(var(--muted-foreground)); max-width: 62ch; }
.dp-pin p a { color: hsl(var(--primary)); font-weight: 600; }
.dp-pin-a { display: flex; flex-direction: column; gap: 7px; }
.dp-pin-a .btn { display: inline-flex; align-items: center; justify-content: center; gap: 7px; text-decoration: none; }
.dp-pin-more { display: flex; gap: 7px; }
/* «Скачать» и «Удалить» вместе — ровно ширина кнопок над ними (правка
   владельца 24.09): делят ряд пополам. */
.dp-pin-more > * { flex: 1 1 0; display: inline-flex; align-items: center; justify-content: center; }
.dp-pin p a.dp-pin-how { display: inline-block; margin-top: 4px; }
.dp-offer { border: 1.5px dashed hsl(var(--primary) / .45); border-radius: 14px; padding: 13px 18px; margin-bottom: 18px;
  display: flex; flex-wrap: wrap; gap: 12px; align-items: center; }
.dp-offer div { flex: 1 1 260px; font-size: 12.5px; color: hsl(var(--muted-foreground)); }
.dp-offer b { display: block; font-size: 14px; color: hsl(var(--foreground)); }
@media (max-width: 720px) {
  .dp-pin { grid-template-columns: 1fr; }
  .dp-pin-a { flex-direction: row; flex-wrap: wrap; }
}
/* ═══ КОНЕЦ: «Свой dev-портал» ═══════════════════════════════════════════════ */

/* «Чат потока» без абонемента (правка владельца 28.09): кнопка видна и
   нажимается — панель чатов объяснит, что чат в абонементе, — но мутная. */
.btn.is-locked { opacity: .5; filter: grayscale(.6); }
.btn.is-locked:hover { opacity: .7; }

/* Личные встречи в «Вайбкодинге» (28.09): два ряда — кто ведёт, потом цена,
   длительность и «Оформить». В одну строку правая колонка их не вмещала. */
.pmeet { display: grid; grid-template-columns: 36px 1fr auto; column-gap: 10px; row-gap: 8px;
  align-items: center; padding: 12px 16px; border-bottom: 1px solid hsl(var(--border)); }
.pmeet img { grid-row: span 2; align-self: start; width: 36px; height: 36px; border-radius: 50%;
  object-fit: cover; background: hsl(var(--muted)); }
.pmeet-t { grid-column: 2 / 4; min-width: 0; }
.pmeet-t b { display: block; font-size: 13px; font-weight: 650; }
.pmeet-t i { display: block; font-style: normal; font-size: 11.5px; color: hsl(var(--muted-foreground)); }
.pmeet-p { font-size: 12px; color: hsl(var(--muted-foreground)); font-variant-numeric: tabular-nums; }
.pmeet-p b { font-size: 13.5px; font-weight: 700; color: hsl(var(--foreground)); }

/* ═══ Уведомления: колокольчик, панель, страница (28.09.2026) ═════════════════
   Решение владельца: «логика уведомления — маленькой иконкой с цифрами».
   Кнопка стоит в шапке СЛЕВА от переключателя темы, пункт «Уведомления» — в
   левом меню между «Живыми встречами» и «Чатами». Разметку держит
   `js/notif.js`, страницу — `js/views-notifications.js`. */

/* Цифра непрочитанных на колокольчике. Кружок наезжает на угол кнопки, как
   значок приложения; рамка цветом карточки отделяет его от самой иконки. */
.nf-bell { overflow: visible; }
.nf-count {
  position: absolute; top: -5px; right: -5px; min-width: 17px; height: 17px; padding: 0 4px;
  border-radius: 9px; background: hsl(var(--primary)); color: #fff; border: 2px solid hsl(var(--card));
  font-family: var(--font-sans); font-size: 10.5px; font-weight: 700; line-height: 13px;
  text-align: center; font-variant-numeric: tabular-nums; pointer-events: none;
}

/* Панель под колокольчиком. Лежит в body (а не в шапке): шапка прокручивается
   по горизонтали при нехватке места, и панель уезжала бы вместе с ней.
   🔴 Строка `[hidden]` ОБЯЗАТЕЛЬНА: `display:flex` перебивает атрибут, и без
   неё «скрытая» панель остаётся на экране — так уже было с надписью о подписке
   в оплате и с админской полосой у учеников. */
.nf-panel {
  position: fixed; z-index: 64; right: 14px; top: calc(var(--kg-navbar-h) + 8px);
  width: 380px; max-width: calc(100vw - 20px); max-height: calc(100vh - var(--kg-navbar-h) - 24px);
  display: flex; flex-direction: column; overflow: hidden;
  background: hsl(var(--card)); border: 1px solid hsl(var(--border)); border-radius: 14px;
  box-shadow: 0 18px 44px hsl(220 18% 7% / .18);
}
.nf-panel[hidden] { display: none !important; }
body.has-adminbar .nf-panel { top: calc(var(--kg-navbar-h) + var(--kg-adminbar-h) + 8px); }
.nf-head { display: flex; align-items: center; gap: 10px; padding: 11px 14px;
  border-bottom: 1px solid hsl(var(--border)); }
.nf-head b { font-size: 13.5px; }
.nf-head .nf-link { margin-left: auto; }
.nf-list { overflow-y: auto; flex: 1; }
.nf-foot { border-top: 1px solid hsl(var(--border)); padding: 9px 14px; text-align: center;
  background: hsl(var(--secondary)); }
.nf-link { background: none; border: 0; padding: 0; font: inherit; font-size: 12px; font-weight: 600;
  color: hsl(var(--primary)); cursor: pointer; text-decoration: none; }
.nf-link:hover { text-decoration: underline; }

/* Карточка повода. Одна и та же в панели и на странице — разница только в
   отступах (`.nf-compact`). */
.nf { display: grid; grid-template-columns: 32px minmax(0, 1fr) auto; gap: 11px;
  padding: 13px 14px; border-bottom: 1px solid hsl(var(--border)); position: relative;
  cursor: pointer; transition: background .14s; }
.nf:last-child { border-bottom: 0; }
.nf:hover { background: hsl(var(--secondary) / .6); }
.nf.is-new { background: hsl(var(--primary) / .05); }
.nf.is-new::before { content: ''; position: absolute; left: 5px; top: 19px; width: 5px; height: 5px;
  border-radius: 50%; background: hsl(var(--primary)); }
.nf-ico { width: 32px; height: 32px; border-radius: 9px; display: grid; place-items: center;
  background: hsl(var(--c, var(--kg-ui-steel)) / var(--kg-ui-fill)); color: hsl(var(--c, var(--kg-ui-steel))); }
.nf-ico svg { width: 16px; height: 16px; }
.nf-b { min-width: 0; }
.nf-t { font-size: 13.5px; font-weight: 650; line-height: 1.35; }
.nf-d { font-size: 12.5px; color: hsl(var(--muted-foreground)); line-height: 1.45; margin-top: 2px; }
.nf-m { font-size: 11.5px; color: hsl(var(--muted-foreground)); margin-top: 3px; }
.nf-a { margin-top: 8px; }
/* Маленькая галочка «прочитано» — единственный способ погасить повод, не
   переходя по нему (правка владельца: «их прочитать можно только кликом —
   надо маленькую аккуратную иконку»). Бледная, проявляется при наведении на
   карточку; на ощупь остаётся нажимаемой и на телефоне. */
.nf-read { width: 24px; height: 24px; border-radius: 7px; border: 0; background: none; padding: 0;
  color: hsl(var(--muted-foreground)); opacity: .35; cursor: pointer; align-self: start;
  display: grid; place-items: center; transition: opacity .14s, background .14s, color .14s; }
.nf-read svg { width: 14px; height: 14px; }
.nf:hover .nf-read { opacity: .75; }
.nf-read:hover { opacity: 1; background: hsl(var(--secondary)); color: hsl(var(--kg-ui-mint)); }
.nf-read:focus-visible { opacity: 1; outline: 2px solid hsl(var(--ring)); outline-offset: 1px; }
.nf-compact .nf-t { font-size: 13px; }

/* Пусто — и в панели, и на странице. */
.nf-empty { padding: 26px 18px; text-align: center; color: hsl(var(--muted-foreground));
  display: flex; flex-direction: column; align-items: center; gap: 5px; font-size: 12.5px; }
.nf-empty svg { width: 22px; height: 22px; opacity: .45; margin-bottom: 3px; }
.nf-empty b { font-size: 13.5px; color: hsl(var(--foreground)); }
.nf-empty-page { padding: 44px 18px; }

/* Страница «Все уведомления». */
.nf-page { max-width: 760px; }
.nf-chips { display: flex; flex-wrap: wrap; gap: 7px; margin-bottom: 14px; }
.nf-chip { height: 28px; padding: 0 12px; border-radius: 999px; border: 1px solid hsl(var(--border));
  background: hsl(var(--card)); color: hsl(var(--muted-foreground)); font-size: 12px; font-weight: 600;
  display: inline-flex; align-items: center; text-decoration: none; }
.nf-chip:hover { background: hsl(var(--secondary)); color: hsl(var(--foreground)); }
.nf-chip.on { background: hsl(var(--primary)); border-color: hsl(var(--primary)); color: #fff; }
.nf-feed { background: hsl(var(--card)); border: 1px solid hsl(var(--border)); border-radius: 14px;
  overflow: hidden; }
.nf-day { padding: 9px 16px 8px; font-size: 10.5px; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: hsl(var(--muted-foreground)); background: hsl(var(--secondary));
  border-bottom: 1px solid hsl(var(--border)); }
.nf-feed .nf { padding-left: 18px; }
.nf-feed .nf.is-new::before { left: 8px; }

@media (max-width: 640px) {
  /* На телефоне панель занимает ширину экрана: 380 px в неё не влезают, а
     уведомление с кнопкой в узкой колонке нечитаемо. */
  .nf-panel { right: 8px; left: 8px; width: auto; }
}
/* ═══ КОНЕЦ: уведомления ═════════════════════════════════════════════════════ */

/* ═══ Телеграм: посты (28.09.2026) ═══════════════════════════════════════════
   Портал пишет черновик, человек правит и отправляет кнопкой. Экраны —
   `js/views-tg.js`. */
.tg-page { max-width: 820px; display: grid; gap: 16px; }
/* Строка поста в списке: вид повода, название, состояние. */
.tgp { display: grid; grid-template-columns: 118px minmax(0, 1fr) auto; gap: 12px; align-items: center;
  padding: 12px 16px; border-bottom: 1px solid hsl(var(--border)); color: inherit; text-decoration: none; }
.tgp:last-child { border-bottom: 0; }
.tgp:hover { background: hsl(var(--secondary) / .6); }
.tgp-k { font-family: var(--font-mono); font-size: 10.5px; letter-spacing: .05em;
  text-transform: uppercase; color: hsl(var(--muted-foreground)); }
.tgp-t { min-width: 0; }
.tgp-t b { display: block; font-size: 13.5px; font-weight: 650; }
.tgp-t i { display: block; font-style: normal; font-size: 11.5px; color: hsl(var(--muted-foreground)); }
.tgp-st { font-size: 11px; font-weight: 700; padding: 3px 9px; border-radius: 999px;
  background: hsl(var(--secondary)); color: hsl(var(--muted-foreground)); white-space: nowrap; }
.tgp-st.is-draft { background: hsl(var(--primary) / .12); color: hsl(var(--kg-ui-orange)); }

/* Сам пост: слева текст, справа адресаты и кнопки. */
.tg-post { display: grid; grid-template-columns: minmax(0, 1fr) 300px; gap: 16px; align-items: start;
  max-width: 1000px; }
.tg-edit .form-area { font-family: var(--font-sans); }
/* Отправленный текст — только чтение, и это ВИДНО: серый лист без поля ввода. */
.tg-ro { border: 1px solid hsl(var(--border)); border-radius: 9px; background: hsl(var(--secondary) / .5);
  padding: 11px 12px; font-size: 13px; line-height: 1.5; white-space: normal; }
.tg-chk { display: flex; align-items: center; gap: 9px; padding: 7px 0; font-size: 12.5px; }
.tg-chk input { width: 15px; height: 15px; accent-color: hsl(var(--primary)); }
.tg-chk i { font-style: normal; color: hsl(var(--muted-foreground)); font-size: 11.5px; }
.tg-acts { display: flex; flex-direction: column; gap: 8px; margin-top: 14px; }
.tg-acts .btn { width: 100%; justify-content: center; }
.tg-done { margin-top: 14px; padding: 12px 13px; border-radius: 10px; display: grid; gap: 3px;
  background: hsl(var(--kg-ui-mint) / .12); font-size: 12px; color: hsl(var(--muted-foreground)); }
.tg-done svg { width: 16px; height: 16px; color: hsl(var(--kg-ui-mint)); }
.tg-done b { font-size: 13px; color: hsl(var(--foreground)); }
.tg-final { margin-top: 5px; font-style: italic; }

@media (max-width: 900px) {
  .tg-post { grid-template-columns: 1fr; }
  .tgp { grid-template-columns: minmax(0, 1fr) auto; }
  .tgp-k { display: none; }
}
/* ═══ КОНЕЦ: Телеграм ════════════════════════════════════════════════════════ */

/* Предпросмотр поста: пузырь Телеграма под полями ввода. Нужен потому, что в
   поле лежит разметка с тегами, и по ней не видно, где абзац, а где жирный. */
.tg-prev-h { display: flex; align-items: center; justify-content: space-between; gap: 10px;
  margin: 16px 0 7px; }
.tg-bubble { border-radius: 12px; border: 1px solid hsl(var(--border)); background: hsl(var(--card));
  box-shadow: 0 1px 2px hsl(220 18% 7% / .06); padding: 11px 13px; font-size: 13px; line-height: 1.5; }
.tg-bubble i { color: hsl(var(--muted-foreground)); }
.tg-bubble-btn { display: block; margin-top: 10px; padding: 8px 10px; border-radius: 8px;
  background: hsl(var(--primary) / .12); color: hsl(var(--kg-ui-orange)); text-align: center;
  font-size: 12.5px; font-weight: 650; }

/* Пояснение «почему замок» под уведомлением. Отдельной строкой со значком:
   это ответ не на «что случилось», а на «почему мне не открыть», и без него
   кнопка «Что даёт абонемент» читается как реклама невпопад (правка владельца
   28.09). */
.nf-note { display: flex; align-items: flex-start; gap: 6px; margin-top: 6px;
  padding: 7px 9px; border-radius: 8px; background: hsl(var(--secondary));
  font-size: 11.5px; line-height: 1.4; color: hsl(var(--muted-foreground)); font-style: italic; }
.nf-note svg { width: 13px; height: 13px; flex: none; margin-top: 1px; font-style: normal; }

/* ═══ Значок «подключите Телеграм-бота» (28.09.2026) ══════════════════════════
   Решение владельца: «между уведомлением и темой — значок телеграм-бота
   красного оттенка и с красной иконкой восклицательного знака, как цифры в
   уведомлениях». Разметку и работу держит `js/tg-nudge.js`.

   ⚠️ Красный берётся из токена `--destructive`, а не вписан цветом: у портала
   одна палитра, и в тёмной теме этот токен другой (11 80% 59% против
   15 90% 41%). Вписанный #b33 в тёмной теме читался бы грязным пятном. */
/* 🔴 ЗДЕСЬ СВОЙ КРАСНЫЙ, А НЕ `--destructive`, И ЭТО НЕ САМОДЕЯТЕЛЬНОСТЬ.
   Палитра портала тёплая: `--primary` это 17° (оранжевый), а «красный»
   палитры — 15° (`--destructive`, он же `--kg-ui-red`). Два градуса разницы:
   на снимке экрана значок тревоги вышел ровно того же цвета, что цифра
   непрочитанных рядом, и «красный восклицательный знак» переставал читаться
   как тревога — он читался как ещё одна фирменная плашка. Поэтому тут
   ИМЕННО красный, 0°, заведённый одним токеном на оба варианта темы.
   Правило поставлено ПОСЛЕ :root в начале файла — при равной специфичности
   побеждает последнее по тексту, переменная просто добавляется к набору. */
:root { --tgn-red: 0 72% 46%; }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) { --tgn-red: 0 72% 64%; } }
:root[data-theme="dark"] { --tgn-red: 0 72% 64%; }

.tgn { overflow: visible;
  color: hsl(var(--tgn-red));
  border-color: hsl(var(--tgn-red) / .5);
  background: hsl(var(--tgn-red) / .1); }
.tgn:hover { color: hsl(var(--tgn-red));
  border-color: hsl(var(--tgn-red) / .75);
  background: hsl(var(--tgn-red) / .17); }

/* Восклицательный знак — ТА ЖЕ форма, что у цифры на колокольчике (.nf-count):
   кружок наезжает на угол кнопки, рамка цветом карточки отделяет его от
   иконки. Отличается только цветом: у колокольчика это счётчик (--primary),
   здесь — тревога (--destructive). */
.tgn-bang {
  position: absolute; top: -5px; right: -5px; min-width: 17px; height: 17px; padding: 0 4px;
  border-radius: 9px; background: hsl(var(--tgn-red)); color: #fff;
  border: 2px solid hsl(var(--card));
  font-family: var(--font-sans); font-size: 10.5px; font-weight: 700; line-height: 13px;
  text-align: center; pointer-events: none;
}

/* Окно под значком. Лежит в body, а не в шапке: шапка при нехватке места
   прокручивается по горизонтали, и окно уезжало бы вместе с ней (ровно та же
   причина, что у панели уведомлений).
   🔴 Строка `[hidden]` ОБЯЗАТЕЛЬНА: `display:flex` перебивает атрибут, и без
   неё «скрытое» окно остаётся на экране. */
.tgn-pop {
  position: fixed; z-index: 64; right: 14px; top: calc(var(--kg-navbar-h) + 8px);
  width: 340px; max-width: calc(100vw - 20px);
  display: flex; flex-direction: column; overflow: hidden;
  background: hsl(var(--card)); border: 1px solid hsl(var(--border)); border-radius: 14px;
  box-shadow: 0 18px 44px hsl(220 18% 7% / .18);
}
.tgn-pop[hidden] { display: none !important; }
body.has-adminbar .tgn-pop { top: calc(var(--kg-navbar-h) + var(--kg-adminbar-h) + 8px); }

.tgn-h { display: flex; align-items: center; gap: 9px; padding: 11px 13px;
  border-bottom: 1px solid hsl(var(--border)); }
.tgn-h b { font-size: 13px; font-weight: 700; flex: 1; min-width: 0; }
.tgn-h-i { display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; border-radius: 8px; flex: none;
  background: hsl(var(--tgn-red) / .1); color: hsl(var(--tgn-red)); }
.tgn-h-i svg { width: 15px; height: 15px; }

.tgn-b { padding: 12px 13px 4px; }
.tgn-b p { margin: 0; font-size: 12.5px; line-height: 1.5; }
.tgn-l { list-style: none; margin: 11px 0 0; padding: 0; display: flex;
  flex-direction: column; gap: 8px; }
.tgn-l li { display: flex; align-items: flex-start; gap: 8px;
  font-size: 12px; line-height: 1.4; color: hsl(var(--muted-foreground)); }
.tgn-l svg { flex: none; margin-top: 1px; color: hsl(var(--primary)); }
.tgn-note { margin-top: 11px !important; padding: 8px 9px; border-radius: 8px;
  background: hsl(var(--secondary)); font-size: 11.5px !important;
  color: hsl(var(--muted-foreground)); }

.tgn-a { display: flex; gap: 8px; padding: 12px 13px; }
.tgn-a .btn { flex: 1; justify-content: center; }
/* Под учеником вместо кнопки — причина. Столбиком, потому что объяснение в
   три строки и рядом с кнопкой «Закрыть» оно бы её сплющило. */
.tgn-a.is-imp { flex-direction: column; align-items: stretch; }
.tgn-imp { display: flex; align-items: flex-start; gap: 7px; padding: 9px 10px;
  border-radius: 8px; background: hsl(var(--secondary));
  font-size: 11.5px; line-height: 1.45; color: hsl(var(--muted-foreground)); }
.tgn-imp svg { flex: none; margin-top: 1px; }

/* Скрытая строка в «Оплатах»: отменённый или тестовый заказ (28.09.2026).
   Видна только при включённой галочке «скрытые заявки» — и должна читаться
   как вычеркнутая, а не как обычная. Приглушаем строку целиком, но НЕ
   ссылки действий: «вернуть» обязана оставаться заметной, иначе отменённый
   по ошибке заказ некому будет вернуть. */
.tbl tr.is-off > td { opacity: .55; }
.tbl tr.is-off:hover > td { opacity: 1; }
.tbl tr.is-off > td a { opacity: 1; }

/* Переключатель предпросмотра: три вкладки в узкой колонке не помещались в
   строку и рвались пополам. */
.tg-prev-h { flex-wrap: wrap; }
.tg-prev-h .seg button { padding: 0 9px; font-size: 11.5px; }

/* Карточка-ссылка подборки на телефоне (28.09.2026, статья «Топ скиллов»).
   Сетка 60px / текст / «github →» задумана для широкой колонки: на 390 px от
   неё остаётся полоска текста в тридцать символов, и описание в четыре строки
   растягивается на двенадцать. Значок сужаем, колонку призыва убираем совсем —
   карточка целиком и так ссылка, а стрелка рядом с заголовком ничего не
   добавляет. Правило общее для .article: те же карточки стоят в конспектах. */
@media (max-width: 560px) {
  .article .course-module { grid-template-columns: 40px 1fr; }
  .article .course-module-cta { display: none; }
  .article .course-module-body { padding: 12px 14px; }
}
