:root {
  /* Color. Светлая тема — основа; тёмная переопределяет только эти
     токены (блок ниже). Цвета в стилях брать только отсюда. */
  color-scheme: light;
  --color-ink: #000000;
  --color-ink-hover: #1c1c1c;
  --color-ink-soft: #656565;
  --color-heading: #2c2c2c;
  /* Фон страницы, плашка и карточка: в тёмной теме карточка светлее
     плашки, а плашка светлее страницы — порядок слоёв сохраняется. */
  --color-page: #ffffff;
  --color-surface: #ffffff;
  --color-surface-hover: #ededed;
  --color-surface-sunken: #f7f7f7;
  --color-border: #e8e8e8;
  --color-overlay: rgba(0, 0, 0, 0.4);
  --color-scroll: rgba(0, 0, 0, 0.15);
  --color-scroll-hover: rgba(0, 0, 0, 0.28);
  /* Пустая ячейка сетки логотипов, её ховер и выбранный сегмент */
  --color-cell: #ededed;
  /* Переключатель: дорожка — лунка, бегунок — плашка, и бегунок всегда
     светлее дорожки. Токены свои, а не клавишные: клавиша лежит на
     странице и потому светлее её, а дорожка, наоборот, утоплена.
     Дорожка — обычный серый сайта, тот же, что у плашек (#f7f7f7 в
     светлой, #171717 в тёмной), а не клавишный #ededed. */
  --color-track: #f7f7f7;
  /* Тот же переключатель на серой плашке: дорожка цвета плашки на ней
     не видна, поэтому для этого случая своя, ещё на ступень ниже. */
  --color-track-sunken: #ededed;
  --color-knob: #ffffff;
  --shadow-knob: 0 0 3px -1px rgba(0, 0, 0, 0.12);
  --color-cell-hover: #e3e3e3;
  --color-cell-active: #f7f7f7;
  /* Приглушённая кнопка: заметна, но не спорит с контентом контрастом —
     как пустые клавиши сетки на главной. */
  --color-control: #ededed;
  --color-control-hover: #e3e3e3;
  /* Просмотр кадра: фон оверлея и подписи на нём */
  --color-zoom-bg: rgba(16, 16, 16, 0.94);
  --color-zoom-ink: #f4f4f4;
  --color-avatar-bg: #d9d9d9;
  /* Подложка под материалом, который остаётся светлым в любой теме:
     видео и выгрузки со светлым фоном. */
  --color-paper: #f7f7f7;

  /* Radius */
  --radius-lg: 48px;
  --radius-md: 40px;
  --radius-sm: 12px;
  --radius-xs: 8px;
  --radius-pill: 100px;

  /* Spacing. Вне этой шкалы в проекте осознанно живут ровно два
     значения: 6px между иконкой и подписью (пара должна читаться как
     один объект) и 20px в строке места работы — оба из макетов. */
  --space-3xs: 4px;
  --space-xs: 8px;
  --space-sm: 12px;
  --space-md: 16px;
  --space-lg: 24px;
  --space-xl: 32px;
  --space-2xl: 40px;
  --space-3xl: 48px;

  /* Аватарка: слот под неё — ровно половина этой величины */
  --avatar-size: 140px;

  /* Typography: families */
  --ff-display: "Roboto Flex", sans-serif;
  --ff-body: "Inter", sans-serif;
  --ff-mono: "Roboto Mono", monospace;

  /* Typography: sizes. Display + large body scale fluidly between the
     mobile floor and the desktop value, so type breathes across widths
     instead of snapping at breakpoints. */
  --fs-display: clamp(24px, 1.1rem + 1.5vw, 32px);
  /* Основной текст один на весь сайт — 16 и на главной, и в кейсе */
  --fs-body-lg: 16px;
  --fs-body-md: 16px;
  --fs-body-sm: 14px;
  /* Subheadline — подпись под заголовком */
  --fs-subheadline: 14px;
  --lh-subheadline: 18px;
  --fs-mono-lg: 20px;
  --fs-mono-md: 16px;

  /* Typography: weights & line-heights */
  --fw-regular: 400;
  --fw-medium: 500;
  --fw-semibold: 600;
  --fw-bold: 700;
  --lh-tight: 1.1;
  --lh-normal: 1.4;

  /* Case stage */
  --stage-ratio: 1220 / 712;

  /* Форма угла — суперэллипс. Чем меньше число, тем круглее:
     1 — обычная дуга, 2 — «сквиркл», выше — всё квадратнее.
     Проверено на образцах в браузере, не на глаз. */
  --corner-smoothing: 1.3;

  /* Motion: one shared easing + duration so every interaction feels
     like it belongs to the same system. */
  --ease: cubic-bezier(0.16, 1, 0.3, 1);
  --dur: 0.22s;

  /* Elevation: layered shadows (a tight contact shadow + a soft ambient
     one) read as real depth, unlike a single flat blur. */
  /* Из макета: смещение вверх на 4, размытие 20, чёрный 8 % */
  --shadow-card: 0 -4px 20px rgba(0, 0, 0, 0.08);
  --shadow-hover: 0 2px 6px rgba(0, 0, 0, 0.08), 0 16px 40px -12px rgba(0, 0, 0, 0.18);
}

/* ---------- Тёмная тема ----------
   Правило одно: переопределяем только токены. Тема берётся из системы,
   а выбор человека (data-theme на <html>) её перебивает — поэтому
   системный блок исключает явно выбранную светлую.
   Тени в темноте не читаются, глубину даёт разница поверхностей,
   поэтому они мягче и служат только отрывом плашки от картинки. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --color-ink: #f4f4f4;
    --color-ink-hover: #e0e0e0;
    --color-ink-soft: #9b9b9b;
    --color-heading: #fafafa;
    --color-page: #101010;
    --color-surface: #1f1f1f;
    --color-surface-hover: #2a2a2a;
    --color-surface-sunken: #171717;
    --color-border: #2b2b2b;
    --color-overlay: rgba(0, 0, 0, 0.6);
    --color-scroll: rgba(255, 255, 255, 0.18);
    --color-scroll-hover: rgba(255, 255, 255, 0.32);
    --color-cell: #242424;
    --color-track: #171717;
    --color-track-sunken: #101010;
    --color-knob: #2a2a2a;
    --shadow-knob: none;
    --color-cell-hover: #2c2c2c;
    --color-cell-active: #333333;
    --color-control: #333333;
    --color-control-hover: #3d3d3d;
    --color-avatar-bg: #2e2e2e;
    --shadow-card: 0 -4px 20px rgba(0, 0, 0, 0.45);
    --shadow-hover: 0 2px 6px rgba(0, 0, 0, 0.4), 0 16px 40px -12px rgba(0, 0, 0, 0.6);
  }
}

:root[data-theme="dark"] {
  color-scheme: dark;
  --color-ink: #f4f4f4;
  --color-ink-hover: #e0e0e0;
  --color-ink-soft: #9b9b9b;
  --color-heading: #fafafa;
  --color-page: #101010;
  --color-surface: #1f1f1f;
  --color-surface-hover: #2a2a2a;
  --color-surface-sunken: #171717;
  --color-border: #2b2b2b;
  --color-overlay: rgba(0, 0, 0, 0.6);
  --color-scroll: rgba(255, 255, 255, 0.18);
  --color-scroll-hover: rgba(255, 255, 255, 0.32);
  --color-cell: #242424;
  --color-track: #171717;
  --color-track-sunken: #101010;
  --color-knob: #2a2a2a;
  --shadow-knob: none;
  --color-cell-hover: #2c2c2c;
  --color-cell-active: #333333;
  --color-control: #333333;
  --color-control-hover: #3d3d3d;
  --color-avatar-bg: #2e2e2e;
  --shadow-card: 0 -4px 20px rgba(0, 0, 0, 0.45);
  --shadow-hover: 0 2px 6px rgba(0, 0, 0, 0.4), 0 16px 40px -12px rgba(0, 0, 0, 0.6);
}

* {
  box-sizing: border-box;
}

@supports (corner-shape: superellipse(2)) {
  * {
    corner-shape: superellipse(var(--corner-smoothing));
  }
}

html {
  scroll-behavior: smooth;
}

body {
  margin: 0;
  background: var(--color-page);
  color: var(--color-ink);
  font-family: var(--ff-body);
  /* Let Roboto Flex pick the optical-size cut that suits each glyph size. */
  font-optical-sizing: auto;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overscroll-behavior-y: none;
}

img {
  display: block;
  max-width: 100%;
}

a {
  color: inherit;
  text-decoration: none;
}

/* Keyboard focus only — never shows for mouse users, always for tab nav. */
:focus-visible {
  outline: 2px solid var(--color-ink);
  outline-offset: 3px;
  border-radius: var(--radius-xs);
}

:focus:not(:focus-visible) {
  outline: none;
}

::selection {
  background: var(--color-ink);
  color: var(--color-surface);
}

/* ---------- Переход между страницами ----------
   Кросс-документный View Transitions: браузер сам снимает старую
   страницу и проявляет новую. Никакого JS и никакой задержки перехода —
   если браузер не умеет, просто ничего не происходит.
   Длительность короткая: переход видят на каждом клике по кейсу. */
@view-transition {
  navigation: auto;
}

::view-transition-old(root),
::view-transition-new(root) {
  animation-duration: 0.18s;
  animation-timing-function: var(--ease);
}

/* Псевдоэлементы перехода живут вне дерева, и общий сброс движения
   их не достаёт — гасим отдельно. */
@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) {
    animation: none !important;
  }
}

/* Respect users who ask for less motion: kill the smooth scroll and any
   transitions/animations so nothing slides or eases for them. */
@media (prefers-reduced-motion: reduce) {
  .experience-toggle {
    transition: none;
  }

  html {
    scroll-behavior: auto;
  }

  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

ul {
  margin: 0;
  padding: 0;
  list-style: none;
}

.page {
  display: flex;
  align-items: flex-start;
  gap: var(--space-md);
  /* 24px frame on all sides. Both columns are inset 24px top and bottom,
     so they line up at the top and — because the right column's scroll
     ends with its last card at the column's bottom edge — also line up
     at the bottom (24px above the viewport edge, level with the left
     floating panel). */
  padding: var(--space-lg);
  max-width: 1920px;
  margin: 0 auto;
}

/* ---------- Typography roles ---------- */

.text-display {
  font-family: var(--ff-display);
  font-variation-settings: "opsz" 96, "wdth" 132;
  font-weight: var(--fw-semibold);
  font-size: var(--fs-display);
  line-height: var(--lh-tight);
  /* Tighten large type slightly, and balance line lengths so headings
     never leave a lonely word on the last line. */
  letter-spacing: -0.015em;
  text-wrap: balance;
}

.text-body {
  font-family: var(--ff-body);
  font-weight: var(--fw-regular);
  font-size: var(--fs-body-lg);
  line-height: var(--lh-normal);
  color: var(--color-ink-soft);
  /* Avoid orphans / ragged last lines in paragraphs. */
  text-wrap: pretty;
}

.text-mono {
  font-family: var(--ff-mono);
  font-weight: var(--fw-regular);
  font-size: var(--fs-mono-md);
  line-height: var(--lh-normal);
}

/* ---------- Shared components ---------- */

/* .panel: soft-bg rounded wrapper that frames a stack of white cards */
.panel {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  background: var(--color-surface-sunken);
  border-radius: var(--radius-lg);
  padding: var(--space-xs);
}

/* .card: white rounded surface inside a panel. Обводки нет намеренно —
   a crisp edge against the sunken bg that a shadow alone can't. */
.card {
  width: 100%;
  background: var(--color-surface);
  border-radius: var(--radius-md);
}

/* ---------- Кнопки ----------
   Одна система на весь сайт. Раньше кнопок было семь разных: высота
   от 40 до 56, три скругления, моноширинный и обычный шрифт, а стрелка
   на карточках была картинкой целой кнопки.

   Высота всегда 48. Скругление полное — у кнопки с подписью пилюля,
   у квадратной круг; форму угла даёт общий суперэллипс сайта.
   Иконки — Heroicons 16 с линией 1,5.

     .btn            основа: размер, шрифт, форма
     .btn--dark      чёрная
     .btn--soft      серая
     .btn--icon      только иконка, 48 × 48
     .btn--wide-icon только иконка, 72 × 48 — стрелка на карточках */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  gap: 6px; /* иконка с подписью — один объект, вне шкалы */
  height: 48px;
  padding: 0 var(--space-lg);
  border: 0;
  border-radius: var(--radius-pill);
  corner-shape: round;
  font-family: var(--ff-body);
  font-size: var(--fs-body-md);
  line-height: 1;
  white-space: nowrap;
  text-decoration: none;
  cursor: pointer;
  transition: background var(--dur) var(--ease), transform var(--dur) var(--ease);
}

.btn:active {
  transform: scale(0.97);
}

.btn--dark {
  background: var(--color-ink);
  color: var(--color-surface);
}

.btn--dark:hover {
  background: var(--color-ink-hover);
}

.btn--soft {
  background: var(--color-surface-sunken);
  color: var(--color-ink);
}

/* Приглушённый вариант: для возврата и переключения языка в кейсе —
   кнопка видна, но не перетягивает внимание полным контрастом. */
.btn--control {
  background: var(--color-control);
  color: var(--color-ink);
}

.btn--control:hover {
  background: var(--color-control-hover);
}

.btn--soft:hover {
  background: var(--color-surface-hover);
}

.btn--icon {
  width: 48px;
  padding: 0;
}

.btn--wide-icon {
  width: 72px;
  padding: 0;
}

/* Пилюля перехода по вертикали — «наверх» */
.btn--tall-icon {
  width: 48px;
  height: 72px;
  padding: 0;
}

/* Иконки — Heroicons, контур 24, рисуются в 16. Линия цвета текста,
   поэтому работают на любой кнопке без отдельных вариантов. */
.btn-icon {
  flex-shrink: 0;
  width: 16px;
  height: 16px;
}

/* Холст 24, а рисуем в 16 — обычная линия сжалась бы до пикселя.
   non-scaling-stroke держит 1,5 на экране при любом размере. */
.btn-icon path {
  vector-effect: non-scaling-stroke;
}

.chip {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 32px;
  padding: 0 var(--space-sm);
  border-radius: var(--radius-pill);
  corner-shape: round;
  background: var(--color-surface-sunken);
  font-family: var(--ff-mono);
  font-size: var(--fs-mono-md);
  line-height: 1;
  color: var(--color-ink);
  white-space: nowrap;
}

/* «Скоро» — не тег, а кнопка, которой пока нельзя воспользоваться */
/* «Скоро»: приглушённая, но читаемая в обеих темах. Раньше фон брался
   из --color-overlay, а он на тёмной теме почти чёрный, и надпись цвета
   плашки на нём пропадала. */
.btn:disabled {
  background: var(--color-ink-soft);
  color: var(--color-page);
  cursor: default;
}

.btn:disabled:active {
  transform: none;
}

/* ---------- Left column ---------- */

.profile-col {
  /* Доля, а не фиксированные 572: фиксированная ширина оставляла
     проектам меньше места, чем занимал профиль. Потолок 572 держит
     прежний вид на широком экране, нижняя граница 375 позволяет
     оставаться в двух колонках до 920 — раньше страница рассыпалась
     в одну колонку уже на 1100. */
  width: clamp(375px, 42%, 572px);
  flex-shrink: 0;
  overflow: hidden;
}

.profile-col-scroll {
  /* Контейнер для сетки клавиш: сколько логотипов показывать, зависит
     от ширины самой колонки, а не окна. */
  container-type: inline-size;
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  width: 100%;
  /* This element is the actual scroll container (overflow-y: auto on
     desktop), so it — not the outer panel — is what clips the scrolling
     content. Round it to the panel's inner corner (outer radius minus the
     8px panel padding) so content slides away behind rounded corners
     instead of showing sharp rectangular ones. */
  border-radius: calc(var(--radius-lg) - var(--space-xs));
}

/* Сетка логотипов лежит прямо на фоне панели, без карточки. Ячейки
   почти квадратные (49x48 в макете) и тянутся вместе с колонкой, так что
   на узком экране сетка сжимается, а не переносится. */
/* Сетка клавиш подстраивается под ширину колонки: клавиша держит
   примерно 48, а число столбцов меняется — на узком экране их меньше,
   на планшете больше. Рядов всегда три: всё, что не поместилось,
   уезжает в ряды нулевой высоты и обрезается. */
.tool-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(48px, 1fr));
  /* Ряд по высоте равен столбцу по ширине — клавиша всегда квадратная.
     Ширину столбца в CSS не достать (она из auto-fill), поэтому её
     измеряет script.js и кладёт в --tool-cell. Через aspect-ratio не
     вышло: ряд считался бы по клавише, а клавиша тянулась бы на ряд —
     замкнутый круг, ряды схлопывались вдвое. */
  grid-template-rows: repeat(3, var(--tool-cell, 48px));
  /* Рядов всегда три, лишние — нулевой высоты, иначе у них остаются
     зазоры и сетка растёт, отодвигая карточку профиля. */
  grid-auto-rows: 0;
  /* Поле и высота считаются от одной переменной: если задать их
     порознь, на узком экране высота остаётся от старого поля и третий
     ряд вылезает за плашку. */
  --tool-pad: var(--space-md);
  height: calc(var(--tool-pad) + 3 * var(--tool-cell, 48px) + 2 * var(--space-3xs));
  gap: var(--space-3xs);
  margin: 0;
  padding: var(--tool-pad) var(--tool-pad) 0;
  /* overflow: hidden обнуляет min-height флекс-элемента, и на десктопе,
     где колонка ограничена по высоте, сетку сплющивало до одного ряда.
     flex: none возвращает ей собственную высоту. */
  flex: none;
  overflow: hidden;
  list-style: none;
}

.tool-cell {
  height: 100%;
  border-radius: var(--radius-sm);
  background: var(--color-cell);
  overflow: hidden;
}

.tool-icon {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.profile-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-lg);
  /* Сверху отступа нет: аватарка выходит из карточки ровно наполовину,
     и её нижняя половина сама задаёт этот отступ. */
  padding: 0 var(--space-xl) var(--space-xl);
}

/* Слот высотой в половину аватарки. Аватарка вылезает наверх, на сетку
   логотипов, и обводкой в цвет фона панели вырезает себе место. */
.avatar-slot {
  position: relative;
  width: var(--avatar-size);
  height: calc(var(--avatar-size) / 2);
  flex-shrink: 0;
}

.avatar {
  position: absolute;
  top: calc(var(--avatar-size) / -2);
  left: 0;
  width: var(--avatar-size);
  height: var(--avatar-size);
  border-radius: 50%;
  /* Глобальное скругление-суперэллипс делает из круга скруглённый
     квадрат — аватарке нужен именно круг. */
  corner-shape: round;
  object-fit: cover;
  background: var(--color-avatar-bg);
  outline: 4px solid var(--color-surface-sunken);
}

/* Сетка, а не вложенные флексы: имя и кнопка стоят в одном ряду, но
   лежат в разметке рядом с остальным — поэтому на мобилке порядок
   переставляется одним правилом, без дублирования блоков. */
.profile-body {
  display: grid;
  grid-template-columns: 1fr auto;
  grid-template-areas:
    "identity pill"
    "links links"
    "bio bio";
  column-gap: var(--space-xs);
  row-gap: var(--space-lg);
  width: 100%;
}

.profile-identity {
  grid-area: identity;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-3xs);
}

.profile-links {
  grid-area: links;
}

.bio {
  grid-area: bio;
}

.profile-name {
  margin: 0;
  font-family: var(--ff-display);
  font-size: clamp(20px, 1rem + 0.6vw, 24px);
  font-weight: var(--fw-semibold);
  font-variation-settings: "opsz" 96, "wdth" 132, "wght" 600;
  line-height: var(--lh-tight);
  letter-spacing: -0.015em;
  color: var(--color-ink);
  text-wrap: balance;
}

.profile-role {
  margin: 0;
  font-size: var(--fs-body-lg);
  line-height: var(--lh-normal);
  color: var(--color-ink-soft);
  text-wrap: balance;
}

.contact-pill {
  grid-area: pill;
  align-self: start;
  /* Кнопка — только иконка, 72×48, как пилюля перехода: рядом с именем
     подпись не нужна и лишь отнимает у него ширину. Подпись
     появляется, когда кнопка уходит вниз, на всю ширину карточки. */
  width: 72px;
  padding: 0;
  white-space: nowrap;
}

.contact-label {
  display: none;
}



/* Значок Behance — фирменный знак на плашке: сама плашка красится
   цветом текста, буквы — цветом страницы. Так он переворачивается
   вместе с темой и остаётся узнаваемым. */
.icon-behance .behance-bg {
  fill: currentColor;
}

.icon-behance .behance-mark {
  fill: var(--color-page);
}

/* У значка Telegram внутри холста больше воздуха, чем у Heroicons:
   в 20 он визуально равен стрелкам в 16. */
.contact-pill .link-icon {
  width: 20px;
  height: 20px;
}


.profile-links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-md);
}

.profile-link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3xs);
  color: var(--color-ink);
  font-size: var(--fs-body-lg);
  line-height: var(--lh-normal);
  transition: opacity var(--dur) var(--ease);
}

.profile-link:hover {
  opacity: 0.6;
}

.link-icon {
  flex-shrink: 0;
  width: 20px;
  height: 20px;
}

.bio {
  margin: 0;
}

.experience-card {
  /* Горизонтальный паддинг тот же, что у карточки профиля: карточки
     стоят друг под другом, и текст в них должен начинаться на одной
     вертикали. */
  padding: var(--space-md) var(--space-xl);
}

/* Аккордеон «Опыт» существует только на телефоне: на десктопе колонка
   и так высокая, список виден целиком и заголовок ему не нужен. */
.experience-toggle {
  display: none;
}

/* Each entry stacks its title (company — role) over the dates, with a
   тонкой линией между записями. */
.experience-row {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  /* Отступ до подписи задаёт сама подпись (4), не строка */
  gap: 0;
  width: 100%;
  /* Исключение из шкалы: 20px по вертикали — значение из макета */
  padding: 20px 0;
  border-bottom: 1px solid var(--color-border);
}

.experience-row:last-child {
  border-bottom: none;
}

.experience-title {
  margin: 0;
  width: 100%;
  font-family: var(--ff-display);
  font-size: var(--fs-body-lg);
  font-weight: var(--fw-semibold);
  font-variation-settings: "wdth" 132;
  line-height: var(--lh-tight);
  color: var(--color-heading);
}

/* Subheadline — подпись под заголовком: мельче основного текста и
   плотнее прижата к нему, чтобы пара читалась одним блоком. */
.experience-dates {
  margin: var(--space-3xs) 0 0;
  width: 100%;
  font-family: var(--ff-body);
  font-size: var(--fs-subheadline);
  font-weight: var(--fw-regular);
  line-height: var(--lh-subheadline);
  color: var(--color-ink-soft);
  font-variant-numeric: tabular-nums;
}

/* ---------- Right column ---------- */

.projects-col {
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
  flex: 1;
  min-width: 0;
}

/* Thin, unobtrusive scrollbars for the independently-scrolling columns. */
.profile-col-scroll,
.projects-col {
  scrollbar-width: thin;
  scrollbar-color: var(--color-scroll) transparent;
}

.profile-col-scroll::-webkit-scrollbar,
.projects-col::-webkit-scrollbar {
  width: 8px;
}

.profile-col-scroll::-webkit-scrollbar-thumb,
.projects-col::-webkit-scrollbar-thumb {
  background: var(--color-scroll);
  border-radius: var(--radius-xs);
}

.profile-col-scroll::-webkit-scrollbar-thumb:hover,
.projects-col::-webkit-scrollbar-thumb:hover {
  background: var(--color-scroll-hover);
}

.case-card {
  position: relative;
  justify-content: flex-end;
  gap: 0;
  width: 100%;
}

.case-stage {
  /* aspect-ratio collapses to 0 height when an ancestor has
     overflow: hidden in this environment, so size via the classic
     padding-bottom trick instead — 712 / 1220 from --stage-ratio. */
  position: relative;
  width: 100%;
  height: 0;
  padding-bottom: 58.3607%;
  border-radius: var(--radius-md);
  overflow: hidden;
  /* Lets the badges size themselves against the stage (cqw units).
     inline-size only — `size` would make the height independent of the
     padding-bottom trick and collapse the stage. */
  container-type: inline-size;
}

/* A single Kinescope clip fills the stage; the stage's padding-bottom
   carries the clip's own aspect ratio, so nothing is letterboxed. */
.case-stage-iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
}

/* Award badges sit over the clip in the stage's top-left corner */
.case-badges {
  position: absolute;
  top: var(--space-xl);
  left: var(--space-xl);
  z-index: 2;
  display: flex;
  align-items: flex-start;
  /* 2.03% of the stage width — 16px at a 1440px viewport — so the spacing
     shrinks with the card just like the badges do. */
  gap: 2.03cqw;
  pointer-events: none;
  /* Height in % of the stage (its padding-bottom makes that definite), so
     the badges shrink and grow with the card instead of staying fixed. */
  height: 8.15%;
}

.case-badge {
  display: block;
  height: 100%;
  width: auto;
}

/* Video cards keep the clip and the info panel visually separate */
/* Sequential Kinescope clips: every layer fills the stage, only the
   currently playing one is visible. */
.case-kin-seq,
.case-kin-players,
.case-kin-slot,
.case-kin-fallback {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
}

.case-kin-slot {
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--dur) var(--ease);
}

.case-kin-slot.is-active {
  opacity: 1;
  pointer-events: auto;
}

.case-kin-slot iframe {
  width: 100%;
  height: 100%;
  border: 0;
}

.case-stage-img,
.case-stage-video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transform-origin: center;
  transition: transform 0.5s var(--ease);
}

/* Кадр чуть уже сцены и прижат к низу. Скругление в 40 съедает у
   краёв больше, чем собственное поле кадра (7,7 % ширины), и на
   сцене уже 533 px срезает углы телефонов. Двадцати четырёх хватает
   в худшем случае — сцена 280 на экране в 320. Низ не отступает:
   на нём телефоны срезаны кадром и уходят под плашку.
   Правка точечная, под эти два кадра, а не общее правило. */
.case-stage-img {
  inset: auto 24px 0;
  width: calc(100% - 48px);
  height: auto;
}

/* Кадр подаётся вперёд на любой карточке с картинкой, в том числе на
   «Скоро»: движение здесь про живость ленты, а не про обещание
   перехода. Стрелка при этом сдвигается только там, где она есть, —
   вот она как раз и говорит, что карточка открывается. */
.case-card:hover .case-stage-img,
.case-card:hover .case-stage-video {
  transform: scale(1.03);
}

.case-stage-kinescope {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

.case-stage-kinescope iframe {
  width: 100%;
  height: 100%;
}

.case-card:has(.btn--wide-icon):hover .btn--wide-icon {
  transform: translateX(4px);
}

.case-content {
  display: flex;
  flex-direction: column;
  gap: var(--space-lg);
  padding: var(--space-xl);
  box-shadow: var(--shadow-card);
  /* Плашка приклеена к низу экрана с зазором 8. Браузер сам не пускает её
     выше верха карточки: пока карточка въезжает, плашка едет вместе с ней,
     как только целиком видна — останавливается и ждёт, а картинка
     проезжает под ней до своего места. Без скриптов и порогов, поэтому
     не бывает ни рывка, ни запаздывания. */
  position: sticky;
  bottom: 8px;
  z-index: 3; /* выше наград на кадре */
}


.tags {
  display: flex;
  /* На самых узких экранах три тега в строку не помещаются и
     распирают карточку — пусть переносятся. */
  flex-wrap: wrap;
  gap: var(--space-xs);
  width: 100%;
}

.case-summary {
  display: flex;
  align-items: flex-end;
  gap: var(--space-md);
  width: 100%;
}

/* Переход в кейс по всей плашке, а не по одной стрелке: цель размером
   72 x 48 мала и требует прицеливания. Растягиваем саму ссылку
   псевдоэлементом — в дереве доступности остаётся один переход с
   названием кейса в имени, а не два одинаковых. Наведение и нажатие
   кнопка получает от всей плашки: ховер приходит на ссылку.

   Системе координат взяться неоткуда не нужно: у .case-content уже
   есть position: sticky, и она сама её задаёт. Своё position сюда не
   ставить — им я однажды перебил sticky, и плашка отлипла от низа. */
.case-open::after {
  content: "";
  position: absolute;
  inset: 0;
}

.case-text {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  flex: 1;
  min-width: 0;
}

/* Только карточка на главной: тем же классом назван и заголовок
   страницы кейса, а он должен остаться крупным. */
.case-text .case-title {
  margin: 0;
  /* Заголовок карточки — того же кегля, что имя в профиле */
  font-size: clamp(20px, 1rem + 0.6vw, 24px);
  color: var(--color-heading);
}

.case-desc {
  margin: 0;
}

/* ---------- Responsive ---------- */

@media (max-width: 919px) {
  .page {
    flex-direction: column;
    padding: var(--space-lg);
    /* Колонки встали вертикально, и flex-start теперь задаёт не
       выравнивание, а ширину: правая колонка переставала слушаться
       flex: 1 и вылезала за паддинг горизонтальным скроллом. */
    align-items: stretch;
  }

  .profile-col {
    width: 100%;
  }
}

/* Desktop scroll model: the whole page scrolls (the right column is NOT a
   clipped scroll box — its cards flow freely and are never cut by a
   container). The left panel is sticky, so it stays pinned 24px from the
   top while the cases scroll past. If the left content is taller than the
   viewport it scrolls internally, staying within its own rounded frame. */
@media (min-width: 920px) {
  .profile-col {
    position: sticky;
    top: var(--space-lg);
    /* Именно max-height: с фиксированной высотой панель растягивалась на
       весь экран и её фон торчал под контентом — тот самый «подбородок». */
    max-height: calc(100dvh - 2 * var(--space-lg));
  }

  .profile-col-scroll {
    min-height: 0;
    overflow-y: auto;
    overscroll-behavior: contain;
  }
}

@media (max-width: 571px) {
  /* На телефоне панели идут от края до края: каждые 12 по бокам
     отнимали у содержимого ширину. Отступ от края экрана теперь несут
     сами элементы вне панелей — заголовки, чипы, кнопки. */
  .page {
    padding: var(--space-sm) 0;
    gap: var(--space-sm);
  }

  .profile-card {
    padding: 0 var(--space-lg) var(--space-lg);
    gap: var(--space-md);
  }

  /* Display / large-body sizes now scale fluidly via clamp(), so no
     hard font-size overrides are needed here. */

  :root {
    --avatar-size: 104px;
  }

  /* Теги набраны моноширинным, а он широкий: на телефоне строка
     из трёх чипов не помещалась и переносилась. */
  .chip {
    font-size: 13px;
  }

  .tool-grid {
    --tool-pad: var(--space-sm);
  }

  /* Вертикальные поля отдаёт шапка и строки — у всех по 20, один ритм
     сверху донизу, в свёрнутом виде плашка симметрична. */
  .experience-card {
    padding: 0 var(--space-lg);
  }

  .experience-toggle {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-sm);
    width: 100%;
    /* Свёрнутая плашка 80, и держат её поля шапки, а не min-height: поля
       умеют плавно меняться. При раскрытии они за ту же длительность
       съезжают к 20, и заголовок не перескакивает наверх рывком. */
    padding: 29px 0;
    /* Обычный ease-out, а не кривая раскрытия: та доезжает за первые
       200 мс, и заголовок всё равно вставал раньше, чем раскрылся
       список. Здесь движение распределено на всю длительность. */
    transition: padding 0.42s ease-out;
    border: 0;
    background: none;
    color: var(--color-ink);
    cursor: pointer;
    text-align: left;
    -webkit-tap-highlight-color: transparent;
  }

  .experience-card.is-open .experience-toggle {
    padding-block: 20px;
  }

  .experience-heading {
    font-family: var(--ff-display);
    font-size: clamp(20px, 1rem + 0.6vw, 24px);
    font-weight: var(--fw-semibold);
    font-variation-settings: "opsz" 96, "wdth" 132, "wght" 600;
    line-height: var(--lh-tight);
    letter-spacing: -0.015em;
  }

  /* Плюс и минус лежат друг на друге и меняются поворотом: так знак
     не прыгает по базовой линии и читается как одно движение. */
  .experience-sign {
    position: relative;
    flex: none;
    width: 20px;
    height: 20px;
  }

  .sign-icon {
    position: absolute;
    inset: 0;
    transition: opacity 0.2s ease, transform 0.38s cubic-bezier(0.32, 0.72, 0, 1);
  }

  .sign-minus {
    opacity: 0;
    transform: rotate(-90deg);
  }

  [aria-expanded="true"] .sign-plus {
    opacity: 0;
    transform: rotate(90deg);
  }

  [aria-expanded="true"] .sign-minus {
    opacity: 1;
    transform: none;
  }

  /* Высоту ставит script.js — она считается по содержимому, иначе
     переход от нуля к auto браузер не анимирует. */
  .experience-fold {
    height: 0;
    overflow: hidden;
    transition: height 0.42s cubic-bezier(0.32, 0.72, 0, 1);
  }

  .experience-row:first-child {
    border-top: 1px solid var(--color-border);
  }

  /* Записи не просто проявляются вместе с высотой, а въезжают одна за
     другой — шаг задаёт --i, номер строки из разметки. */
  .experience-row {
    opacity: 0;
    transform: translateY(6px);
    transition: opacity 0.26s ease, transform 0.42s cubic-bezier(0.32, 0.72, 0, 1);
  }

  .is-open .experience-row {
    opacity: 1;
    transform: none;
    transition-delay: calc(60ms + var(--i, 0) * 55ms);
  }

  .case-content {
    padding: var(--space-lg);
    gap: var(--space-md);
  }

  .case-summary {
    flex-wrap: wrap;
  }
}

/* ---------- Подвал со ссылкой на политику ---------- */

.site-footer {
  display: flex;
  justify-content: center;
  padding: var(--space-lg) 0 var(--space-xs);
}

.site-footer a {
  font-family: var(--ff-mono);
  font-size: var(--fs-mono-md);
  color: var(--color-ink-soft);
  text-decoration: none;
  transition: color var(--dur) var(--ease);
}

.site-footer a:hover {
  color: var(--color-ink);
}

/* Кнопка остаётся спутником имени, пока рядом с ней имени хватает
   места — до 360. Уже на 359 колонка с именем сжимается до 190 и
   выглядит зажатой, поэтому перестраиваемся: кнопка уходит
   под ссылки и занимает всю ширину карточки. Отступы повторяют макет —
   внутри группы «имя + ссылки» 16, между группами 24. */
@media (max-width: 359px) {
  .profile-body {
    display: flex;
    flex-direction: column;
    gap: var(--space-md);
  }

  .profile-identity {
    order: 1;
  }

  .profile-links {
    order: 2;
  }

  .contact-pill {
    order: 3;
    width: 100%;
    padding: 0 var(--space-lg);
    margin-top: var(--space-xs);
  }

  .contact-label {
    display: inline;
  }

  .bio {
    order: 4;
    margin-top: var(--space-xs);
  }
}

/* ---------- Язык и тема ----------
   Живут в сетке логотипов и выглядят как её клавиши: язык занимает одну
   ячейку, тема — две (49 + 4 + 49 = 102, размеры из макета). Радиус и
   высота здесь от сетки, а не от кнопочной системы сайта. */
/* Язык и тема — три последние клавиши нижнего ряда при любом числе
   столбцов, поэтому место задано от правого края сетки. */
.tool-slot {
  display: flex;
  grid-row: 3;
  grid-column: -4 / -3;
}

.tool-slot--wide {
  grid-column: -3 / -1;
}

.lang-switch {
  display: grid;
  place-items: center;
  width: 100%;
  height: 100%;
  border-radius: var(--radius-sm);
  background: var(--color-cell);
  font-family: var(--ff-body);
  font-size: 12px;
  line-height: var(--lh-normal);
  color: var(--color-ink);
  transition: background var(--dur) var(--ease);
}

.lang-switch:hover {
  background: var(--color-cell-hover);
}

/* ---------- Переключатель ----------
   Одна дорожка на весь сайт: тема, устройство в кейсе, режим просмотра,
   табы. Выбранный сегмент показывает бегунок, который под него едет.
   Раньше у каждого переключателя были своя заливка дорожки, свои поля
   и свой способ показать выбор — четыре разных вида одного компонента.

   Сегментов всегда два, поэтому положение бегунка считает CSS, без
   скрипта: активным бывает первый или последний. Выбор размечают
   по-разному (класс, aria-checked, aria-pressed) — компонент понимает
   все три, чтобы не переписывать разметку и скрипты. */
.switch {
  --sw-pad: var(--space-3xs);
  position: relative;
  display: flex;
  gap: var(--sw-pad);
  padding: var(--sw-pad);
  border-radius: var(--radius-pill);
  /* Настоящая пилюля, а не глобальный суперэллипс: иначе дорожка и
     бегунок скругляются по-разному и на торце видна губа. Кнопки
     сайта сделаны так же. */
  corner-shape: round;
  background: var(--color-track);
}

/* Крупный размер — табы кейса: сегмент выше, поля те же 4 */
.switch--lg {
  --sw-pad: var(--space-3xs);
}

/* Бегунок: ровно половина дорожки без полей и просвета */
.switch::before {
  content: "";
  position: absolute;
  z-index: 0;
  top: var(--sw-pad);
  left: var(--sw-pad);
  width: calc((100% - var(--sw-pad) * 3) / 2);
  height: calc(100% - var(--sw-pad) * 2);
  border-radius: var(--radius-pill);
  corner-shape: round;
  background: var(--color-knob);
  box-shadow: var(--shadow-knob);
  transition: transform var(--dur) var(--ease);
}

.switch:has(> :last-child.is-active)::before,
.switch:has(> :last-child[aria-checked="true"])::before,
.switch:has(> :last-child[aria-pressed="true"])::before {
  transform: translateX(calc(100% + var(--sw-pad)));
}

.switch > * {
  position: relative;
  z-index: 1;
  flex: 1;
  display: grid;
  place-items: center;
  min-width: 0;
  padding: 0;
  border: 0;
  border-radius: var(--radius-pill);
  corner-shape: round;
  background: none;
  color: var(--color-ink-soft);
  cursor: pointer;
  transition: color var(--dur) var(--ease);
}

.switch > *:hover,
.switch > .is-active,
.switch > [aria-checked="true"],
.switch > [aria-pressed="true"] {
  color: var(--color-ink);
}

.switch svg {
  width: 20px;
  height: 20px;
}

.theme-switch {
  width: 100%;
}

/* Переключатель темы в сетке инструментов — не общий переключатель, а
   клавиша: своё скругление 12 и заливка выбранного сегмента без
   бегунка. Под общий .switch его не сводим — сетка клавиш это
   отдельная вещь. */
.tool-slot .theme-switch {
  display: flex;
  gap: var(--space-3xs);
  padding: var(--space-3xs);
  border-radius: var(--radius-sm);
  background: var(--color-cell);
}

.tool-slot .theme-option {
  flex: 1;
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  /* Скругление выбранного сегмента на 3 меньше внешнего — как у плашек */
  border-radius: 9px;
  background: none;
  color: var(--color-ink-soft);
  cursor: pointer;
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease);
}

.tool-slot .theme-option:hover {
  color: var(--color-ink);
}

.tool-slot .theme-option[aria-checked="true"] {
  background: var(--color-cell-active);
  color: var(--color-ink);
}

.tool-slot .theme-icon {
  width: 16px;
  height: 16px;
}

.theme-icon {
  display: block;
}

/* Столбцов меньше шести — прячем логотипы с конца: пустое место в ряду
   лучше половины значка, обрезанной краем. Пороги — ширина колонки, при
   которой в сетку перестаёт помещаться очередной столбец 48 + зазор 4. */
@container (max-width: 331px) {
  .tool-cell--logo:nth-child(6) { display: none; }
}

@container (max-width: 279px) {
  .tool-cell--logo:nth-child(5) { display: none; }
}

@container (max-width: 227px) {
  .tool-cell--logo:nth-child(4) { display: none; }
}


/* Кнопка темы на страницах кейсов показывает ту тему, в которую
   переключит: в светлой — луна, в тёмной — солнце. */
.theme-toggle .theme-icon--dark { display: none; }

:root[data-theme="dark"] .theme-toggle .theme-icon--light { display: none; }
:root[data-theme="dark"] .theme-toggle .theme-icon--dark { display: block; }

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .theme-toggle .theme-icon--light { display: none; }
  :root:not([data-theme="light"]) .theme-toggle .theme-icon--dark { display: block; }
}
