  /* ===== Slide 3 — Что это стоит гостинице? (Cost, Figma node 2829:4460) =====
     Позиции объектов заданы в JS (js/slide3-cost.js, ORBS) — единый источник
     правды, т.к. одни и те же числа нужны и для inline-стилей .cost-orb, и
     для радиуса реакции на курсор. Здесь только типографика/структурные
     правила. Z-стек (снизу вверх): heading → orbs → texts.
     Фон слайда — сплошной чёрный (см. правило #slide-3 ниже); никакой
     процедурной водной анимации (волна, пузырьки, размытие) здесь больше
     нет. Иконки — rubl-broken/clock-broken/bubble-broken.png (Figma-узлы
     2891:2637/2891:2612/2891:2635, экспортированы вручную — сетевая
     песочница блокирует прямые загрузки с figma.com), заменили прежние
     cost-ruble/cost-clock/cost-chat-bubbles.png. Позиции/размеры иконок
     (250×250) и текста под ними — 1:1 из get_design_context по трём
     "normal"-группам (узлы 2891:2643/2891:2639/2891:2644). */
  /* background:var(--yellow) убран (стоял тут отдельно от слайдов 1/2, у
     которых своего фона нет вообще) — он полностью перекрывал .color-back
     (общий переезжающий слой, #phone-shared, z-index:1 — ниже #slide-track
     z-index:2), хотя .color-back уже позиционировался на слайд 3 (см.
     SLIDE3_COLORBACK в js/phone-shared.js). Без явного фона тут видно то,
     что ниже по z (жёлтый фон #stage/.color-back) — ровно как на слайдах
     1/2, ничего не сломано, т.к. слайды не перекрываются между собой по
     экранным координатам (просто разные top в треке). */
  /* Собственный статичный чёрный фон тут убран: дублировал уже существующий
     твин .stage-wrap/#stage (STAGE_BLEED_COST/STAGE_BLEED_DEFAULT, см.
     js/phone-shared.js setPhoneSlide3/exitPhoneSlide3), который и так красит
     ВЕСЬ экран (включая внутреннюю 1440px-область) в тот же чёрный, но
     перекрывал её МГНОВЕННО, пока bleed ехал коротким COLOR_DUR_RATIO
     (~0.3с из 0.9с скролла) — на стыке слайдов 2/3 получался заметный шов.
     Bleed-твину заодно удлинили длительность до полных 0.9с (см.
     js/phone-shared.js), иначе шов остался бы виден и без статичной
     подстраховки. .color-back (цветное пятно) раньше был физически
     спозиционирован на Cost (SLIDE3_COLORBACK), но не виден — перекрывался
     ИМЕННО этим статичным фоном; без него он всплыл бы поверх чёрного
     bleed'а, поэтому теперь явно гасим его (opacity) на входе в Cost и
     возвращаем на выходе — см. setPhoneSlide3/exitPhoneSlide3 в
     js/phone-shared.js. */
  #slide-3{ top:1520px; overflow:hidden; }

  /* Поисковая строка — теперь ТРЕТИЙ общий переезжающий элемент наравне с
     .phone-frame/.color-back (см. #searchbar-layer). БАЗОВОЕ CSS-
     состояние здесь — свёрнутый чёрный кружок на слайде 2 (Figma node
     2877:2529 "Question", узел 2816:2105/"Home (desktop)"): left:300/
     top:119.7 = центр .phone-frame по горизонтали (158+300/2) и +20px от её
     верхнего края (99.7+20) — числа сошлись 1:1 с Figma, см. память проекта.
     16×16 — сам кружок. Slide 1 (та же DELTA[1], что и у рамки) и slide 3
     (1092×94 у Figma node 2866:2243, старые left:181/top:147) достигаются
     JS-морфом (js/phone-shared.js setPhone / js/slide3-cost.js
     setSearchbarSlide3-exitSearchbarSlide3) — та же схема, что у .phone-
     frame между BASE_PHONE/SLIDE3_PHONE. perspective — для 3D-флипа между
     front (cost, белый) и back (solution, чёрный) на слайде 3, обязана жить
     на РОДИТЕЛЕ вращаемого элемента (.cost-searchbar-flip), не на нём самом
     — иначе 3D-эффекта не будет, rotateX отрисуется плоско.
     overflow:hidden — иконка/текст (.cost-searchbar__icon/__text) внутри
     спозиционированы АБСОЛЮТНО на финальных координатах, а контейнер растёт
     из кружка 16×16 в пилюлю 1092×94 весь 0.9с перехода (см.
     setSearchbarSlide3 в js/slide3-cost.js) — без обрезки они на миг видны
     за пределами ещё маленького контейнера, поверх предыдущего слайда.
     overflow:hidden физически обрезает иконку/текст/каретку, пока контейнер
     маленький, и естественно проявляет их по мере роста — работает вместе с
     таймингом в JS (задержка показа для повторных заходов, см.
     resetSearchBar в js/slide3-cost.js) как двойная подстраховка. */
  .cost-searchbar-outer{
    position:absolute; left:300px; top:119.7px; width:16px; height:16px;
    pointer-events:none; perspective:2400px; overflow:hidden;
  }
  .cost-searchbar-flip{ position:absolute; inset:0; transform-style:preserve-3d; }
  .cost-searchbar-face{
    position:absolute; inset:0; border-radius:80px;
    backface-visibility:hidden; -webkit-backface-visibility:hidden;
  }
  /* back повёрнута на 180deg ЗАРАНЕЕ (статично в CSS) — когда .cost-
     searchbar-flip анимированно доезжает до rotationX:180, суммарный
     поворот back = 180+180=360=0 — лицом к зрителю, не вверх ногами.
     Стандартный приём для card-flip. */
  .cost-searchbar-face--back{
    background:var(--ink); transform:rotateX(180deg);
    display:flex; align-items:center; justify-content:center;
  }

  /* ---- front-face (cost, белый) — id/классы старые ---- */
  /* background — БАЗОВО чёрный (var(--ink)), т.к. .cost-searchbar-outer
     теперь по умолчанию в свёрнутом (слайд 2) состоянии — перекрашивается в
     белый JS-твином (background-color) синхронно с ростом контейнера в
     setSearchbarSlide3 (js/slide3-cost.js), назад в чёрный в
     exitSearchbarSlide3. border-radius:80px не меняется вообще: на боксе
     16×16 браузер сам клэмпит его до круга, на 1092×94 — даёт пилюлю, одно
     и то же значение работает для обеих форм. scaleX-разворот (раньше был
     тут) убран — рост теперь физический (left/top/width/height контейнера),
     а не разворот плоской заливки внутри уже готового бокса. */
  .cost-searchbar__pill{
    position:absolute; inset:0; background:var(--ink); border-radius:80px;
  }
  .cost-searchbar__icon{ position:absolute; left:35px; top:24px; width:51px; height:51px; opacity:0; }
  .cost-searchbar__icon svg{ display:block; width:100%; height:100%; }
  .cost-searchbar__text{
    position:absolute; inset:0; margin:0; display:flex; align-items:center; justify-content:center;
    font-family:'Unbounded',sans-serif; font-weight:500; font-size:52px;
    line-height:0.9; color:var(--ink); white-space:nowrap; opacity:0;
  }
  /* Мигающий курсор ввода — для полной аналогии с настоящим полем поиска.
     Мигает непрерывно (и во время печати тоже — не стали усложнять
     переключением solid/blink, разница почти незаметна). */
  /* flex-shrink:0 — .cost-searchbar__text это flex-контейнер, а с 2026-08-07
     текст заголовка лежит в разметке с самого начала (см. комментарий в
     index.html). В свёрнутом состоянии контейнер шириной 16px, строка в него
     не влезает, и курсор как flex-элемент ужимался до нулевой ширины. Видно
     этого не было (всё под opacity:0 и overflow:hidden), но полагаться на
     такую случайность незачем. */
  .cost-searchbar__caret{
    display:inline-block; flex-shrink:0; width:4px; height:0.85em; margin-left:4px;
    vertical-align:-0.08em; background:var(--ink);
    animation:cost-caret-blink 1s step-end infinite;
  }
  @keyframes cost-caret-blink{ 50%{ opacity:0; } }
  /* Пункт 1 утверждённого списка («движется само»): в спокойном режиме курсор
     не мигает. Мигание — CSS-анимация, JS её не контролирует, поэтому гасим
     классом на <html> (его ставит js/reduced-motion.js). */
  html.is-calm .cost-searchbar__caret{ animation:none; }

  /* ---- back-face (solution, чёрный) — статичный, БЕЗ печати по буквам:
     текст уже готов ("уже забитым туда текстом" — явное требование
     пользователя), просто физически скрыт поворотом до флипа, никаких
     opacity-твинов ему не нужно. */
  .cost-searchbar__icon--back{ position:absolute; left:35px; top:24px; width:51px; height:51px; }
  .cost-searchbar__icon--back svg{ display:block; width:100%; height:100%; }
  .cost-searchbar__text--back{
    position:absolute; inset:0; margin:0; display:flex; align-items:center; justify-content:center;
    font-family:'Unbounded',sans-serif; font-weight:500; font-size:52px;
    line-height:0.9; color:#fff; white-space:nowrap;
  }

  /* .cost-orb — падение (intro, одна большая GSAP-анимация y).
     .cost-orb__avoid — реакция на курсор (небольшой continuous x/y).
     .cost-orb__bob — лёгкое постоянное покачивание (y + rotation),
     самостоятельный декоративный эффект, с водой технически не связан.
     Три уровня специально разделены — иначе GSAP-твины intro/avoid/ambient
     дрались бы за один и тот же transform одного элемента. */
  .cost-orbs{ position:absolute; inset:0; z-index:3; }
  .cost-orb{ position:absolute; }
  .cost-orb__avoid{ position:absolute; inset:0; }
  /* will-change:transform — не «на всякий случай», а по замеру (правка
     2026-08-08). Покачивание двигает y и rotation КАЖДЫЙ КАДР, но делает это
     из JS (gsap.set в тикере), а не CSS-анимацией — по такому коду браузер не
     догадывается, что элемент анимируется, и своего слоя ему не выдаёт. В
     итоге объект перерисовывался заново каждый кадр: трассировка слайда в
     полном покое показывала 104 мс/с в RasterTask при 30 мс/с собственно
     JS-работы. С отдельным слоем растр готовится один раз, дальше его двигает
     и поворачивает GPU. Тот же приём и та же причина, что у .color-back в
     css/phone-shared.css. */
  .cost-orb__bob{ position:absolute; inset:0; will-change:transform; }
  .cost-orb img{ position:absolute; inset:0; width:100%; height:100%; object-fit:contain; pointer-events:none; }

  /* .cost-text — flex-column с gap (не margin на детях) — 1:1 с Figma
     "normal"-группами (gap:6px между эйбрау/полосой/заголовком). Порядок
     детей в разметке ТОЖЕ поменялся (см. HTML) — раньше bar шёл ПЕРВЫМ
     (перед эйбрау-текстом), в Figma порядок lead → bar → headline (→ note,
     только у 3-го блока). Ширина/позиция — своя у каждого блока (левый край
     и top совпадают с top:262 внутри соответствующей "normal"-группы Figma:
     192/561/324 у rubl, 565/561/310 у clock, 967/561/310 у bubble). */
  .cost-texts{ position:absolute; inset:0; z-index:6; pointer-events:none; }
  .cost-text{
    position:absolute; margin:0; opacity:0; text-align:center;
    display:flex; flex-direction:column; gap:6px;
  }
  .cost-text--1{ left:192px; top:561px; width:324px; }
  .cost-text--2{ left:565px; top:561px; width:310px; }
  .cost-text--3{ left:967px; top:561px; width:310px; }
  .cost-text__bar{ display:block; width:100%; height:3px; background:var(--yellow); }
  .cost-text__lead{
    margin:0; font-family:'Unbounded',sans-serif; font-weight:400;
    font-size:13px; line-height:1.4; color:var(--yellow);
  }
  .cost-text__headline{
    margin:0; font-family:'Unbounded',sans-serif; font-weight:500;
    font-size:24px; line-height:1.2; color:var(--yellow);
  }
  .cost-text__note{
    margin:0; font-family:'Unbounded',sans-serif; font-weight:400;
    font-size:13px; line-height:1.4; color:var(--yellow);
  }

  /* ===== Стили контента "Solution" (Figma node 2872:2459, "Solution
     (desktop)"; редизайн 2026-08-05, прежний узел был 2816:2620) — обычный
     контент #slide-4, реальной отдельной позиции #slide-track (см. подробный
     комментарий у правила #slide-4 ниже и js/slides.js, TOTAL=11),
     приходит/уходит обычным advance()/retreat(), как и у всех остальных
     слайдов (YHA.playSlide4Intro/exitSlide4Intro).

     Тексты переехали ВВЕРХ и подсохли относительно прежней версии
     (today 320→280, service 380→320, body 464→404, body 32→24px,
     width 1020→862) — низ слайда занят тремя блоками-регалиями (см. ниже),
     которых в прежнем макете не было. Начертания приведены к Figma
     (Medium/Regular) отдельной правкой — см. чистку шрифтов
     2026-08-05/06: в проекте живут ровно три веса Unbounded 600/500/400. */
  .solution-parallax{ position:absolute; inset:0; z-index:6; pointer-events:none; }
  /* Центрирование — ЛЕЙАУТОМ (left:0/right:0 + margin:0 auto), а НЕ
     transform:translateX(-50%). Тот же класс багов, что уже описан у
     .iw-col--right в css/iw-block.css, только в более грубом виде.
     GSAP пишет этим элементам inline-transform (resetSlide4() делает
     gsap.set(...,{y:24}) СРАЗУ при загрузке js/slide3-cost.js) и затирает
     CSS-ный transform целиком; исходный translateX(-50%) он пытается
     "запечь" в пиксели, читая computed-transform. В Safari на момент
     выполнения defer-скрипта стили ещё не применены (base.css — 3,7 МБ
     base64-шрифтов), getComputedStyle отдаёт transform:none, GSAP запекает
     x=0 — и текст навсегда остаётся сдвинутым вправо ровно на полширины
     (баг найден пользователем 2026-08-06; в Chrome CSSOM успевал
     примениться до defer-скриптов, поэтому там не воспроизводился).
     Layout-свойства GSAP не трогает — гонка невозможна в принципе.
     left:0/right:0 при width:auto растягивает бокс на весь канвас (1440),
     nowrap-строки центрирует уже стоящий тут text-align:center; у --body
     своя ширина 862 — там центрирует margin:0 auto. */
  .solution-text{
    position:absolute; left:0; right:0; margin:0;
    text-align:center; font-family:'Unbounded',sans-serif; font-weight:500;
    color:var(--ink); white-space:nowrap; opacity:0;
  }
  .solution-text--today{ top:280px; font-size:32px; line-height:0.9; }
  .solution-text--service{ top:320px; font-size:72px; line-height:0.9; }
  .solution-text--body{ font-weight:400; top:404px; width:862px; margin:0 auto; white-space:normal; font-size:24px; line-height:1.4; }

  /* ----- Три блока-регалии внизу слайда (Figma 2872:2459 — покой,
     2945:1652 — ховер) -----
     Заменили кнопку "И он уже здесь!" (была .solution-btn 52×52, разраставшаяся
     в пилюлю с побуквенной печатью — удалена целиком вместе с
     measureSolutionBtnWidth/SOLUTION_BTN_TEXT в js/slide3-cost.js).

     Все три — <button>: боковые открывают попапы с реквизитами
     (js/solution-popups.js), центральный — оверлей "О компании" (слайд 12).
     Ховер по макету — только пунктирная рамка; чтобы содержимое не дёргалось
     на 1px в момент наведения, рамка в покое НЕ появляется из ниоткуда, а
     стоит всегда, просто прозрачная (box-sizing:border-box + переход по
     border-color). Геометрия 1:1 из Figma: top:495, height:233, ширины
     230/480/230, левые края 199/480(центр)/1015, gap 14/11/15. */
  .sol-blocks{ position:absolute; inset:0; z-index:7; pointer-events:none; }
  .sol-block{
    position:absolute; top:495px; height:233px; box-sizing:border-box;
    margin:0; padding:24px 16px; background:none;
    border:1px dashed transparent; border-radius:24px;
    display:flex; flex-direction:column; align-items:center; justify-content:center;
    color:var(--ink); cursor:pointer; pointer-events:auto;
    opacity:0; -webkit-appearance:none; appearance:none;
    transition:border-color 0.18s ease;
  }
  .sol-block:hover,
  .sol-block:focus-visible{ border-color:var(--ink); outline:none; }
  .sol-block--rospatent{ left:199px; width:230px; gap:14px; }
  .sol-block--m5{ left:480px; width:480px; gap:11px; }
  .sol-block--mindigital{ left:1015px; width:230px; gap:15px; }
  .sol-block__icon{ display:block; flex-shrink:0; }
  .sol-block__icon--rospatent{ width:177px; height:128px; }
  .sol-block__icon--mindigital{ width:132px; height:128px; object-fit:contain; }
  /* Тот же media5-logo.svg, что в попапе "О компании", но там он лежит на
     ЧЁРНОЙ карточке и потому сам белый. Здесь фон жёлтый — перекрашиваем в
     чёрный тем же приёмом, что и иконки регалий в тёмной карточке, только
     без invert: brightness(0) гасит любой цвет в чёрный, прозрачность не
     трогает. Второй файл ради одного цвета не заводим. */
  .sol-block__logo{ display:block; width:146px; height:32.591px; flex-shrink:0; filter:brightness(0); }
  /* Подписи внутри блоков — <span> с display:block, а не <p>: блок это
     <button>, а параграф внутри кнопки невалиден по модели контента. */
  .sol-block__label{
    display:block; margin:0; font-family:'Unbounded',sans-serif; font-weight:400;
    font-size:21px; line-height:1.4; text-align:center;
  }
  /* leading-none у "Реестр / Минцифры" — так в Figma (двухстрочная подпись
     собрана плотнее однострочного "Роспатент"). */
  .sol-block--mindigital .sol-block__label{ line-height:1; }
  .sol-block__lead{
    display:block; margin:0; font-family:'Unbounded',sans-serif; font-weight:500;
    font-size:32px; line-height:1.2; text-align:center;
  }

  /* ===== Слайд 4 — "Solution" (Figma node 2816:2620), ЧЕТВЁРТАЯ реальная,
     равноправная позиция #slide-track (см. js/slides.js, TOTAL=11).
     Единственный путь появления — обычный advance()/retreat() из/в Cost
     (index 2), как и у всех остальных слайдов. Общие переезжающие слои
     (рамка/colorBack/searchbar) физически те же элементы, что и на слайде 3
     — НЕ меняют геометрию на этом переходе (те же координаты), меняется
     только цвет, см. js/slide3-cost.js (setSearchbarSolution/
     exitSearchbarSolution). Фон — сплошной ЖЁЛТЫЙ, не чёрный: Solution это
     "ответ" на проблему, показанную на чёрном Cost, и намеренно
     контрастирует с ним. Bleed .stage-wrap слева/справа/сверху (см.
     js/phone-shared.js STAGE_BLEED_*) на этом переходе тоже возвращается в
     жёлтый, см. js/slide3-cost.js setSearchbarSolution. Собственного
     статичного background:var(--yellow) тут нет — избыточен, экран и так
     уже перекрашивается обратно в жёлтый тем же bleed-твином
     .stage-wrap/#stage. .color-back тут, наоборот, снова ПОКАЗЫВАЕМ
     (opacity:1, было погашено на входе в Cost) — см. те же функции в
     js/slide3-cost.js. */
  #slide-4{ top:2280px; }


  /* Обёртка-абзац «Сегодня цифровой сервис становится такой же обязательной
     частью…» (2026-08-08, «версия для чтения») — см. .app-pointer-phrase в
     css/slide4-app.css, приём и причина те же. */
  .solution-phrase{ margin:0; font-size:inherit; font-weight:inherit; }
