/* ============================================================
   Кнопки, поля, ссылки.
   Всё держится на токенах: лицо сменилось с тёмного на светлое 01.10.2026,
   и ни одно правило отсюда править не пришлось.
   ============================================================ */

a {
  color: var(--c-accent-text);
  text-underline-offset: 0.18em;
}

/* Видимая рамка фокуса обязательна: по сайту ходят и с клавиатуры, а
   браузерная рамка по умолчанию теряется и на тёмном фоне, и на светлом. */
:where(a, button, input, select, textarea, summary):focus-visible {
  outline: 2px solid var(--c-focus);
  outline-offset: 2px;
}

/* ---------- поля ---------- */

input, textarea, select, button { font: inherit; }

:where(input:not([type="submit"]):not([type="button"]), textarea, select) {
  background: var(--c-surface);
  color: var(--c-text);
  border: var(--border);
  border-radius: var(--r-sm);
  padding: var(--s-3) var(--s-4);
}

::placeholder { color: var(--c-muted); opacity: 1; }

/* ---------- кнопки ---------- */

:where(button, input[type="submit"], input[type="button"], .button) {
  background: var(--c-accent);
  color: var(--c-accent-in);
  border: 0;
  border-radius: var(--r-md);
  padding: var(--s-3) var(--s-5);
  font-weight: var(--fw-semi);
  cursor: pointer;
  transition: filter var(--dur-fast) var(--ease);
}

/*
 * Наведение **затемняет**, а не осветляет.
 *
 * На ночном лице кнопка светлела и этим выступала из тёмного фона. На белой
 * бумаге осветлённая терракота выцветает: кнопка под курсором выглядит менее
 * нажимаемой, чем до наведения, — ровно наоборот тому, что должна говорить.
 */
:where(button, input[type="submit"], input[type="button"], .button):hover {
  filter: brightness(0.92);
}

/* ============================================================
   Окно загрузки
   ============================================================ */

.op-lede { max-width: 54ch; margin-block-end: var(--s-7); }

.op-eyebrow {
  font-family: var(--f-mono);
  font-size: var(--t-xs);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--c-muted);
  margin: 0 0 var(--s-4);
}

/*
 * Отступ под заголовком больше, чем над ним.
 *
 * Было одинаково: 16 пикселей после надзаголовка в 12 пикселей и столько же
 * после заголовка в 56. Один зазор для крошечной строки и для огромной —
 * отсюда ощущение, что текст слипся. Воздух обязан считаться от роста того,
 * за чем он идёт.
 *
 * Надзаголовок остаётся прижатым: он подпись к заголовку, а не отдельная
 * мысль, и отодвинуть его значило бы разорвать пару.
 */
.op-lede__title { font-size: var(--t-hero); margin: 0 0 var(--s-5); }

.op-lede__sub {
  margin: 0;
  color: var(--c-muted);
  font-size: var(--t-lg);
  max-width: var(--w-text);
}

/* ---------- зона броска ---------- */

/*
 * Окно броска — самое крупное пятно на экране кабинета, и оно должно
 * приглашать, а не ждать.
 *
 * Штрих заметнее обычного — 34 %, а не 22 %. Так заведено ещё на кремовом
 * лице, где волосяная линия читалась случайной складкой, а не «сюда»; на
 * нынешней белой бумаге причина та же, поэтому доля и осталась.
 */
.op-drop {
  display: flex;
  align-items: center;
  gap: var(--s-4);
  padding: var(--s-7) var(--s-6);
  background: var(--c-surface);
  border: 2px dashed color-mix(in srgb, var(--c-edge) 34%, transparent);
  border-radius: var(--r-xl);
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease);
}

.op-drop:hover,
.op-drop.is-over {
  background: var(--c-raised);
  border-color: var(--c-accent);
}

/* Поле спрятано от глаза, но не от клавиатуры и не от чтеца: display:none
   выкинул бы его из порядка обхода, и зона перестала бы открываться с
   клавиатуры вовсе. */
.op-drop__input {
  position: absolute;
  inline-size: 1px;
  block-size: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.op-drop:focus-within {
  outline: 2px solid var(--c-focus);
  outline-offset: 2px;
}

.op-drop.is-over {
  border-color: var(--c-accent-text);
  border-style: solid;
  background: var(--c-hover);
}

.op-drop__mark {
  inline-size: 0.875rem;
  block-size: 0.875rem;
  background: var(--c-accent);
  flex: none;
}

.op-drop__text { display: flex; flex-direction: column; gap: var(--s-1); }
.op-drop__text b { font-weight: var(--fw-semi); }

.op-drop__hint {
  color: var(--c-muted);
  font-size: var(--t-sm);
}

.op-drop__cta {
  margin-inline-start: auto;
  background: var(--c-accent);
  color: var(--c-accent-in);
  padding: var(--s-3) var(--s-5);
  font-weight: var(--fw-semi);
  font-size: var(--t-sm);
  white-space: nowrap;
}

/* ---------- что вышло ---------- */

.op-outcome { margin-block-start: var(--s-6); }

.op-working {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  margin: 0;
  color: var(--c-muted);
  font-family: var(--f-mono);
  font-size: var(--t-sm);
}

.op-working__spin {
  inline-size: 0.75rem;
  block-size: 0.75rem;
  border: 2px solid var(--c-line);
  border-top-color: var(--c-accent-text);
  border-radius: var(--r-dot);
  animation: op-spin 700ms linear infinite;
  flex: none;
}

@keyframes op-spin { to{ transform: rotate(360deg); } }

@media (prefers-reduced-motion: reduce) {
  .op-working__spin { animation: none; border-top-color: var(--c-accent-text); }
}

/*
 * Отступ снизу — не украшение. Без него сообщение об ошибке прилипает к
 * первому полю формы, и «That email and password do not match» читается как
 * подпись к полю «Your email». Так было на странице регистрации с самого
 * начала, просто ошибку там до сих пор никто не видел.
 */
.op-error {
  margin: 0 0 var(--s-5);
  padding: var(--s-4) var(--s-5);
  background: var(--c-surface);
  border-inline-start: 3px solid var(--c-error);
  color: var(--c-text);
  max-width: var(--w-text);
}

.op-ready {
  display: grid;
  grid-template-columns: minmax(0, 300px) minmax(0, 1fr);
  gap: var(--s-6);
  align-items: start;
}

@media (max-width: 720px) {
  .op-ready { grid-template-columns: 1fr; }
}

.op-ready__shot { margin: 0; background: var(--c-sunk); border: var(--border); }
.op-ready__shot img { display: block; inline-size: 100%; block-size: auto; }

.op-ready__title { font-size: var(--t-xl); margin: 0 0 var(--s-4); }

.op-facts {
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr);
  gap: var(--s-2) var(--s-5);
  margin: 0 0 var(--s-5);
  font-size: var(--t-sm);
}

.op-facts dt {
  font-family: var(--f-mono);
  font-size: var(--t-xs);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--c-muted);
  align-self: center;
}

.op-facts dd {
  margin: 0;
  font-variant-numeric: tabular-nums;
}

.op-note {
  margin: 0 0 var(--s-5);
  color: var(--c-ok);
  font-size: var(--t-sm);
  max-width: 46ch;
}

.op-actions { margin: 0; }

/* ---------- кнопка ---------- */

/* Отдельным классом, а не только через :where(button).
   У :where() специфичность ноль, и правило родительской темы его перебивает —
   кнопка выходила серой, хотя цвет был задан. Наши кнопки носят этот класс. */
.op-button {
  display: inline-block;
  background: var(--c-accent);
  color: var(--c-accent-in);
  border: 0;
  border-radius: var(--r-md);
  padding: var(--s-3) var(--s-5);
  font-family: var(--f-body);
  font-weight: var(--fw-semi);
  font-size: var(--t-sm);
  line-height: 1.2;
  cursor: pointer;
  text-decoration: none;
  transition: filter var(--dur-fast) var(--ease);
}

/* Затемнение и ещё немного — по той же причине, что выше. */
.op-button:hover { filter: brightness(0.92); }
.op-button:active { filter: brightness(0.84); }

/* На телефоне зона броска складывается столбиком: в строку она не влезает —
   заголовок рвётся посреди слова, а кнопка отжимает подсказку в узкую колонку.
   Телефон здесь не запасной случай, а основной: товар снимают им же. */
@media (max-width: 560px) {
  .op-drop {
    flex-direction: column;
    align-items: stretch;
    gap: var(--s-4);
    padding: var(--s-5);
  }

  .op-drop__mark { align-self: flex-start; }

  .op-drop__cta {
    margin-inline-start: 0;
    text-align: center;
    padding-block: var(--s-4);
  }
}

/* ---------- ход работы ---------- */

.op-running { margin-block-start: var(--s-6); max-width: var(--w-text); }

/*
 * Жёлоб шалфейный, а не белый.
 *
 * На кремовой бумаге белая полоса с волосяной рамкой почти не видна, и
 * человек не понимает, что вообще есть полоса: он видит только зелёный
 * обрубок слева и принимает его за черту.
 */
.op-bar {
  display: block;
  block-size: 8px;
  background: var(--c-raised);
  border: 0;
  border-radius: 999px;
  overflow: hidden;
  margin-block: var(--s-4) var(--s-3);
}

.op-bar__fill {
  display: block;
  block-size: 100%;
  inline-size: 0;
  background: var(--c-accent);
  transition: inline-size var(--dur-base) var(--ease);
}

.op-running__stage {
  margin: 0;
  font-family: var(--f-mono);
  font-size: var(--t-sm);
  color: var(--c-muted);
}

/* ---------- готовый комплект ---------- */

.op-set { margin-block-start: var(--s-6); }

.op-tiles {
  list-style: none;
  margin: 0 0 var(--s-6);
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
  gap: var(--s-5);
  /* Каждая карточка своей высоты. Растянутые до общей, они оставляют
     пустоту под широким баннером — а разная высота здесь правда: комплект и
     состоит из разных форм. */
  align-items: start;
}

.op-tile {
  display: flex;
  flex-direction: column;
  gap: var(--s-3);
  background: var(--c-surface);
  border: var(--border);
  padding: var(--s-4);
}

/* Плитки разной формы, и это правда о содержимом: комплект и состоит из
   разных форм. Ровная сетка квадратов сказала бы неправду. */
.op-tile img {
  inline-size: 100%;
  block-size: auto;
  display: block;
  background: var(--c-sunk);
}

.op-tile__cap {
  font-family: var(--f-mono);
  font-size: var(--t-xs);
  letter-spacing: 0.04em;
  color: var(--c-muted);
  line-height: 1.5;
  font-variant-numeric: tabular-nums;
}

.op-tile__cap b {
  display: block;
  color: var(--c-text);
  font-weight: var(--fw-medium);
  letter-spacing: 0;
}

.op-tile__link {
  margin-block-start: auto;
  align-self: flex-start;
  font-family: var(--f-body);
  font-size: var(--t-sm);
  font-weight: var(--fw-semi);
  color: var(--c-accent-text);
  text-decoration: none;
  border-block-end: 1px solid currentColor;
}

/* Второстепенное действие рядом с главным: не кнопка, чтобы не спорить с ней. */
.op-plain {
  background: none;
  border: 0;
  color: var(--c-muted);
  font-family: var(--f-body);
  font-size: var(--t-sm);
  cursor: pointer;
  padding: var(--s-3) var(--s-2);
  margin-inline-start: var(--s-3);
  text-decoration: underline;
  text-underline-offset: 0.2em;
}

.op-plain:hover { color: var(--c-text); }

/* ---------- подхват брошенной работы и кошелёк ---------- */

.op-resume {
  margin-block-end: var(--s-5);
  padding: var(--s-5);
  background: var(--c-surface);
  border: var(--border);
  border-inline-start: 3px solid var(--c-accent);
  max-width: var(--w-text);
}

.op-resume__text { margin: 0 0 var(--s-4); }

.op-purse {
  margin: 0 0 var(--s-4);
  font-family: var(--f-mono);
  font-size: var(--t-xs);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--c-muted);
  font-variant-numeric: tabular-nums;
}

.op-error__text { margin: 0; }
.op-error .op-actions { margin-block-start: var(--s-4); }

/* ---------- форма ---------- */

.op-form { max-width: var(--w-form); margin-block-end: var(--s-6); }

.op-field {
  display: flex;
  flex-direction: column;
  gap: var(--s-2);
  margin: 0 0 var(--s-5);
}

.op-label {
  font-family: var(--f-mono);
  font-size: var(--t-xs);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--c-muted);
}

.op-input {
  inline-size: 100%;
  background: var(--c-surface);
  color: var(--c-text);
  border: var(--border);
  border-radius: var(--r-sm);
  padding: var(--s-3) var(--s-4);
  font-size: var(--t-base);
}

.op-hint { font-size: var(--t-sm); color: var(--c-muted); }

/* ---------- строки ставок на странице цен ---------- */

/*
 * Та же ведомость, что и везде, но вместо буквы-ключа — число.
 *
 * Число набрано крупно и в одной ширине для всех строк: взгляд по странице цен
 * идёт по числам, и если они разной ширины, столбец съезжает. `tabular-nums`
 * здесь не роскошь — без него «10» и «20» встают по-разному.
 */
.op-rates .op-spec__row { align-items: baseline; }

.op-rate__n {
  font-size: var(--t-xl);
  font-weight: var(--fw-semi);
  line-height: 1;
  color: var(--c-accent-text);
  font-variant-numeric: tabular-nums;
  min-inline-size: 2.5ch;
  text-align: end;
  flex: none;
}

/* Слово «tokens» под числом — подпись к нему, а не следующая мысль. */
.op-rates .op-spec__value {
  font-family: var(--f-mono);
  font-size: var(--t-xs);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--c-muted);
}

/* ---------- дверь: вход, регистрация, смена пароля ---------- */

/*
 * Колонка по центру и форма в панели.
 *
 * Было: полоса 1200, форма 520 прижата влево на отметке 124, справа 680
 * пикселей пустоты — измерено на /signin/ в окне 1400. Страница читалась
 * недоделанной: не «дверь», а форма, забытая в углу листа.
 *
 * Правила названы `op-gate` и вешаются классом на `<main>`, а не на `.op-form`.
 * Причина записана кровью этого проекта: правило, названное по виду, уже
 * трижды утекало на страницы, которым не предназначалось — последний раз
 * `.op-lede { margin: 0 }` из home.css, из-за чего подзаголовок здесь упирался
 * в первое поле. Форма живёт ещё на обратной связи и на загрузке; им эта
 * колонка не нужна, и они её не получат.
 */

.op-gate__col {
  max-inline-size: 30rem;
  margin-inline: auto;
}

/* Шапка по центру, форма внутри панели — слева.
   Подписи полей выключены по левому краю всегда: читать форму по центру
   нельзя, глаз теряет начало строки. */
.op-gate__say { text-align: center; margin-inline: auto; }
.op-gate__say .op-meta { justify-content: center; }
.op-gate__say .op-lede__sub { margin-inline: auto; }

.op-gate__panel {
  background: var(--c-surface);
  border: var(--border);
  border-radius: var(--r-lg);
  padding: var(--s-6);
}

/* Форма занимает панель целиком: 520 из токена здесь шире самой панели,
   и поля обрезались бы по её краю. */
.op-gate__panel .op-form { max-width: 100%; margin-block-end: 0; }

/* Кнопка во всю ширину панели. У страницы ровно одно дело, и нет причины
   делать главное действие меньше полей, которые к нему ведут. */
.op-button--block { display: block; inline-size: 100%; }

/*
 * Сообщение в панели вместо формы: «письмо ушло», «регистрация закрыта».
 *
 * Панель остаётся на том же месте во всех видах страницы, иначе объект
 * прыгает по странице в зависимости от состояния. Текст по центру, как
 * шапка: действовать в нём не над чем, и выключка по левому краю обещала бы
 * строку подписи, которой нет.
 */
.op-gate__said {
  margin: 0;
  text-align: center;
  color: var(--c-text);
  line-height: 1.6;
}

/* Забытый пароль — внутри панели, но отделён чертой: он про ту же дверь. */
.op-gate__aside {
  margin: var(--s-5) 0 0;
  padding-block-start: var(--s-5);
  border-block-start: var(--border);
  text-align: center;
  font-size: var(--t-sm);
}

/* Заведение счёта — под панелью: другое дело, другой человек. */
.op-gate__foot {
  margin: var(--s-6) 0 0;
  text-align: center;
  color: var(--c-muted);
  font-size: var(--t-sm);
}

@media (max-width: 30rem) {
  /*
   * На телефоне у панели снимаются только боковые края. Рамка шириной почти
   * во весь экран перестаёт что-либо выделять — она просто повторяет края
   * окна. Остаются верхняя и нижняя черты, и форма оказывается в полосе
   * между ними: это тот же язык ведомости, которым набрана вся остальная
   * часть сайта. Проверено на 375 — горизонтальной прокрутки нет.
   */
  .op-gate__panel {
    border-inline: 0;
    border-radius: 0;
    padding-inline: 0;
    background: none;
  }
}

/* ---------- форма обратной связи ---------- */

/*
 * Форма на всю ширину полосы, а не в узкую колонку: у неё есть поле, в которое
 * пишут абзацами, и в колонке шириной с поле ввода оно выглядит как щель.
 */
.op-form--wide { max-width: min(46rem, 100%); }

/*
 * Имя и почта рядом, когда есть место. Оба короткие, и в столбик они растягивают
 * форму вдвое без всякой пользы. Ниже 40rem — обратно в столбик: два поля в ряд
 * на телефоне становятся двумя щелями.
 */
.op-form__row { display: grid; gap: 0 var(--s-4); }

@media (min-width: 40rem) {
  .op-form__row { grid-template-columns: 1fr 1fr; }
}

.op-textarea {
  min-block-size: 9lh;
  resize: vertical;
  line-height: 1.6;
}

.op-label__soft { color: var(--c-muted); font-weight: var(--fw-reg); }

/*
 * Поле-приманка.
 *
 * Не `display: none` и не `hidden`: часть рассылающих машин именно так его и
 * ищет — заполняют то, что спрятано самым очевидным способом. Здесь поле
 * уносится за край окна, то есть остаётся настоящим полем, просто его не видно.
 *
 * `left: -9999px` вместо `translate`: смещение трансформацией не выносит
 * элемент из потока, и страница получает горизонтальную прокрутку.
 */
.op-trap {
  position: absolute;
  inset-inline-start: -9999px;
  inline-size: 1px;
  block-size: 1px;
  overflow: hidden;
}

.op-rule {
  border: 0;
  border-block-start: var(--border);
  margin-block: var(--s-6);
}

/*
 * Строка с галочкой.
 *
 * Не `.op-field`: там подпись стоит над полем, а у галочки она обязана стоять
 * рядом — иначе непонятно, к чему галочка относится, когда их в форме две.
 *
 * `align-items: start` вместо `center`: подпись из двух строк на узком экране
 * иначе съезжает относительно квадратика, и строка выглядит сломанной.
 */
.op-check {
  display: flex;
  align-items: start;
  gap: var(--s-3);
  margin: 0 0 var(--s-5);
}

.op-check__box {
  /* Размер задаётся явно: по умолчанию браузеры рисуют галочку кеглем
     наследуемого шрифта, и в форме с крупным вводом она выходит крошечной. */
  inline-size: 1.15rem;
  block-size: 1.15rem;
  margin-block-start: 0.15rem;
  flex: none;
  accent-color: var(--c-accent);
}

.op-check__label {
  font-size: var(--t-sm);
  color: var(--c-muted);
  cursor: pointer;
}

.op-note-plain {
  color: var(--c-muted);
  font-size: var(--t-sm);
  max-width: var(--w-text);
}

/* Сообщение о подтверждении — на видном месте, но не тревожное. */
.op-said {
  margin-block-end: var(--s-5);
  padding: var(--s-4) var(--s-5);
  background: var(--c-surface);
  border: var(--border);
  border-inline-start: 3px solid var(--c-ok);
  max-width: var(--w-text);
}

/*
 * Между абзацами внутри рамки нужен просвет, а после последнего — нет.
 *
 * Раньше стоял ноль на все: рамка задумывалась однострочной. С двумя абзацами
 * они слипаются в сплошной кусок, и второй читается как продолжение первого
 * предложения.
 */
.op-said p { margin: 0 0 var(--s-3); }
.op-said p:last-child { margin-block-end: 0; }
.op-said.is-warn { border-inline-start-color: var(--c-error); }

.op-history__state.is-bad { color: var(--c-error); }

/* ---------- цены ---------- */

.op-prices {
  inline-size: 100%;
  border-collapse: collapse;
  background: var(--c-surface);
  min-inline-size: 560px;
}

.op-prices th,
.op-prices td {
  text-align: start;
  padding: var(--s-4) var(--s-5);
  border-block-end: var(--border);
  vertical-align: baseline;
}

.op-prices thead th {
  font-family: var(--f-mono);
  font-size: var(--t-xs);
  font-weight: var(--fw-semi);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--c-muted);
  background: var(--c-sunk);
}

.op-prices tbody th {
  font-family: var(--f-display);
  font-weight: var(--fw-display);
  font-stretch: var(--fs-display-stretch);
  font-size: var(--t-lg);
  white-space: nowrap;
}

.op-prices tbody tr:last-child th,
.op-prices tbody tr:last-child td { border-block-end: 0; }

.op-prices .num {
  font-family: var(--f-mono);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/*
 * Скидка — терракотой, а не зелёным «готово».
 *
 * Зелёный был единственным на всей странице: ни в палитре, ни в токенах лица
 * его нет, он взят из состояний («готово» в полях кабинета). На тарифах он
 * читался чужой краской, а внутри выделенной строки до 02.10.2026 ещё и не
 * читался вовсе — 1,57 : 1.
 *
 * `--c-accent-text` полосой подменяется сам, поэтому отдельного правила для
 * выделенной строки больше не нужно. Зелёный остаётся там, где он значит
 * состояние: подтверждения в кабинете.
 */
.op-off {
  display: inline-block;
  margin-inline-start: var(--s-2);
  color: var(--c-accent-text);
  font-size: var(--t-xs);
}

.scroller { overflow-x: auto; border: var(--border); margin-block-end: var(--s-5); }

/* ---------- юридические документы ---------- */

/*
 * Правовые страницы идут во всю ширину полосы, а не колонкой слева.
 *
 * Раньше стояло ограничение в 65 знаков — читаемая длина строки. Владелец
 * попросил шире: столбик в левой части при пустой правой половине выглядит
 * недоделкой. Здесь текст занимает всю ширину, которую даёт оболочка.
 *
 * Плата известна и названа: на широком экране строка выходит длинной, и взгляд
 * тяжелее находит начало следующей. Поэтому вместе с шириной чуть увеличены
 * межстрочный просвет и отступ между абзацами — на длинной строке они и держат
 * глаз на месте.
 */
.op-legal { max-width: none; }

.op-legal__body {
  line-height: 1.75;
  max-inline-size: none;
}

.op-legal__body > p { max-inline-size: none; }

.op-legal__head { margin-block-end: var(--s-6); }

.op-legal__title {
  font-size: var(--t-3xl);
  margin: var(--s-3) 0 0;
}

.op-legal__lede {
  font-size: var(--t-lg);
  color: var(--c-muted);
  margin-block-end: var(--s-6);
}

.op-legal__body h2 {
  font-size: var(--t-xl);
  margin-block: var(--s-7) var(--s-4);
}

.op-legal__body p { margin-block: 0 var(--s-5); }

.op-legal__body ul {
  margin-block: 0 var(--s-4);
  padding-inline-start: var(--s-5);
}

.op-legal__body li { margin-block-end: var(--s-3); }

.op-legal__body b { color: var(--c-text); font-weight: var(--fw-semi); }

/*
 * Абзац, который сам признаётся, что не дописан.
 *
 * Отмечен видимо и нарочно: правовая страница, где одно место ещё не улажено,
 * обязана показывать это место, а не прятать среди ровного текста. Читатель,
 * наткнувшийся на такой абзац, узнаёт ровно то, что есть.
 *
 * Цвет не красный: это не ошибка и не опасность, а честная незавершённость.
 */
.op-legal__body .op-legal__flag {
  padding: var(--s-4) var(--s-5);
  border-inline-start: 3px solid var(--c-accent);
  border-radius: var(--r-md);
  background: var(--c-surface);
  color: var(--c-text);
}

/*
 * Место, которое заполнит форма настроек.
 *
 * Пунктиром и словом, что именно сюда встанет. Видно только владельцу и только
 * пока реквизиты пусты; как только они заполнены, на этом месте стоит обычный
 * текст и никакой пометки не остаётся.
 */
.op-blank {
  font-family: var(--f-mono);
  font-size: 0.85em;
  color: var(--c-accent-text);
  border: 1px dashed var(--c-accent);
  border-radius: var(--r-sm);
  padding: 0.05em 0.4em;
  white-space: nowrap;
}

.op-legal__foot {
  margin-block-start: var(--s-8);
  padding-block-start: var(--s-5);
  border-block-start: var(--border);
  color: var(--c-muted);
  font-size: var(--t-sm);
}

.op-legal__foot p { margin: 0; }


/* ---------- «кому это нужно» ---------- */

/*
 * Карточки со случаями, для кого сервис. Не решётка из ячеек, как были
 * шаги на первой странице: там четыре шага одного вида, здесь пять разных
 * положений, и каждое должно читаться отдельным.
 *
 * Три в ряд при 1200: пятое и шестое место остаются пустыми, и последние две
 * карточки стоят парой, а не сиротой в конце длинного ряда.
 */
/* ---------- «кому это нужно» ----------
 *
 * Здесь были карточки в три ряда со своими нарисованными значками. Значки
 * рисовали магазин, гончарный круг и коробку — язык прошлого товара, — а
 * карточки были той самой сеткой, по которой сайт узнавался как Shelfready.
 *
 * Теперь это строки листа, те же, что на главной: буква, кто он, и что у него
 * за обстоятельство. Стили лежат в `home.css` под `.op-spec__row` — здесь
 * ничего своего не осталось, и это правильно: одна разметка, один вид.
 * Удалено 30.09.2026.
 */

/* ============================================================
   Страница «о нас» — по центру.

   Так попросил владелец, и просьба подтверждена дважды: сперва
   для прошлого лица, потом снова 30.09.2026, когда я центровку
   снял, а владелец вернула. Довод её прежний и верный: это не
   рабочий экран, где глаз идёт по левому полю сверху вниз, а
   текст, который читают подряд.

   Центрируется **всё**, включая мета-строку листа. Снимая
   центровку, я ссылался именно на неё: заголовок посреди
   подписи, растянутой от края до края, выглядел свёрстанным
   отдельно. Это была верная жалоба на смесь, а не на центровку —
   стоило довести центровку до конца, а не отменять.

   Сами абзацы остаются с ровным левым краем: выключка по центру
   на длинном тексте заставляет заново искать начало каждой
   строки.
   ============================================================ */

.op-about { text-align: center; }

/*
 * Мета-строка собирается в одну середину.
 *
 * `--c-meta-rest` не уезжает вправо, а идёт следом за номером листа через
 * ту же точку-разделитель: справа ему тут не за что держаться.
 */
.op-about .op-meta { justify-content: center; }
.op-about .op-meta__rest { margin-inline-start: 0; }

.op-about .op-meta__rest::before {
  content: '·';
  margin-inline-end: var(--s-3);
  opacity: 0.55;
}

.op-about .op-title,
.op-about .op-lede {
  margin-inline: auto;
}

.op-about .op-title { max-inline-size: 20ch; }

/*
 * Абзацы центрируются вместе со всем остальным — решено владельцем 02.10.2026.
 *
 * Прежде они были единственным, что оставалось по левому краю: мета-строка и
 * заголовок стояли по центру, а текст под ними шёл во всю ширину от левого
 * поля. Рядом это читалось не как замысел, а как недоделка.
 *
 * Мера сужена с 78 знаков до 62. Это не вкусовщина: у центрированного абзаца
 * рваны оба края, и чем строка длиннее, тем труднее глазу найти начало
 * следующей. Шестьдесят два — то, на чём рваность ещё не мешает.
 */
.op-about .op-prose {
  margin-inline: auto;
  max-inline-size: 62ch;
  text-align: center;
  font-size: 1.0625rem;
}

.op-about .op-prose p { line-height: 1.75; }
.op-about .op-prose p + p { margin-block-start: var(--s-5); }

/*
 * Перечень «кому это нужно» и «кому не подойдёт» читаются, а не
 * разглядываются: **внутри** строк левый край.
 *
 * Сами блоки при этом стоят посередине, как и всё на этой странице: у строки
 * ведомости есть буква слева и три строки текста за ней, и центрировать их
 * значило бы сломать саму ведомость. Центрируется блок, а не его содержимое.
 */
.op-about .op-who {
  margin-block: var(--s-6) 0;
  margin-inline: auto;
  max-inline-size: 54rem;
  text-align: start;
}

.op-about .op-halves {
  margin-inline: auto;
  max-inline-size: 60rem;
}

.op-about .op-half { text-align: start; }

.op-about .op-note-plain { text-align: center; }

/* ---------- один снимок в, комплект наружу ---------- */

/*
 * Один снимок в, четыре наружу — всё в одну строку.
 *
 * Сперва правый столбец был сеткой два на два, и целиком показ не помещался в
 * экран: приходилось листать, чтобы увидеть, что получилось. Владелец сказала
 * верное — расположить складнее и уменьшить, чтобы было видно сразу всё.
 *
 * Теперь пять предметов в ряд: исходник, стрелка, четыре выхода. Читается
 * слева направо ровно как действие, и весь показ помещается на один экран.
 */
.op-show {
  display: grid;
  grid-template-columns: minmax(0, 190px) auto minmax(0, 1fr);
  gap: var(--s-5);
  /*
   * Столбцы тянутся на всю высоту ряда, а не сжимаются по своему содержимому.
   *
   * Иначе средняя полоса — та, где стоят картинки, — у исходника выходит ниже,
   * чем у выходов, и его пояснение поднимается на шесть десятков точек выше
   * соседних. Растянутые столбцы получают одну высоту, их средние полосы
   * совпадают, и все пять пояснений встают на одну линию, как и картинки.
   */
  align-items: stretch;
  text-align: start;
  margin-block-end: var(--s-5);
}

/*
 * Исходник устроен так же, как выход: подпись, картинка, пояснение — и все три
 * полосы встают на одну высоту с соседями. Пока подпись у него была в одну
 * строку, а у выходов в две, его снимок начинался выше остальных и выпадал
 * из ряда.
 */
.op-show__in {
  margin: 0;
  display: grid;
  grid-template-rows: auto 1fr auto;
  gap: var(--s-4);
  justify-items: center;
  text-align: center;
}

/* Стрелка вбок на телефоне не работает: столбцы встают друг под друга,
   и указывать вправо ей больше некуда. */
@media (max-width: 900px) {
  .op-show {
    grid-template-columns: 1fr;
    gap: var(--s-5);
    justify-items: start;
  }

  .op-show__arrow { transform: rotate(90deg); }
  .op-show__in { max-inline-size: 320px; }
}


/* ============================================================
   Комплект, показанный по местам

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

   Теперь каждый файл лежит внутри своего места. Макеты нарочно
   обобщённые — ни названий площадок, ни логотипов, ни фирменных
   цветов: иначе сайт заявлял бы связь с чужой компанией.
   ============================================================ */

/*
 * Два ряда по два, а не четыре в строку.
 *
 * В строку они лезли по 280 точек на макет — телефон в такую ширину читается
 * с трудом, — и вдобавок ряд выходил рваным: макеты разной высоты и разной
 * ширины, отчего и промежутки казались разными. Владелец сказал прямо:
 * выровнять или разбить на два ряда. Разбиваем — заодно каждый макет
 * становится вдвое крупнее.
 */
.op-uses {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--s-7) var(--s-6);
  text-align: start;
}

/*
 * Три полосы: подпись, макет, пояснение. Средняя тянется, поэтому подписи
 * стоят на одной высоте сверху, пояснения — на одной снизу, а макеты плавают
 * между ними по середине. Их собственная высота при этом остаётся честной:
 * сторис правда выше баннера, просто это больше не перекашивает ряд.
 */
/*
 * Подпись, макет и пояснение стоят одним столбиком по середине ячейки: подпись
 * ровно над картинкой, а не у левого края ячейки, где она читалась сама по
 * себе. Ширина у всех троих одна — макета, — поэтому столбик держится вместе.
 */
.op-uses__item {
  display: grid;
  grid-template-rows: auto 1fr auto;
  gap: var(--s-4);
  justify-items: center;
  text-align: center;

  --mock-w: 320px;
}

.op-uses__item.is-narrow { --mock-w: 200px; }

.op-uses__item > .op-mock {
  align-self: center;
  inline-size: 100%;
  max-inline-size: var(--mock-w);
}

/* Пояснению не даём стать уже читаемого: у телефона макет в 200 точек, и
   строка в такую ширину рвётся на четыре слова. */
.op-uses__k,
.op-uses__note {
  max-inline-size: max(var(--mock-w), 260px);
}

/* Кружок вместо аватара и полоски вместо строк текста: макет должен
   читаться как макет, а не притворяться настоящей страницей. */
.op-mock__ava {
  inline-size: 18px;
  block-size: 18px;
  border-radius: var(--r-dot);
  background: var(--mock-dim);
  flex: none;
}

.op-mock--shop .op-mock__meta {
  padding: var(--s-4);
  display: grid;
  gap: var(--s-2);
  justify-items: start;
}

/* На телефоне два столбца не встают: витрина с кнопкой в 160 точек шириной
   нечитаема. Один столбец, макеты во всю ширину. */
@media (max-width: 700px) {
  .op-uses { grid-template-columns: 1fr; gap: var(--s-6); }
}

/*
 * Внутри показа «один в, четыре наружу» макеты стоят вчетвером в ряд и мельче,
 * чем на первой странице: там у них половина полотна на каждый, здесь — пятая
 * часть. Телефон ещё уже прочих, чтобы его высота не тянула строку вверх.
 */
@media (min-width: 1000px) {
  /*
   * Все пять картинок — члены одной сетки, а не двух вложенных.
   *
   * Выходы лежат в обёртке, под которой стоит ещё и общее пояснение, поэтому
   * обёртка выше самих макетов — и столбец исходника, растянутый на её высоту,
   * опускал своё пояснение ниже соседних. Подпирать это отступами значило бы
   * подгонять числа, которые разойдутся от любой правки текста.
   *
   * `display: contents` снимает обёртки из раскладки, не трогая разметку:
   * исходник и четыре выхода становятся соседями в одной сетке, и три их
   * полосы — подпись, картинка, пояснение — совпадают по устройству, а не по
   * подгонке. Общее пояснение уходит на свою строку во всю ширину.
   */
  .op-show {
    grid-template-columns: minmax(0, 190px) auto repeat(4, minmax(0, 1fr));
  }

  .op-show__out,
  .op-show .op-uses {
    display: contents;
  }

  .op-show .op-show__note {
    grid-column: 1 / -1;
    margin-block-start: var(--s-5);
  }

  .op-show .op-uses__item { --mock-w: 190px; }
  .op-show .op-uses__item.is-narrow { --mock-w: 130px; }

  /*
   * Здесь макеты прижаты к верху, а не выключены по середине.
   *
   * На первой странице они стоят по два в ряд и середина им идёт: полосы
   * широкие, разница в высоте гуляет мягко. Здесь их пять в строку рядом с
   * исходником, и середина разводит верхние края на полсотни точек — ряд
   * перестаёт читаться рядом. Владелец сказала прямо: поставить первую
   * картинку на одну линию с остальными.
   *
   * Нижние края при этом расходятся, и это честно: сторис правда выше баннера.
   * А пояснения всё равно стоят на одной высоте — они в своей полосе.
   */
  .op-show .op-uses__item > .op-mock,
  .op-show .op-show__in img {
    align-self: start;
  }
}

/* Ниже тысячи строка в пять предметов не встаёт: исходник ужимается до
   полоски. Столбцы расходятся, показ читается сверху вниз. */
@media (max-width: 999px) {
  .op-show {
    grid-template-columns: 1fr;
    justify-items: start;
  }

  .op-show__arrow { transform: rotate(90deg); margin-block-start: 0; }
  .op-show__in { max-inline-size: 260px; }
}

/* ------------------------------------------------------------------ */
/*  Готовые варианты: правда о них и исходник для сличения             */
/* ------------------------------------------------------------------ */

/*
 * Предупреждение о перерисованной комнате.
 *
 * Полноценный блок, а не мелкая подпись: замер 28.09.2026 показал, что модель
 * перерисовывает комнату, а машинной сверки у нас пока нет — сличает человек,
 * и прочесть об этом он должен до того, как выложит снимок в объявление.
 *
 * Цвет текста основной, не приглушённый. Приглушённый читается как сноска, а
 * это не сноска. Контраст посчитан заново при смене лица: #1A1715 на #F0EDEA — 16,48 : 1.
 */
.op-warn {
  margin: 0 0 var(--s-5);
  padding: var(--s-5);
  max-width: 62ch;
  /* Приподнятый фон, а не белая карточка: предупреждение стоит среди белых
     плиток с вариантами, и белым читалось бы как ещё одна плитка. Слово
     «шалфей» стояло здесь от кремового лица и называло цвет, которого в
     проекте больше нет. */
  background: var(--c-raised);
  border-inline-start: 4px solid var(--c-accent);
  border-radius: var(--r-md);
  color: var(--c-text);
  font-size: var(--t-sm);
  line-height: 1.55;
}

/* Исходник рядом с вариантами — то, с чем человек их сличает. */
.op-source {
  margin-block-start: var(--s-6);
  padding-block-start: var(--s-5);
  border-block-start: 1px solid var(--c-line-soft);
}

.op-source__title {
  margin: 0 0 var(--s-4);
  font-family: var(--f-body);
  font-size: var(--t-sm);
  font-weight: var(--fw-medium);
  /* Подпись, и здесь приглушённый уместен: 5,81 : 1 на карточке. */
  color: var(--c-muted);
}

/*
 * Высота ограничена нарочно.
 *
 * Снимок комнаты приезжает широкоугольным и в полную величину занял бы экран
 * целиком — сличать пришлось бы, прокручивая страницу туда-обратно, то есть
 * по памяти. А сличают глазами и рядом.
 */
.op-source__shot {
  display: block;
  max-width: 100%;
  max-height: 46vh;
  object-fit: contain;
  background: var(--c-sunk);
  border-radius: var(--r-lg);
}

/* ------------------------------------------------------------------ */
/*  Ползунок «как снято / как вернулось»                               */
/* ------------------------------------------------------------------ */

/*
 * Одна комната под двумя слоями, а не два кадра рядом.
 *
 * Рядом лежащие кадры заставляют глаз бегать и сверять углы, а весь вопрос
 * ровно в том, та же ли это комната. Здесь окно, потолок и пол не двигаются
 * буквально: двигается только граница между снятым и дорисованным.
 */
.op-slider {
  position: relative;
  overflow: hidden;
  border-radius: var(--r-lg);
  background: var(--c-sunk);
  line-height: 0;
  isolation: isolate;
}

.op-slider__shot {
  display: block;
  inline-size: 100%;
  block-size: auto;
}

/*
 * Верхний слой обрезается справа, а не прячется целиком: так под границей
 * остаётся видно ровно ту же геометрию, и сравнивать можно, не отводя глаз.
 *
 * Значение приходит переменной от скрипта. Без скрипта переменная не задана,
 * `clip-path` берёт запасные 50 %, и картинка всё равно показывает обе
 * половины.
 */
.op-slider__shot--top {
  position: absolute;
  inset: 0;
  clip-path: inset(0 calc(100% - var(--op-split, 50%)) 0 0);
}

/*
 * Черта белая, а не лаймовая.
 *
 * Лайм на фотографии комнаты читается как часть картинки — зелёное пятно
 * среди зелёных растений. Белая черта с тонкой тёмной обводкой видна на любом
 * снимке, потому что не притворяется его частью.
 */
.op-slider__line {
  position: absolute;
  inset-block: 0;
  inset-inline-start: var(--op-split, 50%);
  inline-size: 2px;
  margin-inline-start: -1px;
  background: #fff;
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--c-text) 22%, transparent);
  pointer-events: none;
}

/*
 * Ручка — приём Apex: белый кружок со стрелками посреди черты.
 *
 * Без неё ползунок не выглядит ползунком: человек видит две половины картинки
 * и не догадывается, что границу можно двигать. Стрелки нарисованы текстом в
 * `::after`, чтобы не тащить ради двух треугольников ни картинку, ни шрифт
 * значков.
 */
.op-slider__knob {
  position: absolute;
  inset-block-start: 50%;
  inset-inline-start: var(--op-split, 50%);
  transform: translate(-50%, -50%);
  inline-size: 2.5rem;
  block-size: 2.5rem;
  border-radius: var(--r-dot);
  background: #fff;
  box-shadow: 0 2px 10px color-mix(in srgb, var(--c-text) 28%, transparent);
  pointer-events: none;
  display: grid;
  place-items: center;
}

.op-slider__knob::after {
  content: '\2194';
  font-size: var(--t-base);
  color: var(--c-text);
  line-height: 1;
}

.op-slider__tag {
  position: absolute;
  inset-block-start: var(--s-4);
  padding: 0.25rem 0.6rem;
  border-radius: var(--r-sm);
  background: color-mix(in srgb, var(--c-bg) 78%, transparent);
  color: var(--c-text);
  font-family: var(--f-mono);
  font-size: var(--t-xs);
  line-height: 1.6;
}

.op-slider__tag--a { inset-inline-start: var(--s-4); }
.op-slider__tag--b { inset-inline-end: var(--s-4); }

/* Подпись нужна диктору, а глазами читается по самим кадрам. */
.op-slider__label {
  position: absolute;
  inline-size: 1px;
  block-size: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/*
 * Сам ползунок лежит поверх всей картинки и прозрачен: тянут за картинку, а
 * не за полоску под ней. При этом он остаётся обычным `input[type=range]` —
 * его видит клавиатура и произносит экранный диктор.
 */
.op-slider__range {
  position: absolute;
  inset: 0;
  inline-size: 100%;
  block-size: 100%;
  margin: 0;
  padding: 0;
  background: transparent;
  appearance: none;
  cursor: ew-resize;
}

.op-slider__range::-webkit-slider-thumb {
  appearance: none;
  inline-size: 44px;   /* палец, а не мышь: меньше сюда не попасть */
  block-size: 100%;
  background: transparent;
  cursor: ew-resize;
}

.op-slider__range::-moz-range-thumb {
  inline-size: 44px;
  block-size: 100%;
  border: 0;
  background: transparent;
  cursor: ew-resize;
}

.op-slider__range:focus-visible {
  outline: 2px solid var(--c-focus);
  outline-offset: 3px;
}

/* ------------------------------------------------------------------ */
/*  Отметка: пустая комната или с чужой мебелью                        */
/* ------------------------------------------------------------------ */

/*
 * Вопрос, на который отвечает человек, а не машина.
 *
 * Стоит между разбором снимка и кнопкой нарочно: это последнее, что решается
 * до списания, и решается оно деньгами — вторая цена вдвое выше первой.
 */
.op-mark {
  margin: 0 0 var(--s-5);
  padding: 0;
  border: 0;
}

.op-mark__ask {
  padding: 0;
  margin-block-end: var(--s-3);
  font-family: var(--f-body);
  font-size: var(--t-sm);
  font-weight: var(--fw-medium);
  color: var(--c-text);
}

.op-mark__one {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-3) var(--s-4);
  margin-block-end: var(--s-2);
  border: var(--border);
  border-radius: var(--r-md);
  background: var(--c-surface);
  cursor: pointer;
}

/* Наведение и выбор видны и без цвета — рамкой, а не только заливкой. */
.op-mark__one:hover { background: var(--c-hover); }

.op-mark__one:has(input:checked) {
  border-color: var(--c-accent);
  background: var(--c-raised);
}

.op-mark__one:has(input:focus-visible) {
  outline: 2px solid var(--c-focus);
  outline-offset: 2px;
}

.op-mark__what {
  display: flex;
  flex: 1;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-3);
  font-size: var(--t-sm);
  color: var(--c-text);
}

/* Цена рядом с ответом, а не под кнопкой: выбирают-то её. */
.op-mark__cost {
  font-family: var(--f-mono);
  font-size: var(--t-xs);
  color: var(--c-muted);
}

/*
 * Выключенная кнопка обязана выглядеть выключенной.
 *
 * Стиля для этого не было вовсе, хотя `disabled` в разметке стоит с самого
 * начала: кнопка отправки гасится на время работы, а кнопка «обставить» — пока
 * человек не отметил, пустая комната или с мебелью. Выглядели обе нажимаемыми,
 * и нажатие просто ничего не делало — то есть экран врал ровно так же, как
 * кнопка, отвечающая отказом на вопрос, который сама не задала.
 *
 * Отличие не только в цвете: курсор говорит то же самое тем, кто цвета не
 * различает. Контраст подписи посчитан заново при смене лица — #5A5450 на #F0EDEA даёт 6,39 : 1.
 */
.op-button:disabled,
.op-plain:disabled {
  background: var(--c-raised);
  border-color: var(--c-line-soft);
  color: var(--c-muted);
  cursor: not-allowed;
}

.op-button:disabled:hover,
.op-plain:disabled:hover {
  background: var(--c-raised);
}

/* ---------- рабочий экран ---------- */

/*
 * Кабинет живёт по тем же правилам, что листы, но тише.
 *
 * Заголовок меньше, чем на витрине: там он продаёт, здесь — подписывает
 * рабочее место, и человек пришёл сюда не читать, а загружать. Мета-строка
 * при этом та же, и в ней справа стоит цена — то, что человек держит в
 * голове, пока выбирает файл.
 */
.op-work { padding-block-start: var(--s-6); }

.op-title--work {
  font-size: clamp(2rem, 4vw, 2.8rem);
  max-inline-size: 22ch;
  margin-block-end: var(--s-4);
}

.op-work .op-lede {
  max-inline-size: 56ch;
  margin-block-end: var(--s-7);
  padding-block-end: var(--s-6);
  border-block-end: var(--border);
}

/* На «о нас» подвал первого листа — просто абзац, без кнопок справа. */
.op-about .op-sheet__foot--plain { display: block; }
