/* Единственная таблица стилей интерфейса.
 *
 * До неё их было две: современная — внутри app.html, и табличная 2005 года —
 * питоновской строкой _STYLE в api/main.py. Один и тот же продукт выглядел
 * двумя разными программами, смотря куда нажать, а правка цвета требовала
 * помнить про оба места. Правило простое: цвет, отступ и скругление
 * определяются здесь и больше нигде.
 *
 * Имена классов — договор с разметкой и со скриптом одностраничника
 * (app.html обращается к .step, .stat, .opt, .est и другим по имени).
 * Переименование класса здесь ломает поведение там, а не только вид.
 *
 * ОБЛИК. Взят с explee.com (тот же жанр — агент, который сам ищет клиентов):
 * почти белый фон, белые карточки, волосяные рамки, мятно-изумрудный акцент,
 * маленькие скругления, плоскость вместо теней. Отличие от оригинала одно и
 * оно намеренное: там тёмная тема, здесь светлая — так попросил владелец.
 * Тёмной ветки по `prefers-color-scheme` НЕТ: половина смысла светлого
 * оформления пропадает, если у человека с тёмной системой экран всё равно
 * чёрный. `color-scheme:light` заставляет светлыми и родные элементы
 * браузера — иначе выпадающий список и полоса прокрутки остаются тёмными
 * посреди белой страницы.
 */

/* --------------------------------------------------------------------------
 * Шрифт
 *
 * Explee набран DM Sans, и первым делом хотелось взять его же. Нельзя:
 * У DM SANS НА GOOGLE FONTS НЕТ КИРИЛЛИЦЫ — только latin и latin-ext. Весь
 * наш интерфейс русский, то есть заголовки молча падали бы на системный
 * шрифт, а латинские названия компаний набирались бы фирменным. Отказ был бы
 * тихим: ошибки нет, просто два шрифта на одной строке.
 *
 * Inter — то, чем Explee набирает основной текст (Geist — его же родня), и
 * кириллица у него полная. Турецкие названия закрывает latin-ext.
 *
 * Подключение через @font-face, а НЕ через @import: @import задерживает
 * первую отрисовку до ответа Google, и недоступный CDN означает застывшую
 * страницу вместо страницы системным шрифтом. `font-display:swap` рисует
 * текст сразу и подменяет шрифт, когда он доедет.
 * -------------------------------------------------------------------------- */
@font-face{font-family:Inter;font-style:normal;font-weight:400 700;
  font-display:swap;
  src:url(https://fonts.gstatic.com/s/inter/v20/UcCo3FwrK3iLTcvmYwYL8g.woff2) format('woff2');
  unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}
@font-face{font-family:Inter;font-style:normal;font-weight:400 700;
  font-display:swap;
  src:url(https://fonts.gstatic.com/s/inter/v20/UcCo3FwrK3iLTcvvYwYL8g.woff2) format('woff2');
  unicode-range:U+0460-052F,U+1C80-1C8A,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}
@font-face{font-family:Inter;font-style:normal;font-weight:400 700;
  font-display:swap;
  src:url(https://fonts.gstatic.com/s/inter/v20/UcCo3FwrK3iLTcviYwY.woff2) format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,
    U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,
    U+FEFF,U+FFFD}
@font-face{font-family:Inter;font-style:normal;font-weight:400 700;
  font-display:swap;
  src:url(https://fonts.gstatic.com/s/inter/v20/UcCo3FwrK3iLTcvsYwYL8g.woff2) format('woff2');
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,
    U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,
    U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}

/* --------------------------------------------------------------------------
 * Величины
 *
 * Размеры не выбираются на глаз по месту: шкала задаётся здесь, а вёрстка
 * берёт из неё. Иначе «чуть крупнее» в трёх местах превращается в три разных
 * размера, между которыми потом невозможно навести порядок.
 * -------------------------------------------------------------------------- */
:root{
  color-scheme:light;

  --bg:#f6f8f7; --panel:#fff; --panel2:#f3f6f5; --panel3:#e7ecea;
  --line:#e4eae7; --line2:#d2dad7;
  --text:#0b1512; --text2:#3d4a46; --muted:#6b7674;

  /* Акцент разделён на три токена, и это не педантизм, а контраст.
     Фирменный мятный #10b981 на белом даёт 2.3:1 — надписью его читать
     нельзя. Белая надпись НА нём — тоже 2.4:1. Поэтому:
       --accent      заливка, рамка, кольцо фокуса (яркий мятный);
       --accent-ink  текст и ссылки (тёмно-изумрудный, 5.2:1 на белом);
       --on-accent   надпись поверх заливки (почти чёрная, 7.3:1).
     Кнопка «мятная заливка + чёрные буквы» — подпись Explee, а не случайность:
     у них ровно `bg-brand-500 text-black`. */
  --accent:#10b981; --accent2:#0d9467;
  --accent-ink:#0a7854; --on-accent:#04231a;
  --accent-soft:rgba(16,185,129,.10); --accent-line:rgba(16,185,129,.32);

  /* Работа модели — свой цвет, как у Explee (--ai: #8b5cf6). Иначе «идёт
     сейчас» и «получилось» окрашены одинаково зелёным и не различаются
     боковым зрением. */
  --ai:#7008e7; --ai-solid:#8b5cf6; --ai-soft:rgba(139,92,246,.11);

  --ok:#047857; --warn:#b04300; --err:#bf000f;
  /* Заливка против текста: у сплошной полосы и у надписи разные требования
     к контрасту, поэтому оттенка два, а не один. */
  --ok-solid:#10b981; --warn-solid:#f05100; --err-solid:#e40014;

  /* Explee: --radius .625rem (10px), кнопки rounded-lg, поле запроса
     rounded-2xl. Скругления мельче прежних — форма спокойнее. */
  --r-sm:8px; --r:10px; --r-lg:16px; --radius:10px;
  --shadow:0 1px 2px rgba(11,21,18,.04);
  --shadow-lift:0 8px 25px -8px rgba(16,185,129,.42);

  --s1:6px; --s2:10px; --s3:16px; --s4:24px; --s5:36px; --s6:56px;

  --mono:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,monospace;
}

*{box-sizing:border-box}
html{-webkit-text-size-adjust:100%}
/* Мягкое мятное свечение вверху страницы — единственное украшение. У Explee
   его роль играет стекло на тёмном; на светлом фоне тот же приём читается
   как подсветка за заголовком. Не повторяется и не тянется вниз: под
   таблицами фон обязан быть ровным. */
body{margin:0;color:var(--text);
  /* Фон первого экрана шире колонки и упирается в края окна. `100vw`
     считается ВМЕСТЕ с полосой прокрутки, и без этой строки страница
     получала бы лишние пиксели вбок. `clip` не создаёт нового контекста
     прокрутки, `hidden` — запасной путь для тех, кто его не знает. */
  overflow-x:hidden;overflow-x:clip;
  background:radial-gradient(760px 360px at 50% -160px,
      rgba(16,185,129,.13),rgba(16,185,129,0) 72%) no-repeat,
    var(--bg);
  font:16px/1.6 Inter,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,
       "Helvetica Neue",sans-serif;
  -webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility}
.wrap{max-width:780px;margin:0 auto;padding:var(--s4) var(--s3) var(--s6)}
.wrap.wide{max-width:1180px}
@media (min-width:700px){ .wrap{padding:var(--s5) var(--s4) var(--s6)} }

a{color:var(--accent-ink);text-decoration:none}
a:hover{text-decoration:underline}

/* Видимое кольцо фокуса: клавиатурой по этому интерфейсу ходят, а рамка по
   умолчанию на светлом фоне почти не видна. */
:focus-visible{outline:2px solid var(--accent);outline-offset:2px;
  border-radius:var(--r-sm)}

h1{font-size:34px;line-height:1.16;margin:0 0 var(--s2);
  font-weight:640;letter-spacing:-1px}
@media (max-width:520px){ h1{font-size:27px;letter-spacing:-.7px} }
h2{font-size:12px;font-weight:640;letter-spacing:.08em;text-transform:uppercase;
  color:var(--muted);margin:var(--s5) 0 var(--s2)}
h3{margin:0 0 var(--s1);font-size:16.5px;font-weight:620;letter-spacing:-.2px}

.sub,.note{color:var(--muted);font-size:14.5px}
.lead{color:var(--text2);font-size:17.5px;line-height:1.55;margin:0 0 var(--s4)}
.meta{color:var(--muted);font-size:13.5px;margin-top:var(--s1)}
.hidden{display:none !important}
/* Две формы-кнопки в одной ячейке таблицы («выдать» / «отклонить»): формы
   блочные и без этого встают друг под друга. */
.inline{display:inline}

/* --------------------------------------------------------------------------
 * Шапка — общая для одностраничника и для страниц, которые рисует сервер
 * -------------------------------------------------------------------------- */
nav.top{display:flex;align-items:center;gap:var(--s3);flex-wrap:wrap;
  margin-bottom:var(--s5);padding-bottom:var(--s3);
  border-bottom:1px solid var(--line)}
nav.top .brand{display:flex;align-items:center;gap:9px;font-weight:660;
  font-size:15.5px;letter-spacing:-.3px;color:var(--text)}
nav.top .brand:hover{text-decoration:none;color:var(--accent-ink)}
nav.top .brand::before{content:"";width:22px;height:22px;border-radius:7px;
  background:var(--accent);flex:none;
  -webkit-mask:radial-gradient(circle at 42% 42%,transparent 4.5px,#000 5.6px,
    #000 6.8px,transparent 7.9px),
    linear-gradient(45deg,transparent 45%,#000 45%,#000 55%,transparent 55%);
  -webkit-mask-composite:source-over;mask-composite:add}
nav.top a{font-size:14.5px;color:var(--muted);padding:2px 0;
  transition:color .2s}
nav.top a:hover{color:var(--accent-ink);text-decoration:none}
nav.top a.here{color:var(--text)}
nav.top .grow{flex:1}

/* --------------------------------------------------------------------------
 * Шаги заявки
 *
 * Экранов три — заявка, уточнения, работа, — и человек не понимал, где он
 * находится и сколько осталось: страница просто подменяла одну карточку
 * другой. Полоса шагов отвечает на этот вопрос до того, как он задан.
 * -------------------------------------------------------------------------- */
.flow{display:flex;align-items:center;gap:var(--s2);margin-bottom:var(--s4);
  font-size:13.5px;color:var(--muted);flex-wrap:wrap}
.flow i{display:flex;align-items:center;gap:7px;font-style:normal}
.flow i b{display:grid;place-items:center;width:22px;height:22px;border-radius:50%;
  background:var(--panel);border:1px solid var(--line2);
  font-size:12px;font-weight:640;color:var(--muted);flex:none}
.flow i.on{color:var(--text)}
.flow i.on b{background:var(--accent);border-color:var(--accent);
  color:var(--on-accent)}
.flow i.past{color:var(--text2)}
.flow i.past b{background:var(--accent-soft);border-color:var(--accent-line);
  color:var(--accent-ink)}
.flow s{flex:1;min-width:14px;max-width:44px;height:1px;background:var(--line2);
  text-decoration:none}

/* --------------------------------------------------------------------------
 * Первый экран
 *
 * Раньше продукт открывался формой: поле, подсказки, кнопка. Человек, попавший
 * сюда впервые, не понимал, что произойдёт после нажатия и во что это встанет.
 * Обещание названо словами, а сходящиеся к полю линии показывают то же самое
 * движением: разрозненный рынок сходится в одну строку ответа.
 *
 * Линии рисует canvas в app.html — здесь только его место на странице. Он
 * шире колонки и упирается в края окна, поэтому у body погашена
 * горизонтальная прокрутка.
 * -------------------------------------------------------------------------- */
.hero{position:relative;text-align:center;
  padding:clamp(8px,2vw,24px) 0 clamp(24px,4vw,44px);
  margin-bottom:var(--s3)}
/* Вылет вверх и вниз большой: линии приходят длинными плоскими дугами
   издалека, и обрезанные по краю героя они выглядели бы обрубками. */
.hero-bg{position:absolute;top:-96px;bottom:-64px;left:50%;
  transform:translateX(-50%);width:min(1600px,100vw);
  pointer-events:none;z-index:0}
.hero-in{position:relative;z-index:1;display:flex;flex-direction:column;
  align-items:center;gap:22px}
/* Плашка жмётся к заголовку, поле — к плашке: на референсе это одна связка,
   а подпись под полем отбита заметно сильнее. */
.hero-in .pill{margin-top:-4px}
.hero-in .hero-sub{margin-top:14px}
/* Появление сверху вниз, по порядку чтения. */
.hero-in>*{animation:card-enter .65s cubic-bezier(.16,1,.3,1) both}
.hero-in>*:nth-child(2){animation-delay:.06s}
.hero-in>*:nth-child(3){animation-delay:.14s}
.hero-in>*:nth-child(4){animation-delay:.22s}
.hero-in>*:nth-child(5){animation-delay:.3s}

/* Плашка над заголовком: у Explee здесь обещание про бесплатные кредиты, у
   нас — про то, чего у конкурентов нет и о чём спрашивают первым. */
.pill{display:inline-flex;align-items:center;gap:9px;border-radius:999px;
  background:var(--panel);border:1px solid var(--line2);padding:7px 17px;
  font-size:13.5px;color:var(--text2);box-shadow:var(--shadow)}
.pill::before{content:"";width:6px;height:6px;border-radius:50%;
  background:var(--accent);flex:none;animation:pulse 2.4s infinite}

/* Перенос стоит жёстким `<br>`, а не отдан браузеру, и `text-wrap:balance`
   снят. Причина одна: строка меняет ширину вместе со словом. Отданный
   браузеру перенос переезжал бы на каждой смене — на «сайты» в первую строку
   влезало ещё и «пока», на «владельцев» уже нет, — и заголовок
   перекладывался бы сам собой каждые три секунды. */
.hero-h1{margin:0;max-width:920px;font-size:clamp(30px,5.4vw,56px);
  line-height:1.1;font-weight:620;letter-spacing:-.035em;
  color:var(--text);position:relative}

/* Меняющееся слово — в строке, как на референсе, а не отдельной строкой:
   своя строка делала заголовок трёхстрочным.

   Ширину ведёт скрипт (`rot-measure` меряет каждое слово этим же шрифтом) и
   переезжает плавно. Разметка референса держит ширину по самому длинному
   варианту невидимыми копиями — на наших словах («сайты» против
   «владельцев») это оставляло пустой хвост и сдвигало выровненный по центру
   заголовок влево на добрую сотню пикселей.

   Ни `overflow:hidden`, ни `inline-grid`: оба сбивают базовую линию
   inline-блока, и слово село бы ниже строки. На краях оно и так прозрачно. */
.rot{display:inline-block;color:var(--accent-ink);text-align:left;
  white-space:nowrap;overflow:visible;
  /* Слово — тег `b`, и браузер дал бы ему свой bold (700) поверх веса
     заголовка. Мерка — `span`, она наследует 620. Разные шрифты — разная
     ширина, и слово вылезло бы за отмеренную коробку прямо на запятую.
     У референса слово тоже одного веса с остальной строкой, отличается
     только цветом. */
  font-weight:inherit;
  transition:width .34s cubic-bezier(.16,1,.3,1)}
.rot.out{animation:word-out .24s ease-in forwards}
.rot.in{animation:word-in .42s cubic-bezier(.16,1,.3,1) both}
/* Запятая держится за слово: блок с заданной шириной атомарен, и без этого
   перенос уносил её на следующую строку, где она начинала собой строку. */
.nb{white-space:nowrap}
/* Мерка. Живёт в заголовке, чтобы наследовать его шрифт и кегль, но не
   занимает места и не читается вслух. */
.rot-measure{position:absolute;left:-9999px;top:0;white-space:nowrap;
  visibility:hidden;pointer-events:none}
@keyframes word-out{to{opacity:0;transform:translateY(-.3em)}}
@keyframes word-in{from{opacity:0;transform:translateY(.3em)}
  to{opacity:1;transform:translateY(0)}}

/* Поле запроса — то, к чему сходятся линии, и единственное, что нужно
   заполнить. Кнопка внутри рамки, как у Explee: поле и действие читаются
   одним предметом, а не двумя. */
/* Тянется ВСЯ карточка, а не поле внутри неё.
   Уголок браузер рисует в правом нижнем углу того элемента, которому задан
   `resize`. Пока это было поле, уголок оказывался на его дне — то есть
   посреди карточки, над строкой с кнопкой, и читался как случайная риска на
   пустом месте. Тянуть при этом человек хочет карточку: она — предмет.
   `overflow:hidden` не украшение, а условие: без него `resize` не работает,
   и он же обрезает содержимое по скруглению. */
.launch{width:100%;max-width:720px;text-align:left;background:var(--panel);
  border:1px solid var(--line2);border-radius:24px;padding:0;
  display:flex;flex-direction:column;resize:vertical;overflow:hidden;
  /* Ниже этого кнопку начало бы резать: поле 84 + строка с кнопкой. */
  min-height:142px;
  box-shadow:0 1px 2px rgba(11,21,18,.04),0 20px 44px -32px rgba(11,21,18,.55);
  transition:border-color .18s,box-shadow .25s}
.launch:focus-within{border-color:var(--accent);
  box-shadow:0 0 0 4px var(--accent-soft),
    0 20px 44px -30px rgba(16,185,129,.5)}
/* Поле растёт под текст само, но развернуть карточку заранее — право
   человека. Убирали уголок — заметили сразу.
   `flex:1 1 auto`, а не `flex:1`: основа `auto` оставляет полю его
   собственную высоту, ту самую, которую выставляет автоподбор. С `flex:1`
   основа стала бы нулевой, поле всегда занимало бы всю карточку, и
   автоподбор высоты перестал бы что-либо значить. */
.launch textarea{border:0;background:transparent;border-radius:24px 24px 0 0;
  min-height:84px;padding:19px 22px 8px;font-size:17px;resize:none;
  flex:1 1 auto;min-width:0;overflow-y:auto}
.launch textarea:focus{box-shadow:none;border:0}
/* Кнопка стояла в семи пикселях от рамки и упиралась в неё. Отступы здесь
   те же, что у поля сверху, — иначе низ карточки выглядит обрезанным. */
/* Справа отступ больше левого ровно на ширину уголка: он рисуется в углу
   карточки, и кнопка, стоящая вплотную, оказалась бы под ним. */
.launch-row{display:flex;align-items:center;gap:var(--s2);flex-wrap:wrap;
  padding:0 22px 14px 22px}
.launch-row .sub{flex:1;min-width:150px;font-size:13px}
.launch-row button{padding:12px 22px;font-size:15px}

.hero-sub{margin:0;max-width:560px;color:var(--muted);font-size:16.5px;
  line-height:1.55}
.hero .hints{justify-content:center;margin-top:0}

/* На первом экране заголовок и подпись живут внутри героя. Внешняя пара,
   общая для остальных шагов, на нём прячется — иначе одно и то же было бы
   написано дважды подряд. */
body.at-hero #page-title,body.at-hero #page-lead{display:none}

/* Надпись про счёт уезжает ПОД герой. Стоя над ним, она отжимала обещание и
   поле вниз — на ноутбуке до поля приходилось доскроллить, а первый экран
   продукта показывал служебные сведения раньше, чем то, ради чего сюда
   пришли. Полоса шагов при этом наверху: она одна строка, а не блок.

   Порядком, а не переносом в разметке: надпись про счёт вставляет сервер по
   метке `<!--БАЛАНС-->`, и таскать эту метку по файлу значит ломать место
   вставки. Здесь же видно, что порядок разный только на первом экране.

   Оговорка. Правило проекта — человек с пустым счётом узнаёт об этом ДО
   того, как напишет заявку. Надпись идёт первой сразу под героем, то есть
   остаётся в том же экране; а нажатие всё равно упрётся в отказ с той же
   формулировкой. Решение владельца от 01.09.2026. */
body.at-hero .wrap{display:flex;flex-direction:column}
body.at-hero nav.top{order:0;margin-bottom:var(--s3)}
/* Полоса шагов осталась НАВЕРХУ, а вниз ушла только надпись про счёт. Разница
   между ними в цене места: полоса — одна тонкая строка и отвечает на вопрос
   «где я» до того, как он задан; надпись про счёт — блок в три строки в
   рамке, и вот он-то и отжимал обещание с полем за пределы экрана. */
body.at-hero .flow{order:1;justify-content:center;margin:0 0 var(--s3)}
body.at-hero #error{order:2}
body.at-hero #step-ask{order:3}
body.at-hero #balance-note{order:4;margin-top:var(--s4)}
body.at-hero #balance-note,body.at-hero #error{max-width:620px;
  margin-left:auto;margin-right:auto;text-align:center}

@media (max-width:520px){
  .hero-in{gap:22px}
  .launch{border-radius:18px}
  .launch textarea{border-radius:18px 18px 0 0;font-size:16.5px;
    padding:15px 16px 4px}
  .launch-row{padding:0 10px 10px 16px}
  .launch-row button{width:100%}
  .hero-sub{font-size:15.5px}
}

/* --------------------------------------------------------------------------
 * Поверхности
 * -------------------------------------------------------------------------- */
/* Появление. Экраны сменяют друг друга подменой карточки, и без движения
   человек не замечал самой смены: заголовок другой, а глазу не за что
   зацепиться. Кривая и сдвиг — те же, что у Explee (12px снизу). */
@keyframes card-enter{from{opacity:0;transform:translateY(12px)}
  to{opacity:1;transform:translateY(0)}}

.card{background:var(--panel);border:1px solid var(--line);
  border-radius:var(--r-lg);padding:var(--s4);margin-bottom:var(--s3);
  box-shadow:var(--shadow);
  animation:card-enter .5s cubic-bezier(.16,1,.3,1) both}
@media (max-width:520px){ .card{padding:var(--s3);border-radius:var(--r)} }

.err{background:color-mix(in srgb,var(--err) 8%,#fff);
  border:1px solid color-mix(in srgb,var(--err) 28%,#fff);
  color:var(--err);padding:13px 15px;border-radius:var(--r);
  margin-bottom:var(--s3);font-size:14.5px}
.empty{color:var(--muted);background:var(--panel2);
  border:1px dashed var(--line2);border-radius:var(--r);
  padding:var(--s4);text-align:center;font-size:14.5px}

/* --------------------------------------------------------------------------
 * Поля ввода
 * -------------------------------------------------------------------------- */
textarea,input,select{font:inherit;color:var(--text);background:var(--panel);
  border:1px solid var(--line2);border-radius:var(--r);outline:none;
  transition:border-color .15s, box-shadow .15s}
/* Поле запроса — главный элемент первого экрана; у Explee ровно оно набрано
   крупно и скруглено сильнее остального. */
textarea{width:100%;min-height:124px;resize:vertical;padding:17px 19px;
  font-size:17px;line-height:1.5;border-radius:var(--r-lg)}
input,select{padding:11px 13px}
textarea::placeholder{color:var(--muted)}
textarea:focus,input:focus,select:focus{border-color:var(--accent);
  box-shadow:0 0 0 3px var(--accent-soft);outline:none}

/* Подсказки-примеры: нажимаются, значит обязаны выглядеть нажимаемыми. */
.hints{display:flex;gap:var(--s1);flex-wrap:wrap;margin-top:var(--s3)}
.chip{background:var(--panel);border:1px solid var(--line2);
  border-radius:999px;padding:7px 14px;font-size:13.5px;color:var(--text2);
  cursor:pointer;transition:border-color .15s,color .15s,background .15s}
.chip:hover{border-color:var(--accent);color:var(--accent-ink);
  background:var(--accent-soft)}

button{background:var(--accent);color:var(--on-accent);border:0;
  border-radius:var(--r);padding:12px 22px;font:inherit;font-size:15.5px;
  font-weight:620;cursor:pointer;
  transition:background .2s,box-shadow .25s,transform .15s,opacity .15s}
button:hover{background:var(--accent2);box-shadow:var(--shadow-lift);
  transform:translateY(-1px)}
button:active{transform:none;box-shadow:none}
button[disabled]{opacity:.5;cursor:default;box-shadow:none;transform:none}
button[disabled]:hover{background:var(--accent);box-shadow:none;transform:none}
button.ghost{background:transparent;color:var(--text2);border:1px solid var(--line2)}
button.ghost:hover{color:var(--text);border-color:var(--text2);
  background:transparent;box-shadow:none;transform:none}
.row{display:flex;gap:var(--s2);align-items:center;margin-top:var(--s3);flex-wrap:wrap}
.actions{display:flex;gap:var(--s2);align-items:center;flex-wrap:wrap;
  margin-top:var(--s3)}
a.btn{display:inline-block;background:var(--accent);color:var(--on-accent);
  border-radius:var(--r);padding:12px 22px;font-weight:620;font-size:15.5px;
  transition:background .2s,box-shadow .25s,transform .15s}
a.btn:hover{background:var(--accent2);text-decoration:none;
  box-shadow:var(--shadow-lift);transform:translateY(-1px)}
a.btn.ghost{background:transparent;color:var(--text2);border:1px solid var(--line2)}
a.btn.ghost:hover{color:var(--text);border-color:var(--text2);
  box-shadow:none;transform:none}

form.stack{display:grid;gap:var(--s3)}
form.stack label{display:grid;gap:5px;font-size:13.5px;color:var(--muted)}
form.stack .row{display:flex;gap:var(--s2);flex-wrap:wrap;margin:0}
form.stack .row label{flex:1;min-width:140px}

/* --------------------------------------------------------------------------
 * Уточняющие вопросы
 * -------------------------------------------------------------------------- */
.understood{background:var(--accent-soft);border-left:3px solid var(--accent);
  padding:14px 16px;border-radius:0 var(--r) var(--r) 0;margin-bottom:var(--s4);
  font-size:15.5px}
.understood b{font-weight:620}

.q{border:1px solid var(--line);border-radius:var(--r);padding:var(--s3);
  margin-bottom:var(--s2);background:var(--panel2);
  animation:card-enter .5s cubic-bezier(.16,1,.3,1) both}
.q:nth-of-type(2){animation-delay:.06s}
.q:nth-of-type(3){animation-delay:.12s}
.q:nth-of-type(4){animation-delay:.18s}
.why{color:var(--muted);font-size:14px;margin-bottom:var(--s2);line-height:1.55}
.opts{display:grid;gap:var(--s1)}
label.opt{display:flex;gap:11px;align-items:flex-start;padding:13px 15px;
  border:1px solid var(--line);border-radius:var(--r-sm);cursor:pointer;
  background:var(--panel);transition:border-color .15s,background .15s}
label.opt:hover{border-color:var(--line2)}
label.opt.on{border-color:var(--accent);background:var(--accent-soft)}
label.opt input{margin-top:4px;accent-color:var(--accent);flex:none}
.opt b{font-weight:620}
.opt span{color:var(--muted);font-size:13.5px;display:block;margin-top:1px}

/* Оценка перед запуском */
.est{border:1px solid var(--accent-line);border-radius:var(--r);
  padding:var(--s3);margin-bottom:var(--s3);background:var(--accent-soft);
  animation:card-enter .5s cubic-bezier(.16,1,.3,1) both}
.est h3{margin:0 0 var(--s2)}
.est .big{display:grid;grid-template-columns:repeat(auto-fit,minmax(118px,1fr));
  gap:var(--s2)}
.est .big div{background:var(--panel);border:1px solid var(--line);
  border-radius:var(--r-sm);padding:13px 14px}
.est .big b{display:block;font-size:23px;font-weight:660;letter-spacing:-.7px;
  font-variant-numeric:tabular-nums}
.est .big span{color:var(--muted);font-size:12.5px}
.est .note{color:var(--muted);font-size:13.5px;margin-top:var(--s2)}
.est .warn{color:var(--warn)} .est .err{color:var(--err);background:none;border:0;padding:0}
.taglist{display:flex;flex-wrap:wrap;gap:var(--s1);margin-top:var(--s2)}
.tag{background:var(--panel);border:1px solid var(--line);border-radius:999px;
  padding:5px 11px;font-size:12.5px;color:var(--text2)}

/* --------------------------------------------------------------------------
 * Экран работы
 * -------------------------------------------------------------------------- */
.bar{height:5px;background:var(--panel3);border-radius:999px;overflow:hidden;
  margin:var(--s3) 0}
/* Блик бежит по полосе, пока задание идёт. Ширина меняется раз в четыре
   секунды — между опросами полоса стоит, и на неподвижном экране это
   читается как «зависло». Блик отвечает на этот вопрос непрерывно.
   Остановленное задание блика не имеет: движение там означало бы работу. */
.bar i{display:block;height:100%;width:0;border-radius:999px;
  transition:width .6s ease;
  background:linear-gradient(90deg,var(--accent) 0%,#4fdcae 50%,
    var(--accent) 100%);
  background-size:200% 100%;animation:shimmer 2.6s linear infinite}
.bar i.stopped{background:var(--warn-solid);animation:none}
@keyframes shimmer{to{background-position:-200% 0}}

/* Расход против потолка. Это единственное число, которое человек не может
   посмотреть нигде ещё и о котором спрашивает первым: сколько уже стоило и
   сколько осталось до остановки. Полоса отвечает раньше, чем цифра. */
.meter{margin-top:var(--s3)}
.meter .lab{display:flex;justify-content:space-between;align-items:baseline;
  font-size:13.5px;color:var(--muted);margin-bottom:6px}
.meter .lab b{color:var(--text);font-weight:620;font-size:15px;
  font-variant-numeric:tabular-nums}
.meter .track{height:7px;background:var(--panel3);border-radius:999px;
  overflow:hidden}
.meter .track i{display:block;height:100%;background:var(--ok-solid);width:0;
  border-radius:999px;transition:width .6s ease,background .3s}
.meter.near .track i{background:var(--warn-solid)}
.meter.over .track i{background:var(--err-solid)}

.stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(112px,1fr));
  gap:var(--s2);margin-top:var(--s3)}
.stat{position:relative;background:var(--panel);border:1px solid var(--line);
  border-radius:var(--r-sm);padding:14px 15px;
  animation:card-enter .5s cubic-bezier(.16,1,.3,1) both}
.stats .stat:nth-child(2){animation-delay:.04s}
.stats .stat:nth-child(3){animation-delay:.08s}
.stats .stat:nth-child(4){animation-delay:.12s}
.stats .stat:nth-child(5){animation-delay:.16s}
.stats .stat:nth-child(6){animation-delay:.2s}
/* Прирост с прошлого опроса. Число в плитке доезжает до нового значения, а
   эта цифра всплывает и гаснет: без неё изменение раз в четыре секунды
   неотличимо от неподвижного экрана. */
.delta{position:absolute;right:13px;top:15px;font-style:normal;
  text-decoration:none;font-size:12.5px;font-weight:640;
  color:var(--accent-ink);font-variant-numeric:tabular-nums;
  opacity:0;pointer-events:none}
.delta.go{animation:delta-flash 1.7s ease-out}
@keyframes delta-flash{0%{opacity:0;transform:translateY(4px)}
  14%{opacity:1;transform:translateY(0)}
  100%{opacity:0;transform:translateY(-15px)}}
.stat b{display:block;font-size:26px;font-weight:660;letter-spacing:-.9px;
  line-height:1.15;font-variant-numeric:tabular-nums}
.stat span{color:var(--muted);font-size:12.5px;display:block;margin-top:2px}

/* Ход работы вертикальной лентой с направляющей: список серых точек не
   показывал ни порядка фаз, ни того, что они связаны. */
.steps{display:grid;gap:0;margin-top:var(--s4);position:relative}
.step{display:flex;gap:13px;align-items:flex-start;color:var(--muted);
  font-size:14.5px;padding:9px 0;position:relative;flex-wrap:wrap}
.step .dot{width:11px;height:11px;border-radius:50%;background:var(--panel3);
  border:2px solid var(--panel3);flex:none;margin-top:5px;position:relative;
  z-index:1;transition:background .3s,border-color .3s}
/* Направляющая соединяет точки и обрывается на последней. */
.step::before{content:"";position:absolute;left:5px;top:19px;bottom:-9px;
  width:1px;background:var(--line)}
.step:last-child::before{display:none}
.step.done{color:var(--text2)}
.step.done .dot{background:var(--ok-solid);border-color:var(--ok-solid)}
.step.now{color:var(--text);font-weight:560}
.step.now .dot{background:var(--accent);border-color:var(--accent);
  box-shadow:0 0 0 4px var(--accent-soft);animation:pulse 1.5s infinite}
@keyframes pulse{0%,100%{opacity:1;transform:scale(1)}
  50%{opacity:.45;transform:scale(.72)}}

/* Пропущенная фаза не должна выглядеть недоделанной: у неё свой знак и
   своя подпись с причиной, взятой из плана. */
.step.skipped{align-items:baseline}
.step.skipped .dot{background:transparent;border:1.5px dashed var(--line2)}
.why-skip{font-size:11.5px;text-transform:uppercase;letter-spacing:.06em;
  color:var(--muted);border:1px solid var(--line2);border-radius:999px;
  padding:2px 9px;white-space:nowrap}
.skip-note{font-size:13.5px;color:var(--muted);flex:1 1 100%;
  margin-left:24px;opacity:.9;line-height:1.5}

.spin{display:inline-block;width:15px;height:15px;border:2px solid transparent;
  border-top-color:currentColor;border-right-color:currentColor;
  border-radius:50%;animation:sp .7s linear infinite;
  vertical-align:-2px;margin-right:9px;opacity:.85}
@keyframes sp{to{transform:rotate(360deg)}}

/* Человек может не хотеть движения на экране — это настройка системы, а не
   каприз. Пульс и вращение выключаются, ширина полос по-прежнему меняется. */
@media (prefers-reduced-motion: reduce){
  *,*::before,*::after{animation-duration:.001ms !important;
    animation-iteration-count:1 !important;transition-duration:.001ms !important}
}

/* --------------------------------------------------------------------------
 * Страницы, которые рисует сервер: список, карточка, предпросмотр, форма
 * -------------------------------------------------------------------------- */

/* Значок состояния. Слово вместо кода: «failed» на экране у человека, который
   не писал этот код, не значит ничего. */
.badge{display:inline-flex;align-items:center;gap:6px;white-space:nowrap;
  border:1px solid var(--line2);border-radius:999px;padding:3px 11px;
  font-size:12.5px;font-weight:560;color:var(--muted);background:var(--panel);
  vertical-align:middle}
.badge::before{content:"";width:6px;height:6px;border-radius:50%;
  background:currentColor;flex:none}
.badge.ok{color:var(--ok);background:color-mix(in srgb,var(--ok) 7%,#fff);
  border-color:color-mix(in srgb,var(--ok) 26%,#fff)}
.badge.warn{color:var(--warn);background:color-mix(in srgb,var(--warn) 8%,#fff);
  border-color:color-mix(in srgb,var(--warn) 26%,#fff)}
.badge.err{color:var(--err);background:color-mix(in srgb,var(--err) 7%,#fff);
  border-color:color-mix(in srgb,var(--err) 26%,#fff)}
/* «Идёт сейчас» — фиолетовым, а не зелёным: зелёный уже занят словом
   «получилось», и в списке заданий два разных состояния сливались в одно. */
.badge.live{color:var(--ai);background:var(--ai-soft);
  border-color:color-mix(in srgb,var(--ai-solid) 34%,#fff)}
.badge.live::before{animation:pulse 1.4s infinite}

/* Широкая таблица прокручивается ВНУТРИ себя: иначе на телефоне горизонтально
   ездит вся страница и заголовок уезжает из виду. */
.scroll{overflow-x:auto;border:1px solid var(--line);border-radius:var(--r-lg);
  background:var(--panel);box-shadow:var(--shadow)}
table.tbl{border-collapse:collapse;width:100%;font-size:14.5px}
table.tbl th,table.tbl td{padding:13px 16px;text-align:left;vertical-align:top;
  border-bottom:1px solid var(--line)}
table.tbl thead th{background:var(--panel2);color:var(--muted);
  font-size:11.5px;font-weight:650;text-transform:uppercase;letter-spacing:.07em;
  white-space:nowrap}
table.tbl tbody tr:last-child td{border-bottom:0}
table.tbl tbody tr{transition:background .12s}
table.tbl tbody tr:hover{background:var(--panel2)}
table.tbl td.num,table.tbl th.num{text-align:right;
  font-variant-numeric:tabular-nums;white-space:nowrap}
table.tbl .dim{color:var(--muted)}
table.tbl .wrap-cell{min-width:220px;max-width:420px}

/* Категорию планировщик формулирует предложением на две строки и длиннее.
   Целиком она распирает строку таблицы до высоты экрана и мешает сравнивать
   задания между собой; полный текст остаётся во всплывающей подсказке. */
.clamp2{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;
  overflow:hidden;line-height:1.45}

/* Пара «название — значение». Заменяет таблицу из двух колонок, которая на
   узком экране разъезжалась. */
.kv{display:grid;grid-template-columns:minmax(150px,auto) 1fr;gap:1px;
  background:var(--line);border:1px solid var(--line);
  border-radius:var(--r-lg);overflow:hidden;margin:0}
.kv dt{background:var(--panel2);color:var(--muted);padding:12px 16px;
  font-size:14px}
.kv dd{background:var(--panel);margin:0;padding:12px 16px}
.kv dd.num{font-variant-numeric:tabular-nums}
@media (max-width:560px){ .kv{grid-template-columns:1fr}
  .kv dt{padding-bottom:2px} .kv dd{padding-top:2px} }

/* Техническая причина отказа прячется, но не выбрасывается: человеку она не
   нужна, а тому, кто чинит, — единственная зацепка. */
details.tech{margin-top:var(--s2);border:1px solid var(--line);
  border-radius:var(--r-sm);background:var(--panel2);overflow:hidden}
details.tech summary{cursor:pointer;padding:11px 15px;font-size:13.5px;
  color:var(--muted);user-select:none}
details.tech summary:hover{color:var(--text)}
details.tech pre{margin:0;padding:0 15px 15px;font-size:12.5px;line-height:1.55;
  color:var(--muted);white-space:pre-wrap;word-break:break-word;
  font-family:var(--mono)}
