/* Страница «Главная» — стили, специфичные для макета.
   Токены и компоненты дизайн-системы лежат в tokens/ и css/. */

/* overflow-x:clip, а не hidden: hidden сделал бы контейнер прокручиваемым
   и сломал position:sticky у шапки. Обрезает всё, что выходит за края —
   слайды за границей слайдера, кольцо в CTA, круг в хиро. */
#top{background:var(--ks-surface-page);overflow-x:clip}
/* Классы дизайн-системы задают display и перебивают браузерное правило
   для [hidden] — возвращаем атрибуту силу. */
[hidden]{display:none !important}
.ks-skip-link{position:fixed;top:12px;left:12px;z-index:200;
  padding:12px 20px;background:var(--ks-surface-page);color:var(--ks-text-primary);
  transform:translateY(calc(-100% - 24px))}
.ks-skip-link:focus{transform:none}
#ks-main:focus{outline:none;box-shadow:none}
:root{
  /* Статичная высота шапки: геометрия первого экрана не зависит от JS. */
  --ks-nav-h:72px;
  --ks-nav-measured:72px;
  /* Поле страницы: на нём стоит кадр хиро, по нему же выравниваются
     заливки крайних ячеек в полосе фактов. */
  --ks-page-edge:24px;
  /* Насколько заливка крайней ячейки вылезает за контейнер, чтобы дойти
     до линии арки. Считается в app.js: left контейнера минус поле. */
  --ks-facts-edge:0px;
  /* Потолок кадра: выше него он не растягивается ни в стартовой
     полноэкранной фазе, ни в конечной арке. */
  --ks-hero-max:900px;
  /* Кадр занимает весь экран под шапкой минус поля хиро (10px сверху,
     24px снизу), но не выше потолка. */
  --ks-hero-h:clamp(600px, calc(100svh - var(--ks-nav-h) - 34px), var(--ks-hero-max));
}
/* Масштабируется только изображение с оверлеями внутри зарезервированного
   кадра. Старые браузеры без деления длин сохраняют морф формы без масштаба. */
@supports (transform:scale(calc(100vw / 1px))){
  :root{--ks-hero-intro-transform:translateY(var(--ks-page-edge)) scale(
    calc(100vw / (100vw - 2 * var(--ks-page-edge))),
    calc((var(--ks-hero-h) + var(--ks-nav-h) + 10px + var(--ks-page-edge)) / var(--ks-hero-h)))}
}

/* ── Заглушка изображения (аналог <image-slot> из макета) ───────────── */
.ks-slot{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;padding:24px;text-align:center;font-family:var(--ks-font-mono);font-size:11px;line-height:1.5;letter-spacing:.08em;text-transform:uppercase;color:currentColor}
.ks-slot img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
/* Кадр хиро смещён вверх: иначе светлая полка с лампой приходится ровно
   на строку «с аудиторией» и съедает её читаемость. */
#ks-hero img{object-position:center 32%}

/* ── Шапка ──────────────────────────────────────────────────────────── */
/* Шапка — полупрозрачный слой: страница уезжает под неё, а не упирается
   в глухую полосу. Граница остаётся прозрачной, но остаётся: она держит
   высоту 72px, от которой считается стартовый кадр хиро. */
.ks-nav{height:var(--ks-nav-h);position:sticky;top:0;z-index:80;display:flex;align-items:center;justify-content:space-between;gap:32px;padding:18px 48px;width:100%;background:color-mix(in srgb,var(--ks-surface-page) 72%,transparent);backdrop-filter:blur(20px) saturate(180%);-webkit-backdrop-filter:blur(20px) saturate(180%);color:var(--ks-text-primary);border-bottom:1px solid transparent;transition:background var(--ks-dur) var(--ks-ease),color var(--ks-dur) var(--ks-ease)}
/* Мягкая кромка вместо линии — только там, где контент подходит вплотную */
.ks-nav::after{content:"";position:absolute;left:0;right:0;top:100%;height:12px;pointer-events:none;
  background:linear-gradient(rgba(18,18,18,.05),transparent)}
@supports not (backdrop-filter:blur(1px)){
  .ks-nav{background:var(--ks-surface-page)}
}
.ks-nav__links{display:flex;gap:28px;align-items:center}
/* Контакты рядом с кнопкой: телефон крупнее — по нему звонят чаще. */
.ks-nav__side{display:flex;align-items:center;gap:24px}
/* Высота блока задана жёстко: шапка участвует в расчёте стартового кадра
   хиро, и лишний пиксель сдвигал бы всю раскладку. */
.ks-nav__contacts{display:flex;flex-direction:column;justify-content:center;align-items:flex-end;
  gap:0;height:48px;line-height:1.15}
.ks-nav__contacts > a{display:flex;align-items:center;min-height:24px}
.ks-nav__phone{font-family:var(--ks-font-body);font-weight:500;font-size:14px;white-space:nowrap;
  transition:color var(--ks-dur-fast) var(--ks-ease)}
.ks-nav__mail{color:var(--ks-text-muted);white-space:nowrap;
  transition:color var(--ks-dur-fast) var(--ks-ease)}
.ks-nav__phone:hover,
.ks-nav__mail:hover{color:var(--ks-orange-cta)}
.ks-logo{color:inherit}

/* ── Хиро ───────────────────────────────────────────────────────────── */
.ks-hero{padding:10px var(--ks-page-edge) var(--ks-page-edge)}
.ks-hero__frame{position:relative;height:var(--ks-hero-h);color:#a8a49c;
  border-radius:0;transform-origin:center bottom;
  clip-path:inset(0 round clamp(150px,19vw,280px) clamp(150px,19vw,280px) 0 0)}
/* Ровное затемнение кадра. Полноэкранным он показывается «как снят»,
   а к моменту, когда принимает форму арки, уходит в тень — заголовок
   ложится на спокойный фон. */
.ks-hero__dim{position:absolute;inset:0;background:var(--ks-black);opacity:.38;pointer-events:none}
.ks-hero__scrim{position:absolute;inset:0;background:linear-gradient(180deg,rgba(10,10,10,0) 42%,rgba(10,10,10,.58) 96%);pointer-events:none}
.ks-hero__pill{position:absolute;right:0;top:31%;width:78px;height:156px;background:var(--ks-orange);border-radius:156px 0 0 156px;pointer-events:none;transform-origin:right center}
.ks-hero__body{position:absolute;left:56px;right:56px;bottom:56px;color:var(--ks-white);pointer-events:none}
.ks-hero__row{display:flex;align-items:flex-end;justify-content:space-between;gap:48px;flex-wrap:wrap}
.ks-hero__title{color:#fff;font-size:clamp(56px,7.6vw,112px)}
.ks-hero__actions{display:flex;align-items:center;gap:32px;pointer-events:auto;padding-bottom:14px}
.ks-hero__actions .ks-arrow-link{color:#fff}
.ks-hero__actions .ks-arrow-link:hover{color:var(--ks-orange)}
.ks-hero__kicker{color:rgba(255,255,255,.78);margin-bottom:22px}

/* Заголовок выезжает по словам. Маска каждого слова шире line-box: при
   line-height .96 верхушки глифов Poiret One выходят за строку — иначе буква
   и торчит до выезда, и срезается после. Отрицательные margin возвращают
   исходную метрику. Порядок слова задаётся инлайновой --i, старт всей
   группы — переменной --ks-t0 на заголовке. */
/* Геометрия пословного выезда, общая для хиро и заголовков секций.
   line-height дисплейных классов (.96–.98) слишком тесен для кириллицы:
   бревис над «Й» залезает на строку выше. */
.ks-words{line-height:1.06}
.ks-words__line{display:block}
.ks-words .ks-reveal{display:inline-block;vertical-align:top;padding:.3em 0 .16em;margin:-.3em 0 -.16em}

/* В хиро слова стартуют по общему таймеру интро (--ks-t0) */
.ks-hero__title .ks-reveal>*{
  animation-name:ks-hero-reveal;
  animation-duration:.85s;
  animation-delay:calc(var(--ks-t0,0s) + var(--i,0) * .09s);
}

/* В секциях — по появлению блока в кадре */
.ks-js [data-reveal]:not(.is-in) .ks-words .ks-reveal>*{transform:translateY(calc(100% + .42em))}
[data-reveal].is-in .ks-words .ks-reveal>*{
  animation:ks-hero-reveal .8s var(--ks-ease-out) both;
  animation-delay:calc(var(--ks-t0,0s) + var(--i,0) * .09s);
}

@keyframes ks-hero-reveal{
  from{transform:translateY(calc(100% + .42em))}
  to{transform:none}
}

/* ── Интро: визуальный морф без изменения раскладки ────────────────
   Хиро и шапка занимают конечное место с первой отрисовки. Меняются только
   transform, clip-path и opacity; высота, margin и padding неподвижны.
   Гуттер сохраняет ширину страницы при блокировке прокрутки. */
html{scrollbar-gutter:stable}
html.ks-intro-lock{overflow:hidden}
/* До запуска цепочки хиро ждёт в стартовом состоянии: кадр во весь экран,
   шапка и текст скрыты. Иначе при отложенном старте они мелькнули бы. */
.ks-intro:not(.ks-intro-run) .ks-hero__frame{transform:var(--ks-hero-intro-transform,none);clip-path:inset(0 round 0)}
.ks-intro:not(.ks-intro-run) .ks-hero__dim{opacity:0}
.ks-intro:not(.ks-intro-run) .ks-nav{opacity:0}
.ks-intro:not(.ks-intro-run) .ks-hero__kicker,
.ks-intro:not(.ks-intro-run) .ks-hero__actions,
.ks-intro:not(.ks-intro-run) .ks-hero__pill{opacity:0}
.ks-intro:not(.ks-intro-run) .ks-hero__title .ks-reveal>*{animation:none;transform:translateY(calc(100% + .42em))}
.ks-intro-run .ks-hero__frame{animation:ks-hero-shape .9s var(--ks-ease-out) .35s both}
.ks-intro-run .ks-hero__dim{animation:ks-hero-dim .9s var(--ks-ease-out) .35s both}
/* Шапка и заголовок выходят одновременно — и оба после морфа: заголовок
   прижат к низу кадра, поэтому во время изменения высоты он бы полз вверх
   прямо на выезде. */
.ks-intro-run .ks-nav{animation:ks-nav-in .55s var(--ks-ease-out) 1.25s both}
.ks-intro-run .ks-hero__title{--ks-t0:1.25s}
/* Кривые здесь намеренно не ks-ease-out: она отдаёт 92% пути за первую
   треть времени, и рост/подъём читаются как мгновенное включение. */
.ks-intro-run .ks-hero__kicker{animation:ks-rise-in .75s var(--ks-ease) 1.95s both}
.ks-intro-run .ks-hero__actions{animation:ks-rise-in .8s var(--ks-ease) 2.05s both}
.ks-intro-run .ks-hero__pill{animation:ks-hero-pill .8s cubic-bezier(.34,1.28,.64,1) 2.15s both}

@keyframes ks-hero-shape{
  from{transform:var(--ks-hero-intro-transform,none);clip-path:inset(0 round 0)}
  to{transform:none;clip-path:inset(0 round clamp(150px,19vw,280px) clamp(150px,19vw,280px) 0 0)}
}
@keyframes ks-nav-in{
  from{opacity:0;transform:translateY(-100%)}
  to{opacity:1;transform:none}
}
/* Затемнение набирается ровно за время морфа в арку */
@keyframes ks-hero-dim{
  from{opacity:0}
  to{opacity:.38}
}
/* Второстепенное не «включается», а всплывает снизу */
@keyframes ks-rise-in{
  from{opacity:0;transform:translateY(22px)}
  to{opacity:1;transform:none}
}
/* Круг вырастает от правого края кадра, откуда он и «выходит».
   Непрозрачность набирается в первой трети — дальше видно только рост. */
@keyframes ks-hero-pill{
  from{opacity:0;transform:scale(.08)}
  30%{opacity:1}
  to{opacity:1;transform:none}
}

/* ── Факты ──────────────────────────────────────────────────────────── */
/* Отступа сверху нет: расстояние до кадра задаёт padding-bottom у .ks-hero,
   и поле вокруг кадра остаётся равномерным со всех сторон.
   Рамки нарисованы псевдоэлементами, а не border — их можно прочертить. */
.ks-facts{position:relative}
.ks-facts::before,.ks-facts::after{content:"";position:absolute;left:0;right:0;height:1px;background:var(--ks-border-strong);transform-origin:left center}
.ks-facts::before{top:0}
.ks-facts::after{bottom:0}
.ks-facts__row{display:grid;grid-template-columns:repeat(5,1fr)}
.ks-facts__cell{position:relative;padding:40px 28px 36px 32px}
.ks-facts__cell:not(:first-child)::before{content:"";position:absolute;left:0;top:0;bottom:0;width:1px;background:var(--ks-hairline);transform-origin:top center}
.ks-facts__cell:first-child{padding-left:0}
.ks-facts__cell:last-child{padding-right:0}

/* Появление полосы: горизонтали слева направо → вертикали сверху вниз
   поочерёдно → содержимое ячеек. Стартовое состояние задаётся только при
   живом JS, иначе без него блок остался бы невидимым. */
.ks-js .ks-facts:not(.is-in)::before,
.ks-js .ks-facts:not(.is-in)::after{transform:scaleX(0)}
.ks-js .ks-facts:not(.is-in) .ks-facts__cell::before{transform:scaleY(0)}
.ks-js .ks-facts:not(.is-in) .ks-facts__cell > *{opacity:0}

.ks-facts.is-in::before{animation:ks-line-x .85s var(--ks-ease-out) both}
.ks-facts.is-in::after{animation:ks-line-x .85s var(--ks-ease-out) .1s both}
.ks-facts.is-in .ks-facts__cell::before{animation:ks-line-y .45s var(--ks-ease-out) both;animation-delay:calc(.6s + (var(--i,1) - 1) * .07s)}
.ks-facts.is-in .ks-facts__cell > *{animation:ks-rise-in .55s var(--ks-ease-out) both;animation-delay:calc(1.05s + var(--i,0) * .07s)}

@keyframes ks-line-x{from{transform:scaleX(0)}to{transform:scaleX(1)}}
@keyframes ks-line-y{from{transform:scaleY(0)}to{transform:scaleY(1)}}
.ks-stat{position:relative;display:flex;flex-direction:column;gap:10px}
.ks-stat .ks-num{font-size:72px}
.ks-stat .ks-label{color:var(--ks-text-secondary)}
.ks-facts__cell--dot{display:flex;flex-direction:column;gap:10px}
.ks-facts__dot{width:58px;height:58px;border-radius:50%;background:var(--ks-orange);margin:4px 0 12px}
/* Только прямой ребёнок: копия внутри .ks-facts__inv должна остаться белой */
.ks-facts__cell--dot > .ks-label{color:var(--ks-text-secondary)}

/* Ховер: заливка растёт снизу вверх, текст инвертируется. Блок стоит после
   всех правил цвета в полосе — при равной специфичности побеждает нижнее.
   Цвет заливки вынесен в переменную. */
.ks-facts__cell{--ks-facts-fill:var(--ks-orange-cta)}
.ks-facts__cell::after{content:"";position:absolute;inset:0;background:var(--ks-facts-fill);transform:scaleY(0);transform-origin:bottom center;transition:transform .45s var(--ks-ease);z-index:0}
/* Крайние ячейки заливаются до линии арки, а не до края контейнера */
.ks-facts__cell:first-child::after{left:calc(-1 * var(--ks-facts-edge))}
.ks-facts__cell:last-child::after{right:calc(-1 * var(--ks-facts-edge))}
.ks-facts__cell > *{position:relative;z-index:1}
/* Ховер-движение только для мыши и только на десктопной ширине:
   на узких экранах подложки скрыты, и ховер красил текст в никуда. */
@media (hover:hover) and (pointer:fine) and (min-width:861px){
  .ks-facts__cell:hover::after{transform:scaleY(1)}
  .ks-facts__cell:hover .ks-facts__inv{clip-path:inset(0 0 0 0)}
}

/* Инверсия по кромке заливки: поверх лежит копия содержимого в белом,
   обрезанная той же линией, что и заливка. Цвет не «переключается» —
   буквы белеют ровно там, где их накрыл оранжевый.
   Копия создаётся в app.js, разметка остаётся чистой. */
.ks-facts__inv{
  position:absolute;inset:0;z-index:2;pointer-events:none;
  padding:inherit;display:inherit;flex-direction:inherit;gap:inherit;
  clip-path:inset(100% 0 0 0);
  transition:clip-path .45s var(--ks-ease);
}
/* Слой служебный, поэтому цвет задан жёстко: иначе любое правило вида
   «.ks-stat .ks-label» или инлайновый стиль оказывается специфичнее
   и часть копии остаётся тёмной. */
.ks-facts__inv,.ks-facts__inv *{color:#fff !important}
.ks-facts__inv .ks-facts__dot{background:#fff}

/* ── Студии ─────────────────────────────────────────────────────────── */
.ks-studio__meta{display:flex;flex-direction:column}
.ks-studio__idx{color:var(--ks-orange)}
.ks-studio__name{font-family:var(--ks-font-display);font-weight:400;font-size:46px;line-height:1;text-transform:uppercase;margin-top:16px}
.ks-studio__text{color:var(--ks-text-secondary);margin-top:18px}
.ks-studio__specs{color:var(--ks-text-muted);margin-top:16px}
/* Характеристики зала: список с иконками Lucide из локального спрайта */
.ks-specs{list-style:none;margin:18px 0 0;padding:0;display:flex;flex-direction:column;gap:11px;color:var(--ks-text-muted)}
.ks-specs li{display:flex;align-items:center;gap:10px}
.ks-specs__i{width:16px;height:16px;flex:none;color:var(--ks-orange)}

/* Надзаголовок секции выходит перед словами заголовка */
.ks-js .ks-sec-head:not(.is-in) .ks-section-label{opacity:0}
.ks-sec-head.is-in .ks-section-label{animation:ks-rise-in .55s var(--ks-ease-out) both}

/* ── Появление карточки зала ────────────────────────────────────────
   Кадр выезжает снизу и одновременно «отрастает» скруглённый угол —
   у первого зала верхний правый, у второго нижний левый. Текст идёт
   следом: заголовок, описание, характеристики по одной, цена. */
.ks-js #studios .ks-grid:not(.is-in) .ks-media{opacity:0;border-radius:0}
.ks-js #studios .ks-grid:not(.is-in) .ks-studio__meta > *{opacity:0}
.ks-js #studios .ks-grid:not(.is-in) .ks-specs li{opacity:0}

#studios .ks-grid.is-in .ks-round-tr{animation:ks-media-tr .95s var(--ks-ease-out) both}
#studios .ks-grid.is-in .ks-round-bl{animation:ks-media-bl .95s var(--ks-ease-out) both}
#studios .ks-grid.is-in .ks-studio__meta > *{animation:ks-rise-in .6s var(--ks-ease-out) both}
#studios .ks-grid.is-in .ks-studio__meta > *:nth-child(1){animation-delay:.2s}
#studios .ks-grid.is-in .ks-studio__meta > *:nth-child(2){animation-delay:.3s}
#studios .ks-grid.is-in .ks-studio__meta > *:nth-child(3){animation-delay:.4s}
#studios .ks-grid.is-in .ks-studio__meta > *:nth-child(4){animation-delay:.75s}
#studios .ks-grid.is-in .ks-specs li{animation:ks-rise-in .5s var(--ks-ease-out) both}
#studios .ks-grid.is-in .ks-specs li:nth-child(1){animation-delay:.45s}
#studios .ks-grid.is-in .ks-specs li:nth-child(2){animation-delay:.53s}
#studios .ks-grid.is-in .ks-specs li:nth-child(3){animation-delay:.61s}
#studios .ks-grid.is-in .ks-specs li:nth-child(4){animation-delay:.69s}

@keyframes ks-media-tr{
  from{opacity:0;transform:translateY(28px);border-radius:0}
  to{opacity:1;transform:none;border-radius:0 var(--ks-mask,160px) 0 0}
}
@keyframes ks-media-bl{
  from{opacity:0;transform:translateY(28px);border-radius:0}
  to{opacity:1;transform:none;border-radius:0 0 0 var(--ks-mask,160px)}
}
.ks-studio__foot{margin-top:auto;border-top:1px solid var(--ks-hairline);padding-top:20px;display:flex;flex-direction:column;gap:14px;align-items:flex-start}
.ks-studio__price{font-family:var(--ks-font-display);font-weight:600;font-size:30px;line-height:1;text-transform:uppercase}
.ks-studio__price--text{font-size:24px}
.ks-studio__bar{position:absolute;left:0;bottom:0;background:var(--ks-surface-page);padding:24px 36px 0 0;display:flex;align-items:baseline;gap:28px;flex-wrap:wrap;max-width:86%}

/* ── Форматы ────────────────────────────────────────────────────────── */
/* Без внешних линий — только разделители между строками */
.ks-fmt__list{align-self:start}
.ks-fmt__row:last-child{border-bottom:0}
.ks-fmt__row{position:relative;display:flex;align-items:baseline;gap:24px;padding:22px 16px;width:100%;border:0;border-bottom:1px solid var(--ks-hairline);background:none;color:inherit;text-align:left;cursor:pointer}
.ks-fmt__row .num{color:var(--ks-gray-300)}
.ks-fmt__row .name{font-family:var(--ks-font-display);font-size:clamp(30px,3.2vw,46px);line-height:1;text-transform:uppercase}
.ks-fmt__row .meta{margin-left:auto;color:var(--ks-text-muted);text-align:right}

/* Активный формат отмечается заливкой — как в полосе фактов, но
   горизонтальной. Поверх лежит белая копия строки, обрезанная той же
   вертикальной кромкой (копия строится в app.js). */
.ks-fmt__row::after{content:"";position:absolute;inset:0;background:var(--ks-orange-cta);transform:scaleX(0);transform-origin:left center;transition:transform .45s var(--ks-ease);z-index:0}
.ks-fmt__row > *{position:relative;z-index:1}
.ks-fmt__row.is-active::after{transform:scaleX(1)}
.ks-fmt__inv{
  position:absolute;inset:0;z-index:2;pointer-events:none;
  display:flex;align-items:baseline;gap:24px;padding:inherit;
  clip-path:inset(0 100% 0 0);
  transition:clip-path .45s var(--ks-ease);
}
.ks-fmt__inv,.ks-fmt__inv *{color:#fff !important}
.ks-fmt__row.is-active .ks-fmt__inv{clip-path:inset(0 0 0 0)}
.ks-fmt__aside{position:sticky;top:104px}
.ks-fmt__thumb{display:none}
/* Разметка слайдера печатается всегда, но список на десктопе остаётся
   списком: сбрасываем флекс обёртки и ширину слайдов из стилей Swiper. */
.ks-fmt__list > .swiper-wrapper{display:block}
.ks-fmt__list .ks-fmt__row.swiper-slide{width:100%;flex-shrink:1}

/* Появление секции: строки списка каскадом, превью следом */
.ks-js #formats .ks-grid:not(.is-in) .ks-fmt__row{opacity:0}
.ks-js #formats .ks-grid:not(.is-in) .ks-fmt__aside{opacity:0}
#formats .ks-grid.is-in .ks-fmt__row{animation:ks-rise-in .5s var(--ks-ease-out) both;animation-delay:calc(var(--i,0) * .07s)}
#formats .ks-grid.is-in .ks-fmt__aside{animation:ks-rise-in .7s var(--ks-ease-out) .2s both}
.ks-fmt__prev{position:absolute;inset:0;opacity:0;pointer-events:none;transition:opacity .5s var(--ks-ease)}
.ks-fmt__prev.is-active{opacity:1;pointer-events:auto}
.ks-fmt__caption{display:flex;align-items:baseline;gap:16px;margin-top:16px}

/* ── Манифест ───────────────────────────────────────────────────────── */
.ks-manifest{padding:150px 48px;position:relative;overflow:hidden;text-align:center}
/* Фон: кадр студии, приглушённый почти до фактуры — читается как тень,
   а не как фотография, и не спорит с текстом */
.ks-manifest__bg{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;opacity:.09;pointer-events:none}
.ks-manifest > *:not(.ks-manifest__bg){position:relative;z-index:1}

/* Знак рисуется с нуля: сначала контур прочерчивается по кромке, затем
   наливается цветом, а обводка гаснет. pathLength="1" в разметке позволяет
   задать дистанцию рисования в долях, не вычисляя длину пути в JS. */
.ks-manifest__mark{width:clamp(180px,20vw,268px);height:auto;margin:0 auto 64px;color:var(--ks-white);display:block}
.ks-manifest__mark path{
  stroke:currentColor;stroke-width:1.5;vector-effect:non-scaling-stroke;
  stroke-dasharray:1;
}
.ks-manifest__mark .ks-mark__ring{stroke:var(--ks-orange)}
.ks-js .ks-manifest:not(.is-in) .ks-manifest__mark path{fill-opacity:0;stroke-dashoffset:1}

.ks-manifest.is-in .ks-mark__k{animation:ks-draw 1.1s var(--ks-ease) both, ks-fill 1.1s linear .85s both}
.ks-manifest.is-in .ks-mark__ring{animation:ks-draw 1.1s var(--ks-ease) .35s both, ks-fill 1.1s linear 1.2s both}
.ks-manifest.is-in .ks-mark__q{animation:ks-draw .7s var(--ks-ease) .7s both, ks-fill .9s linear 1.45s both}

/* Текст идёт после того, как знак дорисован */
.ks-manifest .ks-words{--ks-t0:1.35s}
/* Внешнее свечение: два слоя — плотный ореол и широкий рассеянный.
   Цвет ореола совпадает с цветом строки, поэтому белая светится белым,
   а оранжевая — оранжевым. */
.ks-manifest .ks-words{font-weight:600}
.ks-manifest__note{color:var(--ks-text-secondary);margin-top:28px}
.ks-js .ks-manifest:not(.is-in) .ks-manifest__note{opacity:0}
.ks-manifest.is-in .ks-manifest__note{animation:ks-rise-in .6s var(--ks-ease-out) 2.2s both}

@keyframes ks-draw{
  from{stroke-dashoffset:1}
  to{stroke-dashoffset:0}
}
@keyframes ks-fill{
  from{fill-opacity:0;stroke-opacity:1}
  60%{stroke-opacity:1}
  to{fill-opacity:1;stroke-opacity:0}
}

/* ── Включено ───────────────────────────────────────────────────────── */
#services{position:relative;overflow:hidden}
.ks-services__bg{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;pointer-events:none}
#services > .ks-container{position:relative;z-index:1}
.ks-included__label{max-width:14ch}
/* Ни margin, ни padding у сетки: высоту блока задаёт контент, а поля
   сверху и снизу — только padding секции, поэтому они равны. */
.ks-included__grid{display:grid;grid-template-columns:minmax(200px,20%) 1fr;gap:64px}
.ks-included__aside{display:flex;flex-direction:column;gap:32px}
.ks-included__note{color:var(--ks-text-secondary)}

/* Пункты — капсулы. При наведении внутрь капсулы проявляется кадр,
   текст переходит в белый, поверх кадра лежит затемнение для читаемости. */
/* Три строки: 2 / 3 / 2. Ширина капсулы подстраивается под длину текста —
   flex-basis auto распределяет остаток пропорционально содержимому. */
.ks-inc{display:flex;flex-direction:column;gap:14px}
.ks-inc__row{display:flex;gap:14px}
.ks-inc__item{
  flex:1 1 auto;position:relative;transform-origin:center;display:flex;align-items:center;justify-content:center;
  height:clamp(62px,6.4vw,86px);padding:0 clamp(18px,2vw,34px);
  border:1px solid var(--ks-hairline);border-radius:var(--ks-radius-full);
  overflow:hidden;cursor:default;
  font-family:var(--ks-font-display);font-size:clamp(24px,2.6vw,38px);
  line-height:1;text-transform:uppercase;white-space:nowrap;
  transition:color .25s var(--ks-ease),border-color .25s var(--ks-ease);
}
.ks-inc__item img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;opacity:0;transition:opacity .25s var(--ks-ease),transform .3s var(--ks-ease)}
.ks-inc__item::after{content:"";position:absolute;inset:0;background:rgba(10,10,10,.42);opacity:0;transition:opacity .25s var(--ks-ease)}
.ks-inc__item > span{position:relative;z-index:1}
/* Капсула разворачивается в кадр — только под мышью на десктопе. */
@media (hover:hover) and (pointer:fine) and (min-width:861px){
  .ks-inc__item:hover img{opacity:1;transform:scale(1.04)}
  .ks-inc__item:hover::after{opacity:1}
  .ks-inc__item:hover{color:#fff;border-color:transparent}
}

/* Появление в два такта: сначала капсулы вырастают из нуля масштабом —
   от точки в центре к полному размеру — и внутри у них сразу кадр, потом кадр растворяется и
   остаётся контур с текстом. Маска, а не масштаб: при scale текст внутри
   Порядок появления задаёт --d, её раздаёт app.js. */
.ks-js .ks-included__grid:not(.is-in) .ks-inc__item{opacity:0;transform:scale(.94)}
.ks-js .ks-included__grid:not(.is-in) .ks-included__aside > *{opacity:0}
/* До появления капсула выглядит как кадр: фото видно, текст белый,
   рамки нет — с этого состояния и начинается растворение. */
.ks-js .ks-included__grid:not(.is-in) .ks-inc__item{color:#fff;border-color:transparent}
.ks-js .ks-included__grid:not(.is-in) .ks-inc__item img{opacity:1}
.ks-js .ks-included__grid:not(.is-in) .ks-inc__item::after{opacity:1}
.ks-js .ks-included__grid:not(.is-in) .ks-inc__item > span{opacity:0}

.ks-included__grid.is-in .ks-inc__item{
  animation:ks-inc-open .8s var(--ks-ease-out) var(--d,0s) both,
            ks-inc-tone .8s var(--ks-ease-out) 1.35s backwards;
}
/* Текст проявляется, когда капсула уже развернулась: в узкой пилюле от
   него была бы видна только середина. */
.ks-included__grid.is-in .ks-inc__item > span{animation:ks-fade-in .45s var(--ks-ease-out) calc(var(--d,0s) + .34s) both}
.ks-included__grid.is-in .ks-inc__item img,
.ks-included__grid.is-in .ks-inc__item::after{animation:ks-inc-photo-out .8s var(--ks-ease-out) 1.35s backwards}
.ks-included__grid.is-in .ks-included__aside > *{animation:ks-rise-in .6s var(--ks-ease-out) both}
.ks-included__grid.is-in .ks-included__aside > *:nth-child(2){animation-delay:.12s}
/* Задержки раздаёт app.js: порядок появления случайный, поэтому в CSS
   их не зашиваем. */

@keyframes ks-inc-photo-out{
  from{opacity:1}
  to{opacity:0}
}
@keyframes ks-inc-tone{
  from{color:#fff;border-color:transparent}
  to{color:var(--ks-text-primary);border-color:var(--ks-hairline)}
}
@keyframes ks-inc-open{
  from{opacity:0;transform:scale(.94)}
  to{opacity:1;transform:scale(1)}
}

/* ── Showreel ───────────────────────────────────────────────────────
   Кадр раздвигается из центра по горизонтали — жест раскрывающегося
   экрана. Кнопка и подписи всплывают следом, когда кадр уже развёрнут. */
.ks-js #showreel [data-reveal]:not(.is-in) .ks-video{clip-path:inset(0 50% round 2px)}
.ks-js #showreel [data-reveal]:not(.is-in) .ks-video__play{opacity:0;transform:translate(-50%,-50%) scale(.4)}
.ks-js #showreel [data-reveal]:not(.is-in) .ks-video__title,
.ks-js #showreel [data-reveal]:not(.is-in) .ks-video__time{opacity:0}

#showreel [data-reveal].is-in .ks-video{animation:ks-reel-open .95s var(--ks-ease-out) .45s backwards}
#showreel [data-reveal].is-in .ks-video__play{animation:ks-reel-play .7s cubic-bezier(.34,1.28,.64,1) 1.15s backwards}
#showreel [data-reveal].is-in .ks-video__title{animation:ks-rise-in .6s var(--ks-ease-out) 1.3s backwards}
#showreel [data-reveal].is-in .ks-video__time{animation:ks-rise-in .6s var(--ks-ease-out) 1.4s backwards}

@keyframes ks-reel-open{
  from{clip-path:inset(0 50% round 2px)}
  to{clip-path:inset(0 round 2px)}
}
@keyframes ks-reel-play{
  from{opacity:0;transform:translate(-50%,-50%) scale(.4)}
  to{opacity:1;transform:translate(-50%,-50%) scale(1)}
}

/* ── Работы ─────────────────────────────────────────────────────────── */
/* ── Работы: слайдер на Swiper ──────────────────────────────────────
   Сам слайдер тянет свои стили из assets/vendor; здесь только раскладка
   шапки, кнопки и ширина слайда. */
.ks-works__head{display:flex;align-items:flex-end;justify-content:space-between;gap:32px}
.ks-works__nav{display:flex;gap:12px;flex:none}
/* background задаём явно: .ks-btn--icon его не определяет, и кнопка
   получала серую заливку из браузерных стилей. */
.ks-works__nav .ks-btn--icon{width:52px;height:52px;background:transparent;border:1px solid var(--ks-border-strong);color:inherit}
.ks-works__nav .ks-btn--icon.swiper-button-disabled{opacity:.35;border-color:var(--ks-hairline);cursor:default}
/* Ховер: заливается сама кнопка, стрелка остаётся на месте — сдвиг из
   дизайн-системы здесь только мешает читать направление. */
.ks-works__nav .ks-btn--icon:hover{background:var(--ks-orange-cta);border-color:var(--ks-orange-cta);color:#fff}
.ks-works__nav .ks-btn--icon:hover .arr{transform:none}
.ks-works__nav .ks-btn--icon.swiper-button-disabled:hover{background:transparent;border-color:var(--ks-hairline);color:inherit}
.ks-works__note{color:var(--ks-text-secondary);margin-top:24px;max-width:52ch}
.ks-js #works .ks-sec-head:not(.is-in) + .ks-works__note{opacity:0}
#works .ks-sec-head.is-in + .ks-works__note{animation:ks-rise-in .6s var(--ks-ease-out) .3s backwards}
.ks-works__swiper{margin-top:56px;overflow:visible}
/* Слайд шире трети контейнера: третья карточка намеренно не помещается,
   иначе листать нечего и слайдер выглядит сломанным. */
.ks-works__swiper .swiper-slide{width:clamp(340px,40%,540px);height:auto;margin-right:24px}

/* Появление: шапка и подпись всплывают, следом слайды каскадом.
   fill-mode backwards — transform слайда после анимации отпускается,
   иначе он мешал бы Swiper двигать дорожку. */
.ks-js #works .ks-sec-head:not(.is-in) .ks-works__nav{opacity:0}
.ks-works__nav--bottom{display:none}
.ks-js .ks-works__nav--bottom:not(.is-in){opacity:0}
.ks-works__nav--bottom.is-in{animation:ks-rise-in .6s var(--ks-ease-out) .2s backwards}
.ks-js #works [data-reveal]:not(.is-in) .swiper-slide{opacity:0}
#works .ks-sec-head.is-in .ks-works__nav{animation:ks-rise-in .6s var(--ks-ease-out) .45s backwards}
#works .ks-works__swiper.is-in .swiper-slide{animation:ks-rise-in .7s var(--ks-ease-out) backwards}
#works .ks-works__swiper.is-in .swiper-slide:nth-child(1){animation-delay:.1s}
#works .ks-works__swiper.is-in .swiper-slide:nth-child(2){animation-delay:.18s}
#works .ks-works__swiper.is-in .swiper-slide:nth-child(3){animation-delay:.26s}

#works .ks-media{border-radius:14px}
/* Подпись в духе выдачи YouTube: название плотным шрифтом в две строки,
   под ним канал мельче и приглушённее. */
.ks-work__meta{display:flex;align-items:flex-start;gap:12px;margin-top:14px}
/* Аватар канала слева от текста — как в выдаче YouTube */
.ks-work__ava{width:36px;height:36px;flex:none;border-radius:50%;object-fit:cover;margin-top:2px}
.ks-work__text{display:flex;flex-direction:column;align-items:flex-start;gap:4px;min-width:0}
.ks-work__title{font-family:var(--ks-font-body);font-weight:500;font-size:16px;line-height:1.35;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.ks-work__channel{font-family:var(--ks-font-body);font-size:14px;line-height:1.4;color:var(--ks-text-muted);transition:color var(--ks-dur-fast) var(--ks-ease)}
.ks-work:hover .ks-work__channel{color:var(--ks-text-secondary)}

/* ── Техника ────────────────────────────────────────────────────────── */
/* Без внешних линий — только разделители между строками */
/* Без внешних линий — только разделители между строками */
.ks-tech__lead{color:var(--ks-text-secondary);margin-top:24px}
.ks-tech__list{margin-top:64px}
/* При наведении строка становится кадром: фотография разворачивается на
   всю ширину, поверх ложится затемнение, текст белеет, круглое превью
   растворяется — оно уже внутри общего кадра. */
.ks-tech__row{position:relative;display:flex;align-items:center;gap:56px;padding:40px 32px;border-radius:0;
  transition:color .25s var(--ks-ease),border-radius .3s var(--ks-ease)}
/* Разделитель — отдельным слоем, а не border-bottom: так он прочерчивается
   при появлении от того края, у которого стоит превью, и гаснет, когда
   строка разворачивается в кадр. */
.ks-tech__row::before{content:"";position:absolute;left:0;right:0;bottom:0;height:1px;
  background:var(--ks-hairline);transform-origin:left center;
  transition:opacity .25s var(--ks-ease)}
.ks-tech__row--rev::before{transform-origin:right center}
/* В покое строка прямоугольная. При наведении скругления отрастают: угол
   напротив круглого превью — крупной дугой, как в знаке и в арке хиро,
   остальные слегка. У перевёрнутых строк превью справа, поэтому дуга
   уходит на левый верхний угол. */
/* Строка становится кадром — только под мышью на десктопе. */
@media (hover:hover) and (pointer:fine) and (min-width:861px){
  .ks-tech__row:hover{border-radius:20px clamp(150px,16vw,260px) 20px 20px}
  .ks-tech__row--rev:hover{border-radius:clamp(150px,16vw,260px) 20px 20px 20px}
  .ks-tech__row:hover{color:#fff}
  .ks-tech__row:hover::after{opacity:1}
  .ks-tech__row:hover::before{opacity:0}
  .ks-tech__row:hover .ks-tech__pic{opacity:0;transform:scale(.92)}
  .ks-tech__row:hover .ks-tech__kind,
.ks-tech__row:hover .ks-tech__text{color:rgba(255,255,255,.82)}
}
.ks-tech__row::after{content:"";position:absolute;inset:0;border-radius:inherit;
  background:linear-gradient(rgba(10,10,10,.58),rgba(10,10,10,.58)), var(--bg) center/cover;
  opacity:0;transition:opacity .28s var(--ks-ease);pointer-events:none}
.ks-tech__row > *{position:relative;z-index:1}
.ks-tech__row:last-child::before{content:none}
.ks-tech__row--rev{flex-direction:row-reverse}
.ks-tech__pic{width:190px;height:190px;aspect-ratio:auto;flex:none;position:relative;color:#a8a49c;
  border-radius:var(--ks-radius-full);transition:opacity .25s var(--ks-ease),transform .3s var(--ks-ease)}
.ks-tech__body{flex:1}
.ks-tech__row--rev .ks-tech__body{text-align:right}
.ks-tech__kind{color:var(--ks-text-muted);display:flex;gap:14px;transition:color .25s var(--ks-ease)}
.ks-tech__row--rev .ks-tech__kind{justify-content:flex-end}
.ks-tech__kind .qty{color:var(--ks-orange)}
.ks-tech__name{font-family:var(--ks-font-display);font-size:clamp(44px,4.6vw,64px);line-height:.95;text-transform:uppercase;margin-top:12px}
.ks-tech__text{color:var(--ks-text-secondary);margin-top:12px;max-width:52ch;transition:color .25s var(--ks-ease)}
.ks-tech__row--rev .ks-tech__text{margin-left:auto}

/* Появление: заголовок пословно, затем строки по очереди — сначала
   прочерчивается разделитель, следом из точки вырастает круглое превью,
   и последним поднимается текст. Задержка каждой строки идёт от --i.
   Заливка — backwards, иначе анимация удержит свойства и ховер
   (превью гаснет, строка становится кадром) не сможет их перебить. */
.ks-js .ks-tech__head:not(.is-in) .ks-tech__lead{opacity:0}
.ks-tech__head.is-in .ks-tech__lead{animation:ks-rise-in .6s var(--ks-ease-out) .45s backwards}

.ks-js .ks-tech__list:not(.is-in) .ks-tech__row::before{transform:scaleX(0)}
.ks-js .ks-tech__list:not(.is-in) .ks-tech__pic{opacity:0;transform:scale(.55)}
.ks-js .ks-tech__list:not(.is-in) .ks-tech__body > *{opacity:0}

.ks-tech__list.is-in .ks-tech__row::before{animation:ks-line-x .75s var(--ks-ease-out) backwards;
  animation-delay:calc(var(--i,0) * .06s)}
.ks-tech__list.is-in .ks-tech__pic{animation:ks-tech-pic .7s var(--ks-ease-out) backwards;
  animation-delay:calc(.18s + var(--i,0) * .06s)}
.ks-tech__list.is-in .ks-tech__body > *{animation:ks-rise-in .6s var(--ks-ease-out) backwards}
.ks-tech__list.is-in .ks-tech__body > *:nth-child(1){animation-delay:calc(.3s + var(--i,0) * .06s)}
.ks-tech__list.is-in .ks-tech__body > *:nth-child(2){animation-delay:calc(.38s + var(--i,0) * .06s)}
.ks-tech__list.is-in .ks-tech__body > *:nth-child(3){animation-delay:calc(.46s + var(--i,0) * .06s)}

@keyframes ks-tech-pic{
  from{opacity:0;transform:scale(.55)}
  to{opacity:1;transform:none}
}

/* ── Цены ───────────────────────────────────────────────────────────── */
/* Линии строк — псевдоэлементами, а не border: так они прочерчиваются при
   появлении. Absolute снимает их с раскладки грида. */
.ks-price__row{position:relative;display:grid;grid-template-columns:1fr auto;gap:32px;align-items:baseline;padding:44px 0}
.ks-price__row::before,
.ks-price__row:last-child::after{content:"";position:absolute;left:0;right:0;height:1px;
  background:var(--ks-border-strong);transform-origin:left center}
.ks-price__row::before{top:0}
.ks-price__row:last-child::after{bottom:0}
.ks-price__name{font-family:var(--ks-font-display);font-size:clamp(36px,3.8vw,52px);line-height:1;text-transform:uppercase}
.ks-price__text{color:var(--ks-text-secondary);margin-top:12px;max-width:46ch}
.ks-price__pick{margin-top:18px;font-size:13px}
.ks-price__sum{text-align:right;white-space:nowrap}
.ks-price__val{font-family:var(--ks-font-display);font-weight:600;font-size:clamp(32px,3.4vw,46px);line-height:1;text-transform:uppercase}
.ks-price__val--accent{color:var(--ks-orange)}
/* Цена словами («индивидуально») длиннее суммы: мельче и с переносом,
   иначе на телефоне строка вылезает за край */
.ks-price__val--text{font-size:clamp(26px,2.8vw,38px);white-space:normal}
/* Знак валюты остаётся в обычном начертании — жирные только цифры */
.ks-rub{font-weight:400}
/* «от» — служебное слово, не часть суммы: мельче и приглушённее */
.ks-price__from,.ks-studio__from{font-size:.52em;font-weight:400;color:var(--ks-text-muted)}
/* «от» плотнее самой суммы: у Poiret One один вес, и жирный из него
   браузер только подделывает — берём Onest с настоящим 600, как у кнопок
   и ссылок рядом с ценой */
.ks-price__from,.ks-studio__from{font-family:var(--ks-font-body);font-weight:600;letter-spacing:.04em}
.ks-price__unit{color:var(--ks-text-muted)}
.ks-price__cta{display:flex;justify-content:center;align-items:center;gap:32px;margin-top:56px}
.ks-extra{display:grid;grid-template-columns:1fr 1fr;gap:24px 64px;margin-top:48px}
.ks-extra__head{color:var(--ks-text-muted);display:block;margin-bottom:8px}
.ks-extra__line{display:flex;justify-content:space-between;align-items:baseline;gap:24px;padding:14px 0;border-bottom:1px solid var(--ks-hairline);font-family:var(--ks-font-body);font-size:15px}
.ks-extra__line b{font-weight:600;white-space:nowrap}

/* Появление: надзаголовок, затем строки прайса — линия прочерчивается,
   следом поднимаются индекс, название с описанием и сумма. Дальше две
   колонки допов и кнопки. Задержка строки идёт от --i. */
.ks-js .ks-section-label[data-reveal]:not(.is-in){opacity:0}
.ks-section-label[data-reveal].is-in{animation:ks-rise-in .55s var(--ks-ease-out) both}

.ks-js .ks-price__list:not(.is-in) .ks-price__row::before,
.ks-js .ks-price__list:not(.is-in) .ks-price__row:last-child::after{transform:scaleX(0)}
.ks-js .ks-price__list:not(.is-in) .ks-price__row > *{opacity:0}

.ks-price__list.is-in .ks-price__row::before{animation:ks-line-x .8s var(--ks-ease-out) both;
  animation-delay:calc(var(--i,0) * .06s)}
.ks-price__list.is-in .ks-price__row:last-child::after{animation:ks-line-x .8s var(--ks-ease-out) both;
  animation-delay:calc(.16s + var(--i,0) * .06s)}
.ks-price__list.is-in .ks-price__row > *{animation:ks-rise-in .6s var(--ks-ease-out) both}
.ks-price__list.is-in .ks-price__row > *:nth-child(1){animation-delay:calc(.22s + var(--i,0) * .06s)}
.ks-price__list.is-in .ks-price__row > *:nth-child(2){animation-delay:calc(.32s + var(--i,0) * .06s)}

.ks-js .ks-extra:not(.is-in) .ks-extra__line,
.ks-js .ks-extra:not(.is-in) .ks-extra__head{opacity:0}
.ks-extra.is-in .ks-extra__head{animation:ks-rise-in .5s var(--ks-ease-out) both;
  animation-delay:calc(var(--i,0) * .07s)}
.ks-extra.is-in .ks-extra__line{animation:ks-rise-in .5s var(--ks-ease-out) both;
  animation-delay:calc(.1s + var(--i,0) * .1s + var(--j,0) * .07s)}
.ks-extra__line:nth-of-type(1){--j:1}
.ks-extra__line:nth-of-type(2){--j:2}
.ks-extra__line:nth-of-type(3){--j:3}
.ks-extra__line:nth-of-type(4){--j:4}

.ks-js .ks-price__cta:not(.is-in) > *{opacity:0}
.ks-price__cta.is-in > *{animation:ks-rise-in .55s var(--ks-ease-out) both}
.ks-price__cta.is-in > *:nth-child(2){animation-delay:.12s}

/* Карточка работы целиком кликабельна */
.ks-work{display:block;color:inherit}
.ks-work:hover{color:inherit}


/* ── Почему мы ──────────────────────────────────────────────────────── */
/* Секция-закрепление: сцена липнет к экрану, а пункты и кадр
   переключаются по мере продвижения по треку. Длина трека — один экран
   на сцену плюс по шагу на каждый переход. */
.ks-why{--ks-why-step:80vh;position:relative}
.ks-why__track{height:calc(100vh + 3 * var(--ks-why-step))}
.ks-why__stage{position:sticky;top:0;height:100vh;display:flex;align-items:center;overflow:hidden}
.ks-why__inner{display:grid;grid-template-columns:minmax(420px,1fr) 1.1fr;gap:64px;align-items:center}
.ks-why__title{margin-top:24px}

/* Пункты лежат стопкой в одном месте: виден только активный. Высота
   задаётся самым длинным — иначе колонка дёргалась бы при переключении. */
.ks-why__cards{display:grid;margin-top:48px}
.ks-why__card{grid-area:1 / 1;opacity:0;transform:translateY(22px);pointer-events:none;
  transition:opacity .5s var(--ks-ease),transform .5s var(--ks-ease)}
.ks-why__card.is-on{opacity:1;transform:none;pointer-events:auto}
.ks-why__name{text-transform:uppercase;letter-spacing:.04em;font-size:26px}
.ks-why__text{color:var(--ks-text-secondary);margin-top:14px;max-width:42ch}

/* Шаги — и указатель положения, и навигация: клик доводит до нужного
   участка трека. Полоска под подписью заполняется у активного. */
.ks-why__steps{display:flex;gap:28px;margin-top:56px}
.ks-why__step{background:none;border:0;padding:0;cursor:pointer;text-align:left;
  color:var(--ks-text-muted);transition:color .35s var(--ks-ease)}
.ks-why__step i{display:block;height:2px;margin-top:10px;background:var(--ks-hairline);position:relative}
.ks-why__step i::after{content:"";position:absolute;inset:0;background:var(--ks-orange);
  transform:scaleX(0);transform-origin:left center;transition:transform .45s var(--ks-ease)}
.ks-why__step:hover{color:var(--ks-text-secondary)}
.ks-why__step.is-on{color:var(--ks-text-primary)}
.ks-why__step.is-on i::after{transform:scaleX(1)}

/* Кадры лежат друг на друге: активный проявляется и доезжает до масштаба 1,
   уходящий чуть отъезжает — переход читается как смена плана, а не мигание. */
.ks-why__frame{position:relative;aspect-ratio:4/5;height:min(74vh,720px);justify-self:end;
  --ks-mask:clamp(140px,18vw,260px);color:#a8a49c;overflow:hidden}
.ks-why__frame img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  opacity:0;transform:scale(1.06);
  transition:opacity .7s var(--ks-ease),transform 1.1s var(--ks-ease)}
.ks-why__frame img.is-on{opacity:1;transform:none}

/* ── Бронирование ───────────────────────────────────────────────────── */
/* Секция без внешних линий — только разделители между шагами.
   Линии шагов — псевдоэлементами: их прочерчивает анимация появления,
   border так не умеет. */
.ks-booking{position:relative;padding-block:96px;overflow:clip}
.ks-booking__inner{display:grid;grid-template-columns:1fr minmax(340px,32%);gap:48px;align-items:stretch}
.ks-booking__form{max-width:none}
.ks-booking__step{position:relative;display:flex;align-items:center;gap:14px;margin-top:36px;padding-top:32px}
.ks-booking__step:not(.ks-booking__step--first)::before{content:"";position:absolute;left:0;right:0;top:0;height:1px;
  background:var(--ks-hairline);transform-origin:left center}
.ks-booking__step--first{margin-top:40px;padding-top:0}
.ks-booking__num{width:26px;height:26px;border-radius:50%;background:var(--ks-orange-cta);color:#fff;display:flex;align-items:center;justify-content:center;font-family:var(--ks-font-mono);font-size:11px;font-weight:500}
.ks-booking__label{text-transform:uppercase;letter-spacing:.03em;font-size:18px}
.ks-booking__hint{color:var(--ks-text-muted);margin-top:8px;padding-left:40px}
.ks-booking__legend{color:var(--ks-text-muted);margin-top:8px;padding-left:40px;display:flex;gap:18px;align-items:center}
.ks-booking__legend span{display:flex;align-items:center;gap:7px}
.ks-booking__legend i{width:8px;height:8px;border-radius:50%;background:var(--ks-orange)}
.ks-booking__legend i.busy{background:var(--ks-gray-300)}
.ks-booking__opts{display:flex;gap:10px;margin-top:18px}
/* Форматы — сеткой: их пять, и в строку они не помещаются. */
.ks-booking__opts--fmt{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr))}

/* Группы шагов. Законченная сворачивается в строку того же ритма, что и
   шаг: линия сверху, кружок, подпись — только вместо номера галочка, а
   справа то, что выбрано. */
.ks-bgroup__done{flex-wrap:wrap;row-gap:6px}
.ks-bgroup__check{width:26px;height:26px;border-radius:50%;border:1px solid var(--ks-orange-cta);color:var(--ks-orange-cta);
  display:flex;align-items:center;justify-content:center;font-size:12px;flex:none}
.ks-bgroup__val{flex:1 1 220px;min-width:0;font-family:var(--ks-font-body);font-weight:600;font-size:15px;color:var(--ks-text-secondary)}
.ks-bgroup__edit{margin-left:auto;font-size:13px;color:var(--ks-text-muted)}
.ks-bgroup__next{margin-top:32px}
.ks-bgroup__next:disabled{opacity:.45;cursor:not-allowed}
/* Группа, открытая после клика, поднимается целиком. Лесенка начальной
   анимации внутри неё гасится: она рассчитана на первый показ блока. */
.ks-bgroup.is-opening,
.ks-bgroup__body.is-opening,
.ks-bgroup__done.is-opening{animation:ks-rise-in .45s var(--ks-ease-out) both}
.ks-booking__form.is-in .is-opening :is(.ks-booking__num,.ks-booking__label,.ks-booking__hint,.ks-booking__legend,.ks-booking__weeknav,.ks-booking__opts,.ks-booking__days,.ks-booking__times,.ks-dur,.ks-extra-opt),
.ks-booking__form.is-in .is-opening .ks-booking__step::before{animation:none}
/* Формат и камеры: название и цена часа в две строки. Двойной класс —
   общий .ks-chip объявлен ниже и иначе перебил бы отступы. */
.ks-chip.ks-chip--fmt{flex-direction:column;gap:8px;padding:16px 24px;line-height:1.2}
.ks-chip--fmt .sub{font-weight:500;white-space:nowrap}

/* Длительность — ползунок от часа до полного дня (студия работает
   09:00–22:00, то есть 13 часов). Точки на шкале — пороги скидок. */
.ks-dur{margin-top:22px;max-width:560px}
.ks-dur__head{display:flex;align-items:baseline;gap:12px}
.ks-dur__val{font-family:var(--ks-font-display);font-size:28px;line-height:1;text-transform:uppercase}
.ks-dur__off{color:var(--ks-orange);border:1px solid var(--ks-orange);border-radius:var(--ks-radius-sm);padding:4px 9px}
.ks-dur__scale{display:flex;justify-content:space-between;margin-top:12px;color:var(--ks-text-muted)}
.ks-dur__scale b{color:var(--ks-orange);font-weight:inherit}

/* Высота задаётся элементу, а не треку: иначе поле остаётся в две точки
   высотой и бегунок вылезает за него вместе с зоной захвата. */
.ks-dur__range{-webkit-appearance:none;appearance:none;width:100%;height:20px;margin-top:16px;background:transparent;cursor:pointer}
.ks-dur__range:focus-visible{outline:2px solid var(--ks-orange);outline-offset:6px}
/* Заполненная часть — градиентом до текущей доли: одна переменная --p,
   которую двигает JS. */
.ks-dur__range::-webkit-slider-runnable-track{height:2px;border-radius:2px;
  background:linear-gradient(to right,var(--ks-orange-cta) 0 var(--p,0%),var(--ks-hairline) var(--p,0%) 100%)}
.ks-dur__range::-moz-range-track{height:2px;border-radius:2px;background:var(--ks-hairline)}
.ks-dur__range::-moz-range-progress{height:2px;border-radius:2px;background:var(--ks-orange-cta)}
.ks-dur__range::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:22px;height:22px;margin-top:-10px;position:relative;
  border-radius:50%;background:var(--ks-orange-cta);border:0;
  transition:transform var(--ks-dur-fast) var(--ks-ease)}
.ks-dur__range::-moz-range-thumb{width:22px;height:22px;border-radius:50%;background:var(--ks-orange-cta);border:0;
  transition:transform var(--ks-dur-fast) var(--ks-ease)}
/* Увеличение бегунка — мышиный ховер. */
@media (hover:hover) and (pointer:fine){
  .ks-dur__range:hover::-webkit-slider-thumb,
.ks-dur__range:active::-webkit-slider-thumb{transform:scale(1.15)}
  .ks-dur__range:hover::-moz-range-thumb,
.ks-dur__range:active::-moz-range-thumb{transform:scale(1.15)}
}
.ks-booking__days{display:flex;gap:10px;margin-top:18px;flex-wrap:wrap}
.ks-booking__times{display:grid;grid-template-columns:repeat(7,1fr);gap:8px;margin-top:18px}
/* Правый верхний угол — крупной дугой, как в знаке; остальные прямые.
   Радиус конечный: с --ks-radius-full браузер растягивает дугу на всю
   ширину панели, и фотография вылезает за её край.
   Дуга фотографии меньше ровно на отступ — тогда обе идут эквидистантно. */
.ks-booking__side{--ks-arc:clamp(150px,14vw,210px);--ks-pad:36px;
  border-radius:0 var(--ks-arc) 0 0;padding:var(--ks-pad) var(--ks-pad) 32px}
.ks-booking__card{position:sticky;top:104px}

/* Дополнительное оборудование — переключатели с ценой. */
.ks-booking__extras{display:flex;flex-wrap:wrap;gap:10px;margin-top:18px}
.ks-extra-opt{display:inline-flex;align-items:center;gap:10px;cursor:pointer;
  border:1px solid var(--ks-hairline);border-radius:var(--ks-radius-sm);padding:12px 16px;
  transition:border-color var(--ks-dur-fast) var(--ks-ease),color var(--ks-dur-fast) var(--ks-ease)}
.ks-extra-opt:hover{border-color:var(--ks-text-primary)}
.ks-extra-opt input{position:absolute;opacity:0;width:0;height:0}
.ks-extra-opt .box{position:relative;flex:none;width:16px;height:16px;border:1px solid var(--ks-border-strong);
  border-radius:3px;transition:background var(--ks-dur-fast) var(--ks-ease),border-color var(--ks-dur-fast) var(--ks-ease)}
.ks-extra-opt .box::after{content:"";position:absolute;left:4px;top:1px;width:5px;height:9px;
  border:solid #fff;border-width:0 1.5px 1.5px 0;transform:rotate(45deg) scale(0);
  transition:transform var(--ks-dur-fast) var(--ks-ease)}
.ks-extra-opt .name{font-family:var(--ks-font-body);font-size:15px}
.ks-extra-opt .price{color:var(--ks-text-muted)}
.ks-extra-opt.is-on{border-color:var(--ks-orange-cta)}
.ks-extra-opt.is-on .box{background:var(--ks-orange-cta);border-color:var(--ks-orange-cta)}
.ks-extra-opt.is-on .box::after{transform:rotate(45deg) scale(1)}
.ks-extra-opt input:focus-visible ~ .name{text-decoration:underline}

/* Заявка: поля на оранжевом — прозрачные, с белой линией снизу. */
/* Кнопка возврата стоит сразу под фото зала: без верхнего отступа она
   липнет к кадру, а заголовок формы поджимает её снизу. */
.ks-booking__back{margin:20px 0 28px}
.ks-booking__leadhead{font-size:19px}
.ks-booking__leadsum{color:var(--ks-text-secondary);margin-top:8px}
.ks-field{display:block;margin-top:16px}
.ks-field > .ks-label{display:block;color:var(--ks-text-secondary);margin-bottom:6px}
.ks-field input,
.ks-field textarea{width:100%;background:transparent;border:0;border-bottom:1px solid var(--ks-hairline);
  color:var(--ks-text-primary);font-family:var(--ks-font-body);font-size:15px;padding:8px 0;
  transition:border-color var(--ks-dur-fast) var(--ks-ease)}
.ks-field textarea{resize:vertical}
.ks-field input::placeholder,
.ks-field textarea::placeholder{color:var(--ks-text-muted)}
.ks-field input:focus,
.ks-field textarea:focus{outline:none;border-bottom-color:#fff}
.ks-booking__lead .ks-field input:focus,
.ks-booking__lead .ks-field textarea:focus{border-bottom-color:var(--ks-hairline);box-shadow:none}
.ks-booking__lead :focus,
.ks-booking__lead :focus-visible,
.ks-request-modal :focus,
.ks-request-modal :focus-visible{outline:none;box-shadow:none}
.ks-booking__lead .ks-check input:focus-visible,
.ks-request-modal .ks-check input:focus-visible{outline:none;box-shadow:none}
.ks-field.is-bad input,
.ks-field.is-bad textarea{border-bottom-color:#fff}
.ks-field.is-bad > .ks-label{color:#fff}

.ks-check{display:flex;align-items:flex-start;gap:10px;margin-top:20px;cursor:pointer}
/* Свой чекбокс вместо системного: accent-color красит галочку в цвет,
   контрастный заливке, и на белом квадрате она получалась белой. Здесь
   квадрат всегда белый, а галочка — оранжевая, как на панели брони. */
.ks-check input{appearance:none;-webkit-appearance:none;position:relative;
  margin-top:2px;width:18px;height:18px;flex:none;cursor:pointer;
  background:#fff;border:1px solid #fff;border-radius:4px;
  transition:box-shadow var(--ks-dur-fast) var(--ks-ease)}
.ks-check input::after{content:"";position:absolute;left:5px;top:2px;width:5px;height:9px;
  border:solid var(--ks-orange-cta);border-width:0 2px 2px 0;
  transform:rotate(45deg) scale(0);transform-origin:center;
  transition:transform var(--ks-dur-fast) var(--ks-ease)}
/* Дизайн-система красит отмеченный чекбокс оранжевым и подставляет
   галочку картинкой (.ks-check input:checked в components.css). Специфичность
   там выше, поэтому гасим фон и картинку тем же по весу селектором. */
.ks-check input:checked{background:#fff;border-color:#fff;background-image:none}
.ks-check input:checked::after{transform:rotate(45deg) scale(1)}
.ks-check input:focus-visible{outline:2px solid #fff;outline-offset:2px}
.ks-check.is-bad input{box-shadow:0 0 0 2px rgba(255,255,255,.55)}
/* Ссылки в согласии отзываются подчёркиванием, а не цветом: общий
   a:hover красит их в оранжевый, и на оранжевой панели текст пропадает. */
.ks-check a{color:inherit;text-decoration:none;text-underline-offset:3px}
.ks-check a:hover,
.ks-check a:focus-visible{color:inherit;text-decoration:underline}
.ks-check.is-bad span{text-decoration:underline}
.ks-booking__error{color:#fff;margin-top:12px;font-weight:500}
/* Экран «отправлено» начинается сразу под фото зала — без отступа
   заголовок липнет к кадру, как это было у кнопки «Изменить выбор». */
.ks-booking__done{padding-top:20px}
.ks-booking__done .ks-booking__note{margin-top:10px}
.ks-booking__pic{aspect-ratio:4/2.9;position:relative;color:#d9c9bf;
  --ks-mask:calc(var(--ks-arc) - var(--ks-pad))}
.ks-booking__pic .ks-slot-wrap{position:absolute;inset:0;opacity:0;pointer-events:none;transition:opacity var(--ks-dur) var(--ks-ease)}
.ks-booking__pic .ks-slot-wrap.is-active{opacity:1;pointer-events:auto}
.ks-booking__picname{position:absolute;left:20px;bottom:14px;font-family:var(--ks-font-display);font-weight:400;font-size:30px;line-height:1;text-transform:uppercase;color:#fff;pointer-events:none}
.ks-booking__line{display:flex;justify-content:space-between;gap:16px;align-items:baseline;border-bottom:1px solid var(--ks-hairline);padding:14px 0 10px}
.ks-booking__line .k{color:var(--ks-text-secondary)}
.ks-booking__line .v{font-family:var(--ks-font-body);font-weight:600;font-size:15px}
.ks-booking__total{display:flex;justify-content:space-between;gap:16px;align-items:baseline;margin-top:24px}
.ks-booking__total .v{font-family:var(--ks-font-display);font-size:clamp(34px,3vw,44px);line-height:.95;white-space:nowrap}
/* Цена заявки: «от» и единица мелко, как в прайсе. Не хватит ширины —
   единица уходит на строку ниже, а не за край карточки */
.ks-booking__total .v.is-req{white-space:normal;text-align:right}
.ks-booking__total .v .sum,.ks-booking__total .v .unit{white-space:nowrap}
/* Цена заявки — ориентир, а не точная сумма: чуть мельче обычного итога,
   иначе на десктопе единица не помещается в строку */
.ks-booking__total .v .sum{font-size:.88em}
.ks-booking__total .v .from{font-family:var(--ks-font-body);font-weight:600;font-size:.42em;letter-spacing:.04em;color:var(--ks-text-secondary)}
.ks-booking__total .v .unit{color:var(--ks-text-secondary)}
.ks-booking__note{color:var(--ks-text-secondary);margin-top:14px}
.ks-booking__submit{width:100%;margin-top:20px}
/* Пока время не выбрано, шаг к контактам недоступен. */
.ks-booking__submit:disabled{opacity:.45;cursor:not-allowed}
.ks-booking__submit:disabled:hover{background:#fff;color:var(--ks-orange-cta)}

/* Появление: линия секции прочерчивается, заголовок выезжает пословно,
   дальше шаги по очереди — линия шага, кружок номера из точки, подпись и
   наборы вариантов. Справа карточка раскрывается сверху вниз, за ней
   содержимое.
   Наборы вариантов анимируются целиком, а не каждой капсулой: капсулы
   перерисовываются из JS на любой клик, и поштучная анимация проигрывала
   бы заново при каждом выборе. */
.ks-js .ks-booking__form:not(.is-in) .ks-booking__step::before{transform:scaleX(0)}
.ks-js .ks-booking__form:not(.is-in) .ks-booking__num{opacity:0;transform:scale(.9)}
.ks-js .ks-booking__form:not(.is-in) .ks-booking__label,
.ks-js .ks-booking__form:not(.is-in) .ks-booking__hint,
.ks-js .ks-booking__form:not(.is-in) .ks-booking__legend,
.ks-js .ks-booking__form:not(.is-in) .ks-booking__opts,
.ks-js .ks-booking__form:not(.is-in) .ks-dur,
.ks-js .ks-booking__form:not(.is-in) .ks-booking__weeknav,
.ks-js .ks-booking__form:not(.is-in) .ks-booking__days,
.ks-js .ks-booking__form:not(.is-in) .ks-booking__times,
.ks-js .ks-booking__form:not(.is-in) .ks-extra-opt{opacity:0}

.ks-booking__form.is-in .ks-booking__step::before{animation:ks-line-x .7s var(--ks-ease-out) both;
  animation-delay:calc(.3s + var(--i,0) * .12s)}
.ks-booking__form.is-in .ks-booking__num{animation:ks-book-dot .55s cubic-bezier(.34,1.28,.64,1) both;
  animation-delay:calc(.38s + var(--i,0) * .12s)}
.ks-booking__form.is-in .ks-booking__label{animation:ks-rise-in .55s var(--ks-ease-out) both;
  animation-delay:calc(.44s + var(--i,0) * .12s)}
.ks-booking__form.is-in .ks-booking__hint,
.ks-booking__form.is-in .ks-booking__legend{animation:ks-rise-in .55s var(--ks-ease-out) both;
  animation-delay:calc(.52s + var(--i,0) * .12s)}
.ks-booking__form.is-in .ks-booking__weeknav{animation:ks-rise-in .55s var(--ks-ease-out) both;
  animation-delay:calc(.56s + var(--i,0) * .12s)}
.ks-booking__form.is-in .ks-booking__opts,
.ks-booking__form.is-in .ks-booking__days,
.ks-booking__form.is-in .ks-booking__times{animation:ks-rise-in .6s var(--ks-ease-out) both;
  animation-delay:calc(.6s + var(--i,0) * .12s)}
.ks-booking__form.is-in .ks-dur{animation:ks-rise-in .6s var(--ks-ease-out) both;
  animation-delay:calc(.68s + var(--i,0) * .12s)}
.ks-booking__form.is-in .ks-extra-opt{animation:ks-rise-in .5s var(--ks-ease-out) backwards;
  animation-delay:calc(.6s + var(--i,0) * .12s + var(--j,0) * .06s)}
.ks-booking__extras .ks-extra-opt:nth-child(2){--j:1}
.ks-booking__extras .ks-extra-opt:nth-child(3){--j:2}

@keyframes ks-book-dot{
  from{opacity:0;transform:scale(.9)}
  to{opacity:1;transform:none}
}

.ks-js .ks-booking__side:not(.is-in){opacity:0;transform:translateY(56px)}
/* Содержимое карточки — по видимости самой карточки, а не панели: панель
   высотой во всю секцию и засчитывается видимой, когда карточка ещё под
   нижним краем экрана, — анимация успевала отыграть до того, как на неё
   посмотрят. */
.ks-js .ks-booking__card:not(.is-in) [data-b-view="summary"] > *{opacity:0}
.ks-js .ks-booking__card:not(.is-in) .ks-booking__pic{clip-path:inset(0 50% round 2px)}
/* Панель поднимается снизу и проявляется — тем же движением, что и текст,
   только длиннее по амплитуде. Секции задан overflow:clip, чтобы выезд
   не был виден за её нижней границей (clip, а не hidden: hidden сделал бы
   секцию скролл-контейнером и сломал липкую карточку). */
.ks-booking__side.is-in{animation:ks-book-rise .85s var(--ks-ease-out) .1s backwards}
.ks-booking__card.is-in .ks-booking__pic{animation:ks-reel-open .85s var(--ks-ease-out) .1s backwards}
.ks-booking__card.is-in [data-b-view="summary"] > *{animation:ks-rise-in .55s var(--ks-ease-out) both}
.ks-booking__card.is-in [data-b-view="summary"] > *:nth-child(1){animation-delay:.45s}
.ks-booking__card.is-in [data-b-view="summary"] > *:nth-child(2){animation-delay:.53s}
.ks-booking__card.is-in [data-b-view="summary"] > *:nth-child(3){animation-delay:.61s}
.ks-booking__card.is-in [data-b-view="summary"] > *:nth-child(4){animation-delay:.69s}
.ks-booking__card.is-in [data-b-view="summary"] > *:nth-child(5){animation-delay:.8s}
.ks-booking__card.is-in [data-b-view="summary"] > *:nth-child(6){animation-delay:.88s}
.ks-booking__card.is-in [data-b-view="summary"] > *:nth-child(7){animation-delay:.96s}
.ks-booking__card.is-in [data-b-view="summary"] > *:nth-child(8){animation-delay:1.04s}

/* Переключение вида — короткий подъём, чтобы смена экрана читалась.
   Возврат идёт обратным путём: вперёд экраны приходят снизу, назад —
   сверху. Иначе «Изменить выбор» ощущается как ещё один шаг вперёд. */
.ks-booking__lead,
.ks-booking__done{animation:ks-rise-in .45s var(--ks-ease-out) both}
.ks-booking__card.is-back [data-b-view="summary"]{animation:ks-fall-in .45s var(--ks-ease-out) both}

@keyframes ks-fall-in{
  from{opacity:0;transform:translateY(-22px)}
  to{opacity:1;transform:none}
}

@keyframes ks-book-rise{
  from{opacity:0;transform:translateY(56px)}
  to{opacity:1;transform:none}
}

.ks-chip{padding:13px 20px;border:1px solid var(--ks-border-strong);border-radius:var(--ks-radius-sm);background:transparent;color:inherit;font-family:var(--ks-font-body);font-weight:600;font-size:14px;line-height:1;cursor:pointer;transition:background .15s var(--ks-ease),border-color .15s var(--ks-ease),color .15s var(--ks-ease);display:flex;align-items:center;justify-content:center;gap:8px}
.ks-chip.is-on{background:var(--ks-orange-cta);border-color:var(--ks-orange-cta);color:#fff}
.ks-chip.is-busy{border-color:var(--ks-hairline);color:var(--ks-gray-300);cursor:not-allowed;text-decoration:line-through}
.ks-chip .sub{opacity:.65}
.ks-chip--studio,.ks-chip--cam{padding:13px 28px}
.ks-chip--day{flex-direction:column;gap:6px;width:64px;padding:13px 0}
.ks-chip--day .d{font-family:var(--ks-font-body);font-weight:600;font-size:19px}
.ks-chip--day .wd{opacity:.7}
.ks-chip--time{font-family:var(--ks-font-mono);font-size:13px;padding:15px 0}
/* Часы, которые уйдут под съёмку вслед за выбранным началом. Тот же
   оранжевый, только разбавленный: промежуток читается целиком, но видно,
   откуда он стартует. Полупрозрачность вместо --ks-orange-soft — тот в
   тёмной теме почти чёрный и выглядит как погашенная клетка. */
.ks-chip--time.is-span{background:color-mix(in srgb,var(--ks-orange-cta) 28%,transparent);
  border-color:var(--ks-orange-cta);color:var(--ks-text-primary)}

/* ── Отзывы ─────────────────────────────────────────────────────────── */
.ks-reviews{border-top:0}

/* ── Отклик на нажатие ──────────────────────────────────────────────
   Обратная связь приходит в момент нажатия, а не по отпусканию: пока её
   нет, интерфейс кажется неотзывчивым, сколько бы ни было анимаций
   появления. Гейт по указателю здесь не нужен — :active корректен и на
   тач-экране. */
.ks-btn,
.ks-chip,
.ks-extra-opt,
.ks-faq__q{transition-property:background,color,border-color,transform;
  transition-duration:var(--ks-dur-fast);transition-timing-function:var(--ks-ease)}
.ks-btn:active,
.ks-chip:not(.is-busy):active,
.ks-extra-opt:active,
.ks-faq__q:active{transform:scale(.97);transition-duration:.1s}
.ks-btn--icon:active,
.ks-btn--round:active{transform:scale(.92)}
.ks-btn:disabled:active,
.ks-booking__submit:disabled:active{transform:none}

/* ── Страница контактов ─────────────────────────────────────────────── */
.ks-cnt__lead{color:var(--ks-text-secondary);margin-top:24px;max-width:56ch}
.ks-cnt__grid{display:grid;grid-template-columns:repeat(4,1fr);gap:24px;margin-top:72px}
/* Линия — псевдоэлементом: при появлении она прочерчивается слева направо,
   border так не умеет. */
.ks-cnt__card{position:relative;display:flex;flex-direction:column;padding-top:22px}
.ks-cnt__card::before{content:"";position:absolute;left:0;right:0;top:0;height:1px;
  background:var(--ks-border-strong);transform-origin:left center}
.ks-cnt__k{color:var(--ks-text-muted)}
.ks-cnt__v{font-family:var(--ks-font-display);font-size:clamp(24px,2vw,30px);line-height:1.05;margin-top:14px;
  transition:color var(--ks-dur-fast) var(--ks-ease)}
a.ks-cnt__v:hover{color:var(--ks-orange-cta)}
.ks-cnt__extra{color:var(--ks-text-secondary);margin-top:12px}

.ks-cnt-map__grid{display:grid;grid-template-columns:minmax(320px,.75fr) 1.25fr;gap:48px;align-items:stretch}
.ks-cnt-map__side{display:flex;flex-direction:column}
.ks-cnt-map__title{font-size:20px;text-transform:uppercase;letter-spacing:.03em}
.ks-cnt-map__row{position:relative;display:grid;grid-template-columns:96px 1fr;gap:20px;align-items:baseline;
  padding:18px 0 14px}
.ks-cnt-map__row::before,
.ks-cnt-map__row:last-of-type::after{content:"";position:absolute;left:0;right:0;height:1px;
  background:var(--ks-hairline);transform-origin:left center}
.ks-cnt-map__row::before{top:0}
.ks-cnt-map__row:last-of-type::after{bottom:0}
.ks-cnt-map__row:first-of-type{margin-top:20px}
.ks-cnt-map__row .k{color:var(--ks-text-muted)}
.ks-cnt-map__row .v{font-family:var(--ks-font-body);font-size:15px;line-height:1.45}
.ks-cnt-map__cta{margin-top:32px;align-self:flex-start}
/* Карта — сторонний виджет, поэтому кадр держит пропорции сам */
.ks-cnt-map__frame{--ks-mask:clamp(120px,12vw,180px);aspect-ratio:16/11;min-height:420px;
  background:var(--ks-surface-alt);overflow:hidden}
.ks-cnt-map__frame iframe{display:block;width:100%;height:100%;border:0}

.ks-js .ks-cnt__head:not(.is-in) .ks-cnt__lead{opacity:0}
.ks-cnt__head.is-in .ks-cnt__lead{animation:ks-rise-in .6s var(--ks-ease-out) .4s backwards}
/* Сначала прочерчиваются линии карточек — одна за другой, — потом
   поднимается содержимое. */
.ks-js .ks-cnt__grid:not(.is-in) .ks-cnt__card::before{transform:scaleX(0)}
.ks-js .ks-cnt__grid:not(.is-in) .ks-cnt__card > *{opacity:0}
.ks-cnt__grid.is-in .ks-cnt__card::before{animation:ks-line-x .7s var(--ks-ease-out) backwards;
  animation-delay:calc(var(--i,0) * .07s)}
.ks-cnt__grid.is-in .ks-cnt__card > *{animation:ks-rise-in .55s var(--ks-ease-out) backwards;
  animation-delay:calc(.25s + var(--i,0) * .07s)}

.ks-js .ks-cnt-map__grid:not(.is-in) .ks-cnt-map__row::before,
.ks-js .ks-cnt-map__grid:not(.is-in) .ks-cnt-map__row:last-of-type::after{transform:scaleX(0)}
.ks-cnt-map__grid.is-in .ks-cnt-map__row::before{animation:ks-line-x .6s var(--ks-ease-out) backwards;
  animation-delay:calc(.2s + var(--i,0) * .06s)}
.ks-cnt-map__grid.is-in .ks-cnt-map__row:last-of-type::after{animation:ks-line-x .6s var(--ks-ease-out) .52s backwards}
.ks-cnt-map__row:nth-of-type(1){--i:0}
.ks-cnt-map__row:nth-of-type(2){--i:1}
.ks-cnt-map__row:nth-of-type(3){--i:2}
.ks-cnt-map__row:nth-of-type(4){--i:3}
.ks-js .ks-cnt-map__grid:not(.is-in) .ks-cnt-map__side > *{opacity:0}
.ks-js .ks-cnt-map__grid:not(.is-in) .ks-cnt-map__frame{clip-path:inset(0 50% round 2px)}
.ks-cnt-map__grid.is-in .ks-cnt-map__side > *{animation:ks-rise-in .55s var(--ks-ease-out) backwards}
.ks-cnt-map__grid.is-in .ks-cnt-map__side > *:nth-child(1){animation-delay:.1s}
.ks-cnt-map__grid.is-in .ks-cnt-map__side > *:nth-child(2){animation-delay:.18s}
.ks-cnt-map__grid.is-in .ks-cnt-map__side > *:nth-child(3){animation-delay:.26s}
.ks-cnt-map__grid.is-in .ks-cnt-map__side > *:nth-child(4){animation-delay:.34s}
.ks-cnt-map__grid.is-in .ks-cnt-map__side > *:nth-child(5){animation-delay:.42s}
.ks-cnt-map__grid.is-in .ks-cnt-map__side > *:nth-child(6){animation-delay:.5s}
.ks-cnt-map__grid.is-in .ks-cnt-map__frame{animation:ks-reel-open .9s var(--ks-ease-out) .2s backwards}

/* ── Вопросы ────────────────────────────────────────────────────────── */
.ks-faq__inner{display:grid;grid-template-columns:minmax(320px,.8fr) 1.4fr;gap:80px;align-items:start}
.ks-faq__col{position:sticky;top:120px}
.ks-faq__lead{color:var(--ks-text-secondary);margin-top:24px;max-width:32ch}
.ks-faq__link{margin-top:24px}

.ks-faq__item{border-bottom:1px solid var(--ks-hairline)}
.ks-faq__item:first-child{border-top:1px solid var(--ks-hairline)}
.ks-faq__q{display:flex;align-items:center;justify-content:space-between;gap:32px;width:100%;
  background:none;border:0;padding:26px 0;cursor:pointer;text-align:left;
  font-family:var(--ks-font-body);font-weight:500;font-size:19px;line-height:1.3;
  color:var(--ks-text-primary);transition:color var(--ks-dur-fast) var(--ks-ease)}
.ks-faq__q:hover{color:var(--ks-orange-cta)}

/* Знак: плюс, у раскрытого пункта поворачивается в крестик. */
.ks-faq__mark{position:relative;flex:none;width:18px;height:18px}
.ks-faq__mark::before,
.ks-faq__mark::after{content:"";position:absolute;left:50%;top:50%;background:currentColor;
  transition:transform .35s var(--ks-ease)}
.ks-faq__mark::before{width:16px;height:1px;transform:translate(-50%,-50%)}
.ks-faq__mark::after{width:1px;height:16px;transform:translate(-50%,-50%)}
.ks-faq__item.is-open .ks-faq__q{color:var(--ks-orange-cta)}
.ks-faq__item.is-open .ks-faq__mark::before{transform:translate(-50%,-50%) rotate(180deg)}
.ks-faq__item.is-open .ks-faq__mark::after{transform:translate(-50%,-50%) rotate(90deg)}

/* Ответ раскрывается по строке грида 0fr → 1fr: так высота анимируется
   без замеров в JS. */
.ks-faq__a{display:grid;grid-template-rows:0fr;
  transition:grid-template-rows .45s var(--ks-ease),opacity .35s var(--ks-ease);opacity:0}
.ks-faq__a > div{overflow:hidden}
.ks-faq__item.is-open .ks-faq__a{grid-template-rows:1fr;opacity:1}
.ks-faq__a p{color:var(--ks-text-secondary);max-width:62ch;padding-bottom:26px}

/* Появление: колонка слева, следом строки вопросов лесенкой. */
.ks-js .ks-faq__col:not(.is-in) .ks-faq__lead,
.ks-js .ks-faq__col:not(.is-in) .ks-faq__link{opacity:0}
.ks-faq__col.is-in .ks-faq__lead{animation:ks-rise-in .6s var(--ks-ease-out) .35s backwards}
.ks-faq__col.is-in .ks-faq__link{animation:ks-rise-in .6s var(--ks-ease-out) .45s backwards}
.ks-js .ks-faq:not(.is-in) .ks-faq__item{opacity:0}
.ks-faq.is-in .ks-faq__item{animation:ks-rise-in .5s var(--ks-ease-out) backwards;
  animation-delay:calc(.15s + var(--i,0) * .06s)}
.ks-reviews__quote{margin:48px 0 0;max-width:1220px}
.ks-reviews__foot{display:flex;align-items:center;gap:20px;margin-top:56px}
.ks-reviews__ava{width:64px;height:64px;flex:none;border-radius:50%;background:var(--ks-orange-soft);display:flex;align-items:center;justify-content:center;font-family:var(--ks-font-display);font-size:30px;line-height:1;text-transform:uppercase;color:var(--ks-orange-deep)}
.ks-reviews__pager{margin-left:auto;display:flex;align-items:center;gap:24px}
.ks-reviews__pager .ks-btn--icon{border:1px solid var(--ks-border-strong);width:46px;height:46px;font-size:16px}

/* ── CTA ────────────────────────────────────────────────────────────── */
.ks-cta{position:relative;overflow:hidden}
.ks-cta__ring{position:absolute;right:-150px;bottom:-230px;width:540px;height:540px;border-radius:50%;border:56px solid currentColor}
.ks-cta__inner{padding-block:132px;position:relative}
.ks-cta__text{color:var(--ks-text-secondary);margin-top:28px}
.ks-cta__actions{display:flex;gap:20px;margin-top:48px;flex-wrap:wrap}

/* ── Футер ──────────────────────────────────────────────────────────── */
.ks-footer{position:relative;overflow:hidden;padding:88px 0 28px}
.ks-footer__grid{display:grid;grid-template-columns:1fr 1fr 1.1fr 1fr 1.2fr;gap:40px}
@media (max-width:1180px){.ks-footer__grid{grid-template-columns:1.4fr 1fr 1fr;row-gap:44px}}
.ks-footer__col{display:flex;flex-direction:column;gap:12px;align-items:flex-start}
.ks-footer__brand{display:flex;flex-direction:column;align-items:flex-start}
.ks-footer__about{color:var(--ks-text-secondary);margin-top:16px;max-width:26ch}
.ks-footer__addr{color:var(--ks-text-secondary)}
/* Надпись — те же контуры, что в знаке, растянутые по ширине футера.
   Прорисовка идёт по контуру каждой буквы, следом набирается заливка. */
.ks-footer__mark{display:block;width:100%;height:auto;margin-top:96px;color:var(--ks-white)}
.ks-footer__mark > *{
  fill:currentColor;
  stroke:currentColor;
  stroke-width:.35;
  stroke-dasharray:1;
  vector-effect:non-scaling-stroke;
}
.ks-footer__legal{position:relative;display:flex;justify-content:space-between;gap:24px;border-top:0;margin-top:48px;padding-top:22px}
.ks-footer__legal::before{content:"";position:absolute;left:0;right:0;top:0;height:1px;
  background:rgba(255,255,255,.18);transform-origin:left center}
.ks-footer__legal .links{display:flex;gap:24px}
/* Подпись разработчика прижата к правому краю: копирайт слева, она справа,
   а запасной список ссылок (если он есть) остаётся между ними. */
.ks-footer__author{margin-left:auto;color:var(--ks-text-muted);
  transition:color var(--ks-dur) var(--ks-ease)}
.ks-footer__author:hover{color:#fff}

/* Появление футера: колонки лесенкой, затем крупное слово выезжает
   из-под нижней кромки, и последней прочерчивается линия с копирайтом. */
.ks-js .ks-footer__grid:not(.is-in) > * > *{opacity:0}
.ks-footer__grid.is-in > * > *{animation:ks-rise-in .55s var(--ks-ease-out) backwards;
  animation-delay:calc(.1s + var(--i,0) * .08s + var(--j,0) * .05s)}
.ks-footer__col > *:nth-child(2),
.ks-footer__brand > *:nth-child(2){--j:1}
.ks-footer__col > *:nth-child(3){--j:2}
.ks-footer__col > *:nth-child(4){--j:3}
.ks-footer__col > *:nth-child(5){--j:4}
.ks-footer__col > *:nth-child(6){--j:5}

/* Буквы прорисовываются по очереди, заливка каждой набирается следом. */
.ks-js .ks-footer__mark:not(.is-in) > *{fill-opacity:0;stroke-dashoffset:1}
.ks-footer__mark.is-in > *{
  animation:ks-draw .8s var(--ks-ease) backwards, ks-fill .9s linear backwards;
  animation-delay:calc(.15s + var(--i,0) * .07s), calc(.6s + var(--i,0) * .07s);
}
.ks-footer__mark > *:nth-child(1){--i:0}
.ks-footer__mark > *:nth-child(2){--i:1}
.ks-footer__mark > *:nth-child(3){--i:2}
.ks-footer__mark > *:nth-child(4){--i:3}
.ks-footer__mark > *:nth-child(5){--i:4}
.ks-footer__mark > *:nth-child(6){--i:5}
.ks-footer__mark > *:nth-child(7){--i:6}
.ks-footer__mark > *:nth-child(8){--i:7}
.ks-footer__mark > *:nth-child(9){--i:8}

.ks-js .ks-footer__legal:not(.is-in)::before{transform:scaleX(0)}
.ks-js .ks-footer__legal:not(.is-in) > *{opacity:0}
.ks-footer__legal.is-in::before{animation:ks-line-x .8s var(--ks-ease-out) .1s backwards}
.ks-footer__legal.is-in > *{animation:ks-rise-in .5s var(--ks-ease-out) backwards}
.ks-footer__legal.is-in > *:nth-child(1){animation-delay:.35s}
.ks-footer__legal.is-in > *:nth-child(2){animation-delay:.43s}


/* Меньше движения — не значит «ничего»: сдвиги, морфы и рост убираем,
   проявление и смену цвета оставляем, они помогают понять, что изменилось. */
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{
    animation-duration:.2s !important;
    animation-delay:0s !important;
    animation-iteration-count:1 !important;
    transition-duration:.2s !important;
  }
  .ks-words .ks-reveal>*,
  .ks-booking__side,
  .ks-booking__lead,
  .ks-booking__done,
  .ks-cnt__card > *,
  .ks-faq__item,
  .ks-tech__pic,
  .ks-inc__item,
  .ks-booking__num,
  .ks-footer__grid > * > *,
  .ks-footer__legal > *{transform:none !important}
  .ks-footer__mark > *{animation-name:ks-fill !important;stroke-dashoffset:0 !important}
  .ks-tech__row:hover{border-radius:0 !important}
  .ks-why__frame img{transform:none !important}
  .ks-hero__pill{animation-name:ks-hero-dim !important}
}

/* Меньше прозрачности — материал становится плотным, размытие уходит. */
@media (prefers-reduced-transparency:reduce){
  .ks-nav{background:var(--ks-surface-page);backdrop-filter:none;-webkit-backdrop-filter:none;
    border-bottom-color:var(--ks-hairline)}
  .ks-nav::after{display:none}
}

/* Больше контраста — сплошной фон и явная граница. */
@media (prefers-contrast:more){
  .ks-nav{background:var(--ks-surface-page);backdrop-filter:none;-webkit-backdrop-filter:none;
    border-bottom-color:var(--ks-border-strong)}
  .ks-nav::after{display:none}
  .ks-field input,
  .ks-field textarea{border-bottom-color:#fff}
  .ks-extra-opt{border-color:var(--ks-border-strong)}
}

/* ── Мобильное меню ──────────────────────────────────────────────────
   Кнопка живёт в шапке всегда, но показывается только когда обычное
   меню спрятано. Панель — тот же материал, что и шапка. */
.ks-nav__burger{display:none;flex:none;width:44px;height:44px;margin-right:-10px;
  align-items:center;justify-content:center;flex-direction:column;gap:5px;
  background:none;border:0;padding:0;cursor:pointer;color:inherit}
.ks-nav__burger span{display:block;width:22px;height:1.5px;background:currentColor;
  transition:transform .3s var(--ks-ease),opacity .2s var(--ks-ease)}
.ks-nav__burger[aria-expanded="true"] span:first-child{transform:translateY(3.25px) rotate(45deg)}
.ks-nav__burger[aria-expanded="true"] span:last-child{transform:translateY(-3.25px) rotate(-45deg)}

/* Верх — по фактической высоте шапки (её меряет app.js): в токене
   --ks-nav-h лежит десктопная, на мобильном шапка ниже. */
.ks-menu{position:fixed;inset:var(--ks-nav-measured,var(--ks-nav-h)) 0 0;z-index:70;
  display:flex;flex-direction:column;justify-content:space-between;gap:32px;
  padding:32px 20px calc(32px + env(safe-area-inset-bottom));
  background:color-mix(in srgb,var(--ks-surface-page) 94%,transparent);
  backdrop-filter:blur(24px) saturate(180%);-webkit-backdrop-filter:blur(24px) saturate(180%);
  overflow-y:auto;overscroll-behavior:contain}
.ks-menu__links{display:flex;flex-direction:column;gap:4px}
.ks-menu__link{font-family:var(--ks-font-display);font-size:clamp(32px,9vw,44px);line-height:1.15;
  text-transform:uppercase;padding:6px 0;transition:color var(--ks-dur-fast) var(--ks-ease)}
.ks-menu__link:active{color:var(--ks-orange-cta)}
.ks-menu__foot{display:flex;flex-direction:column;align-items:flex-start;gap:8px}
.ks-menu__phone{font-family:var(--ks-font-body);font-weight:600;font-size:19px}
.ks-menu__mail{color:var(--ks-text-muted)}
.ks-menu__cta{width:100%;margin-top:16px}

/* Панель и её содержимое въезжают, а не появляются рывком */
.ks-menu[hidden]{display:none}
.ks-js .ks-menu{opacity:0;transition:opacity .25s var(--ks-ease)}
.ks-js .ks-menu.is-open{opacity:1}
.ks-menu.is-open .ks-menu__link,
.ks-menu.is-open .ks-menu__foot > *{animation:ks-rise-in .45s var(--ks-ease-out) backwards}
.ks-menu.is-open .ks-menu__link:nth-child(1){animation-delay:.05s}
.ks-menu.is-open .ks-menu__link:nth-child(2){animation-delay:.1s}
.ks-menu.is-open .ks-menu__link:nth-child(3){animation-delay:.15s}
.ks-menu.is-open .ks-menu__link:nth-child(4){animation-delay:.2s}
.ks-menu.is-open .ks-menu__link:nth-child(5){animation-delay:.25s}
.ks-menu.is-open .ks-menu__foot > *{animation-delay:.3s}
html.ks-menu-lock{overflow:hidden}

@media (min-width:861px){
  .ks-menu{display:none}
}

/* ── Факты: CSS выбирает вид до первой отрисовки ────────────────────
   Обе версии приходят с сервера. На узком экране полоса всегда имеет
   одну высоту, независимо от шрифтов и момента инициализации JS. */
.ks-facts__marquee{display:none;height:82px;overflow:hidden}
.ks-facts__track{display:flex;flex:none;width:max-content;
  animation:ks-facts-scroll var(--ks-marquee-dur,32s) linear infinite}
.ks-facts__set{display:flex;flex:none;align-items:baseline;gap:56px;padding:26px 28px}
.ks-facts__mitem{display:flex;align-items:baseline;gap:14px;white-space:nowrap;line-height:1}
.ks-facts__mitem .ks-num{font-size:30px;line-height:1}
.ks-facts__mdot{width:14px;height:14px;border-radius:50%;background:var(--ks-orange);flex:none;align-self:center}
@media(max-width:1200px){
  .ks-facts__row{display:none}
  .ks-facts__marquee{display:flex}
}
@keyframes ks-facts-scroll{to{transform:translateX(-50%)}}
/* Появление и доступность.

   Лента намеренно не прячется до прихода класса is-in, в отличие от
   остальных блоков: она включается на узких экранах, где секция часто
   оказывается в кадре ещё до того, как наблюдатель успевает её заметить,
   и тогда полоса оставалась невидимой совсем. Проявление сохраняем — но
   как украшение поверх видимого, а не как условие видимости. */
.ks-facts.is-in .ks-facts__marquee{animation:ks-fade-in .8s var(--ks-ease-out) backwards}
@media (prefers-reduced-motion:reduce){
  /* Общее правило выше обрезает любую анимацию до одного прохода за 0.2s —
     лента от этого дёргается и замирает в случайном месте. Останавливаем
     её честно и отдаём прокрутку пальцем. */
  .ks-facts__track{animation:none !important;transform:none !important}
  .ks-facts__marquee{overflow-x:auto}
}

/* ── Стыки одноцветных секций ────────────────────────────────────────
   Две светлые (или две тёмные) секции подряд складывали бы полные
   отступы в двойной. Каждой стороне стыка — половина: визуально между
   блоками один обычный отступ. Секции с другим фоном не задеваются. */
:is(section.ks-section, section.ks-booking):not(.ks-dark):has(+ :is(section.ks-section, section.ks-booking):not(.ks-dark)){
  padding-bottom:calc(var(--ks-section-y) / 2);
}
:is(section.ks-section, section.ks-booking):not(.ks-dark) + :is(section.ks-section, section.ks-booking):not(.ks-dark){
  padding-top:calc(var(--ks-section-y) / 2);
}
section.ks-dark.ks-section:has(+ section.ks-dark.ks-section){padding-bottom:calc(var(--ks-section-y) / 2)}
section.ks-dark.ks-section + section.ks-dark.ks-section{padding-top:calc(var(--ks-section-y) / 2)}

/* ═══════════════════════════════════════════════════════════════════
   Адаптив
   Брейкпоинты: 1280 — узкий десктоп, 1024 — планшет-ландшафт,
   860 — планшет-портрет (здесь меню уходит в бургер), 600 — телефон,
   380 — узкий телефон. Правила идут сверху вниз по секциям страницы.
   ═══════════════════════════════════════════════════════════════════ */

/* ── ≤1280: сужаем поля и крупную типографику ───────────────────────── */
@media (max-width:1280px){
  :root{--ks-section-y:88px;--ks-margin:40px}
  .ks-nav{padding:18px 32px}
  .ks-nav__links{gap:20px}
  .ks-footer{padding:72px 0 24px}
  .ks-tech__row{gap:36px;padding:32px 24px}
  .ks-tech__pic{width:150px;height:150px}
  .ks-included__grid{gap:40px}
  .ks-faq__inner{gap:48px}
  .ks-booking__inner{gap:32px}
}

/* ── ≤1024: планшет-ландшафт ─────────────────────────────────────────── */
@media (max-width:1024px){
  :root{--ks-section-y:72px;--ks-hero-max:720px}

  /* Полоса фактов: пять ячеек в ряд перестают читаться */
  .ks-facts__row{grid-template-columns:repeat(3,1fr)}
  .ks-facts__cell{padding:28px 20px 24px}
  .ks-facts__cell:nth-child(3n+1){padding-left:0}
  .ks-facts__cell:nth-child(-n+3)::after{content:none}

  /* Залы, форматы, работы, вопросы — в один столбец */
  #studios .ks-grid > *,
  #formats .ks-grid > *{grid-column:1 / -1 !important}
  /* В столбце фото зала всегда сверху: у чётных залов оно в DOM после текста */
  #studios .ks-grid > figure{order:-1}
  /* В колонке margin-top:auto схлопывается — линия цены липнет к списку */
  .ks-studio__foot{margin-top:28px}
  .ks-fmt__aside{margin-top:32px}
  .ks-faq__inner{grid-template-columns:1fr;gap:40px}
  .ks-faq__col{position:static}

  /* Бронирование: сводка уезжает под форму. min-width:0 обязателен —
     иначе колонка грида не сжимается уже своего содержимого (лента дней)
     и распирает секцию за край экрана. */
  .ks-booking__inner{grid-template-columns:minmax(0,1fr)}
  .ks-booking__inner > *,
  .ks-booking__form{min-width:0}
  /* Поля в 36px на телефоне забирали пятую часть ширины: сводка ужималась,
     и суммы начинали переноситься. Арка считается от того же значения. */
  .ks-booking__side{--ks-arc:clamp(120px,18vw,180px);--ks-pad:20px;
    border-radius:0 var(--ks-arc) 0 0}
  .ks-booking__card{position:static}
  .ks-booking__times{grid-template-columns:repeat(6,1fr)}

  .ks-included__grid{grid-template-columns:1fr;gap:32px}
  .ks-included__label{max-width:none}
  .ks-cnt__grid{grid-template-columns:repeat(2,1fr);gap:32px}
  .ks-cnt-map__grid{grid-template-columns:1fr;gap:32px}
  .ks-cnt-map__frame{min-height:360px}
  .ks-footer__grid{grid-template-columns:1fr 1fr;gap:32px}
  .ks-price__row{padding:32px 0}

  /* Капсулы «Включено» перестают помещаться по три в ряд */
  .ks-inc{gap:12px}
  .ks-inc__row{flex-wrap:wrap;gap:10px}
  .ks-inc__item{font-size:clamp(18px,2.6vw,26px)}
}

/* ── ≤860: меню в бургер, техника и цены перестраиваются ─────────────── */
@media (max-width:860px){
  :root{--ks-nav-h:55px;--ks-page-edge:16px;--ks-section-y:64px}

  .ks-nav{padding:8px 16px;gap:10px}
  /* Высоту шапки задаёт логотип, а не кнопки: пока знак 34px, поля
     ничего не решают. */
  .ks-nav .ks-logo svg{height:26px;width:auto;display:block}

  /* Бургер лежит вне .ks-nav__side, и space-between растягивал промежуток
     между ним и остальными кнопками до 36px против 6px внутри группы.
     Прижимаем группу вправо и раздаём одинаковый зазор вручную. */
  .ks-nav{justify-content:flex-start}
  .ks-nav__side{margin-left:auto;gap:6px}
  /* У иконок есть внутренние поля: у переключателя 9px, у бургера 11px.
     Между кнопкой и солнцем зазор съедает одно поле, между солнцем и
     полосками — сразу два, поэтому равный gap выглядит неравным. Разницу
     снимаем отрицательным отступом. */
  .ks-nav__burger{width:38px;height:38px;margin-right:-7px;margin-left:-9px}
  .ks-nav__burger span{width:20px}
  .ks-nav__links{display:none}
  .ks-nav__contacts{display:none}
  .ks-nav__burger{display:inline-flex}
  /* Стрелка в узкой шапке съедает место и переносит подпись кнопки. */
  .ks-nav__side .ks-btn .arr{display:none}
  /* Шапка на телефоне должна быть полоской, а не панелью: кнопка тут не
     главное действие — она дублирует крупную кнопку в первом экране. */
  .ks-nav__side .ks-btn{padding:10px 16px;min-height:38px;font-size:12px}
  .ks-nav__side{gap:6px}

  /* Техника: фото слева, категория и название справа от него,
     описание — ниже на всю ширину. body распускается в грид строки. */
  .ks-tech__row,
  .ks-tech__row--rev{display:grid;grid-template-columns:auto 1fr;column-gap:16px;row-gap:4px;
    align-items:center;padding:22px 0}
  .ks-tech__list{margin-top:32px}
  .ks-tech__pic{grid-row:1 / 3}
  .ks-tech__body{display:contents}
  .ks-tech__kind{grid-column:2;align-self:end}
  .ks-tech__name{grid-column:2;align-self:start;margin-top:2px}
  .ks-tech__text{grid-column:1 / -1;margin-top:10px}
  .ks-tech__row--rev .ks-tech__body,
  .ks-tech__row--rev .ks-tech__kind{text-align:left;justify-content:flex-start}
  .ks-tech__row--rev .ks-tech__text{margin-left:0}
  .ks-tech__pic{width:72px;height:72px}
  .ks-tech__name{font-size:clamp(26px,6.4vw,34px)}

  /* Цены: сумма под названием */
  .ks-price__row{grid-template-columns:1fr;gap:12px}
  .ks-price__sum{text-align:left}
  .ks-extra{grid-template-columns:1fr;gap:32px}

  .ks-facts__row{grid-template-columns:repeat(2,1fr)}
  .ks-facts__cell:nth-child(3n+1){padding-left:20px}
  .ks-facts__cell:nth-child(2n+1){padding-left:0}
  .ks-facts__cell:nth-child(-n+2)::after{content:none}

  /* Стрелки — под слайдером: верхняя пара прячется, показывается дубль
     после слайдов (Swiper цепляет обе пары одним селектором). */
  .ks-works__head{flex-direction:column;align-items:flex-start;gap:20px}
  .ks-works__head .ks-works__nav{display:none}
  .ks-works__nav--bottom{display:flex;gap:12px;margin-top:24px}
  /* Пропорции + min-height дают ширину больше экрана: на узких берём
     фиксированную высоту и полную ширину контейнера */
  .ks-cnt-map__frame{aspect-ratio:auto;height:min(56vh,340px);min-height:0;width:100%}

  /* Хиро: поля кадра и заголовок под узкий экран */
  /* Кадр на телефоне узкий: широкие поля съедали строку заголовка и
     заставляли её ломаться на лишние переносы. */
  .ks-hero__body{left:16px;right:16px;bottom:28px}
  .ks-hero__row{gap:24px}
  .ks-hero__title{font-size:clamp(34px,7.6vw,52px)}
  .ks-hero__kicker{font-size:11px;margin-bottom:16px}

  /* «Включено»: капсулы становятся списком в столбик — на тач-экране
     раскрытие по наведению всё равно недоступно, поэтому фон показываем
     сразу, а не прячем под ховер. */
  /* Капсулы обтекают, а не встают столбиком: «Студия» и «Свет» короткие,
     в строку их влезает по две, и блок перестаёт занимать целый экран.
     Высота — по содержимому: фиксированные 62px держали воздух, которого
     на телефоне нет. */
  /* Ряды из админки на телефоне только мешают: они рвут поток и оставляют
     хвосты пустого места в конце каждого. display:contents убирает саму
     коробку ряда, и капсулы обтекают одним потоком. */
  .ks-inc{flex-direction:row;flex-wrap:wrap;gap:8px}
  .ks-inc__row{display:contents}
  .ks-inc__item{flex:0 1 auto;width:auto;height:auto;min-height:0;
    justify-content:center;font-size:clamp(15px,3.9vw,20px);padding:11px 16px}

  /* Слой инверсии — копия строки, поэтому переносы ему нужны те же */
  /* Список форматов на телефоне — лента с прокруткой: семь строк подряд
     занимали целый экран, а кадр к ним приходилось искать глазами внизу.
     Прокрутка нативная, со «прилипанием» карточек — без слайдера на JS,
     который пришлось бы пересобирать при повороте экрана. */
  /* Листание берёт на себя Swiper (он уже есть в проекте и работает в
     «Работах»): своя прокрутка с прилипанием здесь только мешала бы ему. */
  .ks-fmt__list{overflow:visible}
  .ks-fmt__list > .swiper-wrapper{display:flex}
  .ks-fmt__list .ks-fmt__row.swiper-slide{width:78%;flex-shrink:0;margin-right:12px}
  /* Заливка активной строки рассчитана на список во всю ширину: в карточке
     она превращается в оранжевый прямоугольник под кадром. Оставляем метку
     номером и рамкой. */
  .ks-fmt__row::after{display:none}
  .ks-fmt__row.is-active .ks-fmt__thumb{outline:2px solid var(--ks-orange-cta);outline-offset:-2px}
  .ks-fmt__row.is-active .num{color:var(--ks-orange-cta)}

  .ks-fmt__row,
  .ks-fmt__inv{gap:14px;flex-wrap:wrap}
  /* nowrap обязателен: при вертикальном направлении перенос уводит
     элемент не на новую строку, а в новую колонку — подпись уезжала вбок
     за пределы карточки. */
  .ks-fmt__row{flex-direction:column;flex-wrap:nowrap;align-items:flex-start;
    gap:6px;padding:0 0 16px;border-bottom:0;height:auto}
  /* В строке подпись прижата вправо, в карточке она должна идти под
     названием — сбрасываем и отступ, и автоматическое поле. */
  .ks-fmt__row .meta{flex-basis:auto;margin-left:0;text-align:left}

  /* Кадр едет вместе со своим форматом. */
  .ks-fmt__thumb{display:block;width:100%;aspect-ratio:4/3;overflow:hidden;
    border-radius:var(--ks-radius-md,18px);background:var(--ks-surface-alt)}
  .ks-fmt__thumb img{width:100%;height:100%;object-fit:cover;display:block}

  /* Стек превью и подпись под ним больше не нужны: кадр уже в карточке. */
  .ks-fmt__aside{display:none}
  /* Инверсный слой рассчитан на строку во всю ширину и в карточке мешает. */
  .ks-fmt__inv{display:none}
}

/* ── ≤600: телефон ───────────────────────────────────────────────────── */
@media (max-width:600px){
  :root{--ks-section-y:44px;--ks-page-edge:12px}

  /* Компактный вертикальный ритм: крупные отступы десктопа на телефоне
     превращаются в пустые экраны. Инлайновые margin-top блоков
     перебиваются только с !important. */
  .ks-display-xl{font-size:clamp(32px,9.6vw,44px)}
  .ks-sec-head h2{margin-top:14px !important}
  .ks-grid[style*="margin-top"],
  .ks-price__list[style*="margin-top"]{margin-top:36px !important}
  .ks-tech__list{margin-top:36px}
  .ks-included__grid{gap:24px}
  .ks-extra{margin-top:32px;gap:24px}
  .ks-price__row{padding:24px 0}
  .ks-price__cta{margin-top:36px}
  .ks-cnt__grid{margin-top:40px}
  .ks-faq__inner{gap:28px}
  .ks-faq__q{padding:18px 0}
  .ks-works__note{margin-top:16px}
  .ks-footer{padding-top:48px}
  .ks-tech__row{padding:20px 0}
  .ks-booking__step{margin-top:28px;padding-top:24px}
  .ks-booking__step--first{margin-top:28px}
  /* Две капсулы формата в ряд: цена не должна ломаться на «₽ /» и «час». */
  .ks-chip.ks-chip--fmt{padding:14px 8px}
  .ks-chip--fmt .sub{font-size:10px}
  /* Свёрнутая группа: «Изменить» — рядом с заголовком, выбор — строкой ниже. */
  .ks-bgroup__edit{order:2}
  .ks-bgroup__val{order:3;flex-basis:100%;padding-left:40px}

  /* Строки форматов: мета в свою строку, но плотно */
  .ks-fmt__row,
  .ks-fmt__inv{gap:6px 10px}
  .ks-fmt__row{padding:0 0 14px}
  .ks-fmt__list .ks-fmt__row.swiper-slide{width:82%}
  .ks-fmt__row .name{font-size:clamp(24px,6.4vw,30px)}
  .ks-fmt__row .meta,
  .ks-fmt__inv .meta{font-size:10px}
  .ks-container{width:min(var(--ks-container),100% - 32px)}

  /* Хиро: кадр ниже, кнопки на всю ширину, круг мельче */
  :root{--ks-hero-h:clamp(460px,calc(100svh - var(--ks-nav-h) - 24px),620px)}
  .ks-hero__body{left:14px;right:14px;bottom:24px;padding:0}
  .ks-hero__title{font-size:clamp(32px,8.4vw,46px)}
  /* Заголовок и кнопки встают друг под другом на всю ширину кадра */
  .ks-hero__row{flex-direction:column;align-items:stretch;gap:20px}
  .ks-hero__actions{width:100%;flex-direction:column;align-items:stretch;gap:10px;padding-bottom:0}
  .ks-hero__actions > *{width:100%;justify-content:center}
  .ks-hero__pill{width:44px;height:88px;border-radius:88px 0 0 88px}

  .ks-facts__row{grid-template-columns:1fr}
  .ks-facts__cell{padding:20px 0}
  .ks-facts__cell::before{content:none !important}
  .ks-facts__cell:not(:first-child)::before{content:"" !important;left:0;right:0;top:0;bottom:auto;
    width:auto;height:1px;transform-origin:left center}

  .ks-cnt__grid{grid-template-columns:1fr;gap:24px}
  .ks-footer{padding:56px 0 20px}
  .ks-footer__grid{grid-template-columns:1fr;gap:28px}
  /* Копирайт и подпись остаются в строку: два коротких текста по краям
     читаются как подпись под футером, а столбиком выглядят как список. */
  .ks-footer__legal{flex-direction:row;align-items:center;gap:12px;font-size:11px}
  .ks-footer__legal > span,
  .ks-footer__author{white-space:nowrap}

  .ks-faq__q{font-size:17px;gap:20px}
  .ks-price__cta{flex-direction:column;align-items:stretch;gap:16px}
  .ks-price__cta .ks-btn{width:100%}
}

/* ── Тач-экраны: размеры целей и удобство брони ──────────────────────
   На указательном вводе высота 44px и крупный бегунок обязательны, иначе
   в капсулу времени и в ползунок трудно попасть пальцем. */
@media (max-width:860px) and (pointer:coarse), (max-width:600px){
  .ks-btn{padding:16px 24px;min-height:48px}
  .ks-btn--sm{padding:14px 20px;min-height:44px}
  .ks-nav__side .ks-btn--sm{padding:10px 16px;min-height:38px}
  .ks-btn--icon,.ks-btn--round{min-height:44px;min-width:44px}
  .ks-arrow-link{padding-block:10px}
  .ks-menu__link{padding-block:8px}
  /* В футере вертикальный отступ ссылки заменяет зазор колонки: сумма
     из gap и padding разносила пункты на 28px, и список расползался. */
  .ks-footer__col{gap:0}
  .ks-footer__col a,
  .ks-footer__col .ks-arrow-link{padding-block:7px}
  .ks-footer__col > .ks-label{margin-bottom:6px}

  /* Ползунок длительности */
  .ks-dur__range{height:44px;margin-top:10px}
  .ks-dur__range::-webkit-slider-thumb{width:28px;height:28px;margin-top:-13px}
  .ks-dur__range::-moz-range-thumb{width:28px;height:28px}
  .ks-dur__scale{font-size:10px;flex-wrap:wrap;gap:8px}
  .ks-dur{max-width:none}

  /* Дни — лентой с прилипанием, чтобы не ломать строку переносом */
  .ks-booking__days{flex-wrap:nowrap;overflow-x:auto;scroll-snap-type:x mandatory;
    padding-bottom:6px;margin-inline:calc(-1 * var(--ks-page-edge));
    padding-inline:var(--ks-page-edge);scrollbar-width:none}
  .ks-booking__days::-webkit-scrollbar{display:none}
  .ks-chip--day{scroll-snap-align:start;flex:none;width:60px;min-height:64px}

  .ks-booking__times{grid-template-columns:repeat(4,1fr);gap:8px}
  .ks-chip{min-height:48px}
  .ks-chip--time{font-size:14px}
  .ks-extra-opt{width:100%;min-height:52px}

  /* 16px — минимум, при котором iOS не приближает страницу на фокусе */
  .ks-field input,
  .ks-field textarea{font-size:16px;padding:12px 0}
  /* Крупнее для пальца — галочку сдвигаем следом за квадратом. */
  .ks-check input{width:22px;height:22px}
  .ks-check input::after{left:7px;top:3px;width:6px;height:11px}

  /* Никакой подсветки тапа поверх собственного отклика */
  a,button,label,input{-webkit-tap-highlight-color:transparent}
}

/* ── Движение на узких экранах ───────────────────────────────────────
   Те же анимации, но короче и с меньшей амплитудой: на телефоне блок
   занимает весь экран, и длинный вылет читается как задержка. */
@media (max-width:860px){
  :root{--ks-dur-slow:.7s}
  .ks-tech__list.is-in .ks-tech__row::before,
  .ks-price__list.is-in .ks-price__row::before,
  .ks-cnt__grid.is-in .ks-cnt__card::before{animation-duration:.5s}
  .ks-booking__form.is-in .ks-booking__step::before,
  .ks-booking__form.is-in .ks-booking__num,
  .ks-booking__form.is-in .ks-booking__label,
  .ks-booking__form.is-in .ks-booking__hint,
  .ks-booking__form.is-in .ks-booking__legend,
  .ks-booking__form.is-in .ks-booking__opts,
  .ks-booking__form.is-in .ks-dur,
  .ks-booking__form.is-in .ks-booking__days,
  .ks-booking__form.is-in .ks-booking__times,
  .ks-booking__form.is-in .ks-extra-opt{animation-delay:calc(.12s + var(--i,0) * .07s) !important}
  .ks-booking__side.is-in{animation-duration:.6s}
  .ks-footer__mark.is-in > *{animation-duration:.6s,.7s}
}

@keyframes ks-rise-in-sm{
  from{opacity:0;transform:translateY(12px)}
  to{opacity:1;transform:none}
}
@media (max-width:600px){
  /* Подъём короче: 22px на узком экране заметно «проваливает» блок */
  [data-reveal].is-in *{animation-name:ks-rise-in-sm}
  .ks-words .ks-reveal>*{animation-name:ks-hero-reveal}
  /* Правило выше меняет анимацию всем потомкам без разбора, а у ленты
     фактов своя — бесконечная прокрутка. Подменённая, она 23 секунды
     ползла на пару пикселей вверх вместо того, чтобы ехать вбок. */
  [data-reveal].is-in .ks-facts__track{animation-name:ks-facts-scroll}
  .ks-tech__list.is-in .ks-tech__row::before,
  .ks-price__list.is-in .ks-price__row::before,
  .ks-cnt__grid.is-in .ks-cnt__card::before,
  .ks-cnt-map__grid.is-in .ks-cnt-map__row::before,
  .ks-footer__legal.is-in::before{animation-name:ks-line-x}
  .ks-facts.is-in .ks-facts__cell::before{animation-name:ks-line-y}
  .ks-tech__list.is-in .ks-tech__pic{animation-name:ks-tech-pic}
  .ks-booking__card.is-in .ks-booking__pic,
  .ks-cnt-map__grid.is-in .ks-cnt-map__frame{animation-name:ks-reel-open}
  .ks-manifest.is-in .ks-mark__k,
  .ks-manifest.is-in .ks-mark__ring,
  .ks-manifest.is-in .ks-mark__q,
  .ks-footer__mark.is-in > *{animation-name:ks-draw,ks-fill}
  .ks-included__grid.is-in .ks-inc__item{animation-name:ks-inc-open}
  .ks-booking__form.is-in .ks-booking__num{animation-name:ks-book-dot}
}

/* ── Устройства без наведения ────────────────────────────────────────
   Ховер-слои (заливка ячейки, кадр в строке техники, фото в капсуле)
   строятся под мышь. На тач-экране они либо не показываются вовсе, либо
   залипают после тапа, поэтому убираем их из раскладки целиком. */
@media (hover:none), (max-width:860px){
  .ks-facts__cell::after,
  .ks-facts__inv{display:none}
  .ks-tech__row::after{display:none}
  .ks-tech__row{border-radius:0 !important}
  /* Капсула без фото: убираем и подложку, и белый текст, который был
     рассчитан на тёмный кадр под ним */
  .ks-inc__item::after,
  .ks-inc__item img{display:none}
  .ks-js .ks-included__grid:not(.is-in) .ks-inc__item{color:var(--ks-text-primary);border-color:var(--ks-hairline)}
  .ks-included__grid.is-in .ks-inc__item{animation:ks-inc-open .8s var(--ks-ease-out) var(--d,0s) both}
}

/* ── ≤380: узкий телефон ─────────────────────────────────────────────── */
@media (max-width:380px){
  .ks-container{width:min(var(--ks-container),100% - 24px)}
  .ks-tech__pic{width:64px;height:64px}
  .ks-nav{padding:8px 14px}
}


/* ── Реквизиты в подвале ─────────────────────────────────────────────
   Под логотипом, столбиком: юридические данные нужны, но внимание на себя
   тянуть не должны — отсюда приглушённый цвет и мелкий кегль. */
.ks-footer__req{display:flex;flex-direction:column;gap:4px;margin-top:24px;color:var(--ks-text-muted);max-width:26ch}
.ks-footer__req a{color:var(--ks-text-secondary);text-decoration:underline;
  text-underline-offset:3px;margin-top:6px;transition:color var(--ks-dur) var(--ks-ease)}
.ks-footer__req a:hover{color:#fff}

/* ── Блок «Реквизиты» на странице контактов ──────────────────────── */
.ks-req__grid{display:grid;grid-template-columns:1.2fr .8fr;gap:48px;margin-top:56px;align-items:start}
.ks-req__card{padding:32px;border-radius:var(--ks-radius-lg,28px);background:var(--ks-surface-alt)}
.ks-req__org{font-weight:500}
.ks-req__address{color:var(--ks-text-secondary);margin-top:8px}
.ks-req__list{margin-top:24px;display:grid;gap:10px}
.ks-req__row{display:flex;justify-content:space-between;gap:20px;align-items:baseline}
.ks-req__row dt{color:var(--ks-text-muted)}
.ks-req__row dd{margin:0;font-variant-numeric:tabular-nums;text-align:right}
.ks-req__director{margin-top:24px;color:var(--ks-text-muted)}
.ks-req__docshead{display:block;color:var(--ks-text-muted);margin-bottom:18px}
.ks-req__doc{display:flex;justify-content:space-between;gap:16px;padding:14px 0;
  border-bottom:1px solid var(--ks-hairline)}
.ks-req__note{margin-top:20px;color:var(--ks-text-muted)}

.ks-js .ks-req__grid:not(.is-in) > *{opacity:0}
.ks-req__grid.is-in > *{animation:ks-rise-in .6s var(--ks-ease-out) calc(var(--i,0) * .1s) backwards}

@media (max-width:860px){
  .ks-req__grid{grid-template-columns:1fr;gap:28px;margin-top:36px}
  .ks-req__card{padding:24px}
  .ks-req__row{flex-direction:column;gap:2px}
  .ks-req__row dd{text-align:left}
}

/* ── Юридические страницы ─────────────────────────────────────────────
   Документы длинные и читаются подряд, поэтому колонка узкая, а таблицы
   тарифов прокручиваются вбок, а не ломают страницу. */
.ks-doc{max-width:74ch}
.ks-doc h2{margin:48px 0 16px;font-size:clamp(22px,2.4vw,30px);line-height:1.2}
.ks-doc h3{margin:32px 0 12px;font-size:clamp(17px,1.6vw,20px)}
.ks-doc h2:first-child,.ks-doc h3:first-child{margin-top:0}
.ks-doc p{margin:0 0 14px;color:var(--ks-text-secondary);line-height:1.65}
.ks-doc strong{color:var(--ks-text-primary);font-weight:500}
.ks-doc ul{margin:0 0 18px;padding-left:22px;color:var(--ks-text-secondary);line-height:1.65}
.ks-doc li{margin-bottom:8px}
.ks-doc a{color:var(--ks-orange);text-decoration:underline;text-underline-offset:3px}
.ks-doc__table{margin:24px 0 28px;overflow-x:auto}
.ks-doc__table table{border-collapse:collapse;width:100%;min-width:420px;font-size:15px}
.ks-doc__table th,.ks-doc__table td{padding:12px 16px;text-align:left;vertical-align:top;
  border-bottom:1px solid var(--ks-hairline)}
.ks-doc__table th{color:var(--ks-text-muted);font-weight:500;white-space:nowrap}
.ks-doc__lead{color:var(--ks-text-muted);margin-bottom:32px}

/* Оглавление раздела «Документы» */
.ks-doclist{display:grid;gap:2px;margin-top:40px}
.ks-doclist a{display:flex;justify-content:space-between;gap:20px;padding:22px 0;
  border-bottom:1px solid var(--ks-hairline);font-size:clamp(18px,2vw,24px)}
.ks-doclist a .arr{color:var(--ks-orange)}

/* ── Переключение недель в брони ─────────────────────────────────────
   Ряд дней показывает неделю, стрелки листают вперёд до конца горизонта
   брони. Подпись слева говорит, какая это неделя, — иначе на третьем
   листании непонятно, где ты. */
.ks-booking__weeknav{display:flex;align-items:center;justify-content:space-between;
  gap:16px;margin-top:18px;color:var(--ks-text-secondary)}
.ks-booking__weekbtns{display:flex;gap:8px}
/* Те же кнопки, что у слайдера работ: фон задаём явно — .ks-btn--icon его
   не определяет, и браузер подставляет собственную серую заливку. */
.ks-booking__weeknav .ks-btn--icon{width:38px;height:38px;background:transparent;
  border:1px solid var(--ks-border-strong);color:inherit}
.ks-booking__weeknav .ks-btn--icon:hover:not(:disabled){background:var(--ks-orange-cta);
  border-color:var(--ks-orange-cta);color:#fff}
.ks-booking__weeknav .ks-btn--icon:hover:not(:disabled) .arr{transform:none}
.ks-booking__weeknav .ks-btn--icon:disabled{opacity:.35;border-color:var(--ks-hairline);
  cursor:default;pointer-events:none}

/* Попап заявки из блока с ценами. */
.ks-request-modal{position:fixed;inset:0;z-index:100;display:grid;place-items:center;padding:24px;
  opacity:0;visibility:hidden;transition:opacity .28s ease,visibility .28s ease}
.ks-request-modal.is-open{opacity:1;visibility:visible}
.ks-request-modal__backdrop{position:absolute;inset:0;width:100%;height:100%;border:0;
  background:rgba(13,13,13,.68);backdrop-filter:blur(5px);cursor:pointer;opacity:0;transition:opacity .28s ease}
.ks-request-modal.is-open .ks-request-modal__backdrop{opacity:1}
.ks-request-modal__panel{position:relative;z-index:1;width:min(100%,560px);max-height:min(90vh,820px);overflow:auto;
  padding:clamp(24px,5vw,48px);background:var(--ks-surface-page);color:var(--ks-text-primary);
  box-shadow:0 24px 90px rgba(0,0,0,.28);opacity:0;transform:translateY(14px) scale(.985);
  transition:opacity .28s ease,transform .28s cubic-bezier(.2,.75,.25,1)}
.ks-request-modal.is-open .ks-request-modal__panel{opacity:1;transform:translateY(0) scale(1)}
.ks-request-modal__panel:focus{outline:none}
.ks-request-modal__close{position:absolute;top:18px;right:18px;width:40px;height:40px;border:1px solid var(--ks-hairline);
  border-radius:50%;background:transparent;color:inherit;font-size:28px;line-height:1;cursor:pointer}
.ks-request-modal__close:hover{border-color:var(--ks-text-primary)}
.ks-request-modal__title{margin-top:12px;padding-right:36px}
.ks-request-modal__service{margin-top:10px;color:var(--ks-text-secondary)}
.ks-request-modal__panel .ks-field > .ks-label{color:var(--ks-text-secondary)}
.ks-request-modal__panel .ks-field input,
.ks-request-modal__panel .ks-field textarea{border-bottom-color:var(--ks-border-strong);color:var(--ks-text-primary)}
.ks-request-modal__panel .ks-field input:focus,
.ks-request-modal__panel .ks-field textarea:focus{border-bottom-color:var(--ks-border-strong)}
.ks-request-modal__panel .ks-field.is-bad input,
.ks-request-modal__panel .ks-field.is-bad textarea{border-bottom-color:#c7352b}
.ks-request-modal__panel .ks-field.is-bad > .ks-label{color:#c7352b}
.ks-request-modal__panel .ks-check{color:var(--ks-text-secondary)}
.ks-request-modal__panel .ks-check input{background:#fff;border-color:var(--ks-border-strong)}
.ks-request-modal__panel .ks-check input:checked{background:var(--ks-orange-cta);border-color:var(--ks-orange-cta)}
.ks-request-modal__panel .ks-check input::after{border-color:#fff}
.ks-request-modal__panel .ks-check.is-bad input{box-shadow:0 0 0 2px rgba(199,53,43,.35)}
.ks-request-modal__panel .ks-check.is-bad span{color:#c7352b}
.ks-request-modal__panel .ks-check a{color:var(--ks-text-primary)}
.ks-request-modal__message{margin-top:14px;color:#b52e25}
.ks-request-modal__submit{width:100%;margin-top:22px}
.ks-request-modal__success .ks-small{margin-top:10px}
.ks-request-modal__success .ks-btn{margin-top:24px}
.ks-request-modal__trap{position:absolute!important;left:-9999px!important;width:1px!important;height:1px!important;opacity:0!important}
body.ks-request-modal-open{overflow:hidden}
@media(max-width:600px){
  .ks-request-modal{padding:12px}
  .ks-request-modal__panel{max-height:94vh;padding:28px 22px}
}
@media(prefers-reduced-motion:reduce){
  .ks-request-modal,.ks-request-modal__backdrop,.ks-request-modal__panel{transition-duration:.01ms}
}
