/* ══════════════════════════════════════════════════════════════
   Дизайн-код «Предел» — перенесён 1:1 из /root/projects/landing-gruppa-ege/index.html
   Лендинг НЕ редактировался; это отдельная копия для сайта школы.
   ══════════════════════════════════════════════════════════════ */
:root{
  --bg:#050816;
  --bg2:#0a1024;
  --panel:rgba(255,255,255,.045);
  --panel2:rgba(255,255,255,.07);
  --stroke:rgba(255,255,255,.09);
  --stroke2:rgba(0,98,235,.45);
  --text:#EAEEFB;
  --muted:#C6CFEC;          /* почти белый: Владислав дважды просил ярче (#8494C0 и #A9B6DC показались тёмными) */
  --accent:#0062EB;
  --accent2:#9EF91F;
  --blue-light:#3B9CFF;
  /* Отдельные, более тёмные версии лайма/синего специально для заливок
     графиков (2026-08-29) — обычные --accent2/--blue-light откалиброваны
     под текст/иконки на чёрном фоне и слишком светлые (OKLCH L 0.89/0.68)
     для больших закрашенных фигур (дуга, столбец, линия): проверено
     dataviz-скиллом (node validate_palette.js) — исходная пара не проходит
     полосу светлоты для тёмного режима, эта — проходит все проверки
     (контраст, читаемость при дальтонизме) на фоне --bg. Текст и бейджи
     статуса продолжают использовать яркие --accent2/--blue-light как и
     раньше — здесь только заливки самих графиков. */
  --chart-good:#5FA518;
  --chart-warn:#2B7FE0;
  --grad:linear-gradient(92deg,#0052D9,#0062EB);
  --grad-text:linear-gradient(92deg,#3B9CFF,#9EF91F);
  --r:32px;
  --font:'Inter',-apple-system,'Segoe UI',Roboto,sans-serif;
  --display:'Unbounded','Inter',sans-serif;
  /* шкала 1.2 от 16 */
  --fs-0:16px; --fs-1:19px; --fs-2:23px; --fs-3:28px; --fs-4:33px; --fs-5:40px; --fs-6:48px;
  --fs-cap:14px;
  /* ── добавлено для сайта школы: токены отступов вместо магических чисел ── */
  --sp-1:4px; --sp-2:8px; --sp-3:12px; --sp-4:16px; --sp-5:24px; --sp-6:32px; --sp-7:48px;
  --ok:#9EF91F;
  --err:#FF6B6B;
  --err-bg:rgba(255,107,107,.08);
  --err-stroke:rgba(255,107,107,.35);
}
*{box-sizing:border-box;margin:0;padding:0}
/* scroll-behavior убран: плавностью управляет Lenis (smooth-scroll.js);
   два механизма разом дают рывок на якорях. */
html{overflow-x:hidden}
/* overflow-x:hidden на body обязателен: без него на /, /ege, /method и ещё
   нескольких страницах вылезает горизонтальная прокрутка (проверено прогоном
   по 11 страницам × 10 ширин 2026-08-20 — 20+ случаев). Побочный эффект: body
   становится скролл-контейнером и position:sticky внутри страницы не работает,
   поэтому липких колонок в вёрстке нет — раскладки строятся без них. */
body{background:var(--bg);color:var(--text);font-family:var(--font);font-size:var(--fs-0);line-height:1.6;overflow-x:hidden}
body::before{content:"";position:fixed;inset:0;z-index:-2;background:
  radial-gradient(900px 500px at 85% -10%,rgba(0,98,235,.16),transparent 60%),
  radial-gradient(700px 500px at -10% 30%,rgba(158,249,31,.06),transparent 60%),
  radial-gradient(800px 600px at 60% 110%,rgba(59,156,255,.08),transparent 60%);
  transform:translateY(var(--parallax-y,0px)) scale(1);
  animation:bgBreathe 70s ease-in-out infinite}
@keyframes bgBreathe{50%{transform:translateY(var(--parallax-y,0px)) scale(1.04)}}
body.is-public{--parallax-y:0px}
@media (prefers-reduced-motion: reduce){body::before{animation:none}}
body::after{content:"";position:fixed;inset:0;z-index:-1;pointer-events:none;opacity:.5;background-image:
  linear-gradient(rgba(255,255,255,.028) 1px,transparent 1px),
  linear-gradient(90deg,rgba(255,255,255,.028) 1px,transparent 1px);
  background-size:56px 56px;
  -webkit-mask-image:radial-gradient(ellipse at 50% 0%,#000 20%,transparent 75%);
  mask-image:radial-gradient(ellipse at 50% 0%,#000 20%,transparent 75%)}
img{max-width:100%;height:auto}
a{color:inherit}

/* Боковой декор (линии, мини-атомы, частицы) убран 2026-08-20: после появления
   настоящих контентных сцен орнамент читался как лишний шум. */

/* ── полоса прогресса чтения ── */
.progress-bar{position:fixed;top:0;left:0;height:3px;width:0%;z-index:30;
  background:linear-gradient(92deg,var(--accent),var(--accent2));transition:width .1s linear}
@media (prefers-reduced-motion: reduce){.progress-bar{transition:none}}

/* ── кнопка «наверх» ── */
.to-top{position:fixed;right:max(18px,env(safe-area-inset-right));bottom:18px;z-index:19;
  width:48px;height:48px;border-radius:50%;background:var(--panel);border:1px solid var(--stroke2);
  color:var(--text);display:flex;align-items:center;justify-content:center;cursor:pointer;
  opacity:0;transform:translateY(12px);pointer-events:none;transition:opacity .25s,transform .25s,border-color .25s}
.to-top.show{opacity:1;transform:none;pointer-events:auto}
.to-top:hover{border-color:var(--accent2)}
@media (prefers-reduced-motion: reduce){.to-top{transition:opacity .01s}}

.wrap{max-width:1120px;margin:0 auto;padding:0 max(20px,env(safe-area-inset-left)) 0 max(20px,env(safe-area-inset-right))}
/* Узкая колонка форм входа/регистрации/паролей. Заголовок считается от ширины
   самой колонки (cqi), а не окна: колонка ограничена 480px, а h1 по 7.2vw рос
   до 48px на любом компьютере — «РЕГИСТРАЦИЯ» и «ВОССТАНОВЛЕНИЕ» не помещались
   и рвались посреди слова. */
.wrap--narrow{max-width:480px;container-type:inline-size}
.wrap--narrow h1{font-size:clamp(var(--fs-0),7.5cqi,var(--fs-4))}
/* Админка и личные кабинеты (2026-08-26) — рабочий инструмент, не витрина: по
   просьбе пользователя разворачиваем на весь экран под конкретное разрешение,
   а не держим в узкой центрированной колонке 1120px, как на публичном сайте
   (там колонка осознанная — под чтение и конверсию, см. план §12). Заголовки
   описаний разделов уже ограничены `.admin-header p{max-width:60ch}`, поэтому
   от снятия потолка ширины текст не станет нечитаемой простынёй — шире станут
   именно таблицы и формы, ради которых это и просят. /login и /register в
   is-app не входят — это компактные формы, которым лишняя ширина не нужна.*/
body.is-app .wrap{max-width:none}
@media (min-width:1400px){
  body.is-app .wrap{padding-left:40px;padding-right:40px}
}
section{padding:29px 0}
section+section{border-top:1px solid var(--stroke)}
.section-alt{background:var(--bg2)}
h1,h2,h3{font-family:var(--display);text-transform:uppercase;letter-spacing:-0.01em;font-weight:900;overflow-wrap:break-word}
/* Нижняя граница была --fs-3 (28px) — при viewport уже 388px это давало
   плоские 28px без какого-либо масштабирования вниз, поэтому на очень узких
   экранах (до ~300px — увеличенный масштаб экрана на телефоне, реальный
   случай) длинное слово без пробелов («РЕГИСТРАЦИЯ») не помещалось в строку
   и overflow-wrap:break-word отрывал одну букву на вторую строку (2026-09-30,
   подтверждено реальным рендерингом Playwright на 280-300px). Граница
   опущена до --fs-2 (23px, тот же токен, что уже служит нижней границей h2) —
   начиная с ~390px формула 7.2vw даёт ровно те же пиксели, что и раньше,
   меняется только диапазон уже ýже 320px. */
h1{font-size:clamp(var(--fs-2),7.2vw,var(--fs-6));line-height:1.24}
h2{font-size:clamp(var(--fs-2),4.5vw,var(--fs-4));line-height:1.24;margin-bottom:18px}
h3{font-size:var(--fs-1);line-height:1.32;text-transform:none}
p{max-width:75ch;font-size:var(--fs-1)}
p+p{margin-top:1em}
.grad-text{background:var(--grad-text);-webkit-background-clip:text;background-clip:text;color:transparent}
.narrative{font-size:var(--fs-1);line-height:1.65}

.kicker{display:inline-flex;align-items:center;gap:8px;font-size:var(--fs-0);font-weight:700;letter-spacing:.16em;text-transform:uppercase;color:var(--accent2);margin-bottom:14px}
.kicker::before{content:"";width:26px;height:2px;background:var(--grad);border-radius:2px;transition:width .5s ease}
/* полоска перед .kicker «дорисовывается» при появлении секции — только на публичных
   страницах (body.is-public), в /lk/*.kicker есть тоже, но там без decorative-tier.
   Два паттерна разметки: kicker внутри .reveal-обёртки и kicker сам с классом .reveal. */
body.is-public .reveal .kicker::before,
body.is-public .kicker.reveal::before{width:0}
body.is-public .reveal.vis .kicker::before,
body.is-public .kicker.reveal.vis::before{width:26px}
@media (prefers-reduced-motion: reduce){.kicker::before{transition:none}}

.reveal{opacity:0;transform:translateY(14px);transition:opacity .42s,transform .42s}
.reveal.vis{opacity:1;transform:none}
@media (prefers-reduced-motion: reduce){
  .reveal{opacity:1;transform:none;transition:none}
  .orbit,.badge .dot,.float-chip{animation:none!important}
}

/* ─── кнопка CTA ─── */
.btn{display:flex;align-items:center;justify-content:center;gap:10px;width:100%;min-height:56px;
  padding:16px 28px;border-radius:16px;cursor:pointer;position:relative;
  background:linear-gradient(92deg,#0A6AF2,#1E7CFF);color:#fff;text-decoration:none;
  border:1px solid rgba(140,200,255,.9);
  font-family:var(--font);font-weight:700;font-size:var(--fs-1);line-height:1.25;text-align:center;
  box-shadow:0 12px 48px rgba(30,124,255,.85),0 0 36px rgba(90,170,255,.6),inset 0 1px 0 rgba(255,255,255,.35);
  transition:transform .2s,box-shadow .2s}
/* overflow:hidden не ставим на кнопки с тултипом (data-tooltip) — тултип у hero-CTA
   всплывает НАД кнопкой (bottom:calc(100% + 10px)), overflow:hidden его бы обрезал */
.btn:not([data-tooltip]){overflow:hidden}
.btn .ripple{position:absolute;border-radius:50%;background:rgba(255,255,255,.45);
  transform:scale(0);animation:rippleAnim .6s ease-out;pointer-events:none}
@keyframes rippleAnim{to{transform:scale(1);opacity:0}}
@media (prefers-reduced-motion: reduce){.btn .ripple{display:none}}
.btn:hover{transform:translateY(-2px);box-shadow:0 18px 60px rgba(30,124,255,1),0 0 44px rgba(90,170,255,.75),inset 0 1px 0 rgba(255,255,255,.35)}
.btn:active{transform:translateY(0);box-shadow:0 6px 24px rgba(0,98,235,.6),inset 0 1px 0 rgba(255,255,255,.2)}
.btn:focus-visible{outline:3px solid var(--accent2);outline-offset:3px}
.btn svg{flex-shrink:0}
.btn .bt{flex:1;text-align:center;min-width:0}
.btn.secondary{background:transparent;box-shadow:none;border:1px solid var(--stroke2);color:var(--text)}
.btn.secondary:hover{box-shadow:0 0 24px rgba(0,98,235,.35);border-color:var(--accent)}
.btn:disabled,.btn[aria-disabled="true"]{opacity:.5;cursor:not-allowed;box-shadow:none;transform:none}
.cta-note{font-size:var(--fs-cap);color:#EAEEFB;margin-top:12px;max-width:52ch}

/* тултип у hero-кнопки регистрации — «Ваш первый шаг к лучшему результату».
   Раньше висел на всех 8 CTA сайта и на мобильном стеке (кнопка сразу под
   коротким p) перекрывал текст над кнопкой — теперь только на hero, где сверху
   всегда достаточно места, плюс сам компактнее и умеет переноситься в 2 строки. */
.btn[data-tooltip]{position:relative}
.btn[data-tooltip]::after{content:attr(data-tooltip);position:absolute;bottom:calc(100% + 10px);left:50%;
  transform:translateX(-50%) translateY(4px);width:max-content;max-width:220px;text-align:center;
  background:var(--panel);border:1px solid var(--stroke2);
  color:var(--text);font-size:12px;font-weight:600;line-height:1.3;padding:7px 12px;border-radius:10px;
  opacity:0;pointer-events:none;transition:opacity .2s,transform .2s;box-shadow:0 10px 30px rgba(0,0,0,.5);z-index:5}
.btn[data-tooltip]:hover::after,.btn[data-tooltip]:focus-visible::after{opacity:1;transform:translateX(-50%) translateY(0)}
@media (prefers-reduced-motion: reduce){.btn[data-tooltip]::after{transition:opacity .01s}}

/* ─── hero / обложка ─── */
.hero{padding:26px 0 19px;position:relative}
.hero .wrap{position:relative;z-index:1}
.hero-grid{display:grid;grid-template-columns:1fr;gap:36px;align-items:center}
.badge{display:inline-flex;align-items:center;gap:8px;background:rgba(158,249,31,.08);
  border:1px solid rgba(158,249,31,.3);color:var(--accent2);font-size:var(--fs-cap);font-weight:600;
  padding:8px 16px;border-radius:99px;margin-bottom:24px}
.badge .dot{width:8px;height:8px;border-radius:50%;background:var(--accent2);box-shadow:0 0 12px var(--accent2);animation:pulse 1.8s infinite}
@keyframes pulse{50%{opacity:.4}}
.hero .sub{color:var(--muted);font-size:var(--fs-1);margin:20px 0 32px;max-width:64ch}
.hero .sub b{color:var(--text);font-weight:600}

/* атом */
.atom-box{position:relative;display:flex;align-items:center;justify-content:center;min-height:300px}
.atom{position:relative;width:260px;height:260px}
.orbit{position:absolute;inset:0;border:1px solid rgba(158,249,31,.35);border-radius:50%;animation:spin 14s linear infinite}
.orbit.o2{border-color:rgba(0,98,235,.4);animation:spin2 19s linear infinite}
.orbit.o3{border-color:rgba(59,156,255,.35);animation:spin3 24s linear infinite}
.orbit::after{content:"";position:absolute;top:-6px;left:50%;width:12px;height:12px;border-radius:50%;background:var(--accent2);box-shadow:0 0 18px var(--accent2)}
.orbit.o2::after{background:var(--accent);box-shadow:0 0 18px var(--accent)}
.orbit.o3::after{background:var(--blue-light);box-shadow:0 0 18px var(--blue-light)}
/* второй «электрон» на той же орбите — фиксированный угловой сдвиг от первого,
   вращается вместе с кольцом (тематика — атом/физика в логотипе) */
.atom .orbit::before{content:"";position:absolute;top:50%;right:-4px;width:7px;height:7px;border-radius:50%;
  background:var(--accent2);box-shadow:0 0 10px var(--accent2);opacity:.7}
.atom .orbit.o2::before{background:var(--accent);box-shadow:0 0 10px var(--accent)}
.atom .orbit.o3::before{background:var(--blue-light);box-shadow:0 0 10px var(--blue-light)}
@keyframes spin{to{transform:rotate(360deg)}}
@keyframes spin2{from{transform:rotate(60deg)}to{transform:rotate(420deg)}}
@keyframes spin3{from{transform:rotate(-60deg)}to{transform:rotate(300deg)}}
.core{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:104px;height:104px;border-radius:50%;
  background:radial-gradient(circle at 35% 30%,rgba(59,156,255,.95),rgba(0,98,235,.75) 60%,rgba(5,8,22,.2));
  box-shadow:0 0 90px rgba(0,98,235,.6);display:flex;align-items:center;justify-content:center}
.core svg{width:52px;height:52px}
.core img{width:58px;height:auto;filter:drop-shadow(0 0 10px rgba(5,8,22,.4))}
.float-chip{position:absolute;background:rgba(10,15,36,.88);border:1px solid var(--stroke2);border-radius:14px;
  padding:10px 14px;font-size:var(--fs-cap);font-weight:600;backdrop-filter:blur(8px);
  display:flex;align-items:center;gap:8px;animation:float 5s ease-in-out infinite}
.float-chip svg{flex-shrink:0}
.float-chip small{display:block;color:var(--muted);font-weight:500;font-size:12px}
.fc1{top:4%;left:0;animation-delay:0s}
.fc2{bottom:10%;right:0;animation-delay:1.2s}
.fc3{top:44%;right:0;animation-delay:2.4s}
@keyframes float{50%{transform:translateY(-10px)}}

/* статистика */
.stats-row{display:grid;grid-template-columns:repeat(2,1fr);gap:12px;margin-top:28px}
.stat{position:relative;background:var(--panel);border:1px solid var(--stroke);border-radius:24px;padding:18px 16px;transition:border-color .25s,transform .25s}
.stat:hover{border-color:var(--stroke2);transform:translateY(-3px)}
.stat::after{content:"";position:absolute;left:16px;right:16px;bottom:10px;height:2px;border-radius:2px;
  background:linear-gradient(92deg,#4FA9FF,#B0FF33);transform:scaleX(0);transform-origin:left;transition:transform .4s ease}
.stat:hover::after{transform:scaleX(1)}
@media (prefers-reduced-motion: reduce){.stat::after{transition:none}}
.stat .num{font-family:var(--display);font-size:clamp(var(--fs-3),3.6vw,var(--fs-5));font-weight:900;
  font-variant-numeric:tabular-nums;background:linear-gradient(92deg,#4FA9FF,#B0FF33);-webkit-background-clip:text;background-clip:text;color:transparent}
.stat .lbl{font-size:var(--fs-cap);color:var(--muted);margin-top:4px}

/* боль / текстовые блоки */
.pain-box{background:var(--panel);border:1px solid var(--stroke);border-radius:var(--r);padding:28px 24px;position:relative;overflow:hidden}
.pain-box::before{content:"";position:absolute;top:0;left:0;bottom:0;width:4px;background:linear-gradient(var(--blue-light),var(--accent))}
.pain-box p{color:var(--text);font-size:var(--fs-1);line-height:1.65}

/* конвейер занятия */
.flow{display:flex;flex-wrap:wrap;gap:10px;align-items:center;margin-top:26px}
.flow .step{background:var(--bg2);border:1px solid var(--stroke2);border-radius:14px;padding:10px 16px;
  font-size:var(--fs-cap);font-weight:600;white-space:nowrap;transition:border-color .3s,box-shadow .3s}
.flow .arr{color:var(--accent2);font-weight:700}
/* шаги схемы «зажигаются» по цепочке при появлении — как сигнал, идущий по конвейеру
   (только на публичных страницах; задержки переиспользуют .stagger>*:nth-child) */
body.is-public .flow.vis .step{border-color:var(--accent2);box-shadow:0 0 12px rgba(158,249,31,.25)}
@media (prefers-reduced-motion: reduce){.flow .step{transition:none}}

/* карточки направлений / «что внутри» */
.cards{display:grid;grid-template-columns:1fr;gap:14px;margin-top:28px}
.card{background:var(--panel);border:1px solid var(--stroke);border-radius:24px;padding:24px;transition:border-color .25s,transform .25s}
.card:hover{border-color:var(--stroke2);transform:translateY(-3px)}
.card h3{margin-bottom:8px}
.card p{font-size:var(--fs-0);color:var(--muted)}
.card .ic{width:46px;height:46px;border-radius:14px;background:rgba(0,98,235,.12);border:1px solid rgba(0,98,235,.35);
  display:flex;align-items:center;justify-content:center;margin-bottom:14px;color:var(--blue-light);transition:transform .3s ease}
.card:hover .ic{transform:scale(1.1) rotate(-4deg)}
@media (prefers-reduced-motion: reduce){.card:hover .ic{transform:none}}
a.card{text-decoration:none;color:inherit;display:block}
.card.card--soon{opacity:.6}
.card .soon-tag{display:inline-block;font-size:12px;font-weight:700;color:var(--muted);
  border:1px solid var(--stroke);border-radius:999px;padding:2px 10px;margin-top:10px}

/* результаты */
.year-label{font-family:var(--display);font-size:var(--fs-1);color:var(--blue-light);margin:36px 0 16px;letter-spacing:.04em;
  display:flex;align-items:center;gap:14px}
.year-label::after{content:"";flex:1;height:1px;background:var(--stroke)}
.res-intro{color:var(--muted);margin-top:6px}
.rail{display:grid;grid-auto-flow:column;grid-auto-columns:min(70vw,250px);gap:14px;
  overflow-x:auto;overflow-y:hidden;scroll-snap-type:x mandatory;padding:14px 2px 28px;
  overscroll-behavior-x:contain;
  -webkit-overflow-scrolling:touch;scrollbar-width:thin;scrollbar-color:var(--stroke2) transparent}
.rail::-webkit-scrollbar{height:8px}
.rail::-webkit-scrollbar-thumb{background:var(--stroke2);border-radius:4px}
.rail::-webkit-scrollbar-track{background:rgba(255,255,255,.04);border-radius:4px}
.vcard{scroll-snap-align:start;background:var(--panel);border:1.5px solid rgba(158,249,31,.75);border-radius:20px;
  box-shadow:0 0 26px rgba(158,249,31,.3);
  overflow:hidden;transition:border-color .25s,box-shadow .25s,transform .25s;position:relative}
.vcard:hover{border-color:var(--accent2);box-shadow:0 0 38px rgba(158,249,31,.5);transform:translateY(-3px)}
.vcard.top{border-color:var(--accent2);box-shadow:0 0 34px rgba(158,249,31,.4)}
.vcard video{display:block;width:100%;aspect-ratio:9/16;object-fit:cover;background:#000}
.vcard video:fullscreen{object-fit:contain}
.vcard video:-webkit-full-screen{object-fit:contain}
.vcard video:-moz-full-screen{object-fit:contain}
.vcard .meta{padding:12px 8px 14px 10px;display:flex;align-items:center;gap:8px}
.vcard .score{font-family:var(--display);font-size:var(--fs-2);font-weight:900;color:var(--accent2);
  text-shadow:0 0 18px rgba(158,249,31,.4);flex-shrink:0}
.vcard .who{text-align:left;min-width:0}
.vcard .who b{display:block;font-size:17px;font-weight:700;line-height:1.3}
.vcard .who span{font-size:15px;color:var(--muted)}
.vcard .who .uni{display:block;color:var(--blue-light);margin-top:6px;line-height:1.5;
  font-size:14.5px;letter-spacing:-0.005em;text-align:left}
.vcard .who .uni b{color:var(--accent2);font-weight:700}
.vcard .who .uni.pending{color:var(--muted);font-style:italic}
.rail-hint{font-size:var(--fs-cap);color:var(--muted);display:flex;align-items:center;gap:8px;margin-bottom:12px}
.rail-hint svg{color:var(--accent2)}
/* отзывы */
.stars{color:var(--accent2);letter-spacing:2px;font-size:14px}
.quote-card{background:var(--panel);border:1px solid var(--stroke);border-radius:var(--r);padding:22px;
  scroll-snap-align:start;display:flex;flex-direction:column;gap:12px;transition:border-color .25s,transform .25s}
.quote-card:hover{border-color:var(--stroke2);transform:translateY(-3px)}
.quote-card p{color:var(--text);font-size:14.5px;line-height:1.6}
.quote-card .who-row{margin-top:auto;padding-top:8px;border-top:1px solid var(--stroke);display:flex;flex-direction:column;gap:2px}
.quote-card .who-row span{color:var(--muted);font-size:13px}
.faq-list summary .stars{margin-right:2px}

/* крупная цитата-якорь — единственный сильный отзыв вместо очередной сетки карточек */
.pull-quote{position:relative;padding:6px 0 6px 26px;margin:26px 0 32px}
.pull-quote::before{content:"";position:absolute;left:0;top:2px;bottom:2px;width:3px;border-radius:3px;
  background:linear-gradient(var(--accent2),var(--accent))}
.pull-quote .stars{display:block;margin-bottom:10px}
.pull-quote p{font-family:var(--display);font-weight:700;font-size:clamp(var(--fs-1),2.4vw,var(--fs-3));
  line-height:1.4;color:var(--text);max-width:70ch}
.pull-quote .who{margin-top:16px;font-size:var(--fs-0);color:var(--muted)}
.pull-quote .who b{background:var(--grad-text);-webkit-background-clip:text;background-clip:text;color:transparent;font-weight:700}

.diag-cta{margin-top:28px;background:linear-gradient(140deg,rgba(0,98,235,.14),rgba(158,249,31,.06));
  border:1px solid var(--stroke2);border-radius:24px;padding:24px;display:flex;flex-direction:column;gap:16px}
.diag-cta p{margin:0}
/* едва заметное «дыхание» приглашающих блоков — не статус-бейдж (тот анти-паттерн
   остаётся под запретом), а маркетинговый призыв на публичных страницах */
body.is-public .diag-cta,body.is-public .final{animation:ctaBreathe 9s ease-in-out infinite}
@keyframes ctaBreathe{50%{box-shadow:0 0 36px rgba(0,98,235,.14)}}
@media (prefers-reduced-motion: reduce){body.is-public .diag-cta,body.is-public .final{animation:none}}
@media (min-width:768px){
  .diag-cta{flex-direction:row;align-items:center;justify-content:space-between;gap:28px}
  .diag-cta .btn{flex-shrink:0}
}

/* преподаватели / автор */
.author{display:grid;grid-template-columns:1fr;gap:28px;align-items:start;margin-top:28px}
.aphoto{border-radius:var(--r);border:1px solid var(--stroke2);min-height:440px;position:relative;overflow:hidden;
  background:radial-gradient(circle at 30% 20%,rgba(0,98,235,.4),transparent 55%),
             radial-gradient(circle at 80% 80%,rgba(158,249,31,.12),transparent 55%),var(--bg2);
  display:flex;align-items:flex-end;aspect-ratio:3/4;max-height:640px}
.aphoto img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;object-position:50% 0}
.aphoto .cap{position:relative;z-index:2;width:100%;padding:20px;font-weight:700;
  background:linear-gradient(transparent,rgba(5,8,22,.92))}
.aphoto .cap small{display:block;color:var(--muted);font-weight:500;font-size:var(--fs-cap)}
.author-text p{color:var(--text)}
.path{list-style:none;margin-top:24px;position:relative;padding-left:26px}
.path li:not(:last-child)::after{content:"";position:absolute;left:-19px;top:24px;bottom:-4px;width:2px;
  background:linear-gradient(var(--accent2),var(--accent));transform:scaleY(1);transform-origin:top;transition:transform .5s ease .15s}
.path li{position:relative;padding:0 0 20px 14px}
.path li:last-child{padding-bottom:0}
.path li::before{content:"";position:absolute;left:-26px;top:6px;width:16px;height:16px;border-radius:50%;
  background:var(--bg);border:3px solid var(--accent);box-shadow:0 0 12px rgba(0,98,235,.7);
  transform:scale(1);transition:transform .35s ease}
.path li:nth-child(2)::before{transition-delay:.15s}
.path li:nth-child(3)::before{transition-delay:.3s}
.path b{display:block;font-size:var(--fs-0)}
.path span{font-size:var(--fs-cap);color:var(--muted)}
/* точки и линия таймлайна «прорисовываются» вслед за появлением карточки основателя */
body.is-public .author.reveal:not(.vis) .path li::before{transform:scale(0)}
body.is-public .author.reveal:not(.vis) .path li:not(:last-child)::after{transform:scaleY(0)}
@media (prefers-reduced-motion: reduce){
  .path li::before,.path li:not(:last-child)::after{transition:none}
}

/* карточка преподавателя школы (новый компонент) */
.teachers-grid{display:grid;grid-template-columns:1fr;gap:20px;margin-top:28px}
@media (min-width:768px){.teachers-grid{grid-template-columns:repeat(2,1fr)}}
.tcard{background:var(--panel);border:1px solid var(--stroke);border-radius:var(--r);overflow:hidden;
  transition:border-color .25s,transform .25s}
.tcard:hover{border-color:var(--stroke2);
  transform:translateY(-3px) perspective(700px) rotateX(var(--tiltx,0deg)) rotateY(var(--tilty,0deg))}
.tphoto{aspect-ratio:3/4;position:relative;overflow:hidden;
  background:radial-gradient(circle at 30% 20%,rgba(0,98,235,.4),transparent 55%),
             radial-gradient(circle at 80% 80%,rgba(158,249,31,.12),transparent 55%),var(--bg2)}
.tphoto img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;object-position:50% 0}
.tphoto .ph-initials{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;
  font-family:var(--display);font-weight:800;font-size:var(--fs-6);color:var(--accent2);
  text-shadow:0 0 32px rgba(158,249,31,.5)}
.tbody{padding:20px}
.tbody .subjects{color:var(--blue-light);font-size:var(--fs-cap);font-weight:700;letter-spacing:.04em;text-transform:uppercase;margin-bottom:8px}
.tbody .facts{color:var(--muted);font-size:var(--fs-0);margin-top:10px}
.tbody .approach{color:var(--text);font-size:var(--fs-0);margin-top:12px}

/* цена */
.price-grid{display:grid;grid-template-columns:1fr;gap:16px;margin:28px 0}
.price-grid>.price:only-child{grid-column:1/-1}
.price{background:var(--bg2);border:1px solid var(--stroke);border-radius:20px;padding:26px;transition:border-color .25s,transform .25s}
.price:hover{border-color:var(--stroke2);transform:translateY(-2px)}
.price.hot{position:relative;overflow:hidden;border:1.5px solid rgba(0,98,235,.6);border-radius:var(--r);padding:28px;
  background:linear-gradient(160deg,rgba(0,98,235,.16),rgba(158,249,31,.06));
  box-shadow:0 0 40px rgba(0,98,235,.18);transition:border-color .25s,transform .25s,box-shadow .25s}
.price.hot:hover{border-color:rgba(0,98,235,.85);transform:translateY(-3px);box-shadow:0 0 56px rgba(0,98,235,.3)}
.price.hot::after{content:"";position:absolute;top:0;left:-60%;width:35%;height:100%;
  background:linear-gradient(100deg,transparent,rgba(255,255,255,.16),transparent);
  transform:skewX(-20deg);transition:left .7s ease;pointer-events:none}
.price.hot:hover::after{left:130%}
@media (prefers-reduced-motion: reduce){.price.hot::after{display:none}}
.price .cost{font-family:var(--display);font-size:var(--fs-4);font-weight:800;margin-bottom:6px}
.price .cost small{font-family:var(--font);font-size:var(--fs-0);font-weight:500;color:var(--muted)}
.price p{color:var(--muted);font-size:var(--fs-0)}
.win{display:inline-block;background:rgba(158,249,31,.16);color:var(--accent2);font-weight:700;font-size:var(--fs-cap);padding:2px 10px;border-radius:999px;margin-left:2px;white-space:nowrap}
.discount{display:flex;align-items:center;gap:12px;background:rgba(158,249,31,.08);border:1px solid rgba(158,249,31,.3);border-radius:16px;
  padding:16px 20px;font-weight:600;margin-bottom:28px}
.discount svg{flex-shrink:0;color:var(--accent2)}
.diag-note p{color:var(--muted)}
.diag-note p b{color:var(--text)}
.price-cta{margin-top:28px}

/* faq */
.faq-list{margin-top:24px;display:flex;flex-direction:column;gap:12px}
details{background:var(--panel);border:1px solid var(--stroke);border-radius:16px;transition:border-color .25s}
details[open]{border-color:var(--stroke2)}
summary{list-style:none;cursor:pointer;padding:18px 22px;font-weight:600;font-size:var(--fs-0);
  display:flex;justify-content:space-between;align-items:center;gap:16px;min-height:48px}
summary::-webkit-details-marker{display:none}
summary::after{content:"+";font-family:var(--display);font-size:var(--fs-1);color:var(--accent2);transition:transform .25s;flex-shrink:0}
details[open] summary::after{transform:rotate(45deg)}
summary:focus-visible{outline:3px solid var(--accent2);outline-offset:-3px;border-radius:16px}
details .a{padding:0 22px 20px;color:var(--muted)}

/* Группы отзывов на /reviews: свёрнуто — заголовок + стопка превью, по клику
   разворачивается в полный список. Тот же <details>, что и у FAQ (borders,
   +/× уже стилизованы глобально выше) — просто другое содержимое summary. */
.rev-group{margin-top:20px}
.rev-group summary{padding:20px 22px}
.rev-summary-inner{display:flex;align-items:center;justify-content:space-between;gap:16px;flex:1;flex-wrap:wrap}
.rev-summary-text h2{margin:4px 0 0;font-size:var(--fs-1);font-family:var(--font);text-transform:none;letter-spacing:normal}
.rev-preview-strip{display:flex;align-items:center;flex-shrink:0}
.rev-preview-strip img{width:72px;height:72px;object-fit:cover;border-radius:12px;border:2px solid var(--bg2);margin-left:-20px;background:var(--panel)}
.rev-preview-strip img:first-child{margin-left:0}
.rev-more{width:72px;height:72px;border-radius:12px;background:var(--bg2);border:2px solid var(--stroke2);
  display:flex;align-items:center;justify-content:center;font-size:18px;font-weight:700;color:var(--muted);margin-left:-20px}
.rev-group .a{padding:0 22px 22px}
@media (max-width:767px){
  .rev-preview-strip img,.rev-more{width:52px;height:52px;border-radius:10px;margin-left:-14px}
  .rev-more{font-size:15px}
}
@media (max-width:479px){
  .rev-preview-strip img,.rev-more{width:44px;height:44px}
}

/* скриншоты переписки в отзывах */
.shot-grid{display:grid;grid-template-columns:1fr;gap:14px;margin-top:24px}
@media (min-width:640px){.shot-grid{grid-template-columns:repeat(2,1fr)}}
.shot{display:block;border-radius:var(--r);overflow:hidden;border:1px solid var(--stroke);transition:border-color .25s,transform .25s}
.shot:hover{border-color:var(--stroke2);transform:translateY(-3px)}
.shot img{display:block;width:100%;height:auto}

/* финал */
.final{background:linear-gradient(140deg,rgba(0,98,235,.18),rgba(158,249,31,.08));
  border:1px solid var(--stroke2);border-radius:var(--r);padding:36px 24px;position:relative;overflow:hidden}
.final::before{content:"";position:absolute;top:-60px;right:-60px;width:220px;height:220px;border-radius:50%;
  background:radial-gradient(circle,rgba(158,249,31,.14),transparent 70%)}
/* Заголовок призыва: у .final и .diag-cta есть декоративный ::before (радиальное
   свечение) — он позиционированный, а заголовок нет, поэтому по правилам порядка
   отрисовки свечение ложится ПОВЕРХ текста и приглушает его до серого. Поднимаем
   контентную часть на свой слой и фиксируем цвет явно. */
.final > *,.diag-cta > *{position:relative;z-index:1}
.final h2,.diag-cta h2{margin-bottom:18px;color:var(--text);-webkit-text-fill-color:var(--text)}
.final p{color:var(--text)}
.final .btn{margin-top:26px}

/* ── шапка сайта: на лендинге такого компонента не было ── */
.site-nav{position:sticky;top:0;z-index:20;background:rgba(5,8,22,.86);backdrop-filter:blur(10px);
  border-bottom:1px solid var(--stroke)}
.site-nav .wrap{display:flex;align-items:center;justify-content:space-between;gap:var(--sp-4);padding:var(--sp-3) max(20px,env(safe-area-inset-left))}
.site-nav .brand{display:flex;align-items:center;gap:10px;font-family:var(--display);font-weight:800;
  font-size:var(--fs-1);text-decoration:none;color:var(--text);flex-shrink:0}
.site-nav .brand svg{width:32px;height:32px;flex-shrink:0}
.site-nav .brand img{height:32px;width:auto;flex-shrink:0}
/* Промежуток между пунктами сжимается на узком десктопе: после появления
   девятого пункта («План») строка меню при 1024–1279px переставала помещаться
   и последний пункт уезжал под кнопки входа. Шрифт не трогаем — уменьшать его
   ради вместимости значит ухудшать читаемость всем; сжимаем только воздух. */
.site-nav .links{display:none;gap:var(--sp-5);list-style:none}
@media (min-width:1024px) and (max-width:1279px){
  .site-nav .links{gap:15px}
  .site-nav .wrap{gap:var(--sp-3)}
}
.site-nav .links a{text-decoration:none;color:var(--muted);font-weight:600;font-size:var(--fs-0);white-space:nowrap;transition:color .2s}
.site-nav .links a:hover,.site-nav .links a.active{color:var(--text)}
.site-nav .actions{display:flex;align-items:center;gap:var(--sp-3)}
.site-nav .btn-nav{display:inline-flex;align-items:center;gap:6px;padding:8px 18px;border-radius:12px;
  font-weight:700;font-size:14px;text-decoration:none;white-space:nowrap;min-height:40px}
.site-nav .btn-nav.primary{background:var(--grad);color:#fff;border:1px solid rgba(140,200,255,.6)}
.site-nav .btn-nav.ghost{color:var(--text);border:1px solid var(--stroke)}
.site-nav .btn-nav.ghost.login{color:var(--accent2);border-color:rgba(158,249,31,.35)}
.nav-burger{display:flex;flex-direction:column;gap:5px;width:44px;height:44px;align-items:center;justify-content:center;background:none;
  border:1px solid var(--stroke);border-radius:12px;color:var(--text);cursor:pointer;transition:border-color .2s}
.nav-burger:hover{border-color:var(--stroke2)}
.burger-line{width:18px;height:2px;border-radius:2px;background:currentColor;transition:transform .3s ease,opacity .2s ease}
.nav-burger.open .burger-line:nth-child(1){transform:translateY(7px) rotate(45deg)}
.nav-burger.open .burger-line:nth-child(2){opacity:0}
.nav-burger.open .burger-line:nth-child(3){transform:translateY(-7px) rotate(-45deg)}
@media (prefers-reduced-motion: reduce){.burger-line{transition:none}}
/* .nav-mobile: display/раскрытие теперь определяются ниже, в блоке «Живость» (grid-раскрытие) */
/* На узких экранах кнопки входа/регистрации рядом с гамбургером физически не помещались —
   давали горизонтальный скролл всей страницы (найдено реальным браузерным тестом 2026-08-17,
   баг существовал ещё до анимаций, просто раньше проверялось только через curl). Те же ссылки
   уже продублированы в выпадающем мобильном меню — на мобильном оставляем только гамбургер. */
@media (max-width:1023px){
  .site-nav .actions .btn-nav{display:none}
}
@media (min-width:1024px){
  .site-nav .links{display:flex}
  .nav-burger{display:none}
  .nav-mobile{display:none!important}
}

/* ── подвал сайта: карта сайта + юр. блок (на лендинге только юр. блок) ── */
footer{border-top:1px solid var(--stroke);padding:var(--sp-6) 0 max(28px,env(safe-area-inset-bottom));
  font-size:var(--fs-cap);color:var(--muted)}
.footer-grid{display:grid;grid-template-columns:1fr;gap:var(--sp-5)}
@media (min-width:768px){.footer-grid{grid-template-columns:2fr 1fr 1fr}}
.footer-grid h4{font-family:var(--font);font-size:var(--fs-0);color:var(--text);margin-bottom:var(--sp-3);text-transform:none}
.footer-grid ul{list-style:none;display:flex;flex-direction:column;gap:var(--sp-2)}
.footer-grid a{text-decoration:none;color:var(--muted);transition:color .2s}
.footer-grid a:hover{color:var(--text)}
.footer-legal{margin-top:var(--sp-6);padding-top:var(--sp-5);border-top:1px solid var(--stroke)}
/* Список документов в подвале (2026-09-17): их стало пять, и раньше
   разделители «·» стояли текстом — на телефоне ссылки становятся блочными
   (min-height:44px для тапа), и точки повисали в начале строк. Разделяем
   промежутком, а не символом: работает на любой ширине. */
.footer-docs{display:flex;flex-wrap:wrap;gap:4px 18px;margin-top:8px}

/* ── формы: на лендинге форм нет (весь лид-захват в Telegram) ── */
.field{margin-bottom:var(--sp-4)}
.field label{display:block;font-size:var(--fs-cap);font-weight:600;color:var(--muted);margin-bottom:6px}
.field input,.field select,.field textarea{width:100%;min-height:48px;padding:12px 14px;border-radius:12px;
  background:var(--bg2);border:1px solid var(--stroke);color:var(--text);font-family:var(--font);font-size:var(--fs-0)}
.field input:focus,.field select:focus,.field textarea:focus{outline:none;border-color:var(--stroke2);box-shadow:0 0 0 3px rgba(0,98,235,.25)}
.field.field--error input,.field.field--error select,.field.field--error textarea{border-color:var(--err-stroke);background:var(--err-bg)}
.field .hint{font-size:12px;color:var(--muted);margin-top:6px}
.field .error{font-size:12px;color:var(--err);margin-top:6px}
.field input:disabled,.field select:disabled,.field textarea:disabled{opacity:.5;cursor:not-allowed}
.field-check{display:flex;align-items:flex-start;gap:10px;margin-bottom:var(--sp-4)}
.field-check input{width:20px;height:20px;flex-shrink:0;margin-top:2px;accent-color:var(--accent2)}
.field-check label{font-size:var(--fs-cap);color:var(--muted)}
.field-check a{color:var(--blue-light)}

.alert{border-radius:14px;padding:var(--sp-4);font-size:var(--fs-0);margin-bottom:var(--sp-4);border:1px solid}
.alert--info{background:rgba(0,98,235,.08);border-color:rgba(0,98,235,.35);color:var(--text)}
.alert--ok{background:rgba(158,249,31,.08);border-color:rgba(158,249,31,.35);color:var(--text)}
.alert--err{background:var(--err-bg);border-color:var(--err-stroke);color:var(--text)}

/* ── карточка счёта / статус оплаты (ЛК, компонента на лендинге нет) ── */
.order-card{background:var(--panel);border:1px solid var(--stroke);border-radius:20px;padding:var(--sp-5);
  display:flex;flex-direction:column;gap:var(--sp-3)}
.order-card .amount{font-family:var(--display);font-size:var(--fs-3);font-weight:800}
/* «Горящая» доплата (2026-08-24) — статичное выделение, без пульсации: в
   кабинете уже принято правило не мигать тем, что видно при каждом заходе. */
.order-card.is-hot,.card.is-hot{border-color:var(--accent2);box-shadow:0 0 0 1px var(--accent2),0 0 28px rgba(158,249,31,.16)}
.order-status{display:inline-flex;align-items:center;gap:6px;font-size:12px;font-weight:700;
  padding:4px 12px;border-radius:999px;text-transform:uppercase;letter-spacing:.04em;width:fit-content}
.order-status--new,.order-status--pending{background:rgba(59,156,255,.12);color:var(--blue-light)}
.order-status--paid{background:rgba(158,249,31,.14);color:var(--accent2)}
.order-status--cancelled,.order-status--expired,.order-status--noshow{background:rgba(255,107,107,.1);color:var(--err)}
/* Просрочен ≠ отменён (2026-08-29, найдено при аудите оплат) — счёт всё ещё
   можно оплатить, в отличие от отменённого/истёкшего, но раньше выглядел
   визуально идентично («тот же красный»), из-за чего читался как «мёртвый»
   рядом с рабочей кнопкой «Оплатить». Янтарный — третий, отдельный от
   красного (мёртво) и синего (ждём) цвет статуса. */
.order-status--overdue{background:rgba(255,166,43,.14);color:#FFA62B}

.empty-state{text-align:center;padding:var(--sp-7) var(--sp-4);color:var(--muted)}

/* ══════════════════════════════════════════════════════════════
   «Живость»: точечные микро-анимации сверх лендинга (2026-08-17)
   Правило: decorative-tier (каскады, входная сцена hero, счётчики) —
   только на публичных страницах. В /lk/* — только quality-tier
   (плавный фокус, плавные алерты, hover) — без каскадов и счётчиков,
   это экран, которым пользуются каждый день, а не витрина.
   ══════════════════════════════════════════════════════════════ */

/* ── каскадное появление групп: применяется к прямым детям контейнера
   с классом .stagger (обычно они же и .reveal — задержка складывается
   с их собственной transition) ── */
.stagger>*:nth-child(1){transition-delay:0ms}
.stagger>*:nth-child(2){transition-delay:30ms}
.stagger>*:nth-child(3){transition-delay:60ms}
.stagger>*:nth-child(4){transition-delay:90ms}
.stagger>*:nth-child(5){transition-delay:120ms}
.stagger>*:nth-child(6){transition-delay:150ms}
.stagger>*:nth-child(n+7){transition-delay:170ms}

/* ── входная сцена для hero (бейдж → заголовок → подзаголовок → кнопка) ── */
.reveal-d1{transition-delay:.05s}
.reveal-d2{transition-delay:.15s}
.reveal-d3{transition-delay:.25s}
.reveal-d4{transition-delay:.35s}

/* ── курсор-подсветка на карточках: только при реальном hover-курсоре,
   не на тачскринах (там наведения нет и слушатель mousemove не вешаем) ── */
@media (hover:hover){
  .card,.tcard,.price{position:relative}
  .card::after,.tcard::after,.price::after{content:"";position:absolute;inset:0;border-radius:inherit;z-index:-1;
    background:radial-gradient(220px circle at var(--mx,50%) var(--my,50%),rgba(59,156,255,.16),transparent 70%);
    opacity:0;transition:opacity .35s}
  .card:hover::after,.tcard:hover::after,.price:hover::after{opacity:1}
}

/* ── видео-постеры в результатах: лёгкий зум при наведении ── */
.vcard video{transition:transform .35s}
.vcard:hover video{transform:scale(1.03)}

/* ── плавный фокус полей форм (было мгновенно) ── */
.field input,.field select,.field textarea{transition:border-color .2s,box-shadow .2s}

/* ── алерты/баннеры: разовое появление, не зацикленное ── */
@keyframes alertIn{from{opacity:0;transform:translateY(-8px)}to{opacity:1;transform:none}}
.alert{animation:alertIn .4s ease-out}

/* ── шапка сайта: лёгкое уплотнение при скролле (класс ставит JS) ── */
.site-nav{transition:background-color .25s}
.site-nav.is-scrolled{background:rgba(5,8,22,.96)}
.site-nav.is-scrolled .wrap{padding-top:8px;padding-bottom:8px;transition:padding .25s}

/* ── мобильное меню: плавное раскрытие вместо мгновенного display-тоггла ── */
.nav-mobile{display:grid;grid-template-rows:0fr;transition:grid-template-rows .3s ease}
.nav-mobile.open{grid-template-rows:1fr}
.nav-mobile-inner{min-height:0;overflow:hidden;display:flex;flex-direction:column;gap:2px;border-top:1px solid var(--stroke);background:var(--bg)}
.nav-mobile-inner a{padding:14px 20px;text-decoration:none;color:var(--text);font-weight:600;min-height:48px;display:flex;align-items:center}
.nav-mobile-inner a.login{color:var(--accent2)}

@media (prefers-reduced-motion: reduce){
  .stagger>*{transition-delay:0ms!important}
  .reveal-d1,.reveal-d2,.reveal-d3,.reveal-d4{transition-delay:0ms!important}
  .card::after,.tcard::after,.price::after{display:none!important}
  .vcard video{transition:none!important}
  .vcard:hover video{transform:none!important}
  .alert{animation:none!important}
  .site-nav{transition:none!important}
  .site-nav.is-scrolled .wrap{transition:none!important}
  .nav-mobile{transition:none!important}
}

/* ═══ мобилка: направляющие элементы по центру ═══ */
@media (max-width:767px){
  .btn{font-size:clamp(13px,4.2vw,19px);padding:13px 10px;gap:6px}
  .btn svg{width:20px;height:20px}
  .btn .bt{white-space:nowrap;letter-spacing:-0.015em}
  .diag-cta{padding:20px 14px}
  .final{padding:28px 16px}
  .diag-cta .btn,.final .btn{font-size:clamp(13px,4vw,18px)}

  /* ── Выравнивание на телефоне ──
     Заголовки, подписи-кикеры и блоки-призывы остаются по центру: они короткие,
     и центровка читается как оформление. А ВОТ ТЕКСТ, КОТОРЫЙ ЧИТАЮТ, выровнен
     по левому краю. Раньше по центру стояло всё подряд, включая абзацы на
     4–6 строк: у такого блока каждая строка начинается в новом месте, глазу
     не за что зацепиться при переходе на следующую — именно от этого мобильная
     версия и «плохо воспринималась». */
  .hero-grid>div:first-child{text-align:center}
  .hero .sub{text-align:left}
  .cta-note{text-align:left}
  .kicker{display:flex;justify-content:center}
  h2{text-align:center}
  .res-intro{text-align:left}
  .rail-hint{justify-content:center}
  .year-label{justify-content:center}
  .year-label::after{display:none}
  .diag-cta{text-align:center;align-items:center}
  .diag-cta p{text-align:left}
  .discount{justify-content:center;text-align:left}
  .diag-note p{text-align:left}
  .final{text-align:center}
  .final p{text-align:left}
  footer .wrap{text-align:left}
  .footer-grid{text-align:left}
  .footer-legal{text-align:left}

  /* Секции внутри страницы: абзацы под заголовками тоже читаются, а не
     разглядываются — левый край даёт им общую линию старта. */
  section p:not(.res-intro):not(.cta-note){text-align:left}

  /* ── Первый экран короче ──
     Атом с орбитами занимал 300px высоты и вместе с текстом уводил статистику
     за полтора экрана. На телефоне он декоративный: ужимаем, чтобы до первых
     цифр доходили быстрее. */
  .atom-box{min-height:210px}
  .atom{width:190px;height:190px}

  /* ── Мобильная типографика: КОМПАКТНЕЕ, но не мельче нормы ──
     Замер до правки: вводные абзацы шли 19px/30.4 и занимали по 5–8 строк на
     секцию, крупная цитата — 19px жирным Unbounded (280 знаков растягивались
     на два экрана), юридический текст в подвале — тоже 19px. На телефоне
     дисплейный шрифт хорош в заголовках, а в длинном тексте утомляет:
     широкие жирные буквы дают 3–4 слова на строку. Основной размер читаемого
     текста на мобильном — 16px: это норма читаемости, а не «мелко». */
  .hero .sub{font-size:16px;line-height:1.55;margin:16px 0 24px}
  .res-intro{font-size:16px;line-height:1.55}
  section p{font-size:16px;line-height:1.55}
  .narrative{font-size:16px;line-height:1.6}
  .pain-box p{font-size:16px;line-height:1.6}

  /* Крупная цитата: на телефоне — обычный текст с акцентной полосой слева.
     Жирный Unbounded на 280 знаках — это стена, а не цитата. */
  .pull-quote p{font-family:var(--font);font-weight:500;font-size:16.5px;line-height:1.6;text-align:left}
  .pull-quote .who{font-size:14px}

  /* Карточки направлений: иконка и отступы ужаты — карточка со всем контентом
     перестаёт занимать две трети экрана. */
  .card{padding:18px}
  .card .ic{width:38px;height:38px;margin-bottom:10px}
  .card .ic svg{width:19px;height:19px}
  .cards{margin-top:20px;gap:12px}

  /* Юридический блок подвала — служебный текст, а не лид-абзац. */
  .footer-legal p{font-size:13.5px;line-height:1.6}
  .footer-legal{margin-top:20px;padding-top:16px}
  .footer-grid h4{font-size:15px;margin-bottom:4px}
  .footer-grid a{font-size:14px}
  footer .wrap{padding-top:28px;padding-bottom:20px}

  /* Аккордеоны отзывов и вопросов — компактнее по вертикали. */
  .faq-list summary{padding:16px 44px 16px 18px;font-size:15px}
  .faq-list .a{padding:0 18px 16px;font-size:15px}

  /* Панель занятий в кабинетах. */
  .lesson{padding:14px}
  .lesson-time{font-size:var(--fs-1)}

  /* Чипы у атома упирались в самый край экрана (389px из 390) — текст читался
     как обрезанный. Отодвигаем от краёв и чуть уменьшаем. */
  /* Три чипа раскладываем по разным сторонам и по высоте: после того как атом
     ужался, прежние проценты ставили два чипа друг на друга. Проверяется
     попарным пересечением прямоугольников, а не на глаз. */
  .float-chip{padding:8px 11px;font-size:13px;max-width:min(46vw,150px)}
  .float-chip small{font-size:12px}
  /* min-width:0 — элемент сетки по умолчанию не сжимается ниже своего
     содержимого (min-width:auto), из-за чего .atom-box вылезал за колонку на
     24px, и чип, привязанный к его правому краю, уходил за экран на узких
     телефонах. */
  .atom-box{min-height:250px;min-width:0}
  .hero-grid>*{min-width:0}
  .fc1{top:0;left:2px;bottom:auto;right:auto}
  .fc3{top:42%;right:2px;left:auto;bottom:auto}
  .fc2{top:auto;bottom:0;left:2px;right:auto}
}

/* ═══ desktop ═══ */
@media (min-width:768px){
  section{padding:45px 0}
  .hero{padding:45px 0 29px}
  .hero-grid{grid-template-columns:1.15fr .85fr}
  .btn{width:auto;display:inline-flex}
  .stats-row{grid-template-columns:repeat(4,1fr)}
  .atom{width:320px;height:320px}
  .atom-box{min-height:380px}
  .cards{grid-template-columns:repeat(2,1fr)}
  .author{grid-template-columns:1fr 1fr;gap:40px}
  .price-grid{grid-template-columns:1fr 1fr}
  .pain-box{padding:40px 44px}
  .final{padding:56px}
}
@media (min-width:1024px){
  .cards{grid-template-columns:repeat(3,1fr)}
}

/* ══════════════════════════════════════════════════════════════════════
   Итерация 7 (2026-08-20): реальные материалы школы — видео урока,
   ролик со встречи, скриншоты отчётов преподавателей и платформы.
   Принципы, выстраданные прошлыми итерациями и соблюдённые здесь:
   · по оси X при вертикальном скролле НИЧЕГО не двигаем (заказчика
     от этого укачивало) — вся входная анимация только scale/opacity/Y;
   · анимируются только transform/opacity;
   · prefers-reduced-motion отключает и появление, и автозапуск видео
     (автозапуск гасится в gallery.js, здесь — только переходы);
   · никаких 100vw и абсолютных элементов, вылезающих за .wrap.
   ══════════════════════════════════════════════════════════════════════ */

/* ── Сцена с устройствами: ноутбук (запись урока) + телефон (ролик со встречи) ── */
.device-stage{position:relative;max-width:860px;margin:36px auto 0}
.device-stage .laptop{max-width:660px}

.laptop-lid{position:relative;background:linear-gradient(160deg,#1b2338,#0b1122);border:1px solid var(--stroke);
  border-radius:16px 16px 8px 8px;padding:12px 12px 16px;
  box-shadow:0 30px 70px rgba(0,0,0,.55),0 0 0 1px rgba(255,255,255,.03) inset}
.laptop-cam{position:absolute;top:5px;left:50%;transform:translateX(-50%);
  width:5px;height:5px;border-radius:50%;background:rgba(255,255,255,.22)}
.laptop-screen{position:relative;aspect-ratio:3/2;border-radius:6px;overflow:hidden;background:#000;
  box-shadow:0 0 0 1px rgba(255,255,255,.06) inset}
.laptop-screen video,.laptop-screen img{display:block;width:100%;height:100%;object-fit:cover}
.laptop-base{height:13px;margin:0 -5%;border-radius:0 0 14px 14px;position:relative;
  background:linear-gradient(180deg,#222b45,#10162a);border:1px solid var(--stroke);border-top:none}
.laptop-base::after{content:"";position:absolute;top:0;left:50%;transform:translateX(-50%);
  width:88px;height:5px;border-radius:0 0 6px 6px;background:rgba(0,0,0,.45)}

.phone-frame{position:relative;background:linear-gradient(160deg,#232c48,#0c1223);
  border:1px solid var(--stroke);border-radius:30px;padding:7px;
  box-shadow:0 24px 55px rgba(0,0,0,.55)}
.phone-screen{position:relative;aspect-ratio:9/16;border-radius:24px;overflow:hidden;background:#000}
.phone-screen video,.phone-screen img{display:block;width:100%;height:100%;object-fit:cover}
.phone-island{position:absolute;z-index:2;top:15px;left:50%;transform:translateX(-50%);
  width:48px;height:13px;border-radius:8px;background:#05070f}

@media (min-width:900px){
  .device-stage .phone{position:absolute;right:0;bottom:0;width:190px;z-index:2}
}
@media (max-width:899px){
  .device-stage{max-width:560px}
  .device-stage .laptop{max-width:none}
  .device-stage .phone{width:190px;margin:26px auto 0}
}

/* Появление сцены: только масштаб и прозрачность. Стартовое состояние задаётся
   классом .dev-in, который снимается скриптом — если JS не выполнился, макеты
   видны сразу и в полном виде (никакого «пустого места» без скрипта). */
.dev-in .laptop,.dev-in .phone{opacity:0;transform:scale(.94)}
.dev-anim .laptop,.dev-anim .phone{opacity:1;transform:none;
  transition:opacity .6s ease,transform .7s cubic-bezier(.22,.7,.28,1)}
.dev-anim .phone{transition-delay:.14s}
@media (prefers-reduced-motion: reduce){
  .dev-in .laptop,.dev-in .phone{opacity:1;transform:none}
  .dev-anim .laptop,.dev-anim .phone{transition:none}
}

/* Подписи под сценой */
.inside-notes{display:grid;gap:14px;margin-top:34px;grid-template-columns:1fr}
@media (min-width:760px){.inside-notes{grid-template-columns:repeat(3,1fr)}}
.inside-note{background:var(--panel);border:1px solid var(--stroke);border-radius:20px;padding:18px 20px}
.inside-note b{display:block;font-family:var(--display);font-size:var(--fs-1);margin-bottom:6px}
.inside-note span{color:var(--muted);font-size:15px;line-height:1.55}

/* ── Карточки-скриншоты: отчёты преподавателей, платформа, чат ──
   Скриншот показывается «шапкой вперёд» (object-position:top) в карточке
   фиксированной высоты, полностью открывается по клику в лайтбоксе. */
.proof-grid{display:grid;gap:16px;margin-top:26px;grid-template-columns:1fr}
@media (min-width:720px){.proof-grid{grid-template-columns:repeat(2,1fr)}}
@media (min-width:1000px){.proof-grid--3{grid-template-columns:repeat(3,1fr)}}
.proof{display:flex;flex-direction:column;align-items:stretch;text-align:left;width:100%;cursor:zoom-in;
  background:var(--panel);border:1px solid var(--stroke);border-radius:24px;overflow:hidden;
  color:inherit;font:inherit;padding:0;
  transition:border-color .25s,transform .25s,box-shadow .25s}
.proof:hover{border-color:var(--stroke2);transform:translateY(-3px);box-shadow:0 18px 40px rgba(0,0,0,.35)}
.proof:focus-visible{outline:2px solid var(--accent2);outline-offset:3px}
.proof-shot{display:block;position:relative;height:230px;overflow:hidden;background:#0b1122}
.proof-shot img{display:block;width:100%;height:auto;min-height:100%}
/* При наведении скриншот медленно «прокручивается» до конца сообщения — сразу
   видно, что под обрезкой есть продолжение, и незачем гадать, что там дальше.
   translateY в процентах считается от собственной высоты элемента, поэтому одно
   правило работает для скриншотов любой высоты: calc(230px - 100%) ставит низ
   картинки ровно на низ окна карточки. Только мышь, только десктоп. */
@media (hover:hover){
  .proof-shot img{transition:transform 5.5s linear}
  .proof:hover .proof-shot img,.proof:focus-visible .proof-shot img{transform:translateY(calc(230px - 100%))}
}
@media (prefers-reduced-motion: reduce){
  .proof-shot img{transition:none}
  .proof:hover .proof-shot img,.proof:focus-visible .proof-shot img{transform:none}
}
.proof-shot::after{content:"";position:absolute;inset:auto 0 0 0;height:80px;
  background:linear-gradient(180deg,transparent,rgba(5,8,22,.92))}
.proof-zoom{position:absolute;z-index:2;right:12px;top:12px;width:32px;height:32px;border-radius:50%;
  display:grid;place-items:center;background:rgba(5,8,22,.72);border:1px solid var(--stroke);
  color:var(--blue-light);opacity:0;transition:opacity .25s}
.proof:hover .proof-zoom,.proof:focus-visible .proof-zoom{opacity:1}
.proof-cap{display:block;padding:16px 20px 20px}
.proof-cap b{display:block;font-weight:700;margin-bottom:4px}
.proof-cap span{color:var(--muted);font-size:14px;line-height:1.55}
@media (prefers-reduced-motion: reduce){.proof,.proof-zoom{transition:none}}

/* ── Лайтбокс для скриншотов ── */
.lightbox{position:fixed;inset:0;z-index:60;display:none;place-items:center;padding:24px;
  background:rgba(3,5,14,.9);backdrop-filter:blur(6px)}
.lightbox.open{display:grid}
.lightbox img{max-width:min(100%,1100px);max-height:calc(100vh - 120px);width:auto;height:auto;
  border-radius:16px;border:1px solid var(--stroke);box-shadow:0 30px 80px rgba(0,0,0,.6)}
.lightbox-cap{position:absolute;left:24px;right:24px;bottom:22px;text-align:center;
  color:var(--muted);font-size:14px}
.lightbox-close{position:absolute;top:16px;right:16px;width:44px;height:44px;border-radius:50%;
  display:grid;place-items:center;cursor:pointer;color:var(--text);
  background:rgba(255,255,255,.07);border:1px solid var(--stroke)}
.lightbox-close:hover{background:rgba(255,255,255,.14)}
.lightbox-nav{position:absolute;top:50%;transform:translateY(-50%);width:46px;height:46px;border-radius:50%;
  display:grid;place-items:center;cursor:pointer;color:var(--text);
  background:rgba(255,255,255,.07);border:1px solid var(--stroke)}
.lightbox-nav:hover{background:rgba(255,255,255,.14)}
.lightbox-prev{left:14px}
.lightbox-next{right:14px}
@media (max-width:600px){.lightbox-nav{top:auto;bottom:16px;transform:none}.lightbox-cap{bottom:74px}}

/* ══════════════════════════════════════════════════════════════════════
   Итерация 8 (2026-08-20): контентные сцены вместо орнамента.
   Карусель отзывов, лента вузов, таймлайн года, пошаговый разбор задачи,
   переключение экранов внутри макета, магнитные кнопки.

   Правило оси соблюдено везде: горизонтально движутся только элементы
   ВНУТРИ своих контейнеров (лента, карусель) и не в ответ на вертикальный
   скролл — страница при этом стоит. Всё, что привязано к прокрутке,
   двигается только по Y или меняет прозрачность.
   ══════════════════════════════════════════════════════════════════════ */

/* ── Лента вузов: бесконечная бегущая строка, пауза при наведении ── */
.marquee{position:relative;overflow:hidden;margin-top:26px;padding:6px 0;
  -webkit-mask-image:linear-gradient(90deg,transparent,#000 7%,#000 93%,transparent);
  mask-image:linear-gradient(90deg,transparent,#000 7%,#000 93%,transparent)}
.marquee-track{display:flex;width:max-content;animation:marqueeRun 42s linear infinite}
.marquee:hover .marquee-track,.marquee:focus-within .marquee-track{animation-play-state:paused}
.marquee-item{display:flex;align-items:center;gap:12px;padding:0 24px;white-space:nowrap;
  font-family:var(--display);font-weight:700;font-size:var(--fs-1);color:var(--text)}
.marquee-item small{font-family:var(--font);font-weight:500;font-size:var(--fs-cap);color:var(--muted)}
.marquee-item::before{content:"";width:6px;height:6px;border-radius:50%;background:var(--accent2);
  box-shadow:0 0 8px rgba(158,249,31,.7);flex-shrink:0}
@keyframes marqueeRun{to{transform:translateX(-50%)}}
@media (prefers-reduced-motion: reduce){
  .marquee{-webkit-mask-image:none;mask-image:none}
  .marquee-track{animation:none;width:auto;flex-wrap:wrap;justify-content:center;gap:6px 0}
  .marquee-track > .marquee-half:last-child{display:none}
}
.marquee-half{display:flex}

/* ── Карусель: снап по карточкам, автолистание с паузой при наведении ── */
.carousel{position:relative;margin-top:26px}
.carousel-viewport{display:flex;gap:16px;overflow-x:auto;scroll-snap-type:x mandatory;
  overscroll-behavior-x:contain;scrollbar-width:none;padding:6px 2px 10px}
.carousel-viewport::-webkit-scrollbar{display:none}
.carousel-slide{flex:0 0 auto;width:min(460px,84%);scroll-snap-align:center}
@media (min-width:1000px){.carousel-slide{width:352px}}
.carousel-slide > *{height:100%}
.carousel-bar{height:2px;border-radius:2px;background:var(--stroke);overflow:hidden;margin-top:6px}
.carousel-bar i{display:block;height:100%;width:0;border-radius:2px;
  background:linear-gradient(92deg,var(--accent),var(--accent2))}
.carousel-foot{display:flex;align-items:center;justify-content:center;gap:14px;margin-top:16px}
.carousel-dots{display:flex;gap:2px}
/* Кнопка-точка сама по себе крупная (зона нажатия), а видимая точка рисуется
   псевдоэлементом внутри. Прежний вариант расширял зону через ::after{inset:-18px},
   и у соседних точек эти зоны перекрывались: клик по третьей попадал во вторую. */
.carousel-dot{width:30px;height:44px;padding:0;border:none;background:none;cursor:pointer;
  display:grid;place-items:center}
.carousel-dot::before{content:"";width:8px;height:8px;border-radius:50%;background:var(--stroke);
  transition:transform .25s,background .25s}
.carousel-dot.is-on::before{background:var(--accent2);transform:scale(1.6)}
.carousel-dot:focus-visible{outline:2px solid var(--accent2);outline-offset:3px}
.carousel-arrow{width:40px;height:40px;border-radius:50%;display:grid;place-items:center;cursor:pointer;
  background:var(--panel);border:1px solid var(--stroke2);color:var(--text);
  transition:background .25s,border-color .25s,transform .25s}
.carousel-arrow:hover{background:rgba(255,255,255,.1);transform:translateY(-2px)}
@media (prefers-reduced-motion: reduce){
  .carousel-dot,.carousel-arrow{transition:none}
  .carousel-bar{display:none}
}

/* ── Таймлайн учебного года ── */
.tl{margin-top:30px}
.tl-track{position:relative;display:flex;gap:0;overflow-x:auto;scrollbar-width:none;
  padding:26px 0 6px;overscroll-behavior-x:contain}
.tl-track::-webkit-scrollbar{display:none}
.tl-track::before{content:"";position:absolute;left:52px;right:52px;top:33px;height:2px;background:var(--stroke)}
.tl-fill{position:absolute;left:52px;top:33px;height:2px;width:0;border-radius:2px;
  background:linear-gradient(92deg,var(--accent),var(--accent2));transition:width .5s ease}
.tl-item{position:relative;flex:0 0 auto;width:104px;padding:0;border:none;background:none;color:inherit;
  cursor:pointer;text-align:center;font:inherit}
.tl-dot{display:block;width:15px;height:15px;border-radius:50%;margin:0 auto 12px;
  background:var(--bg2);border:2px solid var(--stroke2);
  transition:background .3s,border-color .3s,box-shadow .3s,transform .3s}
.tl-item:hover .tl-dot{transform:scale(1.2)}
.tl-item.is-on .tl-dot{background:var(--accent2);border-color:var(--accent2);
  box-shadow:0 0 0 7px rgba(158,249,31,.14);transform:scale(1.15)}
.tl-month{font-family:var(--display);font-weight:700;font-size:13px;color:var(--muted);transition:color .3s}
.tl-item.is-on .tl-month{color:var(--text)}
.tl-item:focus-visible{outline:2px solid var(--accent2);outline-offset:4px;border-radius:12px}
.tl-panel{margin-top:18px;background:var(--panel);border:1px solid var(--stroke2);border-radius:24px;
  padding:26px 24px;min-height:150px;transition:opacity .3s}
.tl-panel.is-swapping{opacity:0}
.tl-panel .tl-kicker{font-family:var(--display);font-weight:700;color:var(--accent2);font-size:var(--fs-cap);
  letter-spacing:.08em;text-transform:uppercase}
.tl-panel h3{font-family:var(--display);font-size:var(--fs-2);margin:8px 0 10px}
.tl-panel p{color:var(--muted)}
@media (prefers-reduced-motion: reduce){.tl-dot,.tl-month,.tl-fill,.tl-panel{transition:none}}

/* ── Разбор задачи, который «решается» по мере прокрутки ── */
/* Одна колонка: условие сверху, шаги под ним. Двухколоночный вариант с липким
   условием отпадает — position:sticky не работает из-за overflow-x на body (см. выше),
   а без липкости половина экрана оставалась пустой к концу решения. */
.solve{display:grid;gap:18px;margin-top:28px;max-width:820px}
.solve-task{background:var(--panel);border:1px solid var(--stroke2);border-radius:24px;padding:24px}
.solve-task .solve-given{display:flex;flex-wrap:wrap;gap:6px 26px;line-height:1.9}
.solve-task .kicker{margin-bottom:10px}
.solve-task p{color:var(--text);font-size:var(--fs-1);line-height:1.6}
.solve-given{margin-top:16px;padding-top:16px;border-top:1px solid var(--stroke);
  font-family:var(--display);font-weight:700;color:var(--muted);font-size:15px}
.solve-steps{display:grid;gap:12px}
.solve-step{background:var(--panel);border:1px solid var(--stroke);border-radius:20px;padding:18px 20px;
  opacity:.28;transform:translateY(8px);
  transition:opacity .5s ease,transform .5s ease,border-color .5s ease,background .5s ease}
.solve-step.is-on{opacity:1;transform:none;border-color:var(--stroke2);background:rgba(255,255,255,.06)}
.solve-step .n{display:inline-grid;place-items:center;width:26px;height:26px;border-radius:50%;
  background:rgba(0,98,235,.22);border:1px solid var(--stroke2);color:var(--blue-light);
  font-family:var(--display);font-weight:700;font-size:13px;margin-bottom:10px}
.solve-step.is-on .n{background:rgba(158,249,31,.18);border-color:rgba(158,249,31,.5);color:var(--accent2)}
.solve-step p{color:var(--muted);font-size:15px;line-height:1.6}
.solve-f{display:block;margin-top:10px;font-family:var(--display);font-weight:700;
  font-size:var(--fs-1);color:var(--text);letter-spacing:.01em;overflow-wrap:break-word}
.solve-step.is-final .solve-f{background:var(--grad-text);-webkit-background-clip:text;background-clip:text;color:transparent}
@media (prefers-reduced-motion: reduce){
  .solve-step{opacity:1;transform:none;transition:none}
}

/* ── Обводка макета устройства при появлении (одноразовая сцена) ── */
.laptop-trace{position:absolute;inset:0;z-index:4;pointer-events:none;overflow:visible;opacity:0}
.laptop-trace rect{fill:none;stroke:var(--blue-light);stroke-width:2;stroke-linejoin:round;
  filter:drop-shadow(0 0 6px rgba(59,156,255,.8))}

/* ── Стопка экранов внутри макета: переключается по мере чтения подписей ── */
.screen-stack{position:absolute;inset:0}
.screen-stack img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;object-position:top center;
  opacity:0;transition:opacity .55s ease}
.screen-stack img.is-on{opacity:1}
@media (prefers-reduced-motion: reduce){.screen-stack img{transition:none}}
.screen-steps{display:grid;gap:14px;margin-top:26px}
@media (min-width:980px){
  .screen-scene{display:grid;grid-template-columns:minmax(0,1.15fr) minmax(0,.85fr);gap:34px;align-items:start}
  .screen-scene .device-stage{margin:0;position:sticky;top:100px}
  .screen-steps{margin-top:0}
}
.screen-step{background:var(--panel);border:1px solid var(--stroke);border-radius:20px;padding:18px 20px;
  transition:border-color .4s,background .4s,transform .4s}
.screen-step.is-on{border-color:var(--stroke2);background:rgba(255,255,255,.06);transform:translateY(-2px)}
.screen-step b{display:block;font-family:var(--display);font-size:var(--fs-1);margin-bottom:6px}
.screen-step span{color:var(--muted);font-size:15px;line-height:1.55}
@media (prefers-reduced-motion: reduce){.screen-step{transition:none}}

/* ── Микровзаимодействия: магнитные кнопки и пульс у цифр ── */
@media (hover:hover) and (min-width:1024px){
  body.is-public .btn{--mgx:0px;--mgy:0px}
  body.is-public .btn:hover{transform:translate(var(--mgx),calc(var(--mgy) - 2px))}
}
@media (prefers-reduced-motion: reduce){
  body.is-public .btn:hover{transform:none}
}
.stat .num{position:relative;display:inline-block}
.stat .num::before{content:"";position:absolute;left:50%;top:50%;width:64px;height:64px;margin:-32px 0 0 -32px;
  border-radius:50%;border:1px solid rgba(158,249,31,.55);opacity:0;transform:scale(.4);pointer-events:none}
.stat:hover .num::before{animation:numPulse 1.4s ease-out}
@keyframes numPulse{0%{opacity:.8;transform:scale(.4)}100%{opacity:0;transform:scale(1.7)}}
@media (prefers-reduced-motion: reduce){.stat:hover .num::before{animation:none}}

/* ── Плавные состояния в кабинете (quality-tier, работает и в /lk) ── */
.order-card,.alert,.slot,.faq-list details{transition:border-color .25s,background .25s}

/* ══════════════════════════════════════════════════════════════════════
   Итерация 10 (2026-08-20): качество исполнения.
   Видимый фокус, тапабельность, мягкое появление картинок, переходы между
   страницами и разгрузка длинной главной.
   ══════════════════════════════════════════════════════════════════════ */

/* ── Видимый фокус на всём сайте ──
   Раньше кольцо фокуса было браузерным по умолчанию: 1px тёмно-серого на
   почти чёрном фоне, то есть его не было видно вообще. Собственные правила
   были только у кнопок и пары компонентов. Теперь базовое правило одно,
   а частные (у .btn, .proof, .tl-item и т.д.) перебивают его по специфичности. */
:focus-visible{outline:2px solid var(--accent2);outline-offset:3px}
:focus:not(:focus-visible){outline:none}

/* ── Тапабельность: цель не меньше 44px, при этом внешний вид не меняется ──
   Увеличиваем зону нажатия отступами и невидимой подложкой, а не размером
   самого элемента: 83% трафика мобильный, промах по ссылке 17px в футере —
   обычное дело. */
.footer-grid a{display:inline-flex;align-items:center;min-height:28px}
.footer-grid ul{gap:2px}
.footer-legal a{display:inline-flex;align-items:center;min-height:28px}
@media (max-width:1023px){
  .nav-mobile-inner a{min-height:48px;display:flex;align-items:center}
}
/* Тач нужен только на телефоне — на десктопе/планшете с мышью тот же 44px
   выглядел как большие пустые отступы между пунктами «Разделы»/«Контакты». */
@media (max-width:767px){
  .footer-grid a, .footer-legal a{min-height:44px}
}

.carousel-arrow{width:44px;height:44px}
.tl-item{padding-bottom:6px}
/* Чекбоксы — общее правило на весь сайт (2026-09-01, найденный визуальный
   косяк: прежнее правило цепляло только .check-row/.field/#consent, поэтому
   чекбоксы в admin-btn-row — «потребовать сменить при входе», «бронь уже
   оплачена» и т.п. — рендерились чистым браузерным дефолтом, не в стиле
   сайта). Один универсальный селектор вместо накопления частных случаев. */
input[type=checkbox]{width:20px;height:20px;flex-shrink:0;accent-color:var(--accent2);cursor:pointer}
.check-row input[type=checkbox],.field input[type=checkbox],input#consent{width:24px;height:24px}
input#consent + label{cursor:pointer;padding:10px 0;display:inline-block}

/* ── Мягкое появление картинок ──
   Класс .img-fade навешивает скрипт, поэтому без JS картинки видны сразу —
   «пустых мест» не возникает ни при какой ошибке. */
.img-fade{opacity:0;transition:opacity .35s ease}
.img-fade.is-ready{opacity:1}
@media (prefers-reduced-motion: reduce){.img-fade{transition:none}}
/* Подложка цвета панели: пока картинка грузится, на её месте не дыра, а блок */
.shot,.tphoto,.proof-shot,.laptop-screen,.phone-screen{background:var(--panel)}

/* ── Переходы между страницами ──
   Сайт многостраничный, и раньше плавные переходы считались невозможными без
   SPA. Кросс-документные View Transitions это меняют: браузер сам снимает
   старую страницу, показывает новую и перетекает между ними. Где не
   поддерживается — просто обычный переход, ничего не ломается. */
@view-transition{navigation:auto}
::view-transition-old(root){animation-duration:.22s}
::view-transition-new(root){animation-duration:.28s}
/* Шапка одна и та же на всех страницах — пусть остаётся на месте, а не мигает */
.site-nav{view-transition-name:site-nav}
@media (prefers-reduced-motion: reduce){
  ::view-transition-group(*),::view-transition-old(*),::view-transition-new(*){animation:none!important}
}

/* ── Разгрузка длинной главной ──
   Дальние секции браузер не считает, пока до них не дошли. contain-intrinsic-size
   со словом auto означает «запомни настоящую высоту после первого показа» —
   поэтому полоса прокрутки не скачет. Первые три секции не трогаем: они и так
   в кадре, а отложенный расчёт там только вредит. */
body.is-public section:nth-of-type(n+4){
  content-visibility:auto;
  contain-intrinsic-size:auto 1300px}

/* ── Липкая кнопка на мобильном ──
   Главная — шестнадцать экранов прокрутки; без постоянного доступа к действию
   человек, дочитавший до середины, вынужден листать обратно. Показываем одну
   кнопку и только после первого экрана, чтобы не перекрывать hero, где эта же
   мысль уже есть. На десктопе не нужна: там всё близко и есть шапка. */
.cta-dock{position:fixed;left:0;right:0;bottom:0;z-index:18;display:none;
  padding:10px 14px calc(10px + env(safe-area-inset-bottom));
  background:linear-gradient(180deg,rgba(5,8,22,0),rgba(5,8,22,.92) 38%);
  transform:translateY(110%);transition:transform .3s ease}
.cta-dock.show{transform:none}
.cta-dock .btn{min-height:52px;font-size:var(--fs-0);box-shadow:0 10px 34px rgba(30,124,255,.6)}
@media (max-width:1023px){
  .cta-dock{display:block}
  body.is-public footer{padding-bottom:96px}
  /* Пока виден док, кнопка «наверх» прячется: два плавающих элемента в одном
     углу на телефоне наезжают и друг на друга, и на текст под ними. У самого
     низа страницы док уходит — и кнопка возвращается ровно там, где нужна. */
  .cta-dock.show ~ .to-top{opacity:0;pointer-events:none}
}
@media (prefers-reduced-motion: reduce){.cta-dock{transition:none}}

/* ── Состояния в кабинете ──
   Пустой список, отправка формы и сообщение об ошибке — места, куда человек
   попадает чаще всего и где качество ощущается сильнее, чем на витрине.
   Раньше это был просто серый текст по центру. */
.empty-state{text-align:center;padding:34px 24px;color:var(--muted);
  background:var(--panel);border:1px dashed var(--stroke);border-radius:24px}
.empty-state .es-ic{width:52px;height:52px;margin:0 auto 16px;border-radius:50%;display:grid;place-items:center;
  background:rgba(0,98,235,.16);border:1px solid var(--stroke2);color:var(--blue-light)}
.empty-state b{display:block;font-family:var(--display);font-size:var(--fs-1);color:var(--text);margin-bottom:8px}
.empty-state p{max-width:46ch;margin:0 auto}
.empty-state .btn{margin-top:20px;width:auto;display:inline-flex}

/* Кнопка на время отправки формы: понятно, что нажатие услышано */
.btn.is-sending{pointer-events:none;opacity:.75}
.btn .spin{width:16px;height:16px;border-radius:50%;flex-shrink:0;
  border:2px solid rgba(255,255,255,.35);border-top-color:#fff;animation:btnSpin .7s linear infinite}
@keyframes btnSpin{to{transform:rotate(360deg)}}
@media (prefers-reduced-motion: reduce){.btn .spin{animation:none}}

/* Значок у сообщения — ошибку видно раньше, чем прочитан текст */
.alert{display:flex;gap:10px;align-items:flex-start}
.alert::before{content:"";flex-shrink:0;width:20px;height:20px;margin-top:1px;border-radius:50%;
  background:currentColor;opacity:.9;
  -webkit-mask:center/14px no-repeat var(--alert-ic);mask:center/14px no-repeat var(--alert-ic)}
.alert--err{--alert-ic:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M12 7v7' stroke='black' stroke-width='2.4' stroke-linecap='round'/><circle cx='12' cy='17.5' r='1.4' fill='black'/></svg>")}
.alert--ok{--alert-ic:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M5 12.5l4.5 4.5L19 7' stroke='black' stroke-width='2.6' fill='none' stroke-linecap='round' stroke-linejoin='round'/></svg>")}
.alert--info{--alert-ic:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M12 11v6' stroke='black' stroke-width='2.4' stroke-linecap='round'/><circle cx='12' cy='7' r='1.4' fill='black'/></svg>")}

/* ── Бегущая строка кода ──
   На сайте вся «предметная» анимация была про физику: кривая предела и
   орбитальные кольца. Информатика и программирование — половина направлений
   школы — оставались без образа. Это лента с настоящими идиомами из задач ЕГЭ
   по информатике: рекурсия, перевод в двоичную, чтение файла, проверка на
   простоту. Движение горизонтальное, но не привязано к прокрутке — страница
   при этом стоит, как и у ленты вузов. */
.code-ticker{position:relative;overflow:hidden;margin-top:32px;padding:14px 0;
  border-top:1px solid var(--stroke);border-bottom:1px solid var(--stroke);
  -webkit-mask-image:linear-gradient(90deg,transparent,#000 6%,#000 94%,transparent);
  mask-image:linear-gradient(90deg,transparent,#000 6%,#000 94%,transparent)}
.code-track{display:flex;width:max-content;animation:codeRun 52s linear infinite}
.code-ticker:hover .code-track{animation-play-state:paused}
.code-half{display:flex}
/* Именно inline-block, а не inline-flex: во флекс-контейнере пробелы между
   дочерними элементами схлопываются, и код склеивался в «whilen > 0». */
.code-line{display:inline-block;padding:0 32px;white-space:nowrap;
  font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,"Liberation Mono",monospace;
  font-size:13.5px;line-height:1.6;color:var(--muted);opacity:.9}
.code-line::before{content:"›";color:var(--accent2);opacity:.65;margin-right:12px;font-weight:700}
.code-line .k{color:var(--accent2)}
.code-line .s{color:var(--blue-light)}
.code-line .c{color:rgba(198,207,236,.42);font-style:italic}
@keyframes codeRun{to{transform:translateX(-50%)}}
@media (prefers-reduced-motion: reduce){
  .code-ticker{-webkit-mask-image:none;mask-image:none}
  .code-track{animation:none;width:auto;flex-wrap:wrap}
  .code-track > .code-half:last-child{display:none}
  .code-line{padding:2px 24px 2px 0}
}
@media (max-width:600px){.code-line{font-size:12.5px;padding:0 22px}}

/* ── Сетка скриншотов разной высоты (поступления) ──
   Обычный grid выравнивает строку по самому высокому элементу и оставляет
   дыры под короткими. Здесь высота кадров гуляет от 207 до 572 px, поэтому
   раскладка колонками: элементы просто ложатся друг под друга без пустот. */
.adm-grid{columns:1;column-gap:14px;margin-top:24px}
@media (min-width:760px){.adm-grid{columns:2}}
@media (min-width:1200px){.adm-grid{columns:2;column-gap:18px}}
.adm-grid .shot{break-inside:avoid;margin-bottom:14px;display:block}

/* ── Один метод, два формата ──
   Раньше сайт читался так, будто групповые и индивидуальные занятия — две
   разные школы. На деле философия и инструменты общие, различается только
   устройство программы. Блок показывает это прямо: сверху общее, ниже — разница. */
.formats{margin-top:28px}
.formats-shared{background:var(--panel);border:1px solid var(--stroke2);border-radius:24px;padding:24px}
.formats-shared .kicker{margin-bottom:16px}
.tick-list{list-style:none;display:grid;gap:12px;grid-template-columns:1fr}
@media (min-width:720px){.tick-list{grid-template-columns:repeat(2,1fr);gap:12px 26px}}
@media (min-width:1000px){.formats-shared .tick-list{grid-template-columns:repeat(3,1fr)}}
.tick-list li{position:relative;padding-left:30px;color:var(--muted);line-height:1.5}
.tick-list li::before{content:"";position:absolute;left:0;top:3px;width:19px;height:19px;border-radius:50%;
  background:rgba(158,249,31,.16);border:1px solid rgba(158,249,31,.5)}
.tick-list li::after{content:"";position:absolute;left:6px;top:9px;width:7px;height:4px;
  border-left:2px solid var(--accent2);border-bottom:2px solid var(--accent2);transform:rotate(-45deg)}
.formats-cols{display:grid;gap:16px;margin-top:16px;grid-template-columns:1fr}
@media (min-width:820px){.formats-cols{grid-template-columns:repeat(2,1fr)}}
.fcol{background:var(--panel);border:1px solid var(--stroke);border-radius:24px;padding:24px;
  display:flex;flex-direction:column;transition:border-color .25s,transform .25s}
.fcol:hover{border-color:var(--stroke2);transform:translateY(-3px)}
.fcol h3{margin-bottom:6px}
.fcol .fcol-sub{color:var(--blue-light);font-size:var(--fs-cap);font-weight:600;margin-bottom:14px}
.fcol ul{list-style:none;display:grid;gap:10px;margin-bottom:18px}
.fcol li{position:relative;padding-left:16px;color:var(--muted);font-size:15px;line-height:1.5}
.fcol li::before{content:"";position:absolute;left:0;top:9px;width:5px;height:5px;border-radius:50%;background:var(--stroke2)}
.fcol a.fcol-go{margin-top:auto;color:var(--blue-light);font-weight:600;text-decoration:none}
@media (prefers-reduced-motion: reduce){.fcol{transition:none}}

/* ── Лента преподавателей: панели раздвигаются под курсором ──
   Каждая карточка — вертикальная панель. Пока мышь не наведена, видна только
   вертикальная подпись с именем; под курсором панель раздвигается, остальные
   ужимаются, и внутри проявляются предметы и факт о преподавателе.
   Горизонтальная подпись в свёрнутом виде не годится: в 150px имя обрезается
   посреди слова — поэтому в покое оно повёрнуто на 90°.
   Движение вызвано наведением, а не прокруткой, — правило про ось не нарушено.
   Раскрытие срабатывает и по :focus-within, поэтому лента доступна с клавиатуры. */
.tstrip{display:flex;gap:12px;margin-top:26px;height:460px}
.tstrip-item{position:relative;flex:1 1 0;min-width:0;overflow:hidden;
  border:1px solid var(--stroke);border-radius:24px;background:var(--panel);
  transition:flex-grow .85s cubic-bezier(.16,1,.3,1),border-color .5s;
  display:block;text-decoration:none;color:inherit}
/* Только затемнение — без grayscale: обесцвеченные лица читались как надгробные
   плиты, не как фотографии. Только brightness одним плавным переходом. */
.tstrip-item img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;object-position:50% 12%;
  filter:brightness(.6);transition:filter .85s cubic-bezier(.16,1,.3,1)}
.tstrip-item::after{content:"";position:absolute;inset:auto 0 0 0;height:66%;pointer-events:none;
  background:linear-gradient(180deg,transparent,rgba(5,8,22,.95))}

/* имя в покое — вертикально вдоль левого края */
.tstrip-vert{position:absolute;z-index:3;left:18px;bottom:22px;transform-origin:left bottom;
  transform:rotate(-90deg);white-space:nowrap;font-family:var(--display);font-weight:800;
  font-size:var(--fs-1);line-height:1;letter-spacing:-0.01em;transition:opacity .4s ease}

/* раскрытая карточка */
.tstrip-body{position:absolute;z-index:2;left:0;right:0;bottom:0;padding:22px 20px}
.tstrip-body > *{display:block}
.tstrip-name{font-family:var(--display);font-weight:800;font-size:var(--fs-2);line-height:1.2}
.tstrip-role{color:var(--muted);font-size:var(--fs-cap);margin-top:6px}
.tstrip-subj{display:inline-block;background:rgba(0,98,235,.22);border:1px solid var(--stroke2);
  color:var(--blue-light);border-radius:999px;padding:6px 13px;font-size:13px;font-weight:600;margin-top:14px}
.tstrip-fact{color:var(--muted);font-size:14px;line-height:1.5;margin-top:12px}
.tstrip-go{color:var(--accent2);font-weight:600;font-size:14px;margin-top:12px}

@media (min-width:900px) and (hover:hover){
  /* Уход (по умолчанию, без задержки) — контент прячется сразу, чтобы не
     «зависал» текст поверх сужающейся панели. */
  .tstrip-body{opacity:0;transform:translateY(8px);
    transition:opacity .3s ease,transform .4s cubic-bezier(.16,1,.3,1)}
  .tstrip-item:hover,.tstrip-item:focus-within{flex-grow:3;border-color:var(--stroke2)}
  .tstrip-item:hover img,.tstrip-item:focus-within img{filter:brightness(.92)}
  /* Появление — с небольшой задержкой, чтобы текст проявлялся уже в
     достаточно широкой панели, а не сжатым в узкой полосе на старте. */
  .tstrip-item:hover .tstrip-body,.tstrip-item:focus-within .tstrip-body{
    opacity:1;transform:none;transition-delay:.18s}
  .tstrip-item:hover .tstrip-vert,.tstrip-item:focus-within .tstrip-vert{opacity:0}
  /* пока ни на что не наведено, первая панель приоткрыта — видно, что лента живая */
  .tstrip:not(:hover):not(:focus-within) .tstrip-item:first-child{flex-grow:2.2}
  .tstrip:not(:hover):not(:focus-within) .tstrip-item:first-child img{filter:brightness(.92)}
  .tstrip:not(:hover):not(:focus-within) .tstrip-item:first-child .tstrip-body{
    opacity:1;transform:none;transition-delay:.18s}
  .tstrip:not(:hover):not(:focus-within) .tstrip-item:first-child .tstrip-vert{opacity:0}
}

/* Узкие экраны и тач: лента листается пальцем, всё раскрыто — прятать нечего */
@media (max-width:899px),(hover:none){
  .tstrip{height:auto;overflow-x:auto;scroll-snap-type:x mandatory;scrollbar-width:none;
    overscroll-behavior-x:contain;padding-bottom:6px}
  .tstrip::-webkit-scrollbar{display:none}
  .tstrip-item{flex:0 0 auto;width:min(78vw,300px);height:420px;scroll-snap-align:center;transition:none}
  .tstrip-item img{filter:brightness(.82)}
  .tstrip-vert{display:none}
}
@media (prefers-reduced-motion: reduce){
  .tstrip-item,.tstrip-item img,.tstrip-body{transition:none}
}

/* ══════════════════════════════════════════════════════════════════════
   Боковой слой: физические схемы и терминалы с кодом (2026-08-21).
   Возвращён по просьбе заказчика вместо снятых ранее точек и осциллограммы —
   но уже не абстрактным орнаментом, а предметными образами: слева физика,
   справа код. Всё на чистом CSS, без единого скрипта: анимируются только
   transform, opacity и stroke-dashoffset.

   Контейнер — position:absolute (не fixed), высоту выставляет main.js под
   document.body.scrollHeight, сверху overflow:hidden — иначе на короткой
   странице декор растянул бы прокрутку сверх настоящего контента.
   Пороги ширины разные: схемы графические и живут в 115px, терминалу нужен
   текст, поэтому он появляется только с 1700px, где сбоку хватает места.
   ══════════════════════════════════════════════════════════════════════ */
.side-deco{display:none}
@media (min-width:1440px){
  .side-deco{display:block;position:absolute;top:0;left:0;width:100%;z-index:-1;
    pointer-events:none;user-select:none;overflow:hidden}
  .sd-guide{position:absolute;top:220px;bottom:160px;width:1px;
    background:linear-gradient(var(--stroke2),var(--stroke2) 86%,transparent)}
  .sd-guide.l{left:2.2%}
  .sd-guide.r{right:2.2%}
  .sd{position:absolute}
  .sd-l{left:1.5%}
  .sd-r{right:1.5%}
  .sd-phys{width:clamp(112px,8vw,168px);opacity:.9}
  .sd-phys svg{display:block;width:100%;height:auto;overflow:visible}
  .sd-cap{display:block;margin-top:8px;font-size:10.5px;letter-spacing:.1em;text-transform:uppercase;
    color:rgba(198,207,236,.4);font-weight:600;text-align:center}
}

/* линии и подписи схем */
.sd-stroke{fill:none;stroke:var(--stroke2);stroke-width:1.4}
.sd-dim{fill:none;stroke:rgba(255,255,255,.16);stroke-width:1;stroke-dasharray:3 5}
.sd-hot{fill:var(--accent2)}
.sd-blue{fill:var(--blue-light)}

/* 1. Маятник — качается по синусу, как настоящий */
.sd-pend{transform-box:view-box;transform-origin:50px 8px;
  animation:sdSwing 3.6s cubic-bezier(.45,0,.55,1) infinite alternate}
@keyframes sdSwing{from{transform:rotate(-18deg)}to{transform:rotate(18deg)}}

/* 2. Сигнал — штрихи бегут вдоль синусоиды */
.sd-wave{stroke:var(--blue-light);stroke-width:1.5;fill:none;stroke-dasharray:7 6;
  animation:sdDash 1.9s linear infinite}
@keyframes sdDash{to{stroke-dashoffset:-26}}

/* 3. Линза — лучи сходятся в фокусе, точка фокуса дышит */
.sd-ray{stroke:var(--accent2);stroke-width:1.2;fill:none;opacity:.65;
  stroke-dasharray:5 7;animation:sdDash 2.6s linear infinite}
.sd-ray:nth-of-type(2){animation-duration:2.2s}
.sd-ray:nth-of-type(3){animation-duration:3s}
.sd-focus{animation:sdPulse 2.4s ease-in-out infinite}
@keyframes sdPulse{0%,100%{opacity:.45;transform:scale(.8)}50%{opacity:1;transform:scale(1.25)}}
.sd-focus{transform-box:fill-box;transform-origin:center}

/* 4. Контур с импульсом — точка идёт по контуру платы */
.sd-pulse{offset-path:path('M18 18 H102 V102 H18 Z');offset-rotate:0deg;
  animation:sdRun 7s linear infinite}
@keyframes sdRun{to{offset-distance:100%}}

/* ── Терминал с кодом ── */
@media (min-width:1700px){
  .sd-term{display:block;width:clamp(188px,11.5vw,244px);
    background:rgba(8,12,28,.82);border:1px solid var(--stroke);border-radius:12px;
    overflow:hidden;box-shadow:0 14px 40px rgba(0,0,0,.45)}
}
@media (max-width:1699px){.sd-term{display:none}}
.sd-term-bar{display:flex;gap:5px;align-items:center;padding:7px 10px;
  background:rgba(255,255,255,.04);border-bottom:1px solid var(--stroke)}
.sd-term-bar i{width:7px;height:7px;border-radius:50%;background:rgba(255,255,255,.18)}
.sd-term-bar span{margin-left:6px;font-size:9.5px;letter-spacing:.08em;text-transform:uppercase;
  color:rgba(198,207,236,.4);font-weight:600}
.sd-term-body{padding:10px 11px 12px;
  font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,"Liberation Mono",monospace;
  font-size:10.5px;line-height:1.75;color:var(--muted)}
.sd-term-body b{display:block;white-space:nowrap;opacity:0;font-weight:400;
  animation:sdLine 11s linear infinite}
.sd-term-body b:nth-child(1){animation-delay:0s}
.sd-term-body b:nth-child(2){animation-delay:1.2s}
.sd-term-body b:nth-child(3){animation-delay:2.4s}
.sd-term-body b:nth-child(4){animation-delay:3.6s}
.sd-term-body b:nth-child(5){animation-delay:4.8s}
@keyframes sdLine{0%{opacity:0;transform:translateY(3px)}
  4%{opacity:1;transform:none}86%{opacity:1}94%,100%{opacity:0}}
.sd-term-body .k{color:var(--accent2)}
.sd-term-body .s{color:var(--blue-light)}
.sd-term-body .c{color:rgba(198,207,236,.35)}
.sd-caret{display:inline-block;width:6px;height:11px;vertical-align:-1px;margin-left:2px;
  background:var(--accent2);animation:sdBlink 1.05s steps(1) infinite}
@keyframes sdBlink{50%{opacity:0}}

@media (prefers-reduced-motion: reduce){
  .sd-pend,.sd-wave,.sd-ray,.sd-focus,.sd-pulse,.sd-term-body b,.sd-caret{animation:none}
  .sd-term-body b{opacity:1}
}

/* Разводим экземпляры по фазе, иначе восемь маятников качаются в такт
   и слой читается как мигание, а не как живой фон. */
.sd--2 .sd-pend,.sd--2 .sd-wave,.sd--2 .sd-ray,.sd--2 .sd-pulse,.sd--2 .sd-focus{animation-delay:-1.3s}
.sd--3 .sd-pend,.sd--3 .sd-wave,.sd--3 .sd-ray,.sd--3 .sd-pulse,.sd--3 .sd-focus{animation-delay:-2.6s}
.sd--2 .sd-pend{animation-duration:4.3s}
.sd--3 .sd-pend{animation-duration:3.1s}
.sd--2 .sd-pulse{animation-duration:8.5s}
.sd--3 .sd-pulse{animation-duration:6s}
/* у терминалов сдвигать задержку нельзя — на ней держится порядок строк,
   поэтому меняем только скорость всего цикла */
.sd--2 .sd-term-body b{animation-duration:13s}
.sd--3 .sd-term-body b{animation-duration:9.5s}

/* ══════════════════════════════════════════════════════════════════════
   3D-карусель преподавателей на /teachers (2026-08-22, переделано).
   Карточки реально стоят по кругу: каждая развёрнута на свой угол и отодвинута
   от центра по оси Z (rotateY + translateZ на общем preserve-3d кольце).
   Кольцо физически проворачивается, карточки уезжают вбок и вглубь, передняя
   выходит анфас и разворачивается в панель с информацией.
   ══════════════════════════════════════════════════════════════════════ */
.tcar{margin-top:30px}
@media (max-width:899px){.tcar{display:none}} /* на мобильном — рельс ниже */

.tcar-stage{position:relative;height:470px;perspective:1600px;perspective-origin:50% 45%}
/* Кольцо отодвинуто назад ровно на радиус: тогда передняя карточка оказывается
   в плоскости экрана (z=0) и рисуется в свой натуральный размер, а не
   раздувается перспективой. Без этого сдвига она вылезала за пределы сцены и
   перекрывала кнопки листания под ней. Дальние карточки при этом честно
   уменьшаются, потому что уходят вглубь. */
/* Единые время и кривая для всего, что движется в карусели: раньше поворот шёл
   0.9с, ширина 0.8с, прозрачность 0.6с — рассинхрон читался как рывок, потому
   что карточка доезжала, а панель внутри ещё догоняла. Кривая ease-in-out:
   колесо плавно набирает ход и плавно останавливается, как настоящее, — это и
   даёт «ощутимость» вращения. Прежняя expo-out стартовала рывком. */
.tcar-ring{--radius:560px;--step:52deg;--dur:1.05s;--ease:cubic-bezier(.62,.02,.24,1);
  position:absolute;inset:0;transform-style:preserve-3d;
  transform:translateZ(calc(var(--radius) * -1))}

/* Карточка на своём месте окружности: развернуть на угол и отодвинуть от оси.
   Радиус намеренно большой (620px) — при 4 карточках соседние тогда уходят
   далеко вбок и не наезжают на раскрытую переднюю. */
.tcar-card{position:absolute;top:0;left:50%;width:250px;height:100%;margin-left:-125px;cursor:pointer;
  transform-style:preserve-3d;
  /* Место карточки задаётся не абсолютным индексом, а смещением от текущей
     передней (--off, выставляет JS): тогда соседи всегда расходятся в обе
     стороны от центра, а не собираются гуртом с одного края. Шаг 55°, а не
     360/N — при четырёх карточках полный круг ставил бы боковые ровно рёбрами
     к зрителю, и они схлопывались бы в невидимую линию. */
  transform:rotateY(calc(var(--step,55deg) * var(--off,0))) translateZ(var(--radius,560px));
  /* Отсекаем изнанку именно здесь — на элементе, который реально повёрнут.
     На вложенном .tcar-card-in правило не срабатывало, и дальняя карточка
     просвечивала зеркальным текстом сквозь панель передней. */
  backface-visibility:hidden;
  transition:transform var(--dur) var(--ease),width var(--dur) var(--ease),
             margin-left var(--dur) var(--ease),filter var(--dur) var(--ease)}
/* Карточка, оказавшаяся на дальней стороне кольца, повёрнута к нам изнанкой —
   backface-visibility её убирает, иначе она просвечивала бы сквозь переднюю. */
/* Фон намеренно непрозрачный (--bg2, а не полупрозрачный --panel): карточки
   стоят друг за другом в глубине, и сквозь полупрозрачную панель было видно
   то, что стоит за ней на кольце. */
.tcar-card-in{position:absolute;inset:0;display:flex;overflow:hidden;
  background:var(--bg2);border:1px solid var(--stroke);border-radius:28px;
  box-shadow:0 26px 70px rgba(0,0,0,.55)}
.tcar-photo{flex:0 0 250px;position:relative;overflow:hidden;transition:flex-basis var(--dur) var(--ease)}
.tcar-photo img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;object-position:50% 14%;
  filter:brightness(.52);transition:filter var(--dur) var(--ease)}
.tcar-photo::after{content:"";position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(180deg,transparent 45%,rgba(5,8,22,.9))}

/* имя на свёрнутой карточке — снизу, по центру фото */
.tcar-label{position:absolute;z-index:3;left:0;right:0;bottom:22px;text-align:center;padding:0 14px;
  font-family:var(--display);font-weight:800;font-size:16px;line-height:1.3;
  transition:opacity .35s ease}

/* панель с информацией — раскрывается только у передней карточки */
.tcar-info{flex:0 0 0;width:0;overflow:hidden;transition:flex-basis var(--dur) var(--ease)}
.tcar-info-in{width:400px;padding:32px 28px;opacity:0;transform:translateY(8px);
  transition:opacity .35s ease,transform .45s ease}
.tcar-subjects{display:inline-block;background:rgba(0,98,235,.22);border:1px solid var(--stroke2);
  color:var(--blue-light);border-radius:999px;padding:6px 13px;font-size:13px;font-weight:600}
.tcar-name{display:block;font-family:var(--display);font-weight:800;font-size:var(--fs-2);line-height:1.25;margin-top:14px}
.tcar-role{display:block;color:var(--muted);font-size:14px;margin-top:4px}
.tcar-facts{list-style:none;display:flex;flex-direction:column;gap:7px;margin-top:16px}
.tcar-facts li{position:relative;padding-left:15px;color:var(--muted);font-size:14px;line-height:1.5}
.tcar-facts li::before{content:"";position:absolute;left:0;top:8px;width:5px;height:5px;border-radius:50%;background:var(--stroke2)}
.tcar-approach{display:block;color:var(--text);font-size:14.5px;line-height:1.6;margin-top:16px}
.tcar-more{display:inline-block;margin-top:16px;color:var(--accent2);font-weight:600;font-size:14px}

/* передняя карточка: шире, ярче, с раскрытой панелью */
.tcar-card.is-front{width:660px;margin-left:-330px;z-index:2;cursor:default}
.tcar-card.is-front .tcar-card-in{border-color:var(--stroke2)}
.tcar-card.is-front .tcar-photo{flex-basis:250px}
.tcar-card.is-front .tcar-photo img{filter:brightness(.92)}
.tcar-card.is-front .tcar-info{flex-basis:410px}
.tcar-card.is-front .tcar-info-in{opacity:1;transform:none;transition-delay:.55s}
.tcar-card.is-front .tcar-label{opacity:0}
/* Дальние карточки уводим глубже по яркости, чем передняя: разница в свете —
   главный признак глубины на плоском экране, и без неё кольцо читалось плоским.
   Ещё дальше стоящие (|--off| = 2) притушены сильнее ближних. */
.tcar-card:not(.is-front){filter:brightness(.7)}
.tcar-card:not(.is-front) .tcar-card-in{border-color:transparent}

.tcar-nav{position:relative;z-index:5;display:flex;align-items:center;justify-content:center;gap:14px;margin-top:26px}
.tcar-arrow{width:44px;height:44px;border-radius:50%;display:grid;place-items:center;cursor:pointer;
  background:var(--panel);border:1px solid var(--stroke2);color:var(--text);
  transition:background .25s,transform .25s}
.tcar-arrow:hover{background:rgba(255,255,255,.1);transform:translateY(-2px)}
.tcar-arrow:focus-visible{outline:2px solid var(--accent2);outline-offset:3px}
.tcar-dots{display:flex;gap:2px}
.tcar-dot{width:32px;height:44px;padding:0;border:none;background:none;cursor:pointer;
  display:grid;place-items:center}
.tcar-dot::before{content:"";width:9px;height:9px;border-radius:50%;background:var(--stroke);
  transition:transform .25s,background .25s}
.tcar-dot.is-on::before{background:var(--accent2);transform:scale(1.5)}
.tcar-dot:focus-visible{outline:2px solid var(--accent2);outline-offset:4px}

@media (prefers-reduced-motion: reduce){
  .tcar-ring,.tcar-card,.tcar-photo,.tcar-photo img,.tcar-info,.tcar-info-in,.tcar-label,.tcar-arrow,.tcar-dot{transition:none}
}

/* Узкие экраны и тач: 3D-кольцо не показываем (нет места и нет курсора),
   вместо него — листаемый пальцем рельс из уже раскрытых карточек. */
.tcar-rail{display:none}
@media (max-width:899px){
  .tcar-rail{display:flex;gap:14px;margin-top:26px;overflow-x:auto;scroll-snap-type:x mandatory;
    scrollbar-width:none;overscroll-behavior-x:contain;padding-bottom:6px}
  .tcar-rail::-webkit-scrollbar{display:none}
  .tcar-rail .tcard{flex:0 0 auto;width:min(82vw,320px);scroll-snap-align:center}
}

/* ══════════════════════════════════════════════════════════════════════
   Пасхалка внизу сайта (2026-08-22).
   Тонкая едва заметная полоса-«барьер» сразу под футером. Толкать её вниз
   колёсиком/пальцем — она постепенно светлеет и потрескивает, а после
   короткой выдержки «раскалывается»: под ней открывается тёмная полоса
   (немного темноты, в которую скроллишь), а за ней — вспышка с фразой.
   Раскрытие не подменяет прокрутку пользователя (никакого автоскролла) —
   человек продолжает вести колесо сам, просто под барьером находится
   настоящий контент, а не иллюзия. ══════════════════════════════════════ */
.egg-gate{position:relative;height:34px;margin:0 auto;max-width:220px;
  display:flex;align-items:center;justify-content:center;cursor:default;
  padding-bottom:env(safe-area-inset-bottom)}
.egg-gate-line{--charge:0;width:64px;height:2px;border-radius:2px;
  background:var(--stroke2);opacity:calc(.12 + var(--charge) * .8);
  box-shadow:0 0 calc(var(--charge) * 22px) calc(var(--charge) * 4px) rgba(158,249,31,calc(var(--charge) * .8));
  transform:scaleX(calc(1 + var(--charge) * .5));
  transition:opacity .15s linear,box-shadow .15s linear,transform .15s linear}
.egg-gate.is-shaking .egg-gate-line{animation:eggShake .12s linear infinite}
@keyframes eggShake{0%,100%{margin-left:0}50%{margin-left:1px}}
.egg-gate.is-broken .egg-gate-line{animation:eggBreak .5s ease-out forwards}
@keyframes eggBreak{0%{opacity:1;transform:scaleX(1.5)}
  40%{opacity:1;box-shadow:0 0 40px 8px rgba(158,249,31,.9)}
  100%{opacity:0;transform:scaleX(3)}}

.egg-vault{height:0;overflow:hidden;background:#020308;transition:height .85s cubic-bezier(.16,1,.3,1)}
.egg-void{height:62vh;background:radial-gradient(ellipse at 50% 20%,rgba(0,98,235,.06),transparent 60%),#020308}
.egg-msg{min-height:56vh;display:flex;flex-direction:column;align-items:center;justify-content:center;
  text-align:center;padding:60px 24px;gap:22px;opacity:0;transform:scale(.94);
  transition:opacity .5s ease,transform .6s cubic-bezier(.16,1,.3,1)}
.egg-msg.is-in{opacity:1;transform:none}
.egg-msg.is-in::before{content:"";position:fixed;inset:0;background:#fff;opacity:0;pointer-events:none;
  animation:eggFlash .6s ease-out forwards;z-index:5}
@keyframes eggFlash{0%{opacity:0}8%{opacity:.5}100%{opacity:0}}
.egg-line{font-family:var(--display);font-weight:700;font-size:clamp(19px,2.6vw,28px);line-height:1.5;
  color:var(--text);max-width:46ch}
.egg-line2{background:var(--grad-text);-webkit-background-clip:text;background-clip:text;color:transparent}
.egg-sign{color:var(--muted);font-size:14px;letter-spacing:.04em}

@media (prefers-reduced-motion: reduce){
  .egg-vault{transition:none}
  .egg-gate-line{transition:none}
  .egg-gate.is-shaking .egg-gate-line{animation:none}
  .egg-gate.is-broken .egg-gate-line{animation:none;opacity:0}
  .egg-msg{transition:none}
  .egg-msg.is-in::before{animation:none}
}

/* ══════════════════════════════════════════════════════════════════════
   Оболочка админки (2026-08-22).
   Раньше навигация между 4 разделами была текстовой строкой «Пользователи ·
   Счета · Слоты · Записи», а таблицы — HTML-разметкой с инлайновыми стилями
   на каждой ячейке. Теперь общая раскладка с боковой панелью (список пунктов
   с иконками и активным состоянием), заголовком страницы с кратким описанием
   того, что на ней делают, и лёгкими карточками-цифрами для ориентира с
   первого взгляда — без новых запросов к базе, значения уже посчитаны
   на сервере из тех же данных, что и таблица ниже.
   ══════════════════════════════════════════════════════════════════════ */
.admin-shell{display:grid;grid-template-columns:220px 1fr;gap:32px;align-items:start;margin-top:8px}
@media (max-width:899px){.admin-shell{grid-template-columns:1fr;gap:20px}}
/* Без min-width:0 grid-колонка растягивается до min-content самого широкого
   потомка (нативный <select> по самой длинной <option>, широкая таблица
   внутри .admin-table-wrap{overflow-x:auto} — overflow:auto не даёт
   "нулевой авто-минимум" в обычном block-контексте) — контент вылезал за
   экран и обрезался на ВСЕХ ширинах вплоть до 1920px (найдено замером
   getBoundingClientRect, не на глаз, 2026-08-25). */
.admin-main{min-width:0}

.admin-sidebar{position:sticky;top:88px;display:flex;flex-direction:column;gap:2px}
@media (max-width:899px){
  .admin-sidebar{position:static;flex-direction:row;gap:8px;overflow-x:auto;
    scrollbar-width:none;padding-bottom:4px}
  .admin-sidebar::-webkit-scrollbar{display:none}
  /* Виджет «К оплате» в сайдбаре кабинета ученика (2026-08-26) — это не
     пункт навигации, а карточка-контент; если оставить его обычным потомком
     .admin-sidebar, на мобильном он становится ещё одним элементом
     горизонтальной прокручиваемой ленты и съезжает за экран (найдено
     реальным замером getBoundingClientRect: x:388 при ширине экрана 390 —
     то есть виджет физически невидим без ручной прокрутки узкой ленты
     вбок). Отдельный модификатор переносит его на свою строку целиком. */
  .admin-sidebar.lk-sidebar{flex-wrap:wrap}
  .admin-sidebar.lk-sidebar .lk-unpaid-widget{flex:1 0 100%;margin-top:4px}
}
.admin-nav-item{display:flex;align-items:center;gap:10px;padding:10px 12px;border-radius:12px;
  color:var(--muted);text-decoration:none;font-size:14px;font-weight:600;white-space:nowrap;
  border:1px solid transparent;transition:background .2s,color .2s,border-color .2s}
.admin-nav-item svg{flex-shrink:0;opacity:.8}
.admin-nav-item:hover{background:rgba(255,255,255,.05);color:var(--text)}
.admin-nav-item.is-active{background:rgba(0,98,235,.14);border-color:var(--stroke2);color:var(--text)}
.admin-nav-item.is-active svg{opacity:1;color:var(--blue-light)}
.admin-nav-item:focus-visible{outline:2px solid var(--accent2);outline-offset:2px}

/* Сворачивание боковой панели (2026-08-27) — общий .admin-shell используется
   в трёх местах (админка, кабинет преподавателя, кабинет ученика), поэтому
   один переключатель и одно правило коллапса на всех троих. На десктопе
   сайдбар — это первый трек грида, поэтому схлопывается сам грид (анимируемо
   в современных браузерах, если число треков не меняется); на мобильном
   сайдбар и так уже единственная строка над контентом, там переключатель
   просто скрывает её, второй колонки-трека там нет и делать не нужно. */
/* 2026-08-27: кнопка сначала стояла справа с постоянной текстовой подписью —
   по прямой правке пользователя перенесена рядом с киккером слева, подпись
   убрана и заменена всплывающей подсказкой по наведению (переиспользован уже
   существующий .btn[data-tooltip] с публичных CTA-кнопок, см. выше). */
/* .kicker несёт собственный margin-bottom:14px (нужен для отступа перед
   .admin-shell) — внутри флекс-строки этот отступ входит в margin-box кикера
   и центрирование по cross-axis учитывает его, из-за чего кнопка визуально
   съезжала ниже текста. Обнуляем margin у кикера здесь и переносим тот же
   отступ на саму строку — оба элемента центрируются чисто по своей высоте,
   отступ до .admin-shell ниже не меняется. */
.shell-kicker-row{display:flex;align-items:center;gap:12px;margin-bottom:14px}
.shell-kicker-row .kicker{margin-bottom:0}
.shell-toggle{flex-shrink:0;width:36px;padding:0!important}
@media (min-width:900px){
  .admin-shell{transition:grid-template-columns .25s ease,gap .25s ease}
  .admin-shell.is-collapsed{grid-template-columns:0 1fr;gap:0}
  .admin-shell.is-collapsed .admin-sidebar{opacity:0;pointer-events:none}
  .admin-sidebar{transition:opacity .15s ease}
}
@media (max-width:899px){.admin-shell.is-collapsed .admin-sidebar{display:none}}

.admin-header{display:flex;flex-wrap:wrap;gap:16px;align-items:flex-end;justify-content:space-between}
.admin-header h1{margin:0}
.admin-header p{color:var(--muted);margin-top:6px;max-width:60ch}
.admin-header .btn{width:auto;display:inline-flex;flex-shrink:0}
/* Реальный баг с мобильного (2026-08-30): заголовок кабинета — flex-элемент
   .admin-header без min-width, а h1/h2/h3 по проекту получают только
   overflow-wrap:break-word (predel.css:104) — эта настройка ломает слово,
   когда БЛОК УЖЕ сжат до своей ширины, но НЕ уменьшает «автоматический
   минимум» flex-элемента при расчёте раскладки (единственное свойство,
   которое на это влияет, — overflow-wrap:anywhere, здесь сознательно не
   применяем ко всем заголовкам сайта, слишком широкий эффект). Из-за этого
   заголовок с длинным именем (страница ученика/преподавателя в админке,
   «Здравствуйте, {имя}» в кабинете преподавателя) не сжимался вообще и
   вылезал за экран на 39–69px на 360–414px, хотя `.admin-main{min-width:0}`
   (см. выше, Итерация 37) уже защищает саму колонку грида — тот фикс не
   распространяется глубже на flex-обёртку заголовка внутри неё. Тот же
   приём, что и там: снимаем автоматический минимум с обёртки title+desc. */
.admin-header>div:first-child{min-width:0}

.admin-stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:12px;margin-top:22px}
.admin-stat{background:var(--panel);border:1px solid var(--stroke);border-radius:18px;padding:14px 16px}
.admin-stat b{display:block;font-family:var(--display);font-size:var(--fs-3);font-weight:800;
  font-variant-numeric:tabular-nums}
.admin-stat span{color:var(--muted);font-size:13px}
.admin-stat.is-accent b{color:var(--accent2)}
.admin-stat.is-warn b{color:var(--blue-light)}

/* ── таблица: было — инлайновые стили на каждой ячейке в 4 шаблонах ── */
.admin-table-wrap{overflow-x:auto;margin-top:26px;background:var(--panel);border:1px solid var(--stroke);
  border-radius:20px}
/* Список занятий по дням с чекбоксом «проведено» (2026-08-27) — по прямой
   просьбе: выпадающий список статуса в календаре был неудобен для основного
   действия («отметить проведено»), сама галочка убрана в details «Ещё» как
   более редкий случай (перенос/отмена/не пришёл). */
.lesson-row{display:flex;align-items:center;gap:14px;padding:10px 0;border-bottom:1px solid var(--stroke)}
.lesson-row:last-child{border-bottom:none}
.lesson-check{flex-shrink:0;width:30px;height:30px;border-radius:8px;border:2px solid var(--stroke2);
  background:var(--bg2);color:var(--bg);cursor:pointer;display:flex;align-items:center;justify-content:center;
  transition:background .15s,border-color .15s}
.lesson-check:hover{border-color:var(--accent2)}
.lesson-check.is-checked{background:var(--accent2);border-color:var(--accent2)}
.lesson-row-info{flex:1 1 auto;min-width:0}
.lesson-row-info b{font-variant-numeric:tabular-nums;font-size:15px}
.lesson-row-info span{display:block;color:var(--muted);font-size:13.5px;margin-top:2px}
.lesson-row details{flex-shrink:0}
.lesson-strip{display:flex;gap:10px;overflow-x:auto;padding-bottom:6px;scrollbar-width:thin}
.lesson-strip-item{flex:0 0 auto;display:flex;flex-direction:column;align-items:center;gap:6px;
  background:var(--bg2);border:1px solid var(--stroke);border-radius:12px;padding:10px 12px;min-width:84px}
.lesson-strip-item.is-done{border-color:rgba(158,249,31,.4)}
.lesson-strip-item.is-other{opacity:.55}
.lesson-strip .lesson-check{width:26px;height:26px}

/* Разворот предмета в кабинете ученика (2026-08-27) — крупная шапка вместо
   мелкого текста, настоящие кнопки конференции/доски вместо гиперссылок
   (по прямой просьбе — «сейчас это просто гиперссылка, неудобно»). */
.learning-hero{display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:20px;
  background:linear-gradient(135deg,var(--panel),var(--bg2));border:1px solid var(--stroke2);
  border-radius:24px;padding:28px 32px}
.learning-hero h2{font-size:var(--fs-3);font-family:var(--display)}
.learning-hero-actions{display:flex;gap:12px;flex-wrap:wrap}
.learning-cta{width:auto;min-height:52px;padding:0 26px;font-size:16px;flex-shrink:0}
@media (max-width:640px){.learning-hero{padding:22px}.learning-cta{width:100%}}
/* Тот же реальный баг, что и у .admin-header выше (2026-08-30): flex-элемент
   без min-width не сжимается под длинное имя преподавателя/предмет — на
   /lk/learning/{id} вылезало на 225–279px на 360–414px. Тот же приём. */
.learning-hero-title{min-width:0}

/* Карточки предметов на главной кабинета ученика (2026-08-27) — крупнее и
   заметнее, чем обычная .card, по прямой просьбе («тяжело выцепить глазами
   курсы, на которых я занимаюсь») — тот же приём подсветки левым краем, что
   и у .pain-box в админке, ради визуальной согласованности между кабинетами. */
/* auto-fit, не auto-fill (2026-09-01, найденный визуальный баг): auto-fill
   резервирует место под все колонки, которые ТЕОРЕТИЧЕСКИ поместились бы по
   320px, даже если карточка всего одна — она оставалась узкой полоской слева
   на широком экране, а остальное превращалось в пустоту. auto-fit схлопывает
   пустые треки, единственная карточка сама растягивается на всю ширину. */
.subject-cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(320px,1fr));gap:18px;align-items:stretch}
.subject-card-head{display:flex;justify-content:space-between;align-items:flex-start;gap:16px;flex-wrap:wrap}
/* Карточки одного ряда растянуты на равную высоту (align-items:stretch выше),
   но заголовок (имя педагога/предмет) может занимать 1-2 строки в зависимости
   от карточки. Первая попытка (2026-09-01) прибивала к низу только ссылку
   "Расписание и подробности" — сама она действительно выровнялась, но блок
   кнопок остался приклеен к заголовку сверху, и по прямому отзыву "кнопки на
   разных уровнях" (у карточки с двухстрочным заголовком кнопки ниже, чем у
   карточки с однострочным). Правильно — прибивать к низу кнопки И ссылку
   ВМЕСТЕ, одним блоком: .subject-card-foot теперь сам flex-колонка с
   фиксированным зазором между кнопками и ссылкой, а margin-top:auto двигает
   весь этот блок целиком — тогда что кнопки, что ссылка стоят на одной и той
   же высоте во всех карточках ряда, независимо от того, сколько строк занял
   заголовок или сколько кнопок в ряду (2 или 3). */
.subject-card{display:flex;flex-direction:column}
.subject-card-foot{margin-top:auto;padding-top:18px;display:flex;flex-direction:column;gap:14px}
.subject-card-foot p{margin:0}

/* Крупнее текст в кабинете ученика/преподавателя (2026-08-27, по прямой
   просьбе — «текст прям маленький, неудобно читать») — компоненты общие с
   админкой (там плотность оправдана), поэтому увеличиваем только внутри
   is-lk, не трогая саму админку. */
body.is-lk .admin-table{font-size:15.5px}
body.is-lk .admin-table .muted{font-size:13.5px}
body.is-lk .info-item .info-label{font-size:13px}
body.is-lk .info-item .info-value{font-size:17px}
body.is-lk .contact-line{font-size:16px}

/* Тот же запрос повторился (2026-09-01) — «на большом экране информации
   мало, текст маленький, пространства много» — правило выше (2026-08-27)
   охватывало компоненты, которые уже существовали ДО раздела «Статистика»
   (донат-кольцо, плашки-проценты, полоски недельного времени, вывод-
   подсказка), а они добавлены позже и остались мелким служебным шрифтом
   (11-13px), рассчитанным на плотную админку, а не на просторный кабинет
   родителя. Ширину теперь держит .wrap.lk-page (см. ниже, кабинет больше не
   использует безлимитный is-app) — здесь только сама плотность контента. */
@media (min-width:1440px){
  body.is-lk .donut-ring{width:156px;height:156px}
  body.is-lk .donut-ring-label b{font-size:30px}
  body.is-lk .donut-ring-label span{font-size:13px;max-width:110px}
  body.is-lk .sd-figure b{font-size:26px}
  body.is-lk .sd-figure span{font-size:14px}
  body.is-lk .stat-chip{padding:16px 22px;min-width:150px}
  body.is-lk .stat-chip-pct{font-size:27px}
  body.is-lk .stat-chip-label{font-size:13.5px}
  body.is-lk .stat-chip-sub{font-size:12.5px}
  body.is-lk .stat-verdict{font-size:15.5px;padding:14px 18px}
  body.is-lk .week-snapshot{padding:16px 20px}
  body.is-lk .week-snapshot .ws-figure b{font-size:23px}
  body.is-lk .week-snapshot .ws-figure span{font-size:13px}
  body.is-lk .week-bar-row{grid-template-columns:96px 1fr 96px}
  body.is-lk .week-bar-row .wb-range,body.is-lk .week-bar-row .wb-total{font-size:14px}
  body.is-lk .week-bar-track{height:12px}
  body.is-lk .pain-box h3{font-size:19px}
  body.is-lk .hint{font-size:13.5px}
}

/* ══════════════════════════════════════════════════════════════════════
   Личный кабинет ученика/родителя — вкладки сверху вместо боковой панели
   (2026-09-01, замена §«Оболочка админки» специально для lk_base.html —
   admin_base.html/teacher_base.html не трогаем, у них 8+/4+ разделов и своя
   логика). Заодно двухколоночная раскладка профиля (2026-08-27) снята —
   она и была источником «двух столбиков с пустыми окнами»: правая колонка
   была короче левой почти всегда, а на широких экранах это стало заметно
   ещё сильнее. Теперь один столбец, ширину держит .wrap.lk-page ниже.
   ══════════════════════════════════════════════════════════════════════ */
body.is-app .wrap.lk-page{max-width:1120px}
/* На больших мониторах (2560px и т.п.) 1120px оставлял огромные симметричные
   поля слева и справа — хуже, чем прежняя асимметрия боковой панели
   (2026-09-01, прямой отзыв: "и слева пусто, и справа пусто"). Расширяем
   колонку ступенчато на широких экранах — карточки предметов и таблицы
   счетов/статистики реально используют освободившееся место (auto-fit
   сам достраивает колонки), а не просто раздвигают пустоту дальше. Узкие
   формы (Профиль) в саму эту ширину не растягиваются — see margin:auto
   на них в lk_profile.html, они остаются короткими и центрированными. */
@media (min-width:1440px){body.is-app .wrap.lk-page{max-width:1320px}}
@media (min-width:1800px){body.is-app .wrap.lk-page{max-width:1520px}}
@media (min-width:2300px){body.is-app .wrap.lk-page{max-width:1720px}}
.lk-tabs{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:4px}
.lk-tab{display:inline-flex;align-items:center;gap:8px;padding:11px 20px;border-radius:100px;
  color:var(--muted);text-decoration:none;font-size:14.5px;font-weight:600;white-space:nowrap;
  border:1px solid var(--stroke);transition:background .2s,color .2s,border-color .2s}
.lk-tab svg{flex-shrink:0;opacity:.8}
.lk-tab:hover{background:rgba(255,255,255,.05);color:var(--text)}
.lk-tab.is-active{background:rgba(0,98,235,.14);border-color:var(--stroke2);color:var(--text)}
.lk-tab.is-active svg{opacity:1;color:var(--blue-light)}
.lk-tab:focus-visible{outline:2px solid var(--accent2);outline-offset:2px}
.lk-tab--secondary{opacity:.75;border-style:dashed}
@media (max-width:640px){
  .lk-tabs{flex-wrap:nowrap;overflow-x:auto;scrollbar-width:none;padding:2px 20px 6px;
    margin:0 -20px 4px}
  .lk-tabs::-webkit-scrollbar{display:none}
}
.lk-unpaid-bar{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;
  margin-top:16px;padding:14px 20px;border-radius:14px;background:rgba(158,249,31,.08);
  border:1px solid var(--accent2);text-decoration:none;color:var(--text);font-size:15px;
  transition:border-color .2s}
.lk-unpaid-bar:hover{border-color:var(--accent2)}
.lk-unpaid-bar b{font-family:var(--display);color:var(--accent2)}
.lk-unpaid-bar-cta{color:var(--accent2);font-weight:700;white-space:nowrap}

.admin-table{width:100%;border-collapse:collapse;font-size:14px;min-width:640px;font-variant-numeric:tabular-nums}
.admin-table th{text-align:left;color:var(--muted);font-weight:600;font-size:12.5px;
  text-transform:uppercase;letter-spacing:.03em;padding:14px 16px;border-bottom:1px solid var(--stroke)}
.admin-table td{padding:14px 16px;border-bottom:1px solid var(--stroke);vertical-align:top}
.admin-table tbody tr:last-child td{border-bottom:none}
.admin-table tbody tr{transition:background .15s}
.admin-table tbody tr:hover{background:rgba(255,255,255,.025)}
.admin-table .muted{color:var(--muted);font-size:12.5px}
.admin-table select{background:var(--bg2);color:var(--text);border:1px solid var(--stroke);
  border-radius:8px;padding:6px 8px;font-size:13px}
/* Раньше стилизовался только type=text — email/tel/url/date/time/number
   (реестр, счета, занятия) оставались в дефолтном виде браузера, белым
   полем на тёмном фоне («жутковато»). Один общий селектор по образцу
   .field input, а не список типов — так не забудется при добавлении
   нового поля с новым type. */
.admin-table input:not([type=checkbox]):not([type=radio]),
.admin-table textarea,
/* Тот же принцип — один широкий селектор, а не список классов, которые
   забывают навесить на новую форму (это уже случалось трижды: реестр,
   занятия преподавателя, фильтры в «Пользователях»/«Реестре»/«Счетах» —
   2026-08-25). Любая форма-поиск/фильтр, обёрнутая в .admin-btn-row вне
   таблицы, получает тот же тёмный стиль полей автоматически, без класса. */
.admin-btn-row input:not([type=checkbox]):not([type=radio]):not(.btn),
.admin-btn-row textarea{
  background:var(--bg2);color:var(--text);border:1px solid var(--stroke);
  border-radius:8px;padding:7px 10px;font-size:13px;transition:border-color .2s,box-shadow .2s}
.admin-table input:not([type=checkbox]):not([type=radio]):focus,
.admin-table select:focus,.admin-table textarea:focus,
.admin-btn-row input:not([type=checkbox]):not([type=radio]):not(.btn):focus,
.admin-btn-row select:focus,.admin-btn-row textarea:focus{
  outline:none;border-color:var(--stroke2);box-shadow:0 0 0 3px rgba(0,98,235,.25)}
.admin-table input::placeholder,.admin-btn-row input::placeholder{color:var(--muted);opacity:.7}
.admin-btn-row select{background:var(--bg2);color:var(--text);border:1px solid var(--stroke);
  border-radius:8px;padding:6px 10px;font-size:13px}
.admin-btn-row{display:flex;gap:6px;flex-wrap:wrap;align-items:center}
/* Реальный баг с мобильного (2026-08-30): фильтры «Реестра» и «Зарплаты»
   (<select style="flex:0 1 180px"> с именами преподавателей) вылезали за
   экран на 15–45px на 360–390px. flex-shrink:1 у них уже стоял инлайн-стилем
   в шаблоне, но не помогал — родное `<select>` не сжимается у'же текста
   самого длинного `<option>` (закрытый список не переносит строки), а без
   min-width:0 «автоматический минимум» flex-элемента равен именно этой
   ширине. Тот же приём, что и у `.admin-main`/заголовка кабинета выше —
   снимаем автоматический минимум, лишний текст в закрытом списке при этом
   просто аккуратно обрезается, сам список открывается и работает как обычно. */
.admin-btn-row select{min-width:0}
/* Точечные инпуты/селекты внутри .admin-btn-row вне .field и вне .admin-table
   (например строчный перенос занятия в кабинете преподавателя) — тот же
   стиль, что и у .admin-table input, просто под другим именем класса,
   раз контекст не таблица. */
.admin-inline-input,.admin-inline-select{background:var(--bg2);color:var(--text);border:1px solid var(--stroke);
  border-radius:8px;padding:6px 10px;font-size:13px;transition:border-color .2s,box-shadow .2s}
.admin-inline-input:focus,.admin-inline-select:focus{outline:none;border-color:var(--stroke2);box-shadow:0 0 0 3px rgba(0,98,235,.25)}
.admin-btn-sm{width:auto!important;height:32px!important;min-height:0!important;padding:0 12px!important;
  font-size:12px!important;line-height:1!important;display:inline-flex!important;align-items:center!important}

/* Пустая таблица — переиспользуем .empty-state, но компактнее и внутри
   контейнера таблицы, а не отдельным блоком на всю ширину страницы. */
.admin-table-wrap .empty-state{border:none;border-radius:0;background:none;padding:44px 24px}

/* Таблица счетов ученика — карточки на телефоне (2026-08-29, найдено при
   аудите оплат: на 390px .admin-table-wrap{overflow-x:auto} прятал колонки
   «Статус» и саму кнопку «Оплатить» за горизонтальным скроллом БЕЗ какой-либо
   подсказки, что там вообще есть скролл — родитель физически не видел, чем и
   как оплатить счёт с телефона). Класс narrow — только у этой таблицы
   (.orders-table), обычные .admin-table в админке специально остаются
   горизонтально прокручиваемыми таблицами — админ почти всегда на десктопе,
   переделывать не нужно и рискованно для остальных ~15 экранов админки. */
@media (max-width:700px){
  .orders-table{min-width:0}
  .orders-table thead{display:none}
  .orders-table,.orders-table tbody,.orders-table tr,.orders-table td{display:block;width:100%}
  .orders-table tr{border:1px solid var(--stroke);border-radius:16px;padding:6px 14px;margin-bottom:12px}
  .orders-table tr:last-child{margin-bottom:0}
  .orders-table td{display:flex;justify-content:space-between;align-items:flex-start;gap:14px;
    padding:10px 0;border-bottom:1px solid var(--stroke);text-align:right}
  .orders-table td:last-child{border-bottom:none;flex-direction:column;align-items:stretch;text-align:left}
  .orders-table td::before{content:attr(data-label);color:var(--muted);font-size:12px;font-weight:600;
    text-transform:uppercase;letter-spacing:.03em;text-align:left;flex-shrink:0;padding-top:2px}
  .orders-table td:last-child::before{content:none}
  .orders-table td:last-child:empty{display:none}
}

/* ══════════════════════════════════════════════════════════════════════
   Математические анимации (2026-08-22).
   На сайте была физика (маятник, оптика, цепь) и информатика (терминалы,
   бегущий код), а математика — половина направлений школы — оставалась без
   образа. Ниже: боковые схемы (парабола на осях, касательная, единичная
   окружность, треугольник Пифагора) и крупная контентная сцена, где график
   строится на координатной плоскости, а уравнение решается по шагам.
   Всё на чистом CSS/SVG: анимируются только stroke-dashoffset, transform
   и opacity — ни одного layout-свойства.
   ══════════════════════════════════════════════════════════════════════ */

/* ── боковые математические схемы ── */
.sd-axis{fill:none;stroke:rgba(255,255,255,.22);stroke-width:1}
.sd-grid{fill:none;stroke:rgba(255,255,255,.07);stroke-width:.7}
/* кривая «рисуется» пунктиром, который сходит на нет */
.sd-curve{fill:none;stroke:var(--blue-light);stroke-width:1.6;
  stroke-dasharray:150;stroke-dashoffset:150;animation:sdDraw 5.5s ease-in-out infinite}
@keyframes sdDraw{0%{stroke-dashoffset:150}45%,80%{stroke-dashoffset:0}100%{stroke-dashoffset:-150}}
/* точка бежит по параболе */
.sd-dot-move{animation:sdPara 5.5s ease-in-out infinite}
@keyframes sdPara{
  0%{transform:translate(0,0);opacity:0}
  10%{opacity:1}
  50%{transform:translate(52px,-44px)}
  90%{transform:translate(104px,0);opacity:1}
  100%{transform:translate(104px,0);opacity:0}}
/* касательная поворачивается — производная в движении */
.sd-tangent{stroke:var(--accent2);stroke-width:1.4;transform-box:view-box;transform-origin:60px 50px;
  animation:sdTangent 6s ease-in-out infinite alternate}
@keyframes sdTangent{from{transform:rotate(-32deg)}to{transform:rotate(32deg)}}
/* радиус единичной окружности крутится, проекции тянутся за ним */
.sd-radius{stroke:var(--accent2);stroke-width:1.4;transform-box:view-box;transform-origin:50px 50px;
  animation:sdSpin 9s linear infinite}
@keyframes sdSpin{to{transform:rotate(360deg)}}
.sd-tri{fill:rgba(0,98,235,.16);stroke:var(--stroke2);stroke-width:1.3}
.sd-sq{fill:rgba(158,249,31,.1);stroke:rgba(158,249,31,.45);stroke-width:1;
  animation:sdFade 5s ease-in-out infinite}
.sd-sq2{animation-delay:-2.5s}
@keyframes sdFade{0%,100%{opacity:.25}50%{opacity:.9}}

@media (prefers-reduced-motion: reduce){
  .sd-curve{animation:none;stroke-dashoffset:0}
  .sd-dot-move,.sd-tangent,.sd-radius,.sd-sq{animation:none}
}

/* ══ Контентная сцена: график строится на координатной плоскости ══ */
.mathviz{display:grid;gap:26px;margin-top:28px}
@media (min-width:980px){.mathviz{grid-template-columns:minmax(0,1.05fr) minmax(0,1fr);align-items:center}}
.mathviz-plot{background:var(--panel);border:1px solid var(--stroke);border-radius:24px;padding:18px}
.mathviz-plot svg{display:block;width:100%;height:auto}

.mv-grid{stroke:rgba(255,255,255,.06);stroke-width:1;fill:none}
.mv-axis{stroke:rgba(255,255,255,.3);stroke-width:1.4;fill:none}
.mv-tick{stroke:rgba(255,255,255,.22);stroke-width:1}
.mv-num{fill:rgba(198,207,236,.5);font-size:9px;font-family:var(--font)}
.mv-curve{fill:none;stroke-width:2.4;stroke-linecap:round;
  stroke:url(#mvGrad);stroke-dasharray:var(--len,700);stroke-dashoffset:var(--len,700);
  transition:stroke-dashoffset 1.8s cubic-bezier(.35,0,.25,1)}
.mathviz.is-in .mv-curve{stroke-dashoffset:0}
.mv-root{fill:var(--accent2);opacity:0;transform:scale(.4);transform-box:fill-box;transform-origin:center;
  transition:opacity .4s ease,transform .5s cubic-bezier(.2,1.4,.4,1)}
.mathviz.is-in .mv-root{opacity:1;transform:none}
.mathviz.is-in .mv-root--b{transition-delay:.25s}
.mv-vert{stroke:var(--accent2);stroke-width:1.2;stroke-dasharray:3 4;opacity:0;
  transition:opacity .4s ease .9s}
.mathviz.is-in .mv-vert{opacity:.6}
.mv-label{fill:var(--accent2);font-size:11px;font-weight:700;font-family:var(--font);opacity:0;
  transition:opacity .4s ease 1.1s}
.mathviz.is-in .mv-label{opacity:1}

/* пошаговое решение рядом с графиком */
.mathviz-steps{display:grid;gap:10px}
.mv-step{background:var(--panel);border:1px solid var(--stroke);border-radius:18px;padding:15px 18px;
  opacity:.3;transform:translateY(6px);
  transition:opacity .5s ease,transform .5s ease,border-color .5s ease,background .5s ease}
.mv-step.is-on{opacity:1;transform:none;border-color:var(--stroke2);background:rgba(255,255,255,.05)}
.mv-step .mv-n{display:inline-grid;place-items:center;width:24px;height:24px;border-radius:50%;
  background:rgba(0,98,235,.22);border:1px solid var(--stroke2);color:var(--blue-light);
  font-family:var(--display);font-weight:700;font-size:12px;margin-bottom:9px}
.mv-step.is-on .mv-n{background:rgba(158,249,31,.18);border-color:rgba(158,249,31,.5);color:var(--accent2)}
.mv-step p{color:var(--muted);font-size:14.5px;line-height:1.55}
.mv-f{display:block;margin-top:8px;font-family:var(--display);font-weight:700;font-size:var(--fs-1);
  color:var(--text);overflow-wrap:break-word}
.mv-step.is-final .mv-f{background:var(--grad-text);-webkit-background-clip:text;background-clip:text;color:transparent}
@media (prefers-reduced-motion: reduce){
  .mv-curve,.mv-root,.mv-vert,.mv-label,.mv-step{transition:none}
  .mv-curve{stroke-dashoffset:0}
  .mv-root{opacity:1;transform:none}
  .mv-step{opacity:1;transform:none}
}

/* ══════════════════════════════════════════════════════════════════════
   Страница «План до экзамена» (2026-08-22): таймер и калькулятор.
   Оба инструмента живут на одной странице и в одном потоке чтения:
   сколько времени осталось → что за это время реально успеть → записаться.
   Отдельных «умных» подписей не добавляем — смысл несут сами цифры.
   ══════════════════════════════════════════════════════════════════════ */

/* ── Таймер ── */
.cdown{background:var(--panel);border:1px solid var(--stroke2);border-radius:var(--r);
  padding:30px 26px;margin-top:28px}
.cdown-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:12px;margin-top:20px}
@media (max-width:560px){.cdown-grid{grid-template-columns:repeat(2,1fr)}}
.cdown-cell{background:var(--bg2);border:1px solid var(--stroke);border-radius:20px;
  padding:18px 12px;text-align:center}
.cdown-cell b{display:block;font-family:var(--display);font-weight:900;
  font-size:clamp(var(--fs-4),5vw,var(--fs-6));line-height:1;
  font-variant-numeric:tabular-nums;
  background:var(--grad-text);-webkit-background-clip:text;background-clip:text;color:transparent}
.cdown-cell span{display:block;margin-top:8px;color:var(--muted);font-size:13px;
  text-transform:lowercase;letter-spacing:.02em}
.cdown-note{color:var(--muted);font-size:14px;margin-top:18px}

/* ── Калькулятор ── */
.calc{display:grid;gap:20px;margin-top:28px}
@media (min-width:900px){.calc{grid-template-columns:minmax(0,.85fr) minmax(0,1fr);align-items:start}}
.calc-form{background:var(--panel);border:1px solid var(--stroke);border-radius:var(--r);padding:26px 24px}
.calc-row{display:grid;gap:14px;grid-template-columns:1fr 1fr}
.calc-field label{display:block;color:var(--muted);font-size:14px;margin-bottom:8px}
.calc-field input{width:100%;background:var(--bg2);color:var(--text);border:1px solid var(--stroke);
  border-radius:14px;padding:14px 16px;font-family:var(--display);font-weight:800;
  font-size:var(--fs-2);text-align:center;font-variant-numeric:tabular-nums;
  transition:border-color .2s,box-shadow .2s}
.calc-field input:focus{outline:none;border-color:var(--stroke2);box-shadow:0 0 0 3px rgba(0,98,235,.18)}
.calc-field input:focus-visible{outline:2px solid var(--accent2);outline-offset:2px}
.calc-slider{margin-top:22px}
.calc-slider label{display:flex;justify-content:space-between;align-items:baseline;
  color:var(--muted);font-size:14px;margin-bottom:10px}
.calc-slider label b{color:var(--text);font-family:var(--display);font-weight:800;font-size:var(--fs-1);
  font-variant-numeric:tabular-nums}
.calc-slider input[type=range]{width:100%;accent-color:var(--accent2);height:26px;cursor:pointer}
.calc-err{color:var(--err);font-size:14px;margin-top:14px;display:none}
.calc-err.is-on{display:block}

/* ── Результат ── */
.calc-out{background:linear-gradient(140deg,rgba(0,98,235,.16),rgba(158,249,31,.07));
  border:1px solid var(--stroke2);border-radius:var(--r);padding:28px 24px;
  display:flex;flex-direction:column;min-height:100%}
.calc-big{font-family:var(--display);font-weight:900;line-height:1.15;
  font-size:clamp(var(--fs-3),4vw,var(--fs-5));font-variant-numeric:tabular-nums}
.calc-big em{font-style:normal;background:var(--grad-text);
  -webkit-background-clip:text;background-clip:text;color:transparent}
.calc-sub{color:var(--muted);margin-top:12px;line-height:1.6}
.calc-verdict{margin-top:20px;padding-top:20px;border-top:1px solid var(--stroke);
  color:var(--text);line-height:1.65}
.calc-tag{display:inline-block;border-radius:999px;padding:6px 14px;font-size:13px;font-weight:700;
  margin-bottom:12px}
.calc-tag[data-level="calm"]{background:rgba(158,249,31,.15);border:1px solid rgba(158,249,31,.45);color:var(--accent2)}
.calc-tag[data-level="work"]{background:rgba(59,156,255,.15);border:1px solid var(--stroke2);color:var(--blue-light)}
.calc-tag[data-level="hard"]{background:rgba(255,183,77,.14);border:1px solid rgba(255,183,77,.45);color:#FFB74D}
.calc-tag[data-level="tight"]{background:rgba(255,107,107,.12);border:1px solid var(--err-stroke);color:var(--err)}
.calc-out .btn{margin-top:auto;width:auto;display:inline-flex;align-self:flex-start}
.calc-out .btn{margin-top:24px}
@media (prefers-reduced-motion: reduce){.calc-field input{transition:none}}

/* ══════════════════════════════════════════════════════════════════════
   Кабинет преподавателя и занятия ученика (2026-08-22).
   Строится из тех же токенов, что и остальной кабинет; своих цветов не
   заводим, чтобы блоки выглядели как части одного интерфейса.
   ══════════════════════════════════════════════════════════════════════ */
.lesson-list{display:grid;gap:12px;margin-top:18px}
.lesson{background:var(--panel);border:1px solid var(--stroke);border-radius:20px;padding:16px 18px;
  display:grid;gap:12px;grid-template-columns:auto 1fr auto;align-items:center;scroll-margin-top:90px}
@media (max-width:719px){.lesson{grid-template-columns:1fr;gap:10px}}
.lesson.is-now{border-color:var(--accent2);box-shadow:0 0 22px rgba(158,249,31,.14)}
.lesson.is-past{opacity:.55}
.lesson-time{font-family:var(--display);font-weight:900;font-size:var(--fs-2);
  font-variant-numeric:tabular-nums;white-space:nowrap}
.lesson-who b{display:block;font-size:var(--fs-1);font-weight:700}
.lesson-who span{display:block;color:var(--muted);font-size:13.5px;margin-top:2px}
.lesson-actions{display:flex;gap:8px;flex-wrap:wrap;align-items:center}
@media (max-width:719px){.lesson-actions{width:100%}.lesson-actions .btn{flex:1 1 140px}}

/* Кнопки «Конференция» и «Доска» — главное действие преподавателя перед
   занятием, поэтому они полноценные кнопки, а не текстовые ссылки. */
.btn-join{display:inline-flex;align-items:center;justify-content:center;gap:8px;
  min-height:44px;padding:10px 16px;border-radius:14px;text-decoration:none;
  font-weight:700;font-size:14px;white-space:nowrap;
  background:linear-gradient(92deg,#0A6AF2,#1E7CFF);color:#fff;border:1px solid rgba(140,200,255,.7);
  transition:transform .2s,box-shadow .2s}
.btn-join:hover{transform:translateY(-2px);box-shadow:0 10px 26px rgba(30,124,255,.5)}
.btn-join.is-board{background:transparent;border-color:var(--stroke2);color:var(--text)}
.btn-join.is-board:hover{background:rgba(255,255,255,.07);box-shadow:none}
.btn-join[aria-disabled="true"]{opacity:.4;pointer-events:none}
@media (prefers-reduced-motion: reduce){.btn-join{transition:none}}

.tday{background:linear-gradient(140deg,rgba(0,98,235,.16),rgba(158,249,31,.06));
  border:1px solid var(--stroke2);border-radius:var(--r);padding:24px 22px;margin-top:22px}
.tday .kicker{margin-bottom:6px}

.student-grid{display:grid;gap:14px;margin-top:20px;grid-template-columns:1fr}
@media (min-width:760px){.student-grid{grid-template-columns:repeat(2,1fr)}}
.student-card{background:var(--panel);border:1px solid var(--stroke);border-radius:24px;padding:20px}
.student-card h3{margin-bottom:2px}
.student-card .who-sub{color:var(--muted);font-size:13.5px}
.student-card form{margin-top:14px}
.student-card input[type=url],.student-card input[type=text]{width:100%;background:var(--bg2);color:var(--text);
  border:1px solid var(--stroke);border-radius:12px;padding:10px 12px;font-size:13.5px;margin-bottom:8px}
.student-card .admin-btn-row{margin-top:6px}

/* Карточка ученика (2026-08-25) — сетка «подпись сверху, значение снизу»,
   как в панели «About» у CRM-карточек контакта. Раньше три поля были
   втиснуты в .admin-stat (крупные KPI-плашки для чисел) — тире-заглушка
   «—» рендерилась там жирным дисплейным шрифтом 28px и выглядела как
   визуальный сбой, а не как «нет данных». */
/* Единый список "подпись — значение" вместо оборачивающегося грида
   (2026-09-01, по прямому отзыву — при auto-fit с нечётным числом элементов
   последний элемент повисал в одиночестве в новой строке, а сами значения
   разной "физической" высоты — обычный текст vs пилюля-бейдж — создавали
   ощущение хаоса, "одно выше, другое ниже". Простой список строк: подпись
   слева, значение справа, тонкая линия между строками — при любом числе
   элементов (1, 3, 5...) ряды выравниваются одинаково, зигзага не бывает. */
.info-grid{display:flex;flex-direction:column;margin-top:4px}
.info-item{display:flex;flex-direction:row;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:6px 16px;min-width:0;padding:11px 0;border-bottom:1px solid var(--stroke)}
.info-item:last-child{border-bottom:none}
.info-item .info-label{font-size:12.5px;color:var(--muted);text-transform:uppercase;letter-spacing:.03em;font-weight:600}
.info-item .info-value{font-size:15px;color:var(--text);overflow-wrap:break-word;text-align:right}
.info-item .info-value.is-muted{color:var(--muted)}
.pill-role{display:inline-block;border-radius:999px;padding:4px 10px;font-size:12px;font-weight:700;
  background:rgba(255,255,255,.06);border:1px solid var(--stroke);color:var(--muted)}
.pill-role.is-student{background:rgba(158,249,31,.14);border-color:rgba(158,249,31,.45);color:var(--accent2)}
.pill-role.is-accent{background:rgba(59,156,255,.14);border-color:rgba(59,156,255,.45);color:var(--blue-light)}

/* ── шапка профиля в личном кабинете (2026-08-25, по образцу карточки
   ученика в «МойКласс») — фото/заглушка + имя + контакты в одну строку. ── */
.profile-header{display:flex;gap:20px;align-items:flex-start;flex-wrap:wrap;
  background:var(--panel);border:1px solid var(--stroke);border-radius:20px;padding:var(--sp-5)}
.avatar-circle{width:84px;height:84px;border-radius:50%;flex-shrink:0;overflow:hidden;
  background:var(--bg2);border:1px solid var(--stroke);display:flex;align-items:center;justify-content:center;color:var(--muted)}
.avatar-circle img{width:100%;height:100%;object-fit:cover}
.profile-info{flex:1 1 260px;min-width:0}
.profile-info .contact-line{display:flex;align-items:center;gap:8px;color:var(--muted);font-size:14.5px;margin-top:6px}
.profile-info .contact-line svg{flex-shrink:0;color:var(--blue-light)}
@media (max-width:520px){.profile-header{gap:14px}.avatar-circle{width:64px;height:64px}}

/* Явная навигация «Обучение · Оплаты · Статистика» (2026-08-25) — три
   равнозначные, всегда видимые кнопки вместо угадывания нужной карточки. */
.lk-nav{display:flex;gap:10px;flex-wrap:wrap}
.lk-nav-item{flex:1 1 160px;display:flex;align-items:center;justify-content:center;gap:8px;
  min-height:52px;border-radius:14px;background:var(--panel);border:1px solid var(--stroke);
  color:var(--text);font-weight:700;font-size:14.5px;text-decoration:none;transition:border-color .2s,background .2s}
.lk-nav-item svg{color:var(--blue-light);flex-shrink:0}
.lk-nav-item:hover{border-color:var(--stroke2);background:var(--bg2)}
.lk-nav-item.is-disabled{opacity:.45;cursor:not-allowed}

/* Недельный календарь преподавателя (2026-08-24, вторая правка) — по
   образцу Google Calendar/Preply/iTalki: занятия и события личного
   календаря стоят на настоящей часовой оси (позиция и высота блока —
   по времени начала и длительности), а не просто списком друг под другом.
   На узких экранах дни складываются в вертикальный список (не горизонтально
   прокручиваются — этот сайт уже дважды ловил горизонтальный скролл как
   реальный баг), но часовая сетка внутри каждого дня остаётся той же. */
.week-nav{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap}
.week-nav .week-label{font-weight:700;color:var(--text)}
.week-cal{--hour-px:56px;display:flex;border:1px solid var(--stroke);border-radius:14px;overflow:hidden;background:var(--panel)}
.week-daygrid{flex:1 1 0;min-width:0;border-left:1px solid var(--stroke)}
.week-daygrid:first-child{border-left:none}
.week-daygrid.is-today{background:rgba(158,249,31,.04)}
.week-day-head{padding:10px 8px;text-align:center;border-bottom:1px solid var(--stroke)}
.week-day-head .wd-name{display:block;font-size:11px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.03em}
.week-daygrid.is-today .wd-name{color:var(--accent2)}
.week-day-head .wd-date{font-size:13px;color:var(--muted);font-variant-numeric:tabular-nums}
.week-allday{padding:4px 6px;display:flex;flex-direction:column;gap:2px;border-bottom:1px solid var(--stroke)}
.week-allday-item{font-size:10.5px;background:rgba(158,249,31,.1);border:1px solid rgba(158,249,31,.25);
  border-radius:5px;padding:2px 5px;color:var(--muted)}
.week-body{display:flex}
.week-hours{position:relative;width:0;flex-shrink:0;overflow:hidden}
.week-daygrid:first-child .week-hours{width:44px;overflow:visible}
.wh-label{position:absolute;right:6px;transform:translateY(-50%);font-size:10.5px;color:var(--muted);
  white-space:nowrap;font-variant-numeric:tabular-nums}
.week-track{position:relative;flex:1 1 0;min-width:0;height:calc(var(--grid-hours) * var(--hour-px))}
.wh-line{position:absolute;left:0;right:0;border-top:1px solid var(--stroke);pointer-events:none}
.week-now-line{position:absolute;left:0;right:0;height:2px;background:var(--accent2);
  box-shadow:0 0 8px rgba(158,249,31,.6);pointer-events:none}
.week-block{position:absolute;border-radius:6px;padding:3px 5px;font-size:10.5px;line-height:1.25;
  overflow:hidden;text-decoration:none;color:inherit;min-height:22px;box-sizing:border-box;
  font-family:inherit;text-align:left;margin:0;appearance:none;-webkit-appearance:none}
button.week-block{cursor:pointer}
.week-block b{display:block;font-size:10px;font-variant-numeric:tabular-nums;color:var(--text)}
.week-block span{display:block;color:var(--muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.week-block.is-lesson{background:rgba(0,98,235,.28);border:1px solid rgba(0,98,235,.5);transition:border-color .2s}
.week-block.is-lesson:hover{border-color:rgba(0,98,235,.9)}
.week-block.is-lesson.is-past{opacity:.5}
.week-block.is-calendar{background:rgba(158,249,31,.16);border:1px solid rgba(158,249,31,.35)}
.lesson-dialog{border:1px solid var(--stroke);border-radius:14px;background:var(--bg2);color:var(--text);
  padding:20px;max-width:420px;width:calc(100% - 40px)}
.lesson-dialog::backdrop{background:rgba(0,0,0,.6)}
.lesson-dialog h3{margin:0 0 4px}
.lesson-dialog .dlg-close{position:absolute;top:12px;right:14px;background:none;border:none;color:var(--muted);
  font-size:20px;cursor:pointer;line-height:1;padding:4px}
@media (max-width:1023px){
  /* Семь дней друг под другом на телефоне — если у каждого та же высота часа,
     что на десктопе, страница растягивается до ~9000px (найдено и исправлено
     реальным замером, не на глаз). Час на телефоне ниже: день короче, но
     сетка остаётся настоящей сеткой, а не списком. */
  .week-cal{--hour-px:30px;flex-direction:column}
  .week-daygrid{border-left:none;border-top:1px solid var(--stroke)}
  .week-daygrid:first-child{border-top:none}
  .week-hours,.week-daygrid .week-hours{width:44px;overflow:visible}
  .week-day-head{display:flex;justify-content:space-between;align-items:center;text-align:left;padding:10px 12px}
}

/* ══════════════════════════════════════════════════════════════════════
   Визуал статистики (2026-08-29) — кольцо выполнения + график результатов
   по времени. Один ряд данных на каждом графике — по правилу dataviz-скилла
   легенда не нужна (заголовок рядом уже называет, что это), но подсказка
   по наведению обязательна. Заливки — --chart-good/--chart-warn, отдельные
   от ярких --accent2/--blue-light (см. :root) — те слишком светлые для
   больших закрашенных фигур на тёмном фоне, проверено валидатором палитры.
   ══════════════════════════════════════════════════════════════════════ */
.stat-dashboard{display:flex;gap:24px;align-items:center;flex-wrap:wrap;margin-top:20px}
.status-bar{display:flex;width:100%;height:10px;border-radius:6px;overflow:hidden;margin-top:14px;background:var(--panel)}
.status-bar-seg{height:100%;transition:width .6s ease}
.status-bar-seg.is-done{background:var(--chart-good)}
.status-bar-seg.is-partial{background:var(--chart-warn)}
.status-bar-seg.is-none{background:rgba(255,255,255,.12)}
.status-bar-seg.is-before{background:repeating-linear-gradient(45deg,rgba(255,255,255,.06),rgba(255,255,255,.06) 4px,transparent 4px,transparent 8px)}
@media (prefers-reduced-motion:reduce){.status-bar-seg{transition:none}}
.donut-ring{position:relative;flex-shrink:0;width:132px;height:132px}
.donut-ring svg{width:100%;height:100%;transform:rotate(-90deg)}
.donut-ring .dr-track{stroke:rgba(255,255,255,.08)}
.donut-ring .dr-value{stroke:var(--chart-good);stroke-linecap:round;transition:stroke-dasharray 1s ease}
.donut-ring.is-warn .dr-value{stroke:var(--chart-warn)}
.donut-ring-label{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center}
.donut-ring-label b{font-family:var(--display);font-size:26px;font-weight:800;line-height:1;font-variant-numeric:tabular-nums}
.donut-ring-label span{font-size:11.5px;color:var(--muted);margin-top:4px;max-width:90px;line-height:1.25}
.stat-dashboard-figures{display:flex;gap:20px;flex-wrap:wrap}
.sd-figure b{display:block;font-family:var(--display);font-size:22px;font-weight:800;font-variant-numeric:tabular-nums}
.sd-figure span{font-size:12.5px;color:var(--muted)}
.sd-figure.is-good b{color:var(--accent2)}
.sd-figure.is-warn b{color:var(--blue-light)}

/* ══ Гора «Предел» + кольца курсов (2026-09-03, по прямой просьбе) ══════
   Слева треть — кольца курсов, справа две трети — гора. На мобильном
   переворачиваем порядок: гора первой (она и есть главный визуал, ради
   которого сюда заходят), кольца под ней горизонтальной лентой — при 83%
   мобильного трафика вертикальный столбец колец сверху отодвинул бы гору
   за пределы первого экрана. */
.stat-hero{display:grid;gap:18px;grid-template-columns:1fr}
.stat-hero-rings{display:flex;flex-direction:column;gap:10px;order:2}
.stat-hero-mountain{order:1}
@media (min-width:900px){
  .stat-hero{grid-template-columns:minmax(230px,1fr) 2fr;align-items:start}
  .stat-hero-rings{order:1}
  .stat-hero-mountain{order:2}
}

.course-ring{display:flex;align-items:center;gap:12px;width:100%;text-align:left;cursor:pointer;
  padding:12px 14px;border-radius:14px;background:var(--panel);border:1px solid var(--stroke);
  color:var(--text);font:inherit;transition:border-color .2s,background .2s,transform .2s}
.course-ring:hover{border-color:var(--stroke2);transform:translateY(-2px)}
.course-ring.is-active{border-color:var(--accent2);background:rgba(158,249,31,.06)}
.course-ring-text{display:flex;flex-direction:column;gap:1px;min-width:0}
.course-ring-pct{font-family:var(--display);font-size:20px;font-weight:800;font-variant-numeric:tabular-nums}
.course-ring-label{font-size:13px;color:var(--text);line-height:1.25;overflow-wrap:break-word}
.course-ring-sub{font-size:11.5px;color:var(--muted)}
@media (max-width:899px){
  .stat-hero-rings{flex-direction:row;overflow-x:auto;padding-bottom:4px;-webkit-overflow-scrolling:touch}
  .stat-hero-rings .kicker,.stat-hero-rings .hint{display:none}
  .course-ring{width:auto;flex-shrink:0}
}

.mountain{width:100%;height:auto;display:block;border-radius:var(--r);overflow:hidden;
  border:1px solid var(--stroke)}
.mtn-sky-top{stop-color:#0a1024}
.mtn-sky-bottom{stop-color:#111a35}
.mtn-far{fill:rgba(255,255,255,.035)}
.mtn-rock-top{stop-color:#2a3554}
.mtn-rock-bottom{stop-color:#151d33}
.mtn-snow{fill:rgba(255,255,255,.5)}
.mtn-trail{stroke:rgba(255,255,255,.28);stroke-width:2;stroke-dasharray:5 6;stroke-linecap:round}
.mtn-marker circle{fill:rgba(255,255,255,.32)}
.mtn-marker text{fill:var(--muted);font-size:12px;font-weight:600}
.mtn-base circle{fill:var(--blue-light)}
.mtn-base text{fill:var(--muted);font-size:12px;font-weight:600}
.mtn-peak text{fill:var(--text);font-family:var(--display);font-size:19px;font-weight:800;letter-spacing:.08em}
.mtn-climber{transition:transform 1.1s cubic-bezier(.22,.7,.28,1)}
.mtn-climber-dot{fill:var(--accent2)}
.mtn-climber-halo{fill:rgba(158,249,31,.22)}
@media (prefers-reduced-motion:reduce){.mtn-climber{transition:none}}

.mountain-caption{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap;margin-top:12px}
.mountain-caption b{font-family:var(--display);font-size:28px;font-weight:800;color:var(--accent2);font-variant-numeric:tabular-nums}
.mountain-caption span{font-size:14px;color:var(--muted)}

/* ══ Карточки диаграмм ═══════════════════════════════════════════════ */
/* Подзаголовок «Еженедельные варианты» внутри блока курса (2026-09-03) —
   визуально отделяет вторую половину блока от заданий курса. */
.course-subhead{margin:26px 0 14px;padding-top:18px;border-top:1px solid var(--stroke);font-size:16px}
.chart-card{background:var(--panel);border:1px solid var(--stroke);border-radius:var(--r);padding:18px 20px}
.chart-card h3{margin:0 0 4px;font-size:15px}
.chart-duo{display:grid;gap:14px;grid-template-columns:1fr;margin-top:18px}
@media (min-width:760px){.chart-duo{grid-template-columns:1fr 1fr}}

/* Недельные панели курса (2026-09-04) — три карточки в ряд на десктопе,
   столбцом на мобильном; переключатель недель над ними. */
.week-panels-nav{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;margin-top:22px}
.week-panels-nav .wp-title{font-weight:700;font-size:15px}
.week-panels-nav .wp-controls{display:flex;gap:8px}
.chart-trio{display:grid;gap:14px;grid-template-columns:1fr;margin-top:12px}
@media (min-width:900px){.chart-trio{grid-template-columns:1fr 1fr 1fr}}
.wp-list{list-style:none;display:flex;flex-direction:column;gap:8px;margin-top:12px;font-size:13.5px;color:var(--text)}
.wp-list li{padding-left:14px;position:relative;line-height:1.45}
.wp-list li::before{content:"";position:absolute;left:0;top:.55em;width:5px;height:5px;border-radius:50%;background:var(--blue-light)}
.wp-empty{margin-top:12px;font-size:13.5px}
.wp-time{margin-top:12px;font-size:13px;color:var(--muted);border-top:1px solid var(--stroke);padding-top:10px}
/* Долги (2026-09-16): панель и строка наверху — красным в палитре сайта (--err) */
.chart-card.is-debt{border-color:var(--err-stroke);background:var(--err-bg)}
.chart-card.is-debt h3{color:var(--err)}
.chart-card.is-debt .wp-list li{color:var(--err)}
.chart-card.is-debt .wp-list li::before{background:var(--err)}
.chart-card.is-debt .wp-time{border-top-color:var(--err-stroke)}
.debt-alert{display:flex;align-items:center;gap:12px;margin-top:20px;padding:14px 18px;border-radius:14px;
  background:var(--err-bg);border:1px solid var(--err-stroke);border-left:4px solid var(--err);font-size:14px;color:var(--text)}
.debt-alert .da-ic{flex-shrink:0;width:20px;height:20px;display:grid;place-items:center;color:var(--err)}
.debt-alert b{color:var(--err)}
.debt-alert a{color:var(--text);text-decoration:underline;text-decoration-color:var(--err-stroke);text-underline-offset:3px}
.debt-alert a:hover{color:var(--err)}
.course-ring-debt{font-size:11.5px;font-weight:700;color:var(--err)}
.is-good-text{color:var(--accent2)}
.is-warn-text{color:var(--blue-light)}
.recent-variants .rv-name{display:block}
.recent-variants .rv-score{display:block;font-size:12.5px;color:var(--muted);margin-top:2px}

/* Гистограмма распределения баллов */
.histogram{display:flex;align-items:flex-end;gap:10px;height:150px;margin-top:14px}
.histo-col{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:flex-end;height:100%;gap:6px}
.histo-count{font-family:var(--display);font-size:16px;font-weight:800;font-variant-numeric:tabular-nums}
.histo-bar{width:100%;border-radius:8px 8px 0 0;min-height:3px;transition:height .8s cubic-bezier(.22,.7,.28,1)}
.histo-bar.is-good{background:var(--chart-good)}
.histo-bar.is-ok{background:var(--blue-light)}
.histo-bar.is-warn{background:var(--chart-warn)}
.histo-bar.is-bad{background:var(--err)}
.histo-label{font-size:11.5px;color:var(--muted);white-space:nowrap}
@media (prefers-reduced-motion:reduce){.histo-bar{transition:none}}

/* Круговая по типам заданий */
.pie-block{display:flex;align-items:center;gap:20px;flex-wrap:wrap;margin-top:12px}
.pie{width:132px;height:132px;flex-shrink:0;transform:rotate(-90deg)}
.pie-seg{transition:stroke-dasharray .9s ease}
.pie-seg.tone-0,.pie-dot.tone-0{stroke:var(--chart-good);background:var(--chart-good)}
.pie-seg.tone-1,.pie-dot.tone-1{stroke:var(--blue-light);background:var(--blue-light)}
.pie-seg.tone-2,.pie-dot.tone-2{stroke:var(--accent2);background:var(--accent2)}
.pie-seg.tone-3,.pie-dot.tone-3{stroke:var(--chart-warn);background:var(--chart-warn)}
.pie-seg.tone-4,.pie-dot.tone-4{stroke:#8B7BD8;background:#8B7BD8}
.pie-legend{list-style:none;display:flex;flex-direction:column;gap:7px;font-size:13.5px;min-width:0}
.pie-legend li{display:flex;align-items:center;gap:8px}
.pie-dot{width:11px;height:11px;border-radius:3px;flex-shrink:0}
@media (prefers-reduced-motion:reduce){.pie-seg{transition:none}}

/* Тепловая карта активности */
/* Ячейки ограничены по ширине (2026-09-03, найдено при первом же рендере):
   без max-width flex:1 растягивал их на всю ширину карточки — на десктопе
   выходили квадраты по ~150px, и карта занимала пол-экрана вместо компактной
   сводки. Теперь сетка прижата влево и не раздувается. */
.heatmap{display:flex;flex-direction:column;gap:4px;margin-top:14px;overflow-x:auto}
.heatmap-days,.heatmap-row{display:flex;gap:4px;align-items:center;justify-content:flex-start}
.heatmap-rowlabel{width:46px;flex-shrink:0;font-size:11px;color:var(--muted);font-variant-numeric:tabular-nums}
.heatmap-day{flex:1 1 22px;min-width:22px;max-width:44px;text-align:center;font-size:11px;color:var(--muted)}
.heatmap-cell{flex:1 1 22px;min-width:22px;max-width:44px;aspect-ratio:1;border-radius:4px;background:rgba(255,255,255,.05)}
.heatmap-cell.lvl-1{background:rgba(95,165,24,.32)}
.heatmap-cell.lvl-2{background:rgba(95,165,24,.55)}
.heatmap-cell.lvl-3{background:rgba(95,165,24,.78)}
.heatmap-cell.lvl-4{background:var(--chart-good)}
.heatmap-legend{display:flex;align-items:center;gap:5px;margin-top:12px;font-size:11.5px;color:var(--muted)}
.heatmap-legend .heatmap-cell{flex:0 0 14px;min-width:14px}
/* На узких экранах ужимаем подпись недели и минимум ячейки, чтобы все семь
   дней помещались в карточку без горизонтальной прокрутки внутри неё. */
@media (max-width:520px){
  .heatmap-rowlabel{width:34px;font-size:10px}
  .heatmap-day,.heatmap-cell{min-width:16px}
  .heatmap-days,.heatmap-row{gap:3px}
}

.trend-chart{margin-top:18px;position:relative}
.trend-chart svg{width:100%;height:auto;display:block;overflow:visible}
.trend-line{fill:none;stroke:var(--chart-good);stroke-width:2.5;stroke-linejoin:round;stroke-linecap:round}
.trend-area{fill:url(#trendAreaFill);opacity:.5}
.trend-baseline{stroke:var(--stroke);stroke-width:1}
.trend-point{fill:var(--bg);stroke:var(--chart-good);stroke-width:2.5;cursor:pointer;transition:r .15s ease}
.trend-point:hover,.trend-point:focus-visible{r:6.5;outline:none}
.trend-hit{fill:transparent;cursor:pointer}
.trend-xlabel{fill:var(--muted);font-size:10.5px;font-family:var(--font)}
.trend-tooltip{position:absolute;pointer-events:none;background:var(--bg2);border:1px solid var(--stroke2);
  border-radius:10px;padding:8px 12px;font-size:12.5px;color:var(--text);white-space:nowrap;
  box-shadow:0 12px 32px rgba(0,0,0,.5);opacity:0;transform:translate(-50%,calc(-100% - 10px));transition:opacity .12s ease;z-index:5}
.trend-tooltip.is-visible{opacity:1}
.trend-tooltip b{font-variant-numeric:tabular-nums}
.trend-stop-start{stop-color:var(--chart-good);stop-opacity:.35}
.trend-stop-end{stop-color:var(--chart-good);stop-opacity:0}
@media (prefers-reduced-motion:reduce){.donut-ring .dr-value{transition:none}}

/* ══════════════════════════════════════════════════════════════════════
   Сводка «в общем формате» на /lk/homework (2026-08-31) — строка кликабельных
   плашек с процентом по каждому источнику статистики сразу под заголовком
   страницы, ведёт якорем на соответствующий подробный раздел ниже (тот же
   принцип «сначала общая картина, дальше — подробно», что уже есть внутри
   каждого раздела через donut+таблицу, только теперь виден ОДИН взгляд сразу
   по всем разделам, а не только внутри одного). Не заменяет разделы ниже,
   а даёт быстрый обзор + быстрый переход. ══════════════════════════════ */
.stat-chip-row{display:flex;gap:10px;flex-wrap:wrap}
.stat-chip{display:flex;align-items:center;gap:10px;padding:12px 18px;border-radius:14px;
  background:var(--panel);border:1px solid var(--stroke);text-decoration:none;min-width:128px;
  transition:border-color .2s,transform .2s}
.stat-chip:hover,.stat-chip:focus-visible{border-color:var(--stroke2);transform:translateY(-2px)}
.stat-chip-text{display:flex;flex-direction:column;gap:2px}
.stat-chip-pct{font-family:var(--display);font-size:22px;font-weight:800;color:var(--text);font-variant-numeric:tabular-nums}
.stat-chip-label{font-size:12px;color:var(--muted);line-height:1.3}
.stat-chip-sub{font-size:11px;color:var(--muted);margin-top:1px;opacity:.8}
.mini-ring{width:34px;height:34px;flex-shrink:0;transform:rotate(-90deg)}
.mini-ring .dr-track{stroke:rgba(255,255,255,.08)}
.mini-ring .dr-value{stroke:var(--chart-good);stroke-linecap:round;transition:stroke-dasharray 1s ease}
@media (prefers-reduced-motion:reduce){.mini-ring .dr-value{transition:none}}

/* ══════════════════════════════════════════════════════════════════════
   Чистое время по неделям — полоски вместо 9-колоночной таблицы (2026-08-31,
   найденная и исправленная проблема читаемости: таблица «неделя+итого+7 дней»
   не помещается на мобильном без горизонтального скролла и плохо считывается
   даже на десктопе). Полоска — «общий формат» (видно сразу, какая неделя
   активнее), точная раскладка по дням убрана в <details> ниже как
   «супер подробно», сама структура данных (weeks[].by_day) не изменилась. */
.week-bars{display:flex;flex-direction:column;gap:10px;margin-top:14px}
/* Колонки auto/auto, не фиксированные px (2026-08-31, найденный и
   исправленный баг): дата вида «29.06–06.07» на телефоне не помещалась в
   жёстко заданные 60px и наезжала на полоску — auto считает по реальной
   ширине текста, коллизия невозможна в принципе. */
.week-bar-row{display:grid;grid-template-columns:auto 1fr auto;align-items:center;gap:12px}
.week-bar-row .wb-range{font-size:12.5px;color:var(--muted);white-space:nowrap;font-variant-numeric:tabular-nums}
.week-bar-track{display:block;height:10px;border-radius:6px;background:rgba(255,255,255,.06);overflow:hidden}
.week-bar-fill{display:block;height:100%;border-radius:6px;background:linear-gradient(92deg,var(--blue-light),var(--accent2));
  transition:width .8s ease;min-width:2px}
.week-bar-row .wb-total{font-size:13px;font-weight:700;text-align:right;font-variant-numeric:tabular-nums;white-space:nowrap;color:var(--text)}
.week-bar-row.is-empty .wb-total{color:var(--muted);font-weight:400}
@media (max-width:480px){
  .week-bar-row{gap:8px}
  .week-bar-row .wb-range,.week-bar-row .wb-total{font-size:11.5px}
}
@media (prefers-reduced-motion:reduce){.week-bar-fill{transition:none}}

/* ══════════════════════════════════════════════════════════════════════
   Вывод одной строкой + «на этой неделе» (2026-08-31, по отзыву — «выглядит
   пусто и мало информативно») — родителю интереснее прочитать один понятный
   вывод и увидеть, занимается ли ребёнок ПРЯМО СЕЙЧАС, чем считать в уме
   разницу двух чисел в проценте, который почти не двигается день ко дню.
   ══════════════════════════════════════════════════════════════════════ */
.stat-verdict{display:flex;align-items:center;gap:10px;margin-top:16px;padding:12px 16px;
  border-radius:14px;background:var(--panel);border:1px solid var(--stroke);font-size:14px;color:var(--text)}
.stat-verdict .sv-ic{flex-shrink:0;width:20px;height:20px;display:grid;place-items:center}
.stat-verdict.is-warn{border-color:var(--err-stroke);background:var(--err-bg)}
.stat-verdict.is-warn .sv-ic{color:var(--err)}
.stat-verdict.is-good .sv-ic{color:var(--accent2)}
.stat-verdict.is-neutral .sv-ic{color:var(--blue-light)}
.stat-pill{display:inline-flex;align-items:center;gap:6px;margin-top:10px;padding:8px 14px;border-radius:10px;font-size:13px;font-weight:600}
.stat-pill.is-warn{background:var(--err-bg);color:var(--err);border:1px solid var(--err-stroke)}

/* Появление блоков статистики при скролле (2026-09-03, переделано) — раньше
   тут была чистая time-based CSS-анимация без слежения за скроллом вообще
   (сознательно, в обход .reveal/main.js: тот сидит на threshold:0.12, и на
   длинных "Долгах" 100+ строк внутри <details open> процент пересечения не
   набирался ни при какой прокрутке — блок навсегда оставался невидимым).
   Оказалось, это лечится не отказом от скролла, а порогом: threshold:0
   срабатывает от ЛЮБОГО пересечения (хотя бы 1px), независимо от высоты
   элемента — сам баг был именно в 0.12, а не в идее следить за скроллом.
   Свой наблюдатель (не трогаем общий .reveal/main.js с его 0.12 и другой
   логикой) — см. <script> в конце lk_homework.html. Класс-триггер — не
   .vis (это компонент .reveal), а свой .is-shown, чтобы не пересекаться. */
.hw-fade-in{opacity:0;transform:translateY(14px);transition:opacity .5s ease,transform .5s ease}
.hw-fade-in.is-shown{opacity:1;transform:none}
.hw-fade-in.hw-fade-d1{transition-delay:.05s}
.hw-fade-in.hw-fade-d2{transition-delay:.1s}
.hw-fade-in.hw-fade-d3{transition-delay:.15s}
.hw-fade-in.hw-fade-d4{transition-delay:.2s}
@media (prefers-reduced-motion:reduce){.hw-fade-in{transition:none;opacity:1;transform:none}}

.week-snapshot{display:flex;gap:20px;flex-wrap:wrap;align-items:center;margin-top:14px;
  padding:14px 16px;border-radius:14px;background:var(--panel);border:1px solid var(--stroke)}
.week-snapshot .ws-figure b{display:block;font-family:var(--display);font-size:20px;font-weight:800;font-variant-numeric:tabular-nums}
.week-snapshot .ws-figure span{font-size:12px;color:var(--muted)}
.week-snapshot .ws-trend{font-size:12.5px;font-weight:700;display:inline-flex;align-items:center;gap:3px}
.week-snapshot .ws-trend.is-up{color:var(--accent2)}
.week-snapshot .ws-trend.is-down{color:var(--muted)}
.week-snapshot .ws-streak b{color:var(--accent2)}

/* ── Тап-таргеты в кабинете на телефоне (2026-09-05) ───────────────────
   Почти весь трафик кабинета — родители со смартфона. Замер на 320–414px
   показал элементы ниже нормы 44px: недельная навигация статистики и
   кнопки админ-стиля (32px), а также текстовые ссылки-переходы внутри
   карточек («Подробно →», «Открыть платформу →») — 20px.
   Приём тот же, что уже применён к ссылкам подвала: inline-flex + min-height,
   только на телефоне — на десктопе с мышью 44px давали бы пустые отступы. */
@media (max-width:767px){
  .admin-btn-sm{height:auto!important;min-height:44px!important;padding:0 16px!important;font-size:13px!important}
  .card p > a, .pain-box p > a, .subject-card-foot a,
  .lk-lessons a:not(.btn), .order-card p > a{
    display:inline-flex;align-items:center;min-height:44px;
  }
  .site-nav .brand{min-height:44px;display:inline-flex;align-items:center}
}

/* Строка «Сделано: 14 домашек · 6 самостоятельных» вместо прежних абсолютных
   цифр (2026-09-05). У .sd-figure span подпись рассчитана на одно слово под
   крупным числом — здесь это целое предложение, поэтому свой размер и
   межстрочный интервал, иначе на телефоне строка растягивается вдвое. */
.sd-figure .sd-done{display:block;font-size:15px;line-height:1.45;color:var(--text)}
@media (max-width:767px){ .sd-figure .sd-done{font-size:14.5px} }

/* Ссылка на подробную статистику ученика в Moodle (2026-09-05) — стоит после
   блока вариантов, по одной на каждый курс. Оформлена карточкой-строкой, а не
   текстовой ссылкой: это переход на другой сайт, и он должен читаться как
   отдельное действие, а не как пункт внутри статистики. */
.deep-stats-link{display:flex;align-items:center;gap:14px;margin-top:20px;padding:16px 18px;
  background:var(--panel);border:1px solid var(--stroke);border-radius:20px;
  text-decoration:none;color:var(--text);transition:border-color .2s,transform .2s,background .2s}
.deep-stats-link:hover{border-color:var(--blue-light);transform:translateY(-2px)}
.deep-stats-link .dsl-ic{flex:none;display:grid;place-items:center;width:42px;height:42px;
  border-radius:12px;background:rgba(0,98,235,.16);color:var(--blue-light)}
.deep-stats-link .dsl-text{display:flex;flex-direction:column;gap:3px;min-width:0}
.deep-stats-link .dsl-text b{font-size:16px}
.deep-stats-link .dsl-text span{font-size:13.5px;color:var(--muted);line-height:1.4}
.deep-stats-link .dsl-go{margin-left:auto;flex:none;color:var(--muted);font-size:18px}
@media (max-width:767px){
  .deep-stats-link{padding:14px;gap:12px;min-height:44px}
  .deep-stats-link .dsl-ic{width:38px;height:38px}
  .deep-stats-link .dsl-text b{font-size:15px}
  .deep-stats-link .dsl-text span{font-size:13px}
}

/* Чистое время недели рядом с кольцом курса (2026-09-05). Отдельный размер:
   это второстепенная строка под «Сделано: …», не должна с ней конкурировать. */
.sd-figure .sd-time{font-size:14px;color:var(--muted);margin-top:2px}
.sd-figure .sd-time b{color:var(--text);font-family:var(--display);font-weight:800;font-size:15px}
@media (max-width:767px){ .sd-figure .sd-time{font-size:13.5px} }

/* Фильтр преподавателя на календаре (2026-09-05) — выбранная кнопка того же
   стиля активности, что уже принят в боковом меню и вкладках кабинета. */
.btn.secondary.is-active{background:rgba(0,98,235,.14);border-color:var(--stroke2);color:var(--text)}

/* Укрупнённый блок группы на календаре — тот же .week-block, но без ховера
   курсора-указателя (не кликабелен) и с пунктирной рамкой поверх цветной,
   чтобы отличаться от именного занятия на глаз, а не только по подписи. */
.week-block.is-group{cursor:default;outline:1px dashed rgba(255,255,255,.35);outline-offset:-3px}



/* Абонементы по месяцам в кабинете (2026-09-13) — карточка на учебный месяц
   вместо строки счёта: месяц читается первым, чтобы предоплата за ноябрь не
   выглядела как оплата сентября (ровно та путаница, из-за которой переделали). */
.sub-list{display:flex;flex-direction:column;gap:14px;margin-top:22px}
.sub-card{background:var(--panel);border:1px solid var(--stroke);border-radius:20px;padding:20px 22px}
.sub-card.is-current{border-color:var(--stroke2);box-shadow:0 0 0 1px rgba(0,98,235,.18)}
.sub-card.is-paid{opacity:.82}
.sub-card-head{display:flex;justify-content:space-between;align-items:flex-start;gap:16px}
/* Без capitalize (2026-09-14): заголовок теперь «Абонемент сентябрь 2026», и
   автоматическая заглавная дала бы «Абонемент Сентябрь» посреди фразы. */
.sub-month{font-family:Unbounded,Inter,sans-serif;font-weight:700;font-size:20px;line-height:1.3}
.sub-meta{font-size:13px;margin-top:3px}
.sub-amount{font-weight:700;font-size:21px;white-space:nowrap;font-variant-numeric:tabular-nums}
.sub-card-foot{display:flex;align-items:center;justify-content:space-between;gap:14px;
  flex-wrap:wrap;margin-top:16px;padding-top:14px;border-top:1px solid var(--stroke)}
.sub-pay{display:flex;align-items:center;gap:12px;flex-wrap:wrap}
.pay-consent-label{display:flex;align-items:flex-start;gap:7px;font-size:12.5px;max-width:330px}
.pay-consent-label input{margin-top:2px}

@media (max-width:767px){
  .sub-card{padding:17px}
  .sub-month{font-size:17.5px}
  .sub-amount{font-size:19px}
  .sub-card-head{gap:10px}
  .sub-card-foot{align-items:flex-start;flex-direction:column;gap:12px}
  .sub-pay{width:100%;flex-direction:column;align-items:stretch}
  .pay-consent-label{max-width:none}
  .sub-pay .pay-action{width:100%}
}

/* Состав месяца: предоплата и доплата отдельными строками (2026-09-13) */
/* ОДНА сетка на весь блок частей, строки — display:contents (2026-09-14).
   Прежде каждая часть была своим отдельным grid'ом, поэтому колонки считались в каждой
   строке заново и суммы не вставали друг под друга: у оплаченной части справа
   короткая подпись, у неоплаченной — форма оплаты. Теперь колонка суммы общая
   для всех строк, и числа выровнены по одному правому краю. */
.sub-parts{margin-top:14px;border-top:1px solid var(--stroke);padding-top:12px;
  display:grid;grid-template-columns:1fr auto;gap:10px 12px;align-items:center}
.sub-part{display:contents}
.sub-part.is-paid > *{opacity:.72}
.sub-part-sum{display:flex;align-items:center;gap:8px;justify-content:flex-end;
  font-weight:600;font-variant-numeric:tabular-nums;white-space:nowrap}
.sub-part-pay{grid-column:1/-1;display:flex;align-items:center;gap:12px;
  flex-wrap:wrap;justify-content:flex-end}
.sub-part-pay .pay-consent-label{font-size:11.5px;max-width:none;flex:1 1 240px}
@media (max-width:767px){
  .sub-part-pay{flex-direction:column;align-items:stretch;gap:6px}
  .sub-part-pay .pay-consent-label{flex:none}
  .sub-part-pay .pay-action{width:100%}
}

/* Вкладка «Абонементы»: карточка подписки ученика (2026-09-14) */
.sub-admin-list{display:flex;flex-direction:column;gap:14px;margin-top:18px}
.sub-admin-head{display:flex;justify-content:space-between;align-items:flex-start;gap:14px;flex-wrap:wrap}
.sub-admin-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(165px,1fr));
  gap:14px;margin-top:16px;padding-top:14px;border-top:1px solid var(--stroke);font-size:14px}
.sub-admin-label{font-size:11.5px;text-transform:uppercase;letter-spacing:.04em;margin-bottom:3px}
@media (max-width:767px){
  .sub-admin-grid{grid-template-columns:1fr 1fr}
}

/* Зона риска: следующий месяц выставлен, родитель видит, оплаты нет (2026-09-14) */
.sub-risk{border-left:2px solid #FFA62B;padding-left:10px;margin-left:-12px}

/* Чекбокс согласия в карточках абонемента крупнее (2026-09-14): 20×20 на
   телефоне мелковат для пальца. Правило намеренно ограничено своими
   компонентами — общий .pay-consent используется и на прежнем экране счетов,
   который трогать нельзя. */
.sub-part-pay .pay-consent,
.sub-pay .pay-consent{width:22px;height:22px;flex-shrink:0}
.sub-part-pay .pay-consent-label,
.sub-pay .pay-consent-label{gap:9px;line-height:1.45;padding:4px 0}

/* Блок из трёх согласий перед оплатой (2026-09-17): оферта, обработка
   персональных данных, рекламная рассылка. Раньше это была одна строка, и
   формы оплаты ставили её в ряд с кнопкой; трём строкам нужна своя колонка,
   поэтому кнопка уходит под них. Правило общее для обоих экранов оплаты —
   разметка согласий одна (templates/_pay_consent.html). */
.pay-consents{display:flex;flex-direction:column;gap:9px;min-width:0;flex:1 1 auto}
/* flex у самих строк обязательно сбросить: у прежней однострочной версии в
   .sub-part-pay стоял flex:1 1 240px — в строке это была ширина, а в новой
   колонке те же 240px стали базовой ВЫСОТОЙ каждой строки, и между
   чекбоксами зияли пустые экраны (поймано скриншотом, из разметки не видно). */
.pay-consents .pay-consent-label{flex:0 0 auto;max-width:none;font-size:12px;line-height:1.45}
.pay-consents .pay-consent{width:20px;height:20px;flex-shrink:0;margin-top:1px}
.pay-consents a{color:var(--blue-light)}
/* Кнопка «Оплатить» зеленеет, когда оба обязательных согласия отмечены и
   JS (main.js, sync()) снимает disabled — до этого момента она остаётся
   обычной .btn.secondary. Заливка тем же --ok, что и статус «оплачено»
   (.order-status--paid), чтобы «готово к оплате» и «оплачено» читались
   одним цветом бренда. */
.pay-action:not(:disabled){background:var(--ok);border-color:var(--ok);color:#08240a}
.pay-action:not(:disabled):hover{box-shadow:0 0 24px rgba(158,249,31,.45);border-color:var(--ok)}
.sub-pay,.sub-part-pay{flex-direction:column;align-items:stretch;gap:12px;justify-content:flex-start}
.sub-pay .pay-action,.sub-part-pay .pay-action{align-self:flex-end}
/* Колонка с согласиями и кнопкой в таблице счетов: на десктопе ей нужен
   запас ширины, чтобы три строки согласия не сжимались в лапшу, а на телефоне
   таблица превращается в карточки — там любой min-width делает строку шире
   экрана и включает горизонтальный скролл внутри обёртки (поймано скриншотом
   мобильной карточки: текст согласия обрезался справа). */
.orders-table .pay-cell{min-width:0}
@media (min-width:768px){
  .orders-table .pay-cell{min-width:300px}
}
@media (max-width:767px){
  .sub-pay .pay-action,.sub-part-pay .pay-action{align-self:stretch}
}

/* Плашка про cookie (2026-09-18). Внизу экрана, поверх мобильной липкой кнопки
   и кнопки «наверх» — пока плашка висит, они прячутся: три плавающих элемента
   в одном углу уже были реальным багом на телефоне (итерация 11). */
.cookie-bar{position:fixed;left:0;right:0;bottom:0;z-index:30;
  display:flex;align-items:center;gap:16px;
  padding:14px max(18px,env(safe-area-inset-right)) calc(14px + env(safe-area-inset-bottom)) max(18px,env(safe-area-inset-left));
  background:var(--bg2);border-top:1px solid var(--stroke);
  box-shadow:0 -10px 34px rgba(0,0,0,.35)}
.cookie-bar p{margin:0;font-size:13px;line-height:1.5;color:var(--muted);max-width:900px}
.cookie-bar a{color:var(--blue-light)}
.cookie-bar .btn{flex:none;min-width:96px}
body.cookie-bar-open .cta-dock,
body.cookie-bar-open .to-top{opacity:0;pointer-events:none}
@media (max-width:767px){
  .cookie-bar{flex-direction:column;align-items:stretch;gap:10px;padding-top:12px}
  .cookie-bar p{font-size:12.5px}
  .cookie-bar .btn{width:100%}
}

/* Отметка «оплачено» руками (2026-09-18): спрятана под раскрывающийся блок,
   внутри — подсказанные платежи и обязательная причина. Нарочно не кнопка в
   один клик: это единственное место, где деньги признаются полученными без
   подтверждения шлюза. */
.pay-mark{display:inline-block;max-width:320px}
.pay-mark > summary{cursor:pointer;list-style:none;display:inline-flex;align-items:center;
  min-height:32px;padding:0 12px;border:1px solid var(--stroke2);border-radius:10px;
  font-size:13px;color:var(--text);background:var(--panel);white-space:nowrap}
.pay-mark > summary::-webkit-details-marker{display:none}
.pay-mark[open] > summary{border-color:var(--accent2)}
.pay-mark form{margin-top:10px;display:flex;flex-direction:column;gap:8px;white-space:normal}
.pay-mark input[type=text]{width:100%}
.pay-mark-hints{display:flex;flex-direction:column;gap:5px;font-size:12.5px;line-height:1.4}
.pay-mark-hints label{display:flex;align-items:flex-start;gap:7px;cursor:pointer}
.pay-mark-hints input[type=radio]{margin-top:2px;flex:none}

/* Месяц целиком, предметы — строками внутри (2026-09-18). До этого каждая пара
   «преподаватель + предмет» рисовала свою карточку с одинаковым заголовком, и
   у ученика с тремя предметами получалось три «Абонемент сентябрь 2026». */
.sub-item{margin-top:14px;padding-top:13px;border-top:1px solid var(--stroke)}
.sub-item.is-paid{opacity:.78}
.sub-item-head{display:flex;justify-content:space-between;align-items:baseline;gap:14px;flex-wrap:wrap}
.sub-item-name{font-weight:600;font-size:15px}
.sub-item-sum{display:flex;align-items:center;gap:10px;font-weight:600;
  font-variant-numeric:tabular-nums;white-space:nowrap}
.sub-item .sub-parts{margin-top:10px}
.sub-item .sub-pay{margin-top:12px}
@media (max-width:767px){
  .sub-item-head{flex-direction:column;gap:6px}
  .sub-item-sum{width:100%;justify-content:space-between}
}
/* Внутри строки предмета согласия идут у каждой кнопки оплаты (юридически они
   часть конкретной формы и работают без JS), поэтому здесь они компактнее. */
.sub-item .pay-consents{gap:6px}
.sub-item .pay-consents .pay-consent-label{font-size:11.5px;line-height:1.4}
.sub-item .pay-consents .pay-consent{width:18px;height:18px}

/* Разбивка «Финансов» по предметам (2026-09-29): строки одного ученика
   объединены rowspan на ячейках «Ученик»/«Почта», внутри — своя строка на
   каждый предмет. Чередование фона идёт по ученику, а не по строке предмета,
   иначе несколько предметов подряд у одного человека выглядели бы как
   разные ученики. */
.admin-table tr.admin-row-alt td{background:rgba(255,255,255,.022)}

/* Плашка просмотра кабинета ученика из админки — всегда сверху, поверх шапки. */
.view-as-bar{position:sticky;top:0;z-index:1000;display:flex;flex-wrap:wrap;align-items:center;justify-content:center;
  gap:10px 16px;padding:10px 16px;background:#3a2a05;border-bottom:1px solid #FFA62B;color:#ffe2b0;font-size:14px;text-align:center}
.view-as-bar form{margin:0}
.view-as-bar .btn{width:auto;min-height:0;padding:8px 14px}

/* Ссылка на конкретный счёт из письма (/lk/orders#order-N): строка подсвечивается.
   Контур «оплата → доступ», 11.10.2026. */
.order-anchor{scroll-margin-top:90px}
tr.order-anchor:target td{background:rgba(158,249,31,.10)}
.sub-item:has(.order-anchor:target){outline:2px solid var(--accent2);outline-offset:4px;border-radius:12px}
