/* MorrowStars — «сумерки».
   Одна тёмная палитра на всё приложение: тёплая ночь, янтарь как единственный
   акцент, чернильный фон. Светлой темы нет — магазин всегда выглядит одинаково. */

:root {
  color-scheme: dark;

  --ink:      #F7EBD0;
  --canvas:   #171310;
  --canvas-2: #100D0A;
  --canvas-3: #1C1712;
  --surface:  #221B14;
  --surface-2:#2C241A;
  --amber:    #FFC53D;
  --ember:    #FFD98A;
  --dusk:     #FFC53D;
  --dusk-ink: #1A1409;

  --line:     rgba(247,235,208,.14);
  --line-2:   rgba(247,235,208,.26);
  --muted:    rgba(247,235,208,.58);
  --faint:    rgba(247,235,208,.36);
  --press:    rgba(247,235,208,.07);

  --good:  #6FD79B;
  --warn:  #FFC53D;
  --bad:   #FF9B7A;

  --display: 'Unbounded', 'Golos Text', system-ui, sans-serif;
  --body:    'Golos Text', -apple-system, system-ui, 'Segoe UI', sans-serif;
  --mono:    'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, monospace;

  --r-sm: 12px; --r-md: 14px; --r-lg: 18px; --r-xl: 26px;
  --pad: 20px;
  --safe-b: env(safe-area-inset-bottom, 0px);
  --shadow: 0 20px 46px -24px rgba(0,0,0,.85);
  --glow-a: rgba(255,197,61,.40);
  --glow-b: rgba(255,197,61,.10);
}

* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }

/* Must outrank every component rule: several of them set `display`, and an
   author-level `display` beats the user-agent `[hidden] { display: none }` —
   which once left the payment sheet's full-screen scrim on top of the page
   after closing, swallowing every tap. */
[hidden] { display: none !important; }

/* Grid and flex items default to `min-width: auto` and refuse to shrink below
   their content, which let long titles stretch cards past the screen edge. */
.rent-card, .order, .gift, .opt,
.rent-main, .order-main, .opt-main { min-width: 0; }

/* `overflow`/`text-overflow` do nothing on an inline box, and these labels are
   markup <span>s — blockifying them is what makes the ellipsis rules work. */
.rent-title, .rent-sub, .order-title, .order-sub, .opt-title, .opt-sub { display: block; }

/* The gradient lives on <html> as well: if the viewport is ever taller than
   the body — a short page, Telegram resizing its sheet, an over-scroll — the
   exposed strip stays part of the design instead of showing through black. */
html, body { margin: 0; padding: 0; }
html {
  min-height: 100%;
  background: var(--canvas-2);
}
body {
  min-height: 100dvh;
  background: linear-gradient(180deg, var(--canvas-2) 0%, var(--canvas) 42%, var(--canvas-3) 100%)
              fixed;
  color: var(--ink);
  font-family: var(--body);
  font-size: 15px; line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  overscroll-behavior-y: none;
}

button, input, select { font: inherit; color: inherit; }
button { border: 0; background: none; cursor: pointer; }
h1, h2, h3, p, dl, dd { margin: 0; }
:focus-visible { outline: 2px solid var(--amber); outline-offset: 3px; border-radius: 4px; }

/* ------------------------------------------------------------ backdrop --- */
/* A slow sunrise behind the whole app: two warm bodies drifting near the
   horizon. Cheap (two blurred circles), and it gives the shell the same
   atmosphere as the stars screen. */

.aurora { position: fixed; inset: 0; z-index: 0; overflow: hidden; pointer-events: none; }
.aurora .blob { position: absolute; border-radius: 50%; filter: blur(64px); }
.b1 {
  width: 72vw; height: 72vw; left: 50%; margin-left: -36vw; top: 4vh;
  background: radial-gradient(closest-side, var(--glow-a), var(--glow-b) 60%, transparent 78%);
  animation: sunDrift 18s ease-in-out infinite;
}
.b2 {
  width: 56vw; height: 56vw; right: -18vw; top: 42vh;
  background: radial-gradient(closest-side, rgba(255,150,61,.30), transparent 72%);
  animation: sunDrift2 23s ease-in-out infinite;
}
.b3 {
  width: 60vw; height: 60vw; left: -22vw; bottom: -8vh;
  background: radial-gradient(closest-side, rgba(36,31,51,.14), transparent 72%);
  animation: sunDrift3 28s ease-in-out infinite;
}
#starfield { position: absolute; inset: 0; width: 100%; height: 100%; opacity: .5; }

@keyframes sunDrift  { 0%,100% { transform: none } 50% { transform: translate(5vw, 7vh) scale(1.18) } }
@keyframes sunDrift2 { 0%,100% { transform: none } 50% { transform: translate(-8vw, -9vh) scale(1.22) } }
@keyframes sunDrift3 { 0%,100% { transform: none } 50% { transform: translate(7vw, -6vh) scale(1.16) } }

/* ---------------------------------------------------------------- boot --- */

.boot {
  position: fixed; inset: 0; z-index: 90; display: grid; place-content: center;
  gap: 24px; justify-items: center;
  background: linear-gradient(180deg, var(--canvas-2), var(--canvas));
}
.boot.gone { opacity: 0; pointer-events: none; transition: opacity .4s ease; }

/* Появление магазина.
   Раньше загрузчик плавно таял, а сама витрина возникала рывком под ним —
   готовая и неподвижная. Теперь она всплывает навстречу: короткий подъём и
   проявление, ровно на время, пока гаснет загрузчик.

   Блоки приезжают по очереди с небольшой задержкой — так глаз успевает
   заметить порядок: сначала шапка, потом розыгрыш, потом товары. Задержки
   маленькие, это не представление, а сглаживание. */
@keyframes appIn {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}

#app.enter { animation: appIn .42s cubic-bezier(.2,.7,.3,1) both; }
#app.enter > .view:not([hidden]) > * {
  animation: appIn .5s cubic-bezier(.2,.7,.3,1) both;
}
#app.enter > .view:not([hidden]) > *:nth-child(1) { animation-delay: .04s; }
#app.enter > .view:not([hidden]) > *:nth-child(2) { animation-delay: .10s; }
#app.enter > .view:not([hidden]) > *:nth-child(3) { animation-delay: .16s; }
#app.enter > .view:not([hidden]) > *:nth-child(4) { animation-delay: .22s; }
#app.enter > .view:not([hidden]) > *:nth-child(n+5) { animation-delay: .26s; }

/* Переход между вкладками — только проявление, без подъёма: там уже есть
   движение самого списка, и двойная анимация читается как рывок. */
@keyframes viewIn { from { opacity: 0; } to { opacity: 1; } }
.view:not([hidden]) { animation: viewIn .22s ease both; }

@media (prefers-reduced-motion: reduce) {
  #app.enter, #app.enter > .view:not([hidden]) > *, .view:not([hidden]) {
    animation: none;
  }
}
.boot-mark {
  width: 74px; height: 74px; border-radius: 50%;
  display: grid; place-items: center;
  background: radial-gradient(closest-side, var(--amber), rgba(255,197,61,.25) 70%, transparent);
  color: var(--ink); font-family: var(--display); font-weight: 600; font-size: 26px;
  animation: sunPulse 2.2s ease-in-out infinite;
}
@keyframes sunPulse {
  0%,100% { transform: scale(.92); filter: brightness(.95) }
  50%     { transform: scale(1.04); filter: brightness(1.1) }
}
.boot-bar { width: 130px; height: 2px; background: var(--line); overflow: hidden; border-radius: 2px; }
.boot-bar i {
  display: block; height: 100%; width: 38%; border-radius: 2px; background: var(--amber);
  animation: slide 1.2s cubic-bezier(.5,0,.5,1) infinite;
}
@keyframes slide { 0% { transform: translateX(-110%) } 100% { transform: translateX(370%) } }

/* --------------------------------------------------------------- shell --- */

#app {
  position: relative; z-index: 1;
  max-width: 540px; margin: 0 auto;
  padding: 0 var(--pad) calc(104px + var(--safe-b));
}
.view { animation: rise .38s cubic-bezier(.22,.7,.2,1); }
@keyframes rise { from { opacity: 0; transform: translateY(12px) } to { opacity: 1; transform: none } }

/* ----------------------------------------------------------------- bar --- */

.hero { padding: 20px 0 4px; }
.hero-row { display: flex; align-items: center; justify-content: space-between; gap: 16px; }
.eyebrow {
  font-family: var(--display); font-weight: 600; font-size: 13px;
  letter-spacing: .14em; text-transform: uppercase;
}
.eyebrow::before { content: '✦ '; color: var(--amber); font-size: 11px; }

/* Курс звезды. Был набран моноширинным с типографской звёздочкой «✦» — читался
   как отладочная строка. Теперь основной шрифт с табличными цифрами (они не
   пляшут при пересчёте) и звезда Telegram, та же, что и в остальном магазине. */
.hero-rate {
  font-family: var(--body); font-size: 12.5px; font-weight: 500;
  font-variant-numeric: tabular-nums; letter-spacing: -.01em; color: var(--muted);
}
.page-rate { margin-top: 12px; }

.hero-title {
  margin-top: 24px;
  font-family: var(--display); font-weight: 600;
  /* Unbounded is wide; "Магазин Morrow Stars" only fits a phone across two
     lines, so the <br> in the markup is load-bearing. */
  font-size: clamp(27px, 8.4vw, 36px); line-height: 1.04; letter-spacing: -.04em;
}
.hero-lede {
  margin-top: 12px; max-width: 30ch;
  font-size: 13.5px; line-height: 1.5; color: var(--muted);
}

.head-actions { display: flex; align-items: center; gap: 8px; }
.theme-toggle-removed {
  width: 38px; height: 38px; border-radius: 50%; flex: 0 0 auto;
  display: grid; place-items: center; font-size: 15px;
  background: var(--surface); border: 1px solid var(--line); color: var(--ember);
  transition: background .2s, transform .5s cubic-bezier(.3,1.4,.4,1), border-color .2s;
}
.theme-toggle:active { background: var(--press); }

.avatar, .profile-avatar {
  width: 38px; height: 38px; border-radius: 50%; flex: 0 0 auto;
  display: grid; place-items: center;
  background: var(--dusk); color: var(--dusk-ink);
  font-family: var(--display); font-weight: 600; font-size: 15px;
}
.profile-avatar {
  width: 76px; height: 76px; font-size: 28px; margin: 0 auto 16px;
  background: radial-gradient(closest-side, var(--amber), rgba(255,197,61,.3) 72%, transparent);
  color: var(--ink);
}

.wallet {
  margin-top: 20px; padding: 12px 16px; border-radius: var(--r-md);
  background: var(--surface); border: 1px solid var(--line);
  display: flex; align-items: center; justify-content: space-between;
}
.wallet-label { font-size: 12.5px; color: var(--muted); }
.wallet-value { font-family: var(--mono); font-size: 15px; color: var(--ember); }

/* Сколько звёзд уже продано. Подпись приглушённая, а числа горят янтарём — так
   строка читается одним взглядом: глаз цепляется за цифру, а не за слова.
   Свечение мягкое: это витрина доверия, а не сигнал тревоги. */
.sold {
  margin-top: 8px; padding: 0 4px;
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px;
  font-size: 12px; color: var(--muted); line-height: 1.5;
}
.sold-part { white-space: nowrap; }
.sold-dot { color: var(--faint); }
.sold b {
  font-family: var(--mono); font-weight: 600; font-size: 13px;
  color: var(--amber);
  text-shadow: 0 0 10px color-mix(in srgb, var(--amber) 45%, transparent);
}
/* Число дёргается вверх, когда счётчик вырос при обновлении — продажа заметна,
   даже если человек в этот момент смотрел в другую часть экрана. */
@keyframes sold-tick {
  0%   { transform: translateY(0); }
  40%  { transform: translateY(-3px); }
  100% { transform: translateY(0); }
}
.sold b.tick { display: inline-block; animation: sold-tick .5s ease; }
@media (prefers-reduced-motion: reduce) { .sold b.tick { animation: none; } }

/* Галочка над свежевыданным заказом: сначала подтверждение, что всё прошло,
   и только потом просьба оценить. Просить, не сказав «готово», — значит
   заставлять человека гадать, выдали ему заказ или нет. */
.done-mark {
  width: 46px; height: 46px; margin: 0 auto 4px; border-radius: 50%;
  display: grid; place-items: center; font-size: 22px;
  color: var(--good);
  background: color-mix(in srgb, var(--good) 16%, transparent);
  animation: doneIn .4s cubic-bezier(.2,1.4,.4,1) both;
}
@keyframes doneIn { from { opacity: 0; transform: scale(.6); } }
@media (prefers-reduced-motion: reduce) { .done-mark { animation: none; } }

/* ------------------------------------------------- «как это работает» --- */

/* Приглашение прочитать инструкцию до оплаты, а не после. Намеренно спокойное:
   это подсказка, а не реклама, и спорить с товаром на витрине она не должна. */
.how {
  display: flex; align-items: center; gap: 12px; width: 100%; text-align: left;
  margin-top: 14px; padding: 11px 13px; border-radius: var(--r-md);
  background: var(--press); border: 1px solid var(--line);
  transition: transform .14s cubic-bezier(.2,.8,.3,1), border-color .2s ease;
}
.how:active { transform: scale(.99); }
.how-mark {
  width: 26px; height: 26px; flex: 0 0 auto; border-radius: 50%;
  display: grid; place-items: center;
  background: color-mix(in srgb, var(--amber) 18%, transparent);
  color: var(--amber); font-weight: 600; font-size: 13px;
}
.how-text { flex: 1; min-width: 0; }
.how-text b { display: block; font-size: 13.5px; font-weight: 500; }
.how-text span { display: block; font-size: 11.5px; color: var(--faint); margin-top: 1px; }
.how-go { color: var(--faint); font-size: 17px; flex: 0 0 auto; }

.how-tabs { margin-bottom: 4px; }
.how-pane { display: grid; gap: 12px; }
.how-sub { margin-top: 8px; }
.how-link { display: block; margin-top: -4px; }

/* Шаги нумерованные, с подписью под каждым: одной строкой на шаг человек
   читает список команд, а не понимает, что происходит и зачем. */
.how-steps { display: grid; gap: 14px; margin: 0; padding: 0; list-style: none; counter-reset: step; }
.how-steps li {
  position: relative; padding-left: 34px; counter-increment: step;
}
.how-steps li::before {
  content: counter(step);
  position: absolute; left: 0; top: -1px;
  width: 24px; height: 24px; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--surface-2); border: 1px solid var(--line);
  color: var(--amber); font-family: var(--mono); font-size: 12px;
}
.how-steps li b { display: block; font-size: 13.5px; font-weight: 500; margin-bottom: 2px; }
.how-steps li span { display: block; font-size: 12.5px; color: var(--muted); line-height: 1.55; }
.how-steps-tight { gap: 10px; }
.how-steps-tight li::before { background: transparent; }

/* ------------------------------------------------------------ розыгрыш --- */

/* Плашка стоит выше всего на витрине, поэтому кричать ей нельзя: тёплая рамка
   и подсветка вместо заливки — иначе она перебивает сам магазин. */
/* Блок стоит между строкой продаж и плитками. Сверху отступ больше нижнего:
   он отделяет розыгрыш от шапки, а лидерборд под ним читается как его
   продолжение, а не как третья самостоятельная карточка. */
#contestBox { margin: 22px 0 0; }

.contest {
  display: block; width: 100%; text-align: left;
  margin: 0 0 10px; padding: 14px 16px;
  border-radius: var(--r-md);
  background: linear-gradient(180deg,
              color-mix(in srgb, var(--amber) 9%, var(--surface)), var(--surface));
  border: 1px solid color-mix(in srgb, var(--amber) 34%, transparent);
  box-shadow: 0 0 22px -12px var(--amber);
}
.contest-title {
  display: block; font-size: 14.5px; font-weight: 600;
  color: var(--ember); margin-bottom: 6px;
}
.contest-prizes { display: block; font-size: 12.5px; color: var(--muted); }
/* Сроки — обычной третьей строкой, тем же тоном, что и призы. Отдельный блок
   с подписью перетягивал на себя внимание с самих призов. */
.contest-when {
  display: block; font-size: 12.5px; color: var(--muted); margin-top: 2px;
}
/* Цифры отсчёта моноширинные: они меняются каждую минуту, и в пропорциональном
   шрифте строка дёргалась бы вправо-влево на каждый тик. */
.contest-when b {
  font-family: var(--mono); font-weight: 600; color: var(--amber);
  font-variant-numeric: tabular-nums;
}

/* Карточка лидерборда — своя, под плашкой. Нижнего отступа нет: его задаёт
   `.tiles` следующим блоком, и два отступа подряд рвали бы витрину пополам. */
.board-card {
  display: block; width: 100%; text-align: left;
  margin-bottom: 0; padding: 16px 16px 8px;
}
.board-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 12px; margin-bottom: 10px;
}
.board-title { font-size: 16px; font-weight: 600; }
.board-hint { font-size: 11.5px; color: var(--faint); }
.board-empty { display: block; font-size: 12.5px; color: var(--faint); }
.contest-me {
  display: block; margin-top: 7px; padding-top: 7px;
  border-top: 1px solid var(--line);
  font-size: 12px; color: var(--muted);
}
.contest-me b { color: var(--amber); font-family: var(--mono); }

/* Таблица лидеров. Колонки фиксированной ширины по краям и растяжимое имя
   посередине — иначе строки с длинным юзернеймом ломают выравнивание. */
.board { display: grid; gap: 2px; margin: 10px 0 16px; }
/* Строка: медаль, имя во всю оставшуюся ширину, справа счёт и приз вплотную —
   так они читаются как одно «7 — 500 ⭐», а не как две разные колонки. */
.board-row {
  display: grid; grid-template-columns: 26px 1fr auto auto;
  align-items: center; gap: 10px;
  padding: 9px 4px; font-size: 13.5px;
}
.board-card .board-row + .board-row { border-top: 1px solid var(--line); }
/* Своя строка подсвечена: в таблице себя иначе не найти. */
.board-row.on {
  background: color-mix(in srgb, var(--amber) 10%, transparent);
  border-radius: var(--r-sm); padding: 9px 8px;
}
.board-place { font-size: 16px; text-align: center; color: var(--muted); }
.board-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.board-count {
  font-family: var(--mono); font-weight: 600; color: var(--amber);
  text-shadow: 0 0 10px color-mix(in srgb, var(--amber) 40%, transparent);
}
.board-prize { font-size: 12px; color: var(--muted); white-space: nowrap; }
.board-why { font-size: 11.5px; color: var(--faint); text-align: right; }
.board-why.ok { color: var(--good); }
.board-sub { margin-top: 22px; }

/* --------------------------------------------------------------- tiles --- */

/* The bottom margin has to beat the 10px inter-tile gap, otherwise whatever
   section follows reads as one more tile stuck to the grid. */
.tiles { display: grid; gap: 12px; margin: 24px 0 24px; }
.tile {
  position: relative; overflow: hidden; text-align: left;
  display: grid; gap: 4px; align-content: center;
  padding: 20px; min-height: 92px; border-radius: var(--r-lg);
  background: var(--surface); border: 1px solid var(--line);
  transition: transform .14s ease, border-color .2s ease, background .2s ease;
}
.tile:active { transform: scale(.985); background: var(--surface-2); }
.tile::after {
  content: ''; position: absolute; right: -30px; top: 50%; width: 150px; height: 150px;
  margin-top: -75px; border-radius: 50%; pointer-events: none;
  background: radial-gradient(closest-side, var(--tile-glow, transparent), transparent 72%);
  transition: transform .5s cubic-bezier(.22,.7,.2,1);
}
.tile:active::after { transform: scale(1.15); }
.tile-stars   { --tile-glow: rgba(255,197,61,.55); }
.tile-premium { --tile-glow: rgba(138,100,16,.28); }
.tile-gifts   { --tile-glow: rgba(255,150,61,.34); }
.tile-rent    { --tile-glow: rgba(36,31,51,.16); }

.tile-glyph { position: relative; font-size: 21px; line-height: 1; margin-bottom: 6px; }
.tile-name { position: relative; font-family: var(--display); font-weight: 600; font-size: 15px;
             letter-spacing: -.01em; }
.tile-note { position: relative; font-size: 12.5px; color: var(--muted); }

.tiles-row { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }

/* ----------------------------------------------------------- page head --- */

.page-head { padding: 24px 0 16px; }
.page-head h1 {
  font-family: var(--display); font-weight: 600;
  font-size: clamp(28px, 9vw, 36px); letter-spacing: -.035em; line-height: 1;
}
.sub { font-size: 13px; color: var(--muted); margin-top: 8px; }

.segmented {
  display: flex; gap: 6px; padding: 4px; margin-bottom: 20px;
  background: var(--press); border-radius: var(--r-md);
  overflow-x: auto; scrollbar-width: none;
}
.segmented::-webkit-scrollbar { display: none; }
.segmented button {
  flex: 1 0 auto; padding: 8px 16px; border-radius: 10px;
  font-size: 13.5px; color: var(--muted); white-space: nowrap;
  transition: background .18s, color .18s, box-shadow .18s;
}
.segmented button.on { background: var(--surface-2); color: var(--ink); font-weight: 500; }

/* --------------------------------------------------------------- gifts --- */

.gift-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.gift {
  position: relative; text-align: left; display: grid; gap: 4px;
  padding: 16px; border-radius: var(--r-lg);
  background: var(--surface); border: 1px solid var(--line);
  transition: transform .14s ease, border-color .2s ease;
}
.gift:active { transform: scale(.98); border-color: var(--line-2); }
.gift-art {
  height: 78px; margin-bottom: 12px; border-radius: var(--r-md);
  display: grid; place-items: center; font-size: 36px; overflow: hidden;
  background: radial-gradient(closest-side, var(--glow-a), transparent 74%), var(--press);
}
.gift-art img { width: 64px; height: 64px; object-fit: contain; }
.gift-art-lg { height: 132px; margin-bottom: 16px; }
.gift-art-lg img { width: 108px; height: 108px; }
.gift-title { font-family: var(--display); font-weight: 600; font-size: 13.5px; letter-spacing: -.01em; }
.gift-price { font-family: var(--mono); font-size: 13px; color: var(--ember); }
.gift-meta { font-size: 11px; color: var(--faint); }
.chip {
  position: absolute; top: 10px; right: 10px; padding: 4px 8px; border-radius: 999px;
  font-size: 9.5px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase;
}
.chip-limited     { background: rgba(255,197,61,.24); color: var(--ember); }
.chip-available   { background: rgba(47,125,82,.13);  color: var(--good); }

/* ---------------------------------------------------------------- rent --- */

.rent-list { display: grid; gap: 12px; }
.rent-card {
  display: flex; gap: 16px; align-items: center; text-align: left;
  padding: 12px; border-radius: var(--r-lg);
  background: var(--surface); border: 1px solid var(--line);
  transition: transform .14s ease, border-color .2s ease;
}
.rent-card:active { transform: scale(.99); border-color: var(--line-2); }
.rent-art {
  width: 52px; height: 52px; flex: 0 0 auto; border-radius: var(--r-md);
  display: grid; place-items: center; font-size: 23px; overflow: hidden;
  background: radial-gradient(closest-side, var(--glow-a), transparent 76%), var(--press);
}
.rent-art img { width: 100%; height: 100%; object-fit: cover; }
.rent-hero {
  height: 180px; margin-bottom: 16px; border-radius: var(--r-lg);
  display: grid; place-items: center; overflow: hidden; font-size: 56px;
  background: radial-gradient(closest-side, var(--glow-a), transparent 72%), var(--press);
}
.rent-hero img { width: 168px; height: 168px; object-fit: contain; }

/* Пока картинка едет, в плитке стоит глиф — он и остаётся, если она так и не
   доехала. Появление плавное: сотня карточек, дорисовывающихся по мере
   загрузки, иначе выглядит как рябь. */
.rent-art img, .rent-hero img { animation: artIn .22s ease both; }
@keyframes artIn { from { opacity: 0 } to { opacity: 1 } }
.rent-main { flex: 1; }
.rent-title { font-family: var(--display); font-weight: 600; font-size: 14px; letter-spacing: -.01em;
              overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rent-sub { font-size: 12px; color: var(--faint); margin-top: 2px;
            overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rent-price { text-align: right; flex: 0 0 auto; }
.rent-price b { display: block; font-family: var(--mono); font-weight: 500; font-size: 14px; color: var(--ember); }
.rent-price span { font-size: 10.5px; color: var(--faint); }

/* ------------------------------------------------------------- profile --- */

.profile-head { text-align: center; padding: 24px 0 20px; }
.profile-head h1 { font-family: var(--display); font-weight: 600; font-size: 21px; letter-spacing: -.02em; }

/* Статус-эмодзи уровня стоит у имени — это его место: значок про человека, а не
   про раздел. Появляется с серебряного, у бронзы его нет. */
.profile-name { display: inline-flex; align-items: center; gap: 8px; }
/* Свечение делает подложка, а не `filter: drop-shadow` на самом эмодзи: тот
   считает тень по альфе глифа, и на цветном эмодзи она размазывалась и
   подрагивала при любой перерисовке. Тут ореол — отдельный слой под символом,
   он не зависит от того, как платформа рисует эмодзи. */
.profile-badge {
  position: relative; display: inline-grid; place-items: center;
  width: 26px; height: 26px; font-size: 15px; line-height: 1;
  animation: badgeIn .5s cubic-bezier(.16,1,.3,1) backwards;
}
.profile-badge::before {
  content: ''; position: absolute; inset: 0; border-radius: 50%; z-index: -1;
  background: radial-gradient(closest-side, rgba(255,197,61,.30), transparent 78%);
}
@keyframes badgeIn { from { opacity: 0; transform: scale(.4) } }
.tier { margin: 20px auto 0; max-width: 300px; }
/* Родные стрелочки числового поля убираем: на десктопе они стоят рядом с
   нашими «+»/«−», не знают про минимальный и максимальный срок и уводили
   значение в минус. Ввод с клавиатуры и наши кнопки остаются. */
.input[type="number"], .dawn-input[type="number"] { -moz-appearance: textfield; appearance: textfield; }
.input[type="number"]::-webkit-outer-spin-button,
.input[type="number"]::-webkit-inner-spin-button,
.dawn-input[type="number"]::-webkit-outer-spin-button,
.dawn-input[type="number"]::-webkit-inner-spin-button {
  -webkit-appearance: none; appearance: none; margin: 0;
}

/* Аватарка из Telegram поверх буквы-заглушки. */
.avatar.has-photo, .profile-avatar.has-photo { background: var(--press); overflow: hidden; }
.avatar img, .profile-avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* Карточка уровня — теперь кнопка: открывает всю лестницу. */
.tier { display: block; width: 100%; text-align: left; }
.tier:active { transform: scale(.995); }
/* Всегда столбцом. С переносом по ширине короткие привилегии встают в одну
   строку, а длинные — в разные, и один уровень выглядел иначе, чем остальные. */
.tier-perks { display: flex; flex-direction: column; align-items: flex-start; gap: 4px; margin-top: 8px; }
.perk {
  padding: 4px 8px; border-radius: 999px; font-size: 10.5px; line-height: 1.5;
  background: var(--press); color: var(--muted);
}
.perk-hot { background: rgba(255,197,61,.20); color: var(--ember); font-weight: 600; }

.tier-list { display: grid; gap: 8px; margin: 16px 0; }
/* Та же история, что и у отзыва: сетка с `gap` вместо блока с `margin`. */
.tier-item {
  padding: 12px; border-radius: var(--r-md); background: var(--press);
  border: 1px solid transparent;
  display: grid; gap: 8px;
}
.tier-item.on { border-color: var(--amber); background: rgba(255,197,61,.10); }
.tier-item.done:not(.on) .tier-item-name { color: var(--ember); }
.tier-item-head { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; }
.tier-item-name { font-size: 13.5px; font-weight: 500; }
.tier-item-range { font-family: var(--mono); font-size: 10.5px; color: var(--faint); white-space: nowrap; }
.tier-item-perks { display: flex; flex-direction: column; align-items: flex-start; gap: 4px; }
.tier-item-now { font-size: 10.5px; color: var(--amber); font-weight: 600; }
/* Уровень без привилегий не должен оставлять за собой пустой отступ. */
.tier-item-perks:empty, .tier-perks:empty { display: none; }

.tier-row { display: flex; justify-content: space-between; font-size: 12px; color: var(--muted); margin-bottom: 8px; }
.tier-row span:first-child { color: var(--ink); font-weight: 500; }
.tier-bar { height: 4px; border-radius: 4px; background: var(--press); overflow: hidden; }
.tier-bar i {
  display: block; height: 100%; border-radius: 4px; width: 0;
  background: linear-gradient(90deg, var(--ember), var(--amber));
  transition: width .8s cubic-bezier(.22,.7,.2,1);
}

/* Экран «подпишитесь»: рисуется на месте заставки, поэтому центрируется сам. */
.gate {
  max-width: 320px; margin: 0 auto; padding: 32px 20px; text-align: center;
  display: grid; gap: 12px; place-content: center; min-height: 60vh;
}
.gate-mark { font-size: 44px; line-height: 1; }
.gate h2 { font-family: var(--display); font-weight: 600; font-size: 20px; }
.gate .sub { font-size: 13px; color: var(--muted); line-height: 1.5; }
.gate .btn { margin-top: 4px; }

.stat-row { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; margin-bottom: 20px; }
.stat { padding: 16px 8px; border-radius: var(--r-md); text-align: center;
        background: var(--surface); border: 1px solid var(--line); }
.stat b { display: block; font-family: var(--display); font-weight: 600; font-size: 17px; letter-spacing: -.03em; }
.stat span { font-size: 10.5px; color: var(--faint); }

/* -------------------------------------------------------------- panels --- */

.panel {
  margin-bottom: 16px; padding: 20px; border-radius: var(--r-lg);
  background: var(--surface); border: 1px solid var(--line);
}
.panel-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 16px; }
.panel-head h2 { font-family: var(--display); font-weight: 600; font-size: 14.5px; letter-spacing: -.01em; }
.link { font-size: 13px; color: var(--ember); }
.badge {
  padding: 4px 12px; border-radius: 999px; font-family: var(--mono); font-size: 12px;
  background: rgba(255,197,61,.22); color: var(--ember);
}

.ref-copy { font-size: 13px; color: var(--muted); margin-bottom: 16px; }
.ref-copy b { color: var(--ember); }
.ref-stats { display: grid; grid-template-columns: repeat(3,1fr); gap: 8px; margin-bottom: 16px; }
.ref-stats div { text-align: center; padding: 12px 4px; border-radius: var(--r-sm); background: var(--press); }
.ref-stats b { display: block; font-family: var(--display); font-weight: 600; font-size: 16px; }
.ref-stats span { font-size: 10.5px; color: var(--faint); }
.ref-link {
  padding: 12px 16px; border-radius: var(--r-sm); background: var(--press);
  font-family: var(--mono); font-size: 12px; color: var(--muted);
  word-break: break-all; margin-bottom: 12px;
}
.ref-actions { display: grid; grid-template-columns: 1fr auto; gap: 8px; }

/* -------------------------------------------------------------- orders --- */

.order-list { display: grid; gap: 8px; }
.order {
  display: flex; align-items: center; gap: 12px; padding: 12px;
  border-radius: var(--r-md); background: var(--press); text-align: left; width: 100%;
}
.order-icon {
  width: 34px; height: 34px; border-radius: 10px; flex: 0 0 auto;
  display: grid; place-items: center; font-size: 15px;
  background: var(--surface); border: 1px solid var(--line);
}
.order-main { flex: 1; }
.order-title { font-size: 13px; font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.order-sub { font-family: var(--mono); font-size: 10.5px; color: var(--faint); margin-top: 2px;
             overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.order-status { font-size: 10.5px; font-weight: 600; padding: 4px 8px; border-radius: 999px; flex: 0 0 auto; }
.st-completed        { background: rgba(47,125,82,.14);  color: var(--good); }
.st-paid, .st-processing { background: rgba(255,197,61,.22); color: var(--ember); }
.st-awaiting_payment { background: rgba(36,31,51,.10);   color: var(--muted); }
.st-failed           { background: rgba(140,47,18,.13);  color: var(--bad); }
.st-cancelled, .st-refunded, .st-draft { background: var(--press); color: var(--faint); }

.empty { text-align: center; padding: 24px 16px; color: var(--faint); font-size: 13px; }

/* Two verdicts side by side on a queued claim. */
.order-actions { display: flex; gap: 6px; flex: 0 0 auto; }
.order-actions button:disabled { opacity: .5; }

/* ------------------------------------------------------------- reviews --- */

.reviews-score { display: flex; align-items: baseline; gap: 8px; margin-bottom: 16px; }
.reviews-score b { font-family: var(--display); font-weight: 600; font-size: 24px; }
.reviews-stars { color: var(--amber); font-size: 13px; letter-spacing: 1px; }
.reviews-list { display: grid; gap: 8px; }
/* Сетка, а не блок, и отступы через `gap`, а не `margin`. Двух зайцев сразу:
   у отзыва без текста внизу больше не висит пустое место от `margin-bottom`
   шапки, и переводы строк из шаблона перестают считаться содержимым —
   сетка игнорирует узлы из одних пробелов, а блок рисовал ими лишнюю строку. */
.review {
  padding: 12px; border-radius: var(--r-md); background: var(--press);
  display: grid; gap: 6px;
}
.review-head { display: flex; align-items: baseline; gap: 8px; }
.review-author { font-size: 13px; font-weight: 500; }
.review-rating { color: var(--amber); font-size: 11.5px; letter-spacing: 1px; }
.review-date { margin-left: auto; font-family: var(--mono); font-size: 10.5px; color: var(--faint); }
.review-text { font-size: 12.5px; color: var(--muted); line-height: 1.45; }
.review-about { font-family: var(--mono); font-size: 10.5px; color: var(--faint); }

/* The rating picker: five taps, no slider, no half-stars. */
.rate-row { display: flex; gap: 8px; justify-content: center; margin: 20px 0 6px; }
.rate-row button {
  width: 48px; height: 48px; border-radius: 14px; font-size: 21px;
  background: var(--press); border: 1px solid var(--line); color: var(--faint);
}
.rate-row button.on { background: rgba(255,197,61,.20); border-color: var(--amber); color: var(--amber); }
.rate-caption { text-align: center; font-size: 12.5px; color: var(--muted); min-height: 18px; }

/* -------------------------------------------------------------- tabbar --- */

.tabbar {
  position: fixed; left: 50%; transform: translateX(-50%);
  bottom: calc(12px + var(--safe-b)); z-index: 40;
  width: min(calc(100% - 24px), 516px);
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 2px; padding: 4px;
  background: color-mix(in srgb, var(--surface) 92%, transparent);
  border: 1px solid var(--line); border-radius: var(--r-lg);
  backdrop-filter: blur(18px) saturate(1.3); box-shadow: var(--shadow);
}
.tabbar button {
  display: grid; gap: 4px; justify-items: center; padding: 8px 2px 6px;
  border-radius: 12px; font-size: 10px; color: var(--faint);
  transition: color .2s ease, background .2s ease;
}
.tabbar button span {
  display: block; line-height: 0;
  transition: transform .3s cubic-bezier(.3,1.5,.4,1);
}
/* Значок наследует цвет кнопки, поэтому активная вкладка красится один раз —
   и подпись, и контур меняются вместе. */
.tabbar button svg {
  width: 21px; height: 21px; display: block;
  fill: none; stroke: currentColor;
  stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round;
  transition: stroke-width .24s ease;
}
.tabbar button.on { color: var(--amber); background: var(--press); }
.tabbar button.on span { transform: translateY(-2px) scale(1.06); }
/* Активный значок плотнее и со слабым свечением — видно боковым зрением.
   Заливку не трогаем: половина путей здесь открытые (лента подарка, стержень
   ключа), и заливка нарисовала бы у них хорду вместо фигуры. */
.tabbar button.on svg {
  stroke-width: 2;
  filter: drop-shadow(0 0 5px color-mix(in srgb, var(--amber) 45%, transparent));
}

/* --------------------------------------------------------------- sheet --- */

/* `--kb` — высота экранной клавиатуры, её измеряет `followKeyboard`. Рамку
   шторки она не двигает: ею живёт только отступ внутри прокрутки, чтобы
   сфокусированное поле было куда поднять. */
:root { --kb: 0px; }
/* Хост не двигается никогда. Пробовал поднимать его над клавиатурой — и
   каждый раз это ломало что-нибудь ещё: то шторка схлопывалась, то заглушка
   ложилась поверх содержимого. Клавиатуре достаточно отступа внутри прокрутки
   (см. `--kb` ниже), а рамка шторки пусть стоит на месте. */
.sheet-host { position: fixed; inset: 0; z-index: 60; display: flex; align-items: flex-end; }
/* Затемнение — на всё окно, а не на хост: хост поднимается над клавиатурой, и
   вместе с ним обрезалось затемнение. В полосе под шторкой светилась
   незатемнённая витрина. */
.sheet-scrim {
  position: fixed; inset: 0; background: rgba(23,20,13,.42);
  backdrop-filter: blur(2px); animation: fade .28s ease;
}

@keyframes fade { from { opacity: 0 } }

/* Прокручивается содержимое, а не сама шторка.
   Пока скроллером была `.sheet`, жест и прокрутка спорили за один палец:
   браузер решал, что это скролл, обрывал поток указателя — и шторка дёргалась
   и возвращалась. Теперь у шторки `touch-action: none` (жест всегда наш), а
   прокрутка живёт внутри `.sheet-body`. */
.sheet {
  position: relative; width: 100%; max-width: 540px; margin: 0 auto;
  max-height: 93dvh; display: flex; flex-direction: column; overflow: hidden;
  touch-action: none;
  background: linear-gradient(180deg, var(--canvas-2), var(--canvas) 46%, var(--canvas-3));
  border: 1px solid var(--line); border-bottom: 0;
  border-radius: var(--r-xl) var(--r-xl) 0 0;
}

/* Анимация въезда висит на отдельном классе, а не на самой `.sheet`.
   Причина не в красоте. `animation: none` не ставит анимацию на паузу, а
   убирает её: стоило снять класс перетаскивания, как въезд создавался заново и
   играл с нуля — шторка прыгала вверх и бледнела прямо посреди жеста. Пока
   анимация двигала только положение, это проскакивало незамеченным; с
   прозрачностью стало видно всем.

   Теперь класс ставится на открытие и снимается, когда въезд отыграл. Дальше
   анимации у шторки нет вовсе, и перезапускать нечего. */
.sheet.is-opening { animation: up .44s cubic-bezier(.22,1,.28,1); }
.sheet-body {
  flex: 1 1 auto; min-height: 0;
  overflow-y: auto; -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
  /* Внутри разрешаем только вертикальную прокрутку — горизонтальные жесты
     остаются приложению. */
  touch-action: pan-y;
  /* Клавиатура живёт здесь, и только здесь. Снизу к содержимому добавляется
     её высота — тогда прокрутке есть куда поднять последнее поле, — а
     `scroll-padding` отодвигает от неё цель прокрутки. Рамка шторки при этом
     не шевелится: сломать этим нечего, в худшем случае внизу лишний воздух. */
  padding: 0 var(--pad) calc(24px + var(--safe-b));
  /* Отдельной строкой, а не внутри сокращённой записи: сокращённую с `calc` и
     переменной браузер пересчитывает не всегда, и отступ молча оставался
     прежним — то есть места под клавиатуру не появлялось вовсе. */
  padding-bottom: calc(24px + var(--safe-b) + var(--kb));
  scroll-padding-bottom: calc(24px + var(--kb));
  /* Отступ меняется вместе с клавиатурой. Когда она уезжает, прокрутка
     упирается в новый предел, и без перехода содержимое подпрыгивает вверх;
     с ним — доезжает. Кривая та же, что у самой клавиатуры. */
  transition: padding-bottom .25s cubic-bezier(.17,.84,.44,1);
}

/* Ритм обычной шторки.
   Её содержимое собирается в коде из общих кусков — заголовок, подпись, поле,
   счёт, заметка, кнопки, — и у каждого был свой отступ: 8 у подписи, 16 у поля,
   24 у счёта, 12 у итога, а у кнопки после текста не было вовсе. Расстояния
   зависели от того, какие блоки попали на экран, и ни одна шторка не выглядела
   как соседняя. Теперь расстояние одно, а собственные отступы детей сняты.

   `.dawn` сюда не входит: экран покупки звёзд — не набор общих кусков, а
   отдельная вёрстка с выверенным ритмом (20px над небом, 24 над «Кому», 24 над
   итогом). Общее правило стирало эти отступы и ломало экран, поэтому он
   помечается своим классом и живёт по своим правилам. */
.sheet:not(.dawn) .sheet-body { display: grid; align-content: start; gap: 12px; }
.sheet:not(.dawn) .sheet-body > * { margin: 0; }
/* Два исключения, и оба осмысленные: подпись принадлежит заголовку, а кнопки
   в столбце — одной группе. Отрицательный отступ подтягивает их к соседу,
   не ломая общий ритм. */
.sheet:not(.dawn) .sheet-body > h2 + .sub { margin-top: -6px; }
.sheet:not(.dawn) .sheet-body > .btn + .btn { margin-top: -4px; }
/* Шторка не просто выезжает, а слегка проявляется по пути: чистый сдвиг снизу
   на последних кадрах читается как «шлёпнулась», а короткое проявление в
   первой трети скрадывает удар о верхнюю границу. */
@keyframes up {
  from { transform: translateY(100%); opacity: .4; }
  40%  { opacity: 1; }
  to   { transform: translateY(0); opacity: 1; }
}

/* Шторку тянут пальцем, поэтому у неё три состояния: под пальцем — никаких
   переходов, иначе появляется задержка; возврат на место — пружина; уход вниз —
   короткий разгон. Раньше она просто исчезала по нажатию, и это читалось как
   сбой, а не как жест. */
.sheet.is-dragging {
  transition: none; cursor: grabbing;
  /* Слой под палец поднимаем только на время жеста. Постоянный `will-change`
     держал бы отдельный слой всё время, съедая память на слабых телефонах. */
  will-change: transform;
}
.sheet.is-settling { transition: transform .42s cubic-bezier(.16,1,.3,1); }
.sheet-host.is-closing .sheet {
  transform: translateY(100%);
  transition: transform .3s cubic-bezier(.32,0,.67,0);
}
/* Затемнение уходит чуть раньше самой шторки: так закрытие кажется быстрее,
   хотя длится столько же. */
.sheet-host.is-closing .sheet-scrim { opacity: 0; transition: opacity .2s ease-out; }

/* Шапка — это ручка шторки, и она непрозрачная.
   Раньше фон был градиентом в прозрачность: прокручиваемое содержимое
   просвечивало прямо под ползунком, и ручка выглядела палкой, положенной
   поверх текста. Теперь это полноценная планка со своими скруглениями сверху,
   а граница и тень под ней появляются только когда под шапкой действительно
   что-то уехало. */
.sheet-head {
  position: relative; z-index: 2; flex: 0 0 auto;
  padding: 12px var(--pad);
  background: var(--canvas-2);
  border-radius: var(--r-xl) var(--r-xl) 0 0;
  transition: box-shadow .2s ease, border-color .2s ease;
  border-bottom: 1px solid transparent;
  cursor: grab;
  /* Жест по шапке всегда наш — прокрутка его не перехватывает. */
  touch-action: none;
}
.sheet.is-scrolled .sheet-head {
  border-bottom-color: var(--line);
  box-shadow: 0 12px 24px -20px rgba(0,0,0,.95);
}
.sheet.is-dragging .sheet-head { cursor: grabbing; }

/* Сама ручка: 5 px при 28 px области захвата — по тонкой полоске пальцем не
   попасть. Светлее сверху, темнее снизу, поэтому читается предметом, а не
   линией. */
.sheet-grip { display: block; width: 72px; height: 28px; margin: 0 auto; position: relative; }
.sheet-grip::before {
  content: ''; position: absolute; left: 50%; top: 10px; transform: translateX(-50%);
  width: 44px; height: 5px; border-radius: 5px;
  background: linear-gradient(180deg, var(--line-2), rgba(247,235,208,.14));
  transition: width .22s cubic-bezier(.16,1,.3,1), background .22s ease;
}
/* Под пальцем ручка «схватывается» — этим шторка подтверждает, что жест принят. */
.sheet.is-dragging .sheet-grip::before {
  width: 56px; background: linear-gradient(180deg, var(--amber), rgba(255,197,61,.55));
}
@media (hover: hover) {
  .sheet-head:hover .sheet-grip::before {
    background: linear-gradient(180deg, var(--muted), rgba(247,235,208,.28));
  }
}
.sheet-x {
  position: absolute; right: var(--pad); top: 4px; width: 32px; height: 32px;
  border-radius: 50%; display: grid; place-items: center; font-size: 13px;
  background: var(--press); border: 1px solid var(--line); color: var(--muted);
}
.sheet-x:active { background: var(--surface-2); color: var(--ink); }

.sheet h2 { font-family: var(--display); font-weight: 600; font-size: 21px; letter-spacing: -.03em; }
.sheet .sub { margin-bottom: 20px; }

/* ------------------------------------------------------------ controls --- */

.field { margin-bottom: 16px; }
.field label {
  display: block; margin-bottom: 8px;
  font-size: 11px; letter-spacing: .14em; text-transform: uppercase; color: var(--ember);
}
.input, select.input {
  width: 100%; padding: 12px 16px; border-radius: var(--r-sm);
  background: var(--surface); border: 1px solid var(--line); color: var(--ink);
  font-family: var(--mono); font-size: 16px; outline: none; appearance: none;
  transition: border-color .18s ease;
}
.input:focus { border-color: var(--dusk); }
.input::placeholder { color: var(--faint); }

.stepper { display: flex; align-items: center; gap: 12px; }
.stepper .input { text-align: center; font-family: var(--display); font-size: 21px; font-weight: 600; }
.step-btn {
  width: 46px; height: 46px; flex: 0 0 auto; border-radius: var(--r-sm);
  background: var(--surface); border: 1px solid var(--line);
  font-size: 19px; display: grid; place-items: center;
}
.step-btn:active { background: var(--surface-2); }

.quick { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 12px; }
.quick button {
  padding: 8px 16px; border-radius: 999px; font-family: var(--mono); font-size: 13px;
  background: var(--surface); border: 1px solid var(--line); color: var(--ink);
  transition: background .18s, color .18s, border-color .18s, transform .12s;
}
.quick button:active { transform: scale(.97); }
.quick button.on { background: var(--dusk); border-color: var(--dusk); color: var(--dusk-ink); }

.opts { display: grid; gap: 8px; }
.opt {
  display: flex; align-items: center; gap: 12px; padding: 12px; text-align: left; width: 100%;
  border-radius: var(--r-md); background: var(--surface); border: 1px solid var(--line);
  transition: border-color .18s ease, background .18s ease, transform .12s;
}
.opt:active { transform: scale(.99); }
.opt.on { border-color: var(--dusk); background: var(--surface-2); }
.opt-main { flex: 1; }
.opt-title { font-size: 14px; font-weight: 500; }
.opt-sub { font-size: 11.5px; color: var(--faint); margin-top: 2px; }
.opt-price { font-family: var(--mono); font-size: 13.5px; color: var(--ember); flex: 0 0 auto; }
/* A method that cannot pay this order yet — shown rather than hidden, so the
   customer sees it exists and what is missing. */
.opt-off { opacity: .62; border-style: dashed; }
.opt-off .opt-price { color: var(--faint); text-decoration: line-through; }

/* Счёт по строкам: из чего складывается итог. Нужен там, где к цене товара
   добавляется что-то ещё — иначе сумма в конце выглядит как чужая. */
.bill { margin-top: 24px; display: grid; gap: 8px; }
.bill-row { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.bill-row span { font-size: 13px; color: var(--muted); }
.bill-row i { display: block; font-style: normal; font-size: 11px; color: var(--faint); }
.bill-row b { font-family: var(--mono); font-size: 13.5px; font-weight: 500; }

/* После разбивки итог отделяется от неё, а не от предыдущего блока. */
.bill + .total { margin-top: 12px; }

.total {
  display: flex; align-items: baseline; justify-content: space-between;
  margin: 24px 0 16px; padding-top: 16px; border-top: 1px solid var(--line);
}
.total span { font-size: 13px; color: var(--muted); }
.total b {
  font-family: var(--display); font-weight: 600; font-size: 25px; letter-spacing: -.03em;
  text-align: right;
}
.total small { display: block; font-family: var(--mono); font-size: 12px;
               font-weight: 400; color: var(--faint); }

.btn {
  width: 100%; padding: 16px; border-radius: var(--r-md);
  font-size: 15.5px; font-weight: 500; letter-spacing: .01em;
  transition: transform .12s ease, opacity .18s ease, background .18s ease;
}
.btn:active { transform: scale(.99); }
.btn[disabled] { opacity: .45; pointer-events: none; }
.btn-primary { background: var(--dusk); color: var(--dusk-ink); }
.btn-amber { background: var(--amber); color: #17140D; }
.btn-ghost { background: var(--surface); border: 1px solid var(--line); color: var(--ink); }
.btn + .btn { margin-top: 8px; }

.note { font-size: 12.5px; color: var(--faint); line-height: 1.5; margin-top: 16px; }
.note-warn {
  padding: 12px 16px; border-radius: var(--r-sm); margin-top: 16px;
  background: rgba(255,197,61,.14); border: 1px solid rgba(255,197,61,.3);
  color: var(--ember); font-size: 12.5px; line-height: 1.5;
}

.spin {
  width: 16px; height: 16px; border-radius: 50%; display: inline-block; vertical-align: -3px;
  border: 2px solid rgba(255,243,210,.35); border-top-color: currentColor;
  animation: spin .7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg) } }

/* ---------------------------------------------------------------- dawn --- */
/* The stars purchase — the shop's main product — gets the full sunrise. */

.dawn-top { display: flex; align-items: center; justify-content: space-between; }
.dawn-mark {
  font-family: var(--display); font-weight: 600; font-size: 13px;
  letter-spacing: .14em; text-transform: uppercase;
  display: flex; align-items: baseline; gap: 8px;
}
.dawn-mark i { color: var(--amber); font-style: normal; font-size: 11px; }
.dawn-mark span {
  font-family: var(--body); font-weight: 500; font-size: 11.5px;
  letter-spacing: .02em; text-transform: none; color: var(--muted);
}
.dawn-rate {
  font-family: var(--body); font-size: 12.5px; font-weight: 500;
  font-variant-numeric: tabular-nums; letter-spacing: -.01em; color: var(--muted);
}

.dawn-sky {
  position: relative; margin-top: 20px; padding-top: 40px;
  overflow: hidden; border-bottom: 1px solid var(--line);
}
.dawn-glow {
  position: absolute; left: 50%; bottom: -1px;
  width: 320px; height: 320px; margin-left: -160px; margin-bottom: -160px;
  border-radius: 50%; pointer-events: none;
  background: radial-gradient(closest-side, var(--glow-a), var(--glow-b) 62%, transparent 78%);
  transform: scale(var(--rise, 1));
  transition: transform .7s cubic-bezier(.22,.7,.2,1);
}
.dawn-eyebrow {
  position: relative; margin: 0 0 6px; text-align: center;
  font-size: 11px; letter-spacing: .14em; text-transform: uppercase; color: var(--ember);
}
.dawn-count {
  position: relative; margin: 0; text-align: center;
  font-family: var(--display); font-weight: 600;
  font-size: clamp(62px, 20vw, 96px); line-height: .92; letter-spacing: -.045em;
  font-variant-numeric: tabular-nums;
}
.dawn-unit {
  position: relative; margin: 8px 0 24px; text-align: center;
  font-size: 12.5px; letter-spacing: .06em; color: var(--muted);
}

.dawn-chips { display: flex; flex-wrap: wrap; gap: 8px; padding-top: 20px; }
.dawn-chips button {
  padding: 8px 16px; border-radius: 999px;
  border: 1px solid var(--line); background: var(--surface); color: var(--ink);
  font-family: var(--mono); font-size: 13.5px;
  transition: background .18s, color .18s, border-color .18s, transform .12s;
}
.dawn-chips button:active { transform: scale(.97); }
.dawn-chips button.on { background: var(--dusk); border-color: var(--dusk); color: var(--dusk-ink); }
.dawn-chips button.word { font-family: var(--body); font-size: 13px; }

/* Покупка идёт двумя шагами: сколько — и только потом кому. Полоска сверху
   говорит, где мы и сколько всего шагов: без неё «Далее» ведёт в неизвестность,
   а до цены на этом экране человек уже дошёл. */
.dawn-steps {
  display: flex; align-items: center; gap: 10px; padding-top: 22px;
  font-size: 11px; letter-spacing: .12em; text-transform: uppercase; color: var(--faint);
}
.dawn-steps span.on { color: var(--ember); font-weight: 500; }
.dawn-step .dawn-chips { padding-top: 12px; }
/* В обычных шторках между детьми уже есть свой отступ — свой добавлять незачем,
   иначе полоска шагов отъезжает от заголовка на две дистанции сразу. */
.sheet:not(.dawn) .dawn-steps { padding-top: 2px; }
.sheet:not(.dawn) .dawn-step > .dawn-back { margin-top: 0; margin-bottom: 4px; }

/* Возврат к количеству — не стрелка в пустоту, а напоминание о выбранном:
   на втором шаге числа уже не видно, а поменять его хочется именно здесь. */
.dawn-back {
  display: inline-flex; align-items: center; gap: 6px; margin-top: 16px;
  padding: 7px 12px; border-radius: 999px;
  background: var(--press); border: 1px solid var(--line);
  font-family: var(--mono); font-size: 12.5px; color: var(--muted);
  transition: transform .12s, border-color .2s;
}
.dawn-back:active { transform: scale(.98); }

.dawn-label {
  display: block; margin: 24px 0 8px;
  font-size: 11px; letter-spacing: .14em; text-transform: uppercase; color: var(--ember);
}
.dawn-seg { display: flex; gap: 6px; padding: 4px; border-radius: var(--r-md); background: var(--press); }
.dawn-seg button {
  flex: 1; padding: 8px; border-radius: 10px; background: transparent; color: var(--ink);
  font-size: 14px; transition: background .18s, box-shadow .18s;
}
.dawn-seg button.on { background: var(--surface); box-shadow: 0 1px 2px rgba(23,20,13,.1); font-weight: 500; }

.dawn-field {
  display: flex; align-items: center; gap: 2px; margin-top: 12px;
  border: 1px solid var(--line); border-radius: var(--r-sm);
  background: var(--surface); padding: 0 16px;
}
.dawn-field:focus-within { border-color: var(--dusk); }
.dawn-field b { font-family: var(--mono); font-weight: 400; color: var(--faint); }
.dawn-field input {
  flex: 1; min-width: 0; border: 0; background: transparent; padding: 12px 0;
  color: var(--ink); font-family: var(--mono); font-size: 16px; outline: none;
}
.dawn-input {
  width: 100%; margin-top: 12px; padding: 12px 16px; border-radius: var(--r-sm);
  border: 1px solid var(--line); background: var(--surface); color: var(--ink);
  font-family: var(--mono); font-size: 16px; outline: none;
}
/* Кому уйдут звёзды при выборе «Себе»: лицо и имя вместо одной строчки текста.
   Аккаунт после оплаты не переиграть, поэтому получателя стоит узнать в лицо
   до неё, а не после. Фото приходит из initData, буква остаётся под ним. */
.dawn-me {
  display: flex; align-items: center; gap: 10px;
  margin-top: 12px; padding: 10px 12px;
  border-radius: var(--r-md); background: var(--press); border: 1px solid var(--line);
}
.dawn-me-who { display: grid; min-width: 0; }
.dawn-me-who b {
  font-size: 14px; font-weight: 500;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.dawn-me-who small { font-family: var(--mono); font-size: 12px; color: var(--muted); }

.dawn-note { margin: 12px 2px 0; font-size: 13px; color: var(--muted); }
.dawn-hint { margin: 8px 2px 0; font-size: 13px; color: var(--bad); }
/* Подсказка под получателем говорит три вещи, и цвет у каждой свой: отказ —
   красным (по умолчанию), подтверждение — зелёным, ожидание — приглушённым. */
.dawn-hint.ok { color: var(--good); }
/* «Проверяем…» дышит, а не висит статичной надписью: медленный пульс читается
   как «идёт работа», и секунда ожидания уже не выглядит зависанием. */
.dawn-hint.checking { color: var(--faint); animation: hintPulse 1.1s ease-in-out infinite; }
@keyframes hintPulse { 0%, 100% { opacity: .45 } 50% { opacity: 1 } }
@media (prefers-reduced-motion: reduce) { .dawn-hint.checking { animation: none } }

.dawn-total {
  display: flex; align-items: baseline; justify-content: space-between;
  margin-top: 24px; padding-top: 16px; border-top: 1px solid var(--line);
}
.dawn-total dt { font-size: 13px; color: var(--muted); }
.dawn-total dd {
  text-align: right; font-family: var(--display); font-weight: 600;
  font-size: 25px; letter-spacing: -.03em;
}
.dawn-total small { display: block; font-family: var(--mono); font-size: 12px;
                    font-weight: 400; color: var(--faint); }

.dawn-buy {
  width: 100%; margin-top: 16px; padding: 16px; border-radius: var(--r-md);
  background: var(--dusk); color: var(--dusk-ink); font-size: 16px; font-weight: 500;
  transition: background .18s, transform .12s;
}
.dawn-buy:active { transform: scale(.99); }
.dawn-buy[disabled] { opacity: .45; pointer-events: none; }

.dawn-assure {
  margin: 16px 0 0; padding: 0; list-style: none;
  display: flex; flex-wrap: wrap; gap: 6px 16px;
  font-size: 12.5px; color: var(--muted);
}
.dawn-assure li::before { content: '✦'; color: var(--amber); margin-right: 6px; font-size: 10px; }

.steps-list { margin: 16px 0; padding: 0; list-style: none; counter-reset: step; }
.steps-list li {
  counter-increment: step; position: relative;
  padding: 12px 0 12px 32px; border-top: 1px solid var(--line);
  font-size: 13.5px; line-height: 1.45;
}
.steps-list li:last-child { border-bottom: 1px solid var(--line); }
.steps-list li::before {
  content: counter(step); position: absolute; left: 0; top: 13px;
  font-family: var(--mono); font-size: 12px; color: var(--ember);
}
.steps-list b { font-weight: 600; }

/* ------------------------------------------------------------ filters --- */

.filters { display: grid; gap: 8px; margin-bottom: 16px; }
.filter-search { font-family: var(--body); font-size: 15px; }
.filter-row { display: flex; gap: 8px; overflow-x: auto; scrollbar-width: none;
              padding-bottom: 2px; }
.filter-row::-webkit-scrollbar { display: none; }
/* Native <select> drops the OS picker on top of the app and ignores every
   token in this file. These chips open a sheet styled like everything else. */
.chip-filter {
  flex: 0 0 auto; max-width: 60vw;
  display: flex; align-items: center; gap: 6px;
  padding: 8px 16px; border-radius: 999px;
  background: var(--surface); border: 1px solid var(--line); color: var(--muted);
  font-size: 13px; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
  transition: background .18s, color .18s, border-color .18s, transform .12s;
}
.chip-filter::after {
  content: '⌄'; font-size: 13px; line-height: 0.6; opacity: .6; flex: 0 0 auto;
}
.chip-filter:active { transform: scale(.97); }
.chip-filter.on {
  background: var(--dusk); border-color: var(--dusk); color: var(--dusk-ink);
}
.chip-filter.on::after { opacity: .8; }
.chip-filter[hidden] { display: none !important; }
.chip-filter.loading { opacity: .55; pointer-events: none; }

/* The picker itself: a searchable list with a tick on the current choice. */
.picker-search { margin-bottom: 12px; }
.picker-list { display: grid; gap: 2px; max-height: 56dvh; overflow-y: auto; margin: 0 -6px; }
.picker-item {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  width: 100%; text-align: left; padding: 12px 16px; border-radius: var(--r-md);
  font-size: 14.5px; color: var(--ink); transition: background .15s;
}
.picker-item:active { background: var(--press); }
.picker-item.on { background: var(--surface-2); font-weight: 500; }
.picker-item.on::after { content: '✓'; color: var(--amber); font-size: 15px; }
.picker-item span { color: var(--faint); font-size: 12px; }
.picker-empty { padding: 24px 12px; text-align: center; color: var(--faint); font-size: 13px; }
.filter-reset {
  justify-self: start; font-size: 12.5px; color: var(--ember);
  padding: 4px 0; text-decoration: underline;
}

.pager {
  display: flex; align-items: center; justify-content: center;
  gap: 6px; flex-wrap: wrap; margin-top: 20px;
  animation: enter .4s cubic-bezier(.22,.8,.25,1) backwards;
}
.pager-btn {
  min-width: 42px; height: 42px; padding: 0 12px; border-radius: var(--r-md);
  display: grid; place-items: center;
  font-family: var(--mono); font-size: 14px;
  background: var(--surface); border: 1px solid var(--line); color: var(--ink);
  transition: background .2s, color .2s, border-color .2s, transform .14s;
}
.pager-btn:active { transform: scale(.94); background: var(--surface-2); }
.pager-btn.on {
  background: var(--dusk); border-color: var(--dusk); color: var(--dusk-ink);
  font-weight: 600;
}
.pager-btn[disabled] { opacity: .3; pointer-events: none; }
.pager-gap { color: var(--faint); padding: 0 2px; font-family: var(--mono); }

/* The list fades through on a page change, so the swap reads as movement
   rather than the same block of cards flickering. */
.rent-list.turning { animation: pageTurn .34s cubic-bezier(.22,.8,.25,1); }
@keyframes pageTurn {
  from { opacity: 0; transform: translateY(10px) }
  to   { opacity: 1; transform: none }
}

/* ------------------------------------------------------------- wallet --- */

.wallet-panel { border-color: var(--line-2); }
.wallet-big { display: flex; align-items: baseline; gap: 8px; margin-bottom: 16px; }
.wallet-big b {
  font-family: var(--display); font-weight: 600;
  font-size: 30px; letter-spacing: -.03em;
}
.wallet-big span { font-family: var(--mono); font-size: 13px; color: var(--muted); }
button.wallet { width: 100%; text-align: left; }
button.wallet:active { background: var(--surface-2); }

/* -------------------------------------------------------------- toasts --- */

.toast-host { position: fixed; left: 0; right: 0; top: 0; z-index: 80;
              display: grid; gap: 8px; padding: 12px var(--pad); pointer-events: none; }
.toast {
  padding: 12px 16px; border-radius: var(--r-md); font-size: 13.5px; font-weight: 500;
  background: var(--surface); border: 1px solid var(--line-2);
  box-shadow: var(--shadow); animation: toastIn .32s cubic-bezier(.22,.8,.25,1);
}
@keyframes toastIn { from { opacity: 0; transform: translateY(-14px) } }
.toast.err { border-color: rgba(140,47,18,.4); color: var(--bad); }
.toast.ok  { border-color: rgba(47,125,82,.4); color: var(--good); }

/* The admin panel used to live here, inside the customer's app. It moved to
   its own page (`/admin`, login link from the bot) so that none of it — markup,
   styles or the calls it makes — ships to buyers at all. */

/* --------------------------------------------------------- motion ------- */
/* Everything below is what makes the app feel alive rather than painted:
   content arrives in sequence, artwork breathes, and the sun above the shop
   pulses slowly. All of it collapses under prefers-reduced-motion. */

/* Children of a view arrive one after another instead of all at once. */
.tiles-row, .tile-gifts, .tile-rent, .wallet, .hero-title, .hero-lede,
#ordersPanel, .stat-row, .panel, .segmented {
  animation: enter .5s cubic-bezier(.22,.8,.25,1) backwards;
}
.hero-title { animation-delay: .04s }
.hero-lede  { animation-delay: .08s }
.wallet     { animation-delay: .12s }
.tiles-row  { animation-delay: .16s }
.tile-gifts { animation-delay: .22s }
.tile-rent  { animation-delay: .28s }
#ordersPanel{ animation-delay: .34s }
.segmented  { animation-delay: .05s }
.stat-row   { animation-delay: .10s }

@keyframes enter {
  from { opacity: 0; transform: translateY(16px) }
  to   { opacity: 1; transform: none }
}

/* Grid children stagger by position — the catalogue fills in, row by row. */
.gift-grid .gift, .rent-list .rent-card, .order-list .order {
  animation: enter .46s cubic-bezier(.22,.8,.25,1) backwards;
}
.gift-grid .gift:nth-child(1),  .rent-list .rent-card:nth-child(1),  .order-list .order:nth-child(1)  { animation-delay: .02s }
.gift-grid .gift:nth-child(2),  .rent-list .rent-card:nth-child(2),  .order-list .order:nth-child(2)  { animation-delay: .06s }
.gift-grid .gift:nth-child(3),  .rent-list .rent-card:nth-child(3),  .order-list .order:nth-child(3)  { animation-delay: .10s }
.gift-grid .gift:nth-child(4),  .rent-list .rent-card:nth-child(4),  .order-list .order:nth-child(4)  { animation-delay: .14s }
.gift-grid .gift:nth-child(5),  .rent-list .rent-card:nth-child(5),  .order-list .order:nth-child(5)  { animation-delay: .18s }
.gift-grid .gift:nth-child(6),  .rent-list .rent-card:nth-child(6),  .order-list .order:nth-child(6)  { animation-delay: .22s }
.gift-grid .gift:nth-child(7),  .rent-list .rent-card:nth-child(7)  { animation-delay: .26s }
.gift-grid .gift:nth-child(8),  .rent-list .rent-card:nth-child(8)  { animation-delay: .30s }
.gift-grid .gift:nth-child(n+9), .rent-list .rent-card:nth-child(n+9) { animation-delay: .34s }

/* The artwork bobs gently, each item out of phase with its neighbours. */
.gift-art img, .gift-art { animation: bob 4.2s ease-in-out infinite; }
.gift:nth-child(2n) .gift-art { animation-delay: -1.1s }
.gift:nth-child(3n) .gift-art { animation-delay: -2.3s }
.gift:nth-child(5n) .gift-art { animation-delay: -3.1s }
@keyframes bob { 0%,100% { transform: translateY(0) } 50% { transform: translateY(-5px) } }
.gift-art { animation-name: none; }
.gift-art img { will-change: transform; }

/* A slow warm pulse under the gift art, so the grid never looks frozen. */
.gift-art::after {
  content: ''; position: absolute; inset: 0; border-radius: inherit;
  background: radial-gradient(closest-side, var(--glow-a), transparent 70%);
  opacity: .5; animation: breathe 5s ease-in-out infinite;
}
.gift-art { position: relative; }
.gift-art img { position: relative; z-index: 1; }
@keyframes breathe { 0%,100% { opacity: .35; transform: scale(.9) } 50% { opacity: .7; transform: scale(1.05) } }

/* The tile glow sweeps once when the shop opens. */
.tile::after { animation: sweep 6s ease-in-out infinite; }
@keyframes sweep { 0%,100% { transform: scale(1) } 50% { transform: scale(1.16) } }

/* Segmented control: the active pill slides rather than snapping. */
.segmented button, .dawn-seg button { transition: background .24s cubic-bezier(.3,1.2,.4,1),
                                                  color .2s, box-shadow .24s, transform .14s; }
.segmented button:active, .dawn-seg button:active { transform: scale(.96); }

/* The star on the stars tile turns slowly — the shop's one bit of whimsy. */
.tile-stars .tile-glyph { display: inline-block; animation: spinSlow 14s linear infinite; }
@keyframes spinSlow { to { transform: rotate(360deg) } }

.boot-mark { animation: sunPulse 2.2s ease-in-out infinite, bootRise .6s cubic-bezier(.22,.8,.25,1); }
@keyframes bootRise { from { opacity: 0; transform: translateY(20px) scale(.8) } }

/* Отклик на касание. Кнопки его имели, а карточки — нет: список ощущался
   картинкой, хотя каждая строка нажимается. Одна кривая и один масштаб на всё,
   чтобы приложение отвечало одинаково. */
.order, .gift, .rent-card, .opt, .tile, .review, .tier, .quick button,
.chip-filter, .sheet-x, .pager button, .contest, .board-card, .wallet {
  transition: transform .14s cubic-bezier(.2,.8,.3,1), background .2s ease,
              border-color .2s ease;
}
.order:active, .gift:active, .rent-card:active, .opt:active, .review:active,
.chip-filter:active, .pager button:active,
.contest:active, .board-card:active, .wallet:active { transform: scale(.985); }
.tile:active { transform: scale(.975); }
.quick button:active, .sheet-x:active { transform: scale(.94); }

/* На мышке добавляем наведение — на телефоне его не существует, и тратить на
   него состояния нельзя. */
@media (hover: hover) {
  .order:hover, .gift:hover, .rent-card:hover, .opt:hover, .review:hover {
    background: var(--surface-2); border-color: var(--line-2);
  }
  .btn-ghost:hover { background: var(--surface-2); }
  .btn-primary:hover, .btn-amber:hover { filter: brightness(1.06); }
  .link:hover { color: var(--amber); }
}

/* Итог отвечает на каждое изменение срока — короткий толчок, чтобы сумма
   читалась как результат нажатия. Счётчику звёзд он не нужен: тот и так
   считает вверх. */
.total b.bump { animation: bump .26s cubic-bezier(.2,.9,.3,1); display: inline-block; }
@keyframes bump {
  0%   { transform: scale(1) }
  40%  { transform: scale(1.06) }
  100% { transform: scale(1) }
}

.tabbar button { transition: color .2s ease, transform .14s cubic-bezier(.2,.8,.3,1); }
.tabbar button:active { transform: scale(.94); }

/* Активная вкладка. Съезжающую плашку я пробовал сделать отдельным элементом и
   псевдоэлементом — в grid-панели абсолютный ребёнок получает систему координат
   нулевой ширины, и `width` схлопывается в нуль независимо от inline-стиля.
   Оставляю подсветку на самой кнопке: она плавно наливается, а иконка
   приподнимается — этого достаточно, и это работает везде. */
.tabbar button.on { background: var(--press); }
.tabbar button { transition: color .24s ease, background .28s cubic-bezier(.16,1,.3,1),
                             transform .14s cubic-bezier(.2,.8,.3,1); }
.tabbar button:active { transform: scale(.94); }

/* Содержимое шторки приезжает по порядку — как и разделы приложения. Так любая
   шторка ощущается собранной на месте, а не подставленной целиком. */
.sheet-body > * { animation: enter .42s cubic-bezier(.16,1,.3,1) backwards; }
.sheet-body > *:nth-child(1) { animation-delay: .02s }
.sheet-body > *:nth-child(2) { animation-delay: .06s }
.sheet-body > *:nth-child(3) { animation-delay: .10s }
.sheet-body > *:nth-child(4) { animation-delay: .14s }
.sheet-body > *:nth-child(5) { animation-delay: .18s }
.sheet-body > *:nth-child(6) { animation-delay: .22s }
.sheet-body > *:nth-child(n+7) { animation-delay: .26s }
/* Пока шторку тянут, ничего не должно «доезжать» — иначе содержимое дрожит. */
.sheet.is-dragging .sheet-body > * { animation: none; }

/* Полоса уровня наливается, а не появляется заполненной. */
.tier-bar i { transition: width .7s cubic-bezier(.16,1,.3,1); }

/* Заглушка на время загрузки: по ней видно, что данные едут, и на каком месте
   они появятся. Раньше на этом месте была строка «Загружаем…». */
.skeleton {
  position: relative; overflow: hidden;
  background: var(--press); border-radius: var(--r-md);
}
.skeleton::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(90deg, transparent, rgba(247,235,208,.07) 45%,
                                     rgba(247,235,208,.12) 50%, transparent 100%);
  animation: shimmer 1.25s ease-in-out infinite;
}
@keyframes shimmer { from { transform: translateX(-100%) } to { transform: translateX(100%) } }
/* Заглушки ложатся прямо в сетку каталога или списка, поэтому своей раскладки
   у них нет — только размеры под ту карточку, которую они заменяют. */
.skeleton-card { height: 76px; }
.skeleton-tile { height: 168px; border-radius: var(--r-lg); }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important; animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}

/* Плашка с цифрами про клавиатуру. Живёт только у администратора и только
   когда он сам её позвал: тройной тап по шапке. Нужна, чтобы чинить то, что
   не воспроизводится нигде, кроме конкретного телефона. */
.kb-debug {
  position: fixed; top: calc(8px + var(--safe-t, 0px)); right: 8px; z-index: 200;
  margin: 0; padding: 8px 10px; border-radius: 10px;
  background: rgba(23,20,13,.86); color: #ffd479;
  font-family: var(--mono); font-size: 11px; line-height: 1.5;
  white-space: pre; pointer-events: none;
}


/* «Когда» — тот же ритм, что у «Кому»: это одно решение, просто в две строки. */
.when { margin-top: 4px; }
.when .when-at { margin-top: 12px; }
.when .when-note { color: var(--ember); }
