/* Полоса действий Мастерской. Все стили под .pb-ab, глобальных селекторов нет. */

.pb-ab {
  /* геометрия - менять только здесь */
  /* 17.09: было 602px жёстким числом - полоса отказывалась сжиматься и распирала
     раскладку вправо, за край окна (замер на 390px: правое поле -253 против левого 24,
     кадр тянулся за полосой). Ниже 593px ширины полосы ab.js ставит data-ab-stack и
     правая колонка уходит под управление отдельной строкой - место для сжатия появляется,
     и держать минимум больше нечем. 0 вместо снятия свойства: значение читают темы. */
  --ab-min: 0;              /* сжатие ограничивает раскладка stack, а не число */
  --ab-max: 1000px;         /* выше не растягивается */
  --ab-pad: 14px;           /* внутренний отступ со всех четырёх сторон */
  --ab-gap-col: 12px;       /* между левой колонкой и кнопкой */
  --ab-gap-row: 8px;        /* между рядами и между кнопками */
  --ab-row: 48px;           /* высота ряда управления */
  --ab-tile-h: 68px;        /* нижняя граница плитки режима (сетка 3×2 «Фотолаборатории») */
  /* Высота сетки режимов - ОДИН источник для обоих видов полосы: сама сетка и вид уточнения,
     который встаёт на её место. Задана числом, а не выведена из --ab-tile-h: плитки выше
     своего минимума, их высоту набирает подпись в две строки.
     13.09: 175 -> 104. Решение оператора - полоса «Фотолаборатории» встаёт в ту же высоту,
     что «Восстановить» и «Оживить» (134px): полоса была 205px, из них 175 съедала сетка.
     Плитка сложена в СТРОКУ (иконка слева, подпись справа), строка стоимости с плитки снята
     - цену несёт главная кнопка. В режиме уточнения поле ввода отдаёт 2 строки вместо 4,
     текст в нём прокручивается (замер 13.09, ниже 134px полоса не идёт - см. min-height). */
  --ab-grid-h: 104px;
  --ab-cta: 216px;          /* ширина правой колонки */
  --ab-meta: 20px;          /* высота служебной строки */
  --ab-radius: 18px;        /* радиус полосы */
  --ab-radius-in: 11px;     /* радиус элементов внутри */
  --ab-input-max: 86px;     /* поле растёт максимум до трёх строк */

  /* цвета витрины - решение оператора 08.09: палитра пакета заменена на токены проекта
     (tokens.css). --ab-ok оставлен как есть - это уже существующий зелёный успеха проекта. */
  --ab-ink: var(--fg);
  --ab-ink-2: #5c4e3f;
  --ab-muted: var(--muted);
  --ab-muted-2: #6b5c50;
  --ab-line: rgba(46, 28, 18, .14);
  --ab-line-soft: rgba(46, 28, 18, .1);
  --ab-surface: var(--card);
  --ab-surface-2: #fafaf7;
  --ab-track: var(--track);
  /* 09.09, правка оператора: главная кнопка - основной цвет проекта и его реакция на
     наведение, те же, что у «Выбрать фото» (.btn-primary). --pick-ink оказался темнее нужного. */
  --ab-accent: var(--accent);
  --ab-accent-hover: var(--accent-hover);
  --ab-accent-soft: #b8652a;
  --ab-off: var(--paper-3);
  --ab-progress: var(--bg-alt);
  --ab-progress-fill: #e7c9a6;
  --ab-ok: #1e6b30;
  --ab-focus: 3px solid rgba(184, 101, 42, .5);

  box-sizing: border-box;
  width: 100%;
  min-width: var(--ab-min);
  max-width: var(--ab-max);
  margin: 0 auto;
  display: flex;
  align-items: center;
  padding: var(--ab-pad);
  background: var(--ab-surface);
  border: 1px solid var(--ab-line-soft);
  border-radius: var(--ab-radius);
  font-family: var(--sans, Inter, system-ui, sans-serif);
  color: var(--ab-ink);
  /* высота НЕ задаётся: её набирает содержимое, 134px в каждом состоянии */
}

.pb-ab *,
.pb-ab *::before,
.pb-ab *::after { box-sizing: border-box; }

.pb-ab button { font-family: inherit; }
.pb-ab button:focus-visible,
.pb-ab textarea:focus-visible { outline: var(--ab-focus); outline-offset: 2px; }
.pb-ab textarea:focus { outline: none; }

/* ── переключение видов по состоянию ─────────────────────────── */

.pb-ab__work,
.pb-ab__done { display: none; flex: 1 1 auto; min-width: 0; align-items: center; }

.pb-ab[data-ab-state="ready"] .pb-ab__work,
.pb-ab[data-ab-state="working"] .pb-ab__work,
.pb-ab[data-ab-state="empty"] .pb-ab__work,
.pb-ab[data-ab-state="error"] .pb-ab__work,
.pb-ab[data-ab-state="zero"] .pb-ab__work,
.pb-ab[data-ab-state="no-credits"] .pb-ab__work,
.pb-ab[data-ab-state="guest-used"] .pb-ab__work { display: flex; gap: var(--ab-gap-col); }

.pb-ab[data-ab-state="done"] .pb-ab__done,
.pb-ab[data-ab-state="guest-done"] .pb-ab__done { display: flex; gap: 14px; }

/* ── рабочий вид ─────────────────────────────────────────────── */

.pb-ab__controls {
  flex: 1 1 auto;
  min-width: 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--ab-gap-row);
}

/* управление недоступно, но остаётся на месте: полоса не перестраивается */
.pb-ab[data-ab-state="working"] .pb-ab__controls,
.pb-ab[data-ab-state="empty"] .pb-ab__controls,
.pb-ab[data-ab-state="error"] .pb-ab__controls,
.pb-ab[data-ab-state="zero"] .pb-ab__controls,
.pb-ab[data-ab-state="no-credits"] .pb-ab__controls,
.pb-ab[data-ab-state="guest-used"] .pb-ab__controls { opacity: .45; pointer-events: none; }
/* Гость и новорег с нулевым балансом: режимы ОСТАЮТСЯ живыми (решение оператора 17.09,
   одна логика на оба случая). Оба ещё не решили, платить ли, и выбор режима — это и есть
   способ понять, за что платят: «Помахать рукой» и «Поворот головы» дают разный результат.
   Притушенный некликабельный ряд читался как поломка («режимы не переключаются»).
   Тот же приём, что у Фотолаборатории в empty строкой ниже: гасим не всю группу, а только
   поле уточнения. Сама кнопка запуска заперта отдельно, своим состоянием: в zero ведёт
   в баланс, в guest-used на вход.
   ⚠️ .pb-ab__edit НЕ гасим целиком: там живёт «Обрезать» (#flDockCropBtn → runCrop) —
   операция бесплатная и целиком браузерная, сервера она не трогает, и отнимать её у гостя
   или у человека с нулём незачем (находка ревью 17.09; тот же разбор, что 13.09 у прежнего
   широкого гейта в runCrop). «Заменить фото» тоже оставляем: сменить кадр можно до оплаты. */
.pb-ab[data-ab-state="zero"] .pb-ab__controls,
.pb-ab[data-ab-state="guest-used"] .pb-ab__controls { opacity: 1; pointer-events: auto; }
.pb-ab[data-ab-state="zero"] .pb-ab__field,
.pb-ab[data-ab-state="guest-used"] .pb-ab__field { opacity: .45; pointer-events: none; }
/* Фотолаборатория, empty (решение оператора 09.09): режимы можно смотреть и знакомиться с
   ними ДО выбора фото - плитка и знаки «?» остаются кликабельны и не притушены. Применение
   («Применить»/«Заменить»/«Обрезать» в .pb-ab__edit, поле уточнения) неактивно, пока фото
   нет. opacity родителя (.pb-ab__controls) действует на весь поддерево через compositing -
   свой opacity:1 на потомке её не отменяет, поэтому притушиваем точечно .pb-ab__edit/
   .pb-ab__field, а не всю .pb-ab__controls. */
#flAb[data-ab-state="empty"] .pb-ab__controls { opacity: 1; pointer-events: auto; }
#flAb[data-ab-state="empty"] .pb-ab__edit { opacity: .45; pointer-events: none; }
/* Поле уточнения живёт и БЕЗ выбранного фото (правка оператора 09.09): человек описывает
   правку словами, пока фото ещё грузится или выбирается. Заблокированным остаётся только
   применение - «Применить» и «Заменить/Обрезать» выше. */
#flAb[data-ab-state="empty"] .pb-ab__field { opacity: 1; pointer-events: auto; }

.pb-ab__row { display: flex; align-items: center; gap: var(--ab-gap-row); }

/* Сегментов в тугле N (09.09): у «Восстановить» два, у «Оживить» три стиля движения.
   Число задаёт атрибут data-ab-segs на тугле (без него 2); ширина сегмента считается
   из поля 3 и зазора 3, ползунок встаёт под нажатый сегмент по aria-pressed через :has -
   позиции не зависят от имён режимов, поэтому и правило для «bw» ниже остаётся частным
   случаем той же формулы (50% - 4.5px = (100% - 6px - 3px) / 2). */
.pb-ab__toggle {
  --ab-segs: 2;
  --ab-seg-w: calc((100% - 6px - 3px * (var(--ab-segs) - 1)) / var(--ab-segs));
  position: relative;
  flex: 1 1 240px;
  min-width: 236px;
  height: var(--ab-row);
  display: flex;
  gap: 3px;
  padding: 3px;
  background: var(--ab-track);
  border: 1px solid rgba(46, 28, 18, .08);
  border-radius: 12px;
}

/* Трём сегментам с подписями нужно больше двух: при равной с блоком правок доле ряда
   (544 на 1280-1440) сегмент выходил 84px, подписи вылезали на 26-32px (замер 09.09).
   Основа 400 и минимум 354 = три сегмента по ~116 («Помахать рукой» со значком). */
.pb-ab__toggle[data-ab-segs="3"] { --ab-segs: 3; flex: 1 1 400px; min-width: 354px; }

.pb-ab__knob {
  position: absolute;
  top: 3px;
  bottom: 3px;
  left: 3px;
  width: var(--ab-seg-w);
  border-radius: 9px;
  background: #fff;
  box-shadow: var(--shadow-1);
  transition: left 220ms cubic-bezier(.2, .8, .2, 1);
  pointer-events: none;
}

.pb-ab[data-ab-mode="bw"] .pb-ab__knob { left: calc(50% + 1.5px); }
.pb-ab__toggle:has(> .pb-ab__seg:nth-of-type(2)[aria-pressed="true"]) .pb-ab__knob { left: calc(3px + (var(--ab-seg-w) + 3px)); }
.pb-ab__toggle:has(> .pb-ab__seg:nth-of-type(3)[aria-pressed="true"]) .pb-ab__knob { left: calc(3px + 2 * (var(--ab-seg-w) + 3px)); }

.pb-ab__seg {
  position: relative;
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  border: 0;
  border-radius: 9px;
  background: transparent;
  cursor: pointer;
  color: var(--ab-muted);
  font: 600 15px/1 var(--sans, Inter);
  white-space: nowrap;
  transition: color 180ms ease;
}

.pb-ab__seg span { color: var(--ab-ink-2); transition: color 180ms ease; }
.pb-ab__seg svg { flex: 0 0 17px; }
.pb-ab__seg[aria-pressed="true"] span { color: var(--ab-ink); }
.pb-ab__seg[aria-pressed="true"] { color: var(--ab-accent-soft); }
.pb-ab__seg[data-ab-mode="bw"][aria-pressed="true"] { color: var(--ab-ink); }

/* Тугл-карточки (09.09, правка оператора: «тот же переключатель, но карточки выше и с
   описанием»): сегмент - столбик значок / название / пояснение, высота тугла = два ряда
   и зазор (104), то есть ровно высота рабочего вида полосы 134 - ползунок и механика те же.
   На 1280-1440 сегмент ~128px: название 14.5px в одну строку, пояснение 12.5px в две. */
.pb-ab__toggle--cards { height: calc(var(--ab-row) * 2 + var(--ab-gap-row)); }
.pb-ab__toggle--cards .pb-ab__seg {
  flex-direction: column;
  gap: 3px;
  padding: 8px 4px;
  white-space: normal;
  line-height: 1.15;
  text-align: center;
}
.pb-ab__toggle--cards .pb-ab__seg svg { flex: 0 0 20px; width: 20px; height: 20px; margin-bottom: 2px; }
/* 14px и поля 4: на сегменте 128 «Помахать рукой» (110px) в одну строку, при 14.5/6 ломалось. */
.pb-ab__toggle--cards .pb-ab__seg span { font-size: 14px; }
.pb-ab__toggle--cards .pb-ab__note-sm { display: block; font: 400 12.5px/1.2 var(--sans, Inter); color: var(--ab-muted); }
.pb-ab__toggle--cards .pb-ab__seg[aria-pressed="true"] .pb-ab__note-sm { color: var(--ab-ink-2); }
/* Узкая ступень (полоса 602-775): сегмент ~125px, те же кегли - пояснение переносится само. */

.pb-ab__edit {
  flex: 1 1 236px;
  min-width: 236px;
  height: var(--ab-row);
  display: flex;
  align-items: center;
  border: 1px solid var(--ab-line);
  border-radius: var(--ab-radius-in);
  background: var(--ab-surface);
}

.pb-ab__editbtn {
  flex: 1 1 0;
  min-width: 0;
  height: calc(var(--ab-row) - 2px);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  border: 0;
  border-radius: 10px;
  background: transparent;
  color: var(--ab-ink);
  font: 500 15px/1 var(--sans, Inter);
  white-space: nowrap;
  cursor: pointer;
}

.pb-ab__editbtn svg { flex: 0 0 17px; color: var(--ab-ink-2); }
/* Блок правок с одной кнопкой («Оживить»: обрезки нет) не держит 236 - ужимается по
   содержимому и отдаёт ряд туглу; у двух кнопок («Восстановить») правило не срабатывает. */
.pb-ab__edit:has(> .pb-ab__editbtn:only-child) { flex: 0 0 auto; min-width: 0; }
.pb-ab__edit:has(> .pb-ab__editbtn:only-child) .pb-ab__editbtn { padding: 0 16px; }
.pb-ab__editbtn:hover { background: var(--bg); }
.pb-ab__sep { flex: 0 0 1px; width: 1px; height: 24px; background: var(--ab-line); }

/* узкая ступень: подписи прячутся, остаются иконки 46x46 */
.pb-ab[data-ab-tier="narrow"] .pb-ab__edit { flex: 0 0 auto; min-width: 0; }
.pb-ab[data-ab-tier="narrow"] .pb-ab__editbtn { flex: 0 0 46px; width: 46px; padding: 0 !important; }
.pb-ab[data-ab-tier="narrow"] .pb-ab__editlabel { display: none; }

/* ── раскладка «в столбик» (телефон и узкое окно) ─────────────
   Решение оператора 17.09 из двух показанных вариантов: правая колонка уходит ПОД
   управление отдельной строкой во всю ширину, а не ужимается рядом с ним. Признак ставит
   ab.js по СОБСТВЕННОЙ ширине полосы (data-ab-stack), не медиазапросом по окну: полоса
   живёт внутри колонки, и при свёрнутом боковом меню окно про её ширину врёт.
   Тач-цели не мельчают: кнопка 76px, иконки правок 46px - оба выше канонных 44px. */
.pb-ab[data-ab-stack] .pb-ab__work { flex-direction: column; align-items: stretch; gap: var(--ab-gap-row); }
.pb-ab[data-ab-stack] .pb-ab__controls { flex: 0 0 auto; }
/* Ряд переносится: тугл занимает строку целиком, иконки правок встают под ним.
   Без переноса тугл (min 236) и две иконки (93) просят 337px - на 375px окна с полями
   раскладки остаётся 279px, и тугл выдавил бы иконки за край. */
.pb-ab[data-ab-stack] .pb-ab__row { flex-wrap: wrap; }
.pb-ab[data-ab-stack] .pb-ab__toggle { flex: 1 1 100%; min-width: 0; }
/* Колонка действия во всю ширину, кнопка и мета под ней: сетка 1fr auto из широкой
   раскладки здесь дала бы кнопке всю свободную высоту строки. */
.pb-ab[data-ab-stack] .pb-ab__cta { flex: 0 0 auto; align-self: stretch; grid-template-rows: auto auto; }
.pb-ab[data-ab-stack] .pb-ab__meta { justify-content: center; }

/* Текст вместо управления (09.09, «Оживить» в состоянии working: полоса объясняет, что
   ждать не обязательно). Высота ряда та же, что у тугла, - полоса не меняет рост. */
.pb-ab__note {
  flex: 1 1 auto;
  min-width: 0;
  margin: 0;
  min-height: var(--ab-row);
  display: flex;
  align-items: center;
  font: 400 14px/1.4 var(--sans, Inter);
  color: var(--ab-ink-2);
}

.pb-ab__field {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: var(--ab-row);
  padding: 0 var(--ab-pad);
  border: 1px solid var(--ab-line);
  border-radius: var(--ab-radius-in);
  background: var(--ab-surface-2);
}

.pb-ab__input {
  flex: 1 1 auto;
  min-width: 0;
  height: 46px;
  max-height: var(--ab-input-max);
  padding: 13px 0;
  border: 0;
  outline: none;
  resize: none;
  overflow: auto;
  background: transparent;
  font: 400 15px/20px var(--sans, Inter);
  color: var(--ab-ink);
}

.pb-ab__input::placeholder {
  color: var(--ab-muted-2);
  opacity: 1;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: clip;
}

/* ── правая колонка ──────────────────────────────────────────── */

.pb-ab__cta {
  flex: 0 0 var(--ab-cta);
  min-width: 0;
  align-self: stretch;
  display: grid;
  grid-template-rows: 1fr auto;
  gap: var(--ab-gap-row);
}

.pb-ab__go {
  /* 09.09, правка оператора: кнопка одним цветом, без отдельной тёмной колонки под
     стоимость; надпись и стоимость - одна группа по центру кнопки. */
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  /* 76px - высота кнопки во всех разделах Мастерской: «Восстановить», «Оживить» и
     «Фотолаборатория». Колонка действия бывает выше (высокая плитка режимов, поле
     уточнения), и кнопка держит свою высоту, а не тянется за колонкой. */
  height: 76px;
  padding: 0 12px;
  border: 0;
  border-radius: 13px;
  background: var(--ab-accent);
  color: #fff;
  overflow: hidden;
  cursor: pointer;
  transition: background .15s, transform .15s;
}

.pb-ab__go:hover { background: var(--ab-accent-hover); transform: translateY(-1px); }

.pb-ab__go-label {
  flex: 0 1 auto;
  min-width: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font: 600 16.5px/1.2 var(--sans, Inter);
  text-align: center;
}

.pb-ab__go-cost {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 5px;
  font: 600 16px/1 var(--sans, Inter);
}

/* Знак стоимости - тот же .restore-spark, что в шапке (три искры, мерцание по наведению
   ведёт restoreTwinkle из workshop.html). Правка оператора 09.09: до неё в кнопках стояла
   своя контурная звезда, а правило анимации искало .rs-star и не находило - мерцание было
   мертво на всех трёх кнопках. Размер здесь свой: в шапке знак 17px, на кнопке 16px. */
.pb-ab__go-cost .restore-spark { width: 16px; height: 16px; flex-shrink: 0; }

/* Подпись в две строки («Улучшить качество» не влезает в колонку 216px): зазор до цифры
   сокращается, иначе группа «подпись + стоимость» читается разреженнее, чем на однострочных
   кнопках при том же числе (правка оператора 09.09 - «отступ должен меняться, чтобы
   визуально выглядело одинаково»). Класс ставит fitGoLabel по факту переноса, а не по длине
   текста: перенос зависит от шрифта и ширины колонки, считать его по символам нельзя. */
.pb-ab__go.is-wrapped { gap: 6px; }
.pb-ab__go.is-wrapped .pb-ab__go-label { text-align: left; justify-content: flex-start; }

/* нет фото - кнопка неактивна */
.pb-ab[data-ab-state="empty"] .pb-ab__go {
  background: var(--ab-off);
  color: var(--ab-ink-2);
  pointer-events: none;
}
.pb-ab[data-ab-state="empty"] .pb-ab__go-cost { display: none; }

/* денежные состояния - другая надпись, счётчик не нужен */
.pb-ab[data-ab-state="error"] .pb-ab__go-cost,
.pb-ab[data-ab-state="zero"] .pb-ab__go-cost,
.pb-ab[data-ab-state="no-credits"] .pb-ab__go-cost,
.pb-ab[data-ab-state="guest-used"] .pb-ab__go-cost { display: none; }
.pb-ab[data-ab-state="error"] .pb-ab__go-label,
.pb-ab[data-ab-state="zero"] .pb-ab__go-label,
.pb-ab[data-ab-state="no-credits"] .pb-ab__go-label,
.pb-ab[data-ab-state="guest-used"] .pb-ab__go-label { font-size: 16px; }

.pb-ab__progress {
  position: relative;
  display: none;
  align-items: center;
  justify-content: center;
  height: 100%;
  min-height: 56px;
  border-radius: 13px;
  background: var(--ab-progress);
  color: var(--ab-ink);
  overflow: hidden;
}

.pb-ab__progress-fill {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 0%;
  background: var(--ab-progress-fill);
  transition: width 400ms linear;
}

.pb-ab__progress-label { position: relative; font: 600 16px/1 var(--sans, Inter); white-space: nowrap; }

.pb-ab[data-ab-state="working"] .pb-ab__go { display: none; }
.pb-ab[data-ab-state="working"] .pb-ab__progress { display: flex; }

.pb-ab__meta {
  margin: 0;
  height: var(--ab-meta);
  min-width: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  font: 500 14px/1.3 var(--sans, Inter);
  color: var(--ab-ink-2);
  white-space: nowrap;
}

.pb-ab__meta-ico { color: var(--ab-muted); }
.pb-ab__meta-dot { color: var(--ab-muted); }
.pb-ab__meta-text { min-width: 0; overflow: hidden; text-overflow: ellipsis; }

/* «Бесплатное фото использовано» просит 221px при колонке 216 — не хватает 5px.
   Значков в этом состоянии нет (правило ниже показывает часы и кошелёк только
   в ready/working), поэтому текст занимает колонку целиком и 13.5px хватает
   с запасом (213px). Кегль снижен ТОЛЬКО здесь: остальные подписи короче
   и остаются на 14px. */
.pb-ab[data-ab-state="guest-used"] .pb-ab__meta { font-size: 13.5px; }

/* Гость потратил бесплатное фото: кнопка выглядит как обычная «Восстановить», но заперта -
   решение оператора 15.09. Клик НЕ гасим (pointer-events живые): мост уводит его на вход
   (ab-bridge.js, ветка guest-used), иначе человек упирался бы в тупик. Замок тем же приёмом,
   что у плиток Фотолаборатории (.fl-op-locked): приглушение плюс значок.
   Подсказку ставит applyGuestUsedLock через shared/tooltip.js - она отдельный узел в body,
   поэтому opacity кнопки её не гасит (в отличие от псевдоэлемента, где это уже ловили). */
.pb-ab[data-ab-state="guest-used"] .pb-ab__go {
  opacity: .62;
  position: relative;
  /* Замок ниже - абсолютный псевдоэлемент (left:10px, ~12px шириной), он НЕ раздвигает поток.
     Пока текст был коротким («Восстановить»), центрирование оставляло зазор; с «Войти и
     восстановить» (176px в кнопке 216) текст доезжает до значка вплотную - замер 17.09 дал
     зазор -2px, буквы под иконкой. Отступ слева убирает наложение, симметричный справа
     сохраняет оптический центр: содержимое центрируется в том, что осталось. */
  padding-left: 30px;
  padding-right: 30px;
}
.pb-ab[data-ab-state="guest-used"] .pb-ab__go::before {
  content: '🔒';
  position: absolute;
  left: 10px;
  top: 50%;
  transform: translateY(-50%);
  font-size: 12px;
  line-height: 1;
}
/* Цена рядом с текстом в этом состоянии бессмысленна: списывать нечего, пока не вошёл. */
.pb-ab[data-ab-state="guest-used"] .pb-ab__go-cost { display: none; }
/* ...и потому же возвращаем центрирование, которое is-wrapped снимает у переносимых подписей.
   Левый край там нужен, чтобы подпись группировалась с цифрой стоимости справа; в замке цифра
   скрыта строкой выше, выравнивать не по чему, и текст просто съезжает влево (правка оператора
   17.09: CTA всегда центрируется). Перебиваем именно пару из is-wrapped, не трогая gap. */
.pb-ab[data-ab-state="guest-used"] .pb-ab__go.is-wrapped .pb-ab__go-label {
  text-align: center;
  justify-content: center;
}

/* часы - только пока считаем, кошелёк - только в начальном виде.
   Иконки становятся inline-flex, текстовые узлы остаются inline,
   иначе флекс съест пробел внутри «на балансе 99». */
.pb-ab [data-ab-part="clock"],
.pb-ab [data-ab-part="wallet"] { display: none; }
.pb-ab[data-ab-state="ready"] span[data-ab-part="clock"],
.pb-ab[data-ab-state="working"] span[data-ab-part="clock"],
.pb-ab[data-ab-state="ready"] span[data-ab-part="wallet"] { display: inline; }
.pb-ab[data-ab-state="ready"] .pb-ab__meta-ico[data-ab-part="clock"],
.pb-ab[data-ab-state="working"] .pb-ab__meta-ico[data-ab-part="clock"],
.pb-ab[data-ab-state="ready"] .pb-ab__meta-ico[data-ab-part="wallet"] { display: inline-flex; }

/* ── вид результата ──────────────────────────────────────────── */

.pb-ab__rate {
  flex: 0 0 auto;
  display: grid;
  gap: 6px;
  justify-items: center;
  padding-right: var(--ab-pad);
  border-right: 1px solid var(--ab-line-soft);
}

.pb-ab__rate-title { font: 500 14px/1 var(--sans, Inter); color: var(--ab-ink-2); white-space: nowrap; }
.pb-ab__stars { display: flex; align-items: center; gap: 2px; }

.pb-ab__star {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border: 0;
  background: transparent;
  color: var(--ab-accent-soft);
  cursor: pointer;
}

.pb-ab__star[aria-pressed="true"] svg { fill: var(--ab-accent-soft); }
.pb-ab__star:disabled { cursor: default; }
/* Наведение до оценки (правка оператора 09.09, как было у rating-5star-test.js на проде):
   звёзды до наведённой включительно заливаются, после неё - пустые. После оценки кнопки
   disabled, наведение на них ничего не меняет. */
.pb-ab__stars:hover .pb-ab__star:not(:disabled) svg { fill: var(--ab-accent-soft); }
.pb-ab__stars:hover .pb-ab__star:not(:disabled):hover ~ .pb-ab__star svg { fill: none; }

/* Реакция на оценку (ab-bridge.js, 09.09, тот же сценарий, что rating-5star-test.js на
   проде): звёзды и подпись на время уходят (visibility, место остаётся - без сдвига),
   поверх них в границах блока появляется благодарность, через 2,2-3,5 с она исчезает,
   звёзды возвращаются подсвеченными, подпись становится «Ваша оценка». */
.pb-ab__rate { position: relative; }
.pb-ab__rate--flash .pb-ab__rate-title,
.pb-ab__rate--flash .pb-ab__stars { visibility: hidden; }

.pb-ab__rate-thanks {
  position: absolute;
  inset: 0 var(--ab-pad) 0 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  font: 600 14px/1.25 var(--sans, Inter);
  color: var(--ab-ink);
  text-align: center;
  opacity: 0;
  transform: translateY(4px);
  transition: opacity .3s, transform .3s;
}
.pb-ab__rate-thanks.is-show { opacity: 1; transform: translateY(0); }
.pb-ab__rate-thanks-ico { flex: 0 0 auto; font-size: 18px; animation: pb-ab-heart 1.5s ease-in-out 1; }
.pb-ab__rate-thanks-text { flex: 0 1 auto; min-width: 0; }
@keyframes pb-ab-heart {
  0%, 100% { transform: scale(1); }
  30% { transform: scale(1.4); }
  60% { transform: scale(.95); }
}

.pb-ab__result {
  flex: 1 1 auto;
  min-width: 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--ab-gap-row);
  justify-items: center;
}

.pb-ab__summary {
  margin: 0;
  max-width: 100%;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 8px;
  font: 600 14px/1.3 var(--sans, Inter);
  color: var(--ab-ink);
}

.pb-ab__summary > span:last-child { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.pb-ab__ok {
  flex: 0 0 20px;
  display: inline-grid;
  place-items: center;
  width: 20px;
  height: 20px;
  border-radius: 99px;
  background: var(--ab-ok);
  color: #fff;
}

.pb-ab__actions { width: 100%; display: flex; align-items: center; gap: var(--ab-gap-row); }

.pb-ab__btn {
  flex: 1 1 0;
  min-width: 0;
  height: 76px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  /* Поле держит текст на расстоянии от рамки. 18px - то же боковое поле, что у колонки
     действия пакета; box-sizing:border-box, чтобы padding не прибавлялся к высоте 76. */
  padding: 0 18px;
  box-sizing: border-box;
  border: 1px solid rgba(46, 28, 18, .16);
  border-radius: 12px;
  background: var(--ab-surface);
  color: var(--ab-ink);
  font: 600 15px/1 var(--sans, Inter);
  white-space: nowrap;
  cursor: pointer;
}

.pb-ab__btn svg { flex: 0 0 18px; color: var(--ab-ink-2); }
/* Длинная подпись («Следующее оживление») переносится на вторую строку вместо обрезки
   (правка оператора 09.09). nowrap самой кнопки снимаем на подписи, а не на кнопке целиком:
   значок и текст должны остаться одной группой по центру. */
.pb-ab__btn > span { white-space: normal; line-height: 1.15; text-align: center; }
.pb-ab__btn:hover { border-color: rgba(184, 101, 42, .5); background: var(--bg); }
/* Наведение в доке Мастерской (правка оператора 09.09: «Скачать» не отвечал на курсор).
   pb-ws.css держит для #downloadBtn правила с id в селекторе
   (.pb-ws[data-ws-section][data-state="done"] #downloadBtn {background:#fff;border:…}),
   их вес выше класса с :hover, и отклик пакета не доходил. Селектор с id и лишним классом
   перевешивает; pb-ab.css подключён позже - при равном весе тоже победил бы, но равенство
   держать на порядке файлов хрупко. */
#wsRestorePanel .pb-ab .pb-ab__done .pb-ab__btn:hover,
#wsAnimDock .pb-ab .pb-ab__done .pb-ab__btn:hover,
#flDock .pb-ab .pb-ab__done .pb-ab__btn:hover { border-color: rgba(184, 101, 42, .5); background: var(--bg); }
#wsRestorePanel .pb-ab .pb-ab__done .pb-ab__btn--go:hover,
#wsAnimDock .pb-ab .pb-ab__done .pb-ab__btn--go:hover,
#flDock .pb-ab .pb-ab__done .pb-ab__btn--go:hover { background: var(--ab-accent-hover); border-color: var(--ab-accent-hover); }
/* Кнопки ряда «готово» (Восстановить, Оживить, Фотолаборатория) нигде не держат
   min-width:max-content. Пакет делит ряд поровну (flex 1 1 0, min-width 0): когда места
   не хватает, кнопки ужимаются вместо того чтобы выталкивать соседей за полосу. Длинная
   подпись при этом не режется - перенос на вторую строку берёт на себя .pb-ab__btn > span
   (white-space: normal чуть выше). Значки без подписей оператор отклонил, поэтому источник
   свободного места - именно перенос строки, а не сокращение содержимого. */

.pb-ab__btn--go { border: 0; background: var(--ab-accent); color: #fff; }
.pb-ab__btn--go svg { color: #fff; }
.pb-ab__btn--go:hover { background: var(--ab-accent-hover); border: 0; }

.pb-ab__ico-lock { display: none; }
.pb-ab__ico-open { display: inline-flex; }

/* М2 (принятый макет mockups/bonus-modules/m2-result-share.html): метка "+3" на кнопке
   "Поделиться" результата. Скрыта атрибутом hidden по умолчанию, снимает workshop.html
   по состоянию бонусной программы. Правило скопировано из mockups/bonus-modules/modules.css
   § М2 без изменений. */
.pb-ab__badge {
  display: inline-flex;
  align-items: center;
  height: 20px;
  padding: 0 6px;
  margin-left: 2px;
  border-radius: 5px;
  background: #e3f1e5;
  color: #1f6a36;
  font: 700 12px/1 var(--sans, Inter);
  white-space: nowrap;
}

/* гость: те же кнопки, но приглушены и с замком */
.pb-ab[data-ab-state="guest-done"] .pb-ab__btn[data-ab-act="share"],
.pb-ab[data-ab-state="guest-done"] .pb-ab__btn[data-ab-act="next"] {
  border: 1px solid rgba(46, 28, 18, .12);
  background: var(--bg);
  color: var(--ab-ink-2);
}

.pb-ab[data-ab-state="guest-done"] .pb-ab__btn[data-ab-act="next"] svg,
.pb-ab[data-ab-state="guest-done"] .pb-ab__btn[data-ab-act="share"] svg { color: var(--ab-ink-2); }

.pb-ab[data-ab-state="guest-done"] .pb-ab__btn[data-ab-act="share"] .pb-ab__ico-open,
.pb-ab[data-ab-state="guest-done"] .pb-ab__btn[data-ab-act="next"] .pb-ab__ico-open { display: none; }

.pb-ab[data-ab-state="guest-done"] .pb-ab__btn[data-ab-act="share"] .pb-ab__ico-lock,
.pb-ab[data-ab-state="guest-done"] .pb-ab__btn[data-ab-act="next"] .pb-ab__ico-lock { display: inline-flex; }

.pb-ab[data-ab-state="guest-done"] .pb-ab__btn[data-ab-act="share"]:hover,
.pb-ab[data-ab-state="guest-done"] .pb-ab__btn[data-ab-act="next"]:hover {
  border-color: rgba(184, 101, 42, .4);
  color: var(--ab-ink);
  background: var(--bg);
}

/* Значки на кнопках результата возвращены на всех ступенях (09.09, замечание оператора П5):
   раньше узкая ступень (data-ab-tier="narrow") прятала svg правилом display:none - оператор
   попросил значки вернуть. "Поделиться" на узкой ступени (98px клиентской ширины) со значком
   не помещалось (замер _diag-share-narrow-repro2.mjs на форсированной узкой ступени) - подпись
   укорочена до "Делюсь" (см. разметку в workshop.html), тот же приём, что и у guest-used: короче
   формулировка, не обрубок многоточием. */

/* ===== Стык с доком Мастерской (09.09) =====
   #wsRestorePanel (.pb-ws__panel) остаётся владельцем ВНЕШНЕЙ геометрии дока - ширина
   min(100%,900px), позиция в гриде рабочей зоны, min-height/max-height (assets/workshop/
   pb-ws.css, .pb-ws[data-ws-section] .pb-ws__panel) - эти правила НЕ трогаем. Сбрасываем
   только display:grid и его grid-template-columns/rows (те адресованы старым узлам вроде
   #modeToggleBtn/#wsDockRow3, которых внутри пакета больше нет) и padding (полосе пакета
   нужен свой --ab-pad изнутри, двойной отступ панели поверх нет). Без сброса .pb-ab встал
   бы в первую ячейку чужого грида шириной с карточку, а не растянулся на всю панель.
   ID-селектор - тот же приём, которым pb-ws.css само страхуется от инлайн-правил
   workshop.html (см. комментарий у .pb-ws[data-ws-section] .pb-ws__panel): вес ID выше
   любой комбинации классов и атрибутов независимо от порядка в документе. */
/* 09.09: хост выбирается по классу, а не по id - полоса пакета сажается в любой док
   Мастерской (.pb-ws__panel «Восстановить», «Оживить», далее «Фотолаборатория»). Вес
   (0,4,0) выше правила pb-ws.css для .pb-ws[data-ws-section] .pb-ws__panel (0,3,0). */
.pb-ws[data-ws-section] .pb-ws__panel:has(> .pb-ab) {
  display: block;
  padding: 0;
  /* Высоту дока задаёт полоса, не --pb-dock панели (09.09, отступ снизу): при полосе 114
     панель держала min-height 134, полоса прижималась к её верху, и до края окна оставалось
     42 вместо канонных 32 (замер _diag-animate-empty-frame). Равная специфичность с
     pb-ws.css ([data-state] .pb-ws__panel, 0-4-0) - побеждает порядком: этот файл позже. */
  min-height: 0;
}
.pb-ws__panel > .pb-ab { margin: 0; }
/* Высота полосы в доке - константа 134 (решение оператора 08.09 для «Восстановить»; 09.09
   распространено на «Оживить»: «та же высота, по центру, кнопка того же размера»). Пакет сам
   набирает высоту содержимым, поэтому минимум задаём здесь, у хоста. Рабочий вид тянется на
   всю высоту: колонка действия (align-self:stretch) растёт до 76 у кнопки, ряд управления
   центрируется своим align-items; у одного ряда 48 (Оживить) - 28px воздуха сверху и снизу.
   Вид «готово» остаётся по центру полосы (align-items:center корня). */
.pb-ws__panel > .pb-ab { min-height: 134px; }
.pb-ws__panel > .pb-ab > .pb-ab__work { align-self: stretch; }
/* Высота полосы у нас КОНСТАНТА 134 во всех девяти состояниях (решение оператора 08.09),
   тогда как пакет набирает её содержимым. У «Готово» наши звёзды и ряд кнопок дают 80 плюс
   поля = 110, и полоса проседала на 24px при переходе из рабочего состояния - то самое
   «прыгает под курсором», против чего высота и зафиксирована. Задаём числом; содержимое
   вида «Готово» центрируется по вертикали. */
#wsRestorePanel .pb-ab[data-ab-state="done"],
#wsRestorePanel .pb-ab[data-ab-state="guest-done"],
#wsAnimDock .pb-ab[data-ab-state="done"] { min-height: 134px; }
#wsRestorePanel .pb-ab[data-ab-state="done"] .pb-ab__done,
#wsRestorePanel .pb-ab[data-ab-state="guest-done"] .pb-ab__done,
#wsAnimDock .pb-ab[data-ab-state="done"] .pb-ab__done { align-self: center; }

/* ===== «Фотолаборатория» (09.09, тот же перенос, что «Оживить») =====
   Ширина полосы, поля, колонка действия 216, кнопка и мета - из пакета без изменений.
   Своё у раздела: ВОСЕМЬ операций не ложатся в тугл (ползунок считан на 2-3 сегмента) -
   меню становится плиткой 4×2 из тех же «карточек» тугла (значок / название / стоимость),
   выбранная плитка носит тот же белый «ползунок» (фон + тень), только неподвижный. Справа
   от плитки блок правок на её высоту с одной кнопкой «Заменить». Поле уточнения (чипы +
   строка) - второй ряд. Высота полосы своя, набирается содержимым (правка оператора 09.09:
   «канон ширины не меняем, высоту можно, чтобы влезало всё необходимое»). */
.pb-ab__row--top { align-items: stretch; }
/* Шесть операций (09.09: crop и hair ушли из плитки) - сетка 3×2, а не 4×2. Общая подложка
   (фон/рамка track) снята решением оператора («не подложечка, отдельными кнопками») - каждая
   плитка несёт свою рамку сама через .pb-ab__tile. */
.pb-ab__toggle--grid { flex: 1 1 auto; min-width: 0; height: auto; display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 4px; padding: 0; background: transparent; border: 0; }
.pb-ab__toggle--grid .pb-ab__knob { display: none; }
/* Обёртка платных плиток — носитель гейта data-fl-paid (CSS .fl-paid-off прячет весь div
   разом) И якорь для «?» (.fl-op-q, position:absolute). display:contents убирал бы обёртку из
   рендер-дерева — тогда «?» абсолютным позиционированием искал бы ближайшего предка с
   position ЧЕРЕЗ неё и попадал на #flOpGrid ОДИН на всех, а не на свою плитку (баг найден
   09.09 замером Playwright: клик по чужому «?» перехватывал клики соседних). Обёртка сама -
   grid-item на всю ячейку, плитка растягивается в ней на 100%. */
.fl-op-wrap { position: relative; display: block; }
.fl-op-wrap > .pb-ab__tile { width: 100%; height: 100%; }
/* 13.09: плитка сложена в строку. Иконка отдельным рядом стоила ~20px высоты на каждом из
   двух рядов сетки; в строке те же шесть режимов укладываются в 104px вместо 175px. */
.pb-ab__tile {
  position: relative;
  min-width: 0;
  min-height: var(--ab-tile-h);
  display: flex;
  flex-direction: row;
  align-items: center;
  /* От левого края, не по центру (правка оператора 13.09, вторая итерация): при центрировании
     положение значка зависело от длины подписи - «Одежда» короче остальных, и её значок стоял
     на 26,5px против 25 у соседей. Слева значки всех шести плиток встают на одну вертикаль. */
  justify-content: flex-start;
  gap: 7px;
  padding: 6px 8px;
  border: 0;
  border-radius: 9px;
  background: transparent;
  cursor: pointer;
  color: var(--ab-ink-2);
  font: 600 13.5px/1.15 var(--sans, Inter);
  text-align: center;
  transition: background 180ms ease, box-shadow 180ms ease, color 180ms ease;
}
.pb-ab__tile svg { flex: 0 0 20px; width: 20px; height: 20px; color: var(--ab-muted); }
/* Подпись переносится по словам: «Улучшить качество» и «Изменить возраст» в одну строку при
   ширине ячейки ~134px не встают, а сокращать их решением оператора нельзя. */
.pb-ab__tile .fl-op-txt { display: block; min-width: 0; text-align: left; }
.pb-ab__tile .pb-ab__note-sm { display: block; font: 400 12px/1.2 var(--sans, Inter); color: var(--ab-muted); }
.pb-ab__tile:hover { background: rgba(255, 255, 255, .55); }
/* Без общей подложки-track (снята решением оператора) каждая плитка сетки несёт свою рамку,
   иначе невыбранные растворяются на фоне полосы. */
.pb-ab__toggle--grid .pb-ab__tile { border: 1px solid var(--ab-line); background: var(--ab-surface); }
/* Поле под знак «?» - только СПРАВА, где он и висит. Симметричное ставилось под центрирование
   (13.09); после возврата к выравниванию слева оно лишь крадёт ширину подписи, а на тесной
   полосе её и так мало: плитка 94px при окне 900, из них 24 ушло бы впустую. */
.pb-ab__toggle--grid .pb-ab__tile { min-height: 0; padding-left: 10px; padding-right: 24px; }
.pb-ab__toggle--grid .pb-ab__tile:hover { background: var(--bg-alt); }
.pb-ab__tile.is-active { background: #fff; box-shadow: var(--shadow-1); color: var(--ab-ink); }
.pb-ab__tile.is-active svg { color: var(--ab-accent-soft); }
.pb-ab__tile.is-active .pb-ab__note-sm { color: var(--ab-ink-2); }
/* Фотолаборатория: «Заменить» и «Обрезать» стопкой (решение оператора 09.09 - «заменить
   сверху, обрезать снизу», не растянутая на высоту плитки кнопка). Каждая кнопка - высота
   ряда пакета (--ab-row), как .pb-ab__editbtn у эталона «Восстановить». */
/* 13.09: «Заменить»/«Обрезать» стоят столбиком СПРАВА ОТ СЕТКИ режимов, в том же ряду
   управления - как у эталона «Восстановить», где эти же кнопки живут в ряду, а не в колонке
   с главной кнопкой. Класс переименован из --stack в --beside: прежний подразумевал стопку
   у высокой сетки 175px, теперь это столбец у плоской сетки 104px.
   Почему НЕ под главной кнопкой (первая попытка 13.09): правая колонка у эталона держит ровно
   кнопку 76px и мету 20px = 104px, столько же, сколько сетка слева. Третий элемент распирал её
   до 142px, и полоса вставала в 172 вместо 134 - замер показал, что высоту держала не сетка,
   а колонка. В левой колонке ряд бесплатен: сетка и так занимает всю её высоту.
   Подписи прячутся САМИ, когда столбец узкий: container query по ширине блока, а не
   data-ab-tier - тир описывает всю полосу, а ужимается здесь именно этот столбец. */
.pb-ab__edit--beside {
  flex: 0 0 auto;
  min-width: 0;
  align-self: stretch;
  display: flex;
  flex-direction: column;
  /* От верха, а не по центру (13.09): ряды кнопок обязаны встать вровень с рядами сетки —
     при центрировании колонка висела в 827-875 против сетки 799-903 и решётка разъезжалась. */
  justify-content: flex-start;
  /* Обе кнопки одной ширины (по самой длинной подписи) — колонка читается ровным столбцом.
     Пустоту внутри убирает не ширина, а центрирование содержимого: justify-content:center
     у самой кнопки ставит группу «значок + подпись» посередине (правка оператора 13.09). */
  align-items: stretch;
  /* Зазор как между рядами сетки (4px), а не как между рядами полосы: два ряда кнопок
     обязаны совпасть с двумя рядами плиток, иначе решётка разъезжается по вертикали. */
  gap: 4px;
  border: 0;
  background: transparent;
}
.pb-ab__edit--beside .pb-ab__editbtn {
  /* 50px и радиус 9 — ровно ряд сетки режимов: две колонки читаются одной решёткой. */
  flex: 0 0 50px;
  min-width: 0;
  height: 50px;
  border: 1px solid var(--ab-line);
  border-radius: 9px;
  background: var(--ab-surface);
  /* Поля уже 14px: подпись «Заменить фото» идёт в две строки, и лишние поля забирали бы
     ширину у самого длинного слова. */
  padding: 0 10px;
  gap: 7px;
  font-size: 13.5px;
  /* По центру (правка оператора 13.09, откат выравнивания слева): прижатый к краю значок
     оставлял справа пустое поле 87px и читался как смещённый, а не как выровненный. */
  justify-content: center;
}
/* «Заменить фото» в две строки (правка оператора 13.09): в одну ей нужно 158px против 112
   в две — колонка раздулась бы почти вдвое и отобрала ширину у сетки режимов.
   Перенос по словам (в базовой кнопке стоит white-space:nowrap), высоты 50px хватает на две
   строки 15,5px с запасом. Потолок ширины обязателен: без него кнопка просто растягивается
   до одной строки и переносить становится нечего (замер 13.09 — стала 147px). */
/* Ширина колонки закреплена на прежних 122,6px (правка оператора 13.09: «остальные кнопки
   не двигаем»). Свободный max-width давал 112px, колонка отдавала 10px сетке, и плитка росла
   со 133,1 до 136,7 — сетка сдвигалась, хотя правка её не касалась. */
.pb-ab__edit--beside { flex: 0 0 122.6px; max-width: 122.6px; }
/* Свёрнутый вид на узкой ступени: 50px, как ряд сетки. Общее правило пакета (строка выше по
   файлу, .pb-ab[data-ab-tier="narrow"] .pb-ab__editbtn) даёт 46px и обслуживает «Восстановить»
   с «Оживить» — его не трогаем, перекрываем только для своего блока. */
.pb-ab[data-ab-tier="narrow"] .pb-ab__edit--beside .pb-ab__editbtn { flex: 0 0 50px; width: 50px; height: 50px; }
.pb-ab__edit--beside .pb-ab__editlabel { white-space: normal; text-align: left; line-height: 1.15; }
.pb-ab__edit--beside .pb-ab__editbtn:hover:not(:disabled) { background: var(--bg); border-color: var(--ab-accent-soft); }
.pb-ab__edit--beside .pb-ab__editbtn:disabled { opacity: .45; pointer-events: none; }
/* Измеряем РЯД, а не сам столбец: container-type на столбце обнулял его ширину (он берёт
   её от контейнера, а во flex-ряду с flex:0 0 auto взять неоткуда), запрос видел 0 и прятал
   подписи всегда — замер 13.09, besideW=0. Ряд ширину имеет всегда.
   Ограничено #flAb: container-type меняет правила размера для ВСЕХ потомков, а «Восстановить»
   и «Оживить» этой правки не просили и своих запросов не имеют. */
#flAb .pb-ab__row--top { container-type: inline-size; container-name: abrow; }
/* Порог 470px: ниже него подпись рядом с сеткой режимов уже не встаёт без срезки. */
@container abrow (max-width: 470px) {
  .pb-ab__edit--beside .pb-ab__editlabel { display: none; }
  .pb-ab__edit--beside .pb-ab__editbtn { padding: 0; width: 50px; justify-content: center; }
  /* Значок несёт всю кнопку целиком: подписи рядом больше нет. */
  .pb-ab__edit--beside .pb-ab__editbtn svg { flex: 0 0 20px; width: 20px; height: 20px; }
}
/* Запасной путь для браузеров без container queries: узкая ступень полосы прячет подписи. */
@supports not (container-type: inline-size) {
  .pb-ab[data-ab-tier="narrow"] .pb-ab__edit--beside .pb-ab__editlabel { display: none; }
  .pb-ab[data-ab-tier="narrow"] .pb-ab__edit--beside .pb-ab__editbtn { padding: 0; width: 50px; justify-content: center; }
  .pb-ab[data-ab-tier="narrow"] .pb-ab__edit--beside .pb-ab__editbtn svg { flex: 0 0 20px; width: 20px; height: 20px; }
}

/* Знак «?» на плитке режима + попап с примером до/после (перенос прод-механизма, регресс
   после канонизации на pb-ab; мокап 1253-1310). Якорь - .fl-op-wrap (position:relative, см.
   выше), не сама плитка: «?» и .pb-ab__tile - соседи внутри обёртки, оба дети .fl-op-wrap. */
.fl-op-q { position: absolute; top: 5px; right: 6px; z-index: 2; width: 17px; height: 17px; border-radius: 50%;
  border: 1.5px solid var(--ab-line); background: var(--ab-surface); color: var(--ab-muted);
  font: 700 10.5px/1 inherit; cursor: pointer; display: flex; align-items: center; justify-content: center; padding: 0;
  transition: color .15s, border-color .15s, background .15s; }
.pb-ab__tile.is-active ~ .fl-op-q { background: #fff; }
.fl-op-q:hover, .fl-op-q.is-open { border-color: var(--ab-accent-soft); color: var(--ab-accent-soft); background: var(--bg); }
.fl-op-q::after { content: ''; position: absolute; inset: -9px; }
@media (max-width: 900px) { .fl-op-q { width: 21px; height: 21px; font-size: 12px; } }

.fl-help-pop { position: fixed; z-index: 60; width: min(340px, calc(100vw - 32px)); padding: 12px;
  background: var(--bg); border: 1px solid var(--ab-line); border-radius: 14px;
  box-shadow: var(--shadow-3); text-align: center; }
.fl-help-pop[hidden] { display: none; }
.fl-help-imgs { display: grid; grid-template-columns: 1fr 1fr; border-radius: 10px; overflow: hidden; margin: 0 0 9px; }
.fl-help-imgs img { width: 100%; height: auto; max-height: 210px; object-fit: contain; display: block; background: var(--bg-alt); }
.fl-help-imgs img:first-child { border-right: 2px solid #fff; }
.fl-help-imgs img.fl-on-white { background: #fff; }
.fl-help-pop p { margin: 0; font-size: 13px; line-height: 1.45; color: var(--ab-ink-2); text-align: left; }
.fl-help-pop p b { display: block; margin-bottom: 3px; font-size: 14.5px; color: var(--ab-ink); }
.fl-help-note { display: block; margin-top: 6px; font-size: 11px; color: var(--ab-muted); }
@media (max-width: 640px) {
  .fl-help-pop { width: calc(100vw - 16px); padding: 10px; }
  .fl-help-imgs img { max-height: 34vh; }
}
.pb-ab__field--lab { flex-direction: column; align-items: stretch; gap: 6px; padding: 8px 14px; }
.pb-ab__field--lab .fl-chips { margin: 0; }
.pb-ab__field--lab .pb-ab__input { width: 100%; }
.pb-ab--free .pb-ab__go-cost svg { display: none; }
/* Гостю ценник на кнопке запуска не показываем совсем (правка оператора 13.09): слово
   «Бесплатно» занимало 85px справа и ужимало подпись операции — «Улучшить качество» вставало
   в две строки. В «Восстановить» этот блок у гостя тоже нулевой ширины, только там его
   прячет состояние guest-used; здесь попытка ещё не потрачена и полоса честно в ready,
   поэтому прячем по признаку гостя, а не по состоянию.
   Текст атрибута оставлен на месте: его читает пакет (ab.js), и пустое значение он подменяет
   единицей — то есть вместо «Бесплатно» на кнопке появлялась бы цифра 1.
   ⚠️ Сужено по атрибуту, а НЕ по классу pb-ab--free (находка ревью 13.09): класс висит на
   ЛЮБОМ нечисловом ценнике, а у закрытой гостю плитки «Убрать фон» ценник «После регистрации» —
   его прятать нельзя, иначе кнопка обещает обработку и приводит к окну входа. */
html[data-guest="1"] .pb-ab[data-ab-cost="Бесплатно"] .pb-ab__go-cost { display: none; }
.pb-ab__actions a.pb-ab__btn { text-decoration: none; }
/* Высота полосы «Фотолаборатории» задаётся содержимым: два ряда плиток 3×2 и поле
   уточнения. «Готово» держим не ниже 134, чтобы полоса не проседала при переходе. */
#flDock .pb-ab { min-height: 134px; }
/* Приёмка 09.09: кнопка и строка «время · баланс» - одна группа по вертикальному центру
   колонки, а не кнопка сверху и мета, прибитая к низу (между ними набегала дыра ~56px,
   строка отрывалась от кнопки). Ряд «Заменить/Обрезать» центрируется тем же приёмом. */
#flDock .pb-ab__cta { grid-template-rows: auto; align-content: center; gap: 9px; }
#flDock .pb-ab__go { align-self: center; }
#flDock .pb-ab__meta { height: auto; }
/* 13.09: stretch вместо center — колонка кнопок обязана занять ту же полосу по вертикали,
   что и сетка режимов, иначе её два ряда висят посередине против рядов сетки (замер: 827-875
   против 799-903) и решётка читается кривой. */
#flDock .pb-ab__edit--beside { align-self: stretch; }
#flDock .pb-ab[data-ab-state="done"] .pb-ab__done { align-self: center; }

/* Приёмка 09.09 (выбор оператора из шести вариантов - «белые плитки, цвет в иконке и рамке»):
   до правки плитки читались тускло - белый фон и серо-коричневая иконка растворялись в полосе.
   Цвет несут иконка и рамка, фон остаётся белым; выбранную держит двойная рамка (та же
   терракота, вторая линия внутрь). Заливку не трогаем: акцент остаётся за главной кнопкой. */
/* Сетка держит ту же высоту, что и вид уточнения на её месте (--ab-grid-h): переключение
   режима не должно двигать полосу ни вверх, ни вниз (приёмка 09.09). */
#flDock .pb-ab__toggle--grid { min-height: var(--ab-grid-h); grid-auto-rows: 1fr; }
/* Цвета плиток - ДОСЛОВНО прод-канон .fl-op (правка оператора 10.09: «цвета, наведение,
   действие - всё как на проде»). Прод красит плитки правилами .fl-op прямо в workshop.html;
   при переносе меню в полосу пакета 14 из 17 этих правил ушли, и вид разъехался: наведение
   было #fffaf4 вместо #fffdf8, выбранную держала внутренняя обводка вместо рамки с тенью,
   иконка невыбранной плитки светилась терракотой вместо приглушённой #8a7358.
   Числа сняты с живого прода (style.css/workshop.html, замер 10.09), не подобраны на глаз:
   фон #faf6ef, рамка 1.5px по --line, наведение #fffdf8 + терракотовая рамка, выбранная -
   белый фон, терракотовая рамка, жирный текст, тень 0 1px 4px rgba(28,20,16,.12). */
/* Фон числом, а не через --paper-2: в Мастерской этот токен разрешается в #f2ead8 (бумага
   секций), а прод-плитка сидит на #faf6ef - светлее на тон. Через токен плитка потемнела бы,
   сохранив «правильное» имя переменной (замер 10.09). */
#flDock .pb-ab__toggle--grid .pb-ab__tile {
  background: var(--bg);
  border: 1.5px solid var(--line, var(--paper-3));
  color: var(--ink-soft, #5c4e3f);
  transition: border-color .18s, background .18s, color .18s;
}
#flDock .pb-ab__toggle--grid .pb-ab__tile svg { color: #8a7358; }
#flDock .pb-ab__toggle--grid .pb-ab__tile:hover { background: var(--bg-alt); border-color: var(--terra, #b8652a); }
#flDock .pb-ab__toggle--grid .pb-ab__tile.is-active {
  background: var(--paper-0, #fff);
  border-color: var(--terra, #b8652a);
  color: var(--ink, #1c1410);
  font-weight: 700;
  box-shadow: var(--shadow-1);
}
#flDock .pb-ab__toggle--grid .pb-ab__tile.is-active svg { color: var(--terra, #b8652a); }
#flDock .pb-ab__tile.is-active ~ .fl-op-q { background: #fff; }

/* Приёмка 09.09: режим с уточнением (Изменить / Заменить фон / Одежда / Возраст) ЗАМЕНЯЕТ
   сетку плиток, а не достраивается вторым рядом под ней - иначе полоса росла на ~110px.
   Класс is-prompt ставит selectOp; сетка прячется, на её месте строка возврата с названием
   режима, чипы и поле. У режимов без уточнения (Улучшить качество, Убрать фон) вид не
   меняется - там менять нечего. */
/* Сетка уходит, «Заменить/Обрезать» остаются справа, а возврат и поле встают на освободившееся
   место СЛЕВА, в тот же ряд. Иначе (замер 09.09) полоса не сжималась: ряд держал высоту стопки
   кнопок 100px, а возврат 26px и поле 127px складывались под ним - 204.8 → 298.8px.
   .pb-ab__controls переводится в grid из двух колонок: левая - возврат над полем, правая -
   стопка кнопок на обе строки. Ряд --top при этом несёт только стопку. */
#flAb.is-prompt .pb-ab__toggle--grid { display: none; }
#flAb.is-prompt .pb-ab__controls {
  grid-template-columns: minmax(0, 1fr) auto;
  /* Три строки слева: возврат, лента чипов, плашка поля. Чипы вынесены из плашки решением
     оператора 09.09 - готовый вариант и свободный текст разделены, зазор между ними канонный
     (--ab-gap-row, тот же, что между рядами полосы). */
  /* Возврат и лента чипов - по содержимому, плашка поля забирает весь остаток колонки
     (правка оператора 09.09: «поле сразу должно быть растянуто»). max-content у первых двух
     обязателен: со строками auto они делят высоту наравне с полем и зазор между чипами и
     плашкой вырастает с канонных 8px до 19px (замер 09.09). */
  /* 13.09: одна строка вместо трёх - и возврат, и лента вариантов уехали над полосу,
     внутри осталось только поле ввода, оно и забирает всю высоту. */
  grid-template-rows: 1fr;
  align-items: start;
  column-gap: var(--ab-gap-col);
  /* Приёмка 09.09: высота полосы при выборе режима с промптом НЕ меняется - вид уточнения
     занимает ровно место сетки 3×2. Пол берём тот же, что и у сетки (--ab-grid-h): считать
     его из --ab-tile-h нельзя - плитки выше своего минимума, их высоту набирает подпись в
     две строки, и число поехало бы от любой правки текста. Растёт полоса только когда
     растёт textarea (syncInputBox, до пяти строк), поэтому ставим ПОЛ, не фиксацию. */
  /* 13.09: пол, но НЕ потолок. Жёсткая высота колонки сажала полосу в ровные 134px, но
     срезала поле ввода до 41px при содержимом 44 (замер: inputClipped=true) - текст в
     платном пути резать нельзя. Остаток 2,6px принят: полоса в режиме уточнения 136,6px
     против 134 в остальных состояниях, на глаз неотличимо. */
  min-height: var(--ab-grid-h);
  align-content: start;
}
#flAb.is-prompt .pb-ab__row--top { display: contents; }
/* Стопка «Заменить/Обрезать» стоит на ОДНОМ месте в обоих видах полосы и НЕ зависит от
   высоты поля (правка оператора 09.09: «в выбранном режиме уезжает», «эти кнопки не должны
   зависеть от размеров текстового поля»). Высоту ей задаём той же величиной, что и сетке
   (--ab-grid-h), а не строками грида: с align-content:start строки схлопываются к верху по
   содержимому, и center считался бы от них, а не от колонки - замер 09.09 давал уезд 17px. */
#flAb.is-prompt .pb-ab__edit--beside {
  grid-column: 2;
  grid-row: 1 / -1;
  align-self: start;
  height: var(--ab-grid-h);
  justify-content: center;
}
/* 13.09: ни возврата, ни ленты чипов в колонке больше нет - оба уехали в шапку над полосой
   (#flPromptHead). Прежние правила раскладывали их строками грида и держали полосу на 171px,
   потом на 137. */
/* Плашка занимает остаток колонки СРАЗУ (правка оператора 09.09), а textarea внутри неё
   тянется на всю её высоту. Высота поля задана колонкой, а не числом строк текста, поэтому
   длинный текст прокручивается ВНУТРИ поля и меню от него не растёт: скролл в самом поле
   разрешён, полос вокруг меню по-прежнему нет. */
#flAb.is-prompt #flInstr { grid-column: 1; grid-row: 1; align-self: stretch; min-height: 0; }
#flAb.is-prompt #flInstr .fl-input-wrap { flex: 1 1 auto; align-items: stretch; min-height: 0; }
#flAb.is-prompt #flInstr .pb-ab__input {
  height: 100%;
  max-height: none;
  padding-top: 12px;
  padding-bottom: 12px;
}
/* Лента вне плашки поля: собственного нижнего отступа у неё больше нет - строки разводит
   row-gap самой колонки, иначе зазор до плашки набегал бы дважды. */
#flAb.is-prompt #flChips { margin: 0; }
/* Шапка режима уточнения над полосой: возврат + лента вариантов в одну строку. */
#flPromptHead { display: none; }
#flDock.is-prompt-dock > #flPromptHead {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 0 0 6px;
  min-width: 0;
}
#flPromptHead > #flBack { flex: 0 0 auto; margin: 0; }
/* Экран результата (правка оператора 13.09): шапка уходит вместе с управлением полосы -
   на готовом фото нечего уточнять и некуда возвращаться. Класс is-prompt-dock на доке при
   этом остаётся: режим выбран и вернётся на «Следующее фото», поэтому решает состояние
   самой полосы, а не класс. */
#flDock:has(> #flAb[data-ab-state="done"]) > #flPromptHead { display: none; }
/* Лента забирает остаток строки; min-width:0 обязателен, иначе flex-элемент с прокруткой
   раздувается по содержимому и выталкивает возврат за край. */
#flPromptHead > .fl-chips { flex: 1 1 auto; min-width: 0; margin: 0; }

/* Подсказка о прокрутке ленты готовых вариантов (канон 13.09: затенение у края + обрез).
   Замер 13.09 показал, почему одного обреза мало: он случаен и зависит от длины таблетки -
   в режиме «Одежда» на 1200px крайняя была видна на 99% и читалась как конец списка, хотя за
   ней пряталось ещё два варианта.
   Затенение маской, а не наложенной плашкой: шапка стоит на бумаге переменного тона, и
   плашка с фоном выдала бы светлый прямоугольник поверх неё. Маска гасит саму ленту.
   Стороны зажигает JS (fl-chips--l / --r по факту прокрутки), поэтому у самого края ленты
   затенения нет - иначе оно врало бы, что там что-то есть. */
#flPromptHead > .fl-chips {
  --chips-fade: 36px;
  scroll-behavior: smooth;
}
#flPromptHead > .fl-chips.fl-chips--r {
  mask-image: linear-gradient(to right, #000 calc(100% - var(--chips-fade)), transparent 100%);
}
#flPromptHead > .fl-chips.fl-chips--l {
  mask-image: linear-gradient(to left, #000 calc(100% - var(--chips-fade)), transparent 100%);
}
#flPromptHead > .fl-chips.fl-chips--l.fl-chips--r {
  mask-image: linear-gradient(to right, transparent 0, #000 var(--chips-fade),
              #000 calc(100% - var(--chips-fade)), transparent 100%);
}
/* Правое поле = гарантированный обрез: без него последняя таблетка иногда вставала ровно по
   краю, и затенение ложилось на пустоту вместо неё (замер 13.09 - видно 99%). Поле снимается,
   когда прокручивать нечего, чтобы лента не висела со сдвигом на ровном месте. */
#flPromptHead > .fl-chips.fl-chips--r { padding-right: 18px; }
/* Мелкая моторика: у мыши нет жеста горизонтальной прокрутки, поэтому колесо над лентой
   крутит её вбок (обработчик в workshop.html). Тачпад и палец работают штатно. */

#flBack { display: none; }
/* Класс на доке, а не на полосе: шапка стоит НАД .pb-ab и снаружи неё (13.09).
   Ставит его selectOp тем же тиком, что и .is-prompt на самой полосе. */
#flDock.is-prompt-dock #flBack { display: inline-flex; }
#flBack {
  align-items: center;
  gap: 7px;
  /* Без align-self:flex-start (13.09): он остался от раскладки внутри полосы, а в шапке
     прижимал кнопку к верху — её центр стоял на 5,2px выше центра чипов (замер 13.09).
     Выравнивание отдаём шапке (#flPromptHead, align-items:center). */
  /* Зазор до полосы: кнопка сидит над её верхней границей. */
  margin: 0 0 6px;
  padding: 4px 10px 4px 6px;
  border: 1px solid transparent;
  border-radius: 999px;
  background: transparent;
  color: var(--ab-ink-2);
  font: 650 13.5px/1.2 var(--sans, Inter);
  cursor: pointer;
  transition: background .15s, border-color .15s;
}
#flBack:hover { background: var(--bg); border-color: rgba(184, 101, 42, .3); }
#flBack svg { flex: 0 0 15px; width: 15px; height: 15px; color: var(--ab-accent-soft); }
#flBack b { font-weight: 650; color: var(--ab-ink); }
