/* ============================================================
   ЧАТ С ПЕРСОНОЙ · ревизия 2026-08. Все значения — из tokens.css.
   Лицо персонажа — у каждой его реплики.

   Раскладка: body — колонка на 100dvh; лента растягивается, подвал липкий.
   Клавиатура учитывается через --kb (её пишет рантайм из visualViewport):
   на iOS dvh не реагирует на клавиатуру, поэтому одного dvh мало.
   ============================================================ */

.ch-body{
  position:relative;             /* остров и ввод позиционируются от него */
  display:flex; flex-direction:column;
  height:100dvh; min-height:100dvh;
  overflow:hidden;               /* скроллится только лента */
}
/* На iOS dvh не реагирует на клавиатуру: без этого правила подвал с полем
   ввода уезжает под клавиатуру. --kb пишет рантайм из visualViewport. */
html.kb-open .ch-body{ height:calc(100dvh - var(--kb)); min-height:0; }
/* ============================================================
   ШАПКА-ОСТРОВ

   Была полоса во всю ширину: на телефоне она разъезжалась в четыре этажа
   (display:flex стоял только в блоке от 900px) и съедала 177px экрана.
   Стала плавающая капсула, как в нашем мессенджере и в телеграме после
   августовского обновления: контейнер прозрачный и не ловит нажатия, ловят
   только капсула и круглые кнопки, лента видна под ними.
   ============================================================ */
.ch-island{
  position:absolute; left:0; right:0; top:0; z-index:40;
  display:flex; align-items:flex-start; justify-content:space-between; gap:var(--sp2);
  padding:calc(var(--sp3) + var(--sat)) calc(var(--sp3) + var(--sar)) var(--sp3) calc(var(--sp3) + var(--sal));
  pointer-events:none;
}
.ch-island > *{ pointer-events:auto; }

/* круглая стеклянная кнопка */
.ch-fab{
  position:relative; flex:none;
  width:var(--ctl); height:var(--ctl);
  display:grid; place-items:center;
  border:1px solid var(--hair2); border-radius:var(--r-full);
  background:color-mix(in srgb, var(--s2) 72%, transparent);
  box-shadow:var(--sh-md);
  color:var(--t1); text-decoration:none; cursor:pointer;
  transition:transform var(--d-micro) var(--e-spring-soft), background var(--d-micro) var(--e-effect);
  -webkit-tap-highlight-color:transparent;
}
.ch-fab:active{ transform:scale(.92); }
.ch-fab:focus-visible{ outline:var(--focus-w) solid var(--focus); outline-offset:var(--focus-off); }

/* ═══ СТРОКА ДЕЙСТВИЙ ПОД ШАПКОЙ ═══

   ЭТО НЕ ПАНЕЛЬ, А ДВЕ КНОПКИ. Первая версия была именно панелью: рамка,
   заливка, блюр — и внутри неё ещё одна кнопка во всю ширину. Получалась
   «кнопка в коробке», а когда «В друзья» уходила после добавления, золотая
   «Живая история» растягивалась на семьсот пикселей и превращалась в полосу
   поперёк экрана. Коробка убрана: строка прозрачная, кнопки живут по
   содержимому и стоят по центру, каждая со своим стеклом и тенью — ровно как
   круглые кнопки и капсула в самой шапке.

   ЦВЕТА РАЗВЕДЕНЫ ПО ВЕСУ, А НЕ ТОЛЬКО ПО ТОНУ. «В друзья» — сплошной коралл
   раздела: это главное действие. «Живая история» — стекло с золотой волосяной
   рамкой и золотым текстом: заметно, узнаваемо, но не спорит с первой. Две
   одинаково залитые кнопки рядом читались бы как одно действие с двумя
   названиями, и человек выбирал бы наугад.

   Строка внутри #chIsland (flex-wrap), поэтому ResizeObserver в chat.js
   измеряет её вместе с шапкой и лента отступает сама. */
.ch-island{ flex-wrap:wrap; }

.ch-pin{
  flex:0 0 100%; width:100%;
  margin-top:var(--sp2);
  display:flex; align-items:center; justify-content:center;
  flex-wrap:wrap; gap:var(--sp2) var(--sp2);   /* три кнопки на узком телефоне переносятся, а не жмутся в кашу */
  padding:0; border:0; background:none; box-shadow:none;
  animation:ch-pin-in var(--d-spring, .32s) var(--e-out) both;
}
.ch-pin[hidden]{ display:none; }
@keyframes ch-pin-in{
  from{ opacity:0; transform:translateY(-6px); }
  to{ opacity:1; transform:none; }
}
@media (prefers-reduced-motion: reduce){ .ch-pin{ animation:none; } }

/* flex:0 1 auto — ширина по тексту. Кнопка не должна расти на весь экран
   оттого, что соседка ушла: одинокая полоса во всю ширину читается как баннер,
   а не как действие. */
.ch-pin-btn{
  flex:0 1 auto; min-width:0; max-width:min(50%, 260px); cursor:pointer;
  display:inline-flex; align-items:center; justify-content:center; gap:var(--sp2);
  height:34px; padding:0 var(--sp4);
  border:0; border-radius:var(--r-full);
  font:700 var(--fs-small)/1 var(--f-ui);
  text-decoration:none; white-space:nowrap;
  box-shadow:var(--sh-md);
  transition:transform var(--d-micro) var(--e-spring-soft), background var(--d-micro) var(--e-effect);
  -webkit-tap-highlight-color:transparent;
}
.ch-pin-btn[hidden]{ display:none; }
.ch-pin-btn:active{ transform:scale(.95); }
.ch-pin-btn:focus-visible{ outline:var(--focus-w) solid var(--focus); outline-offset:var(--focus-off); }
.ch-pin-btn .pv-ic{ width:15px; height:15px; flex:none; }
.ch-pin-btn span{ overflow:hidden; text-overflow:ellipsis; }

.ch-pin-btn.fr{ background:var(--hero); color:#fff; }
.ch-pin-btn.fr:hover{ background:var(--hero-hi); }
.ch-pin-btn.fr svg{ fill:currentColor; }

/* ═══ «НЕ ПРОПУСТИТЬ» — ВТОРАЯ КНОПКА СТРОКИ ═══
   (разметку дорисовывает persona/assets/nudge.js, 2026-08-22)

   БЫЛО: два призрачных кружка 28×28 в углу каждой реплики персонажа —
   колокольчик и стрелка вниз. Значок без подписи ничего не обещает: человек
   видел украшение пузыря и листал мимо. А обещание за ними одно и крупное —
   «персонаж напишет сам, даже когда вкладка закрыта».

   СТАЛО: одна кнопка рядом с «В друзья», потому что и уведомления, и
   приложение отвечают на один вопрос — дойдёт ли до человека то, что
   персонаж написал в его отсутствие. Две кнопки на одну тему в строке из
   трёх — это выбор без разницы; человек встаёт перед ним и не нажимает ни
   одну. Внутри шторки они уже разные вещи: тумблер и установка, и там на
   объяснение есть целый экран.

   ПОДПИСЬ ОБЕЩАЕТ РЕЗУЛЬТАТ, А НЕ НАЗЫВАЕТ ТЕХНОЛОГИЮ. «Уведомления»
   читается как строка настроек, мимо которой проходят; «Не пропустить» —
   как обещание, ради которого нажимают.

   ЖИВЁТ РОВНО ПОКА ЕСТЬ ЧТО ПРЕДЛОЖИТЬ. Включил уведомления — в шторке
   остаётся установка; поставил приложение — кнопка уходит из строки совсем;
   добавил в друзья — уходит и строка. Интерфейс кончается вместе с задачей,
   а не висит выключенным напоминанием о том, что уже сделано.

   «Не сейчас» кнопку НЕ убирает: это «не в эту секунду», а не «никогда», и
   другого входа в уведомления из чата нет. Тише её делает погасшая точка.

   Цвет: стекло с золотой волосяной рамкой. «В друзья» — сплошной коралл,
   главное действие раздела; две одинаково залитые кнопки рядом читались бы
   как одно действие с двумя названиями. */
.ch-pin-btn.notify{
  background:color-mix(in srgb, var(--s2) 72%, transparent);
  backdrop-filter:blur(12px); -webkit-backdrop-filter:blur(12px);
  color:var(--mark);
  box-shadow:var(--sh-md), inset 0 0 0 1px color-mix(in srgb, var(--mark) 38%, transparent);
}
.ch-pin-btn.notify:hover{ background:color-mix(in srgb, var(--mark) 14%, var(--s2)); }

/* Точка-пульс: единственное «новое» в строке. Гаснет после первого открытия
   шторки — она звала прочитать, а не нажать. Сама кнопка при этом остаётся:
   «не сейчас» закрывает окно, а не предложение. */
.ch-pin-btn .ch-pin-dot{
  width:7px; height:7px; flex:none; border-radius:50%;
  background:var(--mark);
  animation:chPinPulse 1.9s var(--e-out) infinite;
}
@keyframes chPinPulse{
  0%,100%{ transform:scale(1); opacity:1; }
  50%{ transform:scale(1.45); opacity:.5; }
}
@media (prefers-reduced-motion: reduce){ .ch-pin-btn .ch-pin-dot{ animation:none; } }

/* Крестик — того же стекла, что круглые кнопки шапки. Показывается только
   вместе с «В друзья»: когда предлагать нечего, прятать тоже нечего. */
.ch-pin-x{
  flex:none; display:grid; place-items:center;
  width:30px; height:30px; padding:0; cursor:pointer;
  border:1px solid var(--hair2); border-radius:var(--r-full);
  background:color-mix(in srgb, var(--s2) 72%, transparent);
  backdrop-filter:blur(12px); -webkit-backdrop-filter:blur(12px);
  color:var(--t3); box-shadow:var(--sh-md);
}
.ch-pin-x[hidden]{ display:none; }
.ch-pin-x:hover{ color:var(--t1); }
.ch-pin-x:focus-visible{ outline:var(--focus-w) solid var(--focus); outline-offset:var(--focus-off); }

/* Узкий экран: режем поля, а не подписи. «Живая ис…» перестаёт объяснять себя. */
@media (max-width: 360px){
  .ch-pin-btn{ padding-inline:var(--sp3); gap:var(--sp1); font-size:12px; max-width:none; }
  .ch-pin-x{ width:28px; height:28px; }
}

/* капсула с собеседником */
.ch-pill{
  position:relative; overflow:hidden;
  display:flex; align-items:center; gap:var(--sp2);
  min-width:0; max-width:min(62%, 420px);
  padding:var(--sp1) var(--sp2) var(--sp1) var(--sp1);
  border:1px solid var(--hair2); border-radius:var(--r-full);
  background:color-mix(in srgb, var(--s2) 72%, transparent);
  box-shadow:var(--sh-md);
}
.ch-pill-info{
  display:flex; align-items:center; gap:var(--sp2);
  min-width:0; flex:1; text-decoration:none; color:inherit;
}
.ch-pill-info .grow{ min-width:0; display:block; }
.ch-pill-name{
  display:block; font:700 var(--fs-small)/1.2 var(--f-serif); color:var(--t1);
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
.ch-pill-st{
  display:block; margin-top:1px; color:var(--t3);
  font:500 var(--fs-over)/1.2 var(--f-ui);
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
/* «печатает…» — тот же индикатор, что в мессенджерах: статус, а не всплывашка */
.ch-pill-st.live{ color:var(--hero); }

/* Полоса близости — нижняя кромка капсулы. Раньше это была отдельная строка
   в две точки высотой под всей шапкой; ради двух пикселей она занимала место
   и мешала ленте проходить под островом. Ширину пишет скрипт. */
.ch-pill-bar{
  position:absolute; left:0; bottom:0; height:2px;
  width:var(--lvl, 0%); max-width:100%;
  background:var(--hero); border-radius:var(--r-full);
  transition:width var(--d-spring) var(--e-out);
}

.ch-scroll{
  flex:1; min-height:0;
  overflow-y:auto; overscroll-behavior:contain;
  -webkit-overflow-scrolling:touch;
  /* Лента прижата к низу, к полю ввода. В начале разговора одна реплика
     висела вверху, а под ней зияло полэкрана пустоты — чат выглядел пустым.
     margin-top:auto у содержимого работает в обе стороны: когда сообщений
     много, он обнуляется сам и лента скроллится как обычно. */
  display:flex; flex-direction:column;
}
.ch-main{ margin-top:auto; width:100%; }

.ch-feed{
  max-width:var(--col); margin:0 auto;
  /* Высоты острова и ввода меряет скрипт: они зависят от длины имени,
     количества подсказок, плашек и безопасных зон. Захардкоженные значения
     здесь уже приводили к тому, что панель наезжала на содержимое. */
  padding:calc(var(--ch-top, 68px) + var(--sp4)) var(--sp4) calc(var(--ch-bot, 96px) + var(--sp4));
  padding-left:calc(var(--sp4) + var(--sal));
  padding-right:calc(var(--sp4) + var(--sar));
  display:flex; flex-direction:column; gap:var(--sp4);
}
.ch-boot{ display:flex; flex-direction:column; gap:var(--sp4); }

/* уровень близости — чип внутри капсулы, как «ещё» в нашем мессенджере */
.ch-lvl{
  position:relative; flex:none;
  display:inline-flex; align-items:center; gap:var(--sp1);
  height:var(--ctl-xs); padding:0 var(--sp2);
  background:var(--s3); border:1px solid var(--hair); border-radius:var(--r-full);
  color:var(--t2); cursor:pointer;
  font:600 var(--fs-over)/1 var(--f-ui);
}
/* визуально 28px, касаемо — 44 (WCAG 2.2 SC 2.5.8) */
.ch-lvl::after{ content:''; position:absolute; left:50%; top:50%; width:var(--ctl); height:var(--ctl); transform:translate(-50%,-50%); }
.ch-lvl .pv-ic{ width:13px; height:13px; }
.ch-lvl .pv-num{ color:var(--t1); }
.ch-lvl.up{ animation:chLvlUp var(--d-spring) var(--e-bounce); border-color:var(--mark); color:var(--mark); }
@keyframes chLvlUp{ from{ transform:scale(1); } 40%{ transform:scale(1.18); } to{ transform:scale(1); } }

/* дата-разделитель */
.ch-day{
  align-self:center; color:var(--t3);
  font:600 var(--fs-over)/1 var(--f-ui);
  letter-spacing:var(--ls-over); text-transform:uppercase;
  padding:var(--sp2) 0;
}

/* ---------- реплика ---------- */
.ch-msg{ display:flex; gap:var(--sp3); align-items:flex-end; }
.ch-msg.me{ flex-direction:row-reverse; }
.ch-msg > .pv-mono{ margin-bottom:var(--sp1); }
/* Лицо теперь есть у ОБЕИХ сторон, поэтому запас под аватар одинаковый:
   раньше у своих реплик стояло 88% — там аватара просто не было. */
.ch-col{ min-width:0; max-width:calc(100% - var(--mono-sm) - var(--sp3)); }
.ch-msg.me .ch-col{ display:flex; flex-direction:column; align-items:flex-end; }
/* монограмма внутри чипа реакции живёт по своим правилам */
.ch-react .pv-mono{ margin:0; }
.ch-bubble{
  padding:var(--sp3) var(--sp4);
  border-radius:var(--r-md);
  font-size:var(--fs-body); line-height:var(--lh-body);
  white-space:pre-wrap; overflow-wrap:anywhere; text-wrap:pretty;
  /* текст реплики должен выделяться и копироваться — это не кнопка */
  user-select:text; -webkit-user-select:text;
}
.ch-msg .ch-bubble{
  background:var(--s2); border:1px solid var(--hair);
  border-bottom-left-radius:var(--r-xs); color:var(--t1);
}
.ch-msg.me .ch-bubble{
  background:var(--hero-lo); border:1px solid var(--hair);
  border-bottom-right-radius:var(--r-xs); color:var(--t1);
}
.ch-msg-meta{
  display:flex; align-items:center; gap:var(--sp2);
  margin-top:var(--sp1); color:var(--t3); font-size:var(--fs-over);
}
.ch-msg.me .ch-msg-meta{ justify-content:flex-end; }
.ch-name{
  font:600 var(--fs-small)/1 var(--f-serif); color:var(--t2);
  margin-bottom:var(--sp1);
}
/* Своё имя — той же строкой, но по правому краю: подпись обязана стоять над
   своим пузырём, иначе две колонки разговора перестают читаться как две. */
.ch-msg.me .ch-name{ text-align:right; }

/* состояния реплики человека: отправляется / не отправилось */
.ch-msg[data-state="pending"] .ch-bubble{ opacity:.55; }
.ch-msg[data-state="failed"] .ch-bubble{ border-color:var(--lose); }
.ch-fail{
  display:flex; align-items:center; gap:var(--sp2);
  margin-top:var(--sp2); color:var(--lose); font-size:var(--fs-small);
}
.ch-fail button{
  min-height:var(--ctl-xs); padding:0 var(--sp3);
  background:var(--s3); border:1px solid var(--hair2); border-radius:var(--r-full);
  color:var(--t1); font:600 var(--fs-over)/1 var(--f-ui);
  letter-spacing:var(--ls-over); text-transform:uppercase; cursor:pointer;
  position:relative;
}
.ch-fail button::after{ content:''; position:absolute; left:0; right:0; top:50%; height:var(--ctl); transform:translateY(-50%); }

/* закреплено в памяти */
.ch-msg[data-pinned="1"] .ch-bubble{ border-color:var(--mark); }
.ch-pin-flag{ display:inline-flex; align-items:center; gap:var(--sp1); color:var(--mark); }
.ch-pin-flag .pv-ic{ width:12px; height:12px; }

/* ---------- отклик на удержание ----------
   Пузырь проседает, пока палец лежит на нём. Это обещание: сейчас что-то
   произойдёт, не отпускай. Без него человек снимает палец на трёхсотой
   миллисекунде, решив, что жест не работает, — и меню не открывается
   никогда. */
.ch-msg.ch-press .ch-bubble{
  transform:scale(.975);
  transition:transform 220ms var(--e-out);
}
.ch-bubble{ transition:transform var(--d-micro) var(--e-spring-soft); }

/* На тач-устройствах долгое нажатие принадлежит нам, а не системе: без
   этого iOS показывает лупу и «Скопировать / Выделить» поверх нашего меню.
   Текст всё равно копируется — кнопкой «Копия» в самом меню.
   На мыши выделение оставляем: там долгое нажатие никому не нужно. */
@media (hover: none){
  .ch-bubble{
    -webkit-touch-callout:none;
    user-select:none; -webkit-user-select:none;
  }
}

/* появление */
.ch-in{ opacity:0; transform:translateY(var(--sp2)); }
.ch-in.in{
  opacity:1; transform:translateY(0);
  transition:opacity var(--d-std) var(--e-effect), transform var(--d-spring) var(--e-spring);
}

/* печатает */
.ch-typing{ display:flex; gap:var(--sp3); align-items:flex-end; }
.ch-typing .ch-bubble{
  display:flex; gap:var(--sp2); align-items:center; min-height:var(--sp6);
  background:var(--s2); border:1px solid var(--hair);
  border-bottom-left-radius:var(--r-xs);
}
.ch-typing i{
  width:var(--sp2); height:var(--sp2); border-radius:var(--r-full);
  background:var(--t3); opacity:.4;
  animation:chDot 1200ms var(--e-out) infinite;
}
.ch-typing i:nth-child(2){ animation-delay:160ms; }
.ch-typing i:nth-child(3){ animation-delay:320ms; }
@keyframes chDot{ 0%,100%{ opacity:.25; transform:translateY(0); } 40%{ opacity:.9; transform:translateY(-2px); } }

/* ============================================================
   РЕАКЦИИ ПОД ПУЗЫРЁМ

   Реакций на реплике максимум две — своя и персоны, — поэтому здесь не
   счётчики, как в групповом чате, а лица: сразу видно, КТО чем ответил.
   Чип «наезжает» на нижнюю кромку пузыря отрицательным отступом: так он
   читается как часть реплики, а не как отдельная строка под ней.
   ============================================================ */
.ch-reacts{
  display:flex; flex-wrap:wrap; gap:var(--sp1);
  margin-top:calc(var(--sp2) * -1 - 2px);
  padding-left:var(--sp3);
  position:relative; z-index:1;
}
.ch-msg.me .ch-reacts{ justify-content:flex-end; padding-left:0; padding-right:var(--sp3); }
.ch-react{
  position:relative;
  display:inline-flex; align-items:center; gap:var(--sp1);
  height:26px; padding:0 var(--sp1) 0 var(--sp2);
  background:var(--s3); border:1px solid var(--hair2);
  border-radius:var(--r-full);
  cursor:pointer; touch-action:manipulation; -webkit-tap-highlight-color:transparent;
  transition:transform var(--d-micro) var(--e-spring-soft), border-color var(--d-micro) var(--e-effect);
}
/* визуально 26px, касаемо — 44px по WCAG 2.2 SC 2.5.8 */
.ch-react::after{ content:''; position:absolute; left:0; right:0; top:50%; height:var(--ctl); transform:translateY(-50%); }
.ch-react .e{ font-size:15px; line-height:1; }
.ch-react .pv-mono.xs{ width:18px; height:18px; font-size:9px; border-width:1px; }
.ch-react.mine{ background:var(--hero-lo); border-color:var(--hero-line); }
.ch-react:active{ transform:scale(.9); }
.ch-react:focus-visible{ outline:var(--focus-w) solid var(--focus); outline-offset:var(--focus-off); }
/* появление новой реакции: короткий «поп», а не плавное проявление —
   реакция должна ощущаться как удар, иначе её не замечают */
.ch-react.pop{ animation:chReactPop 480ms var(--e-bounce); }
@keyframes chReactPop{
  0%{ transform:scale(.2) rotate(-12deg); opacity:0; }
  55%{ transform:scale(1.22) rotate(4deg); opacity:1; }
  100%{ transform:scale(1) rotate(0); }
}

/* ============================================================
   ПОДНЯТАЯ РЕПЛИКА

   Лента уходит под матовое стекло, выбранная реплика остаётся резкой и
   всплывает ровно с того места, где лежала. Ряд реакций — над ней, меню —
   под ней: палец приходит снизу, и опасные пункты не должны оказаться
   там, где их задевают вслепую.
   ============================================================ */
html.ch-focused, html.ch-picking{ overflow:hidden; }

.ch-focus{ position:fixed; inset:0; z-index:120; }
.ch-focus-veil{
  position:absolute; inset:0;
  background:var(--overlay);
  opacity:0; transition:opacity var(--d-std) var(--e-effect);
  /* размытие — то, что отделяет «выбранное» от «всего остального».
     Там, где его нет, спасает сама подложка: она достаточно плотная. */
  -webkit-backdrop-filter:blur(18px) saturate(120%);
  backdrop-filter:blur(18px) saturate(120%);
}
.ch-focus.on .ch-focus-veil{ opacity:1; }
.ch-focus.out .ch-focus-veil{ opacity:0; }

.ch-focus-stage{
  position:absolute;
  display:flex; flex-direction:column; align-items:flex-start;
  max-width:calc(100vw - 24px);
}
.ch-focus-stage.me{ align-items:flex-end; }

.ch-focus-msg{
  opacity:0;
  transform:translateY(var(--dy, 0)) scale(.96);
  transform-origin:bottom left;
  transition:opacity var(--d-micro) var(--e-effect), transform var(--d-spring) var(--e-spring);
  width:100%;
  border-radius:var(--r-md);
}
.ch-focus-stage.me .ch-focus-msg{ transform-origin:bottom right; }
.ch-focus.on .ch-focus-msg{ opacity:1; transform:translateY(0) scale(1); }
.ch-focus.out .ch-focus-msg{ opacity:0; transform:scale(.97); transition-duration:var(--d-exit-micro); }
/* внутри клона галочка и переключатель вариантов не нужны — их удаляет
   скрипт; здесь только снимаем анимацию появления, чтобы клон не мигал */
.ch-focus-msg .ch-msg{ opacity:1; transform:none; }
.ch-focus-msg .ch-bubble{ box-shadow:var(--sh-lg); }

/* ---------- ряд реакций ---------- */
.ch-reactbar{
  display:flex; align-items:center; gap:2px;
  margin-bottom:10px; padding:var(--sp1) var(--sp2);
  max-width:100%; overflow-x:auto; scrollbar-width:none;
  background:color-mix(in srgb, var(--s3) 86%, transparent);
  -webkit-backdrop-filter:blur(24px) saturate(160%);
  backdrop-filter:blur(24px) saturate(160%);
  border:1px solid var(--hair2); border-radius:var(--r-full);
  box-shadow:var(--sh-md);
  opacity:0; transform:translateY(6px) scale(.9);
  transform-origin:bottom left;
  transition:opacity var(--d-micro) var(--e-effect), transform var(--d-spring) var(--e-spring);
}
.ch-reactbar::-webkit-scrollbar{ display:none; }
.ch-focus-stage.me .ch-reactbar{ transform-origin:bottom right; }
.ch-focus.on .ch-reactbar{ opacity:1; transform:translateY(0) scale(1); }
.ch-focus.out .ch-reactbar{ opacity:0; transform:scale(.92); transition-duration:var(--d-exit-micro); }

.ch-remoji{
  flex:none; position:relative;
  width:var(--ctl); height:var(--ctl);
  display:grid; place-items:center;
  background:transparent; border:0; border-radius:var(--r-full);
  font-size:24px; line-height:1; cursor:pointer;
  color:var(--t2);
  -webkit-tap-highlight-color:transparent; touch-action:manipulation;
  /* каждое эмодзи всплывает на пару кадров позже предыдущего: ряд читается
     слева направо, и глаз успевает пройти по нему вместе с анимацией */
  opacity:0; transform:scale(.3);
  transition:opacity 160ms var(--e-out), transform var(--d-spring) var(--e-bounce), background var(--d-micro) var(--e-effect);
  transition-delay:calc(var(--i, 0) * 26ms);
}
.ch-focus.on .ch-remoji{ opacity:1; transform:scale(1); }
.ch-remoji:active{ transform:scale(1.28); transition-duration:var(--d-micro); transition-delay:0s; }
.ch-remoji:focus-visible{ outline:var(--focus-w) solid var(--focus); outline-offset:calc(var(--focus-off) * -1); }
/* уже поставленная реакция — подсвечена, чтобы повторный тап читался как «снять» */
.ch-remoji.on{ background:var(--hero-lo); box-shadow:inset 0 0 0 1px var(--hero-line); }
.ch-remoji.more{ font-size:0; color:var(--t2); }
.ch-remoji.more .pv-ic{ width:18px; height:18px; }
/* второй ряд разворачивается на месте — без ещё одного слоя поверх слоя */
.ch-remoji.fresh{ animation:chEmojiIn var(--d-spring) var(--e-bounce) backwards; animation-delay:calc(var(--i, 0) * 26ms); }
@keyframes chEmojiIn{ from{ opacity:0; transform:scale(.3); } to{ opacity:1; transform:scale(1); } }
.ch-reactbar.wide{ max-width:calc(100vw - 24px); }

/* ---------- меню действий ----------

   Меню лежит ПОЛОСОЙ, слева направо, как ряд реакций над репликой. Так у
   поднятого сообщения получается одна форма: две капсулы одинаковой длины,
   сверху эмоции, снизу действия, — а не капсула и приклеенный к ней столбик.

   Вертикальный список к тому же уводил палец вниз, к самому опасному пункту:
   в столбце «вернуть разговор сюда» оказывалось ровно под большим пальцем.
   В строке он крайний справа — туда не попадают вслепую. */
.ch-fmenu{
  margin-top:10px;
  display:flex; align-items:stretch; gap:0;
  max-width:calc(100vw - 24px);
  overflow-x:auto; scrollbar-width:none;
  padding:var(--sp1);
  background:color-mix(in srgb, var(--s3) 88%, transparent);
  -webkit-backdrop-filter:blur(24px) saturate(160%);
  backdrop-filter:blur(24px) saturate(160%);
  border:1px solid var(--hair2); border-radius:var(--r-md);
  box-shadow:var(--sh-lg);
  opacity:0; transform:translateY(-8px) scale(.94);
  transform-origin:top left;
  transition:opacity var(--d-micro) var(--e-effect), transform var(--d-spring) var(--e-spring);
  transition-delay:40ms;
}
.ch-fmenu::-webkit-scrollbar{ display:none; }
.ch-focus-stage.me .ch-fmenu{ transform-origin:top right; }
.ch-focus.on .ch-fmenu{ opacity:1; transform:translateY(0) scale(1); }
.ch-focus.out .ch-fmenu{ opacity:0; transform:scale(.96); transition-duration:var(--d-exit-micro); transition-delay:0s; }

.ch-fmenu-row{
  flex:1 0 auto; min-width:66px; max-width:96px;
  min-height:60px;
  display:flex; flex-direction:column; align-items:center; justify-content:center;
  gap:5px; padding:var(--sp2) var(--sp1);
  background:transparent; border:0; border-radius:var(--r-sm);
  color:var(--t1); text-align:center;
  font:600 var(--fs-over)/1.15 var(--f-ui);
  letter-spacing:.02em;
  cursor:pointer; -webkit-tap-highlight-color:transparent;
  /* подписи короткие, но «Запомнить» на узком экране всё же переносится —
     пусть переносится по-человечески, а не обрывается многоточием */
  overflow-wrap:anywhere;
  transition:background var(--d-micro) var(--e-effect), transform var(--d-micro) var(--e-spring-soft);
  /* пункты проявляются по очереди, как эмодзи в ряду выше */
  animation:chMenuIn var(--d-std) var(--e-out) backwards;
  animation-delay:calc(60ms + var(--i, 0) * 24ms);
}
@keyframes chMenuIn{ from{ opacity:0; transform:translateY(4px); } to{ opacity:1; transform:none; } }
/* разделители теперь вертикальные — полоса читается как ряд, а не как стопка */
.ch-fmenu-row + .ch-fmenu-row{ box-shadow:inset 1px 0 0 var(--hair); }
.ch-fmenu-row:active{ background:var(--s4); transform:scale(.94); }
.ch-fmenu-row:focus-visible{ outline:var(--focus-w) solid var(--focus); outline-offset:calc(var(--focus-off) * -1); }
.ch-fmenu-row .pv-ic{ flex:none; width:19px; height:19px; color:var(--t2); }
.ch-fmenu-row.on{ color:var(--mark); }
.ch-fmenu-row.on .pv-ic{ color:var(--mark); }
.ch-fmenu-row.danger{ color:var(--lose); }
.ch-fmenu-row.danger .pv-ic{ color:var(--lose); }

/* ============================================================
   ВЫБОР РЕПЛИК ДЛЯ КАРТОЧКИ
   ============================================================ */
.ch-msg{ position:relative; }
.ch-tick{
  position:absolute; left:0; top:50%;
  width:22px; height:22px; margin-top:-11px;
  display:none; place-items:center;
  border:1.5px solid var(--hair2); border-radius:var(--r-full);
  background:var(--s1); color:var(--on-hero);
  transition:background var(--d-micro) var(--e-effect), border-color var(--d-micro) var(--e-effect), transform var(--d-micro) var(--e-spring-soft);
}
.ch-tick .pv-ic{ width:13px; height:13px; opacity:0; transition:opacity var(--d-micro) var(--e-effect); }
html.ch-picking .ch-tick{ display:grid; animation:chTickIn var(--d-std) var(--e-spring); }
@keyframes chTickIn{ from{ opacity:0; transform:scale(.4); } to{ opacity:1; transform:scale(1); } }
html.ch-picking .ch-msg{ padding-left:32px; cursor:pointer; }
html.ch-picking .ch-msg[data-picked="1"] .ch-tick{
  background:var(--hero); border-color:var(--hero); transform:scale(1.06);
}
html.ch-picking .ch-msg[data-picked="1"] .ch-tick .pv-ic{ opacity:1; }
html.ch-picking .ch-msg[data-picked="1"] .ch-bubble{ border-color:var(--hero-line); }
/* в режиме выбора всё лишнее гасим: человек решает одну задачу */
html.ch-picking .ch-reacts,
html.ch-picking .ch-variants,
html.ch-picking .ch-jump{ opacity:.35; pointer-events:none; }
html.ch-picking .ch-foot{ opacity:0; pointer-events:none; }

.ch-pickbar{
  position:fixed; z-index:110;
  left:var(--sp4); right:var(--sp4);
  bottom:calc(var(--sp4) + var(--sab));
  max-width:var(--col); margin:0 auto;
  display:flex; align-items:center; gap:var(--sp2);
  padding:var(--sp2);
  background:color-mix(in srgb, var(--s3) 88%, transparent);
  -webkit-backdrop-filter:blur(24px) saturate(160%);
  backdrop-filter:blur(24px) saturate(160%);
  border:1px solid var(--hair2); border-radius:var(--r-full);
  box-shadow:var(--sh-lg);
  opacity:0; transform:translateY(12px);
  transition:opacity var(--d-std) var(--e-effect), transform var(--d-spring) var(--e-spring);
}
.ch-pickbar.on{ opacity:1; transform:translateY(0); }
.ch-pick-n{
  flex:1; min-width:0; text-align:center;
  color:var(--t2); font:600 var(--fs-over)/1 var(--f-ui);
  letter-spacing:var(--ls-over); text-transform:uppercase;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
.ch-pick-x, .ch-pick-go{
  flex:none; min-height:var(--ctl-vis); padding:0 var(--sp3);
  display:inline-flex; align-items:center; gap:var(--sp2);
  border-radius:var(--r-full); cursor:pointer;
  font:600 var(--fs-over)/1 var(--f-ui);
  letter-spacing:var(--ls-over); text-transform:uppercase;
  -webkit-tap-highlight-color:transparent;
  transition:transform var(--d-micro) var(--e-spring-soft), opacity var(--d-micro) var(--e-effect);
}
.ch-pick-x{ background:transparent; border:1px solid var(--hair2); color:var(--t2); }
.ch-pick-go{ background:var(--hero); border:1px solid var(--hero); color:var(--on-hero); opacity:.45; pointer-events:none; }
.ch-pickbar.ready .ch-pick-go{ opacity:1; pointer-events:auto; }
.ch-pick-x:active, .ch-pick-go:active{ transform:scale(.94); }
.ch-pick-x:focus-visible, .ch-pick-go:focus-visible{ outline:var(--focus-w) solid var(--focus); outline-offset:var(--focus-off); }
.ch-pick-x .pv-ic, .ch-pick-go .pv-ic{ width:14px; height:14px; }

/* варианты «сказать иначе» */
.ch-variants{ display:flex; gap:var(--sp2); margin-top:var(--sp2); align-items:center; }
.ch-variants .lbl{ font:600 var(--fs-over)/1 var(--f-ui); letter-spacing:var(--ls-over); text-transform:uppercase; color:var(--t3); }
.ch-variant{
  position:relative;
  width:var(--sp6); height:var(--sp6); display:grid; place-items:center;
  border-radius:var(--r-full); cursor:pointer;
  background:var(--s2); border:1px solid var(--hair);
  color:var(--t3); font:600 var(--fs-over)/1 var(--f-num);
  touch-action:manipulation;
}
.ch-variant::after{ content:''; position:absolute; left:50%; top:50%; width:var(--ctl); height:var(--ctl); transform:translate(-50%,-50%); }
.ch-variant.on{ background:var(--s4); color:var(--t1); border-color:var(--hair3); }
.ch-variant:focus-visible{ outline:var(--focus-w) solid var(--focus); outline-offset:var(--focus-off); }

/* ---------- веха и новый уровень ---------- */
.ch-milestone{
  align-self:center; text-align:center; max-width:88%;
  padding:var(--sp4) var(--sp6);
  border:1px solid var(--hair2); border-radius:var(--r-md);
  background:var(--s1);
}
.ch-milestone .pv-over{ display:block; margin-bottom:var(--sp2); color:var(--mark); }
.ch-milestone b{ font:600 var(--fs-title)/var(--lh-tight) var(--f-serif); color:var(--t1); display:block; }
.ch-milestone p{ margin:var(--sp2) 0 0; color:var(--t2); font-size:var(--fs-small); }

/* ---------- «вниз, есть новое» ---------- */
.ch-jump{
  position:fixed; left:50%; z-index:35;
  bottom:calc(var(--sp8) + var(--sp6) + var(--sab) + var(--kb));
  transform:translateX(-50%);
  display:inline-flex; align-items:center; gap:var(--sp2);
  min-height:var(--ctl); padding:0 var(--sp5);
  background:var(--s4); color:var(--t1);
  border:1px solid var(--hair2); border-radius:var(--r-full);
  font:600 var(--fs-small)/1 var(--f-ui); cursor:pointer;
  touch-action:manipulation;
}
.ch-jump[hidden]{ display:none; }
.ch-jump:active{ transform:translateX(-50%) scale(.96); }

/* ---------- низ: уведомления + подсказки + ввод ---------- */
/* Низ парит над лентой, как в телеграме: фона у него нет, есть только
   стеклянная капсула ввода, а сообщения проходят под ней. */
.ch-foot{
  position:absolute; left:0; right:0; bottom:0; z-index:30;
  padding:0 var(--sp3) calc(var(--sp3) + var(--sab));
  padding-left:calc(var(--sp3) + var(--sal));
  padding-right:calc(var(--sp3) + var(--sar));
  pointer-events:none;
}
html.kb-open .ch-foot{ padding-bottom:var(--sp3); }
.ch-foot-inner{ max-width:var(--col); margin:0 auto; pointer-events:auto; }

/* ============================================================
   МЕДЛЕННЫЙ РЕЖИМ БЕСПЛАТНОГО ТАРИФА

   Одна компактная строка над полем ввода — не карточка. Она висит до конца
   суток, поэтому не имеет права занимать экран: разговор важнее объявления.

   Внутри три вещи и ничего больше: кольцо с секундами, две строки текста и
   короткая кнопка. Две строки — необходимый минимум: первая называет саму
   паузу и её длину, вторая — причину. Без второй «Ускорить» повисает в
   воздухе, человек не понимает, что именно замедлилось.

   Кнопка — обычная .pv-btn.sm из ui.css. Своего здесь только кольцо: такого
   в системе не было.

   Два состояния:
     обычное — пауза идёт, в кольце тикают секунды, поле ввода заблокировано;
     .idle   — пауза вышла, писать можно, вместо кольца часы.
   ============================================================ */
.ch-slow{
  display:flex; align-items:center; gap:var(--sp3);
  padding:var(--sp2) var(--sp2) var(--sp2) var(--sp3);
  margin-bottom:var(--sp2);
  border:1px solid var(--hair2); border-radius:var(--r-md); background:var(--s1);
}
.ch-slow[hidden]{ display:none; }
.ch-slow .grow{ flex:1; min-width:0; }
.ch-slow b{ display:block; font:600 var(--fs-small)/1.25 var(--f-ui); color:var(--t1); }
.ch-slow i{
  display:block; font-style:normal; margin-top:2px;
  font-size:var(--fs-small); line-height:1.3; color:var(--t3);
}
.ch-slow .pv-btn{ flex:none; }
/* Ради одной строки кнопка уже обычной: глагол короткий, воздух не нужен. */
.ch-slow .pv-btn.sm{ padding:0 var(--sp3); }

/* Кольцо: дорожка и акцентная дуга, которая укорачивается с каждой секундой. */
.ch-slow-ring{ position:relative; width:26px; height:26px; flex:none; display:block; }
.ch-slow-ring svg{ display:block; transform:rotate(-90deg); }
.ch-slow-ring circle{ fill:none; stroke-width:2.5; }
.ch-slow-ring .tr{ stroke:var(--hair2); }
.ch-slow-ring .fg{
  stroke:var(--hero); stroke-linecap:round;
  transition:stroke-dashoffset var(--d-std) linear;
}
.ch-slow-ring i{
  position:absolute; inset:0; display:grid; place-items:center; margin:0;
  font:600 11px/1 var(--f-num); color:var(--t1); font-variant-numeric:tabular-nums;
}
@media (prefers-reduced-motion:reduce){ .ch-slow-ring .fg{ transition:none; } }

/* Пауза вышла — на месте кольца часы, они же держат ширину строки на месте. */
.ch-slow-ring.static{ display:grid; place-items:center; color:var(--t3); }
.ch-slow-ring.static .pv-ic{ width:17px; height:17px; }

/* Кнопка уезжает под текст только на совсем узких экранах (iPhone SE и уже).
   На обычном телефоне полоса остаётся одной строкой — ради этого и короткий
   глагол на кнопке. */
@media (max-width:340px){
  .ch-slow{ flex-wrap:wrap; }
  .ch-slow .pv-btn{ width:100%; margin-top:var(--sp2); }
}

/* ═══════════════════════════════════════════════════════════════════════
   СТЕНА СУТОЧНОГО ЛИМИТА (.ch-wall) · 30.08.2026
   ───────────────────────────────────────────────────────────────────────
   Живёт рядом с .ch-slow и по тем же правилам: своя карточка над полем
   ввода, в потоке, а не поверх реплики. Отличий от полосы паузы два, и оба
   намеренные.

   ПЕРВОЕ: акцентная рамка вместо серой. Пауза — рабочее состояние, она не
   должна кричать; стена — конец разговора на сегодня, и её задача быть
   замеченной. Заливка при этом почти прозрачная (4% акцента): сплошной
   терракотовый прямоугольник над чёрным чатом читается как ошибка, а это
   не ошибка.

   ВТОРОЕ: элементы стоят СТОЛБИКОМ, а не в строку. Полоса паузы держится
   одной строкой ради высоты — она появляется по десять раз за вечер.
   Стена появляется один раз в сутки, и ей важнее не высота, а чтобы обе
   кнопки читались как равноправные выходы, а не как «главная и сноска».

   Крестика нет намеренно — см. разбор у wallShow() в chat.js.
   ═══════════════════════════════════════════════════════════════════════ */
.ch-wall{
  display:flex; flex-direction:column; gap:var(--sp3);
  padding:var(--sp4); margin-bottom:var(--sp2);
  border:1px solid var(--hero-line); border-radius:var(--r-md);
  background:
    linear-gradient(180deg, var(--hero-lo), transparent),
    var(--s1);
}
.ch-wall[hidden]{ display:none; }

.ch-wall-hd{ display:flex; align-items:flex-start; gap:var(--sp3); }
.ch-wall-hd .pv-ic{ flex:none; margin-top:1px; color:var(--hero); width:18px; height:18px; }
.ch-wall-hd .grow{ flex:1; min-width:0; }
.ch-wall-hd b{ display:block; font:600 var(--fs-body)/1.3 var(--f-ui); color:var(--t1); }
.ch-wall-hd p{ margin:var(--sp1) 0 0; font-size:var(--fs-small); line-height:1.4; color:var(--t2); }

/* Часы. Утопленная плашка (--s0 на --s1): цифра должна читаться как показание
   прибора, а не как ещё одна строка текста. */
.ch-wall-clock{
  display:flex; align-items:center; justify-content:space-between; gap:var(--sp3);
  padding:var(--sp3) var(--sp4); border-radius:var(--r-sm);
  background:var(--s0); border:1px solid var(--hair);
}
.ch-wall-clock .lb{
  font:600 10.5px/1.3 var(--f-ui); letter-spacing:.09em; text-transform:uppercase;
  color:var(--t3);
}
/* tabular-nums обязателен: без него секунды дёргают всю строку влево-вправо
   каждый тик, потому что «1» уже «8» в пропорциональном шрифте. */
.ch-wall-clock .tm{
  font:600 20px/1 var(--f-num); color:var(--t1);
  font-variant-numeric:tabular-nums; letter-spacing:.01em;
}

.ch-wall-acts{ display:flex; flex-direction:column; gap:var(--sp2); }
.ch-wall-acts .pv-btn{ width:100%; }
.ch-wall-acts a.pv-btn{ text-decoration:none; }

.ch-wall-fine{
  margin:0; text-align:center; font-size:11.5px; line-height:1.4; color:var(--t3);
}

/* Совсем узкие экраны: часы становятся столбиком, иначе подпись и цифра
   сталкиваются на 320 px. */
@media (max-width:340px){
  .ch-wall{ padding:var(--sp3); }
  .ch-wall-clock{ flex-direction:column; align-items:flex-start; gap:var(--sp1); }
  /* Запас по бокам кнопок: цена в подписи меняется из админки, и подпись
     подлиннее не должна упираться в края. Высоту не трогаем — по ней палец. */
  .ch-wall-acts .pv-btn{ padding-left:var(--sp2); padding-right:var(--sp2); }
}

/* плашка лимита / гостя / ошибки — в потоке, не поверх ответа */
.ch-notice{
  display:flex; gap:var(--sp3); align-items:flex-start;
  padding:var(--sp3) var(--sp4); margin-bottom:var(--sp3);
  border:1px solid var(--hair2); border-radius:var(--r-md); background:var(--s1);
}
.ch-notice .pv-ic{ flex:none; margin-top:2px; color:var(--t3); width:18px; height:18px; }
.ch-notice .grow{ flex:1; min-width:0; }
.ch-notice b{ display:block; font:600 var(--fs-small)/1.3 var(--f-ui); color:var(--t1); }
.ch-notice p{ margin:var(--sp1) 0 0; font-size:var(--fs-small); color:var(--t2); line-height:1.4; }
.ch-notice .pv-btn{ margin-top:var(--sp3); width:100%; }
.ch-notice.mark{ border-color:var(--mark); }
.ch-notice.mark .pv-ic{ color:var(--mark); }
.ch-notice.err{ border-color:var(--lose); }
.ch-notice.err .pv-ic{ color:var(--lose); }
.ch-notice-x{
  flex:none; width:var(--ctl-xs); height:var(--ctl-xs); margin:-2px -6px 0 0;
  display:grid; place-items:center; border:0; background:transparent;
  color:var(--t3); cursor:pointer; border-radius:var(--r-full); position:relative;
}
.ch-notice-x::after{ content:''; position:absolute; left:50%; top:50%; width:var(--ctl); height:var(--ctl); transform:translate(-50%,-50%); }

/* КОМПАКТНАЯ ПЛАШКА (.sm) — суточный порог.
   Полноразмерная плашка занимала над полем ввода треть экрана: заголовок,
   абзац и кнопка во всю ширину. Над коротким чатом это перекрывало последнюю
   реплику персонажа — то есть ровно то, ради чего человек и зашёл.
   Здесь всё в одну строку: заголовок и пояснение идут подряд, кнопка ужата
   до размера чипа и прижата вправо, крестик закрывает плашку до полуночи.
   На узком экране строка переносится, кнопка уезжает на свою — но высота
   всё равно вдвое меньше прежней. */
.ch-notice.sm{ align-items:center; gap:var(--sp2); padding:var(--sp2) var(--sp3); }
.ch-notice.sm .pv-ic{ margin-top:0; width:16px; height:16px; }
.ch-notice.sm .grow{ display:flex; align-items:center; gap:6px; flex-wrap:wrap; }
.ch-notice.sm b{ display:inline; }
.ch-notice.sm p{ margin:0; }
.ch-notice.sm .pv-btn{
  margin:0 0 0 auto; width:auto; min-height:32px;
  padding:0 var(--sp4); font-size:var(--fs-small);
}
.ch-notice.sm .ch-notice-x{ margin:0 -4px 0 0; }

.ch-sugs{
  display:flex; gap:var(--sp2); overflow-x:auto; scrollbar-width:none;
  scroll-snap-type:x proximity;
}
.ch-sugs:not(:empty){ padding-bottom:var(--sp3); }
.ch-sugs::-webkit-scrollbar{ display:none; }
.ch-sug{
  flex:none; max-width:82%; scroll-snap-align:start;
  display:inline-flex; align-items:center; gap:var(--sp2);
  min-height:var(--ctl); padding:0 var(--sp4);
  background:var(--s2); border:1px solid var(--hair); border-radius:var(--r-full);
  color:var(--t2); font:500 var(--fs-small)/1.3 var(--f-ui);
  cursor:pointer; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
  transition:transform var(--d-micro) var(--e-spring-soft);
  touch-action:manipulation;
}
.ch-sug:active{ transform:scale(.96); }
.ch-sug:focus-visible{ outline:var(--focus-w) solid var(--focus); outline-offset:var(--focus-off); }
.ch-sug.paid{ color:var(--t1); border-color:var(--hair2); }
.ch-sug .pv-ic{ width:14px; height:14px; flex:none; }

/* Капсула ввода: стекло, лента проходит под ней. Кнопка справа одна и
   меняется по состоянию — микрофон / отправить / стоп. */
.ch-inputrow{
  display:flex; gap:var(--sp1); align-items:flex-end;
  padding:var(--sp1) var(--sp1) var(--sp1) var(--sp3);
  background:color-mix(in srgb, var(--s2) 78%, transparent);
  border:1px solid var(--hair2); border-radius:calc(var(--ctl) / 2 + var(--sp1));
  box-shadow:var(--sh-md);
}
.ch-input{
  flex:1; min-width:0;
  /* растёт вверх до трети экрана, дальше прокручивается сам — как в телеграме */
  min-height:var(--ctl); max-height:34dvh;
  box-sizing:border-box; background:none; border:0; color:var(--t1);
  /* 16px минимум — иначе iOS зумит поле при фокусе */
  font:400 max(16px, var(--fs-body))/var(--lh-body) var(--f-ui);
  padding:var(--sp3) 0; outline:none; resize:none;
  scrollbar-width:thin;
}
.ch-input::placeholder{ color:var(--t3); }
.ch-input:disabled{ opacity:.6; }
.ch-inputrow:focus-within{ border-color:var(--hero-line); }

.ch-btn{
  width:var(--ctl); height:var(--ctl); flex:none;
  display:grid; place-items:center;
  background:var(--hero); color:var(--on-hero);
  border:0; border-radius:var(--r-full); cursor:pointer;
  transition:transform var(--d-micro) var(--e-spring-soft), opacity var(--d-micro) var(--e-effect);
  touch-action:manipulation; -webkit-tap-highlight-color:transparent;
}
.ch-btn:active:not(:disabled){ transform:scale(.92); }
.ch-btn:disabled{ opacity:.35; cursor:not-allowed; }
.ch-btn:focus-visible{ outline:var(--focus-w) solid var(--focus); outline-offset:var(--focus-off); }
.ch-btn.stop{ background:var(--s4); color:var(--t1); border:1px solid var(--hair2); }
.ch-btn.ghost{ background:var(--s3); color:var(--t2); border:1px solid var(--hair2); }
.ch-btn[hidden]{ display:none !important; }

/* ---------- запись голоса ---------- */
.ch-inputrow.rec{ border-color:var(--lose); }
/* Поле во время записи НЕ прячем: расшифровка должна быть видна прямо во
   время речи — это и есть защита от «половины ошибок». Индикатор записи
   стоит рядом компактным чипом, а не вместо текста. */
.ch-rec{
  flex:none; align-self:center;
  display:flex; align-items:center; gap:var(--sp2);
  height:var(--ctl-xs); padding:0 var(--sp2);
  border-radius:var(--r-full); background:color-mix(in srgb, var(--lose) 16%, transparent);
  color:var(--t2); font:600 var(--fs-over)/1 var(--f-ui);
}
.ch-rec[hidden]{ display:none; }
/* подпись прячем, когда места мало: главное — точка и время */
@media (max-width: 420px){ .ch-rec .hint{ display:none; } }
.ch-rec .dot{
  width:var(--sp2); height:var(--sp2); flex:none; border-radius:var(--r-full);
  background:var(--lose); animation:chRecDot 1200ms var(--e-out) infinite;
}
.ch-rec .tm{ font-family:var(--f-num); color:var(--t1); font-variant-numeric:tabular-nums; }
.ch-rec .hint{ color:var(--t3); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
@keyframes chRecDot{ 0%,100%{ opacity:1; } 50%{ opacity:.25; } }
/* пока идёт запись, кнопка отправки — красная: она и остановит, и отправит */
.ch-inputrow.rec .ch-btn:not(.ghost){ background:var(--lose); color:#fff; }
@media (prefers-reduced-motion: reduce){ .ch-rec .dot{ animation:none; } }

/* остаток бесплатных сообщений — под полем, до отправки, а не после */
.ch-foot-inner{ position:relative; }
.ch-left{
  /* Наложением, а не в потоке: появляясь, строка иначе толкает панель ввода
     вверх и лента прыгает. Резервировать место заранее нельзя — пустая полоса
     весь разговор выглядит странно. */
  position:absolute; left:0; right:0; bottom:2px;
  margin-top:0; text-align:center; pointer-events:none;
  font:600 var(--fs-over)/1 var(--f-ui); letter-spacing:var(--ls-over);
  text-transform:uppercase; color:var(--t3);
}
.ch-left.warn{ color:var(--mark); }
.ch-left[hidden]{ display:none; }

/* память в шапке (внутри шторки) */
.ch-mem{
  display:inline-flex; align-items:center; gap:var(--sp1);
  min-height:var(--sp7); padding:0 var(--sp3);
  background:var(--s2); border:1px solid var(--hair); border-radius:var(--r-full);
  color:var(--t2); font:600 var(--fs-over)/1 var(--f-ui);
  letter-spacing:var(--ls-over); text-transform:uppercase;
  cursor:pointer; text-decoration:none;
}
.ch-mem .pv-ic{ width:14px; height:14px; }
.ch-mem .pv-num{ color:var(--t1); }

/* содержимое шторки «ещё» */
.ch-sheet-row{
  display:flex; align-items:center; gap:var(--sp3);
  width:100%; box-sizing:border-box;
  min-height:var(--ctl-lg); padding:var(--sp3) 0;
  background:none; border:0; border-top:1px solid var(--hair);
  color:var(--t1); font:500 var(--fs-body)/1.3 var(--f-ui);
  text-align:left; cursor:pointer; text-decoration:none;
  touch-action:manipulation;
}
.ch-sheet-row:first-of-type{ border-top:0; }
.ch-sheet-row .pv-ic{ flex:none; color:var(--t3); }
.ch-sheet-row .grow{ flex:1; min-width:0; }
.ch-sheet-row small{ display:block; color:var(--t3); font-size:var(--fs-small); font-weight:400; }
.ch-sheet-row.danger{ color:var(--lose); }
.ch-sheet-row.danger .pv-ic{ color:var(--lose); }
.ch-sheet-row:active{ opacity:.7; }

.ch-lvlcard{
  padding:var(--sp4); border:1px solid var(--hair2); border-radius:var(--r-md);
  background:var(--s1); margin-bottom:var(--sp4);
}
.ch-lvlcard .top{ display:flex; align-items:baseline; gap:var(--sp2); margin-bottom:var(--sp3); }
.ch-lvlcard .top b{ font:700 var(--fs-title)/1 var(--f-serif); color:var(--t1); }
.ch-lvlcard .top span{ color:var(--t3); font-size:var(--fs-small); }
.ch-lvlcard p{ margin:var(--sp3) 0 0; color:var(--t2); font-size:var(--fs-small); line-height:var(--lh-body); }
.ch-lvlcard .pv-bar{ height:var(--sp2); }

.ch-events{ margin-top:var(--sp2); }
.ch-event{ display:flex; gap:var(--sp3); padding:var(--sp3) 0; border-top:1px solid var(--hair); }
.ch-event:first-child{ border-top:0; }
.ch-event .d{ flex:none; width:64px; color:var(--t3); font:600 var(--fs-over)/1.4 var(--f-num); }
.ch-event b{ display:block; font:600 var(--fs-small)/1.3 var(--f-ui); color:var(--t1); }
.ch-event p{ margin:var(--sp1) 0 0; color:var(--t2); font-size:var(--fs-small); }

.ch-freq{ display:flex; gap:var(--sp2); flex-wrap:wrap; margin-top:var(--sp3); }

@media (prefers-reduced-motion: reduce){
  .ch-in{ opacity:1; transform:none; }
  .ch-typing i{ animation:none; opacity:.6; }
  .ch-lvl.up{ animation:none; }
  /* Поднятая реплика и реакции появляются сразу: движение здесь — приятная
     деталь, а не смысл. Смысл — что реплика выделена, а ряд реакций открыт,
     и это остаётся видимым без единой анимации. */
  .ch-focus-msg, .ch-reactbar, .ch-fmenu{ opacity:1; transform:none; transition:none; }
  .ch-focus-veil{ transition:none; opacity:1; }
  .ch-remoji{ opacity:1; transform:none; transition:none; transition-delay:0s; animation:none; }
  .ch-remoji:active{ transform:none; }
  .ch-fmenu-row{ animation:none; }
  .ch-fmenu-row:active{ transform:none; }
  .ch-msg.ch-press .ch-bubble{ transform:none; }
  .ch-react.pop{ animation:none; }
  .ch-tick{ animation:none; }
  .ch-pickbar{ opacity:1; transform:none; transition:none; }
}

/* Без размытия стеклянные слои превращаются в мутные пятна поверх текста —
   тогда честнее плотная подложка. То же и при «меньше прозрачности». */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))){
  .ch-reactbar, .ch-fmenu, .ch-pickbar{ background:var(--s3); }
  .ch-focus-veil{ background:color-mix(in srgb, var(--s0) 88%, transparent); }
}
@media (prefers-reduced-transparency: reduce){
  .ch-reactbar, .ch-fmenu, .ch-pickbar{
    background:var(--s3); backdrop-filter:none; -webkit-backdrop-filter:none;
  }
  .ch-focus-veil{
    background:color-mix(in srgb, var(--s0) 92%, transparent);
    backdrop-filter:none; -webkit-backdrop-filter:none;
  }
}


.ch-rail{ display:none; padding-top:calc(var(--ch-top, 68px) + var(--sp4)); }
.ch-rail-space{ display:none; }

/* ============================================================
   ДЕСКТОП · ЧАТ
   Было: шапка во всю ширину монитора с кнопками, разъехавшимися по краям,
   лента узкой полосой посреди пустоты, обрезанные подсказки.
   Стало: лента и ввод держатся колонки, справа — панель персонажа,
   в которой видно то, за чем на телефоне надо лезть в шторку.
   ============================================================ */
@media (min-width: 900px){
  .ch-body{ height:100dvh; }

  /* Остров держится той же колонки, что и разговор. Раньше он был растянут
     во всю ширину монитора: «назад» и «ещё» уезжали к краям стекла, а капсула
     с именем вставала по центру ЭКРАНА — то есть заметно правее переписки.
     Капсула должна стоять над тем, к чему относится. */
  .ch-island{
    max-width:var(--wide); margin-inline:auto;
    padding-left:var(--sp5); padding-right:var(--sp5);
  }
  .ch-pill{ max-width:min(46%, 460px); }

  .ch-scroll{ align-items:center; }
  .ch-main{
    display:grid;
    grid-template-columns:minmax(0,1fr);
    /* align-items у .ch-scroll центрирует по горизонтали, margin-top:auto —
       прижимает содержимое к низу. justify-content:center здесь прижимал бы
       ленту к середине по вертикали и ломал прокрутку длинных разговоров. */
    width:100%; max-width:var(--wide); margin:auto auto 0;
  }
  .ch-feed{ padding-left:var(--sp5); padding-right:var(--sp5); }

  .ch-foot{ padding:0 var(--sp5) var(--sp5); }
  .ch-foot-inner{ max-width:var(--wide); }
  /* Подсказки и поле ввода стоят ровно под лентой, а не разъезжаются.
     margin-inline:auto здесь обязателен: подвал шириной во всю колонку
     (--wide), а строка ввода — в --col. Без него она прижималась к левому
     краю подвала и стояла на 80–230px левее ленты — поле ввода уезжало
     из-под разговора тем сильнее, чем шире монитор. width:100% идёт с ними в
     паре: auto-поля отключают растягивание элемента сетки по ячейке, и без
     явной ширины строка ввода схлопывалась до ширины кнопки. */
  .ch-rowline{ width:100%; max-width:var(--col); margin-inline:auto; }

  /* Подсказки — ОДНА строка с прокруткой, а не перенос.
     С flex-wrap они разрастались вверх на три-четыре ряда и накрывали
     половину переписки: чем длиннее вариант, тем меньше видно разговора.
     Строка в ширину занимает ровно 44px и не двигает ленту. */
  .ch-sugs{ flex-wrap:nowrap; overflow-x:auto; }
  .ch-sug{ max-width:min(360px, 46%); }
  /* на десктопе полоса прокрутки уместна — иначе не видно, что есть ещё */
  .ch-sugs{ scrollbar-width:thin; }
  .ch-sugs::-webkit-scrollbar{ display:block; height:4px; }
  .ch-sugs::-webkit-scrollbar-thumb{ background:var(--hair2); border-radius:var(--r-full); }
  .ch-sugs::-webkit-scrollbar-track{ background:transparent; }
}

/* Панель персонажа появляется, когда для неё реально есть место */
@media (min-width: 1200px){
  /* Лента прижата к низу, панель персонажа — к верху. Раньше вниз уезжала
     вся сетка целиком, и в коротком разговоре панель обрезалась нижним краем
     экрана: карточка persona уходила за пределы прокрутки. */
  .ch-main{
    grid-template-columns:minmax(0,1fr) 320px; gap:var(--sp7);
    margin:0 auto; min-height:100%;
  }
  .ch-feed{ align-self:end; }
  .ch-rail{ align-self:start; }
  .ch-foot-inner{
    display:grid; grid-template-columns:minmax(0,1fr) 320px; gap:var(--sp7);
    align-items:end;
  }
  .ch-foot-inner > .ch-rail-space{ display:block; }
  .ch-rail{ display:block; }

  /* Панель персонажа забирает справа 320px + отступ, значит центр разговора
     сдвинут от центра экрана ровно на половину этой полосы. Всё, что висит
     «по центру», должно уехать туда же — иначе остров, кнопка «вниз» и
     панель выбора стоят по центру монитора, а разговор левее. */
  .ch-island{ padding-right:calc(var(--sp5) + 320px + var(--sp7)); }
  .ch-jump{ left:calc(50% - (320px + var(--sp7)) / 2); }
  .ch-pickbar{ right:calc(var(--sp4) + 320px + var(--sp7)); }
}

.ch-rail-card{
  position:sticky; top:calc(var(--ch-top, 68px) + var(--sp4));
  background:var(--s2); border:1px solid var(--hair); border-radius:var(--r-md);
  box-shadow:var(--sh-sm); padding:var(--sp5); text-align:center;
}
.ch-rail-card .pv-mono{ margin:0 auto var(--sp4); }
.ch-rail-name{ font:600 var(--fs-title)/var(--lh-tight) var(--f-serif); color:var(--t1); }
.ch-rail-tag{ color:var(--t2); font-size:var(--fs-small); margin-top:var(--sp2); text-wrap:pretty; }
.ch-rail-lvl{ margin-top:var(--sp5); text-align:left; }
.ch-rail-lvl .top{ display:flex; justify-content:space-between; align-items:baseline; margin-bottom:var(--sp2); }
.ch-rail-lvl b{ font:600 var(--fs-small)/1 var(--f-ui); color:var(--t1); }
.ch-rail-lvl span{ color:var(--t3); font-size:var(--fs-over); }
.ch-rail-acts{ display:flex; flex-direction:column; gap:var(--sp2); margin-top:var(--sp5); }
.ch-rail-acts a, .ch-rail-acts button{
  display:flex; align-items:center; gap:var(--sp3);
  min-height:var(--ctl); padding:0 var(--sp4);
  background:var(--s1); border:1px solid var(--hair); border-radius:var(--r-sm);
  color:var(--t1); font:500 var(--fs-small)/1 var(--f-ui);
  text-decoration:none; cursor:pointer; text-align:left;
}
.ch-rail-acts a:hover, .ch-rail-acts button:hover{ border-color:var(--hair2); }
.ch-rail-acts .pv-ic{ flex:none; color:var(--t3); }
.ch-rail-acts .n{ margin-left:auto; color:var(--t3); font-family:var(--f-num); }

/* ============================================================
   СТЕКЛО

   Полупрозрачность имеет смысл только там, где браузер умеет размывать
   фон. Без размытия это просто мутное пятно поверх текста, поэтому
   запасной вариант — плотная подложка.
   ============================================================ */
@supports (backdrop-filter: blur(1px)){
  .ch-fab, .ch-pill, .ch-inputrow{
    backdrop-filter:blur(24px) saturate(180%);
    -webkit-backdrop-filter:blur(24px) saturate(180%);
  }
}
@supports not (backdrop-filter: blur(1px)){
  .ch-fab, .ch-pill, .ch-inputrow{ background:var(--s2); }
}
@media (prefers-reduced-transparency: reduce){
  .ch-fab, .ch-pill, .ch-inputrow{
    background:var(--s2); backdrop-filter:none; -webkit-backdrop-filter:none;
  }
}

/* Верх и низ ленты слегка растворяются под островом и вводом: без этого
   строки обрываются об край стекла резко, будто их обрезали. */
.ch-scroll{
  -webkit-mask-image:linear-gradient(to bottom,
    transparent 0, #000 calc(var(--ch-top, 68px) - var(--sp5)),
    #000 calc(100% - var(--ch-bot, 96px) + var(--sp5)), transparent 100%);
          mask-image:linear-gradient(to bottom,
    transparent 0, #000 calc(var(--ch-top, 68px) - var(--sp5)),
    #000 calc(100% - var(--ch-bot, 96px) + var(--sp5)), transparent 100%);
}

/* ============================================================
   ПОРОГ ВХОДА — реплика персоны с кнопкой внутри

   Гостю дают несколько реплик, после чего разговор упирается в вход. Просьбу
   произносит сам персонаж, и кнопка живёт внутри его пузыря: человек читает
   не «системное сообщение о лимите», а последнюю фразу собеседника, у которой
   есть продолжение. Плашка над полем ввода отсюда убрана — два призыва на
   одном экране делят внимание и не усиливают друг друга.
   ============================================================ */
.ch-msg[data-gate="1"] .ch-bubble{
  border-color:var(--hero-line);
  background:color-mix(in srgb, var(--hero) 7%, var(--s2));
}
.ch-gate{
  display:flex; flex-direction:column; gap:var(--sp2);
  margin-top:var(--sp3); padding-top:var(--sp3);
  border-top:1px solid var(--hair2);
  /* внутри пузыря живёт текст реплики, а это — управление */
  user-select:none; -webkit-user-select:none;
}
.ch-gate-btn{
  position:relative;
  min-height:var(--ctl-vis); padding:0 var(--sp4);
  display:inline-flex; align-items:center; justify-content:center; gap:var(--sp2);
  background:var(--hero); border:1px solid var(--hero); color:var(--on-hero);
  border-radius:var(--r-full); cursor:pointer;
  font:600 var(--fs-small)/1 var(--f-ui);
  touch-action:manipulation; -webkit-tap-highlight-color:transparent;
  transition:transform var(--d-micro) var(--e-spring-soft), filter var(--d-micro) var(--e-effect);
}
.ch-gate-btn .pv-ic{ width:15px; height:15px; flex:none; }
.ch-gate-btn:active{ transform:scale(.97); }
.ch-gate-btn:focus-visible{ outline:var(--focus-w) solid var(--focus); outline-offset:var(--focus-off); }
.ch-gate-note{
  text-align:center; color:var(--t3);
  font:400 var(--fs-small)/1.3 var(--f-ui);
}

/* Пузырь-порог входит чуть заметнее остальных: это развилка, а не ещё одна
   реплика в потоке. Один короткий импульс, без пульсации по кругу. */
.ch-msg[data-gate="1"].in .ch-bubble{ animation:chGateIn var(--d-spring) var(--e-spring); }
@keyframes chGateIn{
  from{ box-shadow:0 0 0 0 color-mix(in srgb, var(--hero) 34%, transparent); }
  to  { box-shadow:0 0 0 10px transparent; }
}
@media (prefers-reduced-motion: reduce){
  .ch-msg[data-gate="1"].in .ch-bubble{ animation:none; }
}

/* ---------- содержимое шторки (каркас — .pv-sheet из ui.css) ---------- */
.pn-sh{ text-align:center; padding:0 var(--sp2) var(--sp2); }
.pn-sh-ic{
  width:60px; height:60px; margin:0 auto var(--sp4); position:relative;
  display:grid; place-items:center;
  background:var(--hero-lo); border:1px solid var(--hero-line);
  border-radius:20px; color:var(--hero);
}
.pn-sh-ic svg{ width:26px; height:26px; }
.pn-sh-ic .mini{
  position:absolute; right:-6px; top:-6px; width:22px; height:22px;
  display:grid; place-items:center;
  background:var(--hero-grad); color:var(--on-hero);
  border-radius:50%; border:2px solid var(--s5);
}
.pn-sh-ic .mini svg{ width:11px; height:11px; }
.pn-sh h3{
  margin:0; color:var(--t1);
  font:800 var(--fs-title)/1.25 var(--f-ui); letter-spacing:-.01em;
}
.pn-sh h3 em{ font-style:normal; font-family:var(--f-serif); color:var(--hero); }
.pn-sh p{
  font-size:var(--fs-small); color:var(--t2); line-height:var(--lh-body);
  margin:var(--sp2) auto var(--sp4); max-width:300px;
}
.pn-benefits{
  display:flex; flex-direction:column; gap:9px;
  margin:0 0 var(--sp4); text-align:left;
}
.pn-benefit{
  display:flex; gap:10px; align-items:center;
  font-size:var(--fs-small); color:var(--t2);
  background:var(--s3); border:1px solid var(--hair);
  border-radius:14px; padding:9px 12px;
}
.pn-benefit svg{ width:15px; height:15px; flex:none; color:var(--hero); }

/* ---------- строки «связи»: тумблер и установка ----------
   Шторка «Связь» — это маленький экран настроек, а не реклама: две строки,
   у каждой свой орган управления. У уведомлений тумблер, потому что это
   состояние («включено / выключено»), у приложения кнопка, потому что это
   разовое действие. Перепутать их формой — значит соврать о том, что будет
   после нажатия. Строка уходит из шторки, как только сделана: выключенный
   тумблер «Готово» ничего не сообщает, а место занимает. */
.pn-rows{
  display:flex; flex-direction:column; gap:var(--sp2);
  margin:0 0 var(--sp4); text-align:left;
}
.pn-row{
  display:flex; align-items:center; gap:var(--sp3);
  background:var(--s3); border:1px solid var(--hair);
  border-radius:16px; padding:11px var(--sp3);
}
.pn-row-ic{
  flex:none; width:34px; height:34px; display:grid; place-items:center;
  border-radius:12px; background:var(--hero-lo);
  border:1px solid var(--hero-line); color:var(--hero);
}
.pn-row-ic svg{ width:17px; height:17px; }
.pn-row-tx{ flex:1; min-width:0; display:flex; flex-direction:column; gap:2px; }
.pn-row-tx b{ font:700 var(--fs-small)/1.3 var(--f-ui); color:var(--t1); }
.pn-row-tx i{ font-style:normal; font-size:11.5px; line-height:1.4; color:var(--t3); }

/* Тумблер. Визуально 46×27, касаемо — 44 (WCAG 2.2 SC 2.5.8) через ::before. */
.pn-sw{
  position:relative; flex:none; width:46px; height:27px; padding:0; cursor:pointer;
  border:1px solid var(--hair2); border-radius:var(--r-full);
  background:var(--s1);
  transition:background var(--d-std) var(--e-out), border-color var(--d-std) var(--e-out);
}
.pn-sw::before{
  content:''; position:absolute; left:50%; top:50%;
  width:var(--ctl); height:var(--ctl); transform:translate(-50%,-50%);
}
.pn-sw::after{
  content:''; position:absolute; top:3px; left:3px; width:19px; height:19px;
  border-radius:50%; background:var(--t2);
  transition:transform var(--d-std) var(--e-spring), background var(--d-std) var(--e-out);
}
.pn-sw.on{ background:var(--hero); border-color:transparent; }
.pn-sw.on::after{ transform:translateX(19px); background:#fff; }
.pn-sw.busy{ opacity:.55; pointer-events:none; }
.pn-sw:focus-visible{ outline:var(--focus-w) solid var(--focus); outline-offset:var(--focus-off); }
@media (prefers-reduced-motion: reduce){ .pn-sw, .pn-sw::after{ transition:none; } }

/* Кнопка внутри строки — меньше главной, чтобы не спорить с ней по весу */
.pn-row-btn{
  flex:none; height:32px; padding:0 var(--sp3); border:0; border-radius:var(--r-full);
  background:var(--hero); color:#fff; white-space:nowrap; cursor:pointer;
  font:700 12px/1 var(--f-ui); box-shadow:var(--sh-sm);
  transition:transform var(--d-micro) var(--e-out);
}
.pn-row-btn:active{ transform:scale(.96); }
.pn-row-btn[disabled]{ opacity:.6; pointer-events:none; }
.pn-row-btn.ok{ background:var(--win); box-shadow:none; }
.pn-row-btn:focus-visible{ outline:var(--focus-w) solid var(--focus); outline-offset:var(--focus-off); }
.pn-help{
  text-align:left; margin:0 0 var(--sp3);
  background:var(--s3); border:1px solid var(--hair);
  border-radius:14px; padding:12px 14px;
  font-size:var(--fs-small); color:var(--t2); line-height:var(--lh-body);
}
.pn-help b{ color:var(--t1); }
/* Инструкция по установке — нумерованные шаги: «сделай раз, потом два» глаз
   проходит быстрее, чем тот же текст одним абзацем, а ошибиться порядком в
   меню браузера — самый частый способ не установить приложение. */
.pn-help ol{ margin:var(--sp2) 0 0; padding-left:20px; display:flex; flex-direction:column; gap:6px; }
.pn-help li{ line-height:var(--lh-body); }
.pn-help li::marker{ color:var(--hero); font-weight:700; }
.pn-help-tip{
  display:block; margin-top:10px; padding-top:10px;
  border-top:1px solid var(--hair); color:var(--t3); font-size:12px;
}
.pn-sh-col{ display:flex; flex-direction:column; gap:var(--sp2); }
.pn-btn{
  min-height:46px; padding:0 var(--sp4); border:0; border-radius:var(--r-full);
  display:inline-flex; align-items:center; justify-content:center; gap:7px;
  background:var(--hero-grad); color:var(--on-hero);
  font:700 var(--fs-body)/1 var(--f-ui); cursor:pointer;
  box-shadow:var(--sh-hero);
  transition:transform var(--d-micro) var(--e-out);
}
.pn-btn:active{ transform:scale(.97); }
.pn-btn[disabled]{ opacity:.7; pointer-events:none; }
.pn-btn.ok{ background:var(--win); color:#fff; box-shadow:none; }
.pn-btn.ghost{
  background:transparent; color:var(--t2); box-shadow:none; min-height:42px;
}
.pn-btn svg{ width:16px; height:16px; }
.pn-note{
  display:flex; align-items:center; justify-content:center; gap:6px;
  font-size:11px; color:var(--t3); margin-top:var(--sp3);
}
.pn-note svg{ width:12px; height:12px; flex:none; }

/* ============================================================
   РЕЖИМЫ ОБЩЕНИЯ

   Экран выбора живёт ВНУТРИ ленты, под приветствием персонажа, а не поверх
   него модальным окном. Причина простая: выбирают формат разговора с
   конкретным человеком, и решение принимается, когда его уже видно. Модалка
   поверх пустого экрана заставляет выбирать вслепую и читается как
   препятствие на входе.
   ============================================================ */
.ch-modepick{ margin:14px 0 6px; display:flex; flex-direction:column; gap:10px; }
.ch-modepick-t{ font-size:13px; color:var(--t2,#9aa); padding:0 2px; }
.ch-mp{
  display:block; width:100%; text-align:left; cursor:pointer;
  background:var(--surf2,rgba(255,255,255,.04));
  border:1px solid var(--hair,rgba(255,255,255,.12));
  border-radius:16px; padding:14px 16px; color:inherit; font:inherit;
  transition:border-color .15s, background .15s;
}
.ch-mp:hover{ border-color:var(--acc,#7c5cff); background:var(--surf3,rgba(124,92,255,.08)); }
.ch-mp:focus-visible{ outline:2px solid var(--acc,#7c5cff); outline-offset:2px; }
.ch-mp b{ display:block; font-size:15px; font-weight:700; margin-bottom:8px; }
/* Пример важнее подписи: по нему формат понимают за секунду, а абзац
   объяснений на этом экране никто не читает. Поэтому он крупнее подписи по
   объёму и отбит цветом и курсивом. */
.ch-mp i{ display:block; font-style:italic; font-size:13.5px; line-height:1.55; color:var(--t3,#8a8a9a); white-space:pre-line; }
.ch-modepick-n{ font-size:12px; color:var(--t3,#8a8a9a); text-align:center; padding-top:2px; }

/* Переключатели у поля ввода. Размер и форма — как у остальных кнопок строки,
   иначе они читаются как что-то чужое, вклеенное в поле. */
/* Строка над полем ввода: режим и взрослые темы.
   Чип, а не круглая кнопка: у кнопки в этой строке уже есть хозяин —
   «отправить», — и второй такой же круг рядом читается как ошибка вёрстки. */
.ch-modes{ display:flex; gap:var(--sp2); flex-wrap:wrap; margin-bottom:var(--sp2); padding:0 2px; }
.ch-modes[hidden]{ display:none !important; }
/* Чип режима прячется до ответа сервера. Раньше hidden висел на всей строке;
   теперь в ней живёт кнопка жалобы, которая обязана быть на месте всегда. */
.ch-mchip[hidden]{ display:none !important; }

.ch-mchip{
  display:inline-flex; align-items:center; gap:6px;
  height:30px; padding:0 11px; border-radius:var(--r-full);
  background:transparent; border:1px solid var(--hair2);
  color:var(--t3); font:600 var(--fs-small)/1 var(--f-ui);
  cursor:pointer; white-space:nowrap;
  transition:color var(--d-micro) var(--e-effect), border-color var(--d-micro) var(--e-effect), background var(--d-micro) var(--e-effect);
  touch-action:manipulation; -webkit-tap-highlight-color:transparent;
}
.ch-mchip .pv-ic{ width:15px; height:15px; }
.ch-mchip:hover{ color:var(--t1); border-color:var(--hair); }
.ch-mchip:active{ transform:scale(.96); }
.ch-mchip:disabled{ opacity:.4; cursor:default; }
.ch-mchip:focus-visible{ outline:var(--focus-w) solid var(--focus); outline-offset:var(--focus-off); }

/* Иконки обе в разметке, показываем нужную — так подпись и картинка не могут
   разъехаться, и ни одна из них не зависит от JS. */
.ch-mchip .ic{ display:none; }
.ch-mchip[data-m="rp"]    .ic.rp{ display:inline-flex; }
.ch-mchip[data-m="plain"] .ic.pl{ display:inline-flex; }
.ch-mchip[data-m="rp"]{ color:var(--t1); border-color:var(--hair); }

.ch-mchip.adult.on{ color:#e8734a; border-color:rgba(232,115,74,.45); background:rgba(232,115,74,.1); }


/* ── КНОПКА ЖАЛОБЫ (30.08.2026) ──
   Ровно тот же чип, что режим и тариф: та же высота, тот же радиус, та же
   рамка. Своего у неё только пропорция — круг, потому что подписи нет.

   Почему без слова. Строка чипов уже несёт два смысла («ролевая игра»,
   «тариф»); третье слово ломает её на второй этаж на телефоне, а это
   38 лишних пикселей над полем ввода — ровно то, что раздел у себя отыграл.
   Флажок в интернете читается как жалоба без пояснений, а подпись никуда не
   делась: она в title и в aria-label, то есть и в подсказке, и у читалок.

   Приглушена намеренно. Кнопка обязана быть НАЙДЕНА тем, кому она нужна, и
   не обязана лезть в глаза остальным: жалоба — не действие по умолчанию.
   На наведении и в фокусе проступает красным — в этот момент человек уже
   целится в неё, и подтверждение «да, это то самое» уместно. */
.ch-mchip.report{
  width:30px; padding:0; justify-content:center;
  color:var(--t3); flex:none;
}
.ch-mchip.report .pv-ic{ width:16px; height:16px; }
.ch-mchip.report:hover,
.ch-mchip.report:focus-visible{
  color:var(--danger,#e5484d);
  border-color:color-mix(in srgb, var(--danger,#e5484d) 42%, transparent);
  background:color-mix(in srgb, var(--danger,#e5484d) 9%, transparent);
}
/* color-mix знают не все браузеры — для остальных обычная подсветка. */
@supports not (color: color-mix(in srgb, red 50%, transparent)){
  .ch-mchip.report:hover, .ch-mchip.report:focus-visible{
    border-color:rgba(229,72,77,.42); background:rgba(229,72,77,.09);
  }
}
/* Пока жалоба уходит на сервер — гасим, чтобы не отправить дважды. */
.ch-mchip.report[aria-busy="true"]{ opacity:.5; pointer-events:none; }


/* На узком экране чипы жмутся, чтобы строка осталась одной: перенос стоит
   лишних 38px над полем ввода — ровно того, что мы отыграли у плашки. */
@media (max-width:430px){
  .ch-modes{ gap:6px; }
  .ch-mchip{ padding:0 9px; gap:5px; }
}

/* Ремарка в ролевом режиме. Приглушена и курсивом — так глаз отделяет
   описание сцены от прямой речи, не читая их подряд одним потоком. */
.ch-bubble em.ch-rp{ font-style:italic; color:var(--t2); }
.ch-msg.me .ch-bubble em.ch-rp{ color:inherit; opacity:.82; }

/* ============================================================
   ТАРИФ: ЧИП У ПОЛЯ ВВОДА И СОДЕРЖИМОЕ ШТОРКИ (29.08.2026)

   Своей всплывающей панели нет: экран открывается штатной шторкой раздела
   (.pv-sheet + window.pvSheet из ui.css) — той же, что «Ещё» и «Близость».
   Отсюда бесплатно берутся затемнение, пружина, Escape, свайп по ручке,
   ловушка фокуса, безопасные зоны и клавиатура.

   Ни одного нового цвета и ни одного размера мимо токенов: правило из шапки
   tokens.css действует и здесь. Поэтому блок едет за темой сайта — проверено
   в янтарной, в «Полночи» и в светлой «Cloud Dancer».
   ============================================================ */

/* ── ЧИП РЯДОМ С «РОЛЕВОЙ ИГРОЙ» ──
   База целиком у .ch-mchip: те же высота, радиус и рамка, что у чипа режима,
   иначе два соседних чипа читались бы как элементы из разных интерфейсов.
   Своё — только состояние «оплачено». */
.ch-mchip.plan.on{
  color:var(--hero); border-color:var(--hero-line); background:var(--hero-lo);
}
/* Число остатка — цифровым шрифтом раздела, как все числа в нём.
   Разделитель рисует CSS, а не разметка: так его можно убрать вместе с
   подписью, не собирая строку заново. */
.ch-mchip.plan .num{ font-family:var(--f-num); font-weight:700; }
.ch-mchip.plan .lbl + .num::before{ content:' · '; font-family:var(--f-ui); font-weight:600; }

/* УЗКИЙ ЭКРАН: у оплаченного чипа остаются иконка и число.
   «Без пауз · 45 мин» рядом с «Ролевой игрой» не помещается в 320 px, а
   перенос строки чипов стоит 38 лишних пикселей над полем ввода — ровно
   того, чего избегает комментарий к .ch-modes ниже. Смысл не теряется:
   молния и корона различают пропуск и подписку без слов.
   Бесплатный чип подпись СОХРАНЯЕТ — у него нет числа, и без слова
   «Старт» от него осталась бы одна иконка без смысла. */
@media (max-width:430px){
  .ch-mchip.plan.on .lbl{ display:none; }
  .ch-mchip.plan.on .lbl + .num::before{ content:none; }
}
/* Кнопка на полосе паузы — <button>, а .pv-btn рассчитан на <a>. */
.ch-pass-btn{ -webkit-appearance:none; appearance:none; }

/* ── КАРТОЧКА ВНУТРИ ШТОРКИ ──
   Повторяет .ch-lvlcard из «Близости»: та же поверхность --s1, та же рамка
   --hair2, тот же радиус. Две шторки одного экрана не должны отличаться
   ничем, кроме содержания. */
.ch-passcard{
  padding:var(--sp4); border:1px solid var(--hair2); border-radius:var(--r-md);
  background:var(--s1); margin-bottom:var(--sp4);
}

/* Название тарифа. НЕ акцентом и не размером цены: акцент принадлежит тому,
   что продаётся, а бесплатная ступень — это факт, а не приз. */
.ch-planhead{ display:flex; align-items:baseline; gap:var(--sp3); margin-bottom:var(--sp3); }
.ch-planhead b{
  flex:none; font:700 var(--fs-title)/var(--lh-tight) var(--f-serif); color:var(--t1);
}
.ch-planhead span{
  flex:1; min-width:0; text-align:right; font-size:var(--fs-small); color:var(--t3);
}

/* Остаток времени. Подпись НАДСТРОЧНИКОМ над числом, а не сбоку: «7 ч 12 мин»
   — длинная строка, и подпись рядом с ней ломается на три этажа. Проверено
   в браузере на 390 px. */
.ch-over{
  display:block; margin-bottom:var(--sp2);
  font:600 var(--fs-over)/1 var(--f-display); letter-spacing:var(--ls-over);
  text-transform:uppercase; color:var(--t3);
}
/* ОГРАНИЧЕНО ШТОРКОЙ 02.09.2026. Класс .ch-left описан в этом файле дважды:
   строка 942 — тонкая строка «Осталось N из 50» под полем ввода, и это
   правило — крупное число внутри шторки пропуска. Специфичность одинаковая,
   побеждал тот, что ниже, и строка под полем рисовалась тридцатым кеглем:
   поднимала панель ввода и съедала ленту чата. Оба использования этого класса
   в шторке лежат внутри .ch-passcard (chat.js, planSheetHtml, строки 1679
   и 1704), поэтому здесь достаточно спуститься на уровень. */
.ch-passcard .ch-left{
  /* position возвращаем в поток: базовое правило выше делает строку под полем
     ввода наложением (absolute), и без этой строки крупное число внутри
     шторки пропуска улетело бы к нижнему краю карточки. */
  position:static; left:auto; right:auto; bottom:auto;
  pointer-events:auto; text-align:left;
  margin:0 0 var(--sp3);
  font:700 var(--fs-hero)/1 var(--f-num); color:var(--hero);
}
.ch-until{
  margin:var(--sp3) 0 0;
  font-size:var(--fs-small); line-height:var(--lh-body); color:var(--t2);
}

/* Перечисление свойств. */
.ch-passlist{
  list-style:none; margin:0; padding:0;
  display:flex; flex-direction:column; gap:var(--sp2);
}
.ch-passlist li{
  position:relative; padding-left:var(--sp4);
  color:var(--t2); font-size:var(--fs-small); line-height:var(--lh-body);
}
/* Точка, а не галочка: галочки в коротком списке читаются как чек-лист
   выполненных задач, а это перечисление свойств. */
.ch-passlist li::before{
  content:''; position:absolute; left:var(--sp1); top:.62em;
  width:var(--sp1); height:var(--sp1);
  border-radius:var(--r-full); background:var(--hero);
}

/* ── ВТОРОЙ ВАРИАНТ ВЫБОРА ──
   База — .ch-sheet-row из меню «Ещё» (иконка, заголовок, подпись). Добавлены
   рамка и радиус: строка стоит над главной кнопкой, и без рамки она читается
   как текст, а не как то, на что можно нажать. */
.ch-sheet-row.boxed{
  border:1px solid var(--hair2); border-radius:var(--r-md);
  padding:var(--sp3) var(--sp4); margin-bottom:var(--sp3);
}
.ch-sheet-row.boxed .pv-ic{ color:var(--hero); }
.ch-sheet-row.boxed .chev{ flex:none; color:var(--t3); }

.ch-passpay{ width:100%; }
