/* Zeo Topup TMA — компоненты.
 *
 * Источник — дизайн-система как код: `_ds_bundle.js` (React.createElement,
 * инлайн-объекты `style`, ни одного css-класса) плюс сверка по
 * `tokens/*.css`. Задача этого файла — тот же визуальный результат
 * классами: каждый инлайн-объект стал `.zt-<component>` (+модификаторы),
 * литеральные значения и `var(--token)` перенесены без изменений.
 *
 * Конвенция именования: `zt-<component>` — корень, `zt-<component>--<mod>`
 * — вариант/размер/состояние-модификатор (ставит JS, когда состояние не
 * добывается из псевдокласса), `zt-<component>__<part>` — часть составного
 * компонента. Все имена классов ниже перечислены в ответе задачи (`files`/
 * `handoff`) — WP10b пишет `js/components/*.js` по этому списку и другого
 * источника не имеет.
 *
 * Три сознательных добавления сверх `_ds_bundle.js` (задание WP10a):
 *   1. `:focus-visible { box-shadow: var(--ring-focus) }` — у Button,
 *      IconButton, Checkbox, Switch, RadioGroup, Tabs, Tag, AmountOption,
 *      ProductTile его нет вовсе (components.md §6.1: "readme.md
 *      *specifies* a --ring-focus ring — adding it is on-spec, not a
 *      deviation"). У Input/Select фокус-кольцо уже было — оставлено как
 *      было.
 *   2. `:hover`/`:active` — в бандле это `React.useState`, здесь обычные
 *      псевдоклассы; клавиатура и тач получают то, что раньше получала
 *      только мышь (components.md §6.2).
 *   3. `:active` там, где источник вообще не определяет цвет нажатия
 *      (IconButton, Tag, Checkbox, Switch, RadioGroup, ProductTile, Card) —
 *      однородный `opacity:.85` / лёгкий `translateY`, без выдуманных
 *      токенов. Где источник определяет цвет нажатия явно (Button,
 *      AmountOption через `--ring-focus`), используется он.
 *
 * Паттерн отключённого состояния: где стилизуемый элемент — сам нативный
 * `<button disabled>`/`<input disabled>`, состояние берётся из `:disabled`
 * с гвардом `:not(:disabled)` на "живых" вариантах (без гонки
 * специфичности, порядок правил не важен). Где disabled лежит на потомке
 * (Input/Select — на native `<input>`/`<select>` внутри `<label>`, а
 * стилизуется обёртка) — модификатор `--disabled` ставит JS: `:has()` в
 * файле не используется нигде (Safari на старых iPhone у части аудитории,
 * та же причина, что и решение про import-карты в `docs/tma_plan.md`
 * поправка №1).
 *
 * Крестики-нолики "restated redundant branch" и т.п. заметки components.md
 * — про JS-логику самого бандла, не про CSS; здесь не воспроизводятся.
 */

/* ============================================================
   Keyframes — hoisted once (в бандле дублируются в каждом инстансе:
   Button/StatusPill/Toast/Dialog рендерят свой <style> с одним и тем же
   @keyframes zeo-spin через раз; тут — единственное определение).
   ============================================================ */

@keyframes zeo-spin { to { transform: rotate(360deg); } }
@keyframes zeo-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes zeo-rise { from { opacity: 0; transform: translateY(12px) scale(.985); } to { opacity: 1; transform: none; } }
@keyframes zeo-toast { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }

/* Утилита на вращающуюся иконку — применить к `.zt-icon` внутри
   загрузочной кнопки / pending-тоста / spin-статуса. Длительность в
   бандле расходится (700ms Button/Toast, 900ms StatusPill,
   components.md §6.5) — `--slow` даёт вторую длительность на той же
   keyframes, не второй @keyframes. */
.zt-spin { animation: zeo-spin 700ms linear infinite; }
.zt-spin--slow { animation-duration: 900ms; }


/* ============================================================
   1. Core — components.md §1
   ============================================================ */

/* --- 1.1 Icon (§1.1) ---
   В реальной реализации (в отличие от бандла, который берёт SVG маской с
   jsDelivr) иконки — инлайновые SVG из vendor/lucide.js: WP9 обрезал
   width/height у тел иконок именно затем, чтобы их держал компонент.
   `.zt-icon` — класс на самом <svg> (не на обёртке): цвет наследуется
   обычным каскадом (stroke="currentColor" уже в разметке WP9), размер
   компонент проставляет инлайн per-instance (13–36px по реестру размеров
   — величина неоднородна, отдельного класса на каждый шаг нет смысла). */
.zt-icon {
  display: inline-block;
  flex: 0 0 auto;
}

/* --- 1.2 Button (§1.2) --- */
.zt-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid transparent;
  font-family: var(--font-ui);
  font-weight: var(--fw-semibold);
  letter-spacing: -0.01em;
  line-height: 1;
  white-space: nowrap;
  cursor: pointer;
  transform: scale(1);
  transition: var(--transition-control);
}
.zt-button:focus-visible { box-shadow: var(--ring-focus); }
/* Нажатие — --shadow-primary-press у ЛЮБОГО варианта, включая danger/steam
   (components.md §6.7: сознательно оставленная особенность бандла). */
.zt-button:active:not(:disabled) { transform: scale(.99); box-shadow: var(--shadow-primary-press); }
.zt-button:disabled {
  background: var(--surface-sunken);
  color: var(--text-disabled);
  border-color: transparent;
  box-shadow: none;
  cursor: not-allowed;
}

.zt-button--sm { height: var(--control-height-sm); padding: 0 14px; gap: 6px; font-size: 13px; border-radius: var(--radius-sm); }
.zt-button--md { height: var(--control-height-md); padding: 0 20px; gap: 8px; font-size: 15px; border-radius: var(--radius-control); }
.zt-button--lg { height: var(--control-height-lg); padding: 0 28px; gap: 10px; font-size: 17px; border-radius: var(--radius-md); }
.zt-button--full { width: 100%; }

.zt-button--primary:not(:disabled) { background: var(--blue-500); color: var(--text-inverse); }
.zt-button--primary:hover:not(:disabled) { background: var(--blue-600); box-shadow: var(--shadow-primary); }
.zt-button--primary:active:not(:disabled) { background: var(--blue-700); }

.zt-button--secondary:not(:disabled) { background: var(--control-secondary-bg); color: var(--text-primary); border-color: var(--border-subtle); box-shadow: var(--shadow-xs); }
.zt-button--secondary:hover:not(:disabled) { background: var(--control-secondary-bg-hover); }
.zt-button--secondary:active:not(:disabled) { background: var(--surface-sunken); }

.zt-button--ghost:not(:disabled) { background: transparent; color: var(--text-brand); }
.zt-button--ghost:hover:not(:disabled) { background: var(--control-ghost-bg-hover); }
.zt-button--ghost:active:not(:disabled) { background: var(--alpha-blue-16); }

.zt-button--danger:not(:disabled) { background: var(--red-500); color: var(--text-inverse); }
.zt-button--danger:hover:not(:disabled),
.zt-button--danger:active:not(:disabled) { background: var(--red-700); }

.zt-button--telegram:not(:disabled) { background: var(--tg-500); color: var(--text-inverse); }
/* Литеральная тень источника — токена под неё нет (components.md §6.6). */
.zt-button--telegram:hover:not(:disabled) { background: var(--tg-700); box-shadow: 0 8px 20px rgba(42, 171, 238, .28); }
.zt-button--telegram:active:not(:disabled) { background: var(--tg-700); }

.zt-button--steam:not(:disabled) { background: var(--steam-900); color: var(--text-inverse); }
.zt-button--steam:hover:not(:disabled),
.zt-button--steam:active:not(:disabled) { background: var(--steam-700); }

/* --- 1.3 IconButton (§1.3) --- */
.zt-icon-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-control);
  border: 1px solid transparent;
  cursor: pointer;
  transition: var(--transition-control);
}
.zt-icon-button:focus-visible { box-shadow: var(--ring-focus); }
.zt-icon-button:disabled { background: var(--surface-sunken); color: var(--text-disabled); cursor: not-allowed; }

.zt-icon-button--sm { width: 32px; height: 32px; }
.zt-icon-button--md { width: 40px; height: 40px; }
.zt-icon-button--lg { width: 48px; height: 48px; }

.zt-icon-button--ghost:not(:disabled) { background: transparent; color: var(--text-secondary); }
.zt-icon-button--ghost:hover:not(:disabled) { background: var(--control-ghost-bg-hover); color: var(--text-brand); }
.zt-icon-button--ghost:active:not(:disabled) { opacity: .85; }

/* outline на hover теряет белую заливку — берёт tint ghost-hover
   (components.md §1.3: "loses its white fill on hover"). */
.zt-icon-button--outline:not(:disabled) { background: var(--control-secondary-bg); border-color: var(--border-subtle); color: var(--text-secondary); }
.zt-icon-button--outline:hover:not(:disabled) { background: var(--control-ghost-bg-hover); color: var(--text-brand); }
.zt-icon-button--outline:active:not(:disabled) { opacity: .85; }

.zt-icon-button--solid:not(:disabled) { background: var(--blue-500); color: var(--text-inverse); }
.zt-icon-button--solid:hover:not(:disabled) { background: var(--blue-600); }
.zt-icon-button--solid:active:not(:disabled) { background: var(--blue-700); }

/* --- 1.4 Input (§1.4) ---
   `<label class="zt-input [--sm|--lg] [--invalid] [--disabled]">`
     `<span class="zt-input__label">` (опционально)
     `<div class="zt-input__field">`
       `<svg class="zt-icon zt-input__icon">` (опционально)
       `<input class="zt-input__control">`
       `<span class="zt-input__suffix">` (опционально)
     `<span class="zt-input__hint">` (error || hint) */
.zt-input { display: flex; flex-direction: column; gap: 6px; width: 100%; }
.zt-input__label { font-size: 13px; font-weight: var(--fw-semibold); color: var(--text-secondary); }

.zt-input__field {
  display: flex;
  align-items: center;
  gap: 10px;
  height: var(--control-height-md);
  padding: 0 14px;
  background: var(--surface-card);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-control);
  transition: var(--transition-control);
}
.zt-input--sm .zt-input__field { height: var(--control-height-sm); }
.zt-input--lg .zt-input__field { height: var(--control-height-lg); }
/* фокус живёт на нативном <input>, кольцо — на обёртке-FIELD */
.zt-input__field:focus-within { border-color: var(--border-brand); box-shadow: var(--ring-focus); }
.zt-input--invalid .zt-input__field { border-color: var(--border-danger); }
.zt-input--invalid .zt-input__field:focus-within { box-shadow: var(--ring-danger); }
.zt-input--disabled .zt-input__field { background: var(--surface-sunken); }

.zt-input__icon { color: var(--text-tertiary); }
.zt-input__field:focus-within .zt-input__icon { color: var(--text-brand); }

.zt-input__control {
  flex: 1;
  min-width: 0;
  border: none;
  outline: none;
  background: transparent;
  font-family: var(--font-ui);
  font-size: 16px; /* см. примечание про зум iOS ниже по файлу */
  font-weight: var(--fw-medium);
  color: var(--text-primary);
  font-feature-settings: var(--num-tabular);
}
.zt-input--lg .zt-input__control { font-size: 17px; }
.zt-input__control::placeholder { color: var(--text-tertiary); }
.zt-input__control:disabled { cursor: not-allowed; }

.zt-input__suffix { font-size: 15px; font-weight: var(--fw-semibold); color: var(--text-tertiary); }
.zt-input__hint { font-size: 12px; color: var(--text-tertiary); }
.zt-input--invalid .zt-input__hint { color: var(--text-danger); }

/* --- 1.5 Select (§1.5) ---
   Нет size-варианта — высота всегда --control-height-md. Нет ветки
   --ring-danger на фокусе (в отличие от Input) — дословно по источнику. */
.zt-select { display: flex; flex-direction: column; gap: 6px; width: 100%; }
.zt-select__label { font-size: 13px; font-weight: var(--fw-semibold); color: var(--text-secondary); }

.zt-select__wrap {
  position: relative;
  display: flex;
  align-items: center;
  height: var(--control-height-md);
  background: var(--surface-card);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-control);
  transition: var(--transition-control);
}
.zt-select__wrap:focus-within { border-color: var(--border-brand); box-shadow: var(--ring-focus); }
.zt-select--invalid .zt-select__wrap { border-color: var(--border-danger); }
.zt-select--disabled .zt-select__wrap { background: var(--surface-sunken); }

.zt-select__control {
  appearance: none;
  -webkit-appearance: none;
  flex: 1;
  height: 100%;
  padding: 0 40px 0 14px;
  border: none;
  outline: none;
  background: transparent;
  font-family: var(--font-ui);
  font-size: 16px; /* см. примечание про зум iOS ниже по файлу */
  font-weight: var(--fw-medium);
  color: var(--text-tertiary); /* плейсхолдер-вид, пока не выбрано */
  cursor: pointer;
}
/* JS ставит этот модификатор, когда value непусто (== вид "выбрано") */
.zt-select__control--filled { color: var(--text-primary); }
.zt-select--disabled .zt-select__control { cursor: not-allowed; }

.zt-select__chevron { position: absolute; right: 14px; pointer-events: none; color: var(--text-tertiary); }
.zt-select__hint { font-size: 12px; color: var(--text-tertiary); }
.zt-select--invalid .zt-select__hint { color: var(--text-danger); }

/* --- 1.6 Checkbox (§1.6) ---
   `<label class="zt-checkbox [--with-description] [--disabled]">`
     `<input type="checkbox" class="zt-checkbox__input">`
     `<span class="zt-checkbox__box"><svg class="zt-icon zt-checkbox__check"></span>`
     `<span class="zt-checkbox__text">…</span>`
   Порядок DOM важен — `__box` идёт СРАЗУ за `__input` (общий соседний
   селектор `~`). */
.zt-checkbox { display: flex; gap: 12px; align-items: center; cursor: pointer; }
.zt-checkbox--with-description { align-items: flex-start; }
.zt-checkbox--disabled { cursor: not-allowed; opacity: .55; }
.zt-checkbox:active:not(.zt-checkbox--disabled) { opacity: .85; }

.zt-checkbox__input { position: absolute; opacity: 0; width: 0; height: 0; }

.zt-checkbox__box {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  flex: 0 0 auto;
  border-radius: 6px; /* литерал в источнике, НЕ var(--radius-xs) — components.md §6.6 */
  background: var(--surface-card);
  border: 1px solid var(--border-default);
  transition: var(--transition-control);
}
.zt-checkbox__input:checked ~ .zt-checkbox__box { background: var(--blue-500); border-color: var(--blue-500); }
.zt-checkbox__input:focus-visible ~ .zt-checkbox__box { box-shadow: var(--ring-focus); }

.zt-checkbox__check { display: none; color: var(--text-inverse); }
.zt-checkbox__input:checked ~ .zt-checkbox__box .zt-checkbox__check { display: inline-block; }

.zt-checkbox__text { display: flex; flex-direction: column; gap: 2px; }
.zt-checkbox__label { font-size: 14px; font-weight: var(--fw-medium); color: var(--text-primary); }
.zt-checkbox__description { font-size: 12px; color: var(--text-tertiary); }

/* --- 1.7 Switch (§1.7) ---
   `<label class="zt-switch [--with-description] [--disabled]">`
     текст слева, `<input type="checkbox" role="switch" class="zt-switch__input">`,
     `<span class="zt-switch__track"><span class="zt-switch__thumb"></span></span>` */
.zt-switch { display: flex; align-items: center; justify-content: space-between; gap: 16px; cursor: pointer; }
.zt-switch--with-description { align-items: flex-start; }
.zt-switch--disabled { cursor: not-allowed; opacity: .55; }
.zt-switch:active:not(.zt-switch--disabled) { opacity: .85; }

.zt-switch__text { display: flex; flex-direction: column; gap: 2px; }
.zt-switch__label { font-size: 14px; font-weight: var(--fw-medium); color: var(--text-primary); }
.zt-switch__description { font-size: 12px; color: var(--text-tertiary); }

.zt-switch__input { position: absolute; opacity: 0; width: 0; height: 0; }

.zt-switch__track {
  display: block;
  width: 44px;
  height: 26px;
  flex: 0 0 auto;
  border-radius: var(--radius-pill);
  padding: 3px;
  background: var(--border-default);
  transition: background-color var(--dur-fast) var(--ease-standard);
}
.zt-switch__input:checked ~ .zt-switch__track { background: var(--blue-500); }
.zt-switch__input:focus-visible ~ .zt-switch__track { box-shadow: var(--ring-focus); }

.zt-switch__thumb {
  display: block;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--white);
  box-shadow: var(--shadow-sm);
  transform: translateX(0);
  transition: transform var(--dur-fast) var(--ease-out);
}
/* 44 - 2*3 padding - 20 thumb = 18px хода (components.md §1.7 geometry check) */
.zt-switch__input:checked ~ .zt-switch__track .zt-switch__thumb { transform: translateX(18px); }

/* --- 1.8 Card (§1.8) ---
   padding — по спецификации `number|string` per-instance, остаётся
   инлайн-стилем у вызывающего (динамическое значение, не токен). */
.zt-card {
  position: relative;
  background: var(--surface-card);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-card);
  overflow: hidden; /* клипает -9px бейдж AmountOption и Tooltip-пузырь внутри — components.md §1.8/§6.4 */
  transition:
    box-shadow var(--dur-base) var(--ease-standard),
    transform var(--dur-base) var(--ease-standard),
    border-color var(--dur-fast) var(--ease-standard);
}
.zt-card--flat { box-shadow: none; }
.zt-card--sm { box-shadow: var(--shadow-sm); }
.zt-card--md { box-shadow: var(--shadow-md); }
.zt-card--lg { box-shadow: var(--shadow-lg); }

.zt-card--interactive { cursor: pointer; }
.zt-card--interactive:hover { border-color: var(--blue-200); box-shadow: var(--shadow-lg); transform: translateY(-2px); }
.zt-card--interactive:active { transform: translateY(0) scale(.99); }
.zt-card--interactive:focus-visible { box-shadow: var(--ring-focus); }

.zt-card__accent { position: absolute; inset-inline: 0; top: 0; height: 3px; }
.zt-card__accent--brand { background: var(--gradient-brand); }

/* --- 1.9 Badge (§1.9) --- */
.zt-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 24px;
  padding: 0 10px;
  border-radius: var(--radius-pill);
  font-family: var(--font-ui);
  font-size: 12px;
  font-weight: var(--fw-semibold);
  letter-spacing: -0.005em;
  white-space: nowrap;
}
.zt-badge--neutral { background: var(--surface-sunken); color: var(--text-secondary); }
.zt-badge--brand { background: var(--surface-brand-soft); color: var(--text-brand); }
.zt-badge--success { background: var(--surface-success-soft); color: var(--text-success); }
.zt-badge--warning { background: var(--surface-warning-soft); color: var(--text-warning); }
.zt-badge--danger { background: var(--surface-danger-soft); color: var(--text-danger); }
/* Литеральный hex в источнике, без токена (components.md §1.9/§5) */
.zt-badge--stars { background: var(--star-100); color: #8A5E00; }
.zt-badge--solid { background: var(--blue-500); color: var(--white); }

.zt-badge__dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; }

/* --- 1.10 Tag → `.zt-chip` (файл компонента WP10b: `chip.js`) (§1.10) ---
   Hover-tint у источника появляется, только если задан onClick — CSS не
   знает про обработчики, поэтому это отдельный модификатор `--clickable`. */
.zt-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 32px;
  padding: 0 14px;
  border-radius: var(--radius-pill);
  background: var(--surface-card);
  color: var(--text-secondary);
  border: 1px solid var(--border-subtle);
  font-family: var(--font-ui);
  font-size: 13px;
  font-weight: var(--fw-semibold);
  cursor: default;
  transition: var(--transition-control);
}
.zt-chip:focus-visible { box-shadow: var(--ring-focus); }
.zt-chip--clickable { cursor: pointer; }
.zt-chip--clickable:hover:not(.zt-chip--selected) { background: var(--surface-brand-soft); }
.zt-chip--clickable:active:not(.zt-chip--selected) { opacity: .85; }

.zt-chip--selected {
  background: var(--blue-500);
  color: var(--text-inverse);
  border-color: var(--blue-500);
}

.zt-chip__remove { opacity: .6; cursor: pointer; }

/* --- 1.11 Tabs (§1.11) ---
   Используется, например, фильтром списка заказов (all/working/unpaid/done
   — CLAUDE.md «вкладки выведены из перевода»). Вариант по умолчанию —
   pill; `--underline` — вторая ветка источника. */
.zt-tabs { display: inline-flex; }
.zt-tabs--pill { gap: 4px; padding: 4px; background: var(--surface-sunken); border-radius: var(--radius-pill); }
.zt-tabs--underline { gap: 24px; padding: 0; border-bottom: 1px solid var(--border-subtle); }

.zt-tabs__tab {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  border: none;
  background: transparent;
  color: var(--text-tertiary);
  font-family: var(--font-ui);
  font-size: 14px;
  font-weight: var(--fw-semibold);
  cursor: pointer;
  transition: var(--transition-control);
}
.zt-tabs__tab:focus-visible { box-shadow: var(--ring-focus); }
.zt-tabs__tab:active { opacity: .85; }

.zt-tabs--pill .zt-tabs__tab { height: 36px; padding: 0 16px; border-radius: var(--radius-pill); }
.zt-tabs--pill .zt-tabs__tab--active { background: var(--surface-card); box-shadow: var(--shadow-sm); color: var(--text-primary); }

.zt-tabs--underline .zt-tabs__tab { height: 44px; padding: 0 2px; border-bottom: 2px solid transparent; }
.zt-tabs--underline .zt-tabs__tab--active { border-bottom-color: var(--blue-500); color: var(--text-primary); }

.zt-tabs__tab--active .zt-icon { color: var(--text-brand); }
.zt-tabs__count { font-size: 12px; color: var(--text-tertiary); font-feature-settings: var(--num-tabular); }

/* --- 1.12 RadioGroup (§1.12) ---
   `--active` на `.zt-radio-option` ставит JS вместе с `checked` на
   input'е (см. общее примечание про :has() в шапке файла). */
.zt-radio-group { display: flex; flex-direction: column; gap: 8px; }
.zt-radio-group__label { font-size: 13px; font-weight: var(--fw-semibold); color: var(--text-secondary); }
.zt-radio-group__options { display: flex; flex-direction: column; gap: 8px; }
.zt-radio-group__options--row { flex-direction: row; gap: 12px; }

.zt-radio-option {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 14px;
  cursor: pointer;
  background: var(--surface-card);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-control);
  transition: var(--transition-control);
}
.zt-radio-group__options--row .zt-radio-option { flex: 1; }
.zt-radio-option:active { opacity: .9; }
.zt-radio-option--active { background: var(--surface-brand-soft); border-color: var(--border-brand); }

.zt-radio-option__input { position: absolute; opacity: 0; width: 0; height: 0; }

.zt-radio-option__ring {
  width: 18px;
  height: 18px;
  flex: 0 0 auto;
  border-radius: 50%;
  border: 2px solid var(--border-default);
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.zt-radio-option--active .zt-radio-option__ring,
.zt-radio-option__input:checked ~ .zt-radio-option__ring { border-color: var(--blue-500); }
.zt-radio-option__input:focus-visible ~ .zt-radio-option__ring { box-shadow: var(--ring-focus); }

.zt-radio-option__dot { width: 8px; height: 8px; border-radius: 50%; background: var(--blue-500); display: none; }
.zt-radio-option--active .zt-radio-option__dot,
.zt-radio-option__input:checked ~ .zt-radio-option__ring .zt-radio-option__dot { display: block; }

.zt-radio-option__text { display: flex; flex-direction: column; gap: 1px; }
.zt-radio-option__title { font-size: 14px; font-weight: var(--fw-semibold); color: var(--text-primary); }
.zt-radio-option__description { font-size: 12px; color: var(--text-tertiary); }


/* ============================================================
   2. Commerce — components.md §2
   ============================================================ */

/* --- 2.1 AmountOption (§2.1) ---
   Выделение сигналится тем же токеном, что и фокус-кольцо (`--ring-focus`)
   — в источнике отдельного focus-стиля у этого компонента нет вовсе. */
.zt-amount-option {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  padding: 14px 16px;
  min-width: 128px;
  border-radius: var(--radius-control);
  cursor: pointer;
  text-align: left;
  transition: var(--transition-control);
}
.zt-amount-option:focus-visible { box-shadow: var(--ring-focus); }
.zt-amount-option:disabled { cursor: not-allowed; opacity: .5; }

.zt-amount-option:not(:disabled):not(.zt-amount-option--selected) {
  background: var(--surface-card);
  border: 1px solid var(--border-subtle);
  box-shadow: none;
}
.zt-amount-option:not(:disabled):not(.zt-amount-option--selected):hover { border-color: var(--blue-200); }
.zt-amount-option:not(:disabled):not(.zt-amount-option--selected):active { opacity: .85; }

.zt-amount-option--selected:not(:disabled) {
  background: var(--surface-brand-soft);
  border: 1px solid var(--border-brand);
  box-shadow: var(--ring-focus);
}

.zt-amount-option__amount {
  font-family: var(--font-display);
  font-size: 18px;
  font-weight: var(--fw-bold);
  letter-spacing: -0.02em;
  color: var(--text-primary);
  font-feature-settings: var(--num-tabular);
}
.zt-amount-option--selected .zt-amount-option__amount { color: var(--text-brand); }

.zt-amount-option__receives { font-size: 12px; color: var(--text-tertiary); font-feature-settings: var(--num-tabular); }

.zt-amount-option__badge {
  position: absolute;
  top: -9px;
  right: 10px;
  height: 18px;
  padding: 0 7px;
  display: inline-flex;
  align-items: center;
  border-radius: var(--radius-pill);
  background: var(--green-500);
  color: var(--white);
  font-size: 11px;
  font-weight: var(--fw-bold);
}

/* Компактный вариант — реальные числа звёздных/GRAM пресетов на экранах
   продукта (screens_products.md «tmaStarPresets», README «Пресеты: 6
   чипов в две колонки»), а не витринная форма AmountOption из бандла.
   Использовать там, где сетка пресетов должна совпасть с прототипом
   (два в ряд, meta-строка мельче). */
.zt-amount-option--compact {
  flex: 1 1 calc(50% - 4px);
  min-width: 0;
  min-height: 56px;
  padding: 8px 12px;
  gap: 2px;
  border-radius: 14px;
}
.zt-amount-option--compact .zt-amount-option__amount {
  font-family: var(--font-ui);
  font-size: 15px;
  font-weight: var(--fw-bold);
  letter-spacing: normal;
}
.zt-amount-option--compact .zt-amount-option__receives { font-weight: var(--fw-semibold); }

/* --- 2.2 ProductTile (§2.2) --- */
.zt-product-tile {
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 20px;
  background: var(--surface-card);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-sm);
  cursor: pointer;
  transition:
    box-shadow var(--dur-base) var(--ease-standard),
    transform var(--dur-base) var(--ease-standard),
    border-color var(--dur-fast) var(--ease-standard);
}
.zt-product-tile:focus-visible { box-shadow: var(--ring-focus); }
.zt-product-tile:not(.zt-product-tile--selected):hover { border-color: var(--blue-200); box-shadow: var(--shadow-lg); transform: translateY(-2px); }
.zt-product-tile:not(.zt-product-tile--selected):active { transform: translateY(0) scale(.99); }
.zt-product-tile--selected { border-color: var(--border-brand); box-shadow: var(--ring-focus); }

.zt-product-tile__top { display: flex; align-items: center; justify-content: space-between; }
.zt-product-tile__icon { width: 48px; height: 48px; border-radius: var(--radius-md); display: flex; align-items: center; justify-content: center; }
.zt-product-tile--steam .zt-product-tile__icon { background: var(--product-steam-soft); color: var(--steam-900); }
.zt-product-tile--telegram .zt-product-tile__icon { background: var(--product-telegram-soft); color: var(--tg-700); }
.zt-product-tile--premium .zt-product-tile__icon { background: var(--surface-brand-soft); color: var(--blue-700); }
/* Литеральный hex в источнике, без токена — тот же, что у Badge stars */
.zt-product-tile--stars .zt-product-tile__icon { background: var(--product-stars-soft); color: #8A5E00; }

.zt-product-tile__body { display: flex; flex-direction: column; gap: 4px; }
.zt-product-tile__title { font-family: var(--font-display); font-size: 18px; font-weight: var(--fw-bold); letter-spacing: -0.014em; color: var(--text-primary); }
.zt-product-tile__subtitle { font-family: var(--font-body); font-size: 14px; line-height: 1.45; color: var(--text-secondary); }
.zt-product-tile__note { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; font-weight: var(--fw-semibold); color: var(--text-brand); margin-top: auto; }

/* --- 2.3 StatusPill (§2.3) ---
   `--self-start` — фикс верности из docs/tma_plan.md §4: на карточке
   заказа пилюля выравнивается по левому краю, а не тянется строкой. */
.zt-status-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 26px;
  padding: 0 11px;
  border-radius: var(--radius-pill);
  font-family: var(--font-ui);
  font-size: 12px;
  font-weight: var(--fw-semibold);
  white-space: nowrap;
}
.zt-status-pill--paid,
.zt-status-pill--delivered { background: var(--surface-success-soft); color: var(--text-success); }
.zt-status-pill--processing { background: var(--surface-warning-soft); color: var(--text-warning); }
.zt-status-pill--awaiting { background: var(--surface-sunken); color: var(--text-secondary); }
.zt-status-pill--failed { background: var(--surface-danger-soft); color: var(--text-danger); }
.zt-status-pill--refunded { background: var(--surface-brand-soft); color: var(--text-brand); }
.zt-status-pill--self-start { align-self: flex-start; }

/* --- 2.4 StepIndicator → `.zt-step-track` (файл WP10b: `step-track.js`) (§2.4) --- */
.zt-step-track { display: flex; align-items: center; gap: 0; width: 100%; }
.zt-step-track__step { display: flex; align-items: center; gap: 9px; }

.zt-step-track__circle {
  width: 26px;
  height: 26px;
  flex: 0 0 auto;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--surface-sunken);
  border: 2px solid transparent;
  color: var(--text-tertiary);
  font-size: 12px;
  font-weight: var(--fw-bold);
  transition: var(--transition-control);
}
.zt-step-track__step--done .zt-step-track__circle { background: var(--blue-500); border-color: var(--blue-500); color: var(--white); }
.zt-step-track__step--active .zt-step-track__circle { background: var(--surface-card); border-color: var(--blue-500); color: var(--text-brand); }

.zt-step-track__label { font-size: 13px; font-weight: var(--fw-medium); color: var(--text-tertiary); white-space: nowrap; }
.zt-step-track__step--done .zt-step-track__label { color: var(--text-secondary); }
.zt-step-track__step--active .zt-step-track__label { font-weight: var(--fw-semibold); color: var(--text-primary); }

/* Цвет коннектора решает шаг ДО него — components.md §2.4 */
.zt-step-track__connector { flex: 1; height: 2px; margin: 0 12px; border-radius: 2px; background: var(--border-subtle); }
.zt-step-track__connector--done { background: var(--blue-300); }

/* --- 2.5 SummaryRow (§2.5) --- */
.zt-summary-row { display: flex; align-items: baseline; justify-content: space-between; gap: 16px; padding: 7px 0; }
.zt-summary-row--emphasis { padding: 14px 0 0; border-top: 1px solid var(--border-subtle); }

.zt-summary-row__label { display: inline-flex; align-items: center; gap: 6px; font-size: 14px; font-weight: var(--fw-regular); color: var(--text-secondary); }
.zt-summary-row--emphasis .zt-summary-row__label { font-size: 15px; font-weight: var(--fw-semibold); color: var(--text-primary); }

.zt-summary-row__value {
  font-family: var(--font-ui);
  font-size: 14px;
  font-weight: var(--fw-semibold);
  letter-spacing: -0.005em;
  color: var(--text-primary);
  font-feature-settings: var(--num-tabular);
  white-space: nowrap;
}
.zt-summary-row--emphasis .zt-summary-row__value { font-family: var(--font-display); font-size: 22px; font-weight: var(--fw-bold); letter-spacing: -0.02em; }
.zt-summary-row--success .zt-summary-row__value { color: var(--text-success); }
.zt-summary-row--danger .zt-summary-row__value { color: var(--text-danger); }


/* ============================================================
   3. Feedback — components.md §3
   ============================================================ */

/* --- 3.1 Dialog (§3.1) --- */
.zt-dialog-overlay {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--surface-overlay);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  padding: 24px;
  animation: zeo-fade var(--dur-base) var(--ease-out);
}
.zt-dialog-panel {
  width: 100%;
  max-width: 460px; /* default width — конкретный экран может переопределить инлайн */
  background: var(--surface-card);
  border-radius: var(--radius-surface);
  box-shadow: var(--shadow-xl);
  padding: 28px;
  display: flex;
  flex-direction: column;
  gap: 18px;
  animation: zeo-rise var(--dur-base) var(--ease-out);
}
.zt-dialog__head { display: flex; align-items: flex-start; gap: 14px; }
.zt-dialog__icon { width: 44px; height: 44px; flex: 0 0 auto; border-radius: var(--radius-md); background: var(--surface-brand-soft); display: flex; align-items: center; justify-content: center; }
.zt-dialog__body { flex: 1; display: flex; flex-direction: column; gap: 6px; }
.zt-dialog__title { font-family: var(--font-display); font-size: 22px; font-weight: var(--fw-bold); letter-spacing: -0.016em; margin: 0; }
.zt-dialog__description { font-family: var(--font-body); font-size: 15px; line-height: 1.55; color: var(--text-secondary); margin: 0; }
.zt-dialog__footer { display: flex; justify-content: flex-end; gap: 10px; }

/* --- 3.2 Toast (§3.2) --- */
.zt-toast {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  min-width: min(320px, 100%);
  max-width: 420px;
  padding: 14px 16px;
  background: var(--surface-card);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
  animation: zeo-toast var(--dur-base) var(--ease-out);
}
.zt-toast__icon--success { color: var(--green-500); }
.zt-toast__icon--error { color: var(--red-500); }
.zt-toast__icon--info { color: var(--blue-500); }
.zt-toast__icon--pending { color: var(--amber-500); }
.zt-toast__body { flex: 1; display: flex; flex-direction: column; gap: 2px; }
.zt-toast__title { font-size: 14px; font-weight: var(--fw-semibold); color: var(--text-primary); }
.zt-toast__description { font-size: 13px; color: var(--text-secondary); line-height: 1.45; }
.zt-toast__close { color: var(--text-tertiary); cursor: pointer; }

/* --- 3.3 Tooltip (§3.3) ---
   Абсолютное позиционирование внутри потока — родитель с overflow:hidden
   (Card) обрежет пузырь; z-index 60, ниже Dialog (100). Дословно по
   источнику. */
.zt-tooltip { position: relative; display: inline-flex; }
.zt-tooltip__bubble {
  position: absolute;
  z-index: 60;
  padding: 7px 10px;
  max-width: 240px;
  width: max-content;
  background: var(--n-900);
  color: var(--text-inverse);
  border-radius: var(--radius-xs);
  font-size: 12px;
  line-height: 1.4;
  font-weight: var(--fw-medium);
  box-shadow: var(--shadow-md);
  pointer-events: none;
}
.zt-tooltip__bubble--top { bottom: calc(100% + 8px); left: 50%; transform: translateX(-50%); }
.zt-tooltip__bubble--bottom { top: calc(100% + 8px); left: 50%; transform: translateX(-50%); }
.zt-tooltip__bubble--left { right: calc(100% + 8px); top: 50%; transform: translateY(-50%); }
.zt-tooltip__bubble--right { left: calc(100% + 8px); top: 50%; transform: translateY(-50%); }


/* ============================================================
   4. App shell — TMA-специфичная обвязка, НЕ из _ds_bundle.js.
   ============================================================
   Разметка самого прототипа Mini App (design/ZeoTopup.dc.html,
   TMA-раздел), пересказанная в README.md «Screens / Views» и в
   shell.md §5–6 / screens_orders.md §5.1 и §7.3 (числа проверены по
   обоим источникам — совпадают). Тексту задания WP10a эти файлы не
   названы, но без этих классов `js/components/{header,composer,
   soft-card,ticket-pill}.js` (WP10b) собирать не из чего — см. handoff
   в ответе задачи. */

/* --- Шапка экрана (shell.md §5, README «Шапка») --- */
.zt-app-header {
  flex: 0 0 auto;
  padding: 12px 14px;
  display: flex;
  align-items: center;
  gap: 10px;
  border-bottom: 1px solid var(--border-subtle);
  background: var(--surface-card);
}
/* Глиф "←" — литеральный текстовый символ в источнике, не Lucide-иконка
   (shell.md §5.1). Показывается только на пяти вложенных экранах. */
.zt-app-header__back {
  width: 32px;
  height: 32px;
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--surface-sunken);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-pill);
  cursor: pointer;
  font-family: inherit;
  font-size: 15px;
  color: var(--text-secondary);
}
.zt-app-header__back:focus-visible { box-shadow: var(--ring-focus); }
.zt-app-header__back:active { opacity: .85; }

.zt-app-header__title {
  flex: 1;
  min-width: 0;
  font-size: 15px;
  font-weight: var(--fw-bold);
  letter-spacing: -0.012em;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.zt-app-header__pills { display: flex; gap: 8px; flex: 0 0 auto; }
.zt-app-header__pill {
  height: 34px;
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 0 12px 0 10px;
  font-size: 13px;
  font-weight: var(--fw-semibold);
  white-space: nowrap;
  border-radius: var(--radius-pill);
  cursor: pointer;
  font-family: inherit;
  background: transparent;
  border: 1px solid var(--border-subtle);
  color: var(--text-secondary);
  transition: var(--transition-control);
}
.zt-app-header__pill:focus-visible { box-shadow: var(--ring-focus); }
.zt-app-header__pill:active { opacity: .85; }
.zt-app-header__pill--active { background: var(--surface-brand-soft); border-color: var(--border-brand); color: var(--text-brand); }

/* --- Продуктовый переключатель (shell.md §6, README п.2) ---
   4 таба делят ширину поровну; виден на stars/premium/steam/gram/orders/
   support, скрыт вместе с шапка-назад (back-button ⟺ нет таб-бара). */
.zt-product-tabs {
  flex: 0 0 auto;
  padding: 8px;
  margin: 10px 14px 0;
  display: flex;
  gap: 4px;
  background: var(--surface-sunken);
  border-radius: var(--radius-pill);
  overflow-x: auto;
  scrollbar-width: none;
}
.zt-product-tabs::-webkit-scrollbar { display: none; }

.zt-product-tabs__tab {
  flex: 1 1 0;
  min-height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  border: 1px solid transparent;
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--text-secondary);
  font-family: inherit;
  font-size: 14px;
  font-weight: var(--fw-semibold);
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease-standard), color var(--dur-fast) var(--ease-standard);
}
.zt-product-tabs__tab:focus-visible { box-shadow: var(--ring-focus); }
.zt-product-tabs__tab:active { opacity: .85; }
.zt-product-tabs__tab--active {
  background: var(--surface-card);
  border-color: var(--border-brand);
  box-shadow: var(--shadow-sm);
  color: var(--text-brand);
  font-weight: var(--fw-bold);
}

/* --- Мягкая карточка-кнопка → `.zt-soft-card` (файл WP10b: `soft-card.js`)
   Общая база строк заказов/тикетов/сроков Premium/способов оплаты
   (screens_orders.md §2/§5, screens_products.md §5 «softBtn»). --- */
.zt-soft-card {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 14px;
  width: 100%;
  cursor: pointer;
  font-family: inherit;
  text-align: left;
  background: var(--surface-card);
  color: var(--text-primary);
  border: 1px solid var(--border-subtle);
  box-shadow: var(--shadow-sm);
  border-radius: 16px;
  transition: border-color var(--dur-fast) var(--ease-standard), box-shadow var(--dur-fast) var(--ease-standard);
}
.zt-soft-card:focus-visible { box-shadow: var(--ring-focus); }
.zt-soft-card:disabled { cursor: not-allowed; opacity: .6; }
.zt-soft-card:not(:disabled):active { opacity: .92; }
.zt-soft-card--selected { border-color: var(--border-brand); box-shadow: var(--ring-focus); }

/* --- Пилюля тикета — второй, независимый набор от StatusPill (CLAUDE.md:
   «TicketPill stays a second, independent pill system... do not merge
   with StatusPill»; screens_orders.md §5.1 «ticketPill»). Тона, не
   зашитые статусы — какой статус на какой тон ложится, решает WP10b/
   backend (тикет у нас — тумблер open/closed, не воронка из прототипа). --- */
.zt-ticket-pill {
  display: inline-flex;
  align-items: center;
  padding: 3px 9px;
  border-radius: var(--radius-pill);
  font-size: 11px;
  font-weight: var(--fw-bold);
  white-space: nowrap;
}
.zt-ticket-pill--brand { background: var(--surface-brand-soft); color: var(--text-brand); }
.zt-ticket-pill--success { background: var(--surface-success-soft); color: var(--text-success); }
.zt-ticket-pill--warning { background: var(--surface-warning-soft); color: var(--text-warning); }

/* --- Композер треда поддержки (screens_orders.md §7.3) --- */
.zt-composer {
  flex: 0 0 auto;
  padding: 12px 14px;
  border-top: 1px solid var(--border-subtle);
  background: var(--surface-card);
  display: flex;
  gap: 10px;
  align-items: flex-end; /* кнопка отправки прижата к низу, пока textarea не растёт (minHeight фиксирован) */
}

/* Общая textarea — композер треда И форма нового тикета
   (screens_orders.md §6/§7.3, `tmaTextareaStyle`, используется в обоих
   местах). Фокус-кольцо — не в источнике, добавлено по той же логике,
   что и §1 (единообразие с Input/Select). */
.zt-textarea {
  min-height: 58px;
  resize: none;
  padding: 10px 12px;
  flex: 1;
  font-family: var(--font-body);
  font-size: 16px; /* см. примечание про зум iOS ниже по файлу */
  line-height: 1.5;
  color: var(--text-primary);
  background: var(--surface-page);
  border: 1px solid var(--border-default);
  border-radius: 12px;
  outline: none;
  transition: var(--transition-control);
}
.zt-textarea:focus { border-color: var(--border-brand); box-shadow: var(--ring-focus); }
.zt-textarea::placeholder { color: var(--text-tertiary); }

/* --- Пузыри переписки (screens_orders.md §7.2) --- */
.zt-bubble { display: flex; flex-direction: column; gap: 5px; padding: 13px; border-radius: 16px; max-width: 90%; }
.zt-bubble--own { align-self: flex-end; background: var(--surface-sunken); border: 1px solid var(--border-subtle); }
.zt-bubble--support { align-self: flex-start; background: var(--surface-brand-soft); border: 1px solid transparent; }
.zt-bubble__meta { display: flex; gap: 10px; align-items: center; justify-content: space-between; }
.zt-bubble__author { font-size: 11px; font-weight: var(--fw-bold); color: var(--text-tertiary); }
.zt-bubble__time { font-size: 11px; color: var(--text-tertiary); font-feature-settings: var(--num-tabular); }
.zt-bubble__text { font-family: var(--font-body); font-size: 13px; line-height: 1.55; color: var(--text-primary); }

/* Каркас приложения (WP11). Держит четыре полосы во всю высоту окна: высота
   берётся из `--tg-viewport-height`, которую адаптер обновляет по событию
   `viewportChanged` — на 100dvh поверх открытой клавиатуры Telegram лежит не
   то число. `min-height: 0` у тела обязателен: без него flex-ребёнок не
   сжимается ниже своего содержимого и прокручивается страница целиком, вместе
   с шапкой. */
.zt-app {
  display: flex;
  flex-direction: column;
  height: var(--tg-viewport-height, 100dvh);
  overflow: hidden;
  background: var(--surface-page);
}
.zt-app__body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 16px 14px 20px;
}

/* 16px у всех полей ввода — не вкус, а требование платформы: Safari и вебвью
   Telegram на iOS приближают страницу при фокусе на поле, у которого шрифт
   меньше шестнадцати, и обратно она сама не отъезжает. Дизайн называл 15px у
   строки и 14px у textarea (§1.4); разница в пиксель-другой стоит дешевле, чем
   прыгающий экран на каждом вводе имени получателя. Не менять обратно, не
   проверив на живом iPhone. Второй способ — `maximum-scale=1` в viewport —
   намеренно не использован: он выключает и обычный пинч-зум, то есть чинит
   ввод за счёт тех, кто увеличивает текст, чтобы его прочесть. */
