  /* ===== Слайд 5 — "App" (Figma node 2865:1853), ПЯТАЯ реальная позиция
     #slide-track. Единственный путь появления — advance() из Solution
     (index 3, см. js/slides.js). Общие переезжающие слои (рамка/colorBack/
     searchbar) физически те же элементы, что и на слайдах 1/2/3/4 — их морф
     сюда/отсюда живёт в js/phone-shared.js (setPhoneApp/exitPhoneApp) и
     js/slide3-cost.js (setSearchbarApp/exitSearchbarApp). Всё остальное
     здесь — обычный контент #slide-5, те же плоские design-px координаты
     0..1440×0..760, что и у слайдов 1/2 (выравнивание с телефоном достигается
     тем, что числа тут и в APP_PHONE/js/phone-shared.js — одна и та же
     система координат, просто разные независимые слои). */
  #slide-5{ top:3040px; }

  /* Cover (Figma-узел "Cover", 2876:2528) — сплошная ЖЁЛТАЯ (не белая)
     заливка с ВЫРЕЗОМ сверху, которая гасит НИЖНЮЮ часть одновременно и
     маленькой чёрной рамки телефона, и цветного пятна (не только
     colorBack) — рамка на слайде 4 стоит на Z_BASE (см.
     setPhoneApp/js/phone-shared.js, тот же z-index:1, что у слайдов 1/2,
     ПОЗАДИ #slide-track z-index:2), поэтому обычный контент слайда 4 (в
     т.ч. этот блок) рисуется поверх неё автоматически, без доп. z-index —
     та же логика, что у стикеров/карты слайда 2, вылезающих за рамку.
     Реальная Figma-геометрия выреза (узел 2876:2528, сверено через
     get_metadata) — не простой clip-эллипс, а boolean SUBTRACT (Rectangle
     minus Ellipse): в координатах бокса это ШИРОКИЙ ПЛОСКИЙ эллипс у
     САМОГО ВЕРХА бокса (не круг у низа) — отсюда форма/позиция
     mask-image ниже. opacity:0 по умолчанию — часть общего intro/exit
     слайда 4 (js/slide4-app.js). */
  /* Бокс растянут с исходных 886px (277..1163) до ПОЛНОЙ ширины слайда
     (0..1440px) — после того как `APP_COLORBACK` увеличили до размеров
     Solution (см. project-статус в памяти), цветовое пятно стало выступать
     ЗА старые узкие границы Cover, и на этих границах был виден жёсткий
     шов между замаскированной зоной и голым пятном.
     Купол-вырез растянут вместе с боксом на всю его ширину: rx увеличен с
     443 (исходная Figma-пропорция под ширину телефона) до 720 — ровно
     половина от 1440, так что эллипс касается нулевой ширины точно в x=0 и
     x=1440 (края слайда), без "хвоста"-заплатки сплошной заливкой без
     купола за старыми границами. cx=720 (центр телефона) и ry=120 не
     менялись — только rx.
     Форма реализована через mask-image с radial-gradient(), не
     clip-path:path(evenodd,...) с SVG-arc-дугами — последний стабильно
     рендерил в Chromium лишнюю ГОРИЗОНТАЛЬНУЮ ЛИНИЮ на всю ширину бокса
     прямо у верхней границы купола (похоже на огрех тесселяции/
     антиалиасинга SVG-path-клипа при такой почти вырожденной геометрии,
     где эллипс rx=720 ровно touching края бокса — воспроизводилось
     независимо от конкретных чисел центра). mask-image нативно
     антиалиасится браузером без полигональной тесселяции пути — визуально
     даёт ТОТ ЖЕ вырез (жёсткая граница на 99%→100% радиуса), но без
     артефакта.
     top+bottom:0 (не фиксированный height) — высота всегда равна
     фактической высоте #slide-5 (его containing block, JS выставляет
     inline height), которая растягивается динамически на узких/высоких
     окнах (slideH в js/stage.js, растёт от 760 и выше — см. fitStage()).
     Фиксированная height:406px (рассчитана на базовый холст 760px) не
     дотягивала бы до низа там — часть низа слайда (телефон/цветовое пятно)
     оставалась бы не прикрыта; на обычных экранах (slideH=760) видимая
     область не меняется. mask-image не пострадал — эллипс-вырез задан
     абсолютными px ("at 720px 4px", без %), его форма/позиция у самого
     верха бокса не зависят от итоговой высоты бокса.
     opacity целевого (видимого) состояния — 0.8, не 1 (см.
     YHA.playSlide5Intro в js/slide4-app.js; сам элемент по-прежнему
     стартует с opacity:0, меняется только конечное значение intro-твина),
     плюс backdrop-filter:blur — то, что физически ЗА этим слоем по z (низ
     .phone-frame/.color-back, общий переезжающий слой #phone-frame-layer,
     z-index:1, НИЖЕ #slide-track z-index:2, где живёт .app-cover), видно
     сквозь него размытым, а не резким. 16px — ориентировочное значение, не
     сверялось с Figma (там этого эффекта не было), можно скорректировать
     на глаз. */
  /* ===== Правка 2026-08-07, второй заход (жалоба «вижу чистый жёлтый, не
     вижу размытия») ========================================================
     ГЕОМЕТРИЧЕСКАЯ ошибка, а не отсутствие блюра. Заливка была ВЕРТИКАЛЬНЫМ
     градиентом от верха БОКСА (0.30 на 0px → сплошной на 240px), а маска
     открывает элемент только НИЖЕ купола. В центре купол опускается на
     124px — то есть первое, что видно, это уже 0.79 непрозрачности: почти
     сплошной жёлтый ровно по кромке. Отсюда и жёсткая дуга на скриншоте, и
     «размытия не видно»: полоса, где ещё есть что размывать, оказалась
     шириной в сотню пикселей и почти непрозрачной (замерено: вклад блюра —
     строки 651…750 экрана, max 50 из 255, среднее 0.07).

     Как в макете (узел 2876:2528, boolean SUBTRACT: Rectangle 1440×406 minus
     Ellipse 1440×240): кромка купола ЖЁСТКАЯ — это ровный чёткий срез, а не
     растворение. Пользователь показал скриншотами макет и сайт рядом и
     сформулировал прямо: «там должна быть жёсткая граница, но сама маска
     должна быть прозрачным стеклом». То есть по кромке — резкий край, а под
     ней — полупрозрачное матовое стекло, сквозь которое ещё различимы низ
     рамки и частицы, и только ниже жёлтый становится сплошным.

     ⚠️ Промежуточный заход (растушёванная кромка, «фигура проявляется из
     ничего») был ОТКЛОНЁН пользователем — не повторять. Мягкая кромка
     выглядела как размытая клякса вместо среза.

     И заливка, и стекло — эллиптические radial-gradient с ТОЙ ЖЕ геометрией,
     что купол (720×120 at 720px 4px). Стоп на 100% — ровно кромка; у заливки
     на нём скачок с 0 до 0.62 (жёсткий край), дальше проценты это доли того
     же радиуса, так что уплотнение идёт «по нормали» к дуге, а не
     горизонтальной полосой. Прежняя ошибка была именно в этом: заливку
     считали вертикальным градиентом от ВЕРХА БОКСА, а маска открывает
     элемент только ниже купола — в центре это уже 0.79, почти сплошной
     жёлтый, и размывать было нечего (замерено: вклад блюра max 35 из 255).

     Два слоя вместо одного (см. разметку в index.html):
       .app-cover        — только цвет, без blur и без маски. Маска не нужна:
                           внутри купола сам градиент прозрачен;
       .app-cover-glass  — только матовое стекло, своя маска с растушёвкой
                           (0 на кромке купола → 1 на 150% радиуса), чтобы
                           резкость переходила в размытие плавно, а не по
                           линии.
     Разделение ещё и страхует: backdrop-filter в паре с mask-image — самая
     хрупкая часть конструкции (в WebKit эта пара исторически капризна). Если
     стекло где-то отвалится, форма и растворение останутся на месте.

     ⚠️ Слои лежат ВНУТРИ #slide-track (z-index 2), а размывают содержимое
     #phone-frame-layer (z-index 1) — это работает: backdrop-root создают
     filter/opacity/mask, но НЕ transform, а у трека только он.
     ⚠️ Оба элемента гасятся ОДНИМ твином как СОСЕДИ (не родитель+ребёнок):
     родитель с opacity<1 стал бы backdrop-root, и стекло было бы выключено
     всю анимацию появления, а в конце скачком включалось. */
  .app-cover{
    position:absolute; left:0; top:421px; bottom:0; width:1440px;
    opacity:0; pointer-events:none;
    /* Скачок 99.9%→100% — это и есть жёсткая кромка купола. Дальше проценты
       того же радиуса: 0.62 сразу под кромкой (сквозь неё видно матовое
       стекло), 0.80 на 140%, сплошной жёлтый на 200% — в центре это
       y=124 / 172 / 244 от верха бокса. */
    background: radial-gradient(ellipse 720px 120px at 720px 4px,
      rgba(255,227,0,0) 99.9%,
      rgba(255,227,0,0.62) 100%,
      rgba(255,227,0,0.80) 140%,
      var(--yellow) 200%);
  }
  .app-cover-glass{
    position:absolute; left:0; top:421px; bottom:0; width:1440px;
    opacity:0; pointer-events:none;
    backdrop-filter:blur(26px); -webkit-backdrop-filter:blur(26px);
    mask-image: radial-gradient(ellipse 720px 120px at 720px 4px, rgba(0,0,0,0) 99%, rgba(0,0,0,1) 100%);
    -webkit-mask-image: radial-gradient(ellipse 720px 120px at 720px 4px, rgba(0,0,0,0) 99%, rgba(0,0,0,1) 100%);
  }

  /* Параллакс на движение мыши (слайд App) — обёртки двигаются через
     transform на GSAP-тикере (js/slide4-app.js). Обёртки нужны ИМЕННО
     чтобы не трогать собственный `y` элементов внутри — он уже занят
     intro-анимацией выезда (YHA.playSlide4Intro), это разные, независимо
     складывающиеся transform-уровни (тот же приём, что у cost-orb/
     cost-orb__avoid/cost-orb__bob на слайде 3, см. память проекта).
     Обёртки нуждаются в ЯВНОМ z-index (не только их дети): GSAP навешивает
     `transform` даже на нулевые x:0,y:0 (gsap.set вызывается каждый кадр),
     а ЛЮБОЙ transform ≠ none САМ ПО СЕБЕ создаёт новый stacking context,
     НЕЗАВИСИМО от того, задан ли z-index — иначе обёртка с z-index:auto
     проигрывает явному z-index:1 у #app-particles-canvas (тот же паттерн,
     что у .app-logo-big/.app-pointer-text — см. правило "explicit z-index
     всегда выше auto" в памяти проекта), и текст/указатели уходят ПОД
     канвас. .app-parallax-group (текст+указатель, объединяет 4 группы, см.
     HTML-комментарий ниже) — 3, .app-parallax-back (лого) — 2. */
  .app-parallax-group, .app-parallax-back{ position:absolute; inset:0; pointer-events:none; }
  .app-parallax-group{ z-index:3; }
  .app-parallax-back{ z-index:2; }

  /* Крупный лого-знак внутри маленького телефона — переиспользует тот же
     ассет, что и Hero (images/logo-big.svg, .logo-big на слайде 1), просто
     в новых координатах (Figma "Logo-big", 1:1 с container-математикой
     left:626/top:calc(50%-42.5px)+translateY(-50%) → top:337.5-76.5=261). */
  /* z-index:2 — у #app-particles-canvas явный z-index:1, а у лого его не
     было (auto), из-за чего частицы рисовались ПОВЕРХ лого, несмотря на
     более поздний порядок в DOM (позиционированные элементы с явным
     z-index всегда выше элементов с z-index:auto/0). */
  .app-logo-big{ position:absolute; left:626px; top:261px; width:188px; height:153px; opacity:0; pointer-events:none; z-index:2; }

  /* Частицы-кольца ("пылевые кольца Сатурна") — 2 эллиптических "пояса"
     вокруг маленького телефона (в Figma "particles", 2892:2730 — просто
     пунктирный гайд-скрин, не финальная графика, см. память проекта про
     блокировку скачивания реальных ассетов с figma.com). Рендерятся на
     <canvas> (js/slide4-app.js), не DOM-div'ах: (1) внешний вид "богаче"
     (мягкое свечение через переиспользуемый спрайт-градиент, не плоские
     кружки), (2) на intro частицы слетаются к своим местам в кольце ИЗ-ЗА
     ПРЕДЕЛОВ экрана со всех сторон, (3) кольца реагируют на курсор —
     частицы вблизи мыши отталкиваются и плавно возвращаются, когда мышь
     уходит (тикер/mousemove гейтятся через `YHA.activeSlideIndex`, тот же
     паттерн, что у волны/уклонения объектов слайда 3, см.
     js/slide3-cost.js). Внутренняя система координат канваса — тот же
     design-space 0..1440×0..slideH, что у остального контента слайда (см.
     resizeCanvasParticles в js/slide4-app.js) — CX/CY/rx/ry не меняются от
     масштаба/DPR. */
  /* width/height:100% ОБЯЗАТЕЛЬНЫ отдельно от inset:0 — canvas это "replaced
     element", и при position:absolute его width/height по умолчанию auto
     резолвятся в СОБСТВЕННЫЙ intrinsic-размер (буфер, который сама же JS
     двигает через canvas.width/height), а НЕ растягиваются от left/right,
     как у обычного div. Без этой строки resizeCanvasParticles() в
     js/slide4-app.js уходил в раскручивающийся цикл (каждый вызов читал уже
     раздутый intrinsic-размер канваса как "текущий CSS-размер" и делал его
     ещё больше) — поймано на себе, канвас разросся до 19200×9600px. */
  .app-particles-canvas{ position:absolute; inset:0; width:100%; height:100%; z-index:1; pointer-events:none; display:block; }

  /* 4 текстовых тезиса ("pointer" в Figma) — координаты 1:1 из
     get_design_context по узлам 2892:2723/2724/2726/2725 (container-
     относительные calc()-выражения из Figma пересчитаны в плоские абсолютные
     px, та же система координат 0..1440×0..760, что у остального контента
     слайда). Коннекторы (кружок+шеврон + ВЕРТИКАЛЬНАЯ пунктирная линия +
     точка) — приближение к Figma-компоненту "advantages-set-pointer" (сам
     bezier-путь стрелки Right_Arrow_4_ не экспортирован, сетевая песочница
     блокирует figma.com); линия строго вертикальная (не диагональная к
     телефону) — кружок стоит рядом с текстом, точка на фиксированном
     вертикальном отступе от него, и это единственное, что их соединяет. */
  /* z-index:3 у текстов/подписи — у #app-particles-canvas явный z-index:1,
     а у .app-pointer-text/.app-pointer-note/.app-caption его не было
     (auto) — та же причина, что у .app-logo-big выше (позиционированные
     элементы с явным z-index всегда выше z-index:auto/0, независимо от
     порядка в DOM). Кружки/линии/точки уже были на z-index:2-3. */
  .app-pointer-text{
    position:absolute; margin:0; opacity:0; z-index:3;
    font-family:'Unbounded',sans-serif; font-weight:500;
    font-size:21px; line-height:1.3; color:var(--ink);
  }
  .app-pointer-note{
    position:absolute; margin:0; opacity:0; z-index:3;
    font-family:'Unbounded',sans-serif; font-weight:400;
    font-size:14px; line-height:1.3; color:var(--ink);
  }
  .app-pointer-circle{
    position:absolute; width:48px; height:48px; margin:-24px 0 0 -24px;
    border-radius:50%; background:#fff;
    display:flex; align-items:center; justify-content:center;
    opacity:0; z-index:3;
  }
  .app-pointer-circle svg{ width:18px; height:18px; display:block; }
  .app-pointer-circle svg polyline{ fill:none; stroke:var(--ink); stroke-width:2.4; stroke-linecap:round; stroke-linejoin:round; }
  /* Шеврон всегда смотрит В СТОРОНУ ОТ телефона (к тексту) — та же логика,
     что в первой версии, просто теперь только влево/вправо (--flip
     разворачивает "›" в "‹" для пойнтеров слева от телефона). */
  .app-pointer-circle--flip svg{ transform:scaleX(-1); }
  .app-pointer-vline{
    position:absolute; width:0; margin-left:-1px;
    border-left:2px dashed rgba(255,255,255,0.85);
    opacity:0; z-index:2;
  }
  /* box-shadow у .app-pointer-circle/.app-pointer-dot убран целиком — оба
     белых кружка (большой с шевроном и маленькая точка) были с тенью,
     убраны у обоих. */
  .app-pointer-dot{
    position:absolute; width:8px; height:8px; margin:-4px 0 0 -4px;
    border-radius:50%; background:#fff;
    opacity:0; z-index:2;
  }

  /* Нижняя подпись (Figma 2865:1863) — top:calc(50%+178px) относительно
     760-высокого холста = 380+178=558, здесь уже плоское число, как и у
     остального контента слайда. */
  /* Центрирование лейаутом (left:0/right:0 + margin:0 auto), НЕ
     transform:translateX(-50%) — GSAP пишет сюда inline-transform
     (gsap.set(captionEl,{y:16}) в js/slide4-app.js) и затирает CSS-ный;
     в Safari он при этом запекает x=0, и подпись уезжает вправо на
     полширины. Подробный разбор — у .solution-text в css/slide3-cost.css. */
  .app-caption{
    position:absolute; left:0; right:0; top:558px; z-index:3;
    width:955px; margin:0 auto; opacity:0;
    font-family:'Unbounded',sans-serif; font-weight:600; font-size:40px; line-height:1.2;
    text-align:center; color:var(--ink);
  }


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