/* Лёгкое начертание — для призывов написать (в основном Inter один файл
   на все веса, тоньше обычного он не рисуется) */
@font-face {
  font-family: 'Inter Light';
  src: url('assets/fonts/Inter-Light-cyrillic.woff2') format('woff2');
  font-weight: 300;
  font-display: swap;
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: 'Inter Light';
  src: url('assets/fonts/Inter-Light-latin.woff2') format('woff2');
  font-weight: 300;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Inter';
  src: url('assets/fonts/Inter-Regular.woff2') format('woff2');
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

/* ─── Reset ─── */
*, *::before, *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

/* ─── Tokens ─── */
:root {
  --font-main: 'Georgia', 'Times New Roman', serif;
  --font-mono: ui-monospace, 'SF Mono', 'Cascadia Code', 'Segoe UI Mono', 'Roboto Mono', 'Oxygen Mono', 'Ubuntu Monospace', 'Courier New', monospace;

  --transition: 0.4s ease;

  /* Light */
  --bg: #ffffff;
  --fg: #0a0a0a;
  --fg2: #999;
  --border: rgba(0, 0, 0, 0.08);
  --card-hover: #f5f5f5;
}

body.dark {
  --bg: #0a0a0a;
  --fg: #f0f0ec;
  --fg2: #555;
  --border: rgba(255, 255, 255, 0.07);
  --card-hover: #151515;
}

/* ─── Base ─── */
body {
  font-family: var(--font-main);
  background-color: var(--bg);
  color: var(--fg);
  transition: background-color var(--transition), color var(--transition);
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

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

img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* ─── Header ─── */
header {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  padding: 28px 40px;
  position: sticky;
  top: 0;
  background-color: var(--bg);
  z-index: 10;
  border-bottom: 0.5px solid var(--border);
  transition: background-color var(--transition), backdrop-filter var(--transition);
}

header.scrolled {
  background-color: rgba(255, 255, 255, 0.7);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
}

body.dark header.scrolled {
  background-color: rgba(10, 10, 10, 0.7);
}

.site-name {
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

nav {
  display: flex;
  align-items: baseline;
  gap: 28px;
}

nav a {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.07em;
  color: var(--fg2);
  transition: color 0.2s;
}

nav a:hover {
  color: var(--fg);
}

/* ─── Lang & Mode buttons ─── */
.lang-btn,
.burger-btn {
  background: none;
  border: 0.5px solid var(--border);
  color: var(--fg2);
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.07em;
  padding: 3px 8px;
  border-radius: 2px;
  cursor: pointer;
  transition: color 0.2s, border-color 0.2s;
}

.lang-btn:hover,
.burger-btn:hover {
  color: var(--fg);
  border-color: var(--fg);
}

/* ─── Nav menu ─── */
.nav-menu {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  width: 220px;
  background-color: var(--bg);
  border-left: 0.5px solid var(--border);
  display: flex;
  flex-direction: column;
  padding: 80px 32px 40px;
  gap: 4px;
  z-index: 20;
  transform: translateX(100%);
  transition: transform 0.3s ease;
}

.nav-menu.open {
  transform: translateX(0);
}

.nav-menu a {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.07em;
  color: var(--fg2);
  padding: 10px 0;
  border-bottom: 0.5px solid var(--border);
  transition: color 0.2s;
}

.nav-menu a:hover {
  color: var(--fg);
}

.header-right {
  display: flex;
  align-items: baseline;
  gap: 10px;
}

.nav-overlay {
  position: fixed;
  inset: 0;
  z-index: 19;
  display: none;
}

.nav-overlay.open {
  display: block;
}

/* ─── Main ─── */
main {
  flex: 1;
  padding: 64px 40px 80px;
  max-width: 1200px;
  width: 100%;
  margin: 0 auto;
}

/* ─── Hero ─── */
.hero {
  margin-bottom: 72px;
}

.hero h1 {
  font-size: clamp(32px, 5vw, 52px);
  font-weight: 400;
  letter-spacing: -0.03em;
  line-height: 1.15;
  margin-bottom: 12px;
}

.hero .sub {
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--fg2);
  letter-spacing: 0.06em;
}

.tags-row {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

.tag-btn {
  background: none;
  border: none;
  padding: 0;
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.06em;
  color: var(--fg2);
  cursor: pointer;
  transition: color 0.2s;
}

@media (hover: hover) {
  .tag-btn:hover {
    color: var(--fg);
  }
}

.tag-btn.active {
  color: var(--fg);
  text-decoration: underline;
  text-underline-offset: 3px;
}

.tag-sep {
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--fg2);
  pointer-events: none;
}

/* ─── Grid ─── */
.grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 1px;
  border: 0.5px solid var(--border);
}

/* ─── Card ─── */
.card {
  display: flex;
  flex-direction: column;
  border: 0.5px solid var(--border);
  cursor: pointer;
  transition: background-color 0.2s;
  overflow: hidden;
  user-select: none;
  -webkit-user-select: none;
}

.card.hovered {
  background-color: var(--card-hover);
}

.card-inner {
  aspect-ratio: 1 / 1;
  overflow: hidden;
  background-color: var(--bg);
  transition: opacity 0.2s;
  display: flex;
  align-items: center;
  justify-content: center;
}

.card-icon {
  width: 48px;
  height: 48px;
  color: rgba(255, 255, 255, 0.2);
  transition: color 0.3s, transform 0.3s;
  user-select: none;
  pointer-events: none;
}

@keyframes iconPulse {
  0%, 100% { opacity: 0.4; }
  50% { opacity: 1; }
}

.card.loading .card-icon {
  animation: iconPulse 1.4s ease-in-out infinite;
}

.card.loading.hovered .card-icon,
.card.loading.active .card-icon {
  animation: none;
}

.card.hovered .card-icon {
  transform: scale(0.85);
}

.card.hovered .card-icon {
  color: var(--card-color, #888);
}

.card.active .card-icon {
  color: #e8e8e8;
}

.card.active.hovered .card-icon {
  color: var(--card-color, #888);
}

.card.active.hovered .card-thumb {
  filter: grayscale(0);
  transform: translateZ(0) scale(0.85);
}

.card-thumb {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  filter: grayscale(1);
  transition: filter 0.3s, transform 0.3s;
  transform: translateZ(0) scale(1);
}

.card.hovered .card-thumb {
  filter: grayscale(0);
  transform: translateZ(0) scale(0.85);
}

.card.active .card-thumb {
  filter: grayscale(1) brightness(3);
}

.panel-list .card-inner::after {
  content: '';
  position: absolute;
  inset: 0;
  background: #0a0a0a;
  opacity: 0.82;
  transition: opacity 0.3s;
  pointer-events: none;
}

.panel-list .card.hovered .card-inner::after,
.panel-list .card.active .card-inner::after {
  opacity: 0;
}

/* Пока проект не выбран и мышь ещё не заходила в список, знаки проектов
   вразнобой дышат яркостью — видно, что по ним можно нажать. Период и
   сдвиг фазы у каждой плитки свои, их задаёт main.js.
   Мигает именно знак, а не подложка: раньше анимация висела на .card-inner,
   а она лежит поверх рамки плитки, и вместе со знаком пульсировали фон и
   разделительные линии сетки. */
@keyframes tilePulse {
  0%, 100% { opacity: 0.07; }
  50%      { opacity: 0.36; }
}

/* на время подсказки вуаль снимается: её роль — приглушать знак — берёт
   на себя сама прозрачность знака, зато подложка остаётся неподвижной */
.cards-grid.pulsing .card:not(.active):not(.hovered) .card-inner::after {
  opacity: 0;
}

.cards-grid.pulsing .card:not(.active):not(.hovered) .card-thumb,
.cards-grid.pulsing .card:not(.active):not(.hovered) .card-icon {
  animation: tilePulse var(--pulse-dur, 5s) ease-in-out var(--pulse-delay, 0s) infinite;
}

@media (prefers-reduced-motion: reduce) {
  .cards-grid.pulsing .card .card-thumb,
  .cards-grid.pulsing .card .card-icon {
    animation: none;
  }
  .cards-grid.pulsing .card:not(.active):not(.hovered) .card-inner::after {
    opacity: 0.82;
  }
}

.card-label {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.06em;
  color: var(--fg2);
  padding: 10px 14px;
  border-top: 0.5px solid var(--border);
  transition: color 0.2s;
  text-transform: lowercase;
  text-align: center;
}

.card:hover .card-label {
  color: var(--fg);
}

/* ─── Footer ─── */
footer {
  padding: 20px 40px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  border-top: 0.5px solid var(--border);
}

.footer-copy {
  font-family: var(--font-mono);
  font-size: 10px;
  color: var(--fg2);
  letter-spacing: 0.05em;
}

.footer-email {
  font-family: var(--font-mono);
  font-size: 10px;
  color: var(--fg2);
  letter-spacing: 0.05em;
  text-decoration: none;
  transition: color 0.2s;
}

.footer-email:hover {
  color: var(--fg);
}

/* ─── Hero (split-layout variant) ─── */
body.page-split .hero {
  padding: 28px 40px;
  border-bottom: 0.5px solid var(--border);
  margin-bottom: 0;
  display: flex;
  align-items: baseline;
  gap: 24px;
  flex-wrap: wrap;
  flex-shrink: 0;
}

body.page-split .hero h1 {
  font-size: clamp(20px, 2.5vw, 32px);
  margin-bottom: 0;
}

body.page-split .hero .sub {
  margin-top: 0;
}

/* ─── Split layout (index) ─── */
body.page-split {
  height: 100vh;
  overflow: hidden;
}

body.page-split .layout {
  flex: 1;
  min-height: 0;
  display: grid;
  grid-template-columns: 1fr 3fr;
  overflow: hidden;
}

.panel-list {
  overflow-y: auto;
  border-right: 0.5px solid var(--border);
  scrollbar-width: thin;
  scrollbar-color: var(--border) transparent;
}

.panel-list::-webkit-scrollbar {
  width: 2px;
}

.panel-list::-webkit-scrollbar-track {
  background: transparent;
}

.panel-list::-webkit-scrollbar-thumb {
  background-color: var(--border);
}

body.page-texts .panel-list {
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

.panel-search {
  flex-shrink: 0;
  padding: 11px 16px;
  border-bottom: 0.5px solid var(--border);
}

.panel-search input {
  width: 100%;
  background: none;
  border: none;
  outline: none;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.04em;
  color: var(--fg);
  -webkit-appearance: none;
  appearance: none;
}

.panel-search input::placeholder {
  color: var(--fg2);
  opacity: 0.4;
}

.cards-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 1px;
}

button.card {
  -webkit-appearance: none;
  appearance: none;
  background: var(--bg);
  border: 0.5px solid var(--border);
  padding: 0;
  font: inherit;
  text-align: left;
  cursor: pointer;
  width: 100%;
  transition: background-color 0.2s;
}

/* Square tiles in left panel */
.panel-list .card {
  position: relative;
  aspect-ratio: 1 / 1;
}

.panel-list .card-inner {
  position: absolute;
  inset: 0;
  aspect-ratio: unset;
}

.panel-list .card-label {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  border-top: 0.5px solid var(--border);
  background-color: var(--bg);
  transition: color 0.2s, background-color 0.2s, transform 0.25s ease;
  transform: translateY(100%);
}

.panel-list .card.hovered .card-label,
.panel-list .card.active .card-label {
  background-color: var(--card-hover);
  transform: translateY(0);
}

.card.active {
  background-color: var(--card-hover);
}

.card.active .card-label {
  color: var(--fg);
}

/* ─── Кейс на весь экран ─── */
/* Кнопка живёт в .layout, а не в панели: панель целиком перерисовывается
   при каждом открытии кейса и унесла бы кнопку с собой. */
.case-expand {
  position: absolute;
  top: 12px;
  right: 16px;
  z-index: 9;
  font-family: var(--font-mono);
  font-size: 8px;
  letter-spacing: 0.1em;
  background: none;
  border: none;
  padding: 7px 10px;
  cursor: pointer;
  /* кнопка висит поверх страницы кейса, а та бывает и чёрной, и белой.
     difference инвертирует то, что под кнопкой, поэтому она читается на
     любом фоне без подложки: на светлом становится тёмной, на тёмном —
     светлой. Серый в покое, белый (максимальный контраст) на наведении. */
  color: #a8a8a8;
  mix-blend-mode: difference;
  transition: color 0.2s;
}

/* от рамки остаются только уголки: восемь отрезков фоновыми градиентами
   на одном псевдоэлементе — так подпись кнопки не трогаем, её переписывает
   js при смене языка и состояния */
.case-expand::before {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    linear-gradient(currentColor, currentColor)   0    0    / 7px 0.5px no-repeat,
    linear-gradient(currentColor, currentColor)   0    0    / 0.5px 7px no-repeat,
    linear-gradient(currentColor, currentColor) 100%   0    / 7px 0.5px no-repeat,
    linear-gradient(currentColor, currentColor) 100%   0    / 0.5px 7px no-repeat,
    linear-gradient(currentColor, currentColor)   0  100%   / 7px 0.5px no-repeat,
    linear-gradient(currentColor, currentColor)   0  100%   / 0.5px 7px no-repeat,
    linear-gradient(currentColor, currentColor) 100% 100%   / 7px 0.5px no-repeat,
    linear-gradient(currentColor, currentColor) 100% 100%   / 0.5px 7px no-repeat;
}

.case-expand:hover {
  color: #ffffff;
}

body.page-split .layout {
  position: relative;
}

body.case-full header,
body.case-full .hero,
body.case-full .panel-list,
body.case-full footer {
  display: none;
}

body.case-full .layout {
  grid-template-columns: 1fr;
}

/* ─── Project detail panel ─── */
@keyframes pdFadeIn {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: translateY(0); }
}

.pd-title {
  animation: pdFadeIn 0.5s ease both;
}

.pd-desc {
  animation: pdFadeIn 0.5s ease 0.2s both;
}

.pd-meta {
  animation: pdFadeIn 0.5s ease 0.4s both;
}

.panel-detail {
  overflow-y: auto;
  padding: 48px 56px;
}

/* Страница проекта несёт собственную шапку (pf-header), поэтому панель
   отдаётся ей целиком. Прокрутка — внутри iframe: так Tilda подгружает
   картинки по мере скролла, а не все разом. */
.panel-detail.panel-iframe {
  overflow: hidden;
  padding: 0;
}

.panel-detail.panel-iframe > iframe.project-iframe {
  width: 100%;
  height: 100%;
  border: none;
  display: block;
}


.pd-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 24px;
  flex-wrap: wrap;
  margin-bottom: 32px;
  padding-bottom: 24px;
  border-bottom: 0.5px solid var(--border);
}

.pd-title {
  font-size: clamp(22px, 2.5vw, 36px);
  font-weight: 300;
  font-family: 'Inter', sans-serif;
  letter-spacing: -0.01em;
}

.pd-meta {
  display: flex;
  gap: 28px;
  flex-shrink: 0;
}

.pd-meta-item {
  display: flex;
  flex-direction: column;
  gap: 5px;
}

.pd-meta-key {
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: 0.1em;
  color: var(--fg2);
  text-transform: uppercase;
}

.pd-meta-val {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.04em;
  color: var(--fg);
}

.pd-gallery {
  display: flex;
  flex-direction: column;
  gap: 1px;
  margin-bottom: 32px;
}

.pd-img {
  width: 100%;
  aspect-ratio: 16 / 9;
  background-color: var(--card-hover);
}

.pd-img--tall {
  aspect-ratio: 4 / 3;
}

.pd-img img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.pd-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1px;
}

.pd-row .pd-img {
  aspect-ratio: 1 / 1;
}

.pd-desc {
  max-width: 520px;
  margin-bottom: 32px;
}

/* ссылка в описании (например, на сайт клиента) — тонкое подчёркивание */
.pd-desc a {
  color: inherit;
  text-decoration: underline;
  text-decoration-thickness: 0.5px;
  text-underline-offset: 3px;
  transition: opacity 0.2s ease;
}
.pd-desc a:hover { opacity: 0.6; }

.pd-desc p, .pd-desc div {
  font-size: 12px;
  line-height: 1.8;
  color: var(--fg);
  font-family: var(--font-mono);
  letter-spacing: 0.03em;
}

/* ─── Posts list (texts page) ─── */
body.page-texts .panel-list {
  overflow-y: hidden;
  scrollbar-width: none;
  padding: 0;
}

.posts-list {
  flex: 1;
  overflow-y: auto;
  scrollbar-width: thin;
  scrollbar-color: var(--border) transparent;
}

.posts-list::-webkit-scrollbar {
  width: 2px;
}

.posts-list::-webkit-scrollbar-track {
  background: transparent;
}

.posts-list::-webkit-scrollbar-thumb {
  background-color: var(--border);
}

.post-item {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  width: 100%;
  padding: 14px 20px;
  background: none;
  border: none;
  border-bottom: 0.5px solid var(--border);
  cursor: pointer;
  text-align: left;
  -webkit-appearance: none;
  appearance: none;
}

.post-item:first-child {
  border-top: 0.5px solid var(--border);
}

.post-item-title {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.04em;
  color: var(--fg2);
  line-height: 1.5;
  transition: color 0.2s;
}

.post-item-year {
  font-family: var(--font-mono);
  font-size: 10px;
  color: var(--fg2);
  opacity: 0.4;
  flex-shrink: 0;
}

.post-item.active .post-item-title {
  color: var(--fg);
}

.post-item.active .post-item-year {
  opacity: 0.7;
}

@media (hover: hover) {
  .post-item:hover .post-item-title {
    color: var(--fg);
  }
}

/* Description sits inside the header, below the title, above its divider (texts page) */
body.page-texts .pd-header {
  flex-direction: column;
  align-items: stretch;
  gap: 12px;
}

body.page-texts .pd-header-top {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 24px;
  flex-wrap: wrap;
}

body.page-texts .pd-header .pd-desc {
  margin-bottom: 0;
}

/* ─── Post body text ─── */
.pd-body {
  margin-top: 8px;
  animation: pdFadeIn 0.5s ease 0.6s both;
}

/* :where — исключение для перенесённых кейсов (.pf-tz) не добавляет правилу
   веса: иначе оно перебивало собственные размеры картинок в кейсах (PuppAI) */
.pd-body p:where(:not(.pf-tz *)) {
  font-family: 'Inter', sans-serif;
  font-weight: 300;
  font-size: 15px;
  line-height: 1.85;
  color: var(--fg);
  margin-bottom: 24px;
  max-width: 560px;
}

.pd-body p.pd-subhead:where(:not(.pf-tz *)) {
  font-family: var(--font-mono);
  font-size: 12px;
  line-height: 1.8;
  letter-spacing: 0.03em;
  color: var(--fg);
  font-weight: 400;
  margin-top: 16px;
}

/* Пустое состояние панели (проект не выбран) */
.pd-empty {
  min-height: 60vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  color: var(--fg2);
}

.pd-intro {
  position: relative;  /* top выставляет centerIntro() — центр экрана */
  max-width: 420px;
  text-align: center;
  animation: pdFadeIn 0.6s ease both;
}

/* Чувачок над именем (main.js → mountGuy). Линии той же толщины, что
   и прописанное имя (3 в Lottie × 0.42 ≈ 1.26px): при ширине 34px
   единица viewBox (140) — это 0.243px, отсюда stroke-width 5.2.
   Толщину задаём в единицах рисунка, а не non-scaling-stroke, иначе
   ломается прорисовка штрихом (stroke-dash) на заставке. */
.pd-guy {
  display: block;
  width: 34px;
  height: auto;
  margin: 0 auto 20px;
  overflow: visible;
  color: var(--fg);
}
.pd-guy path,
.pd-guy line {
  fill: none;
  stroke: currentColor;
  stroke-width: 5.2;
  stroke-linecap: round;
}

/* Сменяющаяся роль под именем (main.js → mountRole). Ширину места
   задаёт скрипт по самому длинному слову; новое слово въезжает справа,
   старое уходит влево и гаснет. */
.pd-role {
  position: relative;
  display: inline-block;
  text-align: center;
  white-space: nowrap;
}
/* запятая после имени не занимает места — имя остаётся ровно под
   чувачком (ширина знака в моноширинном шрифте — 1ch + разрядка) */
.pd-name-comma,
.pd-role-dot { margin-right: calc(-1ch - 0.06em); }  /* и точка после роли */
.pd-intro-text { white-space: nowrap; }  /* запятая не переносится от имени */

/* роль — отдельной строкой под именем */
.pd-intro-rest { display: block; }
/* невидимая распорка держит высоту и базовую линию строки, а слова
   лежат поверх неё, каждое по центру места — при смене уходящее и
   приходящее на одной строке */
.pd-role-sizer { visibility: hidden; }
.pd-role-word {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  text-align: center;
  transition: transform 0.45s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.45s ease;
}
.pd-role-word.is-out { transform: translateX(-1.6em); opacity: 0; }
.pd-role-word.is-pre { transform: translateX(1.6em); opacity: 0; transition: none; }
@media (prefers-reduced-motion: reduce) {
  .pd-role-word { transition: none; }
}

/* одним абзацем, без деления на имя и должность */
.pd-intro-text {
  margin: 0;
  font-family: var(--font-mono);
  font-size: 11px;
  line-height: 1.9;
  letter-spacing: 0.06em;
  color: var(--fg);
  opacity: 0.9;
}

/* Одна фраза под именем — чем занимаюсь; тише имени и роли */
.pd-intro-tag {
  margin: 18px auto 0;
  max-width: 300px;
  font-family: 'Inter', sans-serif;
  font-size: 13px;
  font-weight: 300;
  line-height: 1.6;
  color: var(--fg2);
}

/* Призыв в конце кейса */
.pd-cta {
  max-width: 560px;
  margin: 150px auto 0;
  padding: 0 20px 170px;
  text-align: center;
}
.pd-cta-text {
  margin: 0 0 40px;
  font-family: 'Inter Light', 'Inter', sans-serif;
  font-size: 15px;
  font-weight: 300;
  line-height: 1.7;
  color: var(--fg);
}
.pd-cta-links { display: flex; justify-content: center; gap: 28px; }
.pd-cta-links a {
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.12em;
  text-transform: lowercase;
  color: var(--fg);
  text-decoration: none;
  border-bottom: 0.5px solid var(--border);
  padding-bottom: 4px;
}
.pd-cta-links a { position: relative; }
/* яркая линия не проявляется, а рисуется слева направо поверх тусклой */
.pd-cta-links a::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: -0.5px;
  height: 0.5px;
  background-color: var(--fg);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 0.35s ease;
}
.pd-cta-links a:hover::after { transform: scaleX(1); }
/* в русской версии первым идёт Telegram */
html[lang="ru"] .pd-cta-tg { order: -1; }
@media (max-width: 768px) {
  .pd-cta { margin-top: 104px; padding-bottom: 128px; }
}

/* Имя внутри того же абзаца — ссылка на страницу «обо мне».
   Выделено только насыщенностью, размер и шрифт общие с абзацем.
   На наведении из-под него выезжает линия: масштабируется от левого края,
   а не проявляется целиком, поэтому читается как росчерк. */
.pd-intro-name {
  position: relative;
  display: inline-block;  /* чтобы на заставке сдвигаться transform'ом */
  font-weight: 700;
  color: inherit;
  text-decoration: none;
}

.pd-intro-name::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  /* рамка ссылки — строка с межстрочным 1.9, буквы в её середине; линия
     поднята внутрь рамки, чтобы шла сразу под буквами */
  bottom: 4px;
  height: 0.5px;
  background-color: currentColor;
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 0.35s ease;
}

.pd-intro-name:hover::after {
  transform: scaleX(1);
}

/* Имя — Lottie, прописанная линией (main.js → mountNameAnim). Обычный
   текст стоит, пока анимация не загрузилась, и остаётся, если не вышло.
   Видна Lottie на языке страницы. */
.pd-name-anim { display: none; }
.pd-intro-text.has-anim .pd-name-text {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}
html[lang="ru"] .pd-intro-text.has-anim .pd-name-anim[data-name="ru"],
html[lang="en"] .pd-intro-text.has-anim .pd-name-anim[data-name="en"] {
  display: inline-block;
  vertical-align: -2px;
}
.pd-name-anim svg { display: block; }
.pd-name-anim path { stroke: currentColor; }

/* Заставка на пустой главной: прописывается имя, за ним линиями
   рисуется чувачок (main.js → drawGuyIn), затем внахлёст проявляется
   остальное — хвост фразы, подсказка, шапка с тегами, список, подвал. */
html.intro-play header,
html.intro-play .hero,
html.intro-play .panel-list,
html.intro-play footer,
html.intro-play .pd-point,
html.intro-play .pd-name-comma,
html.intro-play .pd-intro-tag,
html.intro-play .pd-intro-rest {
  opacity: 0;
  pointer-events: none;
}
/* у подсказки своя анимация появления с fill: both — она перебила бы
   opacity; на заставке её снимаем, и после она сыграет заново сама */
html.intro-play .pd-point { animation: none; }
/* пока идёт заставка, запасной текст имени не показываем — иначе он
   мелькает до загрузки Lottie, и имя будто «сбрасывается» */
html.intro-play .pd-intro-text:not(.has-anim) .pd-name-text { opacity: 0; }
html.intro-reveal header,
html.intro-reveal .hero,
html.intro-reveal .panel-list,
html.intro-reveal footer,
html.intro-reveal .pd-point,
html.intro-reveal .pd-name-comma,
html.intro-reveal .pd-intro-tag,
html.intro-reveal .pd-intro-rest {
  transition: opacity 0.9s ease;
}
html.intro-reveal .pd-intro-tag { transition-delay: 0.3s; }
html.intro-reveal .pd-point   { transition-delay: 0.2s; }
html.intro-reveal header,
html.intro-reveal .hero       { transition-delay: 0.35s; }
html.intro-reveal .panel-list { transition-delay: 0.5s; }
html.intro-reveal footer      { transition-delay: 0.6s; }
@media (prefers-reduced-motion: reduce) {
  html.intro-reveal header,
  html.intro-reveal .hero,
  html.intro-reveal .panel-list,
  html.intro-reveal footer,
  html.intro-reveal .pd-point,
  html.intro-reveal .pd-intro-tag,
  html.intro-reveal .pd-intro-rest { transition: none; }
}

@media (prefers-reduced-motion: reduce) {
  .pd-intro-name::after { transition: none; }
}

/* Стрелка к списку слева — отдельным ярусом, ниже блока «обо мне» */
.pd-point {
  position: relative;  /* на десктопе едет вместе с именем (centerIntro) */
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  margin-top: clamp(48px, 12vh, 120px);
  animation: pdFadeIn 0.6s ease 0.25s both;
}

/* лёгкое движение влево — туда, куда смотрит стрелка (только десктоп,
   на мобильном стрелки нет).
   На движении она ещё и чуть вытягивается: начало координат — у хвоста,
   поэтому растягивается она остриём вперёд, а не в обе стороны */
@keyframes arrowNudge {
  0%, 100% { transform: translateX(0) scaleX(1); }
  50%      { transform: translateX(-5px) scaleX(1.1); }
}

.pd-arrow {
  width: 88px;
  height: 24px;
  color: var(--fg);
  opacity: 0.45;
  transform-origin: right center;
  animation: arrowNudge 2.8s ease-in-out 0.9s infinite;
}

@media (prefers-reduced-motion: reduce) {
  .pd-arrow {
    animation: none;
  }
}

/* строка подсказки: на десктопе только текст, на телефоне по бокам —
   стрелки вверх, к ленте кейсов */
.pd-hint-row {
  display: flex;
  align-items: center;
  gap: 14px;
}
.pd-up { display: none; }

.pd-empty-text {
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.08em;
  color: var(--fg2);
}

@keyframes upNudge {
  0%, 100% { transform: translateY(0) scaleY(1); }
  50%      { transform: translateY(-4px) scaleY(1.1); }
}
/* мигание подсказки на мобильном (сама анимация включается в медиазапросе) */
@keyframes hintPulse {
  0%, 100% { opacity: 0.4; }
  50%      { opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .pd-hint-row, .pd-up { animation: none !important; }
}

.post-img-placeholder {
  width: 100%;
  aspect-ratio: 16 / 9;
  background: var(--card-hover);
  margin: 32px 0;
  border-radius: 1px;
}

.post-loading {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--fg2);
  letter-spacing: 0.1em;
  padding: 8px 0;
  animation: iconPulse 1.4s ease-in-out infinite;
}

/* ─── Загрузка проекта ─── */
/* Кольцо-прелоадер: бледная окружность целиком и поверх неё дуга, которая
   не только крутится, но и меняет длину — дышащий пунктир выглядит живее
   равномерного вращения и не даёт ощущения зависшего кадра. */
.pf-spinner {
  width: 26px;
  height: 26px;
  color: var(--fg);
  opacity: 0.55;
  animation: pfSpin 1.4s linear infinite;
}

.pf-spinner-arc {
  /* 2πr ≈ 113 — длина окружности; ею же меряем пунктир */
  stroke-dasharray: 28 85;
  animation: pfArc 1.6s ease-in-out infinite;
}

@keyframes pfSpin {
  to { transform: rotate(360deg); }
}

@keyframes pfArc {
  0%   { stroke-dasharray: 10 103; stroke-dashoffset: 0; }
  50%  { stroke-dasharray: 60 53;  stroke-dashoffset: -20; }
  100% { stroke-dasharray: 10 103; stroke-dashoffset: -113; }
}

@media (prefers-reduced-motion: reduce) {
  .pf-spinner { animation-duration: 3s; }
  .pf-spinner-arc { animation: none; stroke-dasharray: 40 73; }
}

/* Индикатор занимает высокий бокс и стоит в его середине: иначе фраза
   липнет к описанию проекта и выглядит как часть текста. */
.pf-loading {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  min-height: 55vh;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.1em;
  color: var(--fg2);
  padding: 8px 0;
}

/* Пульсируют только точки: мигающая подпись читалась бы хуже. */
/* Кейс в iframe занимает панель целиком, поэтому индикатор для него —
   слой поверх панели, а не блок в потоке. */
.panel-detail.panel-iframe {
  position: relative;
}

.pf-loading-overlay {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--bg);
  transition: opacity 0.35s ease;
}

.pf-loading-overlay.is-done {
  opacity: 0;
  pointer-events: none;
}

.pf-loading-overlay .pf-loading {
  padding: 0;
  min-height: 0;
}

.post-empty {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--fg2);
}

.pd-body img:where(:not(.pf-tz *)) {
  width: 100%;
  height: auto;
  display: block;
  margin: 32px 0;
}

.pd-body lottie-player:where(:not(.pf-tz *)) {
  width: 100%;
  margin: 32px 0;
}

/* ─── Кейсы, перенесённые из Тильды (_tools/tilda-to-native.mjs) ───
   Раскладка снята в двух ширинах — 1200 и 390 (телефон) — и задана в cqw,
   поэтому масштабируется под ширину поля. Какая из двух действует, решает
   ширина самого кейса: от 800 px — десктопная, уже — мобильная (как было
   и у фрейма). Полотно идёт от края до края панели. */
.pf-tz {
  container: tz / inline-size;
  /* как у страниц Тильды: без этого светлый текст на тёмном жирнее */
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  /* элементы, выходящие за края (капля в шапке Ба — 2000 px), раньше
     обрезал фрейм; clip режет только по горизонтали и не делает блок
     прокручиваемым */
  overflow-x: clip;
  margin: 40px -56px -48px;
}
.pf-tz .tz-ab { position: relative; overflow: hidden; }
/* Общие правила портфолио для картинок (растянуть и обрезать) и текста
   кейса сюда не относятся: у перенесённых страниц своя типографика.
   :where — нулевая специфичность, любые правила самих кейсов сильнее. */
:where(.pf-tz) img { width: auto; height: auto; object-fit: fill; display: inline; }
/* Язык — общим правилом, как было в Тильде: скрипты кейсов дописывают
   двуязычный текст и после того, как портфолио расставило языки */
html[lang="ru"] .pf-tz .lang-en,
html[lang="en"] .pf-tz .lang-ru { display: none !important; }
/* где стоит элемент в каждой раскладке, задают правила во фрагменте */
.pf-tz .tz-el { position: absolute; display: none; }
.pf-tz .tz-w { width: 100%; height: 100%; }
/* только картинка самого элемента-изображения: правило не должно цеплять
   картинки внутри html-элементов (орбита в Ба и т.п.) */
.pf-tz .tz-image > .tz-w > img {
  display: block;
  width: 100%;
  height: auto;
  margin: 0;
}
.pf-tz .tz-shape { width: 100%; height: 100%; }
.pf-tz video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* слово длиннее рамки текста выходит за неё, а не рвётся — как в Тильде */
.pf-tz .tz-txt { overflow-wrap: normal; }
.pf-tz .tz-txt a { color: inherit; }
@media (max-width: 768px) {
  .pf-tz { margin: 24px -20px -60px; }
}

/* ─── Responsive ─── */
@media (max-width: 768px) {
  /* Один скролл на мобильном: тело не скроллится, кейс занимает место под
     навигацией. Тело скроллится (панель Safari сворачивается), навигация
     авто-прячется при скролле — тела, панели или iframe. */
  body.page-split {
    height: auto;
    overflow: auto;
  }

  /* футер в одну строку: кегль ужимается по ширине экрана, чтобы email и
     копирайт помещались в ряд даже на 320px; низ над home-индикатором iOS */
  footer {
    gap: 12px;
    padding: 14px 20px calc(14px + env(safe-area-inset-bottom, 0px));
  }

  .footer-email,
  .footer-copy {
    font-size: clamp(7.5px, 2.9vw, 10px);
    letter-spacing: 0.03em;
    white-space: nowrap;
  }

  .pd-empty {
    min-height: 40vh;
    justify-content: flex-start;
  }

  /* подсказка переезжает наверх, вплотную под ленту логотипов: на мобильном
     кейсы лежат прямо над ней, так что стрелка уже ничего не объясняет */
  .pd-point {
    order: -1;
    margin-top: 14px;
  }

  .pd-arrow {
    display: none;
  }

  /* стрелки вверх по бокам подсказки — тонкие, как десктопная стрелка,
     и так же слегка подталкивают в свою сторону */
  .pd-up {
    display: block;
    width: 10px;
    height: 20px;
    color: var(--fg2);
    transform-origin: center bottom;
    animation: upNudge 2.8s ease-in-out infinite;
  }

  /* вместо стрелки подсказка слегка пульсирует — тем же приёмом, что и
     плитки кейсов, поэтому читается как приглашение нажать. Пульс на всей
     строке, так что надпись и стрелки мигают синхронно; стрелки толкаются
     вверх с тем же периодом, на пике яркости */
  .pd-hint-row {
    animation: hintPulse 2.8s ease-in-out infinite;
  }

  /* через 3 с после появления надпись и стрелки один раз загораются
     белым на пике пульса — это делает main.js (flashHint) */

  /* имя центрируется в том, что осталось под подсказкой, — то есть садится
     чуть ниже середины панели */
  .pd-intro {
    margin: auto 0;
  }

  /* Панель кейса начинается под лентой логотипов и тянется на целый экран,
     поэтому её середина уходит за нижний край. Фон слоя по-прежнему кроет
     панель, а саму фразу со спиннером крепим к середине экрана. */
  .pf-loading-overlay .pf-loading {
    position: fixed;
    top: 50svh;
    left: 50%;
    transform: translate(-50%, -50%);
  }

  body.page-split .hero {
    padding: 16px 20px;
    overflow-x: auto;
    scrollbar-width: none;
    flex-wrap: nowrap;
    /* авто-прячущаяся навигация: теги липнут под шапкой */
    position: sticky;
    top: var(--hh, 46px);
    z-index: 8;
    background-color: var(--bg);
    transition: transform 0.3s ease, margin-bottom 0.3s ease;
  }

  body.page-split .hero::-webkit-scrollbar {
    display: none;
  }

  body.page-split .hero .sub {
    white-space: nowrap;
  }

  body.page-split .hero .tags-row {
    flex-wrap: nowrap;
    white-space: nowrap;
  }

  body.page-split .layout {
    display: flex;
    flex-direction: column;
    overflow: visible;
  }

  .panel-list {
    border-right: none;
    border-bottom: 0.5px solid var(--border);
    overflow: visible;
    scrollbar-width: none;
    /* полоса проектов липнет под строкой тегов и прячется вместе с ней */
    position: sticky;
    top: calc(var(--hh, 46px) + var(--hero-h, 44px));
    z-index: 7;
    background-color: var(--bg);
    transition: transform 0.3s ease, margin-bottom 0.3s ease;
  }

  /* при скролле кейса вниз — теги и полоса уезжают под шапку.
     margin-bottom схлопывает освобождённое место в потоке, иначе под
     ними виден тёмный фон body (заметно на светлых кейсах). */
  body.page-split.nav-hidden .hero {
    transform: translateY(calc(-100% - var(--hh, 46px)));
    margin-bottom: calc(-1 * var(--hero-h, 44px));
  }
  body.page-split.nav-hidden .panel-list {
    transform: translateY(calc(-100% - var(--hh, 46px) - var(--hero-h, 44px)));
    margin-bottom: calc(-1 * var(--strip-h, 110px));
  }

  .cards-grid {
    display: flex;
    flex-direction: row;
    grid-template-columns: unset;
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: none;
  }

  .cards-grid::-webkit-scrollbar {
    display: none;
  }

  /* Строка тегов и лента кейсов — полупрозрачные с размытием, как шапка:
     кейс, уезжающий под них при скролле, просвечивает, а не обрывается
     сплошной плашкой. Плитки и подложки прозрачные, иначе фон ленты
     перекрыли бы они сами. */
  body.page-split .hero,
  .panel-list {
    background-color: rgba(255, 255, 255, 0.7);
    backdrop-filter: blur(4px);
    -webkit-backdrop-filter: blur(4px);
  }
  body.dark.page-split .hero,
  body.dark .panel-list {
    background-color: rgba(10, 10, 10, 0.7);
  }
  .panel-list .cards-grid,
  .panel-list .card:not(.active):not(.hovered),
  .panel-list .card-inner {
    background-color: transparent;
  }

  .panel-list .card {
    width: 28vw;
    flex-shrink: 0;
    aspect-ratio: 1 / 1;
  }

  /* неактивные иконки проектов — ярче (≈50% вместо ~18%) */
  .panel-list .card-inner::after {
    opacity: 0.5;
  }

  body.page-texts .panel-list {
    max-height: 42vh;
    overflow: hidden;
  }

  .posts-list {
    max-height: none;
  }

  .panel-detail {
    overflow: visible;
    padding: 16px 20px 60px;
  }

  .panel-detail.panel-iframe {
    overflow: visible;
    padding: 0;
  }

  .panel-detail.panel-iframe > iframe.project-iframe {
    height: 100svh;
    min-height: 600px;
  }

  /* на мобильном страница скроллится телом — в полноэкранном режиме
     запираем его и отдаём кейсу ровно экран */
  /* на телефоне разворота нет: кейс и так во всю ширину, а прятать шапку
     со списком незачем — выходить из режима было бы нечем */
  .case-expand { display: none; }

  body.page-split.case-full {
    height: 100svh;
    overflow: hidden;
  }

  body.case-full .layout {
    display: block;
    height: 100svh;
    overflow: hidden;
  }

  body.case-full .panel-detail {
    height: 100svh;
    overflow-y: auto;
  }

  body.case-full .panel-detail.panel-iframe > iframe.project-iframe {
    height: 100svh;
    min-height: 0;
  }


  .pd-header {
    align-items: flex-start;
    flex-wrap: nowrap;
  }


  .pd-title {
    line-height: 1;
  }

  .panel-list .card-label {
    display: none;
  }

  .card-icon {
    width: 72px;
    height: 72px;
  }

  .card-thumb {
    transform: translateZ(0) scale(1.3);
  }

  .card.hovered .card-thumb {
    transform: translateZ(0) scale(1.105);
  }

  .card.active .card-thumb {
    transform: translateZ(0) scale(1.3);
  }

  .card.active.hovered .card-thumb {
    transform: translateZ(0) scale(1.105);
  }
}

@media (max-width: 600px) {
  header {
    padding: 12px 20px;
  }

  main {
    padding: 40px 20px 60px;
  }

  nav {
    gap: 16px;
  }

  .pd-header {
    margin-bottom: 10px;
    padding-bottom: 8px;
  }
}
/* Текст только для поисковиков и экранных дикторов: на страницах кейсов
   до отрисовки кейса там лежат название и описание (см. _tools/build-case-pages.mjs). */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}
