/* Хаб: витрина 15 групп. Вся карточка — кнопка; атмосфера фоном (постер
   сцены, где он есть, иначе градиент времени суток); статусы честные.
   Движение по закону: каскад входа один раз (300-400 мс на всё), фон
   дышит только на видимой вкладке и молчит при reduced-motion.

   Единый язык движения: длительности лежат переменными на весь хаб, изинг
   один (--ease). Нажатие --mo-tap, ответ элементов управления --mo-ui,
   жизнь карточки и кросс-фейд лупа --mo-card, панель и вуаль --mo-veil,
   медленный наезд сцены под рукой --mo-scene. Совсем медленный свет (зум
   задника, дыхание) живёт секундами в @keyframes: это погода, а не ответ
   на руку.

   Четыре длительности пришли с законом движения хаба (H-3), и у каждой
   есть работа, а не вкус:
     --mo-door  180 мс  дверь под рукой поднимается на 4 пикселя;
     --mo-step   30 мс  шаг каскада: карточки въезжают друг за другом;
     --mo-in    180 мс  сам въезд карточки и появление строки в панели;
     --mo-enter 320 мс  вход в раздел: карточка растёт в шапку.
   Каскад считается так: min(--i,7)*30 + 180 = 390 мс на всю сетку, то есть
   под потолком 400. Индекс упёрт в 7 нарочно: восьмая карточка на 1440
   лежит ниже сгиба, и ждать её выезда некому. */

.hg{min-height:100vh;background:var(--bg-1,#101511);padding:clamp(18px,3.6vw,40px) clamp(14px,4.5vw,52px) 90px;font-family:var(--font-t);
  --mo-tap:.2s;--mo-ui:.3s;--mo-card:.45s;--mo-veil:.4s;--mo-scene:.9s;
  --mo-door:.18s;--mo-step:30ms;--mo-in:.18s;--mo-enter:.32s;
  /* ── КЛАСС КОНТРАСТА ХАБА (EWA-PRAVKI U01 п.1, OB-3) ──
     Скриншоты тестера в светлой теме: шапка и описания карточек тонули в
     пейзаже. Лечится не одной строкой, а правилом движка: текста поверх
     картинки на хабе ровно два места, и над каждым стоит подложка одной
     плотности. Ниже неё картинка не показывается никогда, поэтому кадр
     под подложкой может быть любым.
       --hg-img    прозрачность самой картинки (сцена и атмосфера карточки);
       --hg-plate  плотность подложки: сколько в ней --bg-1;
       --hg-plate-b нижний край плашки карточки (только плотнее верхнего).
     Числа не на глаз: node tools/hub-contrast.mjs считает по формуле WCAG
     пару «цвет текста против цвета подложки» во всех восьми темах при
     чёрном и при белом кадре под подложкой. Тронешь число — прогони его. */
  --hg-img:.5;--hg-plate:78%;--hg-plate-b:88%;
  /* текстовый оттенок мятного. У --gold и --indigo он есть в themes.css
     (--gold-ink, --indigo-ink, «акцент как ТЕКСТ»), у --mint нет, а слово
     «работает» на карточке написано именно мятным и на светлых темах даёт
     2.5:1. Тот же приём и та же причина: на тёмных совпадает с акцентом,
     на светлых темнее ровно настолько, чтобы взять 4.5. Живёт в хабе, а не
     в themes.css: за themes.css сейчас держится неслитая ветка плеера. */
  --mint-ink:var(--mint)}
:is([data-theme="white"],[data-theme="sage"],[data-theme="cream"],[data-theme="sky"],[data-theme="lavender"]) .hg{
  --mint-ink:color-mix(in srgb,var(--mint) 62%,var(--ink))}
/* Флаг для робота: снимает обе подложки разом. Негативный прогон обязан
   покраснеть и по axe-core, и по tools/hub-contrast.mjs. В проде не стоит. */
body[data-noscrim] .hg-scene::before,body[data-noscrim] .hgc-body{background:none}

/* ── живая сцена над шапкой ──
   Слои: задник (зум 1.00-1.06 за 38 с), свет (дыхание прозрачностью),
   пропсы (плывут 4-8px в противофазе). Скролл сдвигает слои с разной
   скоростью через --sy (один style-write на кадр, только transform). */
.hg-scene{position:absolute;left:0;right:0;top:0;height:clamp(300px,44vh,520px);overflow:hidden;pointer-events:none;z-index:0}
.hgl{position:absolute;inset:0;will-change:transform}
/* глубина: задник отстаёт от скролла (дальше), пропсы обгоняют (ближе) */
.hgl-back{transform:translateY(calc(var(--sy,0)*46px))}
.hgl-img{position:absolute;inset:0;display:block;background-size:cover;background-position:center 30%;opacity:0;transition:opacity .8s var(--ease)}
.hg-scene.is-loaded .hgl-img{opacity:var(--hg-img)}
.hgl-light{background:radial-gradient(90% 70% at 28% 8%,color-mix(in oklab,var(--gold) 16%,transparent),transparent 65%);opacity:.5}
/* Пропсы двигаются только со скроллом (параллакс), собственной жизни у них
   больше нет. Чайка уплывала на 9 пикселей за 26 секунд, куст качался на 5
   за 19: ниже порога, на котором глаз вообще признаёт это движением, и при
   этом два вечных композитных слоя. Проверка вкуса: убери и посмотри,
   потерялось ли что-нибудь. Нет. Значит шум. */
.hgl-gull{transform:translateY(calc(var(--sy,0)*-30px))}
.hgl-gull img{position:absolute;left:16%;top:16%;width:clamp(26px,3.4vw,50px);height:auto;opacity:.85}
.hgl-bush{transform:translateY(calc(var(--sy,0)*-64px))}
.hgl-bush img{position:absolute;left:-2%;bottom:-6%;width:clamp(170px,24vw,360px);height:auto;opacity:.9}
.hg-scene-fade{position:absolute;inset:auto 0 0 0;height:55%;background:linear-gradient(180deg,transparent,var(--bg-1,#101511) 92%)}
/* скрим под текст шапки: сцена не имеет права спорить с заголовком.
   Был косой градиент 82% → 38% → прозрачный с 60% ширины, и правее этой
   отметки текст стоял на голой картинке: на 390 туда уезжала вся шапка.
   Теперь веил сплошной и одной плотности с плашкой карточки, поэтому
   подложку получает КАЖДЫЙ текст над сценой, а не тот, что удачно лёг
   слева: шапка, «продолжить», «сегодня», «что нового». */
.hg-scene::before{content:"";position:absolute;inset:0;z-index:1;background:color-mix(in srgb,var(--bg-1,#101511) var(--hg-plate),transparent)}
.hg-top h1,.hg-line,.hg-stat{text-shadow:0 1px 14px color-mix(in oklab,var(--bg-1,#101511) 85%,transparent)}
/* В покое на хабе живут ровно две бесконечные анимации, обе в сцене над
   шапкой: наезд задника и дыхание света. Было девятнадцать (плюс чайка,
   плюс куст, плюс дыхание атмосферы на каждой из пятнадцати карточек), а
   закон движения разрешает три одновременно. */
@media (prefers-reduced-motion:no-preference){
  body:not(.hg-asleep) .hgl-img{animation:hgl-zoom 38s ease-in-out infinite alternate}
  body:not(.hg-asleep) .hgl-light{animation:hgl-breath 11s ease-in-out infinite}
}
@keyframes hgl-zoom{from{transform:scale(1)}to{transform:scale(1.06)}}
@keyframes hgl-breath{0%,100%{opacity:.38}50%{opacity:.62}}

/* вау-вход: один раз в день сцена проявляется от света, дальше без театра.
   backwards, а не both: both это forwards плюс backwards, то есть кадр
   конца остаётся навсегда приклеенным к элементу. Здесь это был filter, и
   сцена доживала день с намертво зафиксированной яркостью. Конец анимации
   и так равен собственному состоянию элемента, forwards тут ничего не
   держал, кроме лишнего слоя. */
@media (prefers-reduced-motion:no-preference){
  body[data-entrance="wow"] .hg-scene{animation:hg-dawn 1.1s var(--ease) backwards}
}
@keyframes hg-dawn{from{filter:brightness(.3) saturate(.7)}to{filter:brightness(1) saturate(1)}}
body[data-entrance="calm"] .hgl-img{transition:none}

.hg-stat{margin:8px 0 0;min-height:1.3em;color:var(--gold-ink);font-size:.8125rem;font-weight:700;line-height:1.3}
@media (max-width:640px){.hg-stat{min-height:2.6em}}
.hg-stat b{font-variant-numeric:tabular-nums}

.hg-top{position:relative;z-index:1;display:flex;gap:16px;align-items:flex-start;max-width:1180px;margin:0 auto 18px;flex-wrap:wrap}
.hg-top h1{font-family:var(--font-d);font-size:clamp(1.5rem,3.4vw,2.25rem);font-weight:600;color:var(--ink);margin:0 0 6px}
.hg-line{max-width:52ch;color:var(--ink-dim);font-size:.9375rem;line-height:1.5;margin:0}
.hg-map{
  margin-left:auto;display:flex;gap:9px;align-items:center;justify-content:center;flex:none;
  min-height:48px;min-width:48px;padding:0 16px;border-radius:14px;text-decoration:none;
  color:var(--ink-dim);font-weight:700;font-size:.875rem;
  background:var(--shell);box-shadow:inset 0 0 0 1px var(--shell-line);
  transition:color var(--mo-ui) var(--ease),background var(--mo-ui) var(--ease),transform var(--mo-ui) var(--ease);
}
.hg-map:hover{color:var(--ink);background:var(--gold-soft)}
.hg-map:active{transform:scale(.97)}
.hg-map:focus-visible{outline:2px solid var(--gold);outline-offset:2px}
.hg-map svg{width:18px;height:18px}

/* ── продолжить с места ────────────────────────────────────────────
   Пустой блок раньше держал свои 16 пикселей отступа даже с атрибутом
   hidden: авторский display:flex в классе бьёт hidden молча, это тот же
   сорт ошибки, что и панель, которая «есть», но не открывается. Теперь
   пустота убирается :empty, а hidden погашен явно у всех трёх блоков. */
.hg-lead{display:block}
/* Резерв места на телефоне. Блок наполняет hub-groups.js, а он defer и на
   медленной сети доезжает после первого кадра: пустой блок давал сетке
   встать выше, потом она съезжала на 214 px (CLS 0.216 на проде, iPhone 12,
   CPU x4, 4G, 14.09.2026). 253 px это высота блока у нового ученика на 390.
   С резервом, та же проверка подменой CSS на проде: CLS 0.039. */
@media (max-width:640px){.hg-lead{min-height:253px}}
.hg-continue,.hg-today,.hg-new{max-width:1180px;margin-left:auto;margin-right:auto}
.hg-continue:empty,.hg-today:empty,.hg-new:empty{display:none}
.hg-continue[hidden],.hg-today[hidden],.hg-new[hidden]{display:none}
.hg-continue{position:relative;z-index:1;margin-bottom:14px}

/* одна большая карточка: где человек остановился, с прогрессом и временем */
.hg-cont{
  display:grid;gap:5px;padding:15px 18px 16px;border-radius:20px;text-decoration:none;
  color:var(--ink);background:var(--gold-soft);
  box-shadow:inset 0 0 0 1px color-mix(in oklab,var(--gold) 30%,transparent);
  transition:transform var(--mo-door) var(--ease);
}
.hg-cont:hover{transform:translateY(-4px)}
.hg-cont:active{transform:scale(.98)}
.hg-cont:focus-visible{outline:2px solid var(--gold);outline-offset:2px}
.hg-cont-k{color:var(--gold-ink);font-size:.6875rem;font-weight:800;letter-spacing:.08em;text-transform:uppercase}
.hg-cont-title{font-family:var(--font-d);font-size:clamp(1.0625rem,2.4vw,1.375rem);font-weight:600;line-height:1.2}
.hg-cont-where{color:var(--ink-dim);font-size:.8125rem}
.hg-cont-bar{display:block;height:5px;border-radius:99px;margin:5px 0 1px;background:color-mix(in oklab,var(--ink) 12%,transparent);overflow:hidden}
.hg-cont-bar i{display:block;height:100%;border-radius:99px;background:var(--gold)}
.hg-cont-meta{color:var(--gold-ink);font-size:.75rem;font-weight:700;font-variant-numeric:tabular-nums}
.hg-cont-when{color:var(--ink-dim);font-size:.75rem}
.hg-cont-empty{position:relative;z-index:1;margin:0;color:var(--ink-dim);font-size:.8125rem;line-height:1.5}

/* ── сегодня: три дела на пять минут ───────────────────────────── */
.hg-today{position:relative;z-index:1;display:block;margin-bottom:14px}
.hg-today h2{font-family:var(--font-d);font-size:.9375rem;font-weight:600;color:var(--ink);margin:0 0 8px}
.hg-today ul{list-style:none;margin:0;padding:0;display:grid;gap:8px;grid-template-columns:repeat(auto-fit,minmax(232px,1fr))}
.hg-today li{display:flex}
.hg-today a,.hg-today .hgt-off{
  flex:1;display:grid;gap:2px;align-content:center;min-height:56px;padding:10px 14px;
  border-radius:14px;text-decoration:none;
  background:color-mix(in oklab,var(--bg-2,#151b16) 80%,transparent);
  box-shadow:inset 0 0 0 1px var(--shell-line);
  transition:transform var(--mo-door) var(--ease),background var(--mo-door) var(--ease);
}
.hg-today a{color:var(--ink)}
.hg-today a:hover{background:var(--gold-soft);transform:translateY(-4px)}
.hg-today a:active{transform:scale(.98)}
.hg-today a:focus-visible{outline:2px solid var(--gold);outline-offset:2px}
.hg-today b{font-size:.8438rem;font-weight:700}
.hg-today span{color:var(--ink-dim);font-size:.7656rem;line-height:1.4}
.hg-today a span{color:var(--gold-ink);font-weight:700;font-variant-numeric:tabular-nums}
.hg-today .hgt-off{color:var(--ink-dim)}
.hg-today .hgt-off b{font-weight:600}
.hg-today .hg-today-empty{margin:0;color:var(--ink-dim);font-size:.8125rem;line-height:1.5}

/* ── что нового: одна строка, собранная из дат реестра ─────────── */
.hg-new{position:relative;z-index:1;margin:0 auto 16px;color:var(--ink-dim);font-size:.8125rem;line-height:1.5;min-height:1.5em}
.hg-new b{color:var(--gold-ink);font-weight:700}

/* сетка групп. Движение карточки собрано из трёх translate-переменных:
   --drift это параллакс ряда при скролле (пишет JS), --lift подъём на hover,
   --press утапливание при нажатии. Так hover не воюет со скроллом. */
.hg-grid{position:relative;z-index:1;max-width:1180px;margin:0 auto;display:grid;gap:13px;grid-template-columns:repeat(auto-fill,minmax(250px,1fr))}
.hgc{
  position:relative;overflow:hidden;text-align:left;border:0;cursor:pointer;
  border-radius:20px;min-height:158px;padding:0;display:flex;flex-direction:column;
  color:var(--ink);font-family:var(--font-t);text-decoration:none;
  background:var(--core);box-shadow:inset 0 0 0 1px var(--shell-line),var(--inner-light);
  --drift:calc(var(--sy,0)*var(--rf,0));
  transform:translateY(calc(var(--drift)*1px + var(--lift,0)*1px)) scale(var(--press,1));
  transition:box-shadow var(--mo-door) var(--ease),--lift var(--mo-door) var(--ease),--press var(--mo-tap) var(--ease);
}
@property --lift{syntax:"<number>";inherits:false;initial-value:0}
@property --press{syntax:"<number>";inherits:false;initial-value:1}
/* дверь под рукой: 4 пикселя вверх и мягкая тень за 180 мс. Подъём едет
   переменной --lift, а не своим transform: карточка уже держит на себе
   параллакс ряда и утапливание, три источника в одном свойстве иначе
   затирают друг друга. Тень тут единственное некомпозитное свойство на
   хабе, и она поэтому замерена отдельно, а не принята на веру. */
.hgc:hover{--lift:-4;box-shadow:inset 0 0 0 1px color-mix(in oklab,var(--gold) 40%,transparent),0 20px 44px -24px color-mix(in oklab,var(--gold) 45%,transparent)}
.hgc:active{--press:.98}
.hgc:focus-visible{outline:2px solid var(--gold);outline-offset:3px}
/* Прозрачности .78 у описания больше нет. Она множилась на альфу самого
   --ink-dim (.68 на светлых темах), давало 0.53, и строка карточки жила на
   контрасте 2.2:1 при норме 4.5. Свет карточки под рукой остался, строка
   в нём больше не участвует. */
.hgc-atm{transition:opacity var(--mo-card) var(--ease),transform var(--mo-scene) var(--ease)}
/* hover больше не поднимает прозрачность картинки: подложка рассчитана
   на --hg-img, и любая её прибавка под рукой рушила бы гарантию 4.5:1.
   Наезд остаётся, он контраста не трогает. */
.hgc:hover .hgc-atm{transform:scale(1.03)}
@media (prefers-reduced-motion:reduce){.hgc-atm{transition:opacity var(--mo-card) var(--ease)}.hgc:hover .hgc-atm{transform:none}}
/* видео-луп сцены поверх постера, входит кросс-фейдом только на hover */
.hgc-vid{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;opacity:0;transition:opacity var(--mo-card) var(--ease)}
.hgc-vid.on{opacity:1}

/* атмосфера: постер или градиент tod; дышит светом. Градиенты общие с
   миниатюрами строк панели: у карточки без постера и у миниатюры без
   постера одно и то же небо */
.hgc-atm{position:absolute;inset:0;z-index:0;opacity:var(--hg-img);background-size:cover;background-position:center}
.hgc-atm.tod-day,.sec-thumb.tod-day{background-image:linear-gradient(150deg,color-mix(in oklab,var(--gold) 26%,transparent),transparent 55%),radial-gradient(120% 100% at 80% 0%,color-mix(in oklab,var(--mint) 14%,transparent),transparent 60%)}
.hgc-atm.tod-sunset,.sec-thumb.tod-sunset{background-image:linear-gradient(150deg,color-mix(in oklab,var(--gold-deep) 34%,transparent),transparent 60%),radial-gradient(110% 100% at 85% 10%,color-mix(in oklab,var(--indigo) 16%,transparent),transparent 60%)}
.hgc-atm.tod-night,.sec-thumb.tod-night{background-image:linear-gradient(160deg,color-mix(in oklab,var(--indigo) 26%,transparent),transparent 55%),radial-gradient(120% 110% at 75% 0%,color-mix(in oklab,var(--gold) 12%,transparent),transparent 55%)}
.hgc-atm::after{
  content:"";position:absolute;inset:-20%;
  background:radial-gradient(60% 60% at 30% 30%,color-mix(in oklab,var(--gold) 10%,transparent),transparent 70%);
  opacity:.6;
}
/* Дыхание атмосферы здесь было: каждая из пятнадцати карточек качала свой
   ::after 26 секунд по кругу, вечно. Оно не отвечало ни на одно действие,
   его нельзя было заметить, не уставившись в одну карточку полминуты, и
   оно в одиночку превышало бюджет закона движения в пять раз. Свет карточки
   остался статикой, живёт она теперь под рукой: hover будит наезд .hgc-atm
   и видео-луп сцены, то есть движение с адресатом. */

/* Плашка карточки: вторая половина класса контраста. Была прозрачной
   сверху и доходила до 55% только к 78% высоты, а заголовок и описание
   стоят как раз наверху, то есть на голом постере. Теперь плашка начинается
   на --hg-plate и к низу только плотнеет; счётчик считает по слабому краю. */
.hgc-body{position:relative;z-index:1;display:flex;flex-direction:column;gap:6px;padding:16px 16px 14px;flex:1;background:linear-gradient(180deg,color-mix(in srgb,var(--bg-1,#101511) var(--hg-plate),transparent) 0%,color-mix(in srgb,var(--bg-1,#101511) var(--hg-plate-b),transparent) 100%)}
.hgc h2{font-family:var(--font-d);font-size:1.0625rem;font-weight:600;margin:0}
/* Описание карточки живёт в ОДНУ строку (EWA-PRAVKI U01 п.2, OB-3): полное
   описание уезжает в панель раздела. Обрезка запрещена, поэтому строка,
   которая не влезла, прячется целиком: меряет и прячет fitLines() в
   hub-groups.js, здесь только запрет переноса. */
.hgc-line{color:var(--ink-dim);font-size:.8281rem;line-height:1.5;margin:0;max-width:34ch;white-space:nowrap;overflow:hidden}
/* живая строка состояния: только из реально записанного, у новичка её нет */
.hgc-live{margin:0;color:var(--gold-ink);font-size:.75rem;font-weight:700;line-height:1.4;font-variant-numeric:tabular-nums}
.hgc-meta{margin-top:auto;display:flex;gap:6px;flex-wrap:wrap;padding-top:10px}
.hgs{font-size:.6875rem;font-weight:700;padding:4px 9px;border-radius:99px;color:var(--ink-dim);background:color-mix(in srgb,var(--bg-1,#101511) 55%,transparent);box-shadow:inset 0 0 0 1px var(--shell-line)}
.hgs.on{color:var(--mint-ink);box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--mint) 35%,transparent)}
.hgs.soon{color:var(--gold-ink)}

/* каскад входа: один раз в день. 12 пикселей снизу, шаг 30 мс, вся сетка
   укладывается в 390 мс (min(--i,7)*30 + 180).
   fill backwards, а не forwards: forwards навечно замораживал transform
   конечного кадра и глушил hover-подъём и параллакс после входа */
@media (prefers-reduced-motion:no-preference){
  body[data-entrance="wow"] .hg-grid.cascade .hgc{animation:hg-in var(--mo-in) var(--ease) backwards;animation-delay:calc(min(var(--i),7)*var(--mo-step))}
  @keyframes hg-in{from{opacity:0;transform:translateY(12px)}}
}

/* панель группы и карточка «скоро» */
.hg-veil{position:fixed;inset:0;z-index:900;background:rgba(8,10,9,.6);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);opacity:0;transition:opacity var(--mo-veil) var(--ease)}
.hg-veil.on{opacity:1}
.hg-panel{
  position:fixed;z-index:910;left:50%;top:50%;width:min(92vw,640px);max-height:86vh;overflow-y:auto;
  transform:translate(-50%,-48%) scale(.97);opacity:0;
  border-radius:24px;padding:22px;
  background:color-mix(in oklab,var(--bg-2,#151b16) 94%,var(--ink) 2%);
  box-shadow:inset 0 1px 0 color-mix(in oklab,var(--ink) 14%,transparent),0 40px 90px -40px rgba(0,0,0,.8);
  transition:transform var(--mo-veil) var(--ease),opacity var(--mo-veil) var(--ease),height var(--mo-veil) var(--ease);
}
.hg-panel.on{transform:translate(-50%,-50%) scale(1);opacity:1}
/* тихий возврат: группа, развёрнутая в прошлый заход, встаёт на место без
   театра. Класс живёт два кадра, дальше панель отвечает руке как обычно. */
.hg-panel.no-anim,.hg-veil.no-anim{transition:none}
/* Раскрытие группы: высота растёт от роста карточки до своей, содержимое
   проявляется строка за строкой. Соседи при этом не двигаются вообще, и
   это не удача, а устройство: панель position:fixed и лежит НАД потоком,
   в потоке страницы её нет ни на один кадр. Высоту ведёт JS (openPanel):
   пиксель старта и пиксель конца он меряет, после конца отпускает в auto,
   иначе панель осталась бы с замороженной высотой и её содержимое
   переставало бы помещаться при смене темы или размера шрифта. */
.hg-panel.is-growing{overflow:hidden}
@media (prefers-reduced-motion:no-preference){
  .hg-panel.on .hg-sec>*{animation:hg-row var(--mo-in) var(--ease) backwards}
  .hg-panel.on .hg-sec>*:nth-child(1){animation-delay:calc(var(--mo-step)*2)}
  .hg-panel.on .hg-sec>*:nth-child(2){animation-delay:calc(var(--mo-step)*3)}
  .hg-panel.on .hg-sec>*:nth-child(3){animation-delay:calc(var(--mo-step)*4)}
  .hg-panel.on .hg-sec>*:nth-child(4){animation-delay:calc(var(--mo-step)*5)}
  .hg-panel.on .hg-sec>*:nth-child(n+5){animation-delay:calc(var(--mo-step)*6)}
  @keyframes hg-row{from{opacity:0;transform:translateY(6px)}}
}
.hg-close{position:absolute;right:12px;top:12px;display:grid;place-items:center;width:48px;height:48px;border:0;border-radius:12px;cursor:pointer;color:var(--ink-dim);background:var(--shell);box-shadow:inset 0 0 0 1px var(--shell-line);transition:color var(--mo-ui) var(--ease),transform var(--mo-ui) var(--ease)}
.hg-close:hover{color:var(--ink)}
.hg-close:active{transform:scale(.92)}
.hg-close:focus-visible{outline:2px solid var(--gold);outline-offset:2px}
.hg-close svg{width:18px;height:18px}
.hg-panel h2{font-family:var(--font-d);font-size:1.25rem;font-weight:600;color:var(--ink);margin:0 0 6px;padding-right:52px}
.hg-panel .p-line{color:var(--ink-dim);font-size:.875rem;line-height:1.55;margin:0 0 16px}
.hg-sec{display:grid;gap:10px}
.hg-sec a,.hg-sec .sec-soon{
  display:flex;gap:12px;align-items:center;border-radius:16px;padding:13px 15px;text-decoration:none;
  color:var(--ink);background:var(--core);box-shadow:inset 0 0 0 1px var(--shell-line);
  transition:background var(--mo-door) var(--ease),transform var(--mo-door) var(--ease);
}
/* строка панели это такая же дверь, как карточка: тот же подъём, та же
   длительность. Один язык на все двери, иначе рука учится дважды */
.hg-sec a:hover{background:var(--gold-soft);transform:translateY(-4px)}
.hg-sec a:active{transform:scale(.98)}
.hg-sec a:focus-visible{outline:2px solid var(--gold);outline-offset:2px}
.hg-sec b{font-family:var(--font-d);font-weight:600;font-size:.9375rem}
.hg-sec p{margin:2px 0 0;color:var(--ink-dim);font-size:.7969rem;line-height:1.45}
/* миниатюра атмосферы раздела: постер сцены или градиент tod */
.sec-thumb{flex:none;width:54px;height:44px;border-radius:12px;background-size:cover;background-position:center;box-shadow:inset 0 0 0 1px var(--shell-line)}
/* предметная иконка working-раздела (серия icons-obj): вырубка без плашки,
   мягкая контактная тень. Ширина совпадает с sec-thumb, чтобы соседние
   строки working и «скоро» держали одну вертикаль текста */
.sec-obj{flex:none;width:54px;height:44px;display:grid;place-items:center}
.sec-obj img{width:34px;height:34px;object-fit:contain;filter:drop-shadow(0 3px 5px rgba(0,0,0,.3))}
/* живая строка раздела в панели: тот же закон, что у .hgc-live */
.hg-sec .sec-live{display:block;margin-top:3px;color:var(--gold);font-size:.71rem;font-weight:700;font-style:normal;line-height:1.4;font-variant-numeric:tabular-nums}
.hg-sec .tag{margin-left:auto;flex:none;font-size:.6875rem;font-weight:700;padding:4px 9px;border-radius:99px}
.hg-sec .tag.on{color:var(--mint);background:color-mix(in oklab,var(--mint) 13%,transparent)}
.hg-sec .tag.soon{color:var(--gold);background:var(--gold-soft)}
.hg-gate{margin-top:14px;display:flex;gap:10px}
.hg-gate button{flex:1;border:0;cursor:pointer;font-family:var(--font-t);font-weight:700;font-size:.875rem;min-height:48px;border-radius:14px;color:var(--ink);background:var(--shell);box-shadow:inset 0 0 0 1px var(--shell-line);transition:background var(--mo-ui) var(--ease)}
.hg-gate button:hover{background:var(--gold-soft)}
.hg-gate button:focus-visible{outline:2px solid var(--gold);outline-offset:2px}

/* Вход в раздел: карточка растёт и уезжает туда, где через мгновение
   встанет шапка раздела, а всё остальное на хабе отступает. 320 мс, класс
   вешает JS на любую дверь (карточку, строку панели, «продолжить»).
   Смещение --zy и рост --zs считает JS от настоящего места карточки на
   экране: у верхней карточки ехать некуда, у нижней путь длиннее, и
   одинаковый для обеих сдвиг читался бы как случайный рывок.
   Специфичность .hg .hg-zoom и !important на transform нужны, чтобы зум
   победил и составной transform карточки, и hover-подъём строки. */
.hg .hg-zoom{
  transition:transform var(--mo-enter) var(--ease),opacity var(--mo-enter) var(--ease);
  transform:translateY(calc(var(--zy,0)*1px)) scale(var(--zs,1.06)) !important;
  opacity:.92;
  z-index:3;
}
/* остальной хаб отступает: только opacity, ни одного пикселя движения.
   Иначе уход страницы спорил бы с ростом карточки за внимание */
.hg.hg-entering .hg-grid .hgc:not(.hg-zoom),
.hg.hg-entering .hg-top,
.hg.hg-entering .hg-today,
.hg.hg-entering .hg-new{
  opacity:.3;
  transition:opacity var(--mo-enter) var(--ease);
}

/* ── сокращённое движение ──────────────────────────────────────────
   Длительности здесь НЕ гасятся: это уже делает player.css одной строкой на
   всю платформу (`*,*::before,*::after{transition-duration:.001s}`), и тот
   файл хаб грузит первым. Свой такой же блок тут стоял ровно один вечер:
   негативный тест сломал его нарочно и остался зелёным, потому что работал
   не он. Дубль убран, а осталось то, чем владеет хаб: дверь не едет ни под
   курсором, ни под пальцем, вход в раздел не растёт.
   Звук не трогается ни одной строкой: сокращают движение, а не громкость. */
@media (prefers-reduced-motion:reduce){
  .hgc:hover{--lift:0}
  .hgc:active{--press:1}
  .hg-sec a:hover,.hg-cont:hover,.hg-today a:hover{transform:none}
  .hg-sec a:active,.hg-cont:active,.hg-today a:active{transform:none}
  .hg .hg-zoom{transform:none !important;opacity:1}
  .hg.hg-entering .hg-grid .hgc:not(.hg-zoom),
  .hg.hg-entering .hg-top,
  .hg.hg-entering .hg-today,
  .hg.hg-entering .hg-new{opacity:1}
}

/* ── палец вместо курсора ──────────────────────────────────────────
   На телефоне hover не существует, а липкий hover после тапа оставляет
   дверь поднятой навсегда. Поэтому подъёма там нет вовсе, а ответ на
   нажатие один: минус два процента. */
@media (hover:none){
  .hgc:hover{--lift:0}
  .hg-sec a:hover,.hg-cont:hover,.hg-today a:hover{transform:none}
  .hg-sec a:active,.hg-cont:active,.hg-today a:active{transform:scale(.98)}
}


/* ── нижняя панель телефона ────────────────────────────────────────
   Четыре двери, которые не уезжают со страницей: Учиться, Говорить,
   Смотреть, Я. Разметка лежит в hub.html, а не рождается скриптом, поэтому
   без JS панель работает так же. Адреса взяты из реестра
   content/hub-sections.json по data-key, и робот сверяет их с ним.

   Правила, без которых панель врёт:
   - фон непрозрачный (--bg-1 темы), а не полупрозрачный: сквозь панель
     проступающий текст читается как поломка;
   - под панель уходит безопасная зона айфона (env(safe-area-inset-bottom)),
     иначе нижний ряд пальцем не берётся;
   - страница держит снизу отступ на высоту панели плюс воздух, поэтому
     последняя карточка списка из-под панели видна целиком;
   - выше 640 панели нет вовсе: на десктопе рука не внизу экрана;
   - панель лежит НИЖЕ вуали группы (900): открытая группа гасит её вместе
     со всей витриной, и случайного нажатия сквозь вуаль не бывает. */
.hg-bar{display:none}
@media (max-width:640px){
  .hg-bar{
    display:grid;grid-template-columns:repeat(4,1fr);gap:2px;
    position:fixed;left:0;right:0;bottom:0;z-index:880;
    padding:5px 6px calc(5px + env(safe-area-inset-bottom,0px));
    background:var(--bg-1,#101511);
    box-shadow:inset 0 1px 0 var(--shell-line),0 -14px 32px -24px rgba(0,0,0,.9);
  }
  .hg-bar-i{
    display:grid;justify-items:center;align-content:center;gap:4px;
    min-height:56px;min-width:48px;padding:6px 2px;border-radius:14px;
    text-decoration:none;color:var(--ink-dim);
    font-family:var(--font-t);font-weight:700;font-size:.6875rem;line-height:1;
    transition:color var(--mo-ui) var(--ease),background var(--mo-ui) var(--ease);
  }
  .hg-bar-i svg{width:22px;height:22px;flex:none}
  .hg-bar-i:active{color:var(--ink);background:var(--gold-soft)}
  .hg-bar-i:focus-visible{outline:2px solid var(--gold);outline-offset:-2px}
  /* воздух под последней карточкой: высота панели (66) плюс безопасная зона
     плюс 28 пикселей, чтобы список не упирался в неё вплотную */
  .hg{padding-bottom:calc(94px + env(safe-area-inset-bottom,0px))}
}

/* ── телефон: одна колонка и полка дел ─────────────────────────────
   Колонка прописана явно, а не выведена из minmax: на 390 в строку и так
   влезала одна карточка, но правило «одна колонка на телефоне» это решение,
   а не побочный эффект арифметики. Карточка при этом крупнее: палец берёт
   её всей ладонью, а не целится.

   Полка дел: три дела вставали друг под другом и съедали полэкрана до
   витрины. Теперь они листаются пальцем со снапом, точки прокрутки под
   ними. Полка не выходит за поле страницы ни на пиксель: лишнее уезжает
   внутрь неё самой, а горизонтальной прокрутки у страницы нет. */
/* выше 640 точек нет вовсе: их не рисует скрипт, а эта строка страхует
   разметку, пришедшую с телефона в bfcache при повороте окна. Стоит ДО
   медиа-блока нарочно: специфичность у обеих строк одна, и решает порядок. */
.hg-dots{display:none}
@media (max-width:640px){
  .hg-grid{grid-template-columns:1fr;gap:12px}
  .hgc{min-height:168px}
  .hg-today ul{
    display:grid;grid-template-columns:none;grid-auto-flow:column;grid-auto-columns:82%;
    gap:10px;overflow-x:auto;overscroll-behavior-x:contain;
    scroll-snap-type:x mandatory;scrollbar-width:none;
  }
  .hg-today ul::-webkit-scrollbar{display:none}
  .hg-today li{scroll-snap-align:start}
  .hg-today a,.hg-today .hgt-off{min-height:66px}
  .hg-dots{display:flex;justify-content:center;gap:4px;margin-top:2px}
  /* Кружок восемь пикселей, кнопка вокруг него 48 на 48. Меряется кнопка. */
  .hg-dots button{
    width:48px;height:48px;padding:0;border:0;border-radius:16px;cursor:pointer;
    display:grid;place-items:center;background:none;
  }
  .hg-dots button::before{
    content:"";width:8px;height:8px;border-radius:99px;background:var(--ink-faint);
    transition:background var(--mo-ui) var(--ease),transform var(--mo-ui) var(--ease);
  }
  .hg-dots button[aria-current="true"]::before{background:var(--gold);transform:scale(1.4)}
  .hg-dots button:active{background:var(--gold-soft)}
  .hg-dots button:focus-visible{outline:2px solid var(--gold);outline-offset:-4px}
}
