/* aka-gst — одна разметка, две оболочки.
   Палитра переключается на <html data-track>: work — сдержанная, play — неоновая. */

*, *::before, *::after { box-sizing: border-box; }

:root {
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  --sans: Inter, -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
  /* Ширина содержимого: было 1180 при экранах 1920 и шире — Сергей дважды
     сказал, что «всё жмётся». Ограничение осталось (бесконечная строка
     нечитаема), но выросло до 1400: практикум занимал 0.40 ширины экрана. */
  --maxw: 1400px;
  --r: 12px;

  /* Тема «Работа» — графит, один сигнальный цвет, ничего лишнего. */
  --bg: #0b0c0d;
  --bg-soft: #111315;
  --panel: #14171a;
  --line: #23282d;
  --line-soft: #1a1e21;
  --text: #eef1f3;
  --muted: #8b949c;
  --dim: #636b72;
  --accent: #5ee39b;
  --accent-dim: #1d3d2c;
  --warn: #ffb454;
  --fail: #ff6b6b;
  --glow: none;
  --card-hover: #1a1e22;
  --display: var(--sans);
  --title-weight: 600;
  --title-spacing: -0.03em;
}

html[data-track="play"] {
  /* Тема «Игры» — в стиле ACID UNO. */
  --bg: #06060a;
  --bg-soft: #0c0b14;
  --panel: #12101d;
  --line: #2a2440;
  --line-soft: #1d1930;
  --text: #f4f2ff;
  --muted: #9d95c4;
  --dim: #6d6690;
  --accent: #55f58b;
  --accent-dim: #14301f;
  --warn: #ffd23f;
  --fail: #ff4f8b;
  --neon: #9a63ff;
  --neon-2: #22e0ff;
  --glow: 0 0 24px rgba(154, 99, 255, .28);
  --card-hover: #191330;
  --title-weight: 800;
  --title-spacing: -0.04em;
}

html { color-scheme: dark; }

body {
  margin: 0;
  min-height: 100vh;
  background: var(--bg);
  color: var(--text);
  font-family: var(--sans);
  font-size: 15px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}

html[data-track="play"] body {
  background:
    radial-gradient(circle at 8% -6%, rgba(154, 99, 255, .18), transparent 34%),
    radial-gradient(circle at 96% 4%, rgba(34, 224, 255, .12), transparent 32%),
    var(--bg);
}

/* Рассказы — холодные чернила с ягодным сигналом: отдельный зал сайта,
   но не коричневая «бумага» и не копия фиолетовой игровой темы. */
html[data-track="stories"] {
  --bg: #090912;
  --bg-soft: #10101c;
  --panel: #151321;
  --line: #332b4a;
  --line-soft: #221d33;
  --text: #f8f3ff;
  --muted: #aaa0bd;
  --dim: #706680;
  --accent: #ff73b5;
  --accent-dim: #43203a;
  --warn: #ffc86b;
  --fail: #ff6f82;
  --card-hover: #20182c;
}
html[data-track="stories"] body {
  background:
    radial-gradient(circle at 92% -5%, rgba(255, 115, 181, .14), transparent 33%),
    radial-gradient(circle at 3% 16%, rgba(105, 74, 255, .10), transparent 29%),
    var(--bg);
}

a { color: inherit; }
h1, h2, h3 { margin: 0; font-weight: var(--title-weight); letter-spacing: var(--title-spacing); line-height: 1.1; }
p { margin: 0; }
ul, ol, dl { margin: 0; padding: 0; list-style: none; }

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

.skip {
  position: absolute; left: 12px; top: -60px; z-index: 20;
  padding: 10px 14px; border-radius: 8px;
  background: var(--accent); color: #08120c; font-weight: 700; text-decoration: none;
  transition: top var(--an-mikro, 150ms) var(--an-vyhod-rezkiy, ease);
}
.skip:focus { top: 12px; }

:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 4px; }

/* ── Шапка ────────────────────────────────────────────────────────── */

.topbar {
  position: sticky; top: 0; z-index: 10;
  display: flex; align-items: center; gap: 18px;
  max-width: var(--maxw); margin: 0 auto; padding: 14px 24px;
  background: color-mix(in srgb, var(--bg) 88%, transparent);
  backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--line-soft);
}

/* Знак стоит рядом с надписью, а не вместо неё. Три SVG используют один
   лаймово-фиолетовый язык, но разные силуэты; на 28 px они остаются
   различимыми, в отличие от прежней подробной растровой эмблемы. */
.brand-znak {
  height: 34px; width: 34px; margin-right: 10px;
  flex: 0 0 34px; display: block;
}
@media (max-width: 640px) { .brand-znak { height: 28px; width: 28px; flex-basis: 28px; margin-right: 8px; } }

.brand {
  display: inline-flex; align-items: center; min-width: 132px;
  font-family: var(--mono); font-size: 15px; font-weight: 700;
  letter-spacing: -.02em; text-decoration: none;
}
.brand span { color: var(--accent); }

.track-switch {
  display: flex; gap: 2px; margin-left: auto;
  padding: 3px; border: 1px solid var(--line); border-radius: 999px; background: var(--bg-soft);
}
.track-switch button {
  appearance: none; border: 0; cursor: pointer;
  display: inline-flex; align-items: center; gap: 7px;
  padding: 7px 15px; border-radius: 999px;
  background: transparent;
  /* Неактивная вкладка была приглушённо-серой и читалась как надпись, а не
     как выбор: живой человек не нашёл, как перейти к играм. Теперь она
     полноцветная, и переключатель виден как переключатель. */
  color: var(--text);
  font: inherit; font-size: 13px; font-weight: 600;
  transition: background var(--an-mikro, 150ms) linear, color var(--an-mikro, 150ms) linear;
}
/* Игры зовут ярче работы — за них и цепляется глаз. Значок геймпада
   светится акцентом даже когда вкладка не выбрана. */
.track-switch [data-track-to="play"] svg { color: var(--accent); }
.track-switch button:hover { background: var(--bg-soft); }
html[data-track="work"] .track-switch [data-track-to="work"],
html[data-track="play"] .track-switch [data-track-to="play"] {
  background: var(--accent); color: #08120c; box-shadow: var(--glow);
}
/* У выбранной вкладки значок идёт цветом подложки, иначе акцент на акценте. */
html[data-track="play"] .track-switch [data-track-to="play"] {
  /* Игровой трек светится своим цветом, а не общим зелёным. */
  background: var(--neon); color: #f6f2ff;
  box-shadow: 0 0 22px rgba(154, 99, 255, .5);
}
html[data-track="play"] .track-switch [data-track-to="play"] svg { color: #f6f2ff; }
html[data-track="stories"] .track-switch [data-track-to="stories"] {
  background: var(--accent); color: #08120c; box-shadow: 0 0 20px rgba(94, 227, 155, .3);
}

/* Выбранная вкладка чуть дышит — тем же приёмом, что полоски в шапке игр. */
@media (prefers-reduced-motion: no-preference) {
  .track-switch button[aria-pressed="true"] { animation: tabGlow 3.2s ease-in-out infinite; }
}
@keyframes tabGlow {
  0%, 100% { box-shadow: 0 0 14px rgba(94, 227, 155, .28); }
  50%      { box-shadow: 0 0 26px rgba(94, 227, 155, .55); }
}
html[data-track="play"] .track-switch button[aria-pressed="true"] {
  animation-name: tabGlowPlay;
}
@keyframes tabGlowPlay {
  0%, 100% { box-shadow: 0 0 16px rgba(154, 99, 255, .35); }
  50%      { box-shadow: 0 0 30px rgba(154, 99, 255, .65); }
}

.socials { display: flex; gap: 4px; }
.social {
  display: grid; place-items: center; width: 34px; height: 34px;
  border-radius: 9px; color: var(--muted);
  transition: color var(--an-mikro, 150ms) linear, background var(--an-mikro, 150ms) linear;
}
.social:hover { color: var(--text); background: var(--bg-soft); }
.social svg { fill: currentColor; }

/* ── Панели треков ────────────────────────────────────────────────── */

main { max-width: var(--maxw); margin: 0 auto; padding: 0 24px 80px; }
.panel { display: none; }
html[data-track="work"] .panel[data-panel="work"],
html[data-track="play"] .panel[data-panel="play"],
html[data-track="stories"] .panel[data-panel="stories"] { display: block; }

.story-lead { display:grid; grid-template-columns:minmax(0,1fr) auto; gap:18px 28px; align-items:start; margin-top:26px; padding:28px; border:1px solid var(--line); border-radius:16px; background:var(--panel); }
.story-lead h1 { margin:8px 0 12px; font-size:clamp(30px,4vw,47px); line-height:1.03; }
.story-lead p:not(.kicker) { max-width:560px; color:var(--muted); line-height:1.5; }
.story-all-link { align-self:start; padding:0; border:0; background:none; color:var(--accent); font:700 13px var(--sans); white-space:nowrap; cursor:pointer; }
.story-shelves { grid-column:1 / -1; display:grid; grid-template-columns:repeat(3, minmax(0, 1fr)); gap:12px; }
.story-lead-book { display:grid; grid-template-columns:94px minmax(0,1fr); gap:12px; align-items:center; min-height:138px; padding:10px; border:1px solid var(--line); border-radius:11px; background:none; color:var(--text); font:700 16px var(--sans); text-align:left; transition: border-color var(--an-mikro, 150ms) linear, background var(--an-mikro, 150ms) linear; cursor:pointer; }
.story-lead-book:hover, .story-lead-book:focus-visible { border-color:var(--accent); background:var(--card-hover); outline:0; }
.story-lead-book[aria-expanded="true"] { border-color:var(--accent); background:var(--accent-dim); box-shadow:0 0 22px rgba(255,115,181,.12); }
.story-lead-book img { width:94px; height:116px; object-fit:cover; border:1px solid var(--line); border-radius:7px; box-shadow:0 10px 22px rgba(0,0,0,.3); }
.story-collections { grid-column:1 / -1; }
.story-collection { display:grid; grid-template-columns:150px minmax(0, 1fr); gap:22px; margin-top:4px; padding:18px; border:1px solid var(--line); border-radius:12px; background:rgba(6,5,14,.38); scroll-margin-top:calc(var(--shapka,71px) + 16px); }
.story-collection[hidden], .story-reader-inline[hidden] { display:none; }
.story-collection > img { width:150px; height:220px; object-fit:cover; border-radius:8px; box-shadow:0 12px 28px #0006; }
.story-collection h2, .story-reader-inline h2 { margin:5px 0 8px; font-size:28px; }
.story-collection p:not(.kicker) { color:var(--muted); }
.story-collection-intro { max-width:720px; margin:14px 0 18px; padding:14px 16px; border-left:2px solid var(--accent); background:rgba(255,115,181,.05); }
.story-collection-intro p { margin:0 0 8px; }
.story-collection-intro p:last-child { margin-bottom:0; }
.story-collection-count { margin:0; }
.story-collection ol { columns:2; gap:26px; margin:18px 0 0; padding-left:20px; }
.story-collection li { break-inside:avoid; margin:0 0 7px; }
.story-collection button { padding:0; border:0; background:none; color:var(--text); font:inherit; text-align:left; cursor:pointer; }
.story-collection button:hover, .story-collection button:focus-visible { color:var(--accent); outline:0; }
.story-reader-inline { grid-column:1 / -1; margin-top:4px; padding:24px; border:1px solid var(--line); border-radius:12px; background:rgba(0,0,0,.11); }
.story-reader-inline > button { padding:0; border:0; background:none; color:var(--accent); font:700 13px var(--sans); cursor:pointer; }
.story-reader-inline > .story-to-top { font-size:24px; line-height:1; }
.story-reader-inline.is-entering { animation: story-reader-in var(--an-raskrytie, 280ms) var(--an-vyhod, cubic-bezier(.2,.8,.2,1)) both; }
.story-reader-inline.is-exiting { animation: story-reader-out var(--an-mikro, 150ms) var(--an-uhod, ease) both; }
@keyframes story-reader-in { from { opacity:0; transform:translateY(8px); } to { opacity:1; transform:none; } }
@keyframes story-reader-out { from { opacity:1; transform:none; } to { opacity:0; transform:translateY(-6px); } }
.story-reader-copy { max-width:780px; color:var(--text); font:20px/1.75 Georgia,serif; letter-spacing:.005em; }
.story-reader-copy, .story-reader-inline > h2, .story-reader-inline > .kicker { margin-inline:auto; width:100%; max-width:780px; }
.story-reader-copy p { margin:0 0 .82em; text-align:left; }
.story-reader-copy .story-break { width:38%; margin:28px 0; border:0; border-top:1px solid var(--line); }
/* Короткая страница: подвал к низу окна.
   Мерено 4 сентября на /praktikum/, десктоп 1440x900: содержимое кончалось
   на 582, подвал висел на 65% высоты, под ним 318 px фона — 35% экрана.
   Читалось как «страница оборвалась». Класс ставится точечно, а не на body
   вообще: у главной своя раскладка с панелями, и трогать её незачем. */
.page-short { display: flex; flex-direction: column; min-height: 100dvh; }
.page-short > main { flex: 1 0 auto; }
/* Кадр практикума: показывает содержание курса, а не обложку. Снят под
   353 — ширину карточки на обеих ширинах экрана. */
.course-shot { display:block; width:100%; height:auto; margin:0 0 12px; border:1px solid var(--line); border-radius:8px; }
.course-stack { margin: 10px 0 0; color: var(--muted, #97968f); font-size: 12px; line-height: 1.5; }

.story-to-top { position:fixed; right:max(32px,env(safe-area-inset-right)); bottom:max(32px,env(safe-area-inset-bottom)); z-index:30; width:48px; height:48px; display:grid; place-items:center; padding:0; border:1px solid var(--accent); border-radius:50%; background:rgba(15,12,25,.92); box-shadow:0 10px 34px #0009; color:var(--text); font:700 24px/1 var(--sans); cursor:pointer; backdrop-filter:blur(10px); }
.story-to-top:hover, .story-to-top:focus-visible { background:var(--accent-dim); outline:2px solid var(--accent); outline-offset:2px; }
.story-bar { display:flex; flex-wrap:wrap; align-items:center; gap:10px 24px; margin-top:14px; padding:13px 18px; border:1px solid var(--line); border-radius:12px; color:var(--muted); font-size:13px; }
.story-bar b { color:var(--accent); font-size:16px; }.story-bar a { margin-left:auto; color:var(--text); font-weight:700; text-decoration:none; }
@media (max-width:640px){.story-lead{grid-template-columns:1fr; padding:20px;}.story-all-link{justify-self:start;}.story-shelves{grid-template-columns:1fr;}.story-lead-book{grid-template-columns:76px 1fr;min-height:104px;}.story-lead-book img{width:76px;height:94px;}.story-collection{grid-template-columns:86px 1fr;gap:14px;padding:14px;}.story-collection > img{width:86px;height:128px;}.story-collection ol{columns:1;margin-top:14px;}.story-reader-inline{margin-inline:-36px;padding:18px;}.story-reader-copy{font-size:18px;line-height:1.72;}.story-to-top{right:max(24px,env(safe-area-inset-right));bottom:max(24px,env(safe-area-inset-bottom));}.story-bar a{margin-left:0;}}
@media (prefers-reduced-motion:reduce){.work-comics .work-put-main::after{animation:none;display:none}}
@media (prefers-reduced-motion:reduce){.story-reader-inline.is-entering,.story-reader-inline.is-exiting{animation-duration:120ms;animation-name:story-reader-fade}.story-reader-inline.is-exiting{animation-direction:reverse}@keyframes story-reader-fade{from{opacity:0}to{opacity:1}}}
.practicum-more .practicum-detail { height:auto; min-height:76px; }
.practicum-more-body[hidden] { display:none; }
.practicum-more-body:not([data-open]) { visibility:hidden; }
.practicum-more-body[data-open] { visibility:visible; }

/* ── Первый экран работы ──────────────────────────────────────────── */
/* На первом экране две реальные системы: приватный AI-шлюз и продукт с
   агентами. Практика не притворяется частью этой пары и живёт ниже. */
.work-lead {
  position: relative; overflow: hidden; margin-top: 26px; padding: 28px;
  border: 1px solid var(--line); border-radius: 16px; background: var(--panel);
}
.work-duet { display:grid; grid-template-columns:repeat(2, minmax(0,1fr)); gap:14px; align-items:stretch; }
.work-system { display:grid; grid-template-rows:auto 1fr auto; gap:16px; min-width:0; min-height:420px; padding:24px; border:1px solid var(--line); border-radius:12px; background:linear-gradient(135deg,var(--bg-soft),var(--panel)); color:var(--text); text-decoration:none; }
.work-gateway-copy, .work-dharma-copy { align-self:start; }
.work-lead h1 { margin: 8px 0 12px; max-width: 500px; font-size: clamp(30px, 4vw, 47px); line-height: 1.03; }
.work-gateway-copy > p:not(.kicker) { max-width: 500px; color: var(--muted); font-size: 15px; line-height: 1.5; }
.work-lead-fact { margin-top: 12px; color: var(--text) !important; font-size: 13px !important; }
.work-lead-fact b { color: var(--accent); }
.work-gateway-proof { position:relative; align-self:stretch; display:block; overflow:hidden; border:1px solid var(--line); border-radius:10px; background:var(--bg); color:var(--text); text-decoration:none; }
.work-gateway-proof img { display:block; width:100%; height:100%; min-height:166px; object-fit:cover; object-position:left top; filter:brightness(1.15) contrast(1.16) saturate(1.22); transition:transform var(--an-raskrytie, 280ms) var(--an-spokoyno, ease), filter var(--an-raskrytie, 280ms) linear; }
.work-gateway-proof span { display: block; padding: 8px 10px; font: 11px/1.2 var(--mono); color: var(--muted); }
.work-dharma-shot { position:relative; overflow:hidden; border:1px solid var(--line); border-radius:10px; }
/* object-position левее по просьбе Сергея 6 сентября: кадр магазина был
   сдвинут вправо, а читают его слева, рядом с текстом. Пустая правая часть
   там нормальна — это его слова. */
.work-dharma-shot img { display:block; width:100%; height:100%; min-height:166px; object-fit:cover; object-position:left center; transition:transform var(--an-raskrytie, 280ms) var(--an-spokoyno, ease), filter var(--an-raskrytie, 280ms) linear; }
.dharma-journey { position:absolute; right:10px; bottom:10px; z-index:1; display:flex; align-items:center; gap:6px; padding:7px 8px; border:1px solid #65e5b55c; border-radius:999px; background:#07120edd; color:#c8ffe9; font:600 9px/1 var(--mono); letter-spacing:.05em; text-transform:uppercase; opacity:.78; }
.dharma-journey i { display:block; width:12px; height:1px; background:linear-gradient(90deg,#65e5b5,#d3fff0); transform-origin:left center; }
.work-dharma:hover .dharma-journey, .work-dharma:focus-visible .dharma-journey { opacity:1; border-color:#65e5b5; box-shadow:0 0 18px #46dca34d; }
@media (prefers-reduced-motion:no-preference) { .work-dharma:hover .dharma-journey i, .work-dharma:focus-visible .dharma-journey i { animation:dharma-route .8s ease-in-out both; }.work-dharma:hover .dharma-journey i:nth-of-type(2), .work-dharma:focus-visible .dharma-journey i:nth-of-type(2) { animation-delay:.38s; } }
@keyframes dharma-route { from { transform:scaleX(.1); opacity:.2; } to { transform:scaleX(1); opacity:1; } }
.work-dharma-copy { display:grid; align-content:start; gap:9px; }
.work-dharma h2 { margin: 0; font-size: 25px; line-height: 1; }
.work-dharma p { margin: 0; color: var(--muted); font-size: 13px; line-height: 1.45; }
.work-system-link { align-self:end; color:var(--accent); font-size:13px; font-weight:700; text-decoration:none; }
.work-gateway-proof:hover, .work-gateway-proof:focus-visible, .work-dharma:hover, .work-dharma:focus-visible { border-color: var(--accent); outline: 0; }
.work-gateway-proof:hover img, .work-gateway-proof:focus-visible img, .work-dharma:hover img, .work-dharma:focus-visible img { transform: scale(1.035); filter: saturate(1.12); }
/* ═══ Первый экран: две колонки одной высоты ═══════════════════════════
   Правки Сергея 6 сентября 2026. Было: две карточки в ряд, справа под
   текстом Dharma пустота на пол-экрана (карточка тянулась до высоты
   соседа), а подпись под обеими читалась как относящаяся только к левой.
   Стало: слева шлюз, справа Dharma и под ней блок комикса — он и добирает
   остаток высоты, поэтому пустоты не остаётся по устройству, а не по
   подгонке. Комикс заодно стал крупнее, о чём и была просьба.            */
.work-col { display: flex; flex-direction: column; gap: 12px; min-width: 0; }
/* Сергей, 6 сентября: «опять правый блок выше левого!» — и он был прав на
   ВСЕХ ширинах, а не только на своей. Мой прежний замер смотрел на колонки
   сетки: они равны по устройству, 713 и 713, и я отчитался этим числом.
   Человек же видит, где кончается СОДЕРЖИМОЕ, а слева оно кончалось на
   35 пикселей выше: карточка была задана по содержанию, и остаток колонки
   оставался пустым, тогда как справа его добирал блок комикса.
   Теперь остаток забирает тот, кто в колонке один: слева — карточка шлюза
   (у неё внутри строка 1fr, растёт кадр отчёта), справа — блок комикса. */
/* Порядок в правой колонке задан Сергеем 6 сентября: сверху комикс — на
   уровне Gateway, — под ним Dharma. И обе стрелочные подписи должны стоять
   в одну линию: для этого подпись у обеих колонок последняя, а излишек
   высоты забирает то, что стоит выше неё. Слева это карточка шлюза, справа
   блок комикса. */
.work-col--gateway > .work-system { flex: 1 1 auto; }
.work-col--dharma > .work-system { flex: 0 0 auto; }
.work-col > .work-put { flex: 1 1 auto; }
.work-col > .work-duet-note { flex: 0 0 auto; margin-top: auto; }
/* Подпись теперь у каждой карточки своя, со стрелкой вверх — к ней.
   Третьего предложения («две разные рабочие системы, а не концепты») нет:
   его слова — «уже можно открыть исходный код и пройти по сайту». */
.work-duet-note { margin: 0; color: var(--muted); font: 12px/1.4 var(--mono); }
.work-duet-note i { font-style: normal; color: var(--accent); margin-right: 5px; }
.work-duet-note b { color: var(--text); }

/* Живое окно шлюза в карточке первого проекта.
   Свёрнуто по умолчанию: первый экран дороже, чем этот блок, и разворачивает
   его тот, кому интересно. Шаги проступают один за другим — но НЕ по таймеру
   от загрузки, а по открытию, иначе анимация играет в закрытом блоке впустую.
   Работает и без JS: <details> раскрывается сам, шаги просто появляются сразу. */
.gw-live { margin: 14px 0 0; border-top: 1px solid var(--line); padding-top: 12px; }
.gw-live > summary { cursor: pointer; list-style: none; display: inline-flex; align-items: center;
  gap: 7px; min-height: 44px; font: 600 13px/1.3 var(--sans); color: var(--text); }
.gw-live > summary::-webkit-details-marker { display: none; }
.gw-live > summary i { font-style: normal; color: var(--muted); transition: transform var(--an-mikro, 150ms) var(--an-vyhod-rezkiy, ease); }
.gw-live[open] > summary i { transform: rotate(90deg); }
.gw-live > summary:hover i { color: var(--text); }
.gw-steps { list-style: none; margin: 6px 0 0; padding: 0; display: grid; gap: 7px; }
.gw-steps li { display: grid; grid-template-columns: 15px 1fr; gap: 9px; align-items: baseline;
  font: 12.5px/1.45 var(--mono); }
.gw-steps li b { grid-column: 2; font-weight: 700; color: var(--text); }
.gw-steps li span { grid-column: 2; color: var(--muted); }
.gw-steps li::before { grid-column: 1; grid-row: 1 / span 2; font-size: 12px; line-height: 1.45; }
.gw-steps li[data-gw="ok"]::before   { content: "·"; color: var(--muted); }
.gw-steps li[data-gw="warn"]::before { content: "⚠"; color: #ffd479; }
.gw-steps li[data-gw="fix"]::before  { content: "✔"; color: #3ddc84; }
.gw-steps li[data-gw="fix"] b { color: #8ce89a; }
.gw-note { margin: 11px 0 0; font: 11.5px/1.5 var(--mono); color: var(--muted); }

/* Проступание шагов: включается классом, который ставит скрипт при открытии.
   Задержку ведёт переменная, а не :nth-child — прогонов девять, и шагов у них
   от трёх до пяти; список правил на каждый номер пришлось бы дописывать
   руками, и на шестом он бы кончился молча. */
.gw-live.igraet .gw-run:not([hidden]) .gw-steps li {
  opacity: 0; animation: gw-vhod var(--an-raskrytie, 280ms) var(--an-vyhod, ease-out) forwards;
  animation-delay: calc(var(--gw-i, 0) * .6s + .05s);
}
/* Уход перед сменой прогона: не мигание, а короткое затухание блока целиком. */
.gw-live .gw-run.uhodit { animation: gw-uhod var(--an-raskrytie, 280ms) var(--an-uhod, ease-in) forwards; }
@keyframes gw-uhod { to { opacity: 0 } }
.gw-case { margin: 9px 0 0; font: 11px/1.45 var(--mono); color: var(--muted); opacity: .85; }
/* Кому движение мешает — шаги просто стоят на месте, и смены прогонов нет:
   человек читает один целиком, а не догоняет уезжающее. */
@media (prefers-reduced-motion: reduce) {
  .gw-live.igraet .gw-run:not([hidden]) .gw-steps li { opacity: 1; animation: none; }
  .gw-live .gw-run.uhodit { animation: none; }
}
@keyframes gw-vhod { from { opacity: 0; transform: translateY(4px) } to { opacity: 1; transform: none } }
@media (prefers-reduced-motion: reduce) {
  .gw-live.igraet .gw-steps li { opacity: 1; animation: none; }
}
.work-put { display: flex; flex-direction: column; align-items: flex-start; justify-content: center;
  gap: 10px; margin: 0; padding: 18px; border: 1px solid var(--line); border-radius: 12px;
  background: linear-gradient(135deg, var(--bg-soft), var(--panel)); }
.work-put .kicker { margin: 0; }
.work-put-sub { margin: 0; color: var(--muted); font-size: 13px; line-height: 1.45; }
.work-put-links { display: flex; gap: 14px; flex-wrap: wrap; margin: 0; }
.work-put-main { font: 600 16px/1.25 var(--sans); color: var(--text); text-decoration: none;
  border: 1px solid var(--line); border-radius: 10px; padding: 12px 16px; min-height: 48px;
  display: inline-flex; align-items: center; }
.work-put-main:hover { background: var(--card-hover); }
/* Сергей: «выглядит как просто слова». Делаем настоящей кнопкой, по которой
   ИЗРЕДКА пробегает одна полоса света — период 7 с, сам блик 1.1 с, то есть
   85% времени кнопка неподвижна. Мельтешения нет намеренно: движение должно
   привлекать раз в несколько секунд, а не дёргать. */
.work-put-main { position: relative; overflow: hidden; background: var(--panel);
  box-shadow: 0 1px 0 rgba(255,255,255,.04) inset; }
.work-put-main::after {
  content: ""; position: absolute; top: 0; bottom: 0; width: 42%;
  left: -60%; pointer-events: none;
  background: linear-gradient(100deg, transparent, rgba(255,255,255,.13) 45%, transparent);
  animation: put-blik 7s ease-in-out infinite;
}
@keyframes put-blik {
  0%, 84% { left: -60%; }
  100%    { left: 130%; }
}
/* Кому движение мешает — блика нет вовсе, кнопка остаётся кнопкой. */
@media (prefers-reduced-motion: reduce) {
  .work-put-main::after { animation: none; display: none; }
}
.work-put-alt { font: 12px/1.4 var(--mono); color: var(--muted); text-decoration: none;
  border-bottom: 1px solid var(--line); }
.work-put-alt:hover { color: var(--text); }
.work-comics { align-items: stretch; justify-content: center; gap: 12px; }
.work-col--dharma > .work-comics { flex: 0 0 auto; min-height: 0; padding: 18px; }
.work-comics:hover, .work-comics:focus-within { border-color: var(--accent); box-shadow: 0 0 24px #7356d933; }
.work-comics .work-put-main { width: 100%; justify-content: space-between; }
.work-comics .work-put-main::after { animation: comics-scan 9s ease-in-out infinite; }
 .work-comics .work-put-main::after { background: linear-gradient(100deg, transparent, #b68bff 45%, transparent); }
@keyframes comics-scan { 0%, 90% { transform: translateX(-160%); opacity: 0; } 92% { opacity: 1; } 100% { transform: translateX(360%); opacity: 0; } }

/* Сигнал возвращается с прежней частотой, но не «режет» блок мгновенным взмахом. */
@media (prefers-reduced-motion: no-preference) {
  .work-lead::after {
    content: ""; position: absolute; right: -22%; bottom: 0; width: 42%; height: 2px;
    background: linear-gradient(90deg, transparent, #7356d9, #b68bff, transparent);
    animation: work-scan 3.8s cubic-bezier(.2,.7,.2,1) infinite;
  }
}
@keyframes work-scan { 0%, 69% { transform: translateX(145%); opacity: 0; } 72% { opacity: 1; } 94%, 100% { transform: translateX(-330%); opacity: 0; } }

.kicker {
  font-family: var(--mono); font-size: 11px; font-weight: 600;
  letter-spacing: .12em; text-transform: uppercase; color: var(--dim);
}

/* ── Первый экран «Работа»: отчёт прогона ─────────────────────────── */

.report {
  margin: 22px 0 0;
  border: 1px solid var(--line); border-radius: var(--r);
  background: var(--panel);
  overflow: hidden;
}

/* Первый экран продаёт понятный результат, а не стек. Доказательства никуда
   не делись: они раскрываются нативной кнопкой и доступны без JavaScript. */
.report-brief { padding: 24px 26px 20px; max-width: 720px; }
.report-brief h1 { margin: 7px 0 5px; font-size: clamp(30px, 4.4vw, 46px); }
.report-name { font-family: var(--mono); font-size: 11px; color: var(--dim); letter-spacing: .08em; }
.report-benefits { display: grid; gap: 4px; margin-top: 16px; }
.report-benefits p { color: var(--text); font-size: 16px; line-height: 1.45; }
.report-live {
  margin-top: 16px; padding: 10px 12px; border-left: 2px solid var(--warn);
  background: var(--bg-soft); color: var(--muted); font-size: 12.5px; line-height: 1.45;
}
.report-live b { color: var(--warn); }
.report-more { border-top: 1px solid var(--line); }
.report-more > summary {
  display: flex; align-items: baseline; justify-content: space-between; gap: 16px;
  min-height: 54px; padding: 14px 28px; cursor: pointer; list-style: none;
  font-family: var(--mono); font-size: 11px; letter-spacing: .1em; text-transform: uppercase; color: var(--accent);
}
.report-more > summary::-webkit-details-marker { display: none; }
.report-more > summary::after { content: '+'; flex: none; font-size: 18px; line-height: 1; }
.report-more[open] > summary::after { content: '−'; }
.report-more > summary span { color: var(--dim); font-family: var(--sans); font-size: 12px; letter-spacing: 0; text-transform: none; }
.report-more-content { border-top: 1px solid var(--line); }

.report-bar {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 11px 18px;
  border-bottom: 1px solid var(--line);
  background: var(--bg-soft);
  font-family: var(--mono); font-size: 12px; color: var(--muted);
}
.report-bar .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--accent); flex: none; }
.report-bar .dot[data-status="failed"] { background: var(--fail); }
.report-bar code { color: var(--dim); }
.report-bar .verdict {
  margin-left: auto; padding: 3px 10px; border-radius: 999px;
  background: var(--accent-dim); color: var(--accent); font-weight: 600;
}
.report-bar .verdict[data-status="failed"] { background: #3a1c1c; color: var(--fail); }

.report-grid {
  display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(0, .85fr);
  gap: 36px; padding: 30px 28px 26px;
}

.report-lede h1 { margin: 8px 0 12px; font-size: clamp(30px, 4.4vw, 46px); }
.report-lede .tagline { color: var(--text); font-size: 17px; margin-bottom: 12px; }
.report-lede .summary { color: var(--muted); font-size: 14px; }

.report-metrics { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; align-content: start; }
.metric {
  padding: 14px 15px;
  border: 1px solid var(--line); border-left: 2px solid var(--accent);
  border-radius: 10px; background: var(--bg-soft);
}
.metric[data-status="failed"] { border-left-color: var(--fail); }
.metric .value {
  display: block; font-family: var(--mono); font-size: 26px; font-weight: 700;
  letter-spacing: -.03em; line-height: 1.1;
}
.metric .label { display: block; margin-top: 4px; font-size: 13px; font-weight: 600; }
.metric .note { display: block; margin-top: 3px; font-size: 11px; color: var(--dim); line-height: 1.4; }

.report-detail {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 1px;
  background: var(--line); border-top: 1px solid var(--line);
}
.detail { padding: 18px 22px; background: var(--panel); }
.detail-head {
  font-family: var(--mono); font-size: 10px; letter-spacing: .12em;
  text-transform: uppercase; color: var(--dim); margin-bottom: 8px;
}
.detail-body { font-size: 13px; color: var(--muted); }
.detail-body b { color: var(--text); font-family: var(--mono); }
.suites li {
  display: flex; justify-content: space-between; gap: 12px;
  padding: 4px 0; font-size: 13px; color: var(--muted);
  border-bottom: 1px dashed var(--line-soft);
}
.suites li:last-child { border-bottom: 0; }
.suites b { font-family: var(--mono); color: var(--accent); font-weight: 600; }
.suites li[data-status="failed"] b { color: var(--fail); }

.report-context {
  display: grid; gap: 6px;
  padding: 14px 22px 18px; border-top: 1px solid var(--line);
  background: var(--bg-soft);
  font-size: 11.5px; line-height: 1.5; color: var(--dim);
}
.report-context b { color: var(--warn); font-family: var(--mono); font-weight: 600; }
.report-context time { font-family: var(--mono); }

/* ── Снимки экрана ───────────────────────────────────────────────── */
/* Снимок здесь не украшение, а доказательство к числам рядом: отчёт,
   консоль, урок. width/height проставлены в разметке, чтобы место под
   картинку резервировалось до загрузки и текст под ней не прыгал. */
.shot { margin: 0; }
.shot img {
  display: block; width: 100%; height: auto;
  object-fit: cover; object-position: top center;
  background: var(--bg-soft);
}
.shot figcaption { font-size: 11.5px; line-height: 1.45; color: var(--dim); }

/* На первом экране снимки показываем целиком: это продолжение отчёта,
   поэтому и сетка, и фон те же, что у «Слоёв тестов» над ними. */
.report-proof {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1px;
  background: var(--line); border-top: 1px solid var(--line);
}
.report-proof .shot { padding: 18px 22px; background: var(--panel); }
.report-proof .shot img {
  aspect-ratio: 8 / 5;
  border: 1px solid var(--line); border-radius: 8px;
}
.report-proof .shot figcaption { padding-top: 10px; }

/* На карточке снимок идёт во всю ширину — как полоса чисел над ним,
   поэтому его выводим из общего правила с полями по 20px. */
.card > .shot, .card > .card-shot-link { margin: 0; }
.card-shot-link { display:block; color:inherit; text-decoration:none; cursor:pointer; }
.card-shot-link:focus-visible { outline:2px solid var(--accent); outline-offset:4px; }
.card > .shot img, .card-shot-link .shot img {
  /* Обрезаем низ, а не верх: в интерфейсах смысл собран в шапке экрана. */
  aspect-ratio: 16 / 9;
  border-top: 1px solid var(--line); border-bottom: 1px solid var(--line);
}
/* Полоса чисел уже закрыла стык своей нижней линией — вторая дала бы 2px. */
.band + .shot img { border-top: 0; }

/* ── Прокрутка по блокам ─────────────────────────────────────────── */

/* Отступ под прилипшую шапку. Считается от её настоящей высоты, которую
   меряет app.js: константа 88 работала на десктопе (шапка 71) и полностью
   ломалась на телефоне, где шапка переносится и становится 128 — заголовок
   уезжал под неё целиком, 24 пикселя из 24. Запасное значение на случай,
   если скрипт не выполнится, взято по десктопной шапке. */
/* scroll-behavior здесь БЫЛО и убрано намеренно: любой переход по якорю
   ехал плавно, и это читается как притягивание. scroll-padding-top не
   движение, а отступ: без него якорь встаёт под липкую шапку и заголовок
   не видно. Его оставляем. */
html { scroll-padding-top: calc(var(--shapka, 71px) + 17px); }

/* Привязка прокрутки («магнит») убрана 31 августа 2026 по слову Сергея:
   «мы уже несколько раз это проговаривали». Её чинили трижды — mandatory на
   proximity, обязательную остановку, потерю привязки у длинных разделов — и
   каждый раз он снова говорил «прилипание». Симптом не уходил до конца,
   значит причина была не в настройке, а в самой привязке (правило 7м).
   Комментарий выше к тому же врал: он обещал, что магнит берёт на себя
   app.js, а в app.js никакого магнита нет — только класс is-tall.
   Смещение под прилипшую шапку осталось: оно к привязке отношения не имеет
   и нужно, чтобы заголовок не уезжал под шапку при прыжке по якорю. */

/* Проявление блока при входе в экран. Класс data-reveal ставит app.js, а не
   разметка: если скрипт не выполнится, содержимое просто останется видимым,
   а не исчезнет навсегда. */
[data-reveal] {
  opacity: 0; transform: translateY(14px);
  transition: opacity var(--an-stranica, 500ms) var(--an-vyhod, ease-out), transform var(--an-stranica, 500ms) var(--an-vyhod, ease-out);
}
[data-reveal].is-visible { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  [data-reveal] { opacity: 1; transform: none; transition: none; }
}

/* ── Общие блоки ──────────────────────────────────────────────────── */

.block { margin-top: 48px; }
/* Первому блоку панели верхний отступ не нужен: над ним только шапка сайта. */
.panel > .block:first-child { margin-top: 34px; }
/* Без заголовка блоку не нужен прежний отступ — карточки поднимаются выше. */
.block--lead { margin-top: 20px; }
.block-head--tight { margin-bottom: 14px; }
.block-head--tight .kicker + p { margin-top: 6px; }
.block-head { max-width: 720px; margin-bottom: 22px; }
.block-head h2 { margin: 8px 0 10px; font-size: clamp(22px, 2.6vw, 30px); }
.block-head > p:last-child { color: var(--muted); font-size: 14px; }

/* min(310px, 100%) вместо 310px: на экране 320 под сетку остаётся 288, и
   жёсткие 310 выпихивали карточки за край — страницу можно было сдвинуть
   вбок на 31 пиксель. Нашёл Тестер 6 сентября 2026. На широких экранах
   ничего не меняется: там 100% больше трёхсот десяти. */
.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(310px, 100%), 1fr)); gap: 14px; }

.card {
  display: flex; flex-direction: column; gap: 10px;
  padding: 0 0 20px; border: 1px solid var(--line); border-radius: var(--r);
  background: var(--panel);
  /* Без overflow: hidden — иначе карточка обрезает всплывающую панель
     «чем покрыто». Скругление верха берёт на себя сама полоса чисел. */
  transition: border-color var(--an-mikro, 150ms) linear, background var(--an-mikro, 150ms) linear, transform var(--an-mikro, 150ms) var(--an-vyhod-rezkiy, ease), box-shadow var(--an-mikro, 150ms) linear;
}
/* Отклик на наведение. Было −2 точки без тени: Тестер померил наведением
   настоящей мыши, и на карточке высотой под 900 точек две точки глаз считает
   за «ничего не произошло». Тень тут важнее сдвига — именно она читается как
   «карточка поднялась». Масштаб не берём: карточки со снимками от него мылят. */
.card:hover, .card:focus-visible {
  border-color: var(--dim); background: var(--card-hover);
  transform: translateY(-5px);
  box-shadow: 0 10px 24px -8px #0009;
}
@media (prefers-reduced-motion: reduce) {
  .card:hover, .card:focus-visible { transform: none; }
}
/* Полоса чисел и снимок идут во всю ширину карточки, остальное — с полями. */
.card > *:not(.band):not(.shot):not(.card-shot-link) { margin-left: 20px; margin-right: 20px; }
.card h3 { font-size: 19px; }
.card-title {
  color: inherit; text-decoration: none;
  background-image: linear-gradient(var(--accent), var(--accent));
  background-size: 0 1px; background-position: 0 100%; background-repeat: no-repeat;
  transition: background-size var(--an-mikro, 150ms) linear, color var(--an-mikro, 150ms) linear;
}
.card-title:hover { color: var(--accent); background-size: 100% 1px; }
/* Ссылка — само название, а не вся карточка: растянутая на карточку
   невидимая область не даёт выделить мышью текст описания. */
.card .tagline { font-size: 14px; }
.card .summary { font-size: 13px; color: var(--muted); }

/* Полоса чисел наверху — та же интонация, что у отчёта на первом экране. */
.band {
  display: grid; grid-template-columns: repeat(var(--n, 3), minmax(0, 1fr));
  gap: 1px; margin: 0 0 4px; padding: 0; overflow: hidden;
  border-radius: calc(var(--r) - 1px) calc(var(--r) - 1px) 0 0;
  background: var(--line); border-bottom: 1px solid var(--line);
}
.band[data-count="1"] { --n: 1; }
.band[data-count="2"] { --n: 2; }
/* Полоса была 80px и съедала верх карточки почти впустую: под подпись
   всегда резервировались две строки. Подписи укорочены до меток, поэтому
   резерв снят и хватает одной строки.
   В одну строку с числом подпись не ставим: при «v1.2.0» в ячейке шириной
   86px на неё не остаётся места, и она обрезалась бы многоточием — это
   прятало бы то, что она называет. */
.band > div { padding: 5px 12px 6px; background: var(--bg-soft); }
.band dt {
  font-family: var(--mono); font-size: 14px; font-weight: 700;
  letter-spacing: -.03em; line-height: 1.1; color: var(--text);
  white-space: nowrap;
}
.band dd {
  margin: 0; font-size: 9.5px; line-height: 1.2; color: var(--dim);
  white-space: nowrap;
}
.band > div[title] { cursor: help; }

.card-head { position: relative; display: flex; align-items: center; gap: 10px; padding-top: 14px; }
/* Служебная строка карточки: у .kicker своё правило, наследование его не
   перебивает, поэтому задаём размер явно. */
.card-head .kicker {
  font-size: 10px;
  /* Перенос запрещён: длинная подпись раньше уводила заголовок карточки
     на строку ниже соседних по ряду, и заметить это можно было только
     линейкой. Теперь она обрежется — видимо, а не молча. */
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0;
}
.card-marks { display: flex; align-items: center; gap: 8px; margin-left: auto; }

/* Значок «чем покрыто». Раскрывается по наведению и по касанию:
   на телефоне тап ставит фокус, и :focus-within делает то же самое. */
/* Панель прицеплена к строке заголовка, а не к самому значку: значок узкий
   и стоит не у края, поэтому на узком экране панель уходила за левый край. */
.evidence { display: inline-flex; }
.evidence-btn {
  width: 20px; height: 20px; padding: 0; cursor: pointer;
  border: 1px solid var(--accent-dim); border-radius: 50%;
  background: var(--accent-dim); color: var(--accent);
  font: 600 11px/1 var(--mono);
}
.evidence-panel {
  position: absolute; z-index: 5; top: calc(100% + 8px); right: 0; width: min(320px, 78vw);
  display: grid; gap: 8px; padding: 14px 16px;
  border: 1px solid var(--line); border-radius: 10px;
  background: var(--bg-soft); box-shadow: 0 14px 34px rgba(0, 0, 0, .5);
  /* Появление держится на visibility, а не на прозрачности: если бы
     переход по opacity не отработал, панель осталась бы невидимой в
     «видимом» состоянии — отказ, который никто не заметит. */
  visibility: hidden; pointer-events: none; transform: translateY(-4px);
  transition: transform var(--an-mikro, 150ms) var(--an-vyhod-rezkiy, ease);
}
.evidence:hover .evidence-panel,
.evidence:focus-within .evidence-panel {
  visibility: visible; pointer-events: auto; transform: none;
}
.evidence-panel b { font-size: 13px; }
.evidence-panel code {
  font-family: var(--mono); font-size: 11px; color: var(--accent);
  padding: 6px 8px; border-radius: 6px; background: var(--accent-dim);
  overflow-wrap: anywhere;
}
.evidence-panel ul { display: grid; gap: 6px; }
.evidence-panel li {
  position: relative; padding-left: 14px; font-size: 12px; line-height: 1.45; color: var(--muted);
}
.evidence-panel li::before { content: "—"; position: absolute; left: 0; color: var(--dim); }
.evidence-panel p { font-size: 12px; line-height: 1.45; color: var(--dim); border-top: 1px solid var(--line); padding-top: 8px; }

.card-top { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.index { font-family: var(--mono); font-size: 11px; color: var(--dim); }

.status {
  padding: 3px 9px; border-radius: 999px;
  font-family: var(--mono); font-size: 10px; font-weight: 600;
  letter-spacing: .06em; text-transform: uppercase;
  border: 1px solid var(--line); color: var(--muted); white-space: nowrap;
}
/* На игровой карточке метка состояния снимает рамку и фон. С ними она
   выглядела кнопкой — круглой, обведённой, в правом верхнем углу, — и
   человек жал её вместо настоящей ссылки «ЗАПУСТИТЬ», которая стоит
   ниже. Кнопка, которая ничего не делает, это баг, а не мелочь. На
   карточках работы метка остаётся как была: там она несёт версию и
   рядом нет ничего, с чем её спутать. */
.gcard .status { border: 0; background: none; padding: 0; }

.status-released, .status-live, .status-published {
  border-color: var(--accent-dim); background: var(--accent-dim); color: var(--accent);
}
.status-alpha, .status-demo { border-color: #4a3a18; background: #2a2110; color: var(--warn); }
.status-wip { color: var(--dim); }

/* Используются на /praktikum/: карточки главной перешли на .band, но
   индекс раздела по-прежнему рисует чипы. */
.chips { display: flex; flex-wrap: wrap; gap: 6px; }
.chips li {
  padding: 4px 9px; border: 1px solid var(--line); border-radius: 7px;
  background: var(--bg-soft); font-size: 11px; color: var(--muted);
}
.chips b { font-family: var(--mono); color: var(--text); }

.stack { display: flex; flex-wrap: wrap; gap: 5px; }
.stack li {
  padding: 3px 8px; border-radius: 6px; background: var(--bg-soft);
  font-family: var(--mono); font-size: 10.5px; color: var(--dim);
}

.card-links { display: flex; flex-wrap: wrap; gap: 14px; margin-top: auto; padding-top: 4px; }
.link {
  font-size: 13px; font-weight: 600; text-decoration: none; color: var(--accent);
  border-bottom: 1px solid transparent; transition: border-color var(--an-mikro, 150ms) linear;
}
.link:hover { border-bottom-color: currentColor; }
.link b { font-weight: 400; }
.link.muted { color: var(--dim); }

/* ── Переключаемые практикумы ─────────────────────────────────────── */
.practicum-tabs { display: flex; gap: 8px; margin-bottom: 10px; }
.practicum-tabs button {
  flex: 1 1 0; min-height: 48px; padding: 10px 12px; border: 1px solid var(--line);
  border-radius: 9px; background: var(--bg-soft); color: var(--muted); cursor: pointer;
  font: 600 12px/1.25 var(--sans); text-align: left;
}
.practicum-tabs button[aria-selected="true"] { border-color: var(--accent); background: var(--accent-dim); color: var(--text); }
.practicum-card {
  display: grid; gap: 10px; padding: 20px; border: 1px solid var(--line); border-radius: var(--r);
  background: var(--panel);
}
.practicum-card[hidden] { display: none; }
.practicum-card .card-head { padding-top: 0; }
.practicum-card h3 { font-size: 20px; }
.practicum-card .tagline { color: var(--text); font-size: 14px; }
.practicum-card--route { width: min(100%, 620px); grid-template-columns:120px minmax(0,1fr); align-items:center; }
.practicum-route-shot { aspect-ratio:1/1; overflow:hidden; border:1px solid var(--line); border-radius:8px; background:var(--bg); }
.practicum-route-shot img { display:block; width:100%; height:100%; object-fit:cover; object-position:top center; }
.practicum-route-copy { display:grid; gap:8px; }
.practicum-route-copy .card-links { margin-top:0; }

.practicum-teaser .practicum-card { max-width: 760px; }

.practicum-stage { min-height:min(480px,calc(100vh - var(--shapka) - 130px)); }
.practicum-stage > .practicum-card--quest { min-height:inherit; }
/* Полный экран нужен только квесту. Два маршрута с мини-снимком не должны
   оставлять под собой пустую «сцену» после переключения. */
.practicum-stage:has(.practicum-card--route:not([hidden])) { min-height:0; }
.practicum-card--quest { grid-template-columns:minmax(280px, .95fr) minmax(0, 1fr); grid-template-rows:1fr; align-items:stretch; gap:22px; overflow:hidden; background:linear-gradient(135deg,var(--accent-dim),var(--panel)); }
.practicum-quest-mark { display:grid; place-items:center; min-height:280px; overflow:hidden; border:1px solid var(--line); border-radius:9px; background:radial-gradient(circle at 50% 45%,#211542 0, var(--bg) 64%); }
.practicum-quest-mark img { display:block; width:min(82%,360px); height:auto; filter:drop-shadow(0 0 24px #7656ff55); }
.practicum-quest-copy { display:grid; align-content:center; gap:11px; }
.practicum-quest-copy h3 { margin:0; font-size:clamp(28px,4vw,42px); }
.practicum-quest-copy .tagline { font-size:17px; line-height:1.48; }
.practicum-quest-copy s { color:#c6b7ee; text-decoration-color:#9b68ff; text-decoration-thickness:2px; }
.practicum-note { margin:0; color:var(--muted); font-size:13px; line-height:1.45; }
@media (max-width:640px) { .practicum-tabs { overflow-x:auto; }.practicum-tabs button { min-width:155px; }.practicum-stage { min-height:0; }.practicum-card--quest { grid-template-columns:1fr; }.practicum-card--route { grid-template-columns:88px minmax(0,1fr); width:100%; padding:16px; }.practicum-quest-mark { min-height:220px; }.practicum-quest-mark img { width:min(72%,280px); }.practicum-quest-copy .tagline { font-size:15px; } }

/* Один главный квест, а не три одинаково тяжёлые карточки. Два практикума
   остаются рядом как инструменты, не перенося и не выдавливая QueQuest. */
/* Кадр QueQuest — во всю ширину НАД практикумами, практикумы в ряд под ним.
   Решение Сергея 4 сентября, вариант «2». Число за жалобой: строка в правой
   колонке шла по 29 символов при читаемых 45–75, потому что колонке
   оставалось 250 px. Колонки были 745 : 373, ровно два к одному.
   После перестановки строка стала 50 символов — в полосе.
   Кадр НЕ обрезаем: исходник 1280x720, то есть ровно 16:9, и при aspect-ratio
   16/9 не теряется ничего. Пробовали срезать до 1130x380 — у слогана «ТЫ
   БОЛЬШЕ НЕ НУЖЕН ЭТОЙ РАБОТЕ» отрезало верхнюю строку, пропадали шапка игры
   и подпись PALLET. Смотреть надо картинку, а не долю экрана. */
.practicum-switch .practicum-pilot-grid { display:grid; grid-template-columns:1fr; gap:18px; align-items:stretch; }
/* Блок должен занимать экран, а не больше. Замер до правки на 2000×1150:
   блок 1488 при доступных 1079 — кадр QueQuest начинался на 1055, то есть у
   самой кромки, а кнопка «Показать переход» уходила на 1648, под сгиб.
   Раскладку не меняем: одна колонка, QueQuest сверху во всю ширину — это
   выбор Сергея (вариант 2, коммит 4d19b29). Вписываем ВЫСОТУ: карточке
   QueQuest даём остаток экрана, картинка масштабируется внутрь (object-fit
   уже cover), практикумы под ней остаются нетронутыми. */
@media (min-width: 901px) {
  /* Потолок теперь на самой карточке QueQuest, а не на всей сетке. Причина:
     под ней появилось раскрытие «ещё практикумы», и потолок на сетке резал
     бы раскрытое содержимое — человек нажал бы кнопку и увидел обрезок. */
  .practicum-switch .practicum-pilot-grid {
    grid-template-rows: minmax(320px, auto) minmax(0, auto);
  }
  .practicum-switch .quequest-card { max-height: min(560px, calc(100dvh - 360px)); }
  /* Практикумы жёстко заданы в 570 (строка 831), и при нехватке места весь
     дефицит съедал кадр: он падал до 120 пикселей — полоска вместо сцены.
     Здесь отпускаем их высоту и разрешаем прокрутку внутри: кадру гарантируем
     320, остальное делим. Размен назван вслух, а не подобран молча. */
  .practicum-switch .practicum-side { height: auto; min-height: 0; }
  .practicum-switch .practicum-detail-stage { height: auto; min-height: 0; }
  /* Кадру задано соотношение 16:9, и его высоту диктует ШИРИНА, а не место.
     От этого при первой попытке рамка ужалась с 789 до 372, а кадр остался
     636 и вылез наружу — кнопка «Показать переход» уехала на 221 пиксель
     ниже своей же карточки, поверх практикумов. Поймала проверка «кнопка
     внутри карточки», а не глаз. Поэтому здесь соотношение отпускаем и
     делаем кадр гибкой строкой: он занимает остаток и масштабируется внутрь. */
  .practicum-switch .quequest-card { min-height: 0; height: 100%;
    grid-template-rows: minmax(120px, 1fr) auto; }
  .practicum-switch .quequest-visual { aspect-ratio: auto; min-height: 0; height: 100%; }
  .practicum-switch .quequest-poster,
  .practicum-switch .quequest-video { min-height: 0; height: 100%; }
}
.practicum-switch .practicum-side { display:block; }
.practicum-switch .practicum-detail-stage { display:grid; grid-template-columns:repeat(2, minmax(0,1fr)); gap:14px; }
@media (max-width:760px) { .practicum-switch .practicum-detail-stage { grid-template-columns:1fr; } }

.quequest-card { --quequest-neon:#9b5cff; --quequest-pale:#d9c9ff; display:grid; grid-template-columns:minmax(0, 1.15fr) minmax(260px, .85fr); min-height:390px; overflow:hidden; border:1px solid color-mix(in srgb, var(--quequest-neon) 72%, var(--line)); border-radius:var(--r); background:linear-gradient(135deg, #1b1030, var(--panel) 64%); box-shadow:0 0 0 1px color-mix(in srgb, var(--quequest-neon) 14%, transparent), 0 18px 54px rgba(33, 15, 65, .32); }
.quequest-visual { position:relative; display:block; min-height:0; overflow:hidden; border:0; border-right:1px solid var(--line); padding:0; background:#080a0f; cursor:pointer; text-align:left; }
.quequest-poster, .quequest-video { display:block; width:100%; height:100%; min-height:390px; object-fit:cover; object-position:center; }
.quequest-video { position:absolute; inset:0; opacity:0; transition: opacity var(--an-mikro, 150ms) linear; }
.quequest-visual.is-playing .quequest-video, .quequest-visual.is-finished .quequest-video { opacity:1; }
.quequest-play-label { position:absolute; left:14px; bottom:14px; z-index:2; display:inline-flex; gap:8px; align-items:center; padding:8px 10px; border:1px solid #cbb9ff66; border-radius:999px; background:#0d0a1cdd; color:#f1edff; font:600 11px/1 var(--mono); letter-spacing:.04em; }
.quequest-visual:hover .quequest-play-label, .quequest-visual:focus-visible .quequest-play-label { border-color:#cbb9ff; box-shadow:0 0 18px #8063e955; }
.quequest-copy { display:grid; align-content:center; gap:12px; padding:26px; }
.quequest-heading { display:flex; align-items:center; gap:9px; }
.quequest-heading .kicker { margin:0; }
.quequest-copy h3 { margin:0; font-size:clamp(31px, 4vw, 48px); line-height:1; }
.quequest-copy .tagline { margin:0; color:var(--text); font-size:16px; line-height:1.48; }
.quequest-copy .practicum-note { margin:0; }
.practicum-side { display:grid; grid-template-rows:auto minmax(0,1fr); min-width:0; }
.practicum-switch .practicum-tabs { display:grid; grid-template-columns:1fr; gap:7px; margin:0 0 10px; overflow:visible; }
.practicum-switch .practicum-tabs button { min-width:0; min-height:52px; padding:9px 11px; text-align:left; }
.practicum-detail-stage { min-height:0; }
.practicum-detail { display:grid; grid-template-columns:76px minmax(0,1fr); gap:13px; height:100%; min-height:278px; padding:16px; border:1px solid var(--line); border-radius:var(--r); background:var(--panel); }
.practicum-detail[hidden] { display:none; }
.practicum-detail-shot { align-self:start; aspect-ratio:1/1; overflow:hidden; border:1px solid var(--line); border-radius:8px; background:var(--bg); }
.practicum-detail-shot img { display:block; width:100%; height:100%; object-fit:cover; object-position:top center; }
/* На узких экранах кадр в карточке практикума — квадрат 76 px, в котором
   ничего не разглядеть, а текст ужимается до 245 px и идёт по 28 символов в
   строке при читаемых 45–75. Ставим кадр НАД текстом и меняем пропорцию на
   16:9: он становится настоящей картинкой, а строка получает полную ширину.
   Замерено 4 сентября на 390 px. */
@media (max-width:640px) {
  .practicum-detail { grid-template-columns:1fr; }
  .practicum-detail-shot { aspect-ratio:16/9; }
}
.practicum-detail-copy { display:grid; align-content:start; gap:8px; min-width:0; }
.practicum-detail-copy .card-head { display:block; padding:0; }
.practicum-detail-copy .status { display:none; }
.practicum-detail-copy h3 { margin:0; font-size:18px; line-height:1.16; }
.practicum-detail-copy .tagline { margin:0; font-size:13px; line-height:1.43; }
.practicum-detail-copy .metric-chips { gap:5px; }
.practicum-detail-copy .metric-chip { padding:4px 6px; font-size:10px; }
.practicum-detail-copy .card-links { margin-top:2px; }
@media (min-width:901px) { .quequest-card { grid-template-columns:1fr; grid-template-rows:auto minmax(0,1fr); height:auto; min-height:0; }.quequest-visual { border-right:0; border-bottom:1px solid var(--line); aspect-ratio:16/9; }.quequest-poster, .quequest-video { min-height:0; }.quequest-copy { align-content:center; gap:8px; padding:16px 20px; }.quequest-copy .tagline { font-size:15px; line-height:1.4; }.practicum-side { height:570px; }.practicum-detail-stage { height:456px; }.practicum-detail { overflow:auto; } }
@media (max-width:900px) { .practicum-switch .practicum-pilot-grid { grid-template-columns:1fr; }.practicum-side { grid-template-columns:minmax(180px, .72fr) minmax(0, 1.28fr); gap:10px; }.practicum-switch .practicum-tabs { margin:0; }.practicum-detail { min-height:0; }.quequest-card { min-height:0; } }
@media (max-width:640px) { .quequest-card { grid-template-columns:1fr; }.quequest-visual { border-right:0; border-bottom:1px solid var(--line); aspect-ratio:16/9; }.quequest-poster, .quequest-video { min-height:0; }.quequest-copy { padding:18px; }.quequest-copy .tagline { font-size:14px; }.practicum-side { grid-template-columns:1fr; }.practicum-switch .practicum-tabs { grid-template-columns:1fr 1fr; }.practicum-switch .practicum-tabs button { min-height:50px; font-size:11px; }.practicum-detail { min-height:0; }.practicum-detail-copy .metric-chips { display:none; }.quequest-play-label { left:10px; bottom:10px; } }
@media (prefers-reduced-motion:reduce) { .quequest-video { display:none; }.quequest-play-label::before { content:"Кадр"; }.quequest-play-label { cursor:default; } }

/* ── Заявка о партнёрстве ─────────────────────────────────────────── */
.partner-form {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px;
  max-width: 760px; padding: 20px; border: 1px solid var(--line); border-radius: var(--r); background: var(--panel);
}
.partner-form label { display: grid; gap: 6px; color: var(--muted); font-size: 13px; }
.partner-form input, .partner-form textarea {
  width: 100%; min-height: 44px; padding: 10px 11px; border: 1px solid var(--line); border-radius: 8px;
  background: var(--bg-soft); color: var(--text); font: inherit; resize: vertical;
}
.partner-form input:focus, .partner-form textarea:focus { border-color: var(--accent); outline: 0; }
.partner-message { grid-column: 1 / -1; }
.partner-form button {
  justify-self: start; min-height: 44px; padding: 10px 15px; border: 1px solid var(--accent); border-radius: 8px;
  background: var(--accent); color: #092013; font: 700 13px/1 var(--sans); cursor: pointer;
}
.partner-form button:disabled { opacity: .6; cursor: wait; }
.partner-honeypot { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
.partner-status { align-self: center; margin: 0; color: var(--muted); font-size: 13px; }
.partner-status[data-state="ok"] { color: var(--accent); }
.partner-status[data-state="error"] { color: var(--fail); }

/* ── Опыт и навыки ────────────────────────────────────────────────── */

/* Раньше разделители рисовались фоном контейнера в зазорах сетки, и
   неполный последний ряд превращался в светлый пустой блок. Теперь у каждой
   группы своя рамка, а flex растягивает остаток ряда — дыре взяться неоткуда. */
/* Навыки на телефоне занимали 719 пикселей из 844 — почти экран целиком
   на одни теги, и это при том, что весь блок «Опыт и навыки» тянется на
   3.5 экрана. Владелец: «с телефона целую страницу занимает, бессмысленно
   много… надо сильно короче либо как-то раскрываемее».

   Свёрнутый вид — пять названий групп с числами, две строки. Этого хватает,
   чтобы понять, чем человек занимается; кому нужны сами теги, тот раскроет.
   Разметка приходит РАЗВЁРНУТОЙ и сворачивается скриптом: без скриптов и
   для поисковика видны все 43 тега. */
.card-more-btn {
  margin-top: 14px; padding: 0; border: 0; background: none; cursor: pointer;
  font-family: var(--mono); font-size: 11px; letter-spacing: .12em;
  text-transform: uppercase; color: var(--neon); align-self: flex-start;
}
.card-more-btn:hover, .card-more-btn:focus-visible { color: var(--ink); }
.card-more[hidden] { display: none; }
.card-more { margin-top: 14px; }

.skills dt i {
  display: none; margin-left: 6px; font-style: normal;
  font-family: var(--mono); font-size: 11px; color: var(--dim);
}
.skills.is-folded { display: flex; flex-wrap: wrap; gap: 8px; }
.skills.is-folded .skill {
  flex: 0 0 auto; margin: 0; padding: 0; background: none; border: 0; border-radius: 0;
}
.skills.is-folded dd { display: none; }
.skills.is-folded dt {
  margin: 0; padding: 5px 10px; border: 1px solid var(--line); border-radius: 999px;
  font-size: 13px; white-space: nowrap;
}
.skills.is-folded dt i { display: inline; }
.skills-more {
  margin-top: 12px; padding: 0; border: 0; background: none; cursor: pointer;
  font-family: var(--mono); font-size: 11px; letter-spacing: .12em;
  text-transform: uppercase; color: var(--neon);
}
.skills-more:hover, .skills-more:focus-visible { color: var(--ink); }

/* Колонки навыков росли до высоты самой длинной: флекс по умолчанию
   растягивает дочерние. Отсюда у «Product Engineering» полколонки пустоты,
   а у «Надёжности AI» — под завязку. Пусть каждая колонка будет по своему
   содержимому. */
.skills { display: flex; flex-wrap: wrap; gap: 12px; align-items: flex-start; }
.skill {
  flex: 1 1 200px; min-width: 0;
  padding: 16px 18px; background: var(--panel);
  border: 1px solid var(--line); border-radius: var(--r);
}
.skill dt {
  font-family: var(--mono); font-size: 10px; letter-spacing: .12em;
  text-transform: uppercase; color: var(--accent); margin-bottom: 9px;
}
.skill dd { margin: 0; display: flex; flex-wrap: wrap; gap: 5px; }
.skill dd span {
  /* Если метка всё же не влезет в строку, переносим её пополам, а не
     оставляем на второй строке хвост в одно слово с пустым полем рядом. */
  text-wrap: balance;
  padding: 3px 8px; border-radius: 6px; background: var(--bg-soft);
  font-size: 11.5px; color: var(--muted);
}

.jobs { display: grid; gap: 1px; margin-top: 14px; background: var(--line); border: 1px solid var(--line); border-radius: var(--r); overflow: hidden; }
.job { display: grid; grid-template-columns: 170px minmax(0, 1fr); gap: 4px 24px; padding: 20px 22px; background: var(--panel); }
/* У мест работы отклика не было вовсе: `transition: all` стоял, а правила
   :hover не существовало. Подъём тут ставить нельзя по двум причинам —
   карточка не ссылка (проверено: 7 из 7 карточек проектов это <a>, а из
   шести мест работы ни одно), и лежат они плитками через зазор в 1 точку, который подъём бы разорвал. Поэтому подсветка: она говорит «вот эта
   строка», а var(--an-mikro, 150ms) var(--an-vyhod-rezkiy, ease); }
.job:hover { background: var(--card-hover); }
/* Снимок в строке опыта идёт во второй столбец, к тексту, а не в узкую
   колонку с датой: полоса журнала шириной 170 пикселей — марка, а не
   документ. Ширину ограничиваем: во всю колонку она задавила бы список. */
/* Скан показываем маркой, а не обоями: журнальный кегль на экране всё равно
   не прочитать, а 420×591 занимали 0.66 высоты экрана при четырёх строках
   текста — карточка на девять десятых состояла из картинки, единственной во
   всей ленте. Решение Сергея 31 августа: «картинка может быть маленькой, а
   при клике уже раскрываться на весь экран». */
/* Скан кликается — но ссылкой он выглядеть не должен: это картинка,
   а не текст. Убираем всё, что браузер добавляет ссылке по умолчанию. */
/* Петля поверх снимка на карточке работы. Снимок остаётся постером: он
   уже стоит и уже той же формы, поэтому переход между ними не дёргается.
   Слой появляется только когда ролик пошёл — до этого его не видно вовсе. */
.shot { position: relative; }
.shot .gclip {
  position: absolute; inset: 0; width: 100%; height: 100%;
  /* object-position ОБЯЗАН совпадать со снимком под ним (.shot img —
     top center). Он был по умолчанию 50% 50%, и cover резал два слоя
     по-разному: даже при побайтно одинаковых кадрах картинка в момент
     подмены сдвигалась. */
  object-fit: cover; object-position: top center;
  opacity: 0; pointer-events: none;
  transition: opacity var(--an-mikro, 150ms) linear;
}
.shot .gclip.is-playing { opacity: 1; }

.shot-link { display: block; color: inherit; text-decoration: none; }
.shot-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; }

.job .shot { grid-column: 2; margin-top: 10px; text-align: center; }
/* Полоса PlayStation — свидетельство, а не обложка раздела. Держим её в
   правом краю той же строки: высоту задаёт текст, снимок открывается целиком
   по клику. */
.job--with-shot { grid-template-columns: 170px minmax(0, 1fr) 56px; }
.job--with-shot .shot { grid-column: 3; grid-row: 1 / span 3; align-self: center; margin: 0; }
.job--with-shot .shot img { width: 48px; max-height: 64px; object-fit: cover; object-position: top; }
.job--publication { grid-template-columns:170px minmax(0, 1fr) 56px; min-height:0; padding:20px 22px; align-items:center; }
.job--publication .job-period { grid-row:span 2; }
.job-publication-shot { grid-column:3; grid-row:1 / span 2; justify-self:end; text-decoration:none; }
.job-publication-shot img { width:46px; height:64px; object-fit:cover; object-position:top; border-radius:5px; }
.job-publication-shot:hover img, .job-publication-shot:focus-visible img { transform:scale(1.04); box-shadow:0 8px 20px -6px #000a; }
/* Потолок по ВЫСОТЕ, а не по ширине: Глаза мерили высоты всех шести пунктов
   ленты на 375×812. Пункт с публикацией весил 709 px — выше текущей работы
   в 1.4 раза и выше диплома в 5 раз, а картинка занимала 51% экрана. Без
   картинки пункт весит 291, соседи — от 370 до 497. При 160 px пункт встаёт
   ~451: ниже текущей работы, выше короткого диплома, в общем строю.
   Выше 206 начинается «кричит» — это их граница, не наша догадка. */
.job .shot img { max-height: 132px; width: auto; margin: 0 auto; }
.job .shot a { display: block; border-radius: 8px; }
.job .shot img { transition: transform var(--an-mikro, 150ms) var(--an-vyhod-rezkiy, ease), box-shadow var(--an-mikro, 150ms) linear; }
.job .shot a:hover img, .job .shot a:focus-visible img {
  transform: scale(1.03); box-shadow: 0 8px 20px -6px #000a;
}
@media (prefers-reduced-motion: reduce) {
  .job .shot a:hover img, .job .shot a:focus-visible img { transform: none; }
}
.job .shot img { border-radius: 8px; }
/* На телефоне колонка и так узкая — ограничивать её ещё и здесь значит
   показывать журнальную полосу размером с почтовую марку. */
@media (max-width: 900px) {
  .job .shot { max-width: none; grid-column: 1 / -1; }
  .job--with-shot { grid-template-columns: minmax(0, 1fr) 56px; }
  .job--with-shot .shot { grid-column: 2; grid-row: 1 / span 3; }
  .job--with-shot .shot img { width: 48px; max-height: 64px; }
}
.job-period { grid-row: span 3; font-family: var(--mono); font-size: 12px; color: var(--dim); }
/* Пометка «свой проект» стоит под датой, а не рядом с названием: там
   она спорила бы с ним за внимание, а сказать надо тихо — это уточнение
   о происхождении строки, а не её заголовок. */
.job-own {
  display: block; margin-top: 6px; color: var(--accent);
  font-family: var(--mono); font-size: 10px; letter-spacing: .06em;
  text-transform: uppercase;
}

.job h3 { font-size: 16px; }
.job-role { font-size: 13px; color: var(--accent); }
/* Название организации ведёт на её сайт. Подчёркивание рисуем фоном, как
   у названий проектов, — чтобы ссылки на странице выглядели одинаково. */
.job-site {
  color: inherit; text-decoration: none;
  background-image: linear-gradient(var(--accent), var(--accent));
  background-size: 0 1px; background-position: 0 100%; background-repeat: no-repeat;
  transition: background-size var(--an-mikro, 150ms) linear, color var(--an-mikro, 150ms) linear;
}
.job-site:hover, .job-site:focus-visible { color: var(--accent); background-size: 100% 1px; }
.job-site b { font-weight: 400; font-size: .8em; color: var(--dim); }
.job ul { display: grid; gap: 6px; margin-top: 8px; }
.job li { position: relative; padding-left: 16px; font-size: 13px; color: var(--muted); }
.job li::before { content: "—"; position: absolute; left: 0; color: var(--dim); }

/* ── Списки проектов ──────────────────────────────────────────────── */

.list { border: 1px solid var(--line); border-radius: var(--r); overflow: hidden; }
.list li {
  display: grid; grid-template-columns: minmax(160px, 250px) minmax(0, 1fr) auto;
  gap: 8px 20px; align-items: center;
  padding: 13px 20px; border-bottom: 1px solid var(--line-soft); background: var(--panel);
}
.list li:last-child { border-bottom: 0; }
.list li:hover { background: var(--card-hover); }
.list-title { font-weight: 600; font-size: 14px; }
.list-title a { color: var(--accent); text-decoration: none; }
.list-title a:hover { text-decoration: underline; }
.list-note { font-size: 13px; color: var(--muted); }

/* Архив не должен занимать экран, пока человек не решил в него зайти. */
.project-index { border: 1px solid var(--line); border-radius: var(--r); background: var(--panel); }
.project-index summary {
  display: flex; align-items: center; justify-content: space-between; min-height: 54px; padding: 0 18px;
  cursor: pointer; color: var(--text); font-size: 14px; font-weight: 650; list-style: none;
}
.project-index summary::-webkit-details-marker { display: none; }
.project-index summary b { color: var(--accent); font-size: 17px; transition: transform var(--an-mikro, 150ms) var(--an-vyhod-rezkiy, ease); }
.project-index[open] summary { border-bottom: 1px solid var(--line); }
.project-index[open] summary b { transform: rotate(180deg); }
.project-index .list { border: 0; border-radius: 0 0 var(--r) var(--r); }

/* ── Каналы связи ─────────────────────────────────────────────────── */

.channels { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 340px)); gap: 12px; }
.channel {
  display: flex; align-items: center; gap: 14px;
  padding: 16px 18px; border: 1px solid var(--line); border-radius: var(--r);
  background: var(--panel); color: inherit; text-decoration: none;
  transition: border-color var(--an-mikro, 150ms) linear, background var(--an-mikro, 150ms) linear;
}
.channel:hover { border-color: var(--accent); background: var(--card-hover); }
.channel svg { flex: none; fill: currentColor; color: var(--muted); }
.channel:hover svg { color: var(--accent); }
.channel span { display: grid; gap: 2px; min-width: 0; }
.channel b { font-size: 15px; font-weight: 600; }
.channel i {
  font-style: normal; font-family: var(--mono); font-size: 12px; color: var(--dim);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.channel .go { margin-left: auto; color: var(--accent); font-weight: 400; }

/* ── Экран «Игры» ─────────────────────────────────────────────────── */

/* Шапка игрового экрана убрана: большое «aka-gst» дублировало название
   в шапке страницы и отодвигало карточки почти на экран вниз. */

/* Ширину колонки не трогаем: контейнер 1127px, и при 288px четвёртая игра
   уезжает на свою строку, оставляя пустое поле в пол-экрана. «Крупнее»
   даёт высота кадра — она выросла со 132 до 186px. */
.ggrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(272px, 1fr)); gap: 14px; }

/* Игровая карточка: сначала графика, потом название и описание.
   Картинку рисует CSS — отдельных файлов и загрузок нет. */
.gcard {
  display: flex; flex-direction: column; overflow: hidden;
  border: 1px solid var(--line); border-radius: 16px;
  background: linear-gradient(160deg, var(--panel), var(--bg-soft));
  text-decoration: none; color: inherit;
  transition: transform var(--an-mikro, 150ms) var(--an-vyhod-rezkiy, ease), border-color var(--an-mikro, 150ms) linear, box-shadow var(--an-mikro, 150ms) linear;
}
a.gcard:focus-visible,
a.gcard.is-lit {
  transform: translateY(-4px); border-color: var(--neon); box-shadow: var(--glow);
}

.gart {
  position: relative; display: grid; place-items: center;
  /* Кадр стал главным на карточке, поэтому полосе дано больше высоты. */
  height: 186px; flex: none; overflow: hidden;
  background: radial-gradient(circle at 50% 120%, rgba(154, 99, 255, .3), transparent 62%), var(--bg-soft);
  border-bottom: 1px solid var(--line);
}
/* Кадр из игры лежит под пипсами: они идут следом в разметке и потому
   рисуются поверх — анимация остаётся главной, картинка даёт палитру.
   На наведении она разгорается и чуть наезжает, .gart её обрезает. */
/* Кадр теперь несёт карточку один, без фигур поверх, поэтому показываем
   его в полную силу. Движение при наведении осталось прежним — оно просто
   переехало с фигур на сам снимок. */
/* Кадр в покое был приглушён до .62. На светлых играх это читалось как
   мягкость, а на тёмных — ПЕРИМЕТР, Наотмашь, ТехноМагия — кадр гас ещё на
   треть поверх собственной темноты, и карточка выглядела пустой. Разницу
   между покоем и наведением оставляем, но берём её масштабом, а не тем,
   что в покое ничего не видно. */
.gshot {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; opacity: .84; filter: saturate(1.1);
  transition: opacity var(--an-mikro, 150ms) linear, transform .35s ease;
}
a.gcard:focus-visible .gshot,
a.gcard.is-lit .gshot { opacity: 1; transform: scale(1.06); }

/* Кусок игры, который включается при наведении. Лежит поверх кадра, и кадр
   работает постером: пока ролик не начался, видно ровно ту же картинку,
   поэтому подмена не мигает. Движение то же самое, что у кадра, иначе на
   стыке был бы скачок. */
.gclip {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; opacity: 0; pointer-events: none;
  transition: opacity var(--an-raskrytie, 280ms) var(--an-vyhod, ease), transform var(--an-raskrytie, 280ms) var(--an-vyhod, ease);
}
a.gcard.is-lit .gclip { transform: scale(1.06); }
.gclip.is-playing { opacity: 1; }

.gart i {
  position: absolute; display: block;
  transition: transform var(--an-raskrytie, 280ms) var(--an-vyhod, ease), opacity var(--an-raskrytie, 280ms) var(--an-vyhod, ease);
}

/* ACID UNO — веер карт. */
[data-art="uno"] .gart i {
  width: 34px; height: 50px; border-radius: 5px;
  border: 1px solid rgba(255, 255, 255, .22);
  background: linear-gradient(150deg, var(--neon), var(--fail));
}
[data-art="uno"] .gart i:nth-child(1) { transform: rotate(-16deg) translateX(-26px); }
[data-art="uno"] .gart i:nth-child(2) { transform: translateY(-4px); background: linear-gradient(150deg, var(--neon-2), var(--neon)); }
[data-art="uno"] .gart i:nth-child(3) { transform: rotate(16deg) translateX(26px); background: linear-gradient(150deg, var(--warn), var(--accent)); }
[data-art="uno"].is-lit .gart i:nth-child(1) { transform: rotate(-26deg) translateX(-40px) translateY(-6px); }
[data-art="uno"].is-lit .gart i:nth-child(2) { transform: translateY(-14px) scale(1.06); }
[data-art="uno"].is-lit .gart i:nth-child(3) { transform: rotate(26deg) translateX(40px) translateY(-6px); }

/* Тетколор — падающая колонка из трёх кубиков. */
[data-art="cubes"] .gart i { width: 30px; height: 30px; border-radius: 5px; }
[data-art="cubes"] .gart i:nth-child(1) { transform: translateY(-32px); background: var(--fail); }
[data-art="cubes"] .gart i:nth-child(2) { background: var(--warn); }
[data-art="cubes"] .gart i:nth-child(3) { transform: translateY(32px); background: var(--accent); }
[data-art="cubes"].is-lit .gart i:nth-child(1) { transform: translateY(-4px); }
[data-art="cubes"].is-lit .gart i:nth-child(2) { transform: translateY(28px); }
[data-art="cubes"].is-lit .gart i:nth-child(3) { transform: translateY(60px); opacity: 0; }

/* КНБ — три метки по дуге. */
[data-art="rps"] .gart i { width: 34px; height: 34px; border: 2px solid var(--neon-2); }
[data-art="rps"] .gart i:nth-child(1) { border-radius: 50%; transform: translateX(-40px); }
[data-art="rps"] .gart i:nth-child(2) { border-radius: 4px; transform: rotate(45deg); }
[data-art="rps"] .gart i:nth-child(3) { border-radius: 3px; transform: translateX(40px); border-color: var(--accent); }
[data-art="rps"].is-lit .gart i:nth-child(1) { transform: translateX(-46px) translateY(-8px) scale(1.1); }
[data-art="rps"].is-lit .gart i:nth-child(2) { transform: rotate(225deg) scale(1.1); }
[data-art="rps"].is-lit .gart i:nth-child(3) { transform: translateX(46px) translateY(8px) scale(1.1); }

/* Неон Линии — шары, выстраивающиеся в линию. */
[data-art="orbs"] .gart i {
  width: 26px; height: 26px; border-radius: 50%;
  box-shadow: 0 0 14px currentColor;
}
[data-art="orbs"] .gart i:nth-child(1) { background: var(--fail); color: var(--fail); transform: translate(-34px, -14px); }
[data-art="orbs"] .gart i:nth-child(2) { background: var(--neon-2); color: var(--neon-2); transform: translate(4px, 16px); }
[data-art="orbs"] .gart i:nth-child(3) { background: var(--accent); color: var(--accent); transform: translate(38px, -6px); }
[data-art="orbs"].is-lit .gart i:nth-child(1) { transform: translate(-36px, 0); }
[data-art="orbs"].is-lit .gart i:nth-child(2) { transform: translate(0, 0); }
[data-art="orbs"].is-lit .gart i:nth-child(3) { transform: translate(36px, 0); }

/* Техномагия — три стихии. Врозь они лежат по углам, при наведении
   сходятся в одну точку: в этом вся игра. */
[data-art="elements"] .gart i {
  width: 24px; height: 24px; border-radius: 50%;
  box-shadow: 0 0 16px currentColor;
}
[data-art="elements"] .gart i:nth-child(1) { background: #ff5a1f; color: #ff5a1f; transform: translate(-38px, 10px); }
[data-art="elements"] .gart i:nth-child(2) { background: #4de1ff; color: #4de1ff; transform: translate(0, -18px); }
[data-art="elements"] .gart i:nth-child(3) { background: #ffe14d; color: #ffe14d; transform: translate(38px, 10px); }
[data-art="elements"].is-lit .gart i { transform: translate(0, 0); opacity: .85; }

/* Орёл или решка — монета, переворачивающаяся при наведении. */
[data-art="coin"] .gart { perspective: 420px; }
[data-art="coin"] .gart i {
  width: 58px; height: 58px; border-radius: 50%;
  border: 2px solid var(--warn); background: radial-gradient(circle at 34% 30%, #3a3016, #16130a);
}
[data-art="coin"] .gart i:nth-child(2) { border-color: var(--accent); opacity: 0; }
[data-art="coin"].is-lit .gart i:nth-child(1) { transform: rotateY(180deg) scale(.9); opacity: 0; }
[data-art="coin"].is-lit .gart i:nth-child(2) { transform: rotateY(360deg); opacity: 1; }

/* Наотмашь — три звена цепи, растягивающиеся при наведении. */
[data-art="chain"] .gart i {
  width: 26px; height: 26px; border-radius: 50%;
  border: 3px solid var(--neon-2); background: none;
}
[data-art="chain"] .gart i:nth-child(1) { transform: translateX(-22px); }
[data-art="chain"] .gart i:nth-child(3) { transform: translateX(22px); border-color: var(--accent); }
[data-art="chain"].is-lit .gart i:nth-child(1) { transform: translate(-44px, -10px); }
[data-art="chain"].is-lit .gart i:nth-child(3) { transform: translate(44px, 10px); }

/* Один удар — клинок и след от рывка. */
[data-art="blade"] .gart i:nth-child(1) {
  width: 4px; height: 62px; border-radius: 2px; background: var(--fail);
  transform: rotate(28deg);
}
[data-art="blade"] .gart i:nth-child(2) {
  width: 54px; height: 2px; background: linear-gradient(90deg, transparent, var(--warn));
  transform: translate(-6px, 16px);
}
[data-art="blade"] .gart i:nth-child(3) {
  width: 10px; height: 10px; border-radius: 50%; background: var(--warn);
  transform: translate(26px, -22px);
}
[data-art="blade"].is-lit .gart i:nth-child(1) { transform: rotate(-32deg) scale(1.05); }
[data-art="blade"].is-lit .gart i:nth-child(2) { transform: translate(14px, 16px) scaleX(1.4); }

/* Техномагия — руна из трёх граней. */
[data-art="rune"] .gart i {
  width: 30px; height: 30px; border: 2px solid var(--neon); background: none;
}
[data-art="rune"] .gart i:nth-child(1) { transform: rotate(45deg) translateY(-14px); }
[data-art="rune"] .gart i:nth-child(2) { border-radius: 50%; border-color: var(--neon-2); }
[data-art="rune"] .gart i:nth-child(3) { transform: rotate(45deg) translateY(14px); }
[data-art="rune"].is-lit .gart i:nth-child(1) { transform: rotate(135deg) translateY(-20px); }
[data-art="rune"].is-lit .gart i:nth-child(3) { transform: rotate(-45deg) translateY(20px); }

/* ПЕРИМЕТР — чужой взгляд: зрачок и две створки. На наведении глаз
   закрывается, потому что вся игра про то, чтобы в него не попасть. */
[data-art="watch"] .gart i {
  width: 34px; height: 7px; border-radius: 4px; background: var(--neon-2);
}
[data-art="watch"] .gart i:nth-child(1) { transform: translateY(-21px); }
[data-art="watch"] .gart i:nth-child(2) {
  width: 26px; height: 26px; border-radius: 50%;
  background: none; border: 2px solid var(--neon);
}
[data-art="watch"] .gart i:nth-child(3) { transform: translateY(21px); }
[data-art="watch"].is-lit .gart i:nth-child(1) { transform: translateY(-5px); }
[data-art="watch"].is-lit .gart i:nth-child(3) { transform: translateY(5px); }
[data-art="watch"].is-lit .gart i:nth-child(2) { opacity: .2; transform: scaleY(.15); }

/* Лила — пять клеток поля. */
[data-art="dice"] .gart i { width: 16px; height: 16px; border-radius: 3px; background: var(--line); border: 1px solid var(--dim); }
[data-art="dice"] .gart i:nth-child(1) { transform: translate(-30px, -20px); }
[data-art="dice"] .gart i:nth-child(2) { transform: translate(0, -20px); }
[data-art="dice"] .gart i:nth-child(3) { transform: translate(30px, 0); }
[data-art="dice"] .gart i:nth-child(4) { transform: translate(0, 20px); }
[data-art="dice"] .gart i:nth-child(5) { transform: translate(-30px, 20px); }
[data-art="dice"].is-lit .gart i:nth-child(3) { background: var(--accent); border-color: var(--accent); }

.gcard-body { display: flex; flex-direction: column; gap: 8px; padding: 16px 18px 18px; flex: 1; }
.gcard-top { display: flex; justify-content: flex-end; }
.gcard-title { font-size: 22px; font-weight: var(--title-weight); letter-spacing: var(--title-spacing); }
.gcard-formerly {
  display: block; margin-top: 3px; font-family: var(--mono); font-size: 10px;
  letter-spacing: .08em; text-transform: uppercase; color: var(--dim); font-weight: 400;
}
.gcard-text { font-size: 13.5px; color: var(--muted); line-height: 1.5; }
.gcard .launch {
  margin-top: auto; padding-top: 6px;
  font-family: var(--mono); font-size: 11px; letter-spacing: .1em; color: var(--accent);
}
article.gcard .launch { color: var(--dim); }

/* Полоса над играми. Повторяет строку прогона на «Работе» по шрифту и
   высоте, поэтому карточки обеих вкладок начинаются на одном уровне.
   В отличие от неё стоит сама по себе, а не сверху панели, — отсюда своя
   рамка и скругление. */
.play-bar {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 11px 18px; margin-bottom: 14px;
  border: 1px solid var(--line); border-radius: var(--r);
  background: var(--bg-soft);
  font-family: var(--mono); font-size: 12px; color: var(--muted);
}
.play-bar .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--accent); flex: none; }
/* Текст остаётся на строке с точкой и переносится внутри себя: иначе он
   уезжал целиком на следующую строку, а точка оставалась одна на своей. */
.play-bar code { flex: 1 1 200px; min-width: 0; color: var(--dim); }
.play-bar .verdict {
  margin-left: auto; padding: 3px 10px; border-radius: 999px;
  background: var(--accent-dim); color: var(--accent); font-weight: 600;
}

/* Ссылка на рассказы. Не кнопка переключателя: та меняет панель на месте,
   а эта уводит в раздел со своими страницами — путать эти два действия
   одним видом нельзя. */
.topbar-link {
  flex: none; padding: 7px 12px; border-radius: 999px;
  border: 1px solid transparent;
  color: var(--muted); text-decoration: none;
  font-size: 13px; white-space: nowrap;
  transition: color var(--an-mikro, 150ms) linear, border-color var(--an-mikro, 150ms) linear;
}
.topbar-link:hover, .topbar-link:focus-visible { color: var(--text); border-color: var(--line); }


/* Переход между полками. Ссылка одна и в обе стороны — про игру по рассказу
   тут ничего не сказано намеренно: ни одной такой игры пока нет. */
.story-cross {
  margin-top: 34px; padding-top: 18px; border-top: 1px solid var(--rule, var(--line));
  font-size: 14px;
}
.story-cross a { color: var(--accent-read, var(--accent)); text-decoration: none; }
.story-cross a:hover { text-decoration: underline; }
.story-cross b { font-weight: 400; }

/* ── Подвал ───────────────────────────────────────────────────────── */

/* Нижний отступ считает безопасную зону: на iPhone под страницей висит
   плашка с адресом сайта, и без этого она съедала последнюю строку — не
   «почти», а буквально закрывала слова. На машинах без выреза env() даёт
   ноль, и отступ остаётся прежним. */
/* «Код открыт» под играми — просьба владельца: «и вот даже код у проектов
   и игр открыт — смотрите и пользуйтесь». Число проверено: у всех
   девяти игр с вкладки есть публичный репозиторий, сверено по API. Меняется
   число игр — меняется и эта строка, иначе получится обещание вроде
   «четырёх способов пройти», которых не было. */
.story-cross--code { margin-top: 26px; }

.sitefoot {
  display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
  max-width: var(--maxw); margin: 0 auto;
  padding: 22px 24px calc(40px + env(safe-area-inset-bottom, 0px));
  border-top: 1px solid var(--line-soft);
  font-family: var(--mono); font-size: 11px; letter-spacing: .1em; color: var(--dim);
}
.sitefoot .socials { margin-left: auto; }
.foot-note { letter-spacing: 0; font-family: var(--sans); }

/* ── Адаптив ──────────────────────────────────────────────────────── */

@media (max-width: 900px) {
  .report-grid { grid-template-columns: 1fr; gap: 26px; padding: 24px 20px; }
  .report-detail { grid-template-columns: 1fr; }
  .report-proof { grid-template-columns: 1fr; }
  .job { grid-template-columns: 1fr; }
  .job-period { grid-row: auto; }
  .list li { grid-template-columns: 1fr auto; }
  .list-note { grid-column: 1 / -1; }
}

@media (max-width: 640px) {
  main { padding: 0 16px 60px; }
  /* Кнопка «Путь» — на первый экран. Замер до правки: она стояла на 922 при
     экране 844, то есть ниже сгиба, а просили именно первый экран. Ставим её
     между Gateway (кончается на 552) и Дармой (начиналась на 564) — заголовок
     раздела остаётся первым, ничего больше не двигается по смыслу.
     display:contents растворяет обёртку, чтобы карточки и кнопка стали
     соседями и слушались order; gap при этом теряется, поэтому отступы явные. */
  .work-lead { margin-top: 18px; padding: 18px; display: flex; flex-direction: column; }
  .work-duet { display: contents; }
  .work-gateway { order: 1; }
  .work-put { order: 2; margin: 12px 0 0; }
  .work-dharma { order: 3; margin-top: 12px; }
  .work-duet-note { order: 4; }
  .work-system { min-height:0; padding:18px; }
  .work-lead h1 { font-size: 31px; }
  .work-gateway-copy > p:not(.kicker) { font-size: 13px; line-height: 1.45; }
  .work-gateway-proof img { height:auto; min-height:0; aspect-ratio:16 / 8; }
  .work-dharma-shot img { height:auto; min-height:0; aspect-ratio:16 / 8; }
  .work-dharma h2 { font-size: 22px; }
  .work-dharma p { font-size: 12px; }
  .topbar { padding: 12px 16px; gap: 10px; flex-wrap: wrap; }
  /* На телефоне переключатель — главный элемент шапки: две крупные цели
     во всю ширину, а не мелкие подписи. Первый посетитель не нашёл здесь
     переход к играм именно потому, что цель была маленькой и тусклой. */
  .track-switch { order: 3; width: 100%; margin-left: 0; padding: 4px; }
  .track-switch button {
    flex: 1 1 0; justify-content: center;
    padding: 12px 14px; font-size: 15px; gap: 9px;
  }
  .track-switch button svg { width: 19px; height: 19px; }
  .socials { margin-left: auto; }
  /* На телефоне значок встаёт под текстом слева, а не жмётся к правому краю. */
  .play-bar { gap: 8px 10px; padding: 10px 14px; }
  .play-bar .verdict { margin-left: 0; }
  .sitefoot { padding: 20px 16px calc(36px + env(safe-area-inset-bottom, 0px)); }
  .report-metrics { grid-template-columns: 1fr; }
  .report-brief { padding: 18px 18px 14px; }
  .report-brief h1 { font-size: 28px; line-height: 1.04; }
  .report-benefits { margin-top: 12px; gap: 3px; }
  .report-benefits p { font-size: 14px; line-height: 1.35; }
  .report-live { margin-top: 12px; padding: 8px 10px; font-size: 11.5px; line-height: 1.4; }
  .report-more > summary { min-height: 48px; padding: 11px 18px; font-size: 10.5px; }
  .report-more > summary span { display: none; }
  .practicum-tabs { flex-direction: column; }
  .practicum-tabs button { min-height: 44px; }
  .partner-form { grid-template-columns: 1fr; padding: 16px; }
  .partner-message { grid-column: auto; }
  .block { margin-top: 40px; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation: none !important; transition: none !important; }
}

/* ── Снимок во весь экран ──────────────────────────────────────────── */
/* Слой поверх страницы. Появляется по клику, закрывается по Escape, по
   клику мимо картинки и крестиком. Клик по самой картинке не закрывает:
   её как раз пришли смотреть, и промах по ней читался бы как поломка. */
.shot-full {
  position: fixed; inset: 0; z-index: 200;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 14px; padding: 24px;
  background: #060708f2;
}
.shot-full img {
  max-width: min(94vw, 1100px); max-height: 84vh;
  width: auto; height: auto; border-radius: 10px;
  background: var(--panel);
}
.shot-full-cap {
  margin: 0; max-width: 60ch; text-align: center;
  font-family: var(--mono); font-size: 11.5px; line-height: 1.5; color: var(--muted);
}
.shot-full-close {
  position: absolute; top: 14px; right: 16px;
  width: 40px; height: 40px; border-radius: 999px;
  border: 1px solid var(--line); background: var(--panel);
  color: var(--muted); font-size: 15px; cursor: pointer;
}
.shot-full-close:hover, .shot-full-close:focus-visible { color: var(--fg); border-color: var(--muted); }
@media (prefers-reduced-motion: no-preference) {
  .shot-full { animation: shot-full-in var(--an-mikro, 150ms) var(--an-vyhod, ease-out); }
  @keyframes shot-full-in { from { opacity: 0; } to { opacity: 1; } }
}

/* ═══ Мастерская: рама вокруг ленты «Живого цеха» ═══════════════════════
   Ленту рисует сам цех — она приезжает готовым куском со своими стилями
   и своей палитрой. Наше дело здесь только рама и подпись: править чужую
   вёрстку внутри рамы нельзя, иначе при следующем файле правка отвалится.

   Вкладка пока одна, и она НЕ кнопка: кнопка, которая ничего не меняет,
   читается как поломка. Появится вторая — станет переключателем. */
.masterskaya .mast-tabs {
  display: flex; gap: 2px; margin: 0 0 12px;
  padding: 3px; border: 1px solid var(--line); border-radius: 999px;
  background: var(--bg-soft); width: max-content; max-width: 100%;
}
.masterskaya .mast-tab {
  padding: 7px 15px; border-radius: 999px;
  font-size: 13px; font-weight: 600; color: var(--text);
}
.masterskaya .mast-tab.is-current {
  background: var(--accent); color: #08120c; box-shadow: var(--glow);
}
.masterskaya .mast-panel {
  border: 1px solid var(--line); border-radius: 16px;
  background: var(--bg-soft); padding: 12px;
}
/* Возраст снимка стоит НАД лентой: внутри неё написано «работает прямо
   сейчас», и человек должен прочитать дату раньше этих слов. */
.masterskaya .mast-stamp {
  margin: 2px 4px 10px; font-size: 12px; color: var(--muted);
}
@media (max-width: 700px) {
  .masterskaya .mast-panel { padding: 8px; border-radius: 14px; }
  .masterskaya .mast-stamp { margin: 2px 2px 8px; }
}

/* ═══ Совсем узкие экраны (320 и уже) ═══════════════════════════════════
   Тестер, 6 сентября 2026: на 320 у главной scrollWidth 351 при clientWidth
   320 — страницу можно было сдвинуть вбок на 31 пиксель. Виноваты были два
   места: сетка карточек (починена выше) и кнопки переключателя разделов.
   Кнопки лежат в flex и по умолчанию не сжимаются ниже своего содержимого,
   а «Рассказы» со значком в 15 пунктов туда не влезают втроём.
   Крупные цели на телефоне трогать нельзя — их выбрали после того, как
   живой человек не нашёл переход к играм, — поэтому высота и ширина в треть
   остаются, а уходит только значок: подпись человек читает, значок узнаёт.  */
@media (max-width: 359px) {
  .track-switch button { min-width: 0; padding: 12px 6px; font-size: 13px; gap: 0; }
  .track-switch button svg { display: none; }
  .topbar { padding: 12px 12px; }
}

/* ═══ «Ещё практикумы»: раскрытие без скачка ═════════════════════════════
   Сергей 6 сентября 2026: «опять дохуя свободного места, бессмысленно; они
   не сильно важные после QueQuest» — и выбрал спрятать за кнопку.

   Высоту ведёт сетка: строка едет от 0fr к 1fr. У height:auto нет
   анимируемого значения, поэтому раскрытие через него — это рывок; приём
   взят из Аниматеки, где он и записан. Содержимое при этом не трогаем
   вовсе, иначе внутренние карточки пересчитывали бы раскладку каждый кадр. */
.practicum-more { display: grid; gap: 0; min-height: 0; }
/* Кнопка встроена в нижнюю зону главной карточки. 44 точки оставляем и мыши,
   и пальцу: уменьшаем раскрываемые карточки, а не честную область нажатия. */
.practicum-more-btn {
  justify-self: start; display: inline-flex; align-items: center; gap: 8px;
  min-height: 44px; padding: 11px 16px; border: 1px solid var(--line);
  border-radius: 10px; background: var(--panel); color: var(--text);
  font: 600 13px/1.2 var(--sans); cursor: pointer;
  transition: background var(--an-mikro, 150ms) linear, border-color var(--an-mikro, 150ms) linear;
}
.practicum-more-btn:hover { background: var(--card-hover); border-color: var(--accent); }
.practicum-more-btn b { color: var(--accent); transition: transform var(--an-mikro, 150ms) var(--an-vyhod-rezkiy, ease); }
.practicum-more-btn[aria-expanded="true"] b { transform: rotate(180deg); }
.practicum-more-body {
  display: grid; grid-template-rows: 0fr;
  transition: grid-template-rows var(--an-raskrytie, 280ms) var(--an-vyhod, cubic-bezier(.22,.61,.36,1));
}
.practicum-more-body > * { overflow: hidden; min-height: 0; }
.practicum-more-body[data-open] { grid-template-rows: 1fr; }
/* Кому движение мешает — состояние меняется мгновенно, а не «анимация без
   анимации»: раскрытие происходит, движения нет. */
@media (prefers-reduced-motion: reduce) {
  .practicum-more-body { transition: none; }
  .practicum-more-btn b { transition: none; }
}

/* Чип на кадре шлюза. Сергей выбрал вариант В: «запрос → обрыв → ответ всё
   равно» — не про устройство, а про то, чем шлюз ценен. Вид тот же, что у
   чипа Dharma (он ему нравится), поэтому класс переиспользован целиком, а
   здесь только место: у Dharma чип справа внизу, у шлюза справа внизу лёг бы
   на подпись «66 проверок». */
.work-gateway-proof span.gw-journey {
  display: flex; flex-wrap: nowrap; white-space: nowrap;
  right: 10px; bottom: 10px; top: auto; left: auto; width: max-content;
  padding: 7px 8px; font: 600 9px/1 var(--mono); color: #c8ffe9;
}
.work-gateway-proof span.gw-journey b { white-space: nowrap; }
.work-gateway-proof:hover .gw-journey, .work-gateway-proof:focus-visible .gw-journey { opacity: 1; border-color: #65e5b5; box-shadow: 0 0 18px #46dca34d; }

/* ═══ Подложка карточки QueQuest ════════════════════════════════════════
   Сергей 6 сентября: «поле с названием пустоватое: QueQuest большим справа
   снизу, а „Игра · Python“ и описание про ящики — с левой стороны».
   Две колонки: слева знак, кикер и фраза про ящики; справа внизу — имя,
   крупно. Имя прижато к нижней кромке, поэтому пустота уходит вверх, где
   её держит текст, а не остаётся дырой под названием.                     */
@media (min-width: 901px) {
  .quequest-copy {
    display: grid; grid-template-columns: minmax(0, 1fr) minmax(320px, .72fr);
    align-items: stretch; gap: 20px 32px;
  }
  .quequest-left { display: grid; align-content: center; justify-items:start; gap: 8px; min-width: 0; }
  .quequest-cta { display:grid; align-content:end; justify-items:end; gap:12px; min-width:0; }
  .quequest-copy h3 {
    margin: 0; text-align: right;
    font-size: clamp(48px, 6vw, 76px); line-height: .88; letter-spacing: -.035em;
  }
}
/* На телефоне колонок нет: имя над текстом, как и было. */
@media (max-width: 900px) {
  .quequest-left { display: grid; gap: 8px; }
  .quequest-cta { display:grid; gap:10px; margin-top:10px; }
  .quequest-copy h3 { margin: 0; font-size:clamp(36px, 6vw, 48px); }
  .quequest-brand img { width:60px; height:60px; }
}

/* Разделение «22 мышью, 44 пальцем» снято 6 сентября по решению Рота:
   Сергей просил половинную высоту ПО ВИДУ, а не по попаданию, а свод (29б)
   требует области нажатия не меньше 44 точек. Кнопка стала легче за счёт
   шрифта и отступов, а цель осталась пальцевой. */

/* ═══ Карточка QueQuest: картинка ведёт в игру ══════════════════════════
   Сергей 6 сентября: «где ссылка? почему по картинке не переходит!!??».
   Картинка была кнопкой, которая играла ролик, а единственная ссылка на
   игру лежала в списке далеко внизу страницы. Человек кликал по картинке и
   ждал игру — и был прав.

   Теперь картинка и заголовок — ссылки, ролик переехал на отдельную кнопку
   рядом, и есть явная «Открыть игру» пальцевого размера. */
.quequest-visual { display: block; text-decoration: none; }
.quequest-play-btn {
  position: absolute; left: 12px; bottom: 12px; z-index: 2;
  display: inline-flex; align-items: center; gap: 7px;
  min-height: 44px; padding: 11px 15px;
  border: 1px solid var(--line); border-radius: 999px;
  background: color-mix(in srgb, var(--bg) 82%, transparent);
  color: var(--text); font: 600 13px/1 var(--sans); cursor: pointer;
  backdrop-filter: blur(6px);
  transition: background var(--an-mikro, 150ms) linear, border-color var(--an-mikro, 150ms) linear;
}
.quequest-play-btn:hover { background: var(--panel); border-color: var(--accent); }
.quequest-play-btn b { color: var(--accent); }
.quequest-stage { position: relative; display: grid; min-height: 0; }
.quequest-open {
  justify-self: end; display: inline-flex; align-items: center; gap: 8px;
  min-height: 48px; padding: 12px 20px;
  border: 1px solid var(--quequest-neon); border-radius: 10px;
  background: var(--quequest-neon); color: #0b0712;
  font: 700 15px/1 var(--sans); text-decoration: none;
}
.quequest-open:hover { background:#ab75ff; box-shadow:0 0 28px rgba(155,92,255,.5); }
.quequest-copy h3 a { color: inherit; text-decoration: none; }
.quequest-copy h3 a:hover { color: var(--quequest-pale); }
.quequest-brand { display:inline-flex; align-items:center; justify-content:flex-end; gap:14px; }
.quequest-brand img { width:72px; height:72px; flex:none; object-fit:contain; filter:drop-shadow(0 0 18px rgba(155,92,255,.58)); }
.quequest-brand span { white-space:nowrap; }

/* Дополнительные практикумы — короткая справка, а не второй главный экран.
   На десктопе две карточки остаются в ряд; на телефоне — две плотные строки.
   Метрики уже есть внутри самих практикумов и здесь только раздували блок. */
.practicum-more .practicum-detail { grid-template-columns:52px minmax(0,1fr); gap:10px; padding:10px; }
.practicum-more .practicum-detail-shot { width:52px; height:52px; aspect-ratio:1; }
.practicum-more .practicum-detail-copy { align-content:center; gap:4px; }
.practicum-more .practicum-detail-copy h3 { font-size:16px; }
.practicum-more .practicum-detail-copy .kicker,
.practicum-more .practicum-detail-copy .tagline { display:none; }
.practicum-more .practicum-detail-copy .metric-chips,
.practicum-more .practicum-detail-copy .chips { display:none; }
.practicum-more .card-links { white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.practicum-more .card-links a:not(:first-child) { display:none; }
@media (max-width:900px) {
  .quequest-copy { padding:16px; }
  .quequest-open { justify-self:stretch; justify-content:center; }
  .quequest-brand { justify-content:flex-start; gap:10px; }
  .quequest-brand img { width:60px; height:60px; }
  .practicum-more .practicum-detail { grid-template-columns:56px minmax(0,1fr); min-height:82px; }
  .practicum-more .practicum-detail-shot { width:56px; height:56px; }
}

/* Строка терминала рядом с названием сайта: фразы, которые Сергей ловил и
   записывал сам. Его правка: «рядом с названием сайта, а не отдельной полосой
   ниже… пусть читается как строка терминала возле логотипа, а не как баннер» —
   поэтому ни рамки, ни подложки, только знак, текст и курсор.
   Высота фиксирована: фразы разной длины и набираются по букве, а без
   фиксации шапка дышала бы на каждом символе. */
.hero-term {
  display: flex; align-items: center; gap: 6px;
  flex: 1 1 auto; min-width: 0;
  font: 500 11.5px/1.45 var(--mono); color: var(--dim);
  height: 1.45em; overflow: hidden;
}
.hero-term-znak { flex: none; color: var(--accent); opacity: .8; }
.hero-term-tekst { min-width: 0; white-space: pre-wrap; }
/* Курсор виден ТОЛЬКО пока строка набирается или стирается. Слова Сергея:
   «бесит постоянно моргающая штука, прямо отвлекает» — и он прав: мигающий
   прямоугольник рядом с неподвижным текстом тянет взгляд на себя без всякой
   причины. Пока фраза просто висит, курсора нет вовсе, а мигает он вдвое
   медленнее прежнего. */
.hero-term-kursor {
  flex: none; width: .55em; height: 1em; background: var(--accent);
  opacity: 0; transition: opacity 120ms linear;
}
.hero-term[data-nabor] .hero-term-kursor {
  opacity: .75; animation: term-migat 2.1s steps(1, end) infinite;
}
@keyframes term-migat { 0%, 49% { opacity: .75 } 50%, 100% { opacity: 0 } }
/* Глитч как его делают в вебе, а не сдвиг всей строки: расщепление на
   красный и голубой каналы через тень, короткий срез полосой и дрожание на
   доли пикселя. Ничего из этого не занимает места — ни тень, ни clip-path,
   ни transform на layout не влияют, поэтому высота строки не двигается.
   Срабатывает редко и коротко: 260 мс, иначе мешает читать. */
.hero-term[data-glitch] .hero-term-tekst { animation: term-glitch 260ms steps(1, end) 1; }
@keyframes term-glitch {
  0%   { text-shadow: none; clip-path: none; transform: none }
  15%  { text-shadow: 1.6px 0 rgba(255,42,86,.85), -1.6px 0 rgba(60,235,255,.75);
         clip-path: inset(18% 0 42% 0); transform: translateX(-.6px) }
  35%  { text-shadow: -1.4px 0 rgba(255,42,86,.8), 1.4px 0 rgba(60,235,255,.7);
         clip-path: inset(52% 0 12% 0); transform: translateX(.8px) }
  55%  { text-shadow: 1.2px 0 rgba(255,42,86,.7), -1.2px 0 rgba(60,235,255,.6);
         clip-path: none; transform: none }
  75%  { text-shadow: -.8px 0 rgba(255,42,86,.5), .8px 0 rgba(60,235,255,.45);
         clip-path: inset(0 0 68% 0) }
  100% { text-shadow: none; clip-path: none; transform: none }
}
@media (prefers-reduced-motion: reduce) {
  /* Ни мигания, ни глитча вовсе: человек, попросивший меньше движения,
     получает строку, которая просто меняется. */
  .hero-term[data-nabor] .hero-term-kursor { animation: none; opacity: .45; }
  .hero-term[data-glitch] .hero-term-tekst { animation: none; }
}
@media (max-width: 900px) {
  /* Уходит на вторую строку шапки: рядом с названием места уже нет, а резать
     фразу нельзя. Две строки высоты — самая длинная фраза помещается целиком. */
  .topbar { flex-wrap: wrap; }
  .hero-term { flex-basis: 100%; order: 9; height: calc(1.45em * 2); align-items: flex-start; }
}


/* Полоска пути у Gateway оживает так же, как у Dharma: Сергею нравится, как
   у Dharma «выбор — агент — заказ» проявляется по шагам, и он попросил тот же
   ритм у «запрос — обрыв — ответ всё равно». Кривая, длительность и задержка
   второго отрезка взяты у Dharma один в один — иначе карточки на одном экране
   вели бы себя по-разному, и это читалось бы как недоделка.
   Высоту не трогаем: анимируется только масштаб по горизонтали. */
@media (prefers-reduced-motion: no-preference) {
  .work-gateway-proof:hover .gw-journey i,
  .work-gateway-proof:focus-visible .gw-journey i { animation: dharma-route .8s ease-in-out both; }
  .work-gateway-proof:hover .gw-journey i:nth-of-type(2),
  .work-gateway-proof:focus-visible .gw-journey i:nth-of-type(2) { animation-delay: .38s; }
}

/* «(с)» после фразы — шутка Сергея про патентованные фразы ИИ. Чуть тусклее
   самой фразы и не разрывается внутри себя: на узком экране переносится
   целиком, а не по одной скобке. */
.hero-term-c { flex: none; white-space: nowrap; opacity: .55; }

/* На коротких страницах (практикум и прочие) main лежит внутри флекса
   .page-short и потому сжимался ПО СОДЕРЖИМОМУ: max-width на него не влиял
   вовсе, и страница занимала 0.40 ширины экрана при 1920. Сергей сказал про
   это дважды — «всё жмётся к левому краю».
   Ширину задаём явно: элемент флекса иначе не растягивается, сколько бы
   максимума ему ни назначили. */
.page-short > main { width: 100%; }
