*{box-sizing:border-box}
html,body{height:100%}
body{
  margin:0; overflow:hidden; background:var(--paper); color:var(--ink);
  font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,system-ui,sans-serif;
  font-size:15px; line-height:1.45; -webkit-font-smoothing:antialiased;
  overscroll-behavior:none;
}
button{font:inherit;color:inherit;background:none;border:0;cursor:pointer;padding:0}
input,textarea{font:inherit}
:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:6px}
.mono{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-variant-numeric:tabular-nums}
[hidden]{display:none !important}

/* Полосы прокрутки не показываем НИГДЕ: в мини-приложении их роль уже играет
   затухание у края (карточка снизу, слои справа), а системная полоса приходит
   из десктопного Chrome со стрелками на концах и выглядит как чужая деталь —
   ни инстаграм, ни авито её не показывают.
   Прокрутка при этом остаётся: прячем ВИД, а не поведение. Там, где сигнал
   «ниже есть ещё» нужен, он даётся затуханием — иначе обрезанная строка
   читается как поломка (frontend.md 18). */
*{scrollbar-width:none;-ms-overflow-style:none}
::-webkit-scrollbar{width:0;height:0;display:none}

#app{display:flex;flex-direction:column;height:100vh;height:100dvh;width:100%;overflow:hidden}
/* Клавиатуры `100dvh` НЕ ЗАМЕЧАЕТ: высота остаётся прежней, и низ экрана уезжает
   под неё. Замер 04.09.2026 на Pixel 7 (клавиатура 336 px, видимая область 447):
   композер стоял на 441…500 — 53 px за краем, человек печатал в поле, нижнего
   края которого на экране нет. Спасал положение браузер, прокручивая страницу к
   фокусу, — то есть «экран сдвигался вверх» не по нашей воле.
   Правду о видимой высоте знает только visualViewport; её подставляет
   watchKeyboard, и ставит ДО класса, поэтому к моменту включения правила
   переменная уже на месте. */
body.kbd #app{height:var(--app-h)}
/* ...И ТАК ЖЕ каждый полноэкранный слой. `#app` ужимался один, а барахолка,
   афиша, карточка события, формы и модалки прибиты к ОКНУ (`position:fixed;
   inset:0`) и клавиатуры не замечали вовсе: замер на эмуляторе 23.09.2026 —
   окно 786, видно 474, слой по-прежнему 786, то есть 312 px его содержимого
   лежало ПОД клавиатурой. В барахолке это видно как «текст наехал»: карточка
   стоит на 435, из неё видно 39 px, а цена, название, «ещё» и кнопки — за
   краем (жалоба владельца 23.09.2026).
   Перечисление поимённо ломается молча, поэтому его сторожит тест
   `tests/test_marker_layers.py`: каждый слой `position:fixed; inset:0` обязан
   стоять в этой строке. Кадры товара пересчитает свой наблюдатель размера
   (market.md 34а), лента и формы просто станут короче. */
body.kbd .market,
body.kbd .afisha,
body.kbd .ev-page,
body.kbd .evl,
body.kbd .place-form,
body.kbd .pf-crop,
body.kbd .my-places,
body.kbd .add-sheet,
body.kbd .tour-l,
body.kbd .modal{bottom:auto;height:var(--app-h)}
/* Ни одна строка интерфейса не имеет права быть шире экрана: в вебвью
   горизонтальной прокрутки нет, и всё, что вылезло, просто не видно. */
#app > *,
.chat, .chat-bar, .grip, .composer, .feed{min-width:0;max-width:100%}
.chat-bar, .grip, .composer{overflow:hidden}
.toast{
  position:fixed;left:50%;bottom:calc(78px + env(safe-area-inset-bottom));transform:translateX(-50%);
  background:var(--ink);color:var(--paper);padding:8px 14px;border-radius:999px;
  font-size:12.5px;box-shadow:var(--shadow-lift);z-index:calc(var(--z-modal) + 100);max-width:90%;
}


/* Снимок уходящего экрана для общего жеста «назад» (`backswipe.js`). Живёт
   ровно один жест: под ним уже нарисовано назначение, поэтому человек видит,
   КУДА идёт. Тень по левому краю отделяет уезжающий слой от того, что под
   ним, — без неё два светлых экрана слипаются в один. */
/* ЩИПОК ПО СНИМКУ (как в Instagram, 21.09.2026) — общий для барахолки и
   карточки места: копия снимка выезжает за свой кадр поверх всего, фон под ней
   темнеет, отпустили — пружиной на место. Живёт ровно один жест. Правило одно,
   потому что и жест один (`gallery.js`): двух описаний одного движения быть не
   должно (app.md 12). Затемнение — ролью, прозрачность ставит JS по масштабу. */
.gzoom{position:absolute;inset:0;z-index:10;pointer-events:none;
  background:var(--zoom-veil);opacity:0}
.gzoom-img{position:absolute;max-width:none;will-change:transform}

.bs-snap{position:absolute;z-index:6;overflow:hidden;pointer-events:none;
  will-change:transform,opacity;box-shadow:-10px 0 26px rgba(12,22,28,.22)}

/* ---- Подсказка главного жеста экрана (A1, владелец 30.09.2026) ----
   Затемнение градиентом (сверху почти прозрачно — видно, что под ним),
   рука ведёт пальцем со следом, крупная подпись. Касаний не ловит: жест
   уходит карточке, первое касание подсказку убирает (gesture-hint.js).
   Слой — выше полноэкранных страниц, ниже модалок (frontend.md 11). */
.ghint{position:fixed;inset:0;z-index:calc(var(--z-page) + 20);pointer-events:none;
  background:linear-gradient(180deg,rgba(8,18,24,.18) 0%,rgba(8,18,24,.62) 40%,rgba(8,18,24,.9) 72%,rgba(8,18,24,.94) 100%);
  animation:gh-in .35s ease-out both;color:#fff}
.ghint.out{animation:gh-out .22s ease-in both}
.ghint .gh-stage{position:absolute;left:0;right:0;top:40%;height:240px;display:flex;justify-content:center}
.ghint .gh-hand{position:absolute;bottom:20px;overflow:visible}
.ghint .gh-hand path{fill:#fff;stroke:#fff;stroke-width:3;stroke-linecap:round;stroke-linejoin:round}
.ghint .gh-touch{fill:rgba(255,255,255,.3)}
.ghint .gh-trail{position:absolute;bottom:72px;width:6px;border-radius:3px;
  background:linear-gradient(0deg,rgba(255,255,255,0),rgba(255,255,255,.9))}
.ghint .gh-text{position:absolute;left:16px;right:16px;bottom:calc(64px + env(safe-area-inset-bottom));
  display:flex;flex-direction:column;align-items:center;gap:6px;text-align:center}
.ghint .gh-text b{font-size:26px;line-height:1.2;letter-spacing:-.01em}
/* Под карточкой бывает белое (кнопки, блок Google) — тень держит текст на любом фоне. */
.ghint .gh-text,.ghint .gh-tap{text-shadow:0 2px 12px rgba(0,0,0,.55)}
.ghint .gh-text span{font-size:15px;opacity:.88}
.ghint .gh-tap{position:absolute;left:0;right:0;bottom:calc(22px + env(safe-area-inset-bottom));
  text-align:center;font-size:12px;color:rgba(255,255,255,.62)}
.ghint-up .gh-hand{animation:gh-up 1.8s cubic-bezier(.4,0,.2,1) infinite}
.ghint-up .gh-trail{animation:gh-trail 1.8s cubic-bezier(.4,0,.2,1) infinite}
.ghint-right .gh-hand,.ghint-lr .gh-hand{bottom:70px}
.ghint-right .gh-hand{animation:gh-right 1.8s cubic-bezier(.4,0,.2,1) infinite}
.ghint-lr .gh-hand{animation:gh-lr 2.6s ease-in-out infinite}
.ghint-right .gh-trail,.ghint-lr .gh-trail{display:none}
@keyframes gh-in{from{opacity:0}to{opacity:1}}
@keyframes gh-out{to{opacity:0}}
@keyframes gh-up{0%{transform:translateY(90px);opacity:0}15%{opacity:1}70%{transform:translateY(-70px);opacity:1}100%{transform:translateY(-90px);opacity:0}}
@keyframes gh-trail{0%{height:0;opacity:0}20%{opacity:.9}70%{height:160px;opacity:.9}100%{height:170px;opacity:0}}
@keyframes gh-right{0%{transform:translateX(-90px);opacity:0}15%{opacity:1}70%{transform:translateX(70px);opacity:1}100%{transform:translateX(90px);opacity:0}}
@keyframes gh-lr{0%,100%{transform:translateX(0)}25%{transform:translateX(80px)}75%{transform:translateX(-80px)}}
@media (prefers-reduced-motion:reduce){
  .ghint .gh-hand,.ghint .gh-trail{animation:none}
}

