
:root{
  --bg:#0c0d0f; --graphite:#16171a; --graphite2:#1d1f23;
  --copper:#946041; --copper2:#774830; --copper3:#b07b52;
  --steel:#a8a49c; --amber:#ffa620; --amber2:#c97d14;
  /* --dim поднят с #6f6c66: на нём весь мелкий текст (12–13.5 px) и он не проходил даже
     мягкий порог — 2.96 на кнопке «Отправить» и 3.15 на карточке. Стало 4.99 в худшем месте.
     --muted намеренно не тронут (решение заказчика): вторичный текст и без того проходил AA. */
  --text:#ece9e2; --muted:#a09c94; --dim:#95928a;
  --ok:#8fbf6a; --bad:#e06840;
  --mode:#ffa620; --mode-glow:rgba(255,166,32,.55);
  --mono:ui-monospace,SFMono-Regular,Menlo,Consolas,"Liberation Mono",monospace;
  --sans:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
}
html[data-mode="read"]{--mode:#8fbf6a;--mode-glow:rgba(143,191,106,.5)}
html[data-mode="write"]{--mode:#ffa620;--mode-glow:rgba(255,166,32,.55)}
html[data-mode="full"]{--mode:#e06840;--mode-glow:rgba(224,104,64,.55)}
*{box-sizing:border-box}
html{-webkit-text-size-adjust:100%;scroll-behavior:smooth}
body{margin:0;background:var(--bg);color:var(--text);font:17px/1.62 var(--sans);-webkit-font-smoothing:antialiased;overflow-x:hidden}
.wrap{max-width:1240px;margin:0 auto;padding:0 26px}

.mockbar{background:#08090a;border-bottom:1px solid #24262a;font:400 13.5px/1.55 var(--mono);color:var(--dim)}
.mockbar .wrap{padding:9px 26px;display:flex;gap:10px;flex-wrap:wrap}
.mockbar b{color:var(--amber);letter-spacing:.12em}

header.site{border-bottom:1px solid #24262a;background:rgba(12,13,15,.92);backdrop-filter:blur(8px);position:sticky;top:0;z-index:20}
header.site .wrap{display:flex;align-items:center;gap:24px;padding:15px 26px}
.logo{font:700 22px/1 var(--mono);letter-spacing:.06em}
.logo b{color:var(--amber)}
nav.top{display:flex;gap:24px;margin-left:auto;font:400 14px/1 var(--mono);letter-spacing:.12em;text-transform:uppercase}
nav.top a{color:var(--muted);text-decoration:none;padding-bottom:4px;border-bottom:1px solid transparent}
nav.top a:hover{color:var(--text);border-bottom-color:var(--amber)}
.lamp-top{display:flex;align-items:center;gap:8px;font:400 13px/1 var(--mono);letter-spacing:.12em;color:var(--muted);text-transform:uppercase}
.led{width:9px;height:9px;border-radius:50%;background:var(--mode);box-shadow:0 0 10px var(--mode-glow);transition:background .4s ease,box-shadow .4s ease}
html.js .led{animation:blink 2.8s infinite}
@keyframes blink{50%{opacity:.4}}

/* ---------- первый экран ---------- */
.hero{padding:56px 0 64px}
.hero .wrap{display:grid;grid-template-columns:.85fr 1.15fr;gap:44px;align-items:center}
.eyebrow{display:inline-flex;align-items:center;gap:10px;font:400 13px/1 var(--mono);letter-spacing:.16em;text-transform:uppercase;color:var(--muted)}
.eyebrow::after{content:"";width:40px;height:1px;background:#3a3d42}
h1{margin:20px 0 0;font:800 clamp(28px,3.5vw,46px)/1.08 var(--sans);letter-spacing:-.025em;text-transform:uppercase}
h1 b{color:var(--amber);font-weight:800;text-shadow:0 0 30px rgba(255,166,32,.3)}
.lead{margin:18px 0 0;color:var(--muted);font-size:16.5px;max-width:42ch}
.spec{margin-top:24px;border-top:1px solid #2b2d31;border-bottom:1px solid #2b2d31;display:grid;grid-template-columns:repeat(3,1fr)}
.spec>div+div{border-left:1px solid #2b2d31}
.spec div{padding:11px 14px;border-right:1px solid #2b2d31}
.spec div:last-child{border-right:0}
.spec s{display:block;text-decoration:none;font:400 12px/1.6 var(--mono);letter-spacing:.14em;text-transform:uppercase;color:var(--dim)}
.spec b{font:700 16.5px/1.4 var(--mono)}
.cta-row{margin-top:24px;display:flex;gap:12px;align-items:center;flex-wrap:wrap}
.btn{display:inline-block;font:700 14.5px/1 var(--mono);letter-spacing:.11em;text-transform:uppercase;text-decoration:none;
  padding:15px 22px;border-radius:4px;border:1px solid #3c3f45;color:var(--text);
  background:linear-gradient(180deg,#26282d,#191b1f);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.12),0 4px 0 #08090a;transition:transform .1s ease,filter .2s ease}
.btn:hover{filter:brightness(1.15)}
.btn:active{transform:translateY(3px);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 #08090a}
.btn.primary{color:#1a1405;border-color:#b8781a;background:linear-gradient(180deg,#ffc45c,#e09015 46%,#b8781a);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.65),0 4px 0 #6d4708}
.btn.primary:active{transform:translateY(3px);box-shadow:inset 0 1px 0 rgba(255,255,255,.5),0 1px 0 #6d4708}
.hint{font:400 13px/1.5 var(--mono);color:var(--dim);letter-spacing:.05em}

/* ---------- ПАНЕЛЬ (фотография + живые слои) ---------- */
.rigwrap{position:relative}
.rig{position:relative;width:100%;aspect-ratio:1400/785;filter:drop-shadow(0 30px 60px rgba(0,0,0,.75))}
.rig img{width:100%;height:100%;display:block;border-radius:6px}
.ov{position:absolute}

/* дисплей */
.glass{left:16.5%;top:18%;width:67.5%;height:23%;display:flex;align-items:center;gap:3%;padding:2% 3%}
/* Подпись обязана ПОМЕЩАТЬСЯ в поле дисплея: при 7.5px/1.5 четыре строки давали 45px при поле
   дисплея 41.6px (390) и 37.8px (360) — подпись упиралась в серебристую рамку (дефект task-11).
   line-height 1.25 и запас по кеглю ниже — чтобы четыре строки гарантированно были внутри. */
.glass .cap{flex:0 0 32%;min-width:0;font:400 clamp(7px,.86vw,11.5px)/1.25 var(--mono);letter-spacing:.11em;text-transform:uppercase;color:#8b8880}
/* Узкие экраны — основной случай дефекта: кегль и трекинг меньше, чтобы подпись не касалась рамки. */
@media (max-width:520px){.glass .cap{font-size:7px;line-height:1.22;letter-spacing:.08em}}
/* `padding-bottom` резервирует место под «прогонов»: `small` выведен из потока (ниже), поэтому
   низ крупной строки «/ 10» совпадает с низом svg.seg, а хвост остаётся ВНУТРИ поля.
   Значение 4px (было 16px) — из замера по ПИКСЕЛЯМ ФОНА (task-18): при 16px цифра упиралась в
   верхнюю панель (зазор 7–8% поля). Место под «прогонов» добирает нижний padding самого
   `.glass` (6.8–15.1px в зависимости от ширины), поэтому 4px достаточно: хвост остаётся в поле. */
.glass .val{flex:1;height:100%;display:flex;align-items:flex-end;justify-content:center;gap:7px;padding-bottom:4px}
/* ЯВНАЯ высота цифры, а не 100%: при `height:100%` svg растягивается на весь content-box и его верх
   всегда совпадает с верхом `.val` — цифра не опускается, а растёт (проверено измерителем).
   77% даёт зазор сверху 21–24% поля и снизу 22–28% (у образца владельца 20/26). */
.seg{height:77%;width:auto;display:block}
.seg .on{fill:var(--mode);filter:drop-shadow(0 0 6px var(--mode-glow))}
.seg .off{fill:#241f19}
/* inline-block + relative: `small` позиционируется от бокса самой строки «/ 10», а не от `.val`. */
.of{position:relative;display:inline-block;font:700 clamp(11px,1.5vw,20px)/1 var(--mono);color:var(--mode);text-shadow:0 0 12px var(--mode-glow);margin-bottom:1px}
/* `small` — ВНЕ потока: раньше он добавлял высоту под крупной строкой, `align-items:flex-end`
   поднимал «10» на высоту хвоста, и низ «10» расходился с низом «6» на ~15px (дефект task-11). */
.of small{position:absolute;top:100%;left:50%;transform:translateX(-50%);white-space:nowrap;display:block;
  font:400 clamp(7px,.8vw,10.5px)/1.3 var(--mono);letter-spacing:.14em;color:#8b8880;margin-top:1px}

/* подписи под тумблерами */
.rlbl{position:absolute;transform:translateX(-50%);width:16%;text-align:center;
  font:700 clamp(8.5px,1.18vw,13.5px)/1.25 var(--mono);letter-spacing:.06em;text-transform:uppercase;
  color:#e8dccd;text-shadow:0 1px 0 rgba(0,0,0,.85);pointer-events:none;transition:color .3s ease}
html[data-mode="read"]  .rlbl[data-m="read"],
html[data-mode="write"] .rlbl[data-m="write"],
html[data-mode="full"]  .rlbl[data-m="full"]{color:#fff;text-shadow:0 0 12px var(--mode-glow),0 1px 0 rgba(0,0,0,.85)}

/* тумблеры: зона нажатия + подсветка выбранного */
.rbtn{position:absolute;transform:translateX(-50%);width:11%;height:26%;background:none;border:0;cursor:pointer;border-radius:8px;
  padding:0;transition:box-shadow .3s ease,background .3s ease}
.rbtn:hover{box-shadow:0 0 0 2px rgba(255,166,32,.35)}
.rbtn:focus-visible{outline:2px solid var(--amber);outline-offset:3px}
html[data-mode="read"]  .rbtn[data-m="read"],
html[data-mode="write"] .rbtn[data-m="write"],
html[data-mode="full"]  .rbtn[data-m="full"]{background:radial-gradient(60% 55% at 50% 50%,var(--mode-glow),transparent 72%)}

/* живые индикаторы в прорезях */
.slots{inset:0;pointer-events:none}
.slots span{position:absolute;background:#0a0a0a;border-radius:12%;transition:background .35s ease,box-shadow .35s ease}
html[data-mode="read"]  .slots[data-m="read"] span,
html[data-mode="write"] .slots[data-m="write"] span,
html[data-mode="full"]  .slots[data-m="full"] span{background:var(--mode);box-shadow:0 0 10px var(--mode-glow),inset 0 1px 0 rgba(255,255,255,.5)}

/* ---------- журнал ---------- */
.rigfoot{display:flex;gap:22px;align-items:flex-start;margin-top:22px}
.verdict{flex:1;padding:14px 16px;border-radius:5px;border:1px solid #2b2d31;border-left:3px solid var(--mode);
  background:linear-gradient(180deg,#17191d,#121316);font-size:15.5px;transition:border-color .4s ease}
.verdict b{color:var(--mode)}
.logbox{flex:1.1;border-radius:5px;overflow:hidden;border:1px solid #26282c;background:#0a0b0d}
.logbox .h{padding:9px 13px;border-bottom:1px solid #24262a;font:400 12px/1 var(--mono);letter-spacing:.14em;text-transform:uppercase;color:var(--dim)}
.logbox ul{list-style:none;margin:0;padding:11px 13px;display:flex;flex-direction:column;gap:8px;font:400 14px/1.5 var(--mono)}
.logbox li{display:grid;grid-template-columns:50px 15px 1fr;gap:8px;align-items:baseline;opacity:0;transform:translateY(4px);transition:opacity .3s ease,transform .3s ease}
.logbox li.in{opacity:1;transform:none}
/* Весь мелкий текст журнала приведён к одному тону #8b8880. Так сделаны метки времени (.ts),
   текст шага (.ms) и знак (.ic). Тише .ts сделать нельзя: светлейшая его подложка #191b1f,
   и на ней AA требует не темнее #8b8880 (4.87). .ic был последним, кто оставался темнее —
   #6f6c66 давал 3.72 и был единственным элементом ниже порога во всём замере отрисованного
   (114 текстовых элементов, каскад плюс фон по пикселям). Содержательные состояния знака
   красятся отдельными правилами ниже и без этого: .stop → --bad, .silent → --amber,
   .work → --amber, .ok → --ok. */
.logbox li .ts{color:#8b8880}
.logbox li .ic{color:#8b8880}
.logbox li .ms{color:#8b8880;min-width:0}
.logbox li.hot .ms{color:var(--text)}
.logbox li.work .ic{color:var(--amber)}
.logbox li.ok .ic{color:var(--ok)}
.logbox li.answer .ms{color:var(--text)}
.logbox li.answer{border-left:2px solid var(--amber);padding-left:8px}
.logbox li.stop .ic,.logbox li.stop .ms{color:var(--bad)}
.logbox li.silent .ic,.logbox li.silent .ms{color:var(--amber)}
/* R1: журнал рисуется из настоящих трасс. `raw` — короткая машинная строка из журнала
   (в том числе английский текст отказа песочницы): она уезжает под подпись, в третью колонку,
   и не ломает сетку строки. */
.logbox li .raw{grid-column:3;font-size:12px;color:#8b8880;word-break:break-word}
.logbox li.denied .ic,.logbox li.denied .ms{color:var(--bad)}
.logbox li.ok .ic,.logbox li.ok .ms{color:var(--ok)}
.logbox li.artifact .ic,.logbox li.artifact .ms{color:var(--amber);font-weight:700}
.logbox li.refused .ic,.logbox li.refused .ms{color:var(--amber)}
.logbox li.note .ms{color:var(--text);border-left:2px solid var(--amber);padding-left:8px}
.scen{display:flex;flex-wrap:wrap;gap:8px;margin:16px 0 6px}
.scen-btn{font:700 12.5px/1.2 var(--mono);letter-spacing:.04em;text-transform:uppercase;
  padding:9px 12px;border-radius:4px;cursor:pointer;color:var(--muted);
  background:linear-gradient(180deg,#1c1e22,#141619);border:1px solid #34373d}
.scen-btn:hover{color:var(--text);filter:brightness(1.15)}
.scen-btn.selected{color:#1a1405;border-color:#b8781a;background:linear-gradient(180deg,#ffc45c,#e09015 46%,#b8781a)}
.srcnote{font:400 13px/1.6 var(--mono);color:var(--dim);margin-top:14px}

/* ---------- секции ---------- */
section{padding:78px 0;border-top:1px solid #232529}
h2{margin:0 0 10px;font:800 clamp(21px,3vw,32px)/1.14 var(--sans);letter-spacing:-.02em;text-transform:uppercase}
h2 b{color:var(--amber)}
.sub{margin:0 0 32px;color:var(--muted);max-width:56ch;font-size:17px}
.two{display:grid;grid-template-columns:1.6fr 1fr;gap:20px;align-items:start}
.pane{border:1px solid #2b2d31;border-radius:5px;background:linear-gradient(180deg,#191b1f,#131417);overflow:hidden}
.pane-head{display:flex;align-items:center;gap:11px;padding:12px 16px;border-bottom:1px solid #26282c;background:#0e0f11;
  font:400 13.5px/1 var(--mono);letter-spacing:.12em;text-transform:uppercase;color:var(--muted)}
.pane-head .who{color:var(--text);font-weight:700}
.thread{padding:17px 16px;display:flex;flex-direction:column;gap:12px}
/* Окна разговора и шагов — прокручиваемые области ограниченной высоты: новый
   ответ и новый шаг обязаны быть видны сразу, а к предыдущим можно вернуться
   прокруткой (автопрокрутку делает assets/app.js). Без этого блок рос вниз, и
   новый текст уезжал за нижнюю границу экрана. */
/* Высота карточки — прежняя, десктоп не трогаем. Окно разговора не сжимается в остаток
   не из-за высоты карточки: в мобильном блоке ниже окну задана определённая высота,
   а прокрутку к началу ответа чинит assets/app.js (распорка с flex:none и расчёт по
   фактическому запасу прокрутки). */
.two .pane{height:clamp(340px,48vh,470px);display:flex;flex-direction:column}
.two .pane .thread{flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain}
/* Прокручивается САМ СПИСОК `#steps`, а не его родитель: автопрокрутка в app.js
   ставит scrollTop у `#steps`. Раньше overflow висел на `.logbox`, список был
   нерастянутой высоты, и `steps.scrollTop = steps.scrollHeight` не делал ничего —
   окно стояло на месте, и заказчик прокручивал вручную. Проверка это не ловила,
   потому что мерила scrollTop того же не-скроллящегося списка (ложный зелёный). */
.two .pane .logbox{flex:1;min-height:0;display:flex;flex-direction:column;overflow:hidden}
.two .pane #steps{flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain}
.msg{max-width:93%;padding:12px 15px;font-size:16px;line-height:1.6;border-radius:4px;background:#1c1e22;border:1px solid #2d3035}
.msg.b{border-left:3px solid var(--amber)}
.msg .num{font-family:var(--mono);font-weight:700;color:var(--amber)}
.composer{display:flex;gap:10px;padding:12px 16px;border-top:1px solid #26282c;background:#0e0f11}
.composer input{flex:1;min-width:0;font:400 15.5px var(--sans);padding:11px 13px;border-radius:3px;background:#0a0b0d;border:1px solid #2b2d31;color:var(--dim)}
.composer button{font:700 13.5px/1 var(--mono);letter-spacing:.1em;text-transform:uppercase;padding:11px 17px;border-radius:3px;background:#22242a;border:1px solid #34373d;color:var(--dim)}

.lanes{display:grid;grid-template-columns:1fr 1fr;gap:20px}
.lane{padding:26px;border-radius:6px;border:1px solid #2b2d31;background:linear-gradient(180deg,#191b1f,#131417);transition:filter .2s ease}
.lane:hover{filter:brightness(1.12)}
.lane .no{font:700 12.5px/1 var(--mono);letter-spacing:.18em;color:var(--amber);display:block;margin-bottom:14px}
.lane h3{margin:0 0 10px;font:700 20.5px/1.3 var(--sans)}
.lane p{margin:0;color:var(--muted);font-size:16.5px}
/* R4: «что остаётся у вас» — блок результата, без цен и сроков (решение заказчика 2026-10-10).
   Тот же контур, что у полос выше, но список: три пункта, которые можно проверить, а не обещания. */
.deliv{margin-top:20px;padding:24px 26px;border-radius:6px;border:1px solid #2b2d31;background:linear-gradient(180deg,#141619,#101113)}
.deliv h3{margin:0 0 14px;font:700 18px/1.3 var(--sans);letter-spacing:-.01em}
.deliv ul{margin:0;padding-left:22px;color:var(--muted);font-size:16.5px}
.deliv li{margin-bottom:9px}
.deliv li:last-child{margin-bottom:0}
.deliv b{color:var(--text)}

.gauges{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;margin-bottom:24px}
.gauge{border-radius:6px;padding:26px 20px 22px;text-align:center;position:relative;overflow:hidden;
  background:linear-gradient(180deg,#0a0b0d,#111214);border:1px solid #2b2d31}
.gauge .n{font:700 clamp(44px,5.8vw,64px)/1 var(--mono);font-variant-numeric:tabular-nums}
.gauge.zero .n{color:var(--ok);text-shadow:0 0 12px rgba(143,191,106,.55),0 0 34px rgba(143,191,106,.3)}
.gauge.hot .n{color:var(--amber);text-shadow:0 0 12px var(--mode-glow),0 0 34px var(--mode-glow)}
.gauge .c{margin-top:11px;font:400 13px/1.6 var(--mono);color:var(--muted)}
.claim{margin:0;max-width:66ch;font-size:17.5px}
.claim b{color:var(--amber)}
.src{margin-top:14px;font:400 13px/1.7 var(--mono);color:var(--dim)}

.channels{display:flex;flex-wrap:wrap;gap:12px}
.chan{display:flex;flex-direction:column;gap:4px;padding:15px 21px;border-radius:4px;text-decoration:none;color:var(--text);
  font:700 15px/1 var(--mono);letter-spacing:.07em;text-transform:uppercase;
  background:linear-gradient(180deg,#26282d,#191b1f);border:1px solid #3c3f45;box-shadow:0 4px 0 #08090a;transition:transform .1s ease,filter .2s ease}
.chan small{font:400 12.5px/1.4 var(--mono);letter-spacing:.03em;text-transform:none;color:var(--dim)}
.chan:hover{filter:brightness(1.15)}
.chan:active{transform:translateY(3px);box-shadow:0 1px 0 #08090a}
.chan.primary{color:#1a1405;border-color:#b8781a;background:linear-gradient(180deg,#ffc45c,#e09015 46%,#b8781a);box-shadow:0 4px 0 #6d4708}
.chan.primary small{color:#5a3a06}

footer{padding:36px 0 56px;border-top:1px solid #232529;font:400 13.5px/1.7 var(--mono);color:var(--dim)}
footer a{color:var(--muted)}
.disc{margin:12px 0 0;max-width:72ch}

@media (prefers-reduced-motion:reduce){*{animation:none!important;transition:none!important}}
@media (max-width:1000px){
  .hero .wrap{grid-template-columns:1fr;gap:30px}
  .two,.lanes{grid-template-columns:1fr}
  .gauges{grid-template-columns:1fr}
  nav.top{display:none}
  section{padding:54px 0}
  .rigfoot{flex-direction:column}
  .spec{grid-template-columns:1fr;border-bottom:0}
  .spec div{border-right:0;border-bottom:1px solid #2b2d31}
  /* Карточки встают друг под друга и растут под содержимое. Окнам разговора и шагов
     задаём ОПРЕДЕЛЁННУЮ высоту, а не минимум: только у такого окна работает прокрутка
     к началу свежего ответа (assets/app.js), и только так разговор на телефоне
     перестаёт сжиматься до остатка в 119 px — двух строк ответа. */
  .two .pane{height:auto;min-height:0}
  .two .pane .thread{flex:none;height:clamp(240px,42vh,340px)}
  .two .pane .logbox{flex:none;height:clamp(240px,42vh,340px)}
}

/* assets-extra.css — только то, чего нет в макете.
   panel-template.html — источник макета и не правится; стили разговора (.msg, .msg.b,
   .logbox li, .ts/.ic/.ms, .thread, .composer) в нём УЖЕ есть. Здесь добавляем ровно то,
   чего макет не знал: прокрутку живого журнала, пустое состояние строки шагов и вид
   ВКЛЮЧЁННОЙ формы (макет рисовал выключенную). */

/* Схема тёмная, а браузер по умолчанию рисует СВЕТЛУЮ полосу прокрутки. Снимок в браузере
   это и показал: правые 15px во всю высоту — яркость 235 из 255 на фоне страницы 12.
   Объявляем тёмную схему — иначе на тёмной странице белая полоса. */
html{color-scheme:dark}

/* Журнал разговора: длинный разговор прокручивается, а не растягивает страницу. */
#log{max-height:min(54vh,460px);overflow-y:auto;overscroll-behavior:contain;
  scrollbar-width:thin;scrollbar-color:#3a3d42 transparent}
#log::-webkit-scrollbar{width:8px}
#log::-webkit-scrollbar-thumb{background:#3a3d42;border-radius:4px}

/* Пустое состояние строки шагов: пока хода не было, место не выглядит сломанным. */
#steps:empty{padding:11px 13px}
#steps:empty::before{content:"пока ничего не происходит — напишите сообщение";
  color:var(--dim);font:400 14px/1.5 var(--mono)}

/* Форма разговора. Шаблон задаёт display:flex в одну строку; у живой формы четыре
   ребёнка (подпись, поле, кнопка, примечание), поэтому без переноса они уехали бы
   в одну строку с полем. Поле и кнопка включены — значит, ярче выключенных из макета. */
.composer{flex-wrap:wrap;align-items:center;row-gap:8px}
.composer label.hint{flex:1 0 100%;margin:0}
.composer input{flex:1 1 220px;color:var(--text);border-color:#34373d}
.composer input::placeholder{color:var(--dim)}
.composer input:focus-visible{outline:2px solid var(--amber);outline-offset:1px;border-color:var(--amber)}
.composer button{color:var(--text);background:#2c2f35;border-color:#3f434a;cursor:pointer;
  transition:filter .2s ease,transform .1s ease}
.composer button:hover{filter:brightness(1.2)}
.composer button:focus-visible{outline:2px solid var(--amber);outline-offset:2px}
.composer button:active{transform:translateY(1px)}
.composer p.hint{flex:1 0 100%;margin:0}
.composer p.hint a{color:var(--muted)}

/* ОКНО РАЗГОВОРА НЕЛЬЗЯ СЖАТЬ НИЖЕ ЧИТАЕМОГО — НО ТОЛЬКО ТАМ, ГДЕ ЕГО СЖИМАЕТ ФОРМА.
   Оно объявлено как flex:1 с min-height:0, то есть «отдай всю высоту, что попросят соседи».
   Форма «Продолжить с человеком» (флаг IDENTIFY_ENABLED) добавляется ВНУТРЬ панели с жёсткой
   высотой, и окно сжалось до 34px — меньше одной строки; измерено на бою при 1834×736:
   панель 353px, форма 279px, композер 142px, окно 34px.

   ПОЧЕМУ ПРАВИЛО ПРИВЯЗАНО К :has(.identify), А НЕ ДЕЙСТВУЕТ ВСЕГДА. В проекте УЖЕ есть страж
   ровно этого класса: самопроверка `squeeze-thread` (tools/make-broken.mjs) сжимает окно до
   60px через `#talk .thread{height:60px!important}` и требует, чтобы проверка это поймала.
   Безусловный `min-height` на окне БЬЁТ такой `height` (min-height в CSS сильнее height) —
   и страж перестаёт ловить сжатие: самопроверка так и сказала, «поломка не поймана, проверка
   бутафория». Поэтому минимум включается ТОЛЬКО когда в панели есть форма: без формы поведение
   прежнее, и страж продолжает работать. Проверено прогоном самопроверки — все поломки пойманы. */
@media (min-width:1001px){
  .two .pane:has(.identify){height:auto;min-height:clamp(340px,48vh,470px)}
  .two .pane:has(.identify) .thread{min-height:clamp(260px,34vh,420px)}
}

/* Реплика появляется мягко. При prefers-reduced-motion шаблон уже гасит анимации. */
#log .msg{animation:msg-in .18s ease-out both}
@keyframes msg-in{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:none}}

@media (max-width:1000px){
  #log{max-height:none}
  .composer input{flex:1 1 100%}
  .composer button{flex:0 0 auto}
}

/* Кнопка «Почта»: `mailto:` на устройстве без почтовой программы не делает ничего, поэтому
   клик дополнительно копирует адрес, а эта строка объясняет поведение и объявляет результат
   (aria-live в разметке). Текст виден ВСЕГДА — пустой скрытый элемент не прошёл бы браузерный
   смоук контракта, и, главное, молчащая кнопка снова выглядела бы сломанной. */
.chan-note{margin:12px 0 0;font:400 13px/1.5 var(--mono);letter-spacing:.02em;color:var(--dim)}

/* Выбранный канал: нажатие видно и после отпускания. Янтарная ЗАЛИВКА — признак главной кнопки
   (её носит «Агент на сайте»), поэтому выбор показываем РАМКОЙ: иначе два разных смысла
   спорили бы за один и тот же признак. Отступ 3px отделяет рамку от самой кнопки. */
.chan.selected{outline:2px solid var(--amber);outline-offset:3px}

/* Форма идентификации по контакту. В ЗАКОММИЧЕННОЙ странице её нет: разметка вставляется
   при отдаче, только когда включён IDENTIFY_ENABLED (src/identify-page.js). Стили лежат
   здесь, а не инлайном: CSP разрешает style-src 'self', инлайновые стили в проекте не держим.
   Шаг 2 (ввод кода) скрыт до класса is-open; успех первого шага ставит класс из identify.js. */
/* Внутренние отступы: рамку блоку даёт родитель .pane, у которого padding нет, поэтому
   заголовок и строка согласия стояли вплотную к рамке (задача 22). */
.identify{margin-top:14px;padding:12px 16px;display:flex;flex-direction:column;gap:9px;align-items:stretch}
/* Форму шага 1 прячет `hidden` после отправки кода. Явное правило нужно: авторский
   display:flex перебил бы браузерное [hidden]{display:none}. */
.identify[hidden]{display:none}
.identify-h{margin:0;font:600 15px/1.4 var(--mono);color:var(--text)}
.identify label.hint{margin:0}
.identify input[type=text]{color:var(--text);background:#0a0b0d;border:1px solid #34373d;
  border-radius:6px;padding:9px 11px;font:400 14px/1.4 var(--mono)}
.identify input[type=text]::placeholder{color:var(--dim)}
.identify input[type=text]:focus-visible{outline:2px solid var(--amber);outline-offset:1px;border-color:var(--amber)}
.identify-consent{display:flex;align-items:flex-start;gap:8px;font:400 13px/1.5 var(--mono);color:var(--muted)}
.identify-consent input{margin:2px 0 0}
.identify-consent a{color:var(--muted)}
.identify button{color:var(--text);background:#2c2f35;border:1px solid #3f434a;border-radius:6px;
  padding:9px 14px;font:500 14px/1 var(--mono);cursor:pointer;align-self:flex-start;
  transition:filter .2s ease,transform .1s ease}
.identify button:hover{filter:brightness(1.2)}
.identify button:focus-visible{outline:2px solid var(--amber);outline-offset:2px}
.identify button:active{transform:translateY(1px)}
.identify button[disabled]{opacity:.6;cursor:default}
.identify-confirm{display:none}
.identify-confirm.is-open{display:flex}

/* ---- живой прогон (R1, этап 4, шаг 3в) -----------------------------------------------
   Кнопка живого показа стоит рядом с селектором сценария. Стили держим в той же
   тёмной палитре панели: живой режим — не второй интерфейс, а второй источник строк
   в том же журнале. */
.livebar{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin:10px 0 2px}
.live-btn{color:var(--text);background:#2c2f35;border:1px solid #3f434a;border-radius:6px;
  padding:8px 13px;font:500 13px/1 var(--mono);cursor:pointer;
  transition:filter .2s ease,transform .1s ease}
.live-btn:hover{filter:brightness(1.2)}
.live-btn:focus-visible{outline:2px solid var(--amber);outline-offset:2px}
.live-btn:active{transform:translateY(1px)}
.live-hint{font:400 12px/1.4 var(--mono);color:var(--dim)}
/* Отказ живого показа — отдельный вид строки журнала (`li.stop` уже есть), а не успех. */
#feed li.stop{color:var(--stop, #e06c75)}
