/* ── Font: Nunito (self-hosted, variable) ──
   Единственный шрифт контура: тот же Nunito в мобильном приложении и в витрине
   магазина, которая живёт прямо здесь во <iframe>. До 2026-07-30 обёртка лендинга
   набиралась Quicksand, и на одном экране было видно две типографики.
   Подрезки и unicode-range — из канона design-system/goodiny-design-system.html. */
@font-face {
  font-family: 'Nunito';
  font-style: normal;
  font-weight: 300 700;
  font-display: block;
  src: url('../fonts/nunito-latin.woff2?v=1') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Nunito';
  font-style: normal;
  font-weight: 300 700;
  font-display: block;
  src: url('../fonts/nunito-latin-ext.woff2?v=1') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'Nunito';
  font-style: normal;
  font-weight: 300 700;
  font-display: block;
  src: url('../fonts/nunito-cyrillic.woff2?v=1') format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}

@font-face {
  font-family: 'Nunito';
  font-style: normal;
  font-weight: 300 700;
  font-display: block;
  src: url('../fonts/nunito-cyrillic-ext.woff2?v=1') format('woff2');
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}

/* ── Reset ── */
*, *::before, *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

/* Лендинг — один экран без скролла (секции ниже первого экрана убраны
   2026-07-31): overflow глушится в базе, а не по-страничным классам. */
html, body {
  font-family: "Nunito", sans-serif;
  background-color: #faf5f3;
  color: #2d2d2d;
  -webkit-font-smoothing: antialiased;
  overflow: hidden;
}

/* ── Page: single viewport, no scroll ── */
.page {
  position: relative;
  display: flex;
  height: 100vh;
  height: var(--app-h, 100dvh);
  max-width: 1200px;
  margin: 0 auto;
  padding: 16px 80px;
  gap: 50px;
  align-items: center;
}

/* ── Left: brand + footer ── */
/* container-type: слоган масштабируется от ШИРИНЫ ЭТОЙ КОЛОНКИ (cqi в .slogan),
   а не от вьюпорта — синхронно с логотипом, который ужимается только когда
   колонка становится уже его капа (480px / 360px на планшете). */
.brand-side {
  container-type: inline-size;
  position: relative;
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  height: 100%;
  min-width: 0;
  align-items: center;
  text-align: center;
}

.brand-top {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 28px;
}

/* Обёртка логотипа питча (ссылка на главную на некорневых адресах). Блок с тем же
   max-width, что и сам логотип, — иначе как inline-элемент она добавила бы разрыв по
   базовой линии и сдвинула бы колонку. */
.brand-home-link {
  display: block;
  max-width: 480px;
}

.logo {
  width: 100%;
  max-width: 480px;
  height: auto;
}

.slogan {
  font-family: "Nunito", sans-serif;
  /* Размер привязан к логотипу, не к вьюпорту: логотип = min(колонка, 480px),
     кап слогана 2.4rem (38.4px) достигается ровно при колонке 480px →
     коэффициент 38.4/480 = 8cqi. Пока логотип держит 480 — слоган держит
     2.4rem; колонка уже 480 — оба ужимаются пропорционально. Первая строка —
     фолбэк для браузеров без cq-юнитов. */
  font-size: clamp(1.4rem, 2.4vw, 2.4rem);
  font-size: clamp(1.4rem, 8cqi, 2.4rem);
  font-weight: 700;
  color: #2d2d2d;
  line-height: 1.2;
}

/* ── CTA Button ── */
.cta-btn {
  font-family: "Nunito", sans-serif;
  font-size: 1.25rem;
  font-weight: 700;
  background-color: #2d2d2d;
  color: #faf5f3;
  padding: 20px 46px;
  border-radius: 50px;
  border: 2px solid #2d2d2d;
  cursor: pointer;
  transition: background-color 0.3s ease, color 0.3s ease;
  align-self: center;
}

.cta-btn:hover {
  background-color: #faf5f3;
  color: #2d2d2d;
}

.hint {
  font-family: "Nunito", sans-serif;
  font-size: 0.85rem;
  font-weight: 600;
  color: #6f6660;
  margin: -16px 0 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  width: 100%;
}

/* ── SEO View ── */
.seo-side {
  display: none;
}

.seo-content {
  gap: 20px;
  align-items: flex-start;
  text-align: left;
  /* Pin to the top (unlike the vertically-centered brand pitch): SEO pages
     have varying content lengths, so top-aligning keeps the layout stable —
     longer content only grows downward instead of shifting everything. */
  justify-content: flex-start;
  padding-top: 24px;
  /* .brand-side centers its children (align-items: center), which makes this
     block shrink-to-fit and re-center per page — the left edge (logo, title)
     jumped horizontally with content width. Stretch to the full column width
     so the block is anchored identically on every page. */
  align-self: stretch;
}

.seo-home-link {
  display: inline-block;
}

.seo-logo {
  max-width: 160px;
  opacity: 0.5;
  transition: opacity 0.3s ease;
}

.seo-logo:hover {
  opacity: 1;
}

/* Breadcrumbs */
.seo-breadcrumbs {
  font-family: "Nunito", sans-serif;
  font-size: 0.8rem;
  font-weight: 600;
  color: #6f6660;
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
  max-width: 100%;
}

.seo-breadcrumbs a,
.seo-breadcrumbs span {
  overflow-wrap: break-word;
  min-width: 0;
}

.seo-breadcrumbs a {
  color: #6f6660;
  text-decoration: none;
  transition: color 0.3s ease;
}

.seo-breadcrumbs a:hover {
  color: #e91e63;
}

.seo-breadcrumb-sep {
  color: #d4ccc8;
}

.seo-breadcrumbs span:last-child {
  color: #2d2d2d;
}

/* Title */
.seo-title {
  font-family: "Nunito", sans-serif;
  font-size: 1.8rem;
  font-weight: 700;
  color: #2d2d2d;
  margin: 0;
  line-height: 1.3;
  /* Titles can be long unbroken tokens (UUID slugs) — wrap instead of
     overflowing toward the phone frame. */
  overflow-wrap: break-word;
  max-width: 100%;
}

/* Loading skeleton — shown while the app frame boots, before the real entity
   name/tags arrive (stage:data). Beats flashing a bare section label. */
.seo-side.seo-loading .seo-title {
  color: transparent;
  background: #ece7e4;
  border-radius: 10px;
  min-height: 1.8rem;
  width: 55%;
  animation: pulse 1.4s ease-in-out infinite;
}
.seo-side.seo-loading .seo-breadcrumbs span:last-child {
  color: transparent;
  background: #ece7e4;
  border-radius: 6px;
  min-width: 64px;
  display: inline-block;
  animation: pulse 1.4s ease-in-out infinite;
}

/* Meta info */
.seo-meta {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 0;
}

.seo-meta-row {
  display: flex;
  gap: 12px;
  font-family: "Nunito", sans-serif;
  font-size: 0.9rem;
}

.seo-meta-row dt {
  font-weight: 600;
  color: #6f6660;
  min-width: 70px;
}

.seo-meta-row dd {
  font-weight: 700;
  color: #2d2d2d;
  margin: 0;
}

/* Description */
.seo-desc {
  font-family: "Nunito", sans-serif;
  font-size: 0.9rem;
  font-weight: 500;
  color: #5d544c;
  margin: 0;
  line-height: 1.6;
  max-width: 420px;
}

/* Buy link-button */
.seo-buy-link {
  font-family: "Nunito", sans-serif;
  font-size: 0.95rem;
  font-weight: 600;
  color: #6f6660;
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  transition: color 0.3s ease;
}

.seo-buy-link:hover {
  color: #e91e63;
}

.hint-arrow {
  display: inline-block;
  vertical-align: middle;
}

/* ── Modal infrastructure (replaces Bootstrap's .modal/.fade/.show + .modal-backdrop) ── */
.modal {
  position: fixed;
  inset: 0;
  z-index: 1050;
  display: none;
  background: rgba(0, 0, 0, 0.5);
  opacity: 0;
  transition: opacity 150ms linear;
  overflow-y: auto;
  overflow-x: hidden;
  padding: 1rem;
}

.modal.is-open {
  display: flex;
  align-items: center;
  justify-content: center;
}

.modal.is-shown {
  opacity: 1;
}

body.modal-open {
  overflow: hidden;
}

/* ── Modal (original style) ── */
.modal-dialog {
  max-width: 700px;
  width: 100%;
  height: 90vh;
  display: flex;
  align-items: center;
  background-image: url("../images/modal-bg.svg?v=1");
  background-size: contain;
  background-repeat: no-repeat;
  background-position: center;
}

.modal-content {
  width: 100%;
  height: 100%;
  text-align: center;
  background-color: transparent;
  position: relative;
  border: 0;
  display: flex;
  flex-direction: column;
}

.modal-body {
  position: relative;
  background-color: transparent;
  border: 0;
  padding: 40px;
  padding-bottom: 140px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  text-align: center;
  height: 100%;
}

.modal-body .close {
  color: #2d2d2d;
  background-color: transparent;
  padding: 0;
  border: 0;
  font-size: 2rem;
  position: relative;
  margin-bottom: 40px;
  z-index: 10000;
}

.barcode {
  margin: 0 auto 40px;
  width: 280px;
  height: 280px;
}

.barcode img {
  width: 280px;
  height: 280px;
  display: block;
  border-radius: 16px;
  object-fit: contain;
}

.modal-body .text {
  color: #e91e63;
  font-size: 1.5rem;
  font-family: "Nunito", sans-serif;
  font-weight: 700;
  margin: 0;
}

/* ── Footer ── */
.footer {
  display: flex;
  flex-direction: column;
  gap: 12px;
  /* Без паддинга: низ футера = низ контентной области .page = низ рамки
     телефона (у неё height:100% того же бокса) — юр-строка .footer-legal
     ложится ровно на нижнюю границу телефона (решение владельца 2026-08-27),
     а навигация приподнята над ней на gap. */
  padding-bottom: 0;
}

.footer-nav {
  display: flex;
  gap: 12px 20px;
  flex-wrap: wrap;
  align-items: center;
  /* Wrapped rows (long locales push the lang-switch to line 2) stay centered
     instead of ragged-left — the hero column is centered composition. */
  justify-content: center;
}

.footer .link {
  font-family: "Nunito", sans-serif;
  font-size: 1.125rem;
  font-weight: 700;
  color: #2d2d2d;
  text-decoration: none;
  transition: color 0.3s ease;
}

.footer .link:hover {
  color: #e91e63;
}

.copyright {
  font-family: "Nunito", sans-serif;
  font-size: 12px;
  font-weight: 600;
  color: #2d2d2d;
}

/* Юр-строка оператора под футер-навигацией (обе левые панели). Приглушена
   цветом подсказки .hint — читаема, но не спорит со ссылками. */
.footer-legal {
  font-family: "Nunito", sans-serif;
  font-size: 12px;
  font-weight: 600;
  color: #6f6660;
}

/* ── Right: phone frame ── */
.phone-side {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  height: 100%;
}

.phone-frame {
  position: relative;
  height: 100%;
  aspect-ratio: 1 / 2;
  background: #2d2d2d;
  border-radius: 40px;
  /* Нижний борт толще остальных: в нём живёт .phone-live-hint («это живое
     приложение»). Высота экрана внутри чуть меньше — осознанно. */
  padding: 8px 8px 30px;
  box-shadow:
    0 40px 80px rgba(0, 0, 0, 0.12),
    0 16px 32px rgba(0, 0, 0, 0.08),
    inset 0 0 0 2px rgba(255, 255, 255, 0.06);
}

/* ── Подпись в нижнем борте рамки: живой индикатор + мелкий белый текст ── */
.phone-live-hint {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 30px; /* = padding-bottom рамки: строка ровно в борте */
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  font-family: "Nunito", sans-serif;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: rgba(255, 255, 255, 0.9);
  pointer-events: none; /* декорация: клики не перехватывает */
}

.live-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: #34d399;
  box-shadow: 0 0 6px rgba(52, 211, 153, 0.8);
  animation: livePulse 1.6s ease-in-out infinite;
  flex-shrink: 0;
}

@keyframes livePulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.3; }
}

@media (prefers-reduced-motion: reduce) {
  .live-dot { animation: none; }
}

.phone-notch {
  display: none;
}

/* Экран телефона — БЕЛЫЙ ВЕЗДЕ (решение владельца 2026-07-31), и на апексе, и на
   субдомене магазина. Это фон, который видно под символом-лоадером до появления
   бандла и сквозь прозрачный магазин потом: бежевый (#faf5f3, как у маркетинга
   вокруг) давал разный фон на входе в магазин (белый) и на главной маркетплейса
   (бежевый). Бежевым остаётся сам маркетинг — html/body и секции вокруг телефона
   НА ДЕСКТОПЕ; в мобильном app-fullscreen канвас тоже белый (правило ниже). */
.phone-screen {
  position: relative;
  width: 100%;
  height: 100%;
  background: #ffffff;
  border-radius: 34px;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
}

#app-iframe {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  border: none;
  border-radius: 34px;
  transition: opacity 0.4s ease;
}

/* ── Tabbar overlay over the iframe (main page only) ──
   bottom: в WebView приложения мерчанта системный жест-бар прозрачен поверх
   контента (env() там 0) — приложение передаёт его высоту query-параметром
   gd_sab, инлайн-скрипт index.html кладёт в переменную. Формула
   max(14px, sab + 8px) ЗЕРКАЛИТ bottomBarSafeInset бандла (web + host-app) —
   таббар/меню лендинга и корзина/CTA бандла обязаны стоять НА ОДНОМ уровне
   (живой смотр 2026-08-11: «не на одном уровне» при 14+sab против max(14,sab+8)).
   В браузере переменной нет → прежние 14px. */
.app-tabbar {
  position: absolute;
  left: 74px;
  right: 74px;
  bottom: max(14px, calc(var(--gd-sab, 0px) + 8px));
  height: 56px;
  border-radius: 28px;
  background: rgba(223, 223, 223, 0.80);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
  display: none;
  align-items: center;
  padding: 4px;
  z-index: 2;
}

html.app-ready.app-home .app-tabbar {
  display: flex;
}

/* Таббар держит и <button> (Buy), и <a> (Sell → goodiny.ai):
   text-decoration/color гасят дефолты ссылки. */
.app-tab {
  flex: 1 1 0;
  min-width: 0;
  height: 100%;
  display: inline-flex;
  justify-content: center;
  align-items: center;
  gap: 10px;
  padding: 12px 20px;
  border: none;
  background: transparent;
  border-radius: 32px;
  color: #2E2E2E;
  text-decoration: none;
  text-align: center;
  font-family: "SF Pro Text", -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Arial, sans-serif;
  font-size: 16px;
  font-style: normal;
  font-weight: 500;
  line-height: 24px;
  cursor: pointer;
}

.app-tab.is-active {
  background: #2E2E2E;
  color: #FFFFFF;
}

/* ── Menu button: circular 56x56, sits to the right of the tabbar ──
   Bottom bar layout (left → right): 12 edge | 56 reserved-for-future-left-icon
   | 6 gap | tabbar (fills remaining) | 6 gap | 56 menu button | 12 edge */
.app-menu-btn {
  position: absolute;
  right: 12px;
  bottom: max(14px, calc(var(--gd-sab, 0px) + 8px));
  width: 56px;
  height: 56px;
  display: none;
  align-items: center;
  justify-content: center;
  border: none;
  border-radius: 50%;
  background: rgba(223, 223, 223, 0.80);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
  color: #2E2E2E;
  cursor: pointer;
  padding: 0;
  z-index: 2;
}

html.app-ready.app-home .app-menu-btn {
  display: inline-flex;
}

/* Bundle-driven visibility for the Buy/Sell tabbar + menu button. The bundle
   posts EMBED_OVERLAY to flag states where our parent-document overlay would
   otherwise float on top of iframe content it can't be z-indexed below. See
   the EMBED_OVERLAY handler in script.js and docs/iframe-embed-protocol.md.

   "hidden" — in-iframe loading screen: pull the overlay entirely. The extra
   .embed-overlay-hidden class outranks the two display rules above. */
html.embed-overlay-hidden.app-ready.app-home .app-tabbar,
html.embed-overlay-hidden.app-ready.app-home .app-menu-btn {
  display: none;
}

/* "dimmed" — in-app modal open: keep the tabbar + menu in place but lay a gray
   scrim over each so they recede behind the modal's backdrop. The scrim is an
   ::after clipped to each element's rounded shape, so it tints only our
   overlay — not the iframe, which the bundle already dims itself, so there is
   no double-tint seam. The bundle can pass `scrim` to set --embed-scrim and
   match its own backdrop colour exactly; the fallback below is the default. */
.app-tabbar::after,
.app-menu-btn::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: var(--embed-scrim, rgba(18, 18, 18, 0.55));
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.15s ease;
}

html.embed-overlay-dimmed .app-tabbar::after,
html.embed-overlay-dimmed .app-menu-btn::after {
  opacity: 1;
}

/* While dimmed the overlay is decorative only — a tab tap must not open the
   landing's own modal on top of the in-app one. */
html.embed-overlay-dimmed .app-tabbar,
html.embed-overlay-dimmed .app-menu-btn {
  pointer-events: none;
}

/* ── Menu modal: opened on menu-button tap, sits at the bottom of the iframe ── */
.app-menu-modal {
  position: absolute;
  inset: 0;
  display: none;
  align-items: flex-end;
  justify-content: center;
  /* 14px снизу — как у .app-tabbar: единый уровень парения. */
  padding: 0 8px 14px;
  background: rgba(18, 18, 18, 0.70);
  z-index: 3;
}

.app-menu-modal.is-open {
  display: flex;
}

.app-menu-card {
  display: flex;
  width: 100%;
  padding: 16px;
  flex-direction: column;
  gap: 8px;
  border-radius: 12px;
  background: rgba(223, 223, 223, 0.80);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
}

.app-menu-logo {
  width: 116px;
  height: 32px;
  aspect-ratio: 29 / 8;
  align-self: center;
  margin-bottom: 8px;
}

.app-menu-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 56px;
  padding: 0 16px;
  border-radius: 12px;
  background: #F2F2F2;
  color: #000;
  text-decoration: none;
  font-family: "SF Pro Text", -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Arial, sans-serif;
  font-size: 16px;
  font-style: normal;
  font-weight: 400;
  line-height: 24px;
}

.app-menu-item svg {
  flex-shrink: 0;
  color: #2E2E2E;
}

/* Юр-строка оператора под строкой Language в меню-шторке: вторичный мелкий
   текст тем же стеком, что .app-menu-item (шторка мимикрирует под iOS). */
.app-menu-legal {
  padding: 0 8px;
  font-family: "SF Pro Text", -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Arial, sans-serif;
  font-size: 11px;
  line-height: 15px;
  text-align: center;
  color: #6d6d72;
}

/* ── Placeholder inside phone ── */
.app-loader {
  display: flex;
  align-items: center;
  justify-content: center;
}

/* ЗЕРКАЛО: pimmer-3 нативно повторяет этот лоадер на своей заглушке витрины
   (StorefrontWebScreen → _PulsingSymbol: та же иконка favicon.svg, 72px,
   opacity .3↔.8, scale 1↔1.12, 2s ease-in-out) — переход «заглушка приложения
   → лоадер лендинга» бесшовный. Меняешь пульс или иконку здесь —
   синхронизируй там. */
.app-loader-icon {
  width: 72px;
  height: 72px;
  animation: pulse 2s ease-in-out infinite;
}

@keyframes pulse {
  0%, 100% {
    opacity: 0.3;
    transform: scale(1);
  }
  50% {
    opacity: 0.8;
    transform: scale(1.12);
  }
}

/* ── Fade-in animation ── */
.brand-top {
  opacity: 0;
  animation: fadeUp 0.8s ease forwards;
}

.footer {
  opacity: 0;
  animation: fadeUp 0.8s ease 0.3s forwards;
}

@keyframes fadeUp {
  from {
    opacity: 0;
    transform: translateY(30px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* Телефон интро НЕ получает — нигде, включая маркетинговую главную (убрано
   2026-07-16). Фрейм — это сам контент: влёт читался рывком на входе в магазин,
   единственном переходе с настоящей перезагрузкой документа (GOTO_SUBDOMAIN →
   window.location). База .phone-frame обязана оставаться видимой (никакого
   opacity: 0 / fadeUp) — страж: test/frame-intro.test.cjs. */

/* ────────────────────────────────────
   Responsive
   ──────────────────────────────────── */

/* Tablet landscape */
@media (max-width: 1024px) {
  .page {
    padding: 48px 48px;
    gap: 40px;
  }

  .phone-frame {
    width: 280px;
    height: 560px;
    border-radius: 42px;
    padding: 12px 12px 30px;
  }

  .phone-notch {
    top: 12px;
    width: 100px;
    height: 24px;
    border-radius: 0 0 14px 14px;
  }

  .phone-screen {
    border-radius: 32px;
  }

  #app-iframe {
    border-radius: 32px;
  }

  .logo {
    max-width: 360px;
  }

  /* Кап логотипа здесь 360px → кап слогана тем же коэффициентом 8cqi:
     360 × 0.08 = 28.8px = 1.8rem. Иначе слоган рос бы при зафиксированном
     логотипе (колонка 360–480px). */
  .slogan {
    font-size: clamp(1.4rem, 2.4vw, 1.8rem);
    font-size: clamp(1.4rem, 8cqi, 1.8rem);
  }
}

/* Mobile fullscreen mode — JS adds .app-fullscreen on <768px non-bot users;
   bots and desktop users never get this class. */
/* --app-h — истинная высота окна в px, её пишет head-скрипт из `window.innerHeight`
   (см. index.html и docs/viewport-and-keyboard.md). `100dvh` оставлен ФОЛБЭКОМ на
   первый кадр (до первого rAF) и на десктоп, где переменная не ставится вовсе.

   Почему не голый 100dvh: в установленном Android web clip на старте `dvh` ВРЁТ.
   Замер (Chrome WebappActivity, CDP, рекордер внедрён до скриптов страницы):

     t≈73ms   innerHeight=840, но 100dvh резолвится в 896  →  html разложен по 896
     t≈137ms  100dvh становится 840                        →  ПЕРЕРАЗЛОЖЕНИЯ НЕ ПРОИСХОДИТ

   Высота 896 залипает навсегда (проверено 15 с), iframe вырастает выше вьюпорта на
   56px и нижний бар магазина уезжает за экран. Лечится только инвалидацией layout.
   `innerHeight` при этом ВЕРЕН с самого начала — на нём и стоим.

   Это гонка: на медленной загрузке (hard reload) вьюпорт успевает устаканиться до
   раскладки и баг не виден, на быстрой прогретой — виден. Поэтому «у меня не
   воспроизвелось» — не доказательство, что починка не нужна. */
/* Канвас документа в app-режиме — БЕЛЫЙ (вторая половина решения 2026-07-31;
   первая — .phone-screen выше). Chrome edge-to-edge рисует под системной жестовой
   панелью КАНВАС документа (зона ниже var(--app-h), куда фиксированный .phone-side
   не дотягивается); тот же канвас виден на overscroll. На субдомене магазина html
   белит head-скрипт (gd-store-white + inline backgroundColor), на апексе канвас
   оставался бежевым — системная полоса на главной маркетплейса была бежевой, на
   магазине белой. Десктоп-маркетинг не задет: .app-fullscreen там не ставится. */
html.app-fullscreen,
html.app-fullscreen body {
  overflow: hidden;
  overscroll-behavior: none;
  height: var(--app-h, 100dvh);
  background-color: #ffffff;
}

html.app-fullscreen .page {
  padding: 0;
  gap: 0;
}

html.app-fullscreen .brand-side {
  display: none;
}

html.app-fullscreen .phone-side {
  position: fixed;
  inset: 0;
  width: 100vw;
  height: var(--app-h, 100dvh);
  z-index: 50;
}

/* iOS 26 web clip: layout viewport КОРОЧЕ экрана на высоту статус-бара (замер на
   живом web clip 2026-07-16, iPhone 15 Pro iOS 26.3.1: innerHeight = dvh = svh = 793
   при экране 852, lvh = vh = 852, env top 59 / bottom 34) — снизу мёртвая полоса,
   где красится только канвас документа. ↯ НЕ ЧИНИТСЯ высотой: пробовали
   height:100lvh для .phone-side под классом .gd-webclip (v51/v52) — iframe вырастает
   до 852 и бандл раскладывается ниже, но КОМПОЗИТОР клипует контент по нижней границе
   layout viewport: нижние контролы магазина оказались подрезаны, полоса осталась
   (Page.snapshotRect инспектора при этом рендерит всё — ему верить нельзя, только
   глазам на устройстве). Правило откатили: контент в зону 793–852 не дотянуть, клип
   системный. ИТОГОВОЕ лечение (2026-07-16): статус-бар клипа default вместо
   black-translucent (сеттеры в index.html head и script.vN.js) — окно клипа встаёт
   ПОД бар, 793 занимают низ экрана впритык, полосы нет. Ранняя мета capable на
   вьюпорт НЕ влияет (проверено переустановкой клипа), оставлена как гигиена.
   Класс .gd-webclip в html ЖИВ и валиден: детект web clip строго по
   navigator.standalone — @media (display-mode) в web clip iOS 26 лжёт (матчит
   browser, не standalone). */

html.app-fullscreen .phone-frame {
  width: 100%;
  height: 100%;
  background: none;
  border-radius: 0;
  padding: 0;
  box-shadow: none;
}

/* Рамки в fullscreen нет — подпись «живого приложения» прятать вместе с ней. */
html.app-fullscreen .phone-live-hint {
  display: none;
}

html.app-fullscreen .phone-screen,
html.app-fullscreen #app-iframe {
  border-radius: 0;
}

/* На мобильном fullscreen таббар и кнопка меню держат базовые 14px от низа
   БЕЗ добавки safe-area — это КОНТРАКТ с бандлом, а не следствие env:
   у магазина нижние парящие бары на web — ФИКС 14 от низа iframe, env там
   осознанно НЕ участвует (bottomBarSafeInset, rn-pimmer-sdk, решение
   владельца 2026-07-16 «веб выглядит как веб»; старое обоснование «env в
   iframe = 0» опровергнуто замером — в iOS 26 web clip env.bottom = 34,
   из-за чего бары временно всплывали на 48). Оверлей меряет от того же
   края те же 14 — уровни совпадают; добавка инсета (Chrome 135+
   edge-to-edge, home-индикатор iPhone) поднимала Buy/Sell ~24px над
   корзиной приложения. */

/* On mobile fullscreen, modal cards float as a bottom sheet with an 8px
   gap from viewport edges plus the home-indicator safe-area, and a
   darker backdrop so iframe content underneath is more obscured. Card
   keeps its default 12px radius on all corners. Tabbar and menu button
   stay visible behind the backdrop, dimmed by the wrapper background. */
html.app-fullscreen .app-menu-modal {
  padding: 0 8px calc(8px + env(safe-area-inset-bottom));
  background: rgba(0, 0, 0, 0.55);
}

/* ── Internal pages (deep paths or legacy ?p=) ── высота из --app-h, как у
   остальной цепочки (html+body fullscreen, .page, .phone-side); overflow
   дублирует базу как явный гард глубоких ссылок. */
.internal-page body {
  overflow: hidden;
  height: 100vh;
  height: var(--app-h, 100dvh);
}

/* ── Language switcher (EN/ID) — design-system pill-tab-bar, compact ──
   Track = surface-cream-soft, active pill = ink on cream: the same geometry
   vocabulary as the primary CTA (50px pill). Lives in the footer navs
   (Nunito variant) and, as .lang-switch-sheet, inside the app-menu sheet
   over the iframe (SF Pro variant, matches .app-menu-item). */
.lang-switch {
  display: inline-flex;
  align-items: center;
  padding: 3px;
  border-radius: 50px;
  background: #f2ede9;
}

.lang-switch-btn {
  border: none;
  background: transparent;
  border-radius: 50px;
  padding: 6px 14px;
  font-family: "Nunito", sans-serif;
  font-size: 0.85rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  line-height: 1;
  color: #7a7370;
  cursor: pointer;
  transition: color 0.3s ease, background 0.3s ease;
}

.lang-switch-btn:hover {
  color: #2d2d2d;
}

/* RU pill (menu sheet only): pure app-locale indicator — rendered only while
   the frame runs Russian; [hidden] wins over any display we might add later. */
.lang-switch-btn[hidden] {
  display: none;
}

.lang-switch-btn.is-active {
  background: #2d2d2d;
  color: #faf5f3;
}

.lang-switch-btn.is-active:hover {
  color: #faf5f3;
}

/* Sheet variant — harmonizes with the .app-menu-item row it sits in
   (SF Pro, #F2F2F2 row background, overlay chrome — not the brand face). */
.app-menu-lang {
  cursor: default;
}

.lang-switch-sheet {
  background: rgba(0, 0, 0, 0.07);
}

.lang-switch-sheet .lang-switch-btn {
  font-family: "SF Pro Text", -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Arial, sans-serif;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.02em;
  padding: 6px 12px;
  color: #6b6b6b;
}

.lang-switch-sheet .lang-switch-btn:hover {
  color: #000;
}

.lang-switch-sheet .lang-switch-btn.is-active {
  background: #2E2E2E;
  color: #fff;
}

.lang-switch-sheet .lang-switch-btn.is-active:hover {
  color: #fff;
}

/* ─────────────────────────────────────────────────────────────────────────────
   Контур МЕРЧАНТА (S9): {code}.goodiny.app и собственный домен магазина.
   Витрина магазина — не место для бренда маркетплейса (владелец, 04.08: превью
   и левая панель показывали логотип Goodiny, крошку «Goodiny /» и «For merchants»
   на домене мерчанта). Класс merchant-contour ставит инлайн-скрипт <head> ДО
   первого пейнта — иначе элементы успевают мелькнуть перед applyMode.
   Апекс goodiny.app не задет: там это собственная витрина маркетплейса.
   ───────────────────────────────────────────────────────────────────────── */
.merchant-contour .seo-home-link,
.merchant-contour .footer-nav [data-i18n="m_footer_merchants"],
.merchant-contour [data-i18n="menu_merchants"],
.merchant-contour #seo-breadcrumbs > a[href="/"] {
  display: none !important;
}
