.btn {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    width: 100%;
    min-height: 50px;
    padding: 14px var(--space-4);
    border: 0;
    border-radius: var(--radius);
    background: var(--accent);
    color: var(--on-accent);
    font-weight: 600;
    text-align: center;
    cursor: pointer;
    box-shadow: var(--lift);
    transition: transform var(--press), filter var(--press), opacity var(--press);
    user-select: none;
}
.btn__icon { width: 18px; height: 18px; }
.btn:active { transform: scale(.975); filter: brightness(.92); }
.btn:disabled { opacity: .45; transform: none; filter: none; }
.btn--secondary { background: var(--surface); color: var(--accent-text); box-shadow: none; }
.btn--secondary:active { filter: none; background: var(--tint-strong); }
.btn--danger { background: transparent; color: var(--danger); box-shadow: none; }
.btn--danger:active { filter: none; background: var(--tint); }

/* Занятая кнопка не гаснет, а крутит спиннер: гашение читается как поломка. */
.btn--busy { pointer-events: none; filter: brightness(.9); }
.btn--busy::after {
    content: "";
    width: 16px;
    height: 16px;
    border: 2px solid currentColor;
    border-top-color: transparent;
    border-radius: 50%;
    animation: spin .7s linear infinite;
}
@keyframes spin { to { transform: rotate(1turn); } }

.actions { display: flex; flex-direction: column; gap: var(--space-2); }
/* Действия прижаты к низу: экран перестаёт быть шапкой над пустотой,
   а кнопка оказывается там, где её ищет большой палец. */
.actions--bottom { margin-top: auto; padding-top: var(--space-4); }
/* Пояснение стоит у кнопки, которую объясняет, а не отдельным блоком посреди экрана. */
.actions .hint { margin: 0 0 var(--space-1); text-align: center; }

.glyph {
    display: grid;
    place-items: center;
    width: 92px;
    height: 92px;
    border-radius: 50%;
    background: var(--tint);
    color: var(--hint);
}
.glyph svg { width: 42px; height: 42px; }
.glyph--sm { width: 38px; height: 38px; flex: none; }
.glyph--sm svg { width: 20px; height: 20px; }
/* Без второй стороны знак сам занимает место аватарки: у промо и у перевода на номер лица нет. */
.glyph--md { width: 64px; height: 64px; margin: 0 auto var(--space-3); }
.glyph--md svg { width: 28px; height: 28px; }
/* Бонус, касса и пополнение стоят в строке вместо лица, поэтому заливка у них плотная,
   как у заглушки с инициалом: иначе такая строка выглядит бледнее соседних. Цвета взяты
   из той же палитры, что и заглушки аватарок. */
.glyph--bonus, .glyph--unlock { color: #fff; background: linear-gradient(180deg, #bda9f2, #a695e7); }
.glyph--cash { color: #fff; background: linear-gradient(180deg, #ffc38a, #faa774); }
.glyph--topup { color: #fff; background: linear-gradient(180deg, #97d97b, #7bc862); }
.glyph--in { color: var(--ok); background: color-mix(in srgb, var(--ok) 12%, transparent); }

/* Тип перевода жетоном на аватарке: ради отдельного блока экран перестаёт помещаться.
   Заливка сплошная, иначе фотография просвечивает сквозь значок. */
.avatar-stack { position: relative; display: inline-block; flex: none; }
.card__head .avatar-stack { margin-bottom: var(--space-3); }
.card__person { display: inline-block; color: inherit; text-decoration: none; transition: transform var(--press); }
.card__person:active { transform: scale(.94); }
.glyph--badge {
    position: absolute;
    /* Центр жетона на 2px внутри кромки круга: цветной кружок наполовину снаружи, а белая
       обводка читается как выемка в фотографии. Глубже обводка замыкается почти в кольцо,
       и жетон выглядит дыркой в снимке, а не значком на краю. Обводка того же цвета, что
       фон карточки, поэтому снаружи круга она невидима и «выступом» не считается. */
    right: -2px;
    bottom: -2px;
    width: 26px;
    height: 26px;
    border: 3px solid var(--surface);
    background: var(--hint);
    color: var(--surface);
}
.glyph--badge.glyph--in { background: var(--ok); }
.glyph--badge.glyph--cash { background: #faa774; }
.glyph--badge svg { width: 13px; height: 13px; }
/* Жетон в строке списка мельче, поэтому линии рисунка приходится утолщать. */
.avatar-stack--sm .glyph--badge { right: -1px; bottom: -1px; width: 18px; height: 18px; border-width: 2px; }
.avatar-stack--sm .glyph--badge svg { width: 11px; height: 11px; stroke-width: 2.4; }

/* Заглушка повторяет телеграмовскую: кружок с градиентом и инициалом. Цвет закреплён
   за человеком, поэтому строка узнаётся даже без фотографии. */
.avatar {
    position: relative;
    display: grid;
    place-items: center;
    width: 38px;
    height: 38px;
    flex: none;
    border-radius: 50%;
    overflow: hidden;
    color: #fff;
    font-size: 15px;
    font-weight: 600;
    line-height: 1;
    user-select: none;
}
.avatar img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.avatar--lg { width: 64px; height: 64px; font-size: 26px; }
.avatar--t0 { background: linear-gradient(180deg, #ff8f7a, #e17076); }
.avatar--t1 { background: linear-gradient(180deg, #ffc38a, #faa774); }
.avatar--t2 { background: linear-gradient(180deg, #bda9f2, #a695e7); }
.avatar--t3 { background: linear-gradient(180deg, #97d97b, #7bc862); }
.avatar--t4 { background: linear-gradient(180deg, #8fdcde, #6ec9cb); }
.avatar--t5 { background: linear-gradient(180deg, #7fc0ea, #65aadd); }
.avatar--t6 { background: linear-gradient(180deg, #f795c2, #ee7aae); }

/* Перевод показан одним чеком: шапка и факты в общей карточке, разделённые волоском. */
.card { background: var(--surface); border-radius: var(--radius-lg); overflow: hidden; }
/* Делать на завершённом переводе нечего, кнопок нет: карточка встаёт по центру,
   иначе она висит вверху над пустым экраном. */
.screen:not(:has(.actions)) .card { margin-block: auto; }
.card__head { padding: var(--space-6) var(--space-4) var(--space-5); text-align: center; }
.card .list { border-top: 1px solid var(--line); border-radius: 0; }

.list:empty { display: none; }
.list { list-style: none; margin: 0; padding: 0; background: var(--surface); border-radius: var(--radius); overflow: hidden; }
.row {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    min-height: 56px;
    transition: background var(--press);
}
a.row:active, label.row:active { background: var(--tint-strong); }
.list > li + li .row { border-top: 1px solid var(--line); }
.row__main { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.row__title { font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.row__meta { color: var(--hint); font-size: var(--text-sm); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.row__side { flex: none; }
/* Строка факта: слева спокойная подпись, справа само значение. */
.row--fact { min-height: 44px; }
.row--fact .row__title { font-weight: 400; color: var(--hint); }
.row--fact .row__side { font-weight: 500; max-width: 62%; text-align: right; }
.accent { color: var(--accent-text); }

.amount { font-variant-numeric: tabular-nums; font-weight: 600; }
.amount--in { color: var(--ok); }
.row__title { color: var(--text); }

/* Выбор получателя. Закрытый вид повторяет строку списка, открытый кладёт поверх
   карточку с поиском: список известен заранее, поэтому фильтрация идёт на месте. */
.picker { position: relative; }
.picker__control {
    width: 100%;
    background: var(--surface);
    border: 0;
    border-radius: var(--radius);
    text-align: left;
    cursor: pointer;
    transition: background var(--press);
}
.picker__control:active { background: var(--tint-strong); }
.picker__control .row__title { color: var(--hint); font-weight: 400; }
.picker--chosen .row__title { color: var(--text); font-weight: 500; }
.picker__face:empty { display: none; }
.picker__chevron { width: 20px; height: 20px; flex: none; color: var(--hint); transition: transform var(--press); }
.picker__control[aria-expanded="true"] .picker__chevron { transform: rotate(180deg); }
.picker__panel {
    position: absolute;
    z-index: 5;
    top: calc(100% + var(--space-2));
    left: 0;
    right: 0;
    padding: var(--space-2);
    border-radius: var(--radius);
    background: var(--surface);
    box-shadow: 0 12px 32px -12px color-mix(in srgb, var(--text) 45%, transparent);
    animation: drop .16s cubic-bezier(.2, .7, .3, 1) both;
}
@keyframes drop { from { opacity: 0; transform: translateY(-6px); } }
.picker__search { background: var(--bg); }
.picker__options { max-height: 38vh; overflow-y: auto; margin-top: var(--space-2); background: transparent; }
.picker__option { width: 100%; border: 0; background: transparent; text-align: left; cursor: pointer; }
.picker__option:active { background: var(--tint-strong); }
.picker__option--chosen { background: var(--accent-tint); }
.picker__group {
    padding: var(--space-2) var(--space-4) var(--space-1);
    font-size: var(--text-sm);
    color: var(--section-title);
}
.picker__group + li .row { border-top: 0; }
.picker__empty { margin: var(--space-3) var(--space-4); }

/* Частые получатели: одни лица в ряд, без имён — так их видно больше и ряд не разъезжается. */
/* Частые: колонка фиксированной ширины, под лицом одна строка имени по центру, как в листах
   Telegram. Длинное имя обрезается многоточием, полоса листается по горизонтали. */
.faces { display: flex; gap: var(--space-2); overflow-x: auto; padding-bottom: var(--space-1); }
.face { flex: none; width: 66px; display: flex; flex-direction: column; align-items: center; gap: 6px; color: var(--text); text-decoration: none; transition: transform var(--press); }
.face:active { transform: scale(.92); }
.face__name { max-width: 100%; font-size: 12px; line-height: 1.2; text-align: center; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.face .avatar, .face .glyph { width: 54px; height: 54px; font-size: 21px; }
.face .glyph svg { width: 26px; height: 26px; }

.field { display: flex; flex-direction: column; gap: var(--space-1); }
.field__label { color: var(--hint); font-size: var(--text-sm); }
.field__error { display: block; margin: 0; color: var(--danger); font-size: var(--text-sm); }
.field__error:empty { display: none; }
/* Сумма больше баланса: краснеет само число — у поля нет рамки, которую можно было бы подсветить. */
.amount-field--error .input--amount, .amount-field--error .amount-field__sign { color: var(--danger); }
/* appearance сбрасываем ради type=search: в iOS поле иначе оставляет свой фон и скругление
   и стоит в ряду с кнопкой как чужое. Высота и радиус общие с кнопкой. */
.input {
    appearance: none;
    width: 100%;
    min-height: 50px;
    padding: 13px var(--space-4);
    border: 0;
    border-radius: var(--radius);
    background: var(--surface);
    outline: none;
}
.input:focus { box-shadow: 0 0 0 2px var(--accent); }
/* Знак валюты внутри поля, значение остаётся чистым числом, как в сумме. Правило ниже общего
   .input: иначе его padding перекрыл бы отступ под знак, и цифры легли бы поверх него. */
.field__input { position: relative; display: block; }
.field__sign { position: absolute; left: var(--space-4); top: 50%; transform: translateY(-50%); color: var(--hint); pointer-events: none; }
.input--signed { padding-left: calc(var(--space-4) + 1.1em); }
/* Знак валюты стоит рядом с полем, а не внутри значения: в поле остаётся чистое
   число, а пара «$50» центруется целиком, как сумма на балансе. Ширину полю задаёт
   количество цифр: они моноширинные, поэтому ch считает её точно. */
.amount-field { display: flex; align-items: baseline; justify-content: center; }
.amount-field__sign {
    font-size: var(--text-hero);
    font-weight: 700;
    letter-spacing: -.035em;
    line-height: 1;
    margin-right: -.035em;
}
/* Пустое поле: знак гаснет вместе с подсказкой, иначе чёрный «$» стоит над блёклым нулём. */
.amount-field:has(input:placeholder-shown) .amount-field__sign {
    color: color-mix(in srgb, var(--hint) 45%, transparent);
}
.input--amount, .amount-field__mirror {
    font-size: var(--text-hero);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    letter-spacing: -.035em;
    line-height: 1;
}
.input--amount {
    /* Запасная ширина на первый кадр: дальше её задаёт замер по зеркалу. */
    width: calc(var(--n, 1) * (1ch - .035em) + 2px);
    max-width: 100%;
    min-width: 0;
    text-align: center;
    background: transparent;
    padding: var(--space-2) 0;
}
.amount-field__mirror { position: absolute; visibility: hidden; white-space: pre; pointer-events: none; }
.input--amount:focus { box-shadow: none; }
.input--amount { caret-color: var(--accent); }
.input--amount::placeholder { color: color-mix(in srgb, var(--hint) 45%, transparent); }
/* Подсказка гаснет при вводе: иначе курсор встаёт в середину «0.00» и разрезает его. */
.input--amount:focus::placeholder { color: transparent; }
.search { display: flex; gap: var(--space-2); }
.search .btn { width: auto; padding-inline: var(--space-4); }

.badge {
    display: inline-block;
    padding: 1px var(--space-2);
    border-radius: 999px;
    font-size: var(--text-sm);
    background: var(--tint);
    color: var(--hint);
}
/* Статус, изменившийся только что: он и есть ответ на нажатие, поэтому появляется на глазах. */
.badge--changed { animation: badge-pop .34s cubic-bezier(.2, .8, .3, 1) both; }
@keyframes badge-pop {
    from { opacity: 0; transform: scale(.82); }
    60% { transform: scale(1.06); }
    to { opacity: 1; transform: scale(1); }
}
.badge--pending { color: var(--warn); background: color-mix(in srgb, var(--warn) 14%, transparent); }
.badge--completed { color: var(--ok); background: color-mix(in srgb, var(--ok) 14%, transparent); }
.badge--cancelled, .badge--expired { color: var(--danger); background: color-mix(in srgb, var(--danger) 12%, transparent); }

.notice { padding: var(--space-3) var(--space-4); border-radius: var(--radius-sm); background: var(--surface); font-size: var(--text-sm); }
.notice--error { color: var(--danger); }
.notice--success { color: var(--ok); }
.notice--info { color: var(--hint); }

.tabs { display: flex; gap: var(--space-2); overflow-x: auto; }
.tab {
    flex: none;
    padding: var(--space-2) var(--space-3);
    border-radius: 999px;
    font-size: var(--text-sm);
    color: var(--hint);
    transition: background var(--press), color var(--press);
}
.tab:active { background: var(--tint-strong); }
.tab--active { background: var(--accent); color: var(--on-accent); }
/* Метка следующей страницы: пустое место под списком, которое крутится, пока страница едет. */
.more { height: var(--space-6); }
.more--busy { display: grid; place-items: center; }
.more--busy::before {
    content: "";
    width: 20px;
    height: 20px;
    border: 2px solid var(--tint-strong);
    border-top-color: var(--accent);
    border-radius: 50%;
    animation: spin .8s linear infinite;
}

.steps { display: flex; flex-direction: column; gap: var(--space-3); }
.step {
    display: grid;
    grid-template-columns: auto 1fr;
    align-items: center;
    gap: var(--space-2) var(--space-4);
    padding: var(--space-5);
    border-radius: var(--radius-lg);
    background: var(--surface);
}
.step__badge {
    display: grid;
    place-items: center;
    width: 38px;
    height: 38px;
    border-radius: 50%;
    background: var(--tint);
    color: var(--hint);
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    transition: background var(--press), color var(--press);
}
.step__badge svg { display: none; width: 20px; height: 20px; }
.step__body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.step__title { display: flex; align-items: center; gap: var(--space-2); font-weight: 600; }
.step__title svg { width: 17px; height: 17px; flex: none; color: var(--hint); }
.step__note { color: var(--hint); font-size: var(--text-sm); }
.step > .btn { grid-column: 1 / -1; margin-top: var(--space-2); }

/* Пройденный шаг не гаснет, а подтверждается: кружок заливается акцентом,
   цифра сменяется галочкой, а подпись превращается в само значение. */
.step--done .step__badge { background: var(--accent); color: var(--on-accent); }
.step--done .step__num { display: none; }
.step--done .step__badge svg { display: block; }
.step--done .step__note { color: var(--text); font-size: var(--text-base); }
.step--done .step__title svg { color: var(--accent); }

/* Экран выдачи наличных: заявка, видоискатель и подпись идут одним столбцом с общим шагом,
   поэтому между состояниями ничего не подпрыгивает. */
.kyc { flex: 1; display: flex; flex-direction: column; gap: var(--space-3); }
.kyc__target { margin: 0; text-align: center; font-weight: 600; }
.kyc__hint { margin: 0; text-align: center; }

/* Видоискатель занимает всё, что осталось от экрана, а не задан пропорцией: с пропорцией он
   ужимался по ширине и уезжал влево, а на низких экранах кнопки уходили под сгиб. */
.camera { position: relative; flex: 1; min-height: 200px; border-radius: var(--radius); overflow: hidden; background: #000; }
/* Кадр вынут из потока: иначе снимок задавал бы высоту сам и выталкивал кнопки за экран. */
.camera video, .camera img { position: absolute; inset: 0; width: 100%; height: 100%; }
.camera video { object-fit: cover; }
/* Снятый кадр показывается целиком, с полями: кассир проверяет именно то, что уйдёт на сервер. */
.camera img { object-fit: contain; }
/* Сохранённый снимок на своей странице показывается целиком: обрезать доказательство нельзя. */
.kyc-photo { display: block; width: 100%; height: auto; }

/* Ожидание с движением: пустой экран во время загрузки выглядит как зависание. */
.loader { flex: 1; display: flex; align-items: center; justify-content: center; min-height: 40vh; }
.loader::before {
    content: "";
    width: 48px;
    height: 48px;
    border: 3px solid var(--tint-strong);
    border-top-color: var(--accent);
    border-radius: 50%;
    animation: spin .8s linear infinite;
}

/* Строка со свайпом: кнопка удаления лежит под ней у правого края, строка сдвигается влево
   и открывает её. pan-y оставляет вертикальную прокрутку списка пальцу. */
.swipe { position: relative; overflow: hidden; }
/* z-index у строки: кнопка стоит после неё в разметке и без этого рисовалась бы поверх. */
.swipe .row { position: relative; z-index: 1; background: var(--surface); transition: transform .2s ease-out; touch-action: pan-y; }
/* Сдвиг открытой строки по числу кнопок под ней: у покупок две, у кампаний три. */
.swipe--open .row { transform: translateX(calc(-64px * var(--swipe-actions, 2))); }
/* Под пальцем строка идёт без доводки: сдвиг задаёт каждое событие, анимация только после отпускания. */
.swipe--drag .row { transition: none; }
.swipe__actions { position: absolute; top: 0; right: 0; bottom: 0; display: flex; }
.swipe__edit, .swipe__delete { width: 64px; border: 0; padding: 0; display: grid; place-items: center; color: #fff; }
.swipe__edit { background: var(--accent); }
.swipe__delete { background: var(--danger); }
.swipe__edit svg, .swipe__delete svg { width: 22px; height: 22px; }

/* Сводка прибыли: продано и куплено стоят рядом зеркально, чтобы курсы сравнивались одним
   взглядом. В каждой колонке одно крупное число, шекели и курс под ним мелко и серым:
   три числа в одной строке справа не читались. */
.stats { display: grid; grid-template-columns: 1fr 1fr; background: var(--surface); border-radius: var(--radius); overflow: hidden; }
.stat { display: flex; flex-direction: column; gap: var(--space-1); padding: var(--space-4); min-width: 0; }
.stat + .stat { border-left: 1px solid var(--line); }
.stat__label { color: var(--hint); font-size: var(--text-sm); }
.stat__value { font-size: 24px; font-weight: 600; line-height: 1.15; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.stat__meta { color: var(--hint); font-size: var(--text-sm); }

/* Три списка одним блоком: вкладки сверху, панели листаются свайпом с привязкой к странице.
   Прокрутка нативная, поэтому с вертикальной не спорит; скрипт только подсвечивает вкладку. */
.tabs--panes { align-items: center; }
.tabs--panes .tab { border: 0; background: none; font: inherit; font-size: var(--text-sm); }
.tabs--panes .tab--active { background: var(--accent); }
/* Полоса, у которой нет ссылки справа, стоит по центру. Не justify-content: при переполнении
   оно отрезало бы левый край без возможности докрутить; авто-поля крайних вкладок центрируют,
   пока место есть, и схлопываются, когда полоса длиннее экрана. */
.tabs--center > :first-child { margin-left: auto; }
.tabs--center > :last-child { margin-right: auto; }
/* Зазор между панелями: при пролистывании это две карточки, а не одна склеенная. Высоту
   не выравниваем: короткий список заканчивается там, где заканчивается. */
/* position: relative обязателен: offsetLeft панелей считается от контейнера, а не от страницы,
   иначе прыжок к панели, ближайшая панель и высота блока сдвинуты на отступ страницы. */
.panes { position: relative; display: flex; gap: var(--space-4); overflow-x: auto; scroll-snap-type: x mandatory; scrollbar-width: none; }
.panes::-webkit-scrollbar { display: none; }
.pane { flex: 0 0 100%; min-width: 0; scroll-snap-align: start; align-self: flex-start; }

/* Экран едет за пальцем при свайпе «назад» от левого края; на отпускании доезжает или возвращается. */
.screen--dragging { transition: none; }
.screen--settling { transition: transform .22s ease-out; }

/* Переключатель в строке настроек: дорожка и бегунок, как в Telegram, вместо системной галки. */
.switch {
    appearance: none;
    flex: none;
    width: 50px;
    height: 30px;
    border-radius: 999px;
    background: var(--tint-strong);
    position: relative;
    transition: background var(--press);
}
.switch::after {
    content: '';
    position: absolute;
    top: 2px;
    left: 2px;
    width: 26px;
    height: 26px;
    border-radius: 50%;
    background: var(--surface);
    box-shadow: 0 1px 3px rgba(0, 0, 0, .25);
    transition: transform var(--press);
}
.switch:checked { background: var(--ok); }
.switch:checked::after { transform: translateX(20px); }
/* Закрытый выключатель: условие кампании с получателями уже не меняют. */
.switch:disabled { opacity: .45; }

/* Кнопки под строкой кампании: пауза или пуск по признаку строки, и отправка карточки. */
.swipe__toggle, .swipe__card { width: 64px; border: 0; padding: 0; display: grid; place-items: center; color: #fff; }
.swipe__toggle { background: var(--warn); }
/* Свой цвет у каждого действия: правка синяя, пауза оранжевая, отправка зелёная, удаление красное. */
.swipe__card { background: var(--ok); }
.swipe__toggle svg, .swipe__card svg { width: 22px; height: 22px; }
.swipe[data-active="1"] .when-inactive, .swipe[data-active="0"] .when-active { display: none; }
/* Значки в подписи строки: ростом с букву, сидят на базовой линии, отступ до текста как пробел.
   Состояние кампании цветным кружком: зелёный «играет» у активной, оранжевая пауза у остановленной,
   тот же оранжевый, что у кнопки паузы под строкой. */
.promo__state, .promo__picture { width: 1.15em; height: 1.15em; vertical-align: -0.2em; margin-right: 0.35em; }
.promo__state circle { fill: currentColor; }
.promo__state path, .promo__state rect { fill: #fff; }
.promo__state--on { color: var(--ok); }
.promo__state--off { color: var(--warn); }
[data-active="1"] .promo__state--off, [data-active="0"] .promo__state--on { display: none; }

/* Строка-переход в конце списка: ниже строк данных, обычное начертание акцентным цветом,
   шеврон вправо серым. Читается как «дальше», а не как ещё одна запись. */
.row--more { min-height: 48px; padding-block: var(--space-2); justify-content: center; }
.row--more .row__title { font-weight: 400; color: var(--accent-text); }
/* Шеврон строчный внутри подписи: сидит на её средней линии, а не по центру коробки строки. */
.row__chevron { display: inline-block; vertical-align: -4px; width: 18px; height: 18px; margin-left: 2px; color: var(--hint); }

/* Подсказка под полем, в отличие от жалобы, серая: объясняет, а не ругается. */
.field__hint { color: var(--hint); font-size: var(--text-sm); }
.input[readonly] { color: var(--hint); }

/* Бонус на финале онбординга: знак, сумма и подсказка столбиком по центру, между шагами и «Начать». */
.bonus { display: flex; flex-direction: column; align-items: center; gap: var(--space-2); text-align: center; }
.bonus .glyph--md { margin: 0; }
.bonus__title { margin: 0; font-weight: 500; }
.promo-notice { margin: 0; text-align: center; }

/* Редактор описания кампании: панель кнопок над редактируемым блоком в форме поля ввода. */
.toolbar__btn {
    width: 40px;
    height: 34px;
    border: 0;
    border-radius: var(--radius-sm);
    background: var(--surface);
    color: var(--text);
    font: inherit;
    display: grid;
    place-items: center;
}
.toolbar__btn svg { width: 18px; height: 18px; }
.toolbar__btn:active { background: var(--tint-strong); }
.format-bar {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    max-width: 480px;
    margin: 0 auto;
    z-index: 15;
    display: flex;
    align-items: center;
    gap: var(--space-1);
    padding: var(--space-1) var(--space-3);
    background: var(--bg);
    border-top: 1px solid var(--line);
    will-change: transform;
}
.format-bar--pinned { top: auto; bottom: 0; padding-bottom: calc(var(--space-1) + env(safe-area-inset-bottom)); }
.format-bar__done { margin-left: auto; height: 34px; padding: 0 var(--space-2); border: 0; background: none; color: var(--accent-text); font: inherit; font-weight: 600; }
.format-bar__done:active { opacity: .6; }
.editor { min-height: 96px; white-space: pre-wrap; overflow-wrap: anywhere; }
.editor:empty::before { content: attr(data-placeholder); color: color-mix(in srgb, var(--hint) 45%, transparent); }
.editor a { color: var(--accent-text); }

/* Картинка кампании: в форме превью с крестиком, на странице бонуса во всю ширину над текстом. */
/* Строка картинки: значок, состояние и действие. Тап по строке выбирает или заменяет файл,
   «Убрать» снимает. Саму картинку показывает предпросмотр, месту по вертикали она не нужна. */
.picture { padding: 0; }
.picture__pick {
    flex: 1;
    min-width: 0;
    min-height: 56px;
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    border: 0;
    background: none;
    color: var(--text);
    font: inherit;
    text-align: left;
}
.picture__pick:active { background: var(--tint-strong); }
.picture__glyph { flex: none; width: 24px; height: 24px; color: var(--hint); }
[data-attached="1"] .picture__glyph { color: var(--accent-text); }
.picture__cta { flex: none; color: var(--accent-text); font-weight: 500; }
.picture__remove { flex: none; align-self: stretch; padding: 0 var(--space-4); border: 0; background: none; color: var(--danger); font: inherit; font-weight: 500; }
.picture__remove:active { background: var(--tint); }
[data-attached="1"] .picture__cta, [data-attached="0"] .picture__remove { display: none; }

/* Лист предпросмотра снизу, как системные листы Telegram: ручка, заголовок, крестик. Внутри чат
   с обоями и входящим пузырём бота: сообщение выглядит так, каким придёт клиенту, а всё вокруг
   молчит. Время плавает в конце последней строки, кнопка-ссылка со стрелкой в углу, как в Telegram. */
.sheet { position: fixed; inset: 0; z-index: 20; display: flex; align-items: flex-end; background: rgba(0, 0, 0, 0); transition: background 240ms ease; }
.sheet--open { background: rgba(0, 0, 0, .42); }
.sheet__panel {
    width: 100%;
    max-height: 88vh;
    overflow-y: auto;
    background: var(--bg);
    border-radius: var(--radius-lg) var(--radius-lg) 0 0;
    padding: var(--space-2) var(--space-4) calc(var(--space-6) + env(safe-area-inset-bottom));
    transform: translateY(100%);
    transition: transform 240ms cubic-bezier(.2, 0, .2, 1);
}
.sheet--open .sheet__panel { transform: none; }
.sheet__handle { display: block; width: 36px; height: 5px; margin: 0 auto var(--space-3); border-radius: 999px; background: var(--tint-strong); }
.sheet__head { display: flex; align-items: center; justify-content: space-between; margin-bottom: var(--space-3); }
.sheet__title { margin: 0; font-size: var(--text-base); font-weight: 600; }
.sheet__close { width: 30px; height: 30px; border: 0; border-radius: 50%; background: var(--tint); color: var(--hint); font: inherit; font-size: 22px; line-height: 1; display: grid; place-items: center; }
.sheet__close:active { background: var(--tint-strong); }
.chat {
    padding: var(--space-5) var(--space-3);
    border-radius: var(--radius);
    background: radial-gradient(color-mix(in srgb, var(--accent) 24%, transparent) 1px, transparent 1.6px) 0 0 / 14px 14px,
        color-mix(in srgb, var(--accent) 12%, var(--bg));
}
.chat__message { width: fit-content; max-width: 86%; }
.chat__bubble { overflow: hidden; background: var(--surface); border-radius: var(--radius) var(--radius) var(--radius) 4px; box-shadow: 0 1px 1px rgba(0, 0, 0, .08); }
.chat__photo { display: block; width: 100%; height: auto; }
.chat__text { display: flow-root; margin: 0; padding: var(--space-2) var(--space-3); white-space: pre-wrap; overflow-wrap: anywhere; line-height: 1.35; }
.chat__text a { color: var(--accent-text); }
.chat__time { float: right; margin: 7px 0 0 var(--space-2); font-size: 12px; line-height: 1; color: var(--hint); }
.chat__button {
    position: relative;
    display: flex;
    justify-content: center;
    align-items: center;
    margin-top: var(--space-1);
    padding: 9px var(--space-3);
    border-radius: var(--radius-sm);
    background: rgba(0, 0, 0, .34);
    backdrop-filter: blur(6px);
    color: #fff;
    font-size: 15px;
    font-weight: 500;
}
.chat__button svg { position: absolute; top: 4px; right: 5px; width: 10px; height: 10px; }
@media (prefers-reduced-motion: reduce) {
    .sheet, .sheet__panel { transition: none; }
}

/* Карточка бонуса: монета в кристалле льда, потому что бонус приходит замороженным и тает с
   пополнениями. Лёд замешан из акцента темы и подложки, монета в бонусной лаванде значков.
   Один блик по кристаллу при загрузке, дальше всё спокойно. Недоступная акция гасит кристалл. */
.gift {
    --gift-ice: color-mix(in srgb, var(--accent) 24%, var(--surface));
    position: relative;
    overflow: hidden;
    text-align: center;
    padding: var(--space-4) var(--space-4) var(--space-6);
    border-radius: var(--radius-lg);
    background: radial-gradient(120% 90% at 50% 0%, color-mix(in srgb, #bda9f2 30%, var(--surface)), var(--surface) 72%);
}
.stack--center .gift { margin-top: auto; }
/* Карточка клиента: лицо крупно, имя заглавием, замороженный приглушён. */
.client .avatar--lg, .client .glyph--md { margin: 0 auto var(--space-3); }
.client__name { margin: 0 0 var(--space-1); }
.client--frozen .avatar, .client--frozen .glyph--md { filter: grayscale(1); opacity: .6; }
.client__state { color: var(--danger); }
/* Строки-поля: подпись слева, значение справа плоским полем без рамки, как в настройках iOS. */
.row--field { gap: var(--space-3); }
.row__input { flex: 1; min-width: 0; padding: 0; border: 0; background: none; font: inherit; color: var(--text); text-align: right; outline: none; }
.row__input::placeholder { color: var(--hint); }
.row__select { position: relative; display: inline-flex; align-items: center; flex: none; color: var(--hint); }
.row__select select { appearance: none; padding: 0 22px 0 0; border: 0; background: none; font: inherit; color: var(--text); text-align: right; text-align-last: right; outline: none; }
.row__select svg { position: absolute; right: 0; width: 18px; height: 18px; pointer-events: none; }
/* Строка-действие: кнопка во всю строку, подпись по центру акцентом, опасное действие красным. */
.row--action { width: 100%; min-height: 48px; padding-block: var(--space-2); justify-content: center; border: 0; background: none; font: inherit; font-weight: 500; color: var(--accent-text); text-align: center; }
.row--action:active { background: var(--tint-strong); }
.row--danger { color: var(--danger); }
.gift__art { display: block; width: 200px; height: auto; margin: 0 auto var(--space-1); }
.gift__label, .gift__hint { margin: 0; color: var(--hint); font-size: var(--text-sm); }
.gift__hint b { font-weight: 600; color: var(--text); }
.gift__amount {
    margin: var(--space-1) 0 var(--space-2);
    font-size: var(--text-hero);
    font-weight: 700;
    letter-spacing: -.035em;
    line-height: 1;
    font-variant-numeric: tabular-nums;
}
.gift__glow { fill: #bda9f2; opacity: .3; }
.gift__ice-hi { stop-color: color-mix(in srgb, #fff 72%, var(--gift-ice)); }
.gift__ice-lo { stop-color: var(--gift-ice); }
.gift__crystal { fill: url(#gift-ice); stroke: color-mix(in srgb, #fff 45%, var(--accent)); stroke-width: 1.5; stroke-linejoin: round; }
.gift__facets { fill: none; stroke: #fff; stroke-opacity: .6; stroke-width: 1; stroke-linejoin: round; }
.gift__edge { fill: none; stroke: #fff; stroke-opacity: .85; stroke-width: 2.5; stroke-linecap: round; stroke-linejoin: round; }
.gift__coin { fill: url(#gift-coin); }
.gift__coin-ring { fill: none; stroke: #fff; stroke-opacity: .4; stroke-width: 1.2; stroke-dasharray: 2 2.6; }
.gift__sign { fill: #fff; font: 700 26px var(--font); }
.gift__spark { fill: #a695e7; }
.gift__glint { fill: #fff; opacity: .55; transform: translateX(-70px) skewX(-20deg); animation: glint 1.1s cubic-bezier(.2, 0, .2, 1) .4s forwards; }
@keyframes glint { to { transform: translateX(150px) skewX(-20deg); } }
.gift--off .gift__art { filter: grayscale(1); opacity: .55; }
.gift--off .gift__glint { animation: none; opacity: 0; }
@media (prefers-reduced-motion: reduce) {
    .gift__glint { animation: none; opacity: 0; }
}
