@charset "utf-8";

/* ══════════════════════════════════════════════════════════════════
   «ВСЕ 32» — ДВИЖЕНИЕ

   Подключается ПОСЛЕ adaptive.css: движение — верхний слой, он должен
   перебивать переходы, объявленные ниже по каскаду. Кегли, зоны нажатия
   и безопасные отступы из adaptive.css здесь не трогаются ни одним
   селектором — только transform, opacity, тени и цвет.

   ── Три правила, которым подчинено всё в этом файле ──────────────
   1. Анимируются ТОЛЬКО transform и opacity. Ни width/height, ни
      margin/padding, ни gap: пересчёт раскладки на каждом кадре роняет
      частоту и дёргает текст под курсором. Единственное исключение по
      всему проекту — grid-template-rows у панелей аккордеона: это
      чужой файл (parts/services.css), там осознанный компромисс,
      потому что height:auto не анимируется в принципе.
   2. Кривые — только ease-out (все контрольные точки y ≤ 1). Никаких
      пружин и отскоков: клиника, а не игра. Выход быстрее входа —
      исчезающее не должно задерживать взгляд.
   3. Всё, что здесь есть, включается классом .m на <html>, а его
      ставит motion.js. Нет скрипта — нет ни одного правила отсюда,
      страница остаётся ровно такой, какой была. Поэтому ни одно
      правило этого файла не может спрятать контент навсегда.

   Состояния, которые ставит motion.js:
     html.m         — скрипт жив, движение разрешено
     html.m-fab     — контроллер липкой кнопки звонка запущен
     html.m-call    — кнопку звонка пора показать
     .site-head.is-stuck — страница тронулась с места
     .site-head.is-away  — шапка убрана (прокрутка вниз)
   ══════════════════════════════════════════════════════════════════ */

:root{
  /* ease-out без отскока: быстрый старт, мягкая остановка */
  --m-out: cubic-bezier(.16,.84,.44,1);
  --m-press: 90ms;   /* отклик на нажатие — быстрее реакции глаза */
  --m-fast: .17s;    /* наведение */
  --m-mid: .3s;      /* смена состояния */
  --m-slow: .55s;    /* появление */
}

/* ══════════════════════════════════════════════════════════════════
   1. ЛИПКАЯ ШАПКА
   Пока страница не тронулась — шапка не отличается от фона первого
   экрана. Стоило прокрутить — под ней появляется тень: это единственный
   способ показать, что она лежит НАД содержимым, а не в потоке.
   При долгой прокрутке вниз шапка уезжает (transform, не height —
   раскладка не пересчитывается и текст не прыгает), при первом же
   движении вверх возвращается. Условия, при которых она не уезжает
   никогда, перечислены в motion.js.
   ══════════════════════════════════════════════════════════════════ */
html.m .site-head{
  transition:transform var(--m-mid) var(--m-out),
             box-shadow var(--m-fast) linear,
             background-color var(--m-fast) linear;
}
html.m .site-head.is-stuck{
  background:rgba(255,255,255,.95);
  box-shadow:0 10px 30px -18px rgba(42,40,51,.55);
}
html.m .site-head.is-away{transform:translateY(-100%)}

/* Полоса прочитанного. Чистая заливка мятой — это графика, а не текст,
   требование 4.5:1 к ней не относится. Элемент создаёт motion.js и
   держит его на <body>: фиксированная полоса переживает и уехавшую
   шапку, и режим position:static в альбомной ориентации. */
html.m .m-prog{
  position:fixed;left:0;top:0;z-index:300;
  width:100%;height:2px;
  background:var(--mint);
  transform:scaleX(0);transform-origin:0 50%;
  will-change:transform;           /* единственное место: полоса едет постоянно */
  pointer-events:none;
}

/* Мобильное меню приезжает сверху. Только смещение, без прозрачности:
   показ идёт атрибутом hidden, то есть это @keyframes, а у остановленной
   в фоновой вкладке анимации кадр from продолжает применяться. С
   opacity:0 меню могло бы остаться невидимым — со смещением худший
   случай это «меню на 10px выше». Тот же приём, что в parts/faq.css. */
@keyframes m-drop{from{transform:translateY(-10px)}to{transform:none}}
html.m .mobnav:not([hidden]){animation:m-drop var(--m-mid) var(--m-out)}

/* ══════════════════════════════════════════════════════════════════
   2. ПЕРВЫЙ ЭКРАН
   Базовое появление (.reveal → .is-in) объявлено в style.css и
   управляется наблюдателем из app.js. Здесь оно только удлиняется и
   разводится по времени: строчка адреса, заголовок, лид, кнопки,
   цифры — в том порядке, в каком их читают. Ниже первого экрана
   ничего не меняется: там появление должно быть незаметным.
   ══════════════════════════════════════════════════════════════════ */
html.m .hero .reveal{transform:translateY(22px)}
html.m .hero .reveal.is-in{
  transform:none;
  transition:opacity var(--m-slow) var(--m-out),transform var(--m-slow) var(--m-out);
}
html.m .hero .reveal[data-delay="1"].is-in{transition-delay:.08s}
html.m .hero .reveal[data-delay="2"].is-in{transition-delay:.16s}
html.m .hero .reveal[data-delay="3"].is-in{transition-delay:.24s}
html.m .hero .reveal[data-delay="4"].is-in{transition-delay:.32s}

/* Кадр в герое «оседает»: приходит чуть крупнее и садится на место.
   overflow:hidden — чтобы увеличенная картинка не вылезала за скруглённый
   угол; свойство статическое, не анимируется. Худший случай (наблюдатель
   не сработал и .is-in не пришёл) — кадр остаётся на 4% крупнее, кадрировка
   object-fit это переживает, содержимое не теряется. */
html.m .hero-shot{overflow:hidden;border-radius:var(--r-lg)}
html.m .hero-shot img{transform:scale(1.04);transition:transform 1.1s var(--m-out)}
html.m .hero-shot.is-in img{transform:none}

/* Бегущей строке направлений появление НЕ добавлено, и это не забывчивость.
   Она единственная в герое не помечена .reveal, то есть видна с первого
   кадра. Скрыть её можно только тем же классом .m, а он приходит вместе
   со скриптом — уже после первой отрисовки. Строка успела бы мигнуть и
   пропасть. У остальных элементов герой такой проблемы нет: opacity:0 им
   ставит .js из style.css в том же пересчёте стилей, что и .m. */

/* ══════════════════════════════════════════════════════════════════
   3. РАСКРЫТИЕ АККОРДЕОНА УСЛУГ
   Панель раскрывается своим переходом (grid-template-rows, 0.42s,
   parts/services.css). Пустая раскрывающаяся коробка выглядит как
   ошибка отрисовки, поэтому содержимое въезжает следом лесенкой:
   текст → фотографии → перечень позиций → примечание → кнопка.
   Задержки только на ВХОДЕ. Закрытие — 0.17s без задержек: убирать
   надо быстрее, чем показывать, иначе закрытие ощущается как затуп.

   Переходы, а не @keyframes, и это принципиально. У анимации есть
   кадр from, который продолжает применяться, пока таймлайн стоит
   (фоновая вкладка): содержимое зависло бы прозрачным. У перехода
   такого кадра нет — если он не запустился, элемент просто сразу
   в конечном состоянии, то есть видим.

   Скрытое состояние привязано к [data-ready] — этот атрибут ставит
   services.js. Без скрипта аккордеон раскрыт весь, и ни одно правило
   отсюда до него не дотягивается.
   ══════════════════════════════════════════════════════════════════ */
html.m .svc-acc[data-ready] .svc-acc__body>*{
  opacity:0;transform:translateY(10px);
  transition:opacity .17s linear,transform .17s var(--m-out);
}
html.m .svc-acc[data-ready] .svc-acc__item.is-open .svc-acc__body>*{
  opacity:1;transform:none;
  transition:opacity .42s var(--m-out),transform .42s var(--m-out);
}
html.m .svc-acc[data-ready] .svc-acc__item.is-open .svc-acc__body>:nth-child(1){transition-delay:.07s}
html.m .svc-acc[data-ready] .svc-acc__item.is-open .svc-acc__body>:nth-child(2){transition-delay:.12s}
html.m .svc-acc[data-ready] .svc-acc__item.is-open .svc-acc__body>:nth-child(3){transition-delay:.17s}
html.m .svc-acc[data-ready] .svc-acc__item.is-open .svc-acc__body>:nth-child(4){transition-delay:.21s}
html.m .svc-acc[data-ready] .svc-acc__item.is-open .svc-acc__body>:nth-child(5){transition-delay:.25s}
/* первые два кадра после старта скрипта — без переходов, как и всё
   остальное в аккордеоне (см. [data-boot] в parts/services.css) */
html.m .svc-acc[data-boot] .svc-acc__body>*{transition:none !important}

/* Вложенный спойлер «Показать все позиции» — то же правило, короче. */
html.m .svc-acc[data-ready] .svc-acc__rest-inner>*{
  opacity:0;transition:opacity .15s linear;
}
html.m .svc-acc[data-ready] .svc-acc__rest.is-open .svc-acc__rest-inner>*{
  opacity:1;transition:opacity .34s var(--m-out) .1s;
}
html.m .svc-acc[data-boot] .svc-acc__rest-inner>*{transition:none !important}

/* ══════════════════════════════════════════════════════════════════
   4. НАВЕДЕНИЕ
   Только там, где есть настоящий курсор: (hover:hover) и (pointer:fine).
   На пальце :hover залипает после нажатия — карточка так и осталась бы
   приподнятой. Тач получает отклик на нажатие, он ниже, вне медиазапроса.
   ══════════════════════════════════════════════════════════════════ */
@media (hover:hover) and (pointer:fine){

  /* ── строки аккордеона: строка отзывается на курсор ── */
  html.m .svc-acc__text{transition:transform var(--m-fast) var(--m-out)}
  html.m .svc-acc__item:not(.is-open) .svc-acc__trigger:hover .svc-acc__text{
    transform:translateX(3px)}

  /* ── карточки предложений ── */
  html.m .offer{
    transition:transform var(--m-mid) var(--m-out),
               box-shadow var(--m-mid) var(--m-out),
               border-color var(--m-fast) linear,
               background-color var(--m-fast) linear;
  }
  html.m .offer:hover{transform:translateY(-5px)}
  /* тёмная плоскость: тень не читается, поэтому отзывается рамка */
  html.m .offer:not(.offer-hero):hover{
    background:rgba(255,255,255,.11);border-color:rgba(255,255,255,.36)}
  /* белая карточка на тёмном: мягкое мятное свечение вместо серой тени */
  html.m .offer-hero:hover{box-shadow:0 22px 46px -24px rgba(92,231,189,.75)}
  /* обрезка кадра уже стоит на .offer-media в style.css — здесь только ход */
  html.m .offer-media img{transition:transform .7s var(--m-out)}
  html.m .offer:hover .offer-media img{transform:scale(1.045)}

  /* ── карточки отзывов: тише, они не кликабельны ── */
  html.m .q{
    transition:transform var(--m-mid) var(--m-out),
               border-color var(--m-fast) linear;
  }
  html.m .q:hover{transform:translateY(-3px)}
  html.m .q:not(.q-big):hover{border-color:var(--ink-2)}

  /* ── карта: превью отзывается так же, как кадры клиники ── */
  html.m .map-load img{transition:transform var(--m-slow) var(--m-out)}
  html.m .map-load:hover img{transform:scale(1.03)}

  /* ── кнопки ── */
  html.m .btn{
    transition:background-color var(--m-fast) linear,color var(--m-fast) linear,
               border-color var(--m-fast) linear,
               transform var(--m-fast) var(--m-out),
               box-shadow var(--m-fast) var(--m-out);
  }
  html.m .btn-solid:hover{transform:translateY(-2px);
    box-shadow:0 12px 24px -14px rgba(42,40,51,.65)}
  html.m .btn-quiet:hover{transform:translateY(-2px)}

  /* ── ссылка со стрелкой ────────────────────────────────────────
     В style.css на наведении растёт gap. gap — свойство раскладки:
     каждый кадр пересчитывает строку и толкает соседей. Здесь оно
     зафиксировано, а едет сама стрелка — тот же смысл, но transform. */
  html.m .link-arrow,
  html.m .link-arrow:hover{gap:.5em;transition:color var(--m-fast) linear}
  html.m .link-arrow span{transition:transform var(--m-mid) var(--m-out)}
  html.m .link-arrow:hover span{transform:translateX(5px)}

  /* ── вопросы ── */
  html.m .faq-q{transition:transform var(--m-fast) var(--m-out)}
  html.m .faq-board.is-live .faq-item:not(.is-open) .faq-btn:hover .faq-q{
    transform:translateX(3px)}
}

/* ══════════════════════════════════════════════════════════════════
   5. НАЖАТИЕ
   Работает и на курсоре, и на пальце: единственный отклик, который
   тач получает вместо наведения. 90 мс — быстрее, чем человек успевает
   отпустить палец, поэтому переход не может задержать переход по ссылке.
   ══════════════════════════════════════════════════════════════════ */
html.m .btn:active{transform:translateY(1px) scale(.985);
  transition-duration:var(--m-press)}
html.m .svc-acc__trigger:active,
html.m .faq-board.is-live .faq-btn:active,
html.m .svc-acc__more:active,
html.m .map-load:active{transform:scale(.995);
  transition:transform var(--m-press) var(--m-out)}

/* ══════════════════════════════════════════════════════════════════
   6. ЛИПКАЯ КНОПКА ЗВОНКА (телефоны)
   Кнопка уходит вниз, когда пользователь дошёл до формы записи: там она
   ложится ровно на нижние поля и на «Отправить», а телефон в этот момент
   и так стоит в тексте рядом с формой («Или позвоните сами»). Это и есть
   «не мешать клику» — движение здесь убирает препятствие.

   Порога по прокрутке НЕТ намеренно: с первого экрана кнопка видна.
   На телефоне номер из шапки убран в бургер, и это единственный способ
   позвонить в один тап — прятать его ради эффектного выезда нельзя.

   Спрятанное состояние привязано к html.m-fab, а этот класс motion.js
   ставит ТОЛЬКО после того, как подписался на прокрутку и посчитал
   первое состояние. Сломается скрипт на полпути — класса не будет,
   кнопка останется на месте. Главное действие страницы не может
   пропасть из-за анимации.
   ══════════════════════════════════════════════════════════════════ */
html.m-fab .callnow{
  transform:translateY(calc(100% + 24px));
  transition:transform var(--m-mid) var(--m-out);
}
html.m-fab.m-call .callnow{transform:none}
/* отклик на нажатие — ПОСЛЕ показа, иначе transform:none из строки выше
   перебьёт его при равной специфичности */
html.m .callnow:active,
html.m-fab.m-call .callnow:active{transform:scale(.985);
  transition:transform var(--m-press) var(--m-out)}
/* В альбомной ориентации adaptive.css вынимает кнопку из фиксированного
   слоя (position:static) — сдвигать её нельзя, останется дыра в потоке. */
@media (max-height:520px) and (orientation:landscape){
  html.m-fab .callnow,
  html.m-fab.m-call .callnow{transform:none}
}

/* ══════════════════════════════════════════════════════════════════
   7. СОКРАЩЕНИЕ ДВИЖЕНИЯ
   В style.css уже стоит общий глушитель длительностей. Он гасит
   переходы, но не отменяет состояний: сдвинутая шапка так и осталась бы
   сдвинутой, кадр в герое — увеличенным. Здесь снимаются сами состояния,
   поимённо. Скопом transform:none ставить нельзя: на странице есть
   статические повороты (гранёные точки у имён врачей, метка на карте,
   угол карточки ответа) — их сбрасывать нечего и незачем.

   Плюс дублирующая защита в motion.js: при включённом «сокращении
   движения» скрипт не создаёт полосу прокрутки, не убирает шапку
   и не трогает кнопку звонка вовсе.
   ══════════════════════════════════════════════════════════════════ */
@media (prefers-reduced-motion:reduce){
  html.m .site-head,
  html.m .site-head.is-away{transform:none !important}
  html.m .m-prog{display:none !important}

  html.m .hero .reveal,
  html.m .hero .reveal.is-in{transform:none !important;opacity:1 !important}
  html.m .hero-shot img{transform:none !important}
  html.m .hero .ticker{opacity:1 !important;transition:none !important}

  html.m .svc-acc[data-ready] .svc-acc__body>*,
  html.m .svc-acc[data-ready] .svc-acc__rest-inner>*{
    opacity:1 !important;transform:none !important;transition:none !important}

  html.m .offer:hover,
  html.m .q:hover,
  html.m .btn:hover,
  html.m .btn:active,
  html.m .offer:hover .offer-media img,
  html.m .map-load:hover img,
  html.m .link-arrow:hover span,
  html.m .svc-acc__trigger:hover .svc-acc__text,
  html.m .faq-btn:hover .faq-q,
  html.m .svc-acc__trigger:active,
  html.m .faq-board.is-live .faq-btn:active,
  html.m .svc-acc__more:active,
  html.m .map-load:active,
  html.m .callnow:active{transform:none !important}

  html.m-fab .callnow,
  html.m-fab.m-call .callnow{transform:none !important}
  html.m .mobnav:not([hidden]){animation:none !important}
}
