/* Лендинг iris.direct — страница-разговор.
   Токены берём из tokens.css (генерируется из theme.ts), утилиты — из iris-ds.css.
   Своих цветов и шрифтов здесь нет: любой литерал цвета — признак ошибки, кроме
   градиентов-стёкол, где нужна прозрачность поверх фотографии.

   Облик собранного интерфейса приходит переменными --iris-* от генеративной темы
   @iris/core, и дальше сетки вокруг пульта (.grid) он не уходит: страница вокруг
   остаётся нашей всегда (решение founder-а 10.08). */

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

:root {
  /* Цвет шва между дисплеем и пультом: он один на оба контура. */
  --seam: color-mix(in srgb, var(--accent) 30%, transparent);

  /* ── СВЕТ, КОТОРЫЙ СТОИТ ДОРОГО ──────────────────────────────────────────
     Дешёвый неон — это одно размытое пятно вокруг всего. Дорогой собирается
     СЛОЯМИ и держит край: резкая цветная линия сверху, короткий цветной отброс
     под ней, глубокая мягкая тень третьей.
     Токенов `--lit-*` здесь больше нет: слоями светились элементы, а они стали
     полосами сетки — плоскими, с кромками из линий (уточнение founder-а 11.08).
     Светятся ДЕЙСТВИЯ: гнездо отправки, кнопка пилота, образцы ответов и пузырь
     человека — каждый набирает слои у себя, ниже по файлу. */
  /* ── ГАРНИТУРА ────────────────────────────────────────────────────────────
     Sora НЕ СОДЕРЖИТ КИРИЛЛИЦЫ. Весь русский текст на этой странице рисовался
     системным фолбэком — то есть гарнитуру для него никто и не выбирал, отсюда
     и ощущение дешёвого, чужого шрифта (разбор founder-а 11.08). Onest —
     геометрический гротеск того же характера, но с полноценной кириллицей:
     теперь и русский, и английский набраны одним рисунком.
     Переопределяем ЗДЕСЬ, а не в theme.ts: канон приложения остаётся на Sora,
     это решение лендинга. */
  --font-ui: 'Onest', system-ui, -apple-system, sans-serif;
  --font-display: 'Onest', system-ui, -apple-system, sans-serif;

  /* Поле страницы: одно число ведёт шапку, герой, блоки и пульт — они обязаны
     стоять на одной вертикали, иначе пульт «плавает» относительно заголовка. */
  --gutter: clamp(16px, 4.4vw, 72px);
  /* Эталон макета: 1440 с полями 72 — значит содержимое 1296. Без потолка на
     широком мониторе пульт растягивается вдвое против макета. */
  --content-max: 1296px;
  --dock-h: 100px;
  /* Ширина КОРПУСА — дисплей и пульт как одна вещь. Одно число ведёт среднюю
     колонку консоли, пульт и нижнюю ветку сетки: разойдутся — сегменты сетки
     перестанут стоять по краям корпуса, и провода к нему уткнутся в пустоту. */
  /* Корпус — ТЕЛЕФОН 9:16 (решение founder-а 14.08): область тред+пульт держит
     портретную пропорцию, ширина выводится из высоты окна. Пара к этой формуле —
     min-height дисплея ниже: вместе они дают (ширина):(дисплей+пульт) = 9:16.
     На телефоне формула не мешает: вьюпорт уже и сам ограничивает ширину. */
  --corpus: clamp(320px, calc((100svh - 48px) * 9 / 16), 560px);
  /* Сколько содержимого держит полоса сетки рядом с корпусом. Заливка полосы
     идёт до края экрана, а текст дальше этого потолка от пульта не уходит:
     440 — ровно колонка эталонных 1440 ((1440 − 560) / 2), поэтому на макетной
     ширине потолок не срабатывает и раскладка остаётся прежней. */
  --seg: 420px;
  /* Боковой отступ содержимого — один на элемент и на пустой сегмент: полосы
     обязаны набираться по одной вертикали, иначе сетка «дрожит» по краю. */
  --pad-x: clamp(13px, 1.1vw, 17px);
  --ink: #EEF4F8;
  --ink-2: #98A5B0;
  --ink-3: #68737E;
}

/* scroll-behavior: smooth НА КОРНЕ НЕ СТАВИМ. Она делает плавным КАЖДУЮ
   программную прокрутку, включая ту, которой лента следует за печатью, — и два
   ведущих начинают тянуть страницу в разные стороны: экран прыгает и уезжает в
   пустоту. Плавность запрашивается точечно, там где она осмысленна. */
html, body { margin: 0; }
body {
  background: var(--aurora-base-1);
  color: var(--ink);
  font-family: var(--font-ui);
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
}
a { color: var(--accent); text-decoration: none; }
a:hover { color: color-mix(in srgb, var(--accent) 70%, white); }
::selection { background: color-mix(in srgb, var(--accent) 30%, transparent); }
.sr {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.said { display: none; }

/* ── Фон ──────────────────────────────────────────────────────────────────
   Фотографии города здесь больше нет (решение founder-а 11.08). Она забирала
   внимание у единственного, что на этом экране имеет значение, — у вопроса,
   который задаёт Айрис. Остаётся глубина: два мягких пятна фирменного света и
   темнота. Растр не грузится вовсе — первый экран стал легче на четверть мегабайта. */
.scene { position: fixed; inset: 0; z-index: 0; overflow: hidden; pointer-events: none; }
/* Свет сцены берёт тон интента, как только он назван: человек написал — и мир
   вокруг пульта отозвался, ещё до того как соберётся первый элемент. Токены
   --tint ставит tint() из посчитанной палитры; до этого светят фирменные. */
.scene-veil {
  position: absolute; inset: 0;
  --l1: var(--tint, var(--accent));
  --l2: var(--tint-2, var(--like));
  background:
    radial-gradient(1100px 760px at 18% -6%, color-mix(in srgb, var(--l1) 10%, transparent), transparent 64%),
    radial-gradient(940px 720px at 88% 104%, color-mix(in srgb, var(--l2) 9%, transparent), transparent 62%),
    radial-gradient(760px 620px at 92% 12%, color-mix(in srgb, var(--l1) 8%, transparent), transparent 60%);
  transition: background 1.1s var(--ease-out), opacity .7s var(--ease-out);
}
/* Отозвалась сцена — отзывается и обводка пульта: он перестаёт переливаться
   фирменной радугой и берёт цвет того, что человек описал. */
body.tinted #dockStroke stop { animation: none; }
body.tinted #dockStroke stop:nth-child(1) { stop-color: var(--tint); }
body.tinted #dockStroke stop:nth-child(2) { stop-color: var(--tint-2); }
body.tinted #dockStroke stop:nth-child(3) { stop-color: var(--tint); }
.scene-photo, .scene-arcs { display: none; }

/* ── Поток страницы ───────────────────────────────────────────────────────
   Ниже сцены — обычная длинная страница: те же тексты прозой, для тех кто
   крутит, и для краулера. Сама она не приезжает и никуда не тянет. */
.page {
  position: relative; z-index: 1;
  padding-inline: max(var(--gutter), (100% - var(--content-max)) / 2);
  padding-bottom: clamp(28px, 5vh, 56px);
}

/* ── ТАКТЫ ПЕРВОГО ЭКРАНА ────────────────────────────────────────────────
   hush  — тишина: пусто, только пульт едва дышит из темноты. Секунда без единого
           слова нужна, чтобы следующая фраза читалась как обращение, а не как
           очередной баннер, встретивший на входе.
   talk  — Айрис написала; пульт по центру, вокруг по-прежнему пусто.
   built — вокруг пульта встала сетка его процесса, и сегменты заполняются.
   Ниже сцены лежит обычная длинная страница — для тех, кто крутит, и для
   краулера. Сама она не приезжает: тянуть человека вниз здесь нечем. */

/* Шапка и пульт — КАРКАС сцены, а не её содержимое: они стоят с первого кадра и
   не «появляются». Плавно возникающий пульт читался как ещё одна анимация в
   очереди, хотя это рама, внутри которой всё и происходит (разбор 11.08). */
/* ОДИН ЭКРАН, БЕЗ ПРОКРУТКИ — требование founder-а с самого начала. Длинная
   страница никуда не делась: она в разметке (её читает краулер и человек без JS)
   и открывается отдельным режимом чтения по ссылке под пультом. Прокрутка в
   консоли не отключена «на всякий случай» — её там просто нечего прокручивать. */
body.js:not([data-act="read"]) { overflow: hidden; height: 100svh; }
/* ШАПКА — ЧАСТЬ ИНТЕРФЕЙСА, А НЕ ПЛЁНКА НАД НИМ. Сцена ростом в целый экран
   стояла ПОД шапкой: суммарно выходило экран плюс шапка, и верх разговора уезжал
   ей за спину — коробка соприкасалась с ней и обрезалась (разбор founder-а
   11.08). Теперь страница — настоящая колонка: шапка занимает своё место, сцена
   получает ровно остаток, и залезть друг на друга им нечем. */
/* Поля страницы в консоли НЕ ДЕЙСТВУЮТ: сетка обязана доходить до края экрана,
   а поле оставляло между полосой и краем пустую рамку — полоса снова читалась
   карточкой. Полям есть где работать: они держат режим чтения и телефон. */
body.js:not([data-act="read"]) .page {
  height: 100svh; display: flex; flex-direction: column;
  padding-inline: 0; padding-bottom: 0;
}
/* Сцена берёт высоту от страницы, а не требует её себе. Пока у неё стояло
   min-height:100svh, к целому экрану прибавлялись её же поля — выходило больше
   окна, и верх разговора вылезал за потолок браузера. Теперь высоту задаёт
   колонка страницы, а сцена в неё вписывается. */
body.js:not([data-act="read"]) .stage { flex: 1 1 auto; min-height: 0; height: auto; }
body.js:not([data-act="read"]) .hero,
body.js:not([data-act="read"]) .acts,
body.js:not([data-act="read"]) .foot { display: none; }
body.js[data-act="read"] .stage { display: none; }
/* Шапка возвращается вместе с первой репликой, остальная страница — молча
   стоит внизу и ждёт прокрутки. */


/* ── Сцена: пульт в центре, сетка интерфейса вокруг ──────────────────────
   Пустота вокруг пульта — не «недоделали», а место, куда встанет сетка того,
   что человек своими ответами создаёт. */
.stage {
  position: relative; z-index: 2;
  min-height: 100svh;   /* только для режима чтения, где страница прокручивается */
  /* Сцена не РЕЖЕТ содержимое, а прокручивает его, если оно физически не влезло.
     На зуме 150% и на низком окне «один экран» превращался в обрезанный: до
     нижней половины было не добраться вовсе. В обычном окне прокручивать нечего,
     и полосы не появится. */
  overflow: hidden auto; overscroll-behavior: contain;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: clamp(10px, 1.6vh, 20px);
  padding: clamp(12px, 2vh, 24px) 0;
}
/* Плавная перекраска мира: --accent/--like зарегистрированы как цвета, и
   выбор схемы едет переходом по всей консоли, а не щелчком (разбор founder-а
   14.08: «смена цвета слишком неплавная»). */
@property --accent { syntax: '<color>'; inherits: true; initial-value: #27F0DE; }
/* В @property var() запрещён спекой — литерал обязан совпадать с --like из tokens.css. */
@property --like { syntax: '<color>'; inherits: true; initial-value: #FF2E8A; }
.console { transition: --accent .9s var(--ease-out), --like .9s var(--ease-out); }

.console {
  /* ПУЛЬТ РОВНО ПО ЦЕНТРУ ЭКРАНА. Верхняя и нижняя половины сцены равны (обе
     1fr), поэтому середина сетки совпадает с серединой окна независимо от того,
     сколько набралось разговора и сколько элементов внизу.
     Разговор сидит НА пульте (align-self: end) и растёт вверх, упираясь в
     потолок своей половины: при коротком он начинается в середине экрана, при
     длинном доходит до шапки и дальше прокручивается внутри себя. */
  flex: 1 1 auto;
  width: 100%; min-height: 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, var(--corpus)) minmax(0, 1fr);
  /* Половины НЕ равны. При равных на разговор оставалось 287 точек при 432
     нужных — сто сорок пять уезжало в прокрутку, и первый экран нельзя было
     прочесть (замер 11.08). Верх получает больше: там текст, ради которого
     человек пришёл; низ — элементы, которых сначала нет вовсе. */
  /* Нижняя половина берёт НЕ МЕНЬШЕ своего содержимого. Доля 0.52 описывает
     эталонные 900 точек; на 1280×720 — самом частом окне ноутбука — той же доли
     не хватало, и за экран уходило 37 точек ровно по замыкающему предложению
     (замер probe 11.08). Отрезать то, ради чего человек сюда пришёл, нельзя:
     недостающее забирает разговор, у него для этого есть своя прокрутка. */
  grid-template-rows: minmax(0, 1fr) auto minmax(min-content, 0.52fr);
  /* Боковые ветки сетки стоят РОВНО ПРОТИВ КОРПУСА — на строках дисплея и
     пульта, и ни на строку ниже. Пока они спускались в третью строку, нижний
     сегмент оказывался напротив пустоты, и провод к корпусу вести было
     неоткуда. Нижняя ветка идёт во всю ширину и сама держится ширины корпуса. */
  grid-template-areas:
    "left thread right"
    "left dock   right"
    "below below below";
  align-items: center; justify-items: center;
  /* ЗАЗОРА У СЕТКИ НЕТ. Сегмент упирается в корпус и тянется до края экрана —
     соединяет их не отрезок в пустоте, а сама линия сетки (разбор founder-а
     11.08: «карточку надо растянуть по горизонтали, начало от пульта»). */
  gap: 0;
}
/* Дисплей и пульт занимают общую полосу сцепки: SVG дисплея рисует заполнение
   над канонической кромкой, а SVG пульта кладёт на неё саму кромку. Без этого
   перекрытия две одинаковые ступени шли одна за другой. Глубину даёт paint()
   из seamAnchors(), поэтому здесь нет второй геометрии. */
.display { grid-area: thread; align-self: end; max-height: 100%; }
/* ТЕЛЕФОН 9:16 НА ДЕСКТОПЕ (решение founder-а 14.08). Дисплей разговорных
   тактов держит высоту так, чтобы стекло с пультом вместе давали портрет
   9:16 при ширине --corpus: пустота над короткой перепиской — экран
   мессенджера, а не недогруз. Ряд треда расширяется до содержимого
   (minmax(min-content, 1fr)) — иначе min-height дисплея вылезал бы из ряда. */
@media (min-width: 900px) {
  /* Консоль разговорных тактов — ровно вьюпорт, как на телефоне: ряд треда
     забирает весь рост, пульт стоит у нижней кромки, ход человека (вкладки)
     — под ним. Стекло тянется на весь ряд, а не по контенту: длинный разговор
     прокручивается ВНУТРИ треда и не выталкивает пульт за сгиб. */
  body.js:is(:not([data-stage]), [data-stage="hello"], [data-stage="talk"], [data-stage="theme"], [data-stage="format"], [data-stage="tabs"], [data-stage="assembled"], [data-stage="intent-simulation"], [data-stage="intent-brief"]) .console {
    grid-template-rows: minmax(0, 1fr) auto minmax(min-content, auto);
    height: calc(100svh - 2 * clamp(12px, 2vh, 24px));
    max-height: calc(100svh - 2 * clamp(12px, 2vh, 24px));
  }
  body.js:is(:not([data-stage]), [data-stage="hello"], [data-stage="talk"], [data-stage="theme"], [data-stage="format"], [data-stage="tabs"], [data-stage="assembled"], [data-stage="intent-simulation"], [data-stage="intent-brief"]) .display {
    height: 100%; max-height: 100%; min-height: 0;
    /* Разговор прижат к пульту, как в мессенджере: пустота остаётся сверху,
       а не между репликой и вводом. */
    justify-content: flex-end;
  }
  /* Плашка .who — шапка телефона: авто-отступ отталкивает её от прижатого
     к низу разговора к верхней кромке стекла. */
  body.js:is(:not([data-stage]), [data-stage="hello"], [data-stage="talk"], [data-stage="theme"], [data-stage="format"], [data-stage="tabs"], [data-stage="assembled"], [data-stage="intent-simulation"], [data-stage="intent-brief"]) .display .who {
    margin-bottom: auto;
  }
  body.js:is(:not([data-stage]), [data-stage="hello"], [data-stage="talk"], [data-stage="theme"], [data-stage="format"], [data-stage="tabs"], [data-stage="assembled"], [data-stage="intent-simulation"], [data-stage="intent-brief"]) .display .thread {
    max-height: none; min-height: 0; overflow-y: auto; overscroll-behavior: contain;
  }
}
.dock   { grid-area: dock; margin-top: calc(-1 * var(--couple-depth, 44px)); }
/* Ветки сетки прижаты к НИЖНЕЙ кромке пульта и растут вверх — тем же движением,
   что и разговор. Прижатые к центру, они разъезжались с корпусом на любой
   высоте окна. Ширину ветка берёт всю: от корпуса до края экрана. */
.grid-l { grid-area: left;  align-self: end; justify-self: stretch; }
.grid-r { grid-area: right; align-self: end; justify-self: stretch; }
.below {
  grid-area: below; align-self: start; justify-self: center;
  display: flex; flex-direction: column;
  min-height: 0; width: 100%; max-width: var(--corpus);
}

/* ── СЕТКА ИНТЕРФЕЙСА ─────────────────────────────────────────────────────
   Разбор founder-а 11.08: элемент, который собрал агент, появлялся в пустоте
   рядом с пультом — «в воздухе». Корень был в раскладке: три свободные колонки
   без собственной формы, куда карточки дописывались по счётчику.

   Теперь вокруг корпуса стоит СЕТКА: сегмент (.cell) на каждую часть процесса,
   которую назвала модель, в трёх ветках — слева, справа и под пультом. Пустой
   сегмент подписан именем своей части и ждёт; ответ человека его заполняет.

   ГЕОМЕТРИЯ (уточнение founder-а 11.08). Сегмент — не карточка рядом с пультом,
   а ПОЛОСА: начинается от корпуса и тянется до края экрана. Соединяют его с
   пультом не отрезок в пустоте, а перпендикулярные линии самой сетки — стойка
   вдоль корпуса (border ветки) и полки между сегментами (border-top сегмента).
   Зазоров в консоли поэтому нет: между полосой и корпусом нечему быть. */
.grid {
  display: flex; min-width: 0;
  /* Линия сетки берёт тон интента — как поверхность элемента и как свет сцены.
     Пока она светила фирменным, у любого процесса чертёж был один и тот же, а
     индивидуальность мы держим во всём, что собралось. --seam — запасной. */
  --rule: color-mix(in srgb, var(--iris-accent, var(--accent)) 26%, transparent);
}
/* Ветка не выше КОРПУСА: полосы росли от пульта на свою высоту и уходили выше
   разговора — сетка и тред жили каждый своей мерой (разбор founder-а 11.08).
   Меру считает paint() и кладёт в --corpus-h; полосы делят её между собой. */
.grid-l, .grid-r {
  flex-direction: column; justify-content: flex-end; width: 100%;
  max-height: var(--corpus-h, none); overflow: hidden;
}
.grid-l .cell, .grid-r .cell { flex: 0 1 auto; min-height: 0; overflow: hidden; }
/* Стойка сетки — линия по корпусу. Она же кромка сегментов: своей рамки у них нет. */
.grid-l { border-right: 1px solid var(--rule, var(--seam)); }
.grid-r { border-left: 1px solid var(--rule, var(--seam)); }
.grid-b { flex-direction: row; width: 100%; }
.grid-b .cell { flex: 1 1 0; }
.grid-b .cell + .cell { border-left: 1px solid var(--rule, var(--seam)); }

/* Полка: линия между сегментами одной ветки и верхняя кромка всей сетки. */
.cell {
  position: relative; min-width: 0; display: flex; flex-direction: column;
  /* Внутренняя кромка заканчивается внутри коробки. Border рисовался по центру
     границы и оставлял полупиксельные усики в T-стыках с боковыми стойками. */
  box-shadow: inset 0 1px 0 var(--rule, var(--seam));
}
.cell.on { padding: 0; }
/* Содержимое полосы ДЕРЖИТСЯ ПУЛЬТА, а заливка идёт до края экрана. Отступ, а не
   потолок ширины: отступ лежит ВНУТРИ коробки, поэтому фон под ним остаётся и
   полоса доходит до края. На эталонных 1440 разница — двадцать точек, на широком
   мониторе она и удерживает текст у корпуса, вместо того чтобы уводить его
   на полэкрана. */
.grid-l .cell .card { padding-left: max(var(--pad-x), calc(100% - var(--seg))); }
.grid-r .cell .card { padding-right: max(var(--pad-x), calc(100% - var(--seg))); }


/* ── ТРИ СПОСОБА СОБРАТЬ ЧАТ ─────────────────────────────────────────────
   Зона варианта — такая же полоса сетки, только её содержимое ещё не собрано, а
   предложено: имя, из каких частей будет собран чат, и строка «что изменится».
   Три зоны стоят в трёх ветках и различаются обликом — выбор варианта это и
   выбор доминанты (решение founder-а 11.08). */
.cell.opt { cursor: pointer; }
.cell.opt .card { gap: 10px; }

/* ── ПРЕВЬЮ ФОРМАТА: ТРИ РАЗНЫЕ ВЁРСТКИ ──────────────────────────────────
   Три карточки со списком имён частей отличались только доминантой, и человек
   не чувствовал, что соберётся РАЗНОЕ (разбор founder-а 11.08). Формат — это
   кто с кем говорит, и каждый рисуется своей вёрсткой: гостевой узкой колонкой
   как экран телефона, «люди с людьми» широкой лентой с участниками, кураторский
   — вопросом агента и подтверждением. Цвет остаётся, но он больше не единственное
   отличие. */
.pv { display: flex; flex-direction: column; gap: 7px; font-size: 12.5px; line-height: 1.35; }
.pv b { font-weight: var(--weight-regular); padding: 7px 10px; }
.pv em, .pv i {
  font-style: normal; font-family: var(--font-mono); font-size: 9px; letter-spacing: .16em;
  text-transform: uppercase; color: var(--iris-muted, var(--ink-3));
}

/* Гостевой — узкая колонка: один снаружи говорит с Айрис. */
.pv-guest { max-width: 260px; }
.pv-guest .pv-in {
  align-self: flex-end; max-width: 92%;
  background: var(--iris-accent, var(--accent)); color: var(--iris-on-accent, var(--on-accent));
}
.pv-guest .pv-iris {
  align-self: flex-start; max-width: 92%; padding-left: 0;
  color: var(--iris-accent, var(--accent));
}
.pv-guest .pv-field {
  margin-top: 2px; padding: 7px 10px; font-size: 11.5px;
  color: var(--iris-muted, var(--ink-3));
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--iris-accent, var(--accent)) 26%, transparent);
}

/* Люди с людьми — широкая лента: сверху кто в чате, ниже говорят они, а не мы. */
.pv-peers .pv-who {
  list-style: none; margin: 0 0 2px; padding: 0; display: flex; flex-wrap: wrap; gap: 4px 12px;
  font-family: var(--font-mono); font-size: 9px; letter-spacing: .14em; text-transform: uppercase;
  color: color-mix(in srgb, var(--iris-accent, var(--accent)) 70%, transparent);
}
.pv-peers .pv-a, .pv-peers .pv-b {
  max-width: 88%;
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--iris-accent, var(--accent)) 20%, transparent);
}
.pv-peers .pv-b { align-self: flex-end; }
.pv-peers .pv-note { display: flex; align-items: baseline; gap: 6px; text-transform: none; letter-spacing: .02em; font-size: 11px; font-family: var(--font-ui); }

/* Куратор — ведёт агент: его вопрос, подтверждение, оценка. */
.pv-curated .pv-iris { padding: 0; color: var(--iris-text, var(--ink)); }
.pv-curated .pv-check { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 5px; }
.pv-curated .pv-check li { display: flex; align-items: center; gap: 8px; font-size: 12px; color: var(--iris-muted, var(--ink-2)); }
.pv-curated .pv-check li::before {
  content: ''; width: 12px; height: 12px; flex-shrink: 0;
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--iris-accent, var(--accent)) 55%, transparent);
}
.pv-curated .pv-check li.on { color: var(--iris-text, var(--ink)); }
.pv-curated .pv-check li.on::before { background: var(--iris-accent, var(--accent)); }
.pv-rate { display: flex; gap: 4px; }
.pv-rate b {
  width: 22px; height: 22px; display: grid; place-items: center; padding: 0;
  font-size: 11px; color: var(--iris-muted, var(--ink-3));
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--iris-accent, var(--accent)) 30%, transparent);
}
.pv-rate b.on { background: var(--iris-accent, var(--accent)); color: var(--iris-on-accent, var(--on-accent)); }
/* КТО ТУТ И ЧЕМ ЗАНЯТ — то, ради чего человек карточку и читает. Раньше он
   видел имя варианта и две реплики, но не понимал, что будут делать люди
   (разбор founder-а 11.08). Роль слева, её дело справа. */
.opt-who { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 4px; }
.opt-who li { display: flex; align-items: baseline; gap: 8px; justify-content: space-between; font-size: 12.5px; }
.opt-who b { font-weight: var(--weight-semibold); color: var(--iris-text, var(--ink)); }
.opt-who span {
  font-family: var(--font-mono); font-size: 9.5px; letter-spacing: .1em; text-transform: uppercase;
  color: color-mix(in srgb, var(--iris-accent, var(--accent)) 72%, transparent); text-align: right;
}

.opt-t { font-family: var(--iris-font-heading, var(--font-display)); font-size: clamp(16px, 1.4vw, 19px); font-weight: 600; letter-spacing: -.03em; line-height: 1.2; }
.opt-g { font-style: normal; font-size: 12.5px; line-height: 1.45; color: var(--iris-muted, var(--ink-2)); }
/* Кнопка выбора — единственное действие зоны, и выглядеть должна действием. */
.opt-go {
  align-self: flex-start; font: inherit; font-family: var(--font-mono);
  font-size: 9.5px; letter-spacing: .16em; text-transform: uppercase; cursor: pointer;
  padding: 7px 13px; border-radius: var(--radius-pill);
  color: var(--iris-on-accent, var(--on-accent));
  background: var(--iris-accent, var(--accent)); border: 0;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .35);
}
.cell.opt:hover .card { box-shadow:
  inset 0 1px 0 color-mix(in srgb, var(--iris-accent, var(--accent)) 62%, transparent),
  inset 1px 0 0 color-mix(in srgb, var(--iris-accent, var(--accent)) 26%, transparent),
  inset -1px 0 0 color-mix(in srgb, var(--iris-accent, var(--accent)) 26%, transparent),
  inset 0 -1px 0 rgba(0, 0, 0, .5); }
/* Невыбранное не исчезает: сворачивается в подписанную полоску — передумать
   можно одним кликом, и видно, что мир мог быть другим. */

/* Чат человека — главная зона экрана, а не карточка наравне с подробностями:
   он выбрал его глазами и должен продолжать его видеть (разбор founder-а 11.08). */
.card[data-part="chat"] .pv { margin-top: 10px; }
.card[data-part="chat"] { padding-block: 15px 17px; }
.cell[data-part="chat"] .card h3 { color: var(--iris-accent, var(--accent)); }

/* ИСХОД ПОД ИМЕНЕМ ЧАТА: чем он закрыл дело человека. Головная карточка
   показывала имя и значок «чат идёт» — ярлык, повторяющий подпись строкой выше
   (разбор founder-а 11.08). */
.p-gain {
  font-style: normal; font-size: clamp(13px, 1.05vw, 15px); line-height: 1.4;
  color: color-mix(in srgb, var(--iris-text, var(--ink)) 40%, white);
}
/* Имя говорящего — подписью НАД репликой: без него сцена читается монологом. */
.pv b > em {
  display: block; font-style: normal; margin-bottom: 3px;
  font-family: var(--font-mono); font-size: 8.5px; letter-spacing: .18em; text-transform: uppercase;
  color: color-mix(in srgb, var(--neon, var(--accent)) 58%, transparent);
}
.pv-curated .pv-a {
  align-self: flex-end; max-width: 88%;
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--iris-accent, var(--accent)) 20%, transparent);
}
.pv-curated .pv-note, .pv-guest .pv-note {
  display: flex; align-items: baseline; gap: 6px; font-style: normal;
  font-size: 11px; color: var(--iris-muted, var(--ink-2));
}

/* ── СВЕЖЕЕ ГОРИТ, ОСТАЛЬНОЕ ЖДЁТ ────────────────────────────────────────
   Мир чата держит один цвет, но акцент носит ровно один сегмент — тот, что
   появился этим ходом. Когда акцентом горело всё сразу, глазу не за что было
   зацепиться, и человек перечитывал экран по второму разу (разбор founder-а
   11.08). Приглушаем не цвет мира, а его силу: подписи и отметки уже прочитанных
   сегментов уходят в тон текста, свежий остаётся акцентным. */
.cell.on:not(.fresh) .card h3 { color: color-mix(in srgb, var(--neon, var(--accent)) 40%, var(--paper, #04050a)); }
.cell.on:not(.fresh) .card {
  box-shadow:
    inset 0 1px 0 color-mix(in srgb, var(--iris-text, var(--ink)) 12%, transparent),
    inset 1px 0 0 color-mix(in srgb, var(--iris-text, var(--ink)) 5%, transparent),
    inset -1px 0 0 color-mix(in srgb, var(--iris-text, var(--ink)) 5%, transparent),
    inset 0 -1px 0 rgba(0, 0, 0, .5);
}
.cell.on:not(.fresh) .p-fix li.on i,
.cell.on:not(.fresh) .p-step.on i,
.cell.on:not(.fresh) .p-rate b.on { background: color-mix(in srgb, var(--iris-text, var(--ink)) 30%, transparent); }
.cell.on:not(.fresh) .p-role b,
.cell.on:not(.fresh) .p-act-row.out b,
.cell.on:not(.fresh) .p-path li.now {
  /* Гасим В ПОЛЕ, а не в белом: смешение с белым не приглушает, а выбеливает —
     прочитанная строка становилась ярче свежей (замер 11.08). */
  color: color-mix(in srgb, var(--neon, var(--accent)) 52%, var(--paper, #04050a));
  text-shadow: none;
}
/* Свежий держит акцент и чуть более светлую кромку — это и есть «новое здесь». */
.cell.fresh .card {
  box-shadow:
    inset 0 1px 0 color-mix(in srgb, var(--iris-accent, var(--accent)) 58%, transparent),
    inset 1px 0 0 color-mix(in srgb, var(--iris-accent, var(--accent)) 20%, transparent),
    inset -1px 0 0 color-mix(in srgb, var(--iris-accent, var(--accent)) 20%, transparent),
    inset 0 -1px 0 rgba(0, 0, 0, .5);
}

/* ── МАТЕРИАЛ И ПЛОТНОСТЬ: ФАКТУРА ПОД ИНТЕНТ ────────────────────────────
   Цветом одним индивидуальности не набрать: два разных чата отличались только
   оттенком линии и читались одной вещью (разбор founder-а 11.08). Материал и
   плотность приходят от модели тем же seed-ом, что и палитра (`--iris-material`,
   `--iris-space`), и меняют ПОВЕРХНОСТЬ: свадьбе — бумага, ночному стриму —
   неон, разбору задач — стекло. Своего словаря материалов у лендинга нет,
   набор — канон @iris/core: flat | glass | paper | neon | bevel | pixel. */
.cell { --grain: .18; }
/* Бумага: матовая, без плёнки и без цветного отброса — свет её не отражает. */
.grid[data-material="paper"] .cell { --grain: .04; }
/* Стекло: сквозь полосу видно сцену, кромка светлее. */
.grid[data-material="glass"] .card { backdrop-filter: blur(10px); }
.grid[data-material="glass"] .cell { border-top-color: color-mix(in srgb, var(--iris-accent, var(--accent)) 44%, transparent); }
/* Неон: светится сама линия сетки, а не коробка вокруг неё. */
.grid[data-material="neon"] .cell {
  box-shadow: inset 0 1px 0 color-mix(in srgb, var(--iris-accent, var(--accent)) 55%, transparent),
              0 -14px 26px -24px var(--iris-accent, var(--accent));
}
/* Пиксель: линии рубленые, подписи содержимого — моноширинные. */
.grid[data-material="pixel"] .cell { border-top-style: dashed; --grain: .4; }
.grid[data-material="pixel"] .card { font-family: var(--font-mono); font-size: 12.5px; }
/* Фаска: кромка толще и с бликом — полоса читается вырезанной, а не налитой. */
.grid[data-material="bevel"] .cell { border-top-width: 2px; }
.grid[data-material="bevel"] .card { box-shadow: inset 0 1px 0 color-mix(in srgb, white 14%, transparent); }
/* Плотность: воздух внутри полосы — тот же, которым дышит собранный интерфейс. */
.card { gap: calc(var(--iris-space, 8px) * 1.1); }

/* ── Элемент интерфейса ──────────────────────────────────────────────────
   Один элемент за один ответ. Он подробный, но понятный: заголовок говорит, что
   это, тело показывает, как это работает.

   ЭЛЕМЕНТ — ЗАЛИВКА ПОЛОСЫ СЕТКИ, А НЕ КОРОБКА В НЕЙ (уточнение founder-а
   11.08). Скругление, обводка по периметру и тень отрывали его от корпуса: это
   предмет, положенный рядом с пультом. Кромки ему теперь дают линии сетки —
   стойка вдоль корпуса и полки между сегментами, — а сам он занимает свою
   полосу целиком, от пульта до края экрана.

   ПОЛЫЙ КОРПУС, СВЕТ ПО КРАЯМ — ТОТ ЖЕ ПРИЁМ, ЧТО У ПУЛЬТА (уточнение
   founder-а 11.08: «сейчас какой-то металик»). Пульт устроен просто: тело залито
   ровно тем же тоном, что и сцена, а видно его по светящейся кромке. Наслоенные
   градиенты и утопленная ниша давали блик, которого в этом мире нет ни у одной
   поверхности, — элемент читался вклейкой из другого макета. Теперь у полосы
   ОДНА заливка и свет по контуру, слабее пультового: сверху линия контакта,
   с боков — отблеск, снизу тёмная отбивка. */
.card {
  --lit: var(--iris-accent, var(--accent));
  position: relative; overflow: hidden; width: 100%; height: 100%;
  /* ПОЧТИ ЧЁРНОЕ ПОЛЕ, ЯРКИЙ ИНТЕНТ. Заливку брали прямо из --iris-bg, и на
     тёплом интенте выходила молочно-коричневая плита: текст по ней шёл в
     полтона, контраст падал вдвое против остальной страницы, неон пропадал
     (разбор founder-а 11.08). Тон интента остаётся, но как ЛЁГКИЙ градиент от
     чёрного, а не как сама бумага: цвет живёт в свете и буквах, не в фоне. */
  --paper: color-mix(in srgb, var(--iris-bg, var(--aurora-base-1)) 24%, #04050a);
  --neon: color-mix(in srgb, var(--iris-accent, var(--accent)) 86%, white);
  background:
    linear-gradient(180deg, color-mix(in srgb, var(--neon) 6%, var(--paper)), var(--paper) 72%);
  color: color-mix(in srgb, var(--iris-text, var(--ink)) 45%, white);
  font-family: var(--iris-font-body, var(--font-ui));
  padding: clamp(11px, 1vw, 15px) var(--pad-x);
  display: flex; flex-direction: column; gap: 9px;
  box-shadow:
    inset 0 1px 0 color-mix(in srgb, var(--iris-accent, var(--accent)) 38%, transparent),
    inset 1px 0 0 color-mix(in srgb, var(--iris-accent, var(--accent)) 14%, transparent),
    inset -1px 0 0 color-mix(in srgb, var(--iris-accent, var(--accent)) 14%, transparent),
    inset 0 -1px 0 rgba(0, 0, 0, .5);
}
/* Содержимое лежит НА полом корпусе, без своей плашки: вторая поверхность
   внутри первой и давала «металик» (уточнение founder-а 11.08). Отделяют его от
   подписи воздух и линии самих строк, а не ещё одна коробка. */
.card::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: var(--scan); opacity: var(--grain, .45); mix-blend-mode: overlay;
}
/* Карточку можно СПРОСИТЬ. Человек разговаривает не с агентом в вакууме, а с
   интерфейсом, который перед ним собрался, — и вопрос «а это зачем?» обязан
   задаваться прямо отсюда, а не набираться словами в пульте. */
.ask-it {
  position: absolute; inset: auto 0 0 0; z-index: 2;
  font: inherit; font-family: var(--font-mono); font-size: 9px; letter-spacing: .16em;
  text-transform: uppercase; cursor: pointer; border: 0; padding: 8px 0 9px;
  color: var(--iris-accent, var(--accent));
  background: linear-gradient(180deg, transparent, color-mix(in srgb, var(--iris-accent, var(--accent)) 16%, transparent));
  opacity: 0; transition: opacity var(--dur-base) var(--ease-out);
}
.card:hover .ask-it, .ask-it:focus-visible { opacity: 1; }
.card { padding-bottom: clamp(13px, 1.2vw, 17px); }

/* Метка тише самого содержимого: она подписывает, а не кричит. Разрядка вместо
   размера — так подпись читается служебной, а вес достаётся тому, что под ней. */
.card h3 {
  margin: 0; font-family: var(--font-mono); font-size: 9px; letter-spacing: .24em;
  text-transform: uppercase; font-weight: var(--weight-regular);
  color: color-mix(in srgb, var(--iris-accent, var(--accent)) 72%, transparent);
}


/* Сделка: то, что человек нам сдаёт. Появляется, когда собрано достаточно. */
/* ПРЕДЛОЖЕНИЕ — последний шаг сборки, а не подпись под ней: здесь человек видит,
   что собралось, кому это уходит и по какой ссылке. */
/* Предложение — ЗАМЫКАЮЩАЯ ПОЛОСА СЕТКИ, а не плашка под ней: те же линии, та же
   заливка на всю ширину. Скруглённая коробка с тенью снова читалась бы предметом,
   положенным на страницу, — ровно тем, от чего мы ушли в элементах. */
.deal {
  position: relative;
  display: flex; align-items: center; gap: clamp(12px, 1.6vw, 22px); flex-wrap: wrap;
  width: 100%; padding: 14px clamp(14px, 1.4vw, 20px);
  --lit: var(--iris-accent, var(--accent));
  background: color-mix(in srgb, var(--iris-bg, var(--aurora-base-1)) 24%, #04050a);
  box-shadow:
    inset 1px 0 0 color-mix(in srgb, var(--lit) 18%, transparent),
    inset -1px 0 0 color-mix(in srgb, var(--lit) 18%, transparent);
}
.deal .who-to { display: flex; flex-direction: column; gap: 2px; }
.deal .who-to b { font-size: 13px; }
.deal .who-to span { font-family: var(--font-mono); font-size: 10px; letter-spacing: .1em; color: var(--ink-3); }
.deal[hidden] { display: none; }
.deal > b { font-family: var(--font-mono); font-size: 9.5px; letter-spacing: .18em; text-transform: uppercase; color: var(--iris-accent, var(--accent)); }
.deal .grow { flex: 1 1 auto; min-width: 0; font-size: 12.5px; color: var(--ink-2); }
.deal .pilot { height: 36px; padding: 0 16px; font-size: 12.5px; }
.copy {
  font: inherit; font-family: var(--font-mono); font-size: 10px; letter-spacing: .12em;
  text-transform: uppercase; cursor: pointer; padding: 0; border: 0; background: none;
  color: var(--ink-3); border-bottom: 1px solid currentColor;
  transition: color var(--dur-base) var(--ease-out);
}
.copy:hover { color: var(--accent); }

/* Текстовая версия — в углу и одной буквой. Под пультом она стояла рядом с
   целевым действием и уводила от него; нужна же она редко и не всем. */
.astext {
  position: fixed; right: clamp(12px, 1.6vw, 22px); bottom: clamp(12px, 1.6vw, 22px); z-index: 4;
  width: 30px; height: 30px; display: grid; place-items: center;
  font-family: var(--font-mono); font-size: 12px; letter-spacing: 0; color: var(--ink-3);
  border: 1px solid var(--glass-border); border-radius: var(--radius-sm);
  transition: color var(--dur-base) var(--ease-out), border-color var(--dur-base) var(--ease-out), opacity .5s var(--ease-out);
}
.astext:hover { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 45%, transparent); }
body.js[data-act="hush"] .astext { opacity: 0; pointer-events: none; }
body.js[data-act="read"] .astext { display: none; }

/* ── Единственное действие на сцене ──────────────────────────────────────
   Шапки нет (решение founder-а 11.08): метрики, статус и логотип уводили
   внимание от вопроса, ради которого страница и существует. Осталась одна
   кнопка — в углу, не спорит с пультом и всегда под рукой. */
.pilot.start {
  position: fixed; top: clamp(12px, 1.8vh, 22px); right: clamp(12px, 1.6vw, 22px); z-index: 4;
  height: 38px; padding: 0 18px; font-size: 12.5px;
  transition: opacity .5s var(--ease-out), transform var(--dur-fast) var(--ease-out), box-shadow var(--dur-base) var(--ease-out);
}
body.js[data-act="hush"] .pilot.start { opacity: 0; pointer-events: none; }
/* В интерактивном пути продажа начинается внутри пульта после прожитого хода.
   Верхняя ссылка остаётся для страницы без JS и режима чтения, но не дублирует
   главный CTA brief и не перетягивает внимание до результата. */
body.js:not([data-act="read"]) .pilot.start { display: none; }

/* Переключатель языка живёт в двух местах: who-lang в шапке треда — сменить
   язык хочется в первый момент, глядя на пульт (решение founder-а 14.08), —
   и foot-lang в подвале, потому что hreflang и sitemap объявляют версии
   равными и язык обязан быть достижим со статической страницы. */
.foot-lang {
  display: inline-flex; align-items: center; justify-content: center;
  height: 30px; padding: 0 10px; margin-right: 18px;
  font-family: var(--font-mono); font-size: 11px; letter-spacing: .12em;
  color: var(--ink-2); border: 1px solid var(--glass-border); border-radius: var(--radius-pill);
}
.foot-lang:hover { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 45%, transparent); }

.pilot { height: 36px; padding: 0 16px; font-size: 12.5px; }
.copy {
  font: inherit; font-family: var(--font-mono); font-size: 10px; letter-spacing: .12em;
  text-transform: uppercase; cursor: pointer; padding: 0; border: 0; background: none;
  color: var(--ink-3); border-bottom: 1px solid currentColor;
  transition: color var(--dur-base) var(--ease-out);
}
.copy:hover { color: var(--accent); }

/* Текстовая версия — в углу и одной буквой. Под пультом она стояла рядом с
   целевым действием и уводила от него; нужна же она редко и не всем. */
.astext {
  position: fixed; right: clamp(12px, 1.6vw, 22px); bottom: clamp(12px, 1.6vw, 22px); z-index: 4;
  width: 30px; height: 30px; display: grid; place-items: center;
  font-family: var(--font-mono); font-size: 12px; letter-spacing: 0; color: var(--ink-3);
  border: 1px solid var(--glass-border); border-radius: var(--radius-sm);
  transition: color var(--dur-base) var(--ease-out), border-color var(--dur-base) var(--ease-out), opacity .5s var(--ease-out);
}
.astext:hover { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 45%, transparent); }
body.js[data-act="hush"] .astext { opacity: 0; pointer-events: none; }
body.js[data-act="read"] .astext { display: none; }





.pilot {
  flex-shrink: 0; display: inline-flex; align-items: center; gap: 9px;
  height: 40px; padding: 0 22px; border-radius: var(--radius-pill);
  background: linear-gradient(120deg, var(--accent), var(--violet));
  color: var(--on-accent); font-size: 13px; font-weight: var(--weight-semibold);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .45),
              0 2px 0 color-mix(in srgb, var(--accent) 30%, transparent),
              0 14px 28px -16px color-mix(in srgb, var(--accent) 55%, transparent),
              0 20px 40px -26px rgba(0, 0, 0, 1);
  transition: transform var(--dur-fast) var(--ease-out), box-shadow var(--dur-base) var(--ease-out);
}
.pilot:hover {
  color: var(--on-accent); transform: translateY(-1px);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .55),
              0 2px 0 color-mix(in srgb, var(--accent) 42%, transparent),
              0 18px 34px -16px color-mix(in srgb, var(--accent) 70%, transparent),
              0 24px 46px -26px rgba(0, 0, 0, 1);
}

.backrow { margin: clamp(16px, 3vh, 28px) 0 0; }
.back { font-family: var(--font-mono); font-size: 11px; letter-spacing: .14em; text-transform: uppercase; color: var(--ink-3); }
.back:hover { color: var(--accent); }
body.js:not([data-act="read"]) .backrow { display: none; }

/* ── Герой ───────────────────────────────────────────────────────────────── */
.hero { padding: clamp(28px, 12vh, 132px) 0 clamp(24px, 6vh, 64px); }
.hero h1 {
  margin: 0 0 clamp(16px, 2.2vh, 26px);
  font-family: var(--font-display); font-weight: 800;
  font-size: clamp(29px, 4vw, 58px); line-height: 1.1; letter-spacing: -.045em;
  max-width: 21ch;
  transition: opacity var(--dur-slow) var(--ease-out);
}
.hero h1 .lit { color: var(--accent); }
.lead { margin: 0; font-size: clamp(14.5px, 1.25vw, 18px); line-height: 1.62; color: #C6D2DA; max-width: 46ch; }

/* ── Свет от текста ───────────────────────────────────────────────────────
   Страница освещается текстом, а не подсветкой под ним: узкая тёмная контактная
   тень отделяет глиф от линии за ним, широкий ореол — свет, который надпись
   отбрасывает на кадр. Чёрных плашек в этой схеме нет. */
.hero h1 {
  text-shadow:
    0 0 1px rgba(0, 0, 0, .6), 0 2px 10px rgba(0, 0, 0, .45),
    0 0 34px rgba(226, 244, 252, .3), 0 0 78px rgba(226, 244, 252, .16);
}
.hero h1 .lit {
  text-shadow:
    0 0 1px rgba(0, 0, 0, .5), 0 2px 10px rgba(0, 0, 0, .4),
    0 0 30px color-mix(in srgb, var(--accent) 55%, transparent),
    0 0 76px color-mix(in srgb, var(--accent) 28%, transparent);
}
.lead, .links a, .ways, .blk p, .blk h2 {
  text-shadow: 0 0 1px rgba(0, 0, 0, .55), 0 1px 6px rgba(0, 0, 0, .4), 0 0 22px rgba(198, 214, 226, .18);
}

/* ── Такт 1: блоки приезжают ритмом ──────────────────────────────────────
   Печатается только речь Айрис; секции ПРОЯВЛЯЮТСЯ. Печатать их целиком значило
   бы заставить человека ждать чтение. Без JS класса .js нет — блоки видны сразу,
   и страница остаётся обычным лендингом. */
.acts { display: flex; flex-direction: column; gap: clamp(34px, 7vh, 78px); }
body.js .blk { opacity: 0; transform: translateY(16px); }
/* Режим чтения показывает ВСЕ секции: скрытие .blk — язык разговорного режима,
   где блоки проявляет rebuild() по ходу реплик агента. В act="read" проявителя
   нет, и без этого правила читатель видел hero, пять тысяч точек пустоты и
   подвал (найдено на мобильном прогоне 14.08, воспроизводилось и на десктопе). */
body.js[data-act="read"] .blk { opacity: 1; transform: none; }
body.js .blk.on {
  opacity: 1; transform: none;
  transition: opacity .5s var(--ease-out), transform .5s var(--ease-out);
}
.blk { scroll-margin-top: 92px; }
.blk h2 {
  margin: 0 0 clamp(12px, 1.6vh, 18px);
  font-family: var(--font-display); font-weight: 700;
  font-size: clamp(22px, 2.4vw, 34px); line-height: 1.15; letter-spacing: -.04em;
  max-width: 20ch;
}
.blk p { margin: 0 0 12px; font-size: clamp(14px, 1.1vw, 16.5px); line-height: 1.65; color: #C6D2DA; max-width: 62ch; }
/* Вводная строка от агента: она объясняет, зачем ЭТОМУ человеку этот блок. */
.blk .lead-in {
  color: var(--accent); font-weight: var(--weight-medium); max-width: 52ch;
  text-shadow: 0 0 1px rgba(0,0,0,.5), 0 0 20px color-mix(in srgb, var(--accent) 32%, transparent);
}

/* Карточки и шаги — та же проза, другая подача: data-tag на абзаце даёт метку,
   второго текста для витрины не существует. */
.blk[data-shape="cards"] { display: grid; gap: clamp(12px, 1.4vw, 20px); grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); align-items: start; }
.blk[data-shape="cards"] h2 { grid-column: 1 / -1; }
.blk[data-shape="cards"] p,
.blk[data-shape="steps"] p {
  margin: 0; max-width: none;
  padding: clamp(14px, 1.3vw, 18px) clamp(15px, 1.4vw, 20px);
  border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent);
  border-radius: var(--radius-xl);
  background: linear-gradient(160deg, rgba(9,9,13,.86), rgba(5,5,8,.78));
  backdrop-filter: blur(7px);
  box-shadow: inset 0 1px 0 color-mix(in srgb, var(--accent) 26%, transparent);
  font-size: clamp(13px, 1vw, 14.5px);
}
.blk[data-shape="cards"] p::before,
.blk[data-shape="steps"] p::before {
  content: attr(data-tag);
  display: block; margin-bottom: 8px;
  font-family: var(--font-mono); font-size: 10.5px; letter-spacing: .16em;
  text-transform: uppercase; color: var(--accent);
}
.blk[data-shape="cards"] p:nth-of-type(2n) { border-color: color-mix(in srgb, var(--like) 30%, transparent); }
.blk[data-shape="cards"] p:nth-of-type(2n)::before { color: var(--like); }
.blk[data-shape="steps"] { display: grid; gap: clamp(10px, 1.1vw, 14px); grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); align-items: stretch; }
.blk[data-shape="steps"] h2 { grid-column: 1 / -1; }

/* Лента живых экранов .shots: телефонные кадры кейсов внутри карточного блока.
   Кадр обрезан до чистого экрана ещё в файле, рамку и скругление даёт CSS —
   поэтому у всех пяти кейсов одна и та же оправа. Высота фиксирована, ширина
   плывёт: кадры сняты с разных экранов и ряд без этого рвётся по нижней кромке. */
.blk .shots {
  grid-column: 1 / -1;
  display: flex; gap: clamp(12px, 1.4vw, 20px);
  overflow-x: auto; padding: 4px 2px 10px;
  scroll-snap-type: x proximity;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: thin;
}
.blk .shots figure { margin: 0; flex: 0 0 auto; scroll-snap-align: start; }
.blk .shots img {
  display: block; height: clamp(300px, 36vh, 430px); width: auto;
  border-radius: 22px;
  border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent);
  background: #000;
  box-shadow: inset 0 1px 0 color-mix(in srgb, var(--accent) 26%, transparent), 0 12px 32px rgba(0,0,0,.42);
}
.blk .shots figcaption {
  margin-top: 8px;
  font-family: var(--font-mono); font-size: 10.5px; letter-spacing: .16em;
  text-transform: uppercase; color: var(--accent);
}
.blk .shots figure:nth-of-type(2n) img { border-color: color-mix(in srgb, var(--like) 30%, transparent); }
.blk .shots figure:nth-of-type(2n) figcaption { color: var(--like); }

/* ── Части экрана ────────────────────────────────────────────────────────
   Одна разметка, любое подмножество — приём листа форм /w/forms. Части рисуют
   содержимое сегмента; рамку и подпись даёт сетка.
   Оболочка `.built`/`.scr-*` удалена 11.08 как сирота: ни разметка, ни скрипт
   этих узлов не создавали ни разу. Вместе с ней ушли токены `--lit-*` — их
   последним потребителем была её кнопка. */
.p-head { display: flex; flex-direction: column; gap: 9px; align-items: flex-start; }
/* Три практики под его дело: что мы поставим. Первая уже отмечена — видно, что
   решение принимается на глазах, а не обещается. */
.p-fix { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.p-fix li {
  display: flex; align-items: flex-start; gap: 9px; padding: 7px 0;
  border-top: 1px solid var(--iris-border, var(--glass-border));
  font-size: 13.5px; line-height: 1.4; color: var(--iris-text, var(--ink));
}
.p-fix li:first-child { border-top: 0; }

/* Свёрнутые строки задания: их не читают, они держат счёт. Отметки у них нет —
   отмечать нечего, это не пожелание, а память о том, что их больше. */
.p-fix li.more { opacity: .55; }
.p-fix li.more i { border-style: dashed; background: none; }
.p-fix i {
  width: 14px; height: 14px; flex-shrink: 0; margin-top: 2px;
  border-radius: 0;
  border: 1px solid color-mix(in srgb, var(--iris-accent, var(--accent)) 55%, transparent);
}
.p-fix li.on i { background: var(--iris-accent, var(--accent)); }
.p-fix li:not(.on) span { color: var(--iris-muted, var(--ink-2)); }
.p-head b { font-family: var(--iris-font-heading, var(--font-display)); font-size: clamp(17px, 1.5vw, 20px); font-weight: 600; line-height: 1.18; letter-spacing: -.035em; }

.p-roles { display: flex; flex-direction: column; gap: 11px; }
/* Дело — ЗАГОЛОВОК СТРОКИ, имя роли — подпись над ним. Раньше строка шла в две
   колонки по базовой линии, дело стояло справа мелким капслоком и обрезалось
   многоточием: «фиксирует голоса на…» (разбор founder-а 11.08). Ради этих слов
   карточку и читают — они и получают вес. */
.p-role { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; min-width: 0; }
.p-role span {
  font-family: var(--font-mono); font-size: 9px; letter-spacing: .18em; text-transform: uppercase;
  color: color-mix(in srgb, var(--neon, var(--accent)) 62%, transparent);
}
.p-role b {
  font-size: clamp(15px, 1.25vw, 17.5px); line-height: 1.25; letter-spacing: -.01em;
  font-weight: var(--weight-medium); color: var(--neon, var(--accent));
  text-shadow: 0 0 20px color-mix(in srgb, var(--neon, var(--accent)) 32%, transparent);
}

.p-steps { display: flex; flex-direction: column; }
.p-step { display: flex; align-items: center; gap: 9px; padding: 5px 0; border-top: 1px solid var(--iris-border, var(--glass-border)); font-size: 13px; line-height: 1.35; }
.p-step:first-child { border-top: 0; }
.p-step i { width: 14px; height: 14px; border-radius: 0; flex-shrink: 0; border: 1px solid var(--iris-accent, var(--accent)); }
.p-step.on i { background: var(--iris-accent, var(--accent)); }

.p-shot { display: flex; flex-direction: column; gap: 8px; }
.p-frame { min-height: 62px; border-radius: 0; border: 1px dashed var(--iris-border, var(--glass-border)); background: var(--iris-raised, transparent); display: grid; place-items: center; }
.p-frame i { width: 26px; height: 20px; border: 2px solid var(--iris-accent, var(--accent)); border-radius: 0; position: relative; }
.p-frame i::after { content: ''; position: absolute; inset: 4px 4px auto; height: 2px; background: var(--iris-accent, var(--accent)); }
.p-frame { position: relative; flex-direction: column; gap: 6px; }
.p-frame em {
  font-style: normal; font-family: var(--font-mono); font-size: 9.5px; letter-spacing: .12em;
  text-transform: uppercase; color: var(--iris-muted, var(--ink-3)); text-align: center; padding: 0 8px;
}
.p-shot span { font-family: var(--font-mono); font-size: 10px; letter-spacing: .12em; text-transform: uppercase; color: var(--iris-muted, var(--ink-3)); }

.p-note { display: flex; align-items: flex-start; gap: 9px; font-size: 13px; line-height: 1.45; }
.p-note i { width: 7px; height: 7px; border-radius: 50%; background: var(--iris-success, var(--free)); flex-shrink: 0; margin-top: 5px; box-shadow: 0 0 12px -2px currentColor; }

.p-rate { display: flex; flex-direction: column; gap: 10px; }
.p-scale { display: flex; gap: 5px; }
/* Число под шкалой — то, ради чего оценка вообще ставится. Пять одинаковых
   кнопок без него остаются картинкой. */
.p-rate em {
  font-style: normal; display: flex; align-items: baseline; gap: 8px;
  font-family: var(--iris-font-heading, var(--font-display));
  font-weight: 600; letter-spacing: -.04em; font-size: 23px; letter-spacing: -.03em; color: var(--iris-text, var(--ink));
}
.p-rate em span {
  font-family: var(--font-mono); font-size: 9.5px; letter-spacing: .12em;
  text-transform: uppercase; color: var(--iris-muted, var(--ink-3));
}
.p-rate b {
  width: 26px; height: 26px; display: grid; place-items: center; font-size: 12.5px; font-weight: var(--weight-semibold);
  border-radius: 0;
  border: 1px solid var(--iris-border, var(--glass-border)); color: var(--iris-muted, var(--ink-3));
}
.p-rate b.on { background: color-mix(in srgb, var(--iris-accent, #27F0DE) 28%, transparent); color: var(--iris-text, var(--ink)); border-color: transparent; }
.p-rate b.pick { background: var(--iris-accent, var(--accent)); color: var(--iris-on-accent, var(--on-accent)); border-color: transparent; box-shadow: inset 0 1px 0 rgba(255,255,255,.4), 0 2px 0 color-mix(in srgb, var(--iris-accent, #27F0DE) 40%, transparent); }

.p-flag { display: flex; align-items: center; gap: 9px; font-size: 13px; color: var(--iris-danger, var(--danger)); }
.p-flag i { width: 8px; height: 8px; border-radius: 50%; background: var(--iris-danger, var(--danger)); flex-shrink: 0; animation: pulse 1.1s ease-in-out infinite; }

.p-stamp { display: flex; flex-direction: column; gap: 3px; }
.p-stamp b { font-family: var(--iris-font-heading, var(--font-display)); font-size: 21px; letter-spacing: -.03em; }
.p-stamp span { font-family: var(--font-mono); font-size: 9.5px; letter-spacing: .12em; text-transform: uppercase; color: var(--iris-muted, var(--ink-3)); }

.p-path { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px 10px; }
.p-path li { font-family: var(--font-mono); font-size: 10px; letter-spacing: .12em; text-transform: uppercase; color: var(--iris-muted, var(--ink-3)); }
.p-path li + li::before { content: '→ '; opacity: .5; }
.p-path li.was { color: var(--iris-text, var(--ink)); opacity: .7; }
.p-path li.now { color: var(--iris-accent, var(--accent)); font-size: 12.5px; letter-spacing: .04em; text-transform: none; }

/* ДЕЙСТВИЕ С ИСХОДОМ: ход слева, что вышло справа, запись строкой под ними. */
.p-act { display: flex; flex-direction: column; gap: 7px; }
/* Та же иерархия, что у ролей: подпись сверху, содержание — заголовком. */
.p-act-row { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; min-width: 0; }
.p-act-row span {
  font-family: var(--font-mono); font-size: 9px; letter-spacing: .18em; text-transform: uppercase;
  color: color-mix(in srgb, var(--neon, var(--accent)) 55%, transparent);
}
.p-act-row b {
  font-size: clamp(14px, 1.15vw, 16.5px); line-height: 1.25;
  font-weight: var(--weight-medium); text-align: left;
}
.p-act-row.out b {
  color: var(--neon, var(--accent));
  text-shadow: 0 0 20px color-mix(in srgb, var(--neon, var(--accent)) 32%, transparent);
}
.p-act i {
  font-style: normal; font-size: 11.5px; color: var(--iris-muted, var(--ink-2));
  padding-top: 6px; border-top: 1px solid var(--iris-border, var(--glass-border));
}

.p-tally { display: flex; gap: 18px; }
.p-tally div { display: flex; flex-direction: column; gap: 2px; }
.p-tally span { font-family: var(--font-mono); font-size: 11px; letter-spacing: .1em; text-transform: uppercase; color: var(--iris-text, var(--ink)); }

/* ── Лента разговора ──────────────────────────────────────────────────────
   Стоит в потоке страницы: реплики копятся сверху вниз, страница едет за ними
   (pin() в console.js). Плавающей поверх содержимого лента была ровно один
   заход — и закрывала треть того, что человек пришёл читать. */
/* Разговор — отдельный предмет на сцене, а не текст на фоне: у него своя рамка
   и своя прокрутка. Без рамки реплики висели в воздухе, а длинный ответ уезжал
   в никуда — прочитать его было нельзя. */
/* ДИСПЛЕЙ И ПУЛЬТ — ОДИН КОРПУС. Рамку дисплея рисует силуэт (cardBodyPath),
   поэтому своей рамки и своего фона у ленты нет: два контура на одном месте
   читаются как коробка, положенная на пульт, а не как сцепка.

   ВЫСОТА. Дисплей — флекс-колонка с min-height:0, лента внутри — flex:1 1 auto.
   Это единственная связка, при которой процентный потолок работает: у контейнера
   с height:auto проценты разрешаются в none, лента растёт без предела и выносит
   текст за пределы собственного силуэта. */
/* Дисплей — ОБЫЧНЫЙ БЛОК с содержимым: скругления сверху, ровный низ. Всё, чего
   ему не хватает для сцепки, — одна деталь снизу, зеркальная верхней кромке
   пульта; её и рисует SVG. Своей геометрии у дисплея нет. */
/* Дисплей — обычный блок с содержимым; всё, чего ему не хватает для сцепки, —
   подошва снизу, повторяющая кромку пульта. Рамка нарисована ВНУТРЕННЕЙ тенью, а
   не border: border сдвинул бы содержимое на пиксель внутрь, и подошва перестала
   бы совпадать с пультом один в один. */
/* Потолок ширины у дисплея — тот же --corpus, что и у пульта: это ОДНО тело,
   и подошва дисплея повторяет кромку пульта один в один. Пока у дисплея стоял
   свой потолок в 900 точек, расхождение прятала средняя колонка
   десктопной сетки; в мобильной стопке колонка одна — и на широком низком
   окне (ландшафт телефона, зум браузера, форточка ноутбука) дисплей
   разъезжался на всю ширину, а пульт оставался в своих 320: неоновая кромка
   повисала под дисплеем в стороне (скрин founder-а 25.08). Мера — одна. */
.display {
  position: relative; width: 100%; max-width: var(--corpus);
  display: flex; flex-direction: column; min-height: 0;
  height: auto;
  padding: 0 0 var(--foot, 44px);
  border-radius: var(--radius-xl) var(--radius-xl) 0 0;
  /* Нижние --foot пикселей — не коробка, а фигурная подошва: прямоугольный фон
     здесь перекрывал прозрачный вырез даже при правильном SVG-контуре. */
  background: transparent;
  box-shadow: inset 0 1px 0 var(--seam), inset 1px 0 0 var(--seam), inset -1px 0 0 var(--seam);
  transition: border-radius 460ms var(--ease-out);
}
/* До появления разговора пульт стоит отдельным предметом. Пустая рамка
   дисплея без текста не должна проступать раньше фактической сцепки. */
body.js .display:not(.in) { opacity: 0; pointer-events: none; }
.display::before {
  content: ''; position: absolute; z-index: 0; pointer-events: none;
  inset: 0 0 var(--foot, 44px); border-radius: inherit;
  background: var(--aurora-base-1);
}
.display::after {
  content: ''; position: absolute; z-index: 5; pointer-events: none;
  inset: 0 0 var(--foot, 44px); border-radius: inherit;
  border: 1px solid var(--seam); border-bottom: 0;
}
/* Подошва: заливка без обводки. Её нижняя граница И ЕСТЬ верхняя кромка пульта —
   вторая линия на том же месте читалась бы швом между двумя предметами. Подошва на пиксель
   шире: stroke пульта идёт по центру пути и на полпикселя выходит за габарит. */
.disp-shell {
  position: absolute; left: -1px; right: -1px; bottom: 0;
  z-index: 4; width: calc(100% + 2px); overflow: visible; pointer-events: none;
}
.thread {
  position: relative; z-index: 1;
  display: flex; flex-direction: column; gap: 12px;
  flex: 0 1 auto; min-height: 0;
  overflow-y: auto; overscroll-behavior: contain;
  padding: 4px 22px 18px;
  scrollbar-width: thin; scrollbar-color: color-mix(in srgb, var(--accent) 35%, transparent) transparent;
}

/* Кольцо собралось — читают его, и коробка уступает элементам высоту. */
.thread::-webkit-scrollbar { width: 6px; }
.thread::-webkit-scrollbar-thumb { background: color-mix(in srgb, var(--accent) 32%, transparent); border-radius: 3px; }
.msg { max-width: 74%; padding: 13px 17px; border-radius: var(--radius-lg); font-size: clamp(14px, 1.1vw, 16px); line-height: 1.6; white-space: pre-wrap; }
.msg.me {
  align-self: flex-end; color: var(--on-accent);
  background: linear-gradient(120deg, #2FF0E6, var(--accent));
  border: 1px solid color-mix(in srgb, var(--accent) 60%, transparent);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .45),
              0 2px 0 color-mix(in srgb, var(--accent) 26%, transparent),
              0 16px 30px -22px rgba(0, 0, 0, 1);
}
/* Речь агента лежит прямо на кадре — читаемость даём светом и тенью по форме
   букв, а не плашкой под ними. */
.msg.it {
  align-self: flex-start; color: #F4FAFD; padding-left: 0; padding-right: 0;
  max-width: min(62ch, 62%);
  text-shadow:
    0 0 1px rgba(0, 0, 0, .7), 0 2px 8px rgba(0, 0, 0, .5),
    0 0 28px rgba(214, 238, 248, .34), 0 0 68px rgba(214, 238, 248, .16);
}
/* В такте greet реплика — самое светлое и самое крупное на экране: она задаёт
   вопрос, на который отвечают. */
/* Речь Айрис — главный текст страницы, и читать его должно быть легко: крупнее
   интерфейсного, с воздухом между строк и нормальной длиной строки. Мелкий
   плотный текст в тёмной коробке не читают, его проглядывают. */
body.js[data-act="talk"] .msg.it, body.js[data-act="hush"] .msg.it {
  font-size: clamp(17px, 1.5vw, 21px); line-height: 1.62; letter-spacing: -.01em;
  max-width: min(46ch, 82%);
}
/* Подпись говорящего. Человек обязан понимать, ЧТО он видит, с первого кадра:
   без имени светлый текст на фотографии читается как слоган страницы, а не как
   обращённая к нему реплика — и отвечать на него незачем. */
/* Подпись говорящего ЗАКРЕПЛЕНА в корпусе, а не едет внутри ленты: уехав в
   прокрутку, она оставляла человека без ответа на вопрос «кто это говорит». */
.who {
  position: relative; z-index: 1; flex: 0 0 auto;
  margin: 0; padding: 14px 22px 8px; font-family: var(--font-mono); font-size: 10.5px; letter-spacing: .18em;
  text-transform: uppercase; color: var(--accent);
  display: flex; align-items: center; gap: 8px;
  text-shadow: 0 0 1px rgba(0,0,0,.6), 0 0 20px color-mix(in srgb, var(--accent) 45%, transparent);
}
.who i { width: 6px; height: 6px; border-radius: 50%; background: var(--accent); box-shadow: var(--bloom-accent); }
/* who-lang прижат к правому краю шапки flex-ом самой .who; тише имени и без
   его свечения, а hover красится акцентом текущего мира, как весь корпус. */
.who-lang {
  margin-left: auto; padding: 3px 9px;
  font-size: 10px; letter-spacing: .14em; text-decoration: none;
  color: var(--ink-2); text-shadow: none;
  border: 1px solid var(--glass-border); border-radius: var(--radius-pill);
  transition: color var(--dur-base) var(--ease-out), border-color var(--dur-base) var(--ease-out);
}
.who-lang:hover { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 45%, transparent); }

/* Что легло на экран этим ходом. Не реплика и не подсказка — отчёт страницы о
   собственном действии: человек прочитывает его прежде, чем услышит следующий
   вопрос. Поэтому он тише речи и стоит отдельной строкой. */
.added {
  margin: 0; flex-shrink: 0; align-self: flex-start;
  font-family: var(--font-mono); font-size: 10px; letter-spacing: .16em;
  text-transform: uppercase; color: color-mix(in srgb, var(--accent) 66%, transparent);
  display: flex; align-items: center; gap: 8px;
}
.added::before {
  content: ''; width: 16px; height: 1px; background: currentColor; opacity: .6;
}

/* ВОПРОС АЙРИС — ХОД ЧЕЛОВЕКА, И ЕГО ВИДНО НЕ ЧИТАЯ. Реплика шла одним
   полотном, и до «от меня чего-то ждут» человек добирался только к последней
   строке (разбор founder-а 11.08). Вопрос стоит отдельной плашкой: своя
   поверхность, светящаяся кромка со стороны текста, акцент и вес. Курсива тут
   нет намеренно — настоящего у гарнитуры нет, а поддельный наклон на кириллице
   выглядит дёшево. */
.msg.it .q {
  display: block; margin-top: 14px; padding: 12px 16px 13px;
  font-weight: var(--weight-semibold); font-size: clamp(16px, 1.35vw, 19px);
  line-height: 1.45; letter-spacing: -.01em;
  color: color-mix(in srgb, var(--accent) 34%, white);
  background: color-mix(in srgb, var(--accent) 11%, transparent);
  box-shadow: inset 2px 0 0 var(--accent),
              inset 0 1px 0 color-mix(in srgb, var(--accent) 26%, transparent);
  text-shadow: 0 0 1px rgba(0, 0, 0, .6),
               0 0 22px color-mix(in srgb, var(--accent) 42%, transparent);
}
/* Реплике с вопросом даём больше ширины: в узкой колонке вопрос ломался на пять
   коротких строк и читался хуже собственной речи. */
.msg.it:has(.q) { max-width: min(62ch, 80%); }
/* В такте приветствия речь и так крупная — плашке хватает кромки и веса. */
body.js[data-act="talk"] .msg.it .q, body.js[data-act="hush"] .msg.it .q { margin-top: 16px; }

/* ── Подсказки следующего шага ────────────────────────────────────────────
   Освещённые, а не затенённые: это то, что человек должен нажать следующим. */
.chips-next { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; flex-shrink: 0; }

.samples { justify-content: flex-start; }
.chips-label {
  font-family: var(--font-mono); font-size: 10px; letter-spacing: .16em;
  text-transform: uppercase; color: #9FB0BC; margin-right: 2px;
  text-shadow: 0 0 1px rgba(0, 0, 0, .6), 0 0 18px rgba(214, 238, 248, .22);
}
.chip {
  font: inherit; font-size: clamp(13px, 1.05vw, 14.5px);
  padding: 9px 15px; border-radius: var(--radius-pill); cursor: pointer;
  color: #F4FAFD;
  background: linear-gradient(180deg, color-mix(in srgb, var(--accent) 12%, transparent), color-mix(in srgb, var(--accent) 5%, transparent));
  border: 1px solid color-mix(in srgb, var(--accent) 55%, transparent);
  box-shadow: inset 0 1px 0 color-mix(in srgb, var(--accent) 45%, transparent),
              0 1px 0 color-mix(in srgb, var(--accent) 22%, transparent),
              0 14px 26px -20px rgba(0, 0, 0, 1);
  text-shadow: 0 0 1px rgba(0, 0, 0, .5), 0 0 16px color-mix(in srgb, var(--accent) 35%, transparent);
  backdrop-filter: blur(4px);
  transition: border-color var(--dur-base) var(--ease-out), box-shadow var(--dur-base) var(--ease-out), transform var(--dur-fast) var(--ease-out);
}
.chip:hover {
  border-color: var(--accent); transform: translateY(-1px);
  box-shadow: inset 0 1px 0 color-mix(in srgb, var(--accent) 70%, transparent),
              0 2px 0 color-mix(in srgb, var(--accent) 34%, transparent),
              0 16px 30px -20px rgba(0, 0, 0, 1);
}
.chip:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
/* Готовый мир-пример — самый громкий чип ряда: клик открывает собранный мир
   мгновенно, без разговора. Плитка kit-а слева, имя + шёпот-подпись справа
   (срез 1 эпика journey, спека 2026-08-15). */
.chip.example {
  display: inline-flex; align-items: center; gap: 9px; text-align: left;
  padding: 8px 14px 8px 10px;
}
.chip.example svg {
  width: 17px; height: 17px; flex: 0 0 auto; color: var(--accent);
}
.chip.example > span { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.chip.example b { font-size: 13px; font-weight: var(--weight-medium, 500); line-height: 1.2; }
.chip.example i {
  font-style: normal; font-family: var(--font-mono); font-size: 8.5px;
  letter-spacing: .1em; text-transform: uppercase; color: var(--ink-2);
}
/* Опция движка — не вопрос, а ТРЕБОВАНИЕ, которое человек кладёт в свой чат.
   Поэтому она тише вопросов и помечена плюсом: нажал — сегмент встал в сетку. */
.chip.add {
  background: none; color: var(--ink);
  border-style: dashed; border-color: color-mix(in srgb, var(--accent) 40%, transparent);
  box-shadow: none;
}
.chip.add::before { content: '+ '; color: var(--accent); }
.chip.add:hover { border-style: solid; box-shadow: none; }
/* Отказ говорить — legal путь, а не серая кнопка отмены: он тише образцов, но
   не спрятан. Не навязчиво — это про громкость, а не про наличие выхода. */
.chip.quiet {
  background: none; color: var(--ink-2);
  border-color: var(--glass-border); box-shadow: none; text-shadow: 0 0 1px rgba(0,0,0,.6);
}
.chip.quiet:hover { color: var(--ink); border-color: var(--ink-3); box-shadow: none; }
.caret { color: var(--accent); animation: blink 1s steps(1) infinite; }

/* ── ОДИН ПРИЁМ ПОЯВЛЕНИЯ ─────────────────────────────────────────────────
   ГЛАВНОЕ ПРАВИЛО СТРАНИЦЫ: всё возникает в одном и предсказуемом ритме. Коробка
   разговора — такой же блок, как элементы интерфейса, и появляется тем же
   движением и за то же время; отличаться может только очередь. Своих кривых и
   длительностей у отдельных блоков нет — иначе первый блок выскакивает не так,
   как следующие, и ритма не складывается. */
/* Короткое затухание на время уточнения: содержимое элемента меняется под ним, и
   правка читается как уточнение, а не как новый элемент. */
.card > *:not(.ask-it) { transition: opacity .28s var(--ease-out); }
.card.redo > *:not(.ask-it) { opacity: .25; transition: opacity .18s var(--ease-out); }

body.js .appear { opacity: 0; transform: translateY(14px) scale(.985); }
body.js .appear.in {
  opacity: 1; transform: none;
  transition: opacity .38s var(--ease-out), transform .38s var(--ease-out);
}
@keyframes rise { from { opacity: 0; transform: translateY(14px) scale(.985); } to { opacity: 1; transform: none; } }
@keyframes blink { 0%, 49% { opacity: 1; } 50%, 100% { opacity: 0; } }
@keyframes pulse { 0%, 100% { opacity: .45; transform: scale(1); } 50% { opacity: 1; transform: scale(1.12); } }

/* ── Пульт ────────────────────────────────────────────────────────────────
   Силуэт, ниша и места вкладок считает канон (dock-silhouette.js из @iris/core):
   лендинг показывает тот же пульт, что и приложение, а не его подобие. */
.dock { position: relative; flex: 0 0 auto; height: var(--dock-h); width: 100%; max-width: var(--corpus); pointer-events: none; }
.dock-shell { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }
.tabs, .well, .send { pointer-events: auto; }

/* Полка пуста на входе. Три значка ничего не переключали, пока переключать
   нечего, — а первый экран должен быть силуэтом пульта и ничем больше. Полка
   материализуется тогда, когда в ней появляются элементы (телефон). */
.tabs { position: absolute; display: flex; align-items: center; }
body.js .tabs { opacity: 0; pointer-events: none; transition: opacity .5s var(--ease-out); }
body.js .tabs.lit { opacity: 1; pointer-events: auto; }
.tab { display: grid; place-items: center; padding: 0; border: 0; background: none; color: var(--ink-2); cursor: pointer; transition: color var(--dur-base) var(--ease-out), filter var(--dur-base) var(--ease-out); }
.tab svg { width: 100%; height: 100%; display: block; }
/* Вкладка появившегося элемента: две буквы вместо значка — рисовать иконку под
   каждую часть значит завести одиннадцать значков и словарь к ним. */
.tabmark { font-family: var(--font-mono); font-size: 10px; letter-spacing: .04em; text-transform: uppercase; }
.tab.is-on { color: var(--accent); filter: drop-shadow(0 0 10px color-mix(in srgb, var(--accent) 40%, transparent)); }
.tab:hover { color: var(--ink); }
.tab:focus-visible, .send:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

.well {
  position: absolute; display: flex; align-items: center;
  left: 24px; right: 76px; top: 50.3px; height: 39.7px;
  border-radius: 20px; padding: 0 20px;
  border: 1px solid var(--glass-border); background: rgba(255,255,255,.02);
  box-shadow: var(--edge-sink);
  transition: background 1.1s var(--ease-out), border-color 1.1s var(--ease-out), box-shadow 1.1s var(--ease-out);
}
/* ТВОЙ ХОД. Айрис дописала — поле ввода за полсекунды уходит в голубой: не
   вспышкой и не пульсацией, а так, чтобы заметилось краем глаза и не отвлекало
   от чтения. Гаснет, как только человек начал печатать: подсказывать тому, кто
   уже отвечает, нечего. */
/* Полсекунды читались щелчком: цвет успевал «переключиться», а не проступить.
   Секунда с небольшим — это уже проступание, которое ловишь краем глаза. */
.dock.turn .well {
  background: color-mix(in srgb, var(--accent) 5%, transparent);
  border-color: color-mix(in srgb, var(--accent) 30%, transparent);
  box-shadow: var(--edge-sink), 0 0 26px -10px color-mix(in srgb, var(--accent) 40%, transparent);
}
.dock.turn .well input::placeholder { color: color-mix(in srgb, var(--accent) 45%, white); transition: color 1.1s var(--ease-out); }
.well input {
  flex: 1; min-width: 0; height: 100%; border: 0; background: transparent; outline: none;
  color: var(--ink); font-family: var(--font-ui); font-size: clamp(14px, 1.15vw, 17px);
  opacity: 1; transition: opacity .28s var(--ease-out);
}
body.js[data-act="hush"] .well input { opacity: 0; }
.well input::placeholder { color: var(--ink-3); }

/* Микрофон .mic живёт внутри поля ввода и молчит, пока к нему не обратились:
   голос — запасная дверь, целевое действие пульта остаётся у .send. Во время
   записи кнопка дышит акцентом — единственный признак, что слушаем. */
/* Авторский display сильнее UA-правила для [hidden]: без этой строки кнопка в
   браузере без Web Speech API стояла бы видимой и мёртвой. */
.mic[hidden] { display: none; }
.mic {
  flex: 0 0 auto; display: grid; place-items: center; cursor: pointer;
  width: 32px; height: 32px; margin-right: -12px; border: 0; padding: 0;
  border-radius: 50%; background: transparent; color: var(--ink-3);
  transition: color var(--dur-base) var(--ease-out), background var(--dur-base) var(--ease-out);
}
.mic svg { width: 20px; height: 20px; }
.mic:hover { color: var(--ink); }
.mic:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Дыхание записи — цветом заливки, не кольцом и не геометрией: ровный ореол
   запрещён каноном, а движение цвета на 32px стоит дёшево. */
.mic.live {
  color: var(--accent);
  background: color-mix(in srgb, var(--accent) 10%, transparent);
  animation: mic-breathe 1.5s var(--ease-out) infinite;
}
@keyframes mic-breathe {
  50% { background: color-mix(in srgb, var(--accent) 28%, transparent); }
}
/* Отказ в доступе — видимое состояние, а не исчезнувшая кнопка: перечёркнутый
   микрофон остаётся на месте, повторный клик пробует снова. */
.mic.denied { color: var(--ink-3); opacity: .6; }
.mic.denied::after {
  content: ''; position: absolute; left: 8px; right: 8px; top: 50%;
  height: 1.5px; background: currentColor; transform: rotate(-45deg);
}
.mic { position: relative; }
/* Когда в нише вместо поля ввода стоит заказ или сборка — микрофону тоже нечего делать. */
.well:has(.order-go) > .mic, .well:has(.build-world) > .mic { display: none; }

/* Кнопка отправки — целевое действие пульта, и выглядеть она обязана как
   действие. Единственная реакция — блик при наведении: отклик на человека, а не
   попытка удержать внимание любой ценой. */
.send {
  position: absolute; display: grid; place-items: center; cursor: pointer; border: 0; padding: 0;
  right: 24px; top: 48px; width: 44px; height: 44px;
  border-radius: 50%; overflow: hidden;
  font-size: 18px; line-height: 1; color: #06222A;
  background: linear-gradient(140deg, #5BFFC4, #27F0DE 45%, var(--violet));
  /* Резкая кромка сверху + короткий цветной отброс + глубина. Ровный ореол по
     кругу читается наклейкой; слои держат край и дают предмету вес. */
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .5),
              0 10px 22px -12px rgba(39, 240, 222, .55),
              0 18px 34px -22px rgba(0, 0, 0, 1);
  transition: transform 140ms cubic-bezier(.34, 1.56, .64, 1), box-shadow var(--dur-base) var(--ease-out);
}
.send > span { position: relative; z-index: 1; transform: translateY(-1px); }
.send::after {
  content: ''; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background: linear-gradient(115deg, transparent 38%, rgba(255, 255, 255, .55) 50%, transparent 62%);
  transform: translateX(-120%);
}
.send:hover {
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .6),
              0 14px 28px -12px rgba(39, 240, 222, .75),
              0 20px 38px -22px rgba(0, 0, 0, 1);
}
.send:hover::after { animation: send-shine 600ms var(--ease-out); }
@keyframes send-shine { to { transform: translateX(120%); } }
.send:active { transform: scale(.9); transition-duration: 90ms; }

/* Есть что отправить — свет усиливается и уходит в фиолетовый. */
.dock:has(#ask:not(:placeholder-shown)) .send {
  background: linear-gradient(140deg, #27F0DE, var(--violet) 55%, #FF2E8A);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .55),
              0 14px 28px -12px rgba(124, 108, 240, .7),
              0 20px 38px -22px rgba(0, 0, 0, 1);
}

/* Перелив по обводке: три стопа градиента меняют цвет со сдвигом фазы, и по
   контуру бежит волна. Анимируем цвет, а не геометрию и не фильтр — движение
   стоит кадров, и платить за него перерисовкой слоя мы не будем. */
#dockStroke stop:nth-child(1) { animation: hue-a 9s linear infinite; }
#dockStroke stop:nth-child(2) { animation: hue-b 9s linear infinite; }
#dockStroke stop:nth-child(3) { animation: hue-c 9s linear infinite; }
#dockStroke stop { transition: stop-color .72s var(--ease-out); }
@keyframes hue-a { 0%,100% { stop-color: #27F0DE } 33% { stop-color: var(--violet) } 66% { stop-color: #FF2E8A } }
@keyframes hue-b { 0%,100% { stop-color: var(--violet) } 33% { stop-color: #FF2E8A } 66% { stop-color: #27F0DE } }
@keyframes hue-c { 0%,100% { stop-color: #FF2E8A } 33% { stop-color: #27F0DE } 66% { stop-color: var(--violet) } }

/* ── Пульт в такте greet: единственный светящийся объект ──────────────────
   Пока человек не ответил, пульт — это и есть действие страницы. Свет корпуса и
   пульсация гнезда держатся только здесь и спадают, как только разговор пошёл:
   постоянная пульсация через всю сессию кричит, а не приглашает. */
body.js[data-act="talk"] .dock, body.js[data-act="hush"] .dock { filter: drop-shadow(0 0 26px color-mix(in srgb, var(--accent) 40%, transparent)); }
/* Пульсации гнезда больше нет: знак «твой ход» даёт поле ввода одним спокойным
   переходом, а две подсказки об одном спорят друг с другом. */


/* ── Подвал ──────────────────────────────────────────────────────────────── */
.foot {
  position: relative; z-index: 1;
  margin-top: clamp(34px, 7vh, 78px);
  padding-block: clamp(28px, 5vh, 56px) clamp(28px, 4vh, 48px);
  border-top: 1px solid var(--glass-hairline);
}
.ways { margin: 0 0 14px; font-size: clamp(14px, 1.1vw, 16px); line-height: 1.7; color: var(--ink-2); }
.ways a { margin-left: 14px; font-weight: var(--weight-medium); }
.links { display: flex; gap: 18px; flex-wrap: wrap; font-size: 13px; }
.links a { color: var(--ink-2); }
.links a:hover { color: var(--accent); }
.sign { margin: clamp(22px, 3vh, 34px) 0 0; font-family: var(--font-mono); font-size: 11px; letter-spacing: .1em; color: var(--ink-3); }

/* ── Узкие экраны ─────────────────────────────────────────────────────────
   Пульт остаётся каноническим (полка 168 единиц), поэтому меняется не он, а то,
   что вокруг: карточки в один столбец, шапка в две строки. */
/* ── Телефон: элементы становятся вкладками пульта ───────────────────────
   Сетка вокруг пульта — приём широкого экрана. На узком те же самые элементы
   живут в полке: видно один, остальные переключаются. Второго набора карточек
   для этого не заводим — те же узлы, другая раскладка. */
@media (max-width: 899px) {
  body.js[data-stage="exploded"] .stage,
  body.js[data-stage="assembling"] .stage,
  body.js:is([data-stage="assembled"], [data-stage="intent-simulation"], [data-stage="intent-brief"]) .stage {
    justify-content: flex-start; overflow-y: auto;
  }
  body.js[data-stage="exploded"] .console,
  body.js[data-stage="assembling"] .console,
  body.js:is([data-stage="assembled"], [data-stage="intent-simulation"], [data-stage="intent-brief"]) .console {
    flex: 0 0 auto; min-height: max-content;
  }
  body.js[data-stage="exploded"] .grid-l,
  body.js[data-stage="exploded"] .grid-r,
  body.js[data-stage="exploded"] .grid-b,
  body.js[data-stage="assembling"] .grid-l,
  body.js[data-stage="assembling"] .grid-r,
  body.js[data-stage="assembling"] .grid-b { max-height: none; }
  .console {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto;
    /* Пульт идёт СРАЗУ за дисплеем и на телефоне: между ними ничего не встаёт,
       иначе сцепка разъезжается на сто с лишним точек (замер 11.08). */
    grid-template-areas: "thread" "dock" "left" "right" "below";
    gap: 0;
  }
  /* ПУЛЬТ ПРИБИТ К НИЗУ ЭКРАНА (решение founder-а 14.08): телефон — мессенджер,
     а не витрина. Консоль ровно во вьюпорт, ряд thread забирает весь свободный
     рост, пульт уходит к нижней кромке, и реплика агента печатается прямо над
     пультом. Разговор при этом прокручивается ВНУТРИ своего ряда — правило
     «лента без потолка» ниже по файлу растило консоль вместе с тредом и
     сталкивало пульт под сгиб. Режим чтения не трогаем: там страница —
     обычный документ. */
  body.js:not([data-act="read"]):not([data-stage]) .stage,
  body.js:is([data-stage="hello"], [data-stage="talk"], [data-stage="theme"], [data-stage="tabs"], [data-stage="order"], [data-stage="built"], [data-stage="assembled"], [data-stage="intent-simulation"], [data-stage="intent-brief"]) .stage { padding-block: 0; }
  /* Пиновка действует по ПОЗИТИВНОМУ списку разговорных стадий (плюс старт без
     data-stage). Кокпит-стадии V2-заказов (building/cockpit/simulation/brief)
     живут своим каноном «страница-документ со sticky-пультом» — кламп 100svh
     обрезал их панели и вешал пульт посреди контента. Стадии exploded и
     assembling клампит собственное правило стопки ниже. */
  body.js:not([data-act="read"]):not([data-stage]) .stage .console,
  body.js:is([data-stage="hello"], [data-stage="talk"], [data-stage="theme"], [data-stage="tabs"], [data-stage="order"], [data-stage="built"], [data-stage="assembled"], [data-stage="intent-simulation"], [data-stage="intent-brief"]) .stage .console {
    grid-template-rows: minmax(0, 1fr) auto auto auto auto;
    height: 100svh; max-height: 100svh; min-height: 0;
    padding-bottom: env(safe-area-inset-bottom, 0px);
  }
  body.js:not([data-act="read"]) .display { max-height: 100%; min-height: 0; }
  body.js:not([data-act="read"]) .display .thread {
    max-height: none; overflow-y: auto; overscroll-behavior: contain;
  }
  /* Строки схем/форматов в треде описаны вне медиа-блока: с телефоном 9:16 они
     живут в разговоре на любой ширине (решение 14.08). */
  /* Поля возвращаются: на 390 точках полоса во всю ширину упирается в бортик
     экрана, а корпус стоит в полях — сетка и пульт разъезжаются по краю. */
  body.js:not([data-act="read"]) .page { padding-inline: var(--gutter); }
  /* Линии сетки и пустые сегменты — язык широкого экрана. На телефоне то, что
     впереди, показывает полка пульта, а пять пустых полос на 390 точках съели бы
     весь экран. Остаются те же узлы, без обвязки. */
  .grid { flex-direction: column; border: 0; }
  .grid .cell, .grid-b .cell + .cell { width: 100%; border: 0; box-shadow: none; }
  /* Экран на телефоне ВИДЕН. Прежде карточки прятались, а показывалась одна по
     полке пульта — механика, которой больше нет: вкладки стали плитками, и на
     экране и так стоит ровно один собранный экран. Пока правило жило, телефон
     не видел даже блока выбора схемы и застревал на втором такте (замер
     11.08). */
  .grid .card { display: flex; width: 100%; }
  .below { max-width: none; }
  .stage { height: auto; min-height: 100svh; }
  .display { max-height: none; }
  /* На телефоне сцена прокручивается по построению, поэтому потолок ленте не
     нужен: восемьдесят точек разговора уезжали в её собственную прокрутку —
     прокрутка внутри прокрутки, и человек не понимал, что текст ещё есть. */
  .display .thread { max-height: none; overflow: visible; }
  .hero h1 { max-width: none; }
  .msg { max-width: 88%; }
  /* На телефоне фотографии нет — держать речь в левой колонке незачем. */
  .msg.it { max-width: 94%; }
  .thread { max-height: min(40svh, 300px); }
  body.js[data-stage="exploded"] .console,
  body.js[data-stage="assembling"] .console {
    min-height: 100svh; grid-template-rows: minmax(0, 1fr) auto;
    grid-template-areas: "thread" "dock";
  }
  /* Шаги превью на телефоне СЛОЖЕНЫ СТОПКОЙ и листаются свайпом по вертикали,
     а не сменяют друг друга по таймеру: первый экран уезжал раньше, чем его
     дочитывали (разбор founder-а 14.08). Каждый шаг на 56 точек ниже кромки
     контейнера; последний держит полную высоту. Когда снизу срендерился
     следующий, предыдущий теряет :last-child и переходом с лёгким отскоком
     «подпрыгивает» на эти 56 точек — показывает верх нового шага и всё:
     дальше человек листает сам, принудительной прокрутки нет. */
  /* Консоль стопки зажата ровно во вьюпорт: прежнее min-height: max-content
     растягивало страницу под контент — прокручивалась сцена, а не стопка.
     Селектор через .stage поднимает специфичность: ниже по файлу, вне
     медиазапроса, живёт десктопная мозаика exploded с тремя рядами
     (minmax(250px,1fr) auto minmax(0,.62fr)), и при равной специфичности она
     перебивала мобильные два ряда порядком в файле. Сцена в стопке без полей:
     пульт стоит на самой нижней кромке экрана. */
  body.js:is([data-stage="exploded"], [data-stage="assembling"]) .stage { padding-block: 0; }
  body.js:is([data-stage="exploded"], [data-stage="assembling"]) .stage .console {
    flex: 0 0 auto; height: 100svh; max-height: 100svh; min-height: 0;
    grid-template-rows: minmax(0, 1fr) auto;
    grid-template-areas: "thread" "dock";
  }
  /* Ветки сетки на телефоне в стопке не участвуют, но атрибут hidden проигрывает
     их display:flex, и пустые ветки создавали неявные ряды, крадя высоту у
     ряда thread. Глушим их явно — области "left/right/below" в этой сетке нет. */
  body.js:is([data-stage="exploded"], [data-stage="assembling"]) .console > :is(.grid-l, .grid-r, .below) {
    display: none;
  }
  body.js[data-stage="exploded"] .display.mobile-preview-active,
  body.js[data-stage="assembling"] .display.mobile-preview-active {
    display: flex; align-self: stretch; height: auto; max-height: none; min-height: 0;
    overflow-y: auto; overscroll-behavior: contain;
    scroll-snap-type: y mandatory;
  }
  .display.mobile-preview-active > .intent-fragment {
    flex: 0 0 auto; border: 0; width: 100%; overflow: hidden;
    min-height: calc(100% - 56px); max-height: calc(100% - 56px);
    scroll-snap-align: start;
    transition: min-height 420ms cubic-bezier(.3, 1.4, .55, 1), max-height 420ms cubic-bezier(.3, 1.4, .55, 1);
  }
  .display.mobile-preview-active > .intent-fragment:last-child { min-height: 100%; max-height: 100%; }
  /* Подошва-сцепка в стопке отцеплена: прокрутка рисует контент и в padding
     подошвы, и текст следующего шага пробивался сквозь прозрачную нишу пульта.
     Пульт в стопке — отдельный предмет поверх, как в мессенджере. */
  .display.mobile-preview-active { padding-bottom: 0; }
  .display.mobile-preview-active::before, .display.mobile-preview-active::after { inset: 0; }
  .display.mobile-preview-active .disp-shell { display: none; }
  body.js:is([data-stage="exploded"], [data-stage="assembling"]) .stage .console .dock { margin-top: 0; }
  .display.mobile-preview-active .intent-fragment-body,
  .display.mobile-preview-active .intent-screen { height: 100%; min-height: 0; }
  .display.mobile-preview-active .intent-preview-world {
    max-height: none; grid-template-columns: 1fr; align-content: center;
  }
  body.js .display.mobile-preview-active .intent-fragment.appear {
    transform: translateY(calc(var(--collapse-y) + 22px)) scale(.96);
  }
  body.js .display.mobile-preview-active .intent-fragment.appear.in { transform: none; }
  body.js .display.mobile-preview-active .intent-fragment.appear.in.to-dock {
    opacity: 0; transform: translateY(var(--collapse-y)) scale(.84); filter: blur(4px);
  }
}
@media (max-width: 620px) {
  /* Кнопка встаёт в строку с метриками: на телефоне шапка иначе съедает больше
     трети экрана. */
  .hero { padding-top: 20px; }
  .hero h1 { margin-bottom: 12px; }
  .blk[data-shape="cards"], .blk[data-shape="steps"] { grid-template-columns: 1fr; }
}
/* ЗАДАНИЕ НЕ ВЫНОСИТ СЕБЯ ЗА ПОТОЛОК. Ветка сетки прижата к пульту и растёт
   вверх: на 1280×720 список пожеланий уходил выше кромки окна, и первым уезжало
   то, ради чего он заведён (замер probe 11.08 — верх −79, а вниз при этом ноль).
   На низком окне показываем меньше строк; правду о целом держит счётчик в
   подписи, полный список едет в ссылке заказа. */
@media (max-height: 820px) and (min-width: 900px) {
  .card[data-part="spec"] .p-fix li.more,
  .card[data-part="spec"] .p-fix li:nth-last-child(-n+2) { display: flex; }
  /* Воздуха меньше, света столько же: сжимается отступ, а не кромка. */
  .card { padding-block: 9px; gap: 7px; }
}
/* Низкие окна (ноутбук 1024×600, ландшафт телефона): герой ужимается первым —
   пульт и реплика важнее заголовка. */
@media (max-height: 680px) and (min-width: 900px) {
  .hero { padding-top: 24px; }
  .hero h1 { font-size: clamp(24px, 2.6vw, 34px); margin-bottom: 8px; }
  .lead { font-size: 14px; }
  .thread { max-height: min(38svh, 260px); }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .msg, .card, .chips-next, .thread-history > li { animation: none; }
  .send::after, #dockStroke stop, .mic.live { animation: none; }
  .caret, .p-flag i { animation: none; }
  .work-status > i { animation: none; }
  .work-meter > i { transition: none; }
  body.js .appear { opacity: 1; transform: none; }
  body.js .blk { opacity: 1; transform: none; }
  .send:hover, .pilot:hover, .chip:hover { transform: none; }
  .intent-guide, .intent-screen.guide-focus-action .world-action,
  [data-row-key].guide-result { animation: none; }
  [data-row-key] { transition: none; }
}

/* Ожидание живого ответа — это компактный журнал работы, не немые три точки.
   Меняется только подпись и полоска: история не прыгает и не растёт сама. */
.msg.it.working {
  width: min(34rem, 82%); max-width: min(34rem, 82%); min-height: 54px;
  display: grid; gap: 10px; align-content: center;
  padding-top: 10px; padding-bottom: 10px;
}
.work-status { display: flex; align-items: center; min-width: 0; gap: 9px; }
.work-status > i {
  width: 7px; height: 7px; flex: 0 0 auto; border-radius: 50%;
  background: var(--accent);
  animation: pulse 1.1s ease-in-out infinite;
}
.work-status b {
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  color: color-mix(in srgb, var(--accent) 32%, white);
  font-size: 13px; font-weight: var(--weight-medium); letter-spacing: .01em;
}
.work-status small {
  margin-left: auto; flex: 0 0 auto; color: #83919a;
  font: 9px/1 var(--font-mono); letter-spacing: .12em;
}
.work-meter {
  position: relative; display: block; width: 100%; height: 2px; overflow: hidden;
  background: color-mix(in srgb, var(--accent) 14%, transparent);
}
.work-meter > i {
  position: absolute; inset: 0 auto 0 0; width: var(--work-progress, 33%);
  background: linear-gradient(90deg, var(--accent), var(--like));
  transition: width 420ms var(--ease-out);
}

/* ══ ПОСТАНОВКА ЗАДАЧИ: ПЯТЬ ТАКТОВ ═══════════════════════════════════════
   Сценарий founder-а 11.08. Человек выбирает схему, называет инвентарь, жмёт
   «собрать» и получает решение с одной кнопкой. Стили ниже — ровно эти пять
   тактов и ничего сверх них. */

/* ── Такт 1.5: формат одним вопросом ────────────────────────────────────── */
/* Опции .opt — продуманные ответы на вопрос агента, а не товарные карточки:
   строка-кнопка с названием формата и одной строкой «что вы получаете».
   Один ожидаемый ход — блок стоит один, без соседей (решение 14.08). */
.opt-row { display: flex; flex-direction: column; gap: 10px; }
.opt {
  cursor: pointer; font: inherit; color: inherit; text-align: left;
  display: flex; flex-direction: column; gap: 4px; padding: 12px 14px;
  background: rgba(255,255,255,.02); border: 1px solid var(--glass-border);
  border-radius: 14px;
  transition: border-color var(--dur-base) var(--ease-out), background var(--dur-base) var(--ease-out);
}
.opt b {
  font-family: var(--font-mono); font-size: 10px; letter-spacing: .18em; text-transform: uppercase;
  color: color-mix(in srgb, var(--accent) 82%, white);
}
.opt span { font-size: 13px; color: var(--ink-2); }
.opt:hover, .opt:focus-visible {
  border-color: color-mix(in srgb, var(--accent) 45%, transparent);
  background: color-mix(in srgb, var(--accent) 6%, transparent);
}

/* ── Такт 2: схемы одним блоком ─────────────────────────────────────────── */
.sch-row { display: flex; gap: 10px; flex-wrap: wrap; }
/* Схемы и форматы живут В РАЗГОВОРЕ над пультом на любой ширине: корпус —
   телефон 9:16, и ход человека стоит внутри него строками (лицо схемы слева,
   имя справа; вся строка — одна кнопка). Скоуп .thread поднимает специфичность
   над базовыми .sch-стилями. */
.thread .schemes, .thread .formats { width: 100%; }
.thread .schemes .card, .thread .formats .card { width: 100%; }
.thread .sch-row { flex-direction: column; gap: 12px; }
.thread .sch { flex-direction: row; align-items: center; gap: 14px; flex: 0 0 auto; }
.thread .sch-face { width: 92px; flex: 0 0 92px; height: 46px; padding: 9px 10px 0; gap: 4px; }
.sch {
  flex: 1 1 140px; min-width: 0; cursor: pointer; border: 0; padding: 0;
  display: flex; flex-direction: column; gap: 8px; text-align: left;
  font: inherit; color: inherit; background: none;
}
/* Лицо схемы — не кружок с краской, а кусок будущего интерфейса: поле, свет по
   кромке и три строки. Человек выбирает среду, в которой будет работать. */
.sch-face {
  display: block; height: 62px; position: relative; overflow: hidden;
  background: linear-gradient(180deg, color-mix(in srgb, var(--sac) 8%, var(--sbg)), var(--sbg) 74%);
  box-shadow: inset 0 1px 0 color-mix(in srgb, var(--sac) 62%, transparent),
              inset 1px 0 0 color-mix(in srgb, var(--sac) 20%, transparent),
              inset -1px 0 0 color-mix(in srgb, var(--sac) 20%, transparent),
              inset 0 -1px 0 rgba(0, 0, 0, .5);
  padding: 12px 12px 0; display: flex; flex-direction: column; gap: 6px;
}
.sch-face i { display: block; height: 6px; opacity: .85; }
.sch-face i:nth-child(1) { width: 88%; background: color-mix(in srgb, var(--s-text) 78%, var(--sbg)); }
.sch-face i:nth-child(2) { width: 62%; background: var(--sac); }
.sch-face i:nth-child(3) { width: 38%; background: var(--s-alt); opacity: .72; }
.sch b {
  font-family: var(--font-mono); font-size: 9px; letter-spacing: .2em; text-transform: uppercase;
  color: color-mix(in srgb, var(--sac) 82%, white);
}
.sch:hover .sch-face, .sch:focus-visible .sch-face { box-shadow: inset 0 0 0 1px var(--sac), 0 0 26px -6px var(--sac); }

/* ── Пульт замерзает, когда ход за человеком ────────────────────────────── */
/* Шаг схем: пульт приглушён как «второстепенный», но ввод живой — текст на этом
   шаге ведёт ветка stage()==='theme' в ask() (прогон founder-а 14.08). */
.well.frozen { opacity: .75; }
body.js[data-stage="theme"] .send { opacity: .35; pointer-events: none; }

/* ФОКУС НА ТОМ, ЧТО ГОВОРИТ АГЕНТ. Две части одной болезни (разбор founder-а
   11–12.08). Первая: её реплика жила в верхней панели, а нажимать надо было под
   пультом — 238 точек и корпус между словом и кнопкой. Ту же фразу ставим
   заголовком блока действия. Вторая: реплики шли одного веса, и отработавшая не
   отличалась от текущей. Вес держит только последняя. */
.k-title.k-said {
  font-family: var(--iris-font-body, var(--font-ui)); font-weight: var(--weight-medium);
  font-size: clamp(15px, 1.35vw, 18px); line-height: 1.4; letter-spacing: -.01em;
  color: color-mix(in srgb, var(--neon, var(--accent)) 34%, white);
}
/* Селектор бьёт `body.js .appear.in`, который ставит opacity: 1 при появлении:
   без равной силы приглушение не наступало вовсе (замер 12.08). */
body.js .msg.it.appear:not(.now), body.js .msg.it:not(.now) {
  opacity: .42;
  transition: opacity var(--dur-base) var(--ease-out);
}
.msg.it:not(.now) .q {
  color: color-mix(in srgb, var(--iris-text, var(--ink)) 40%, transparent);
  background: none; box-shadow: none; text-shadow: none;
}
body.js .msg.it:not(.now):hover { opacity: .8; }

/* ── Такт 3: вкладки демо предлагает Айрис ──────────────────────────────
   Человек НЕ печатает с нуля: он выключает лишнее. Прежний пульт из нескольких
   строк ввода не объяснял, что писать, вылезал за корпус, а «готово» на пустых
   строках молчало (разбор founder-а 11.08). */
.pick-row { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 4px; }
.pick {
  display: flex; align-items: center; gap: 8px; cursor: pointer; border: 0;
  padding: 9px 13px 9px 10px; font: inherit; font-size: 13.5px;
  color: color-mix(in srgb, var(--iris-text, var(--ink)) 40%, white);
  background: none; opacity: .4;
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--neon, var(--accent)) 20%, transparent);
}
.pick svg { width: 17px; height: 17px; flex-shrink: 0; }
.pick.on {
  opacity: 1; color: var(--neon, var(--accent));
  background: color-mix(in srgb, var(--neon, var(--accent)) 10%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--neon, var(--accent)) 52%, transparent);
}
.pick-go {
  align-self: flex-start; margin-top: 12px; border: 0; cursor: pointer; font: inherit;
  font-family: var(--font-mono); font-size: 10px; letter-spacing: .18em; text-transform: uppercase;
  padding: 11px 18px; font-weight: var(--weight-semibold);
  background: var(--iris-accent, var(--accent)); color: var(--iris-on-accent, var(--on-accent));
}

/* ЭКРАН ДЕМО В КОРПУСЕ. Плитка пульта переключает панель: на месте разговора
   встаёт придуманный экран, той же ширины и с той же прокруткой. Отсюда и
   респонсивность — сущности библиотеки строятся сетками auto-fill и занимают
   всю доступную ширину, какой бы она ни была (решение founder-а 11.08). */
/* Атрибут hidden слабее любого display в правиле: без этой строки разговор
   оставался под экраном, и панель читалась двумя вещами разом (прогон 11.08). */
.thread[hidden], .screen[hidden] { display: none; }
.screen {
  position: relative; z-index: 1; flex: 1 1 auto; min-height: 0;
  display: flex; flex-direction: column;
  container-type: inline-size;
  overflow-y: auto; overscroll-behavior: contain; scrollbar-width: none;
}
.screen::-webkit-scrollbar { display: none; }
.screen .card {
  height: auto; min-height: 100%; background: none; box-shadow: none;
  padding: clamp(16px, 1.6vw, 24px) clamp(16px, 1.8vw, 26px) clamp(22px, 2vw, 30px);
}
.screen .card::after { content: none; }
.screen .k-title { font-size: clamp(20px, 1.9vw, 26px); margin-bottom: 6px; }
.screen .k-slots { grid-template-columns: repeat(auto-fill, minmax(clamp(120px, 14vw, 180px), 1fr)); }
.screen .k-goods { grid-template-columns: repeat(auto-fill, minmax(clamp(132px, 15vw, 200px), 1fr)); }
.screen .k-dash { grid-template-columns: repeat(auto-fit, minmax(clamp(104px, 12vw, 160px), 1fr)); }
.screen .k-msg { max-width: min(88%, 560px); }

/* Старые brief-ссылки v1 по-прежнему открывают один прежний экран и освобождают
   для него сетку. Новая сборка кокпита этим состоянием не пользуется. */
body.js[data-stage="order"] .grid { min-height: 0; }

/* ── Такт 4: плитки вкладок и кнопка сборки ─────────────────────────────── */
.tabs.tiles { gap: 6px; }
.tabs.tiles .tile {
  display: grid; place-items: center;
  background: color-mix(in srgb, var(--iris-accent, var(--accent)) 8%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--iris-accent, var(--accent)) 26%, transparent);
  color: color-mix(in srgb, var(--iris-accent, var(--accent)) 78%, white);
}
.tabs.tiles .tile svg { width: 62%; height: 62%; }
.tabs.tiles .tile.is-on {
  background: color-mix(in srgb, var(--iris-accent, var(--accent)) 22%, transparent);
  box-shadow: inset 0 0 0 1px var(--iris-accent, var(--accent));
}
/* КНОПКА СТОИТ В САМОМ ПУЛЬТЕ, а не парит над ним. Плавающая перекрывалась
   панелью разговора — и клик уходил в неё (прогон 11.08). А по сценарию это и
   есть пульт: он превращается в кнопку, когда ход за ней. */
/* Кнопка ЗАПОЛНЯЕТ ГНЕЗДО, а не лежит на корпусе. Своя высота и свои поля делали
   из неё прямоугольник поверх пульта, накрывавший и торец, и плитки (разбор
   founder-а 11.08). Высоту и скругление берёт у гнезда. */
.order-go {
  flex: 1 1 auto; min-width: 0; height: 100%; border-radius: inherit;
  border: 0; cursor: pointer; font: inherit; font-family: var(--font-mono);
  text-transform: uppercase; text-align: center;
  background: var(--iris-accent, var(--accent)); color: var(--iris-on-accent, var(--on-accent));
  font-weight: var(--weight-semibold);
}
.well:has(.order-go) > input#ask { display: none; }

/* ── Intent-world: разложить и физически собрать ────────────────────────── */
.build-world {
  flex: 1 1 auto; min-width: 0; height: 100%; border: 0; border-radius: inherit;
  cursor: pointer; padding: 0 20px; font-family: var(--font-mono); font-size: clamp(12px, 1.2vw, 16px);
  font-weight: var(--weight-semibold); letter-spacing: .18em; text-transform: uppercase;
  color: var(--iris-on-accent, var(--on-accent)); background: var(--iris-accent, var(--accent));
  /* Свет собран у кромки, а не облаком на полсцены: дорогая кнопка — это
     резкий контраст заливки с чёрным, не ореол. */
  box-shadow: 0 0 22px -12px var(--iris-accent, var(--accent));
}
.well:has(.build-world) > input#ask { display: none; }
/* На такте сборки кнопка заменяет весь input, включая место прежней круглой
   отправки: это одно действие пульта, а не кнопка рядом с пустым хвостом поля. */
.well:has(.build-world) {
  right: var(--dock-edge, 20px); width: auto !important; padding: 0 !important;
}
.well:has(.build-world) ~ .send { display: none; }
body.js:is([data-stage="exploded"], [data-stage="assembling"]) .send { opacity: .28; pointer-events: none; }

.intent-fragment {
  /* min-width: 0 обязателен: флекс/грид-элемент по умолчанию не сжимается ниже
     min-content содержимого, и на 100% зуме нижний сосед выталкивался за кромку
     экрана (скрин founder-а 14.08, «Доска улик»). */
  min-width: 0; min-height: 150px; overflow: hidden;
  container-type: inline-size;
  /* Дорогой контуринг: ветка сцены — чистый чёрный, поверхность несут плиты
     с линиями; тонированная подложка добавляла рассеянный шум. */
  background: transparent;
  /* Вставка скрытого фрагмента — подготовка, а не движение. Transition включён
     только у .appear.in. */
  transition: none;
}
/* Карточка ПРИХОДИТ на экран плавным подъёмом (plateIn из референса v3
   founder-а 14.08): короткий сдвиг снизу с выкатом по мягкой кривой — вместо
   прежнего рождения из точки пульта, которое читалось вспышкой. Возврат в
   пульт при сборке остаётся прежним: собирание — это движение К пульту. */
body.js .intent-fragment.appear {
  opacity: 0;
  transform: translateY(12px);
}
body.js .intent-fragment.appear.in {
  opacity: 1; transform: none; filter: none;
  transition: opacity .5s cubic-bezier(.16, 1, .3, 1), transform .7s cubic-bezier(.16, 1, .3, 1);
}
body.js .intent-fragment.appear.in.to-dock {
  opacity: 0;
  transform: translate(var(--collapse-x), var(--collapse-y)) scale(.12);
  filter: blur(5px);
}
.intent-fragment-body { min-height: 0; height: 100%; overflow: hidden; }
/* Тред и роль в превью — та же плита v3: единая панель со своим контуром,
   а не текст на подложке ветки. */
.intent-fragment :is(.intent-thread, .intent-role) {
  border-radius: 20px;
  border: 1px solid color-mix(in srgb, var(--iris-accent, var(--accent)) 22%, rgba(244, 238, 240, .06));
  background: #0B0A0E;
  box-shadow: 0 26px 60px -46px rgba(0, 0, 0, .9);
}
/* Внутри плит экран — плоский чёрный: фирменный градиент экрана остаётся в
   собранном телефоне, на сцене он читался цветным туманом. */
.intent-fragment .intent-screen { background: #070609; }
.intent-fragment .evt-bubble { box-shadow: none; }

/* СОБРАННЫЙ ЛАЙНИНГ (решение founder-а 14.08: «спокойно, но темно — это
   вайб»). Внутри плиты ровно две линии: тихая структурная и одна акцентная
   у главного предмета. Внутренние блоки не носят собственных цветных фонов
   и рамок — краску несут текст, чип и пилюля. */
.intent-fragment :is(.k-card, .k-rulebook li, .thread-embed, .thread-event.tool, .thread-event.state) {
  background: transparent;
  box-shadow: inset 0 0 0 1px rgba(244, 238, 240, .07);
}
.intent-fragment .thread-event.result {
  background: transparent;
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--iris-accent, var(--accent)) 30%, transparent);
}
/* Типографика модуля не спорит с шапкой карточки: внутри плиты один масштаб —
   заголовки библиотечных рендеров прижаты к тексту. */
.intent-fragment .intent-preview-module :is(h3, h4) {
  font-size: 13px; line-height: 1.3; letter-spacing: 0;
}
.intent-fragment .intent-preview-module .role-avatar { width: 34px; height: 34px; font-size: 14px; }
.intent-fragment .intent-screen { min-height: 150px; max-height: 230px; }
.intent-fragment .world-actions { display: none; }
.intent-fragment button { pointer-events: none; }

/* Перед сборкой занятые ветки образуют мозаику вокруг пульта. Никакой ветке не
   резервируется пустое место: JS скрывает незанятые области, а оставшиеся
   поверхности делят всю доступную меру. */
/* Сцена слайдера: дисплей растягивается на все колонки и весь рост — карусель
   широкая, соседние силуэты видны в глубине; пульт остаётся по центру внизу. */
body.js:is([data-stage="exploded"], [data-stage="assembling"]) .console {
  grid-template-rows: minmax(0, 1fr) auto 0;
  height: calc(100svh - 2 * clamp(12px, 2vh, 24px));
  max-height: calc(100svh - 2 * clamp(12px, 2vh, 24px));
}
/* СЦЕНА = ТОТ ЖЕ ВЕРТИКАЛЬНЫЙ ТЕЛЕФОН (решение founder-а 14.08, финал):
   активный силуэт заполняет раму 9:16 целиком, рельса и кнопка сборки в
   пульте, а соседние слайды выглядывают ПОЗАДИ стекла по бокам — глубина
   живёт за рамой, не внутри неё. */
body.js:is([data-stage="exploded"], [data-stage="assembling"]) .display {
  height: 100%; max-height: 100%; min-height: 0;
}
.display.slider-active { display: flex; flex-direction: column; overflow: visible; }

/* ── Слайдер сцены: объёмная карусель силуэтов ──────────────────────────── */
.scene-slider {
  position: relative; flex: 1 1 auto; min-height: 0;
  display: flex; flex-direction: column; padding: 16px 0 4px;
}
.slider-track {
  position: relative; flex: 1 1 auto; min-height: 0;
  perspective: 1400px; transform-style: preserve-3d;
  touch-action: pan-y;
}
.scene-slide {
  position: absolute; left: 50%; top: 50%;
  width: 92%;
  /* Все силуэты одной высоты — ровные экраны в раме; длинное содержимое
     прокручивается внутри активного, а не вытекает под пульт. */
  height: 92%; max-height: 92%;
  transform: translate(-50%, -50%);
  /* Переход только у transform: прозрачность и фильтр глубины щёлкают сразу —
     их полукадры давали кашу текстов при листании, а в фоновых вкладках
     заморозка перехода оставляла дальние слайды видимыми. */
  transition: transform .64s cubic-bezier(.16, 1, .3, 1);
  will-change: transform;
  cursor: pointer;
}
.scene-slide.is-active { cursor: default; }
/* ОДИН ИНТЕРФЕЙС (разбор founder-а 14.08): контент ЛЮБОГО слайда живёт без
   собственных рамок — рамку носит только силуэт телефона. У активного это
   рама стекла; сосед за стеклом — мини-телефон с ТОЙ ЖЕ рамой-силуэтом. */
.scene-slide .intent-fragment .intent-plate,
.scene-slide .intent-fragment.is-primary .intent-plate,
.scene-slide .intent-fragment :is(.intent-thread, .intent-role) {
  border-color: transparent;
  background: transparent;
  box-shadow: none;
}
.scene-slide:not(.is-active) {
  overflow: hidden; border-radius: 26px;
  border: 1px solid color-mix(in srgb, var(--iris-accent, var(--accent)) 30%, rgba(244, 238, 240, .05));
  background: #070609;
  box-shadow: inset 0 0 0 1px color-mix(in srgb, color-mix(in srgb, var(--iris-accent, var(--accent)) 60%, white) 10%, transparent),
              0 26px 60px -46px rgba(0, 0, 0, .9);
}

.scene-slide .intent-fragment { min-height: 0; height: 100%; max-height: none; }
/* Длинный экран прокручивается внутри активного силуэта. */
.scene-slide.is-active .intent-fragment { overflow-y: auto; overscroll-behavior: contain; }
/* Стрелки — контурные кнопки-трубки по бокам. */
.slide-nav {
  position: absolute; top: 50%; z-index: 30; transform: translateY(-50%);
  width: 46px; height: 46px; border-radius: 999px; cursor: pointer;
  display: grid; place-items: center; padding: 0;
  border: 1px solid color-mix(in srgb, var(--iris-accent, var(--accent)) 42%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, color-mix(in srgb, var(--iris-accent, var(--accent)) 60%, white) 18%, transparent);
  background: rgba(5, 5, 8, .6); color: var(--iris-accent, var(--accent));
  font-size: 20px; line-height: 1;
  transition: color var(--dur-base) var(--ease-out), border-color var(--dur-base) var(--ease-out);
}
.slide-nav:hover { color: color-mix(in srgb, var(--iris-accent, var(--accent)) 55%, white); border-color: color-mix(in srgb, var(--iris-accent, var(--accent)) 70%, transparent); }
/* Стрелки живут ВНЕ рамы телефона — на полях сцены, у выглядывающих соседей:
   поверх текста активной карточки им не место (разбор founder-а 14.08). */
.slide-nav.prev { left: -72px; }
.slide-nav.next { right: -72px; }
/* На телефоне полей нет — листают свайпом и рельсой, стрелки не нужны. */
@media (max-width: 899px) {
  .slide-nav { display: none; }
}
/* Рельса пути: трубка со светом внутри и узлы шагов. Заполнение растёт по
   мере прихода силуэтов, активный узел горит накалом. */
.slider-rail {
  position: relative; flex: 0 0 auto; height: 36px;
  display: flex; align-items: center; justify-content: center;
}
/* Трубка привязана к РЯДУ УЗЛОВ, а не к процентам контейнера: на широком
   дисплее проценты уводили линию мимо узлов (скрин founder-а 14.08). */
.rail-nodes { position: relative; display: inline-flex; align-items: center; gap: 30px; padding: 0 4px; }
.rail-tube {
  position: absolute; left: 0; right: 0; top: 50%; height: 3px;
  border-radius: 2px; transform: translateY(-50%);
  background: color-mix(in srgb, var(--iris-accent, var(--accent)) 14%, transparent);
}
.rail-tube-fill {
  position: absolute; left: 0; top: 0; bottom: 0; width: 0;
  border-radius: 2px;
  background: color-mix(in srgb, var(--iris-accent, var(--accent)) 44%, transparent);
  box-shadow: inset 0 1px 0 color-mix(in srgb, color-mix(in srgb, var(--iris-accent, var(--accent)) 55%, white) 60%, transparent);
  transition: width .64s cubic-bezier(.16, 1, .3, 1);
}
.rail-node {
  position: relative; z-index: 1; width: 10px; height: 10px; border-radius: 999px;
  background: rgba(244, 238, 240, .1);
  transition: background var(--dur-base) var(--ease-out), box-shadow var(--dur-base) var(--ease-out);
}
.rail-node > i { position: absolute; inset: 3px; border-radius: 999px; }
.rail-node.is-lit { background: color-mix(in srgb, var(--iris-accent, var(--accent)) 45%, transparent); }
.rail-node.is-active { background: var(--iris-accent, var(--accent)); }
.rail-node.is-active > i { background: color-mix(in srgb, var(--iris-accent, var(--accent)) 45%, white); }
/* Верх корпуса становится продолжением боковых поверхностей ровно тогда, когда
   они присутствуют. Это та же сцепка, что у подошвы пульта: в свободном виде
   экран снова получает фирменный радиус, а transition не даёт форме щёлкнуть. */
body.js:is([data-stage="exploded"], [data-stage="assembling"]) .console:is([data-scene-layout*="left"], [data-scene-layout*="right"]) .display {
  border-radius: 0;
}
body.js:is([data-stage="exploded"], [data-stage="assembling"]) .console[data-scene-bottom="false"] {
  grid-template-rows: minmax(250px, 1fr) auto 0;
}
body.js:is([data-stage="exploded"], [data-stage="assembling"]) .grid:not([hidden]) { height: 100%; }
body.js:is([data-stage="exploded"], [data-stage="assembling"]) .grid[hidden] { display: none; }
body.js:is([data-stage="exploded"], [data-stage="assembling"]) .below {
  align-self: stretch; justify-self: stretch; max-width: none;
}
body.js:is([data-stage="exploded"], [data-stage="assembling"]) .grid-b {
  min-height: 0; height: 100%;
}
body.js:is([data-stage="exploded"], [data-stage="assembling"]) .grid-b .intent-fragment {
  min-height: 0; min-width: 0; flex: 1 1 0;
}
body.js:is([data-stage="exploded"], [data-stage="assembling"]) .grid-b .intent-fragment-body {
  height: 100%;
}
body.js:is([data-stage="exploded"], [data-stage="assembling"]) .grid-b .intent-fragment.is-primary {
  flex: 2 1 auto;
}
body.js:is([data-stage="exploded"], [data-stage="assembling"]) .grid-b .intent-screen {
  padding-inline: clamp(24px, 4vw, 72px);
}
body.js:is([data-stage="exploded"], [data-stage="assembling"]) .grid-b .intent-preview-world {
  padding-block: clamp(12px, 1.5vh, 18px); gap: clamp(12px, 1.4vw, 20px);
}
body.js:is([data-stage="exploded"], [data-stage="assembling"]) .grid-b .intent-preview-usage {
  margin-top: 4px; padding-block: 7px; gap: 3px;
}
body.js:is([data-stage="exploded"], [data-stage="assembling"]) .grid-b .intent-preview-module {
  padding-block: 9px; gap: 5px;
}
body.js:is([data-stage="exploded"], [data-stage="assembling"]) .grid-b .intent-preview-world[data-preview-layout="deck"] {
  grid-template-columns: minmax(250px, .78fr) minmax(0, 1.7fr);
  grid-template-rows: minmax(0, 1fr);
}
body.js:is([data-stage="exploded"], [data-stage="assembling"]) .grid-b .intent-preview-world[data-preview-layout="deck"] .intent-preview-modules {
  align-content: stretch;
}
body.js:is([data-stage="exploded"], [data-stage="assembling"]) .grid-b .intent-preview-world[data-preview-layout="deck"] .k-deck {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
body.js:is([data-stage="exploded"], [data-stage="assembling"]) .intent-fragment .intent-screen {
  height: 100%; max-height: none;
}
body.js:is([data-stage="exploded"], [data-stage="assembling"]) .intent-fragment .intent-preview-world {
  height: 100%; max-height: none;
}
body.js:is([data-stage="exploded"], [data-stage="assembling"]) .intent-fragment-body {
  overflow: hidden;
}
body.js:is([data-stage="exploded"], [data-stage="assembling"]) .grid-l .intent-fragment,
body.js:is([data-stage="exploded"], [data-stage="assembling"]) .grid-r .intent-fragment {
  min-height: 0; flex: 1 1 0;
}
body.js:is([data-stage="exploded"], [data-stage="assembling"]) :is(.grid-l, .grid-r) {
  max-height: none; align-self: stretch;
}
/* Когда боковая ветка делится между двумя экранами, плотность меняется у всей
   экскурсии, а не только у данных. Иначе инструкции съедают функциональный
   блок, хотя рядом остаётся пустая ширина. */
body.js:is([data-stage="exploded"], [data-stage="assembling"]) :is(.grid-l, .grid-r):has(> .intent-fragment + .intent-fragment) .intent-screen {
  padding-block: 9px; gap: 5px;
}
body.js:is([data-stage="exploded"], [data-stage="assembling"]) :is(.grid-l, .grid-r):has(> .intent-fragment + .intent-fragment) .intent-preview-usage {
  margin-top: 3px; padding-block: 7px; gap: 3px;
}
body.js:is([data-stage="exploded"], [data-stage="assembling"]) :is(.grid-l, .grid-r):has(> .intent-fragment + .intent-fragment) .intent-preview-cta {
  font-size: clamp(14px, 1.2vw, 18px);
}
body.js:is([data-stage="exploded"], [data-stage="assembling"]) :is(.grid-l, .grid-r):has(> .intent-fragment + .intent-fragment) .intent-preview-module {
  padding: 8px; gap: 5px;
}
body.js:is([data-stage="exploded"], [data-stage="assembling"]) :is(.grid-l, .grid-r):has(> .intent-fragment + .intent-fragment) .intent-screen h3 {
  font-size: clamp(18px, 1.7vw, 24px);
}

.intent-screen {
  min-width: 0; min-height: 100%; padding: clamp(16px, 2vw, 26px);
  display: flex; flex-direction: column; gap: 13px;
  color: var(--iris-text, var(--ink));
  background: linear-gradient(145deg,
    color-mix(in srgb, var(--iris-accent, var(--accent)) 7%, var(--aurora-base-1)),
    var(--aurora-base-1) 58%);
}
.intent-preview-world {
  max-height: min(38svh, 440px); overflow: hidden;
  display: grid; grid-template-columns: minmax(240px, .72fr) minmax(0, 1.7fr);
  gap: clamp(16px, 2vw, 30px); padding: clamp(18px, 2.4vw, 34px);
}
/* Renderer задаёт не только начинку, но и ритм поверхности. Rulebook читаетcя
   широкой инструкцией, deck — как поле карточек, dice/vote — как один крупный
   жест. Это универсальные формы библиотеки, а не словарь предметных областей. */
.intent-preview-world:is([data-preview-layout="rulebook"], [data-preview-layout="board"], [data-preview-layout="goods"], [data-preview-layout="schedule"], [data-preview-layout="slots"], [data-preview-layout="dash"]) {
  grid-template-columns: minmax(190px, .52fr) minmax(0, 1.9fr);
}
.intent-preview-world[data-preview-layout="deck"] {
  grid-template-columns: 1fr; grid-template-rows: auto minmax(0, 1fr);
}
.intent-preview-world[data-preview-layout="deck"] .intent-preview-modules {
  grid-template-columns: 1fr; align-content: stretch;
}
.intent-preview-world:is([data-preview-layout="dice"], [data-preview-layout="vote"], [data-preview-layout="control"]) {
  grid-template-columns: minmax(190px, .78fr) minmax(240px, 1.22fr); align-items: center;
}
.intent-preview-world:is([data-preview-layout="dice"], [data-preview-layout="vote"], [data-preview-layout="control"]) .intent-preview-module {
  min-height: min(30cqi, 210px); justify-content: center;
}
.intent-preview-head { position: sticky; top: 0; align-self: start; }
/* ── Карточки сцены v3 (референс founder-а 14.08) ─────────────────────────
   Плита .intent-plate — один предмет на колонку: тонирование акцентом сверху,
   глубокая смещённая тень (не ореол), свет по верхней кромке. Шаг-хедер стоит
   над плитой; служебные строки слиты; действие — пилюля. */
/* ДОРОГОЙ КОНТУРИНГ (решение founder-а 14.08): чёрная плоскость + точная
   линия с акцентом в контуре — вместо заливки-облака и мягкого света.
   Рассеянные градиенты и цветные тени читались шумом и дешевили сцену. */
.intent-plate {
  display: flex; flex-direction: column; gap: 12px;
  min-width: 0; overflow: hidden;
  margin-top: 10px; padding: 16px;
  border-radius: 22px;
  /* Контур-трубка: внешняя линия несёт акцент, светлое ядро — инсетом внутри.
     Свет в линии, не вокруг неё (эталон-логотип 14.08). */
  border: 1px solid color-mix(in srgb, var(--iris-accent, var(--accent)) 30%, rgba(244, 238, 240, .05));
  background: #0B0A0E;
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--iris-accent, var(--accent)) 10%, transparent),
              0 26px 60px -46px rgba(0, 0, 0, .9);
}
.intent-preview-usage { display: flex; flex-direction: column; gap: 10px; }
/* Шапка плиты — РЯД: чип слева, строки справа. Селектор с двумя классами
   перекрывает общий «.intent-screen header { flex-direction: column }»,
   который складывал чип и текст стопкой по центру (разбор founder-а 14.08:
   «побитые экраны»). */
.intent-preview-usage .usage-head, .usage-head {
  display: flex; flex-direction: row; align-items: center; gap: 12px;
  text-align: left; justify-content: flex-start;
}
/* Киккер поверхности в превью-карточке дублировал подпись шаг-хедера
   («КОМАНДА» дважды): в фрагментах его гасим, в собранных экранах он живёт. */
.intent-fragment .surface-kicker { display: none; }
/* Широкая плита нижней ветки — две колонки: шапка с исходом слева, модули
   справа; одна колонка растягивала шапку на весь экран с пустотой вокруг. */
@container (min-width: 640px) {
  .intent-plate { display: grid; grid-template-columns: minmax(240px, 320px) minmax(0, 1fr); align-items: start; column-gap: 18px; }
}
/* Круглый чип с иконкой renderer-а — той же, что на плитке вкладки: карточка
   узнаётся до чтения текста. */
.usage-chip {
  flex: 0 0 auto; width: 38px; height: 38px; display: grid; place-items: center;
  border-radius: 999px; background: color-mix(in srgb, var(--iris-accent, var(--accent)) 13%, transparent);
  color: var(--iris-accent, var(--accent));
}
.usage-chip svg { width: 18px; height: 18px; }
.usage-head-lines { min-width: 0; display: flex; flex-direction: column; gap: 4px; }
.usage-head-lines > small {
  font-family: var(--font-mono); font-size: 9px; line-height: 1.2; letter-spacing: .18em;
  text-transform: uppercase; color: color-mix(in srgb, var(--iris-accent, var(--accent)) 72%, var(--ink-2));
}
.intent-preview-cta {
  margin: 7px 0 0; display: flex; flex-direction: column; gap: 8px;
  color: var(--iris-ink, var(--ink-0));
  font-size: clamp(17px, 1.5vw, 24px); line-height: 1.24; font-weight: var(--weight-medium, 500);
  letter-spacing: -.02em;
}
/* Строка шага: киккер акцентом, тонкая линия до правого края, справа — область. */
.intent-preview-cta > small {
  display: flex; align-items: center; gap: 12px;
  font-family: var(--font-mono); font-size: clamp(9px, .72vw, 10px); line-height: 1;
  letter-spacing: .2em; text-transform: uppercase; color: var(--iris-muted, var(--ink-2));
  font-weight: var(--weight-regular, 400);
}
.intent-preview-cta > small > b { font-weight: var(--weight-medium, 500); color: var(--iris-accent, var(--accent)); }
.intent-preview-cta > small > i { flex: 1 1 auto; height: 1px; background: rgba(244, 238, 240, .09); }
.intent-preview-cta > span { color: var(--iris-ink, var(--ink-0)); text-wrap: balance; }
.intent-preview-title {
  color: var(--iris-ink, var(--ink-0)); font-size: clamp(14px, 1.15vw, 17px);
  line-height: 1.25; font-weight: var(--weight-semibold);
}
.intent-preview-outcome { display: grid; gap: 3px; font-size: 11.5px; line-height: 1.45; color: var(--iris-muted, var(--ink-2)); }
.intent-preview-outcome > i { font-family: var(--font-mono); font-size: 7.5px; font-style: normal; letter-spacing: .14em; text-transform: uppercase; color: color-mix(in srgb, var(--iris-accent, var(--accent)) 70%, var(--ink-2)); }
/* Действие — пилюля на всю ширину плиты: тихая подпись слева, кнопка справа.
   Кликов в превью нет — но выглядеть решение обязано как решение. */
.usage-action {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding-top: 12px; border-top: 1px solid rgba(244, 238, 240, .07);
  font-size: 11px; font-style: normal; color: var(--iris-muted, var(--ink-2));
}
.usage-action > strong {
  display: inline-flex; align-items: center; min-height: 34px; padding: 0 16px;
  border-radius: 999px; background: var(--iris-accent, var(--accent));
  color: var(--iris-on-accent, var(--on-accent, #0b0b0f));
  font-family: var(--font-mono); font-size: 9.5px; letter-spacing: .18em; text-transform: uppercase;
  font-weight: var(--weight-medium, 500);
}
/* Бюджет высоты карточки доезжает до КАЖДОГО модуля, не только до стола (рез
   15.08 повторился на board): .intent-preview-modules берёт остаток плиты и
   умеет сжиматься, а перелив контента режется скруглением самого модуля —
   кромка плиты больше не шинкует талоны посередине «как скриншот». */
.intent-preview-modules {
  min-width: 0; flex: 1 1 auto; min-height: 0;
  display: grid; grid-template-columns: repeat(auto-fit, minmax(min(180px, 100%), 1fr));
  grid-template-rows: minmax(0, 1fr);
  gap: 10px; align-content: start;
}
/* Центр плиты — внутренняя панель: глубже фона плиты, свой скруглённый контур.
   Кубик, канбан, рубашка роли живут здесь как предмет карточки. */
.intent-preview-module {
  min-width: 0; min-height: 0; overflow: hidden;
  padding: 14px; display: flex; flex-direction: column; gap: 8px;
  /* Вставка ФОНОМ, без собственного контура: рамка в рамке с одинаковым
     бордером дешевила карточку (разбор founder-а 14.08). */
  border-radius: 12px; border: 0;
  background: rgba(244, 238, 240, .035);
}
.intent-preview-module > div {
  flex: 1 1 auto; min-height: 0;
  display: flex; flex-direction: column;
}
/* Страховка узкой плиты: строки библиотечных модулей переносятся, а не
   раздувают min-content карточки за кромку экрана. */
.intent-preview-module .k-line { flex-wrap: wrap; }
.intent-preview-module :is(b, span, small, strong, em, i) { overflow-wrap: break-word; min-width: 0; }

/* ── Герой-центры превью (ветка A) ──────────────────────────────────────── */
.hero-dice { display: flex; align-items: center; gap: 16px; padding: 4px 2px; }
.hero-die {
  flex: 0 0 auto; width: 52px; height: 52px; margin: 5px 7px; transform: rotate(45deg);
  border-radius: 10px; border: 1px solid color-mix(in srgb, var(--iris-accent, var(--accent)) 55%, transparent);
  background: color-mix(in srgb, var(--iris-accent, var(--accent)) 10%, transparent);
  display: grid; place-items: center;
}
.hero-die > b { transform: rotate(-45deg); font-family: var(--font-mono); font-size: 15px; color: var(--iris-accent, var(--accent)); }
.hero-die-meta { min-width: 0; display: flex; flex-direction: column; gap: 4px; }
.hero-die-meta i { font-style: normal; font-size: 11.5px; line-height: 1.4; color: var(--iris-muted, var(--ink-2)); }
.hero-die-meta em { font-style: normal; font-family: var(--font-mono); font-size: 12px; color: var(--iris-accent, var(--accent)); }
.hero-vote { display: flex; flex-direction: column; gap: 8px; }
.hero-vote-row { display: flex; align-items: center; gap: 10px; font-size: 11.5px; }
.hero-vote-row > span { flex: 0 0 30%; min-width: 0; overflow-wrap: break-word; color: var(--iris-text, var(--ink)); }
.hero-vote-row > i { flex: 1 1 auto; height: 5px; border-radius: 3px; background: rgba(244, 238, 240, .07); }
.hero-vote-row > i > b { display: block; height: 5px; border-radius: 3px; background: var(--iris-accent, var(--accent)); }
.hero-vote-row > em { flex: 0 0 auto; font-style: normal; font-family: var(--font-mono); font-size: 11px; color: var(--iris-accent, var(--accent)); }
.hero-roster { display: flex; flex-wrap: wrap; gap: 10px; }
.hero-face { display: flex; flex-direction: column; align-items: center; gap: 5px; max-width: 64px; }
.hero-face > i {
  width: 34px; height: 34px; border-radius: 999px; display: grid; place-items: center;
  font-style: normal; font-weight: var(--weight-semibold); font-size: 13px;
  background: color-mix(in srgb, var(--iris-accent, var(--accent)) 14%, transparent);
  color: var(--iris-accent, var(--accent));
}
.hero-face > b { font-size: 9.5px; font-weight: var(--weight-regular); color: var(--iris-muted, var(--ink-2)); text-align: center; overflow-wrap: break-word; min-width: 0; }
.hero-deck { display: flex; align-items: center; gap: 16px; }
.hero-card {
  flex: 0 0 auto; width: 46px; height: 62px; border-radius: 8px;
  border: 1px solid color-mix(in srgb, var(--iris-accent, var(--accent)) 45%, transparent);
  background: repeating-linear-gradient(135deg,
    color-mix(in srgb, var(--iris-accent, var(--accent)) 14%, transparent) 0 5px,
    color-mix(in srgb, var(--iris-accent, var(--accent)) 4%, transparent) 5px 10px);
  display: grid; place-items: center;
  font-size: 20px; font-weight: var(--weight-semibold); color: var(--iris-accent, var(--accent));
}
.hero-deck-meta { min-width: 0; display: flex; flex-direction: column; gap: 5px; }
.hero-deck-meta b { font-size: 12px; font-weight: var(--weight-medium, 500); color: var(--iris-text, var(--ink)); overflow-wrap: break-word; }
/* Канбан .k-kanban — общий рендер board для превью И собранного мира: колонки
   этапов из kit.js, карточка задачи — .k-ticket. Прежние две копии (hero-board
   в превью и чек-лист .k-board в мире) расходились и обманывали демо. */
.k-kanban { display: flex; align-items: stretch; gap: 8px; }
.k-col {
  flex: 1 1 0; min-width: 0; padding: 9px; display: flex; flex-direction: column; gap: 7px;
  border-radius: 10px; background: rgba(244, 238, 240, .03);
}
.k-col > small { font-family: var(--font-mono); font-size: 8px; letter-spacing: .12em; text-transform: uppercase; color: var(--iris-muted, var(--ink-2)); overflow-wrap: break-word; }
.k-ticket {
  display: inline-flex; align-items: center; min-height: 24px; padding: 3px 9px; border-radius: 6px;
  font-size: 11px; line-height: 1.3; overflow-wrap: anywhere;
  background: rgba(244, 238, 240, .08); color: var(--iris-text, var(--ink));
}
/* Живой канбан собранного мира: карточка тянется пальцем/мышью (Pointer
   Events в console.js), колонка-цель подсвечивается, «+» создаёт позицию.
   touch-action: none обязателен — иначе жест уходит в прокрутку страницы. */
.intent-view .k-ticket { cursor: grab; touch-action: none; user-select: none; -webkit-user-select: none; }
.k-ticket.is-lifted { opacity: .35; }
.k-ticket-ghost {
  position: fixed; z-index: 99; pointer-events: none;
  transform: translate(-50%, -140%);
  background: var(--iris-accent, var(--accent)); color: var(--iris-on-accent, var(--on-accent, #0b0b0f));
  box-shadow: 0 12px 30px rgba(0, 0, 0, .5);
}
.k-col.is-drop {
  box-shadow: inset 0 0 0 1px var(--iris-accent, var(--accent));
  background: color-mix(in srgb, var(--iris-accent, var(--accent)) 8%, transparent);
}
.k-add {
  border: 0; cursor: pointer; font: inherit; font-size: 10.5px; text-align: left;
  margin-top: auto; padding: 5px 8px; border-radius: 6px;
  color: var(--iris-muted, var(--ink-2)); background: transparent;
  box-shadow: inset 0 0 0 1px rgba(244, 238, 240, .08);
}
.k-add:hover, .k-add:focus-visible {
  color: var(--iris-accent, var(--accent));
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--iris-accent, var(--accent)) 35%, transparent);
}
.k-add-input {
  font: inherit; font-size: 11px; min-width: 0; width: 100%;
  padding: 4px 8px; border-radius: 6px; border: 0;
  background: rgba(244, 238, 240, .08); color: var(--iris-text, var(--ink));
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--iris-accent, var(--accent)) 45%, transparent);
}
/* В превью шага доска — обещание, а не пульт: кнопки добавления там нет
   (mountKanbanAdders вешает их только на .intent-view), а талон не тянется. */
.hero-slots { display: flex; flex-wrap: wrap; gap: 8px; }
.hero-slot {
  min-width: 0; padding: 8px 10px; display: flex; flex-direction: column; gap: 3px;
  border-radius: 10px; background: rgba(244, 238, 240, .03);
}
.hero-slot > b { font-family: var(--font-mono); font-size: 11px; color: var(--iris-text, var(--ink)); }
.hero-slot > i { font-style: normal; font-size: 9.5px; color: var(--iris-muted, var(--ink-2)); }
.hero-slot.is-hot { border: 1px solid color-mix(in srgb, var(--iris-accent, var(--accent)) 40%, transparent); background: color-mix(in srgb, var(--iris-accent, var(--accent)) 8%, transparent); }
.hero-slot.is-hot > b { color: var(--iris-accent, var(--accent)); }
.hero-dash { display: flex; align-items: baseline; gap: 12px; }
.hero-dash > b { font-family: var(--font-mono); font-size: 30px; line-height: 1; color: var(--iris-accent, var(--accent)); }
.hero-dash > span { font-size: 11.5px; color: var(--iris-muted, var(--ink-2)); min-width: 0; overflow-wrap: break-word; }

/* ── Стат-полоса плиты (ветка B): живые цифры мира ──────────────────────── */
.plate-stats {
  display: flex; align-items: center; gap: 16px;
  margin: 2px -16px -16px; padding: 13px 16px;
  border-top: 1px solid color-mix(in srgb, var(--iris-accent, var(--accent)) 20%, rgba(244, 238, 240, .05));
  border-radius: 0 0 21px 21px;
}
.plate-stats > span { display: flex; align-items: baseline; gap: 7px; min-width: 0; }
.plate-stats b { font-family: var(--font-mono); font-size: 19px; line-height: 1; font-weight: var(--weight-medium, 500); color: var(--iris-accent, var(--accent)); }
.plate-stats i { font-family: var(--font-mono); font-style: normal; font-size: 9px; letter-spacing: .14em; text-transform: uppercase; color: var(--iris-muted, var(--ink-2)); overflow-wrap: break-word; }
.plate-stats > u { width: 1px; height: 20px; background: rgba(244, 238, 240, .1); text-decoration: none; }

/* ── Температура карточки по аудитории шага (ветка E) ───────────────────
   Один акцент, разные прозрачности: главная поверхность мира — плотнее и
   ярче, роль — теплее (гость встречает её первой), лента — нейтральная
   панель. Никаких новых цветов. */
/* Иерархию говорит ЛИНИЯ, а не заливка: главная поверхность — накалённый
   контур (яркая внешняя линия + светлое ядро сильнее). */
.intent-fragment.is-primary .intent-plate {
  border-color: color-mix(in srgb, var(--iris-accent, var(--accent)) 62%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, color-mix(in srgb, var(--iris-accent, var(--accent)) 60%, white) 24%, transparent),
              0 26px 60px -46px rgba(0, 0, 0, .9);
}
.intent-fragment .intent-role {
  background: #0B0A0E;
  border-color: color-mix(in srgb, var(--iris-accent, var(--accent)) 34%, rgba(244, 238, 240, .06));
}

/* ── Нить маршрута (ветка C): контур собирается ─────────────────────────── */
.scene-thread { position: absolute; inset: 0; z-index: 0; pointer-events: none; overflow: visible; }
/* НЕОНОВАЯ ТРУБКА (эталон founder-а 14.08 — логотип iris.direct): свет живёт
   ВНУТРИ линии. Широкий штрих акцента несёт цвет, узкое светлое ядро поверх
   даёт накал — ноль размытий, один цвет, скруглённые стыки. */
.scene-thread path {
  stroke-linecap: round; stroke-linejoin: round;
  transition: stroke-dashoffset 1.1s var(--ease-out);
}
.scene-thread .thread-tube {
  stroke: color-mix(in srgb, var(--iris-accent, var(--accent)) 44%, transparent);
  stroke-width: 3;
}
.scene-thread .thread-core {
  stroke: color-mix(in srgb, var(--iris-accent, var(--accent)) 55%, white);
  stroke-width: 1.1;
}
/* Нить лежит под карточками И под телефоном: она соединяет шаги, а не режет
   разговор поверх контента (скрин founder-а 14.08). */
.intent-fragment { position: relative; z-index: 1; }
body.js:is([data-stage="exploded"], [data-stage="assembling"]) .console .display,
body.js:is([data-stage="exploded"], [data-stage="assembling"]) .console .dock {
  position: relative; z-index: 1;
}
.intent-preview-module > small { color: var(--iris-accent, var(--accent)); font-family: var(--font-mono); font-size: 8px; letter-spacing: .13em; text-transform: uppercase; }
.intent-preview-module > b { font-size: 12px; }
.intent-preview-module .k-dice { min-height: 104px; }

/* Боковая ветка может быть узкой даже на широком мониторе. Медиазапрос окна
   этого не видит, поэтому композиция адаптируется по ширине своего сегмента. */
@container (max-width: 560px) {
  .intent-preview-world[data-preview-layout] { grid-template-columns: 1fr; grid-template-rows: auto minmax(0, 1fr); }
  .intent-preview-head { position: static; }
  .intent-screen { padding-inline: clamp(13px, 5cqi, 22px); }
  .intent-screen h3 { font-size: clamp(18px, 8cqi, 25px); overflow-wrap: anywhere; }
  .intent-preview-usage, .intent-role > p { overflow-wrap: break-word; }
  .k-roster .k-line, .k-schedule .k-line { flex-wrap: wrap; }
}
@container (max-width: 360px) {
  .k-role { grid-template-columns: 1fr; }
  .k-card { grid-template-columns: 26px minmax(0, 1fr); }
  .k-card em { grid-column: 2; }
}

@media (min-width: 900px) {
  body.js:is([data-stage="exploded"], [data-stage="assembling"]) :is(.grid-l, .grid-r):has(> .intent-fragment + .intent-fragment) .intent-preview-world[data-preview-layout] {
    grid-template-columns: minmax(252px, 1.45fr) minmax(0, .55fr);
    grid-template-rows: minmax(0, 1fr); align-items: center;
  }
}

@media (max-width: 899px) {
  body.js:is([data-stage="exploded"], [data-stage="assembling"]) .intent-fragment .intent-preview-world { max-height: none; }
  .intent-preview-world[data-preview-layout] { grid-template-columns: 1fr; grid-template-rows: auto minmax(0, 1fr); }
  .intent-preview-head { position: static; }
}
.intent-screen h3, .intent-screen p { margin: 0; }
.intent-screen h3 { font-size: clamp(19px, 2vw, 28px); line-height: 1.08; }
.intent-screen header { display: flex; flex-direction: column; gap: 6px; }
.intent-screen header > p:last-child, .intent-role > p {
  max-width: 68ch; font-size: 12.5px; line-height: 1.48; color: var(--iris-muted, var(--ink-2));
}
.intent-views { display: flex; flex-wrap: wrap; gap: 5px; }
.intent-views button {
  border: 0; cursor: pointer; padding: 7px 10px; font: inherit; font-size: 11px;
  color: var(--iris-muted, var(--ink-2)); background: transparent;
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--iris-accent, var(--accent)) 22%, transparent);
}
.intent-views button[aria-pressed="true"] {
  color: var(--iris-accent, var(--accent));
  background: color-mix(in srgb, var(--iris-accent, var(--accent)) 10%, transparent);
  box-shadow: inset 0 0 0 1px var(--iris-accent, var(--accent));
}
.intent-view { flex: 1 1 auto; min-height: 0; overflow: hidden; display: flex; flex-direction: column; gap: 10px; }
.intent-view-body { flex: 1 1 auto; min-height: 0; display: flex; align-items: stretch; }
.intent-view-body > * { width: 100%; align-content: start; }
.intent-view-title { font-size: 13px; font-weight: var(--weight-semibold); }
.intent-view[data-view-renderer="dice"] > .intent-view-title { display: none; }
.intent-thread .thread-history { overflow: hidden; }
.intent-role .role-traits { margin-top: 4px; }
.thread-embed {
  padding: 12px; display: grid; grid-template-columns: minmax(0, 1fr) auto;
  gap: 10px; align-items: end;
  background: color-mix(in srgb, var(--iris-accent, var(--accent)) 7%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--iris-accent, var(--accent)) 28%, transparent);
}
.thread-embed > header, .thread-embed-body { min-width: 0; grid-column: 1 / -1; }
.thread-embed > header small {
  font-family: var(--font-mono); font-size: 8px; letter-spacing: .12em;
  text-transform: uppercase; color: var(--iris-accent, var(--accent));
}
.thread-embed-action { grid-column: 2; justify-self: end; }
.thread-embed-ref {
  padding: 11px 12px; display: grid; grid-template-columns: 8px minmax(0, 1fr);
  gap: 10px; align-items: center;
  background: color-mix(in srgb, var(--iris-accent, var(--accent)) 7%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--iris-accent, var(--accent)) 24%, transparent);
}
.thread-embed-ref > i { width: 8px; height: 8px; border-radius: 50%; background: var(--iris-accent, var(--accent)); }
.thread-embed-ref > div { min-width: 0; display: grid; gap: 2px; }
.thread-embed-ref b { font-size: 11px; }
.thread-embed-ref span { font-size: 10px; color: var(--iris-muted, var(--ink-2)); overflow-wrap: anywhere; }
.k-control { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 7px; }
.k-control-state {
  padding: 10px; display: grid; gap: 4px;
  background: color-mix(in srgb, var(--iris-accent, var(--accent)) 6%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--iris-accent, var(--accent)) 22%, transparent);
}
.k-control-state small { font-size: 8px; color: var(--iris-muted, var(--ink-2)); }
.k-control-state b { font-size: 12px; }
.k-control-state span { color: var(--iris-accent, var(--accent)); font-family: var(--font-mono); font-size: 10px; }
.k-schedule { display: grid; gap: 7px; }
.k-schedule .k-line { padding: 9px 10px; display: flex; justify-content: space-between; gap: 10px;
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--iris-accent, var(--accent)) 20%, transparent); }
.k-schedule .k-line > span { display: flex; gap: 5px; }
.k-schedule .k-line i { padding: 2px 5px; font-style: normal; color: var(--iris-accent, var(--accent));
  font-family: var(--font-mono); font-size: 8px; }

/* Направляемый первый ход. Предметные слова приходят из blueprint; стили знают
   только четыре универсальных фокуса: роль, действие, работа, результат. */
.intent-guide {
  order: -1; padding: 11px 13px; display: grid; grid-template-columns: auto minmax(0, 1fr);
  gap: 3px 12px; align-items: baseline;
  color: var(--iris-text, var(--ink));
  background: color-mix(in srgb, var(--iris-accent, var(--accent)) 12%, var(--aurora-base-1));
  box-shadow: inset 3px 0 0 var(--iris-accent, var(--accent));
  animation: guide-in .34s var(--ease-out) both;
}
.intent-guide small {
  grid-column: 1 / -1; font-family: var(--font-mono); font-size: 8px;
  letter-spacing: .14em; text-transform: uppercase; color: var(--iris-accent, var(--accent));
}
.intent-guide b { font-size: 13px; }
.intent-guide span { min-width: 0; font-size: 12.5px; color: var(--iris-muted, var(--ink-2)); }
.guide-skip {
  grid-row: 2 / 4; grid-column: 3; align-self: center; border: 0; padding: 7px 9px;
  cursor: pointer; font: inherit; font-size: 10px; color: var(--iris-muted, var(--ink-2));
  background: transparent; box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--iris-accent, var(--accent)) 28%, transparent);
}
.guide-next {
  margin-top: auto; align-self: flex-start; border: 0; padding: 10px 14px; cursor: pointer;
  font: inherit; font-size: 12px; color: var(--iris-on-accent, var(--on-accent));
  background: var(--iris-accent, var(--accent));
}
.intent-screen.guide-focus-action .world-action {
  position: relative; outline: 2px solid var(--iris-accent, var(--accent)); outline-offset: 2px;
  box-shadow: 0 0 36px -10px var(--iris-accent, var(--accent));
  animation: guide-pulse 1.4s ease-in-out infinite;
}
[data-row-key] { transition: background .28s var(--ease-out), box-shadow .28s var(--ease-out), transform .28s var(--ease-out); }
[data-row-key].guide-result {
  position: relative; z-index: 1; transform: translateX(4px);
  background: color-mix(in srgb, var(--iris-accent, var(--accent)) 16%, transparent);
  box-shadow: inset 3px 0 0 var(--iris-accent, var(--accent)), 0 0 30px -16px var(--iris-accent, var(--accent));
  animation: guide-result-in .52s var(--ease-out) both;
}
.tabs.tiles .tile:disabled { opacity: .22; cursor: default; filter: saturate(.25); }
@keyframes guide-in { from { opacity: 0; transform: translateY(-7px); } }
@keyframes guide-pulse { 50% { filter: brightness(1.16); transform: translateY(-1px); } }
@keyframes guide-result-in { from { opacity: .45; transform: translateX(-6px); } }

.k-vote { display: flex; flex-direction: column; gap: 7px; }
.k-choice {
  min-width: 0; padding: 9px 10px; display: grid;
  grid-template-columns: 10px minmax(0, 1fr) auto; gap: 8px; align-items: center;
  background: color-mix(in srgb, var(--iris-accent, var(--accent)) 6%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--iris-accent, var(--accent)) 20%, transparent);
}
.k-choice > i { width: 7px; height: 7px; border: 1px solid var(--iris-accent, var(--accent)); border-radius: 50%; }
.k-choice.leading > i { background: var(--iris-accent, var(--accent)); box-shadow: 0 0 12px var(--iris-accent, var(--accent)); }
.k-choice b { min-width: 0; font-size: 12px; }
.k-choice span { color: var(--iris-accent, var(--accent)); font-family: var(--font-mono); font-size: 10px; }
.k-choice em { grid-column: 2 / -1; font-style: normal; font-size: 10px; color: var(--iris-muted, var(--ink-2)); }

.k-deck { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(180px, 100%), 1fr)); gap: 8px; }
.k-card {
  min-width: 0; padding: 11px; display: grid; grid-template-columns: 26px minmax(0, 1fr);
  gap: 9px; align-items: start;
  background: color-mix(in srgb, var(--iris-accent, var(--accent)) 6%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--iris-accent, var(--accent)) 24%, transparent);
}
.k-card > i { width: 26px; height: 34px; display: grid; place-items: center; font-style: normal;
  font-family: var(--font-mono); font-size: 9px; color: var(--iris-accent, var(--accent));
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--iris-accent, var(--accent)) 48%, transparent); }
.k-card > div { min-width: 0; display: grid; gap: 3px; }
.k-card b { font-size: 12px; overflow-wrap: break-word; word-break: normal; }
.k-card span { font-size: 10px; line-height: 1.35; color: var(--iris-muted, var(--ink-2)); overflow-wrap: break-word; word-break: normal; }
.k-card em { grid-column: 2; font-style: normal; font-family: var(--font-mono); font-size: 10px; line-height: 1.3; color: var(--iris-accent, var(--accent)); overflow-wrap: break-word; word-break: normal; }
.k-rulebook { margin: 0; padding: 0; list-style: none; display: grid; gap: 7px; }
.k-rulebook li { min-width: 0; padding: 10px; display: grid; grid-template-columns: 26px minmax(0, 1fr); gap: 9px;
  background: color-mix(in srgb, var(--iris-accent, var(--accent)) 6%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--iris-accent, var(--accent)) 22%, transparent); }
.k-rulebook li > i { font-style: normal; font-family: var(--font-mono); font-size: 9px; color: var(--iris-accent, var(--accent)); }
.k-rulebook li > div { min-width: 0; display: grid; gap: 2px; }
.k-rulebook b { font-size: 12px; }
.k-rulebook span, .k-rulebook em { font-size: 10px; line-height: 1.35; font-style: normal; overflow-wrap: anywhere; }
.k-rulebook em { color: var(--iris-muted, var(--ink-2)); }

/* Собранное демо — тот же телефон 9:16: экран мира с пультом держат портрет,
   а не широкую карточку (решение founder-а 14.08). */
body.js[data-stage="assembled"] .display,
body.js:is([data-stage="intent-simulation"], [data-stage="intent-brief"]) .display {
  display: flex;
  height: calc(var(--corpus) * 16 / 9 - var(--dock-h) + var(--couple-depth, 44px));
  max-height: calc(100svh - var(--dock-h) - 8px);
}
/* Бюджет ряда сильнее портрета: на десктопе консоль брифа ограничена
   вьюпортом, и жёсткая высота 9:16 от ширины корпуса вылезала НАД корпусом
   на 90–120 точек (скрин founder-а 15.08, «контент выпирает за экран»).
   Потолок — свой ряд сетки; мир прокручивается внутри .screen, как тред
   разговорных тактов. Телефон (page-scroll целиком) не трогаем. */
@media (min-width: 900px) {
  body.js[data-stage="assembled"] .display,
  body.js:is([data-stage="intent-simulation"], [data-stage="intent-brief"]) .display {
    min-height: 0;
    max-height: 100%;
  }
}
body.js:is([data-stage="assembled"], [data-stage="intent-simulation"], [data-stage="intent-brief"]) .grid:empty { display: none; }
body.js:is([data-stage="assembled"], [data-stage="intent-simulation"], [data-stage="intent-brief"]) .screen { flex: 1 1 auto; }
body.js:is([data-stage="assembled"], [data-stage="intent-simulation"], [data-stage="intent-brief"]) .screen > .intent-screen {
  min-height: 100%; height: auto; flex: 0 0 auto;
}
body.js:is([data-stage="assembled"], [data-stage="intent-simulation"], [data-stage="intent-brief"]) .intent-view {
  overflow: visible; flex: 0 0 auto; min-height: auto;
}
body.js:is([data-stage="assembled"], [data-stage="intent-simulation"], [data-stage="intent-brief"]) .intent-view-body {
  overflow: visible; flex: 0 0 auto; min-height: auto;
}
body.js:is([data-stage="assembled"], [data-stage="intent-simulation"], [data-stage="intent-brief"]) .intent-thread .thread-history {
  overflow: visible;
  /* Чат прижат к пульту: свежая реплика — у руки, как в главном треде
     лендинга. Прежде история липла к верху растянутого экрана, и между ней и
     пультом зияла мёртвая дыра (скрин founder-а 15.08). */
  margin-top: auto;
}

/* ── Такт 5: пульт заливается краской ───────────────────────────────────── */
/* Кнопка не подпись под собранным, а САМ пульт: человек только что собрал своё
   решение, и следующий шаг обязан быть очевиден и огромен (решение founder-а
   11.08). */
/* Кнопка не выше строки ввода: плитки над ней человек ТРОГАЕТ, и накрывать их
   собой она не имеет права (прогон 11.08). Вес берёт цветом и свечением, а не
   высотой. */
.order-go {
  padding: 0 20px; font-size: clamp(14px, 1.3vw, 18px); letter-spacing: .16em;
  box-shadow: 0 0 70px -12px var(--iris-accent, var(--accent));
}
body.js[data-stage="order"] .well {
  background: color-mix(in srgb, var(--iris-accent, var(--accent)) 14%, transparent);
}
body.js:is([data-stage="order"], [data-stage="brief"], [data-stage="intent-brief"]) .send {
  display: none;
}
body.js[data-stage="intent-brief"] .send { display: none; }
body.js:is([data-stage="order"], [data-stage="brief"], [data-stage="intent-brief"]) .well {
  right: var(--dock-edge, 20px);
  width: auto !important;
  padding: 0 !important;
}
body.js[data-stage="intent-brief"] .well { right: var(--dock-edge, 20px); }
body.js:is([data-stage="order"], [data-stage="brief"], [data-stage="intent-brief"]) .order-go {
  width: 100%;
}

/* ── Библиотека экранов (kit.js) ────────────────────────────────────────── */
.k-title {
  font-family: var(--iris-font-heading, var(--font-display)); font-size: clamp(17px, 1.5vw, 20px);
  font-weight: 600; line-height: 1.2; letter-spacing: -.03em; margin-bottom: 2px;
}
.k-roles { display: flex; flex-direction: column; gap: 11px; }
.k-role { display: grid; grid-template-columns: 1fr; gap: 2px; min-width: 0; overflow-wrap: normal; word-break: normal; }
.k-role span, .k-slot span, .k-metric span, .k-step span, .k-good span, .k-tool span {
  font-family: var(--font-mono); font-size: 9px; letter-spacing: .18em; text-transform: uppercase;
  color: color-mix(in srgb, var(--neon, var(--accent)) 60%, transparent);
}
.k-role b {
  font-size: clamp(15px, 1.25vw, 17.5px); line-height: 1.25; font-weight: var(--weight-medium);
  color: var(--neon, var(--accent));
  text-shadow: 0 0 20px color-mix(in srgb, var(--neon, var(--accent)) 32%, transparent);
}
.k-slots { display: grid; grid-template-columns: repeat(auto-fill, minmax(112px, 1fr)); gap: 8px; }
.k-slot {
  padding: 11px 12px; display: flex; flex-direction: column; gap: 3px; min-width: 0;
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--neon, var(--accent)) 22%, transparent);
}
.k-slot.full { background: color-mix(in srgb, var(--neon, var(--accent)) 9%, transparent); }
.k-slot b { font-size: 14px; font-weight: var(--weight-medium); }
.k-dash { display: grid; grid-template-columns: repeat(auto-fit, minmax(96px, 1fr)); gap: 12px; }
.k-metric { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
/* Страховка метрики: даже неожиданно длинное число переносится, а не
   наезжает на соседнюю колонку (инцидент 4.6999… 15.08). */
.k-metric b { overflow-wrap: anywhere; }
.k-metric b {
  font-family: var(--iris-font-heading, var(--font-display)); font-size: clamp(22px, 2.4vw, 32px);
  line-height: 1; font-weight: 600; letter-spacing: -.04em; color: var(--neon, var(--accent));
  text-shadow: 0 0 26px color-mix(in srgb, var(--neon, var(--accent)) 34%, transparent);
}
.k-funnel { display: flex; flex-direction: column; gap: 7px; }
.k-step { display: flex; align-items: center; gap: 10px; font-size: 13.5px; }
.k-step i {
  height: 20px; width: var(--w); flex: 0 0 auto; max-width: 46%;
  background: color-mix(in srgb, var(--neon, var(--accent)) 26%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--neon, var(--accent)) 46%, transparent);
}
.k-step b { font-weight: var(--weight-regular); flex: 1 1 auto; min-width: 0; }
.k-goods { display: grid; grid-template-columns: repeat(auto-fill, minmax(128px, 1fr)); gap: 9px; }
.k-good {
  display: flex; flex-direction: column; gap: 4px; padding: 10px 11px 12px;
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--neon, var(--accent)) 20%, transparent);
}
.k-good i {
  /* Слот фото: настоящий снимок приходит инлайн-фоном из kit-а (goods, 4-я
     ячейка строки); без снимка — штрихованный материал, как рубашка карты:
     глухой прямоугольник читался дырой (разбор founder-а 15.08). */
  height: 64px; display: block; border-radius: 6px;
  background: repeating-linear-gradient(135deg,
    color-mix(in srgb, var(--neon, var(--accent)) 14%, transparent) 0 4px,
    color-mix(in srgb, var(--neon, var(--accent)) 5%, transparent) 4px 8px);
  background-size: cover; background-position: center;
}
/* Фото-реплика чата: снимок в пузыре, как в живом QC-мире. */
.evt-photo {
  display: block; width: 100%; max-width: 236px;
  border-radius: 10px; margin: 3px 0 6px;
}
.k-good b { font-size: 13.5px; font-weight: var(--weight-medium); }
.k-thread { display: flex; flex-direction: column; gap: 8px; }
.k-msg {
  max-width: 88%; padding: 9px 12px; font-size: 13.5px; font-weight: var(--weight-regular);
  line-height: 1.4; background: color-mix(in srgb, var(--neon, var(--accent)) 8%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--neon, var(--accent)) 20%, transparent);
}
.k-msg.them { align-self: flex-end; }
.k-msg > em {
  display: block; font-style: normal; margin-bottom: 3px;
  font-family: var(--font-mono); font-size: 8.5px; letter-spacing: .18em; text-transform: uppercase;
  color: color-mix(in srgb, var(--neon, var(--accent)) 58%, transparent);
}
/* Инструменты агента: он не переписывается, а ДЕЛАЕТ — и это видно строкой. */
.k-tools { display: flex; flex-direction: column; gap: 9px; }
.k-tool { display: flex; align-items: baseline; gap: 9px; flex-wrap: wrap; }
.k-tool i {
  width: 7px; height: 7px; border-radius: 50%; align-self: center; flex-shrink: 0;
  background: var(--neon, var(--accent)); box-shadow: 0 0 12px -1px currentColor;
}
.k-tool b {
  font-size: 14.5px; font-weight: var(--weight-medium); color: var(--neon, var(--accent));
}

/* СОСТАВНЫЕ МЕХАНИКИ. Человек описал «внутри бабла ячейки со слотами: сказал
   „беру гитару“ — она легла в инвентарь» (разбор founder-а 11.08). Пузырь несёт
   слоты внутри себя — это одна вещь, а не чат рядом с сеткой. */
.k-ts .k-msg { padding-bottom: 8px; }
.k-took { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 8px; font-style: normal; }
.k-took s {
  text-decoration: none; font-size: 11px; padding: 4px 9px;
  font-family: var(--font-mono); letter-spacing: .06em;
  color: var(--neon, var(--accent));
  background: color-mix(in srgb, var(--neon, var(--accent)) 12%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--neon, var(--accent)) 46%, transparent);
}
/* Кто что взял: строка на человека, справа — что за ним числится. */
.k-roster { display: flex; flex-direction: column; gap: 9px; }
.k-line { display: flex; align-items: baseline; gap: 12px; justify-content: space-between; flex-wrap: wrap; }
.k-line b { font-size: 14.5px; font-weight: var(--weight-medium); color: var(--neon, var(--accent)); }
.k-line span { display: flex; flex-wrap: wrap; gap: 5px; justify-content: flex-end; }
.k-line i {
  font-style: normal; font-size: 11.5px; padding: 3px 8px;
  color: color-mix(in srgb, var(--iris-text, var(--ink)) 42%, white);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--neon, var(--accent)) 24%, transparent);
}

/* ══ СОСТАВНОЙ КОКПИТ МИРА ════════════════════════════════════════════════
   Все обязательные поверхности существуют одновременно. Пульт не заменяет их
   одной панелью, а фокусирует уже видимую часть мира. */
.cockpit-host[hidden] { display: none; }
.cockpit-host {
  position: relative; z-index: 3;
  grid-column: 1 / -1; grid-row: 1 / 4;
  align-self: stretch; justify-self: center;
  width: min(calc(100% - clamp(24px, 5vw, 72px)), 1280px);
  min-height: 0; padding-bottom: calc(var(--dock-h) * .72);
  display: flex; align-items: stretch;
}
.cockpit {
  width: 100%; min-height: 0;
  display: grid;
  grid-template-columns: minmax(150px, .8fr) minmax(250px, 1.15fr) minmax(220px, 1.25fr);
  grid-template-rows: minmax(180px, 1fr) minmax(150px, .82fr);
  grid-template-areas: "role thread work" "cards thread stats";
  gap: 1px;
  background: var(--iris-border, var(--seam));
  box-shadow: 0 34px 90px -62px var(--iris-accent, var(--accent));
}
.cockpit-pane {
  position: relative; min-width: 0; min-height: 0; overflow: auto;
  padding: clamp(12px, 1.25vw, 18px);
  display: flex; flex-direction: column; gap: 9px;
  color: var(--iris-text, var(--ink));
  background: color-mix(in srgb, var(--aurora-base-1) 94%, var(--iris-accent, var(--accent)));
  transition: background var(--dur-base) var(--ease-out), box-shadow var(--dur-base) var(--ease-out), opacity var(--dur-base) var(--ease-out), transform var(--dur-base) var(--ease-out);
  scrollbar-width: thin;
  scrollbar-color: color-mix(in srgb, var(--iris-accent, var(--accent)) 28%, transparent) transparent;
}
.cockpit-pane.pre-assemble { opacity: 0; transform: translateY(10px); }
.cockpit-pane.focused {
  z-index: 1;
  background: color-mix(in srgb, var(--aurora-base-1) 88%, var(--iris-accent, var(--accent)));
  box-shadow: inset 0 0 0 1px var(--iris-accent, var(--accent)), 0 0 34px -20px var(--iris-accent, var(--accent));
}
.cockpit-role { grid-area: role; }
.cockpit-thread { grid-area: thread; }
.cockpit-cards { grid-area: cards; }
.cockpit-stats { grid-area: stats; }
.cockpit-work { grid-area: work; }
.surface-kicker {
  margin: 0; font-family: var(--font-mono); font-size: 9px; line-height: 1.3;
  letter-spacing: .2em; text-transform: uppercase;
  color: color-mix(in srgb, var(--iris-accent, var(--accent)) 64%, var(--iris-text, var(--ink)));
}
.cockpit-pane h3, .cockpit-pane p { margin: 0; }
.cockpit-pane h3 { font-size: clamp(17px, 1.5vw, 22px); line-height: 1.12; }
.cockpit-pane > p:not(.surface-kicker) { font-size: 12.5px; line-height: 1.45; color: var(--iris-muted, var(--ink-2)); }

.role-person { display: flex; align-items: center; gap: 10px; }
.role-person > div { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.role-person b { font-size: 12px; color: var(--iris-muted, var(--ink-2)); }
.role-avatar {
  width: 44px; height: 44px; flex: 0 0 auto; display: grid; place-items: center;
  font-style: normal; font-family: var(--iris-font-heading, var(--font-display)); font-size: 20px;
  color: var(--iris-on-accent, var(--on-accent));
  background: var(--iris-accent, var(--accent));
  clip-path: polygon(16% 0, 84% 0, 100% 16%, 100% 84%, 84% 100%, 16% 100%, 0 84%, 0 16%);
}
.role-traits, .stat-grid { margin: auto 0 0; display: grid; gap: 7px; }
.role-traits { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.role-traits div, .stat-grid div {
  padding: 8px; min-width: 0;
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--iris-accent, var(--accent)) 22%, transparent);
}
.role-traits dt, .stat-grid dt {
  font-family: var(--font-mono); font-size: 8px; letter-spacing: .12em; text-transform: uppercase;
  color: var(--iris-muted, var(--ink-2));
}
.role-traits dd, .stat-grid dd { margin: 3px 0 0; font-size: 15px; font-weight: var(--weight-semibold); }
.role-cover {
  flex: 1 1 auto; width: 100%; min-height: 118px; cursor: pointer; border: 0;
  display: grid; place-items: center; align-content: center; gap: 10px;
  font: inherit; color: var(--iris-text, var(--ink));
  background: repeating-linear-gradient(135deg,
    color-mix(in srgb, var(--iris-accent, var(--accent)) 10%, var(--aurora-base-1)) 0 8px,
    var(--aurora-base-1) 8px 16px);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--iris-accent, var(--accent)) 56%, transparent);
}
.role-cover span { font-size: 26px; color: var(--iris-accent, var(--accent)); }
.role-cover b { font-family: var(--font-mono); font-size: 9px; letter-spacing: .14em; text-transform: uppercase; }

.thread-history { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.thread-event {
  padding: 9px 10px; display: flex; flex-direction: column; gap: 3px;
  background: color-mix(in srgb, var(--iris-accent, var(--accent)) 7%, transparent);
  box-shadow: inset 2px 0 0 color-mix(in srgb, var(--iris-accent, var(--accent)) 32%, transparent);
}
.thread-event small {
  font-family: var(--font-mono); font-size: 8px; letter-spacing: .12em;
  color: var(--iris-muted, var(--ink-2));
}
.thread-event b { font-size: 12.5px; line-height: 1.35; font-weight: var(--weight-medium); }
.thread-event span { font-size: 11.5px; line-height: 1.35; color: var(--iris-muted, var(--ink-2)); }
.thread-event.tool { box-shadow: inset 2px 0 0 var(--iris-accent, var(--accent)); }
.thread-event.result { background: color-mix(in srgb, var(--iris-accent, var(--accent)) 13%, transparent); }
/* Смена состояния — системная заметка, не плита: узкая пилюля по центру,
   «что → куда» одной строкой. Полноширинный блок в чате читался дефектом
   вёрстки (скрин founder-а 15.08). */
.thread-event.state {
  flex-direction: row; align-items: baseline; flex-wrap: wrap; gap: 6px;
  align-self: center; max-width: 94%; text-align: center;
  padding: 4px 13px; border-radius: 999px;
  background: rgba(244, 238, 240, .045);
  box-shadow: none;
}
.thread-event.state b { font-size: 11.5px; font-weight: var(--weight-regular); color: var(--iris-muted, var(--ink-2)); }
.thread-event.state span { font-size: 11.5px; color: var(--iris-accent, var(--accent)); }
.thread-event.state span::before { content: '→ '; color: var(--iris-muted, var(--ink-2)); }
/* Реплики людей — канонический чат (разбор founder-а 14.08: «люди привыкли к
   другим баблам»): свои — справа акцентным пузырём, чужие — слева, на пузыре
   имя автора, рядом аватар-буква. Служебные события (tool/state) остаются
   тихими строками — они не разговор. */
.thread-event.message {
  flex-direction: row; align-items: flex-end; gap: 8px;
  padding: 0; background: transparent; box-shadow: none;
}
.thread-event.message[data-side="me"] { flex-direction: row-reverse; }
.evt-avatar {
  flex: 0 0 auto; width: 26px; height: 26px; border-radius: 999px;
  display: grid; place-items: center;
  font-size: 11px; font-style: normal; font-weight: var(--weight-semibold);
  background: rgba(244, 238, 240, .08); color: var(--iris-muted, var(--ink-2));
}
.thread-event.message[data-side="me"] .evt-avatar {
  background: color-mix(in srgb, var(--iris-accent, var(--accent)) 20%, transparent);
  color: var(--iris-accent, var(--accent));
}
.evt-bubble {
  min-width: 0; max-width: min(82%, 560px); padding: 9px 12px;
  display: flex; flex-direction: column; gap: 3px;
  border-radius: 16px 16px 16px 5px;
  background: color-mix(in srgb, var(--iris-accent, var(--accent)) 8%, transparent);
}
.thread-event.message[data-side="me"] .evt-bubble {
  border-radius: 16px 16px 5px 16px;
  color: var(--iris-on-accent, var(--on-accent));
  background: linear-gradient(125deg,
    var(--iris-accent, var(--accent)), var(--iris-accent-alt, var(--like)));
  box-shadow: 0 10px 28px -18px var(--iris-accent, var(--accent));
}
.thread-event.message[data-side="me"] .evt-bubble small { color: currentColor; opacity: .68; }
.thread-transition {
  margin: 8px 0 6px; padding: 10px 0; display: grid;
  grid-template-columns: minmax(20px, 1fr) auto minmax(20px, 1fr);
  gap: 10px; align-items: center; color: var(--iris-accent, var(--accent));
}
.thread-transition i {
  height: 1px; background: linear-gradient(90deg, transparent, currentColor);
}
.thread-transition i:last-child { transform: scaleX(-1); }
.thread-transition span {
  max-width: 30ch; text-align: center; font-family: var(--font-mono);
  font-size: 8px; line-height: 1.45; letter-spacing: .11em; text-transform: uppercase;
}

.card-stack { display: flex; flex-direction: column; gap: 6px; }
.cockpit-card {
  width: 100%; cursor: pointer; border: 0; padding: 9px 10px; text-align: left;
  display: flex; flex-direction: column; gap: 4px; font: inherit; color: inherit;
  background: color-mix(in srgb, var(--iris-accent, var(--accent)) 7%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--iris-accent, var(--accent)) 20%, transparent);
}
.cockpit-card b { font-size: 13px; }
.cockpit-card .card-description { display: none; font-size: 11.5px; line-height: 1.4; color: var(--iris-muted, var(--ink-2)); }
.cockpit-card[aria-expanded="true"] .card-description { display: block; }
.cockpit-card small { display: flex; flex-wrap: wrap; gap: 4px; }
.cockpit-card small i {
  padding: 2px 5px; font-style: normal; font-family: var(--font-mono); font-size: 7px;
  color: var(--iris-accent, var(--accent));
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--iris-accent, var(--accent)) 30%, transparent);
}

.stat-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.stat-grid dd { color: var(--iris-accent, var(--accent)); font-size: clamp(18px, 1.8vw, 25px); }
.stat-grid small { display: block; margin-top: 3px; color: var(--iris-muted, var(--ink-2)); font-size: 9px; }
.work-body { min-height: 0; overflow: auto; }
.world-actions { margin-top: auto; display: flex; flex-wrap: wrap; gap: 6px; }
/* Кнопка действия отвечает НА НАЖАТИЕ телом: короткое сжатие вместо
   перерисовки экрана — отклик виден до того, как пришёл результат. */
.world-action {
  border: 0; padding: 8px 11px; cursor: pointer; font: inherit; font-size: 11px;
  color: var(--iris-on-accent, var(--on-accent)); background: var(--iris-accent, var(--accent));
  transition: transform 120ms var(--ease-out), filter 120ms var(--ease-out);
}
.world-action:hover { filter: brightness(1.08); }
.world-action:active { transform: scale(.96); transition-duration: 60ms; }
.world-action:disabled { opacity: .5; cursor: default; }
/* Новое событие треда въезжает одной строкой — понятное движение результата
   вместо пересборки всего экрана. */
.thread-history > li { animation: evt-in .32s var(--ease-out) both; }
@keyframes evt-in { from { opacity: 0; transform: translateY(5px); } }
.k-dice { display: flex; align-items: center; gap: 12px; }
.k-die {
  width: 54px; height: 54px; flex: 0 0 54px; display: grid; place-items: center;
  font-size: 30px; line-height: 1; color: var(--iris-accent, var(--accent));
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--iris-accent, var(--accent)) 48%, transparent);
}
.k-dice > div { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.k-dice span, .k-dice em { font-style: normal; color: var(--iris-muted, var(--ink-2)); }
.k-dice b, .k-dice span, .k-dice em { overflow-wrap: anywhere; }
@container (max-width: 360px) {
  .k-dice { display: grid; grid-template-columns: 54px minmax(0, 1fr); align-items: start; }
}
@container (max-width: 260px) {
  .k-dice { grid-template-columns: 1fr; }
}
.cockpit.skeleton { background: transparent; }
.skeleton i { height: 9px; width: 80%; background: color-mix(in srgb, var(--iris-accent, var(--accent)) 12%, transparent); }
.skeleton i:nth-child(2) { width: 58%; }
.skeleton i:nth-child(3) { width: 70%; }

body.js:is([data-stage="building"], [data-stage="cockpit"], [data-stage="simulation"], [data-stage="brief"]) .grid,
body.js:is([data-stage="building"], [data-stage="cockpit"], [data-stage="simulation"], [data-stage="brief"]) .display { display: none; }
body.js:is([data-stage="building"], [data-stage="cockpit"], [data-stage="simulation"], [data-stage="brief"]) .dock { z-index: 5; align-self: end; }
body.js:is([data-stage="building"], [data-stage="cockpit"], [data-stage="simulation"], [data-stage="brief"]) .below { pointer-events: none; }

@media (max-width: 899px) {
  body.js:not([data-act="read"]) { overflow-y: auto; height: auto; min-height: 100svh; }
  body.js:not([data-act="read"]) .page { height: auto; min-height: 100svh; }
  .cockpit-host {
    grid-column: 1; grid-row: 1; width: 100%; padding-bottom: 0;
    max-height: none; overflow: visible;
  }
  .cockpit {
    grid-template-columns: 1fr;
    grid-template-rows: auto;
    grid-template-areas: "role" "thread" "cards" "stats" "work";
  }
  .cockpit-pane { min-height: 150px; overflow: visible; scroll-margin-top: 12px; }
  .cockpit-thread { min-height: 220px; }
  .intent-fragment .intent-screen { max-height: none; }
  /* Экран собранного мира занимает ВЕСЬ ряд над пультом (решение founder-а
     14.08): прежний кап min(62svh, 560px) оставлял над миром пустую верхнюю
     половину — при прибитом пульте пустота сверху ничем не занята. Контент
     прокручивается внутри экрана мира, а не двигает корпус. */
  body.js[data-stage="assembled"] .display,
  body.js:is([data-stage="intent-simulation"], [data-stage="intent-brief"]) .display {
    height: auto; align-self: stretch; max-height: 100%; min-height: 0;
  }
  body.js:is([data-stage="building"], [data-stage="cockpit"], [data-stage="simulation"], [data-stage="brief"]) .console {
    grid-template-areas: "thread" "dock" "below";
  }
  body.js:is([data-stage="building"], [data-stage="cockpit"], [data-stage="simulation"], [data-stage="brief"]) .dock {
    position: sticky; bottom: 8px; grid-row: 2; margin-top: -8px;
  }
  /* Sticky-пульт кокпита липнет к вьюпорту, только если сцена — не
     скролл-контейнер: базовый overflow: hidden auto делал .stage рамкой
     прилипания, и пульт оставался под сгибом (найдено прогоном V2 14.08). */
  body.js:is([data-stage="building"], [data-stage="cockpit"], [data-stage="simulation"], [data-stage="brief"]) .stage {
    overflow: visible;
  }
}

/* ── Морф «стол» (визуальная развязка) ────────────────────────────────────
   Смысловой граф отделён от воплощения: roster с motif=table садится местами
   вокруг сукна, центр показывает общие узлы других экранов. Красится темой
   заказа через --accent — пиксели детерминированные, токены не тратятся. */
.intent-view[data-surface="table"] .intent-view-body { padding: 0; }
.table-surface {
  position: relative;
  aspect-ratio: 5 / 4;
  min-height: 240px;
  margin: 28px 8px 12px;
}
.table-felt {
  position: absolute;
  inset: 16% 10%;
  border-radius: 50%;
  background:
    radial-gradient(ellipse at 50% 38%, color-mix(in srgb, var(--accent) 26%, #0b120d) 0%, color-mix(in srgb, var(--accent) 14%, #090f0b) 62%, #070c09 100%);
  border: 1px solid color-mix(in srgb, var(--like, var(--accent)) 55%, transparent);
  box-shadow:
    inset 0 0 34px color-mix(in srgb, var(--accent) 22%, transparent),
    inset 0 0 4px color-mix(in srgb, var(--like, var(--accent)) 45%, transparent),
    0 14px 34px rgba(0, 0, 0, 0.45);
  display: grid;
  place-items: center;
}
.table-felt::before {
  content: "";
  position: absolute;
  inset: 9%;
  border-radius: 50%;
  border: 1px dashed color-mix(in srgb, var(--like, var(--accent)) 40%, transparent);
  pointer-events: none;
}
.table-center {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  justify-content: center;
  align-items: center;
  max-width: 78%;
}
.table-chip {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: 7px 12px;
  border-radius: 12px;
  background: color-mix(in srgb, var(--accent) 12%, rgba(0, 0, 0, 0.55));
  border: 1px solid color-mix(in srgb, var(--like, var(--accent)) 45%, transparent);
  backdrop-filter: blur(4px);
}
.table-chip small {
  font-size: 10px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-2);
}
.table-chip b {
  font-size: 13px;
  color: color-mix(in srgb, var(--like, var(--accent)) 55%, var(--ink));
  max-width: 130px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.table-seat {
  position: absolute;
  transform: translate(-50%, -50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  text-align: center;
  max-width: 92px;
  z-index: 1;
}
.table-seat b, .table-seat small {
  background: rgba(4, 8, 6, 0.72);
  padding: 1px 7px;
  border-radius: 7px;
}
.table-seat i {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  font-style: normal;
  font-weight: 700;
  color: #08110b;
  background: linear-gradient(160deg, var(--accent), color-mix(in srgb, var(--accent) 55%, #ffffff));
  box-shadow: 0 4px 12px color-mix(in srgb, var(--accent) 40%, transparent);
}
.table-seat b {
  font-size: 12px;
  color: var(--ink);
  max-width: 92px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.table-seat small {
  font-size: 10px;
  color: var(--ink-2);
  max-width: 92px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* ПОЛ И ПОЛЯ ТОЖЕ ЖЁСТКИЕ — и режут ровно так же, как резал aspect-ratio.
   Цепочка сжатия выше работает: при 1180×640 она честно отдаёт столу 3 точки.
   Но min-height: 76px отказывался их принять, и сукно уезжало за плиту вместе
   с местами. Замер 15.08 по семи окнам показал рез при СТАРОМ правиле в пяти:
   1180×640 −82, 1280×720 −27, 390×660 −12, 375×600 −68, 320×568 −134. То есть
   любой пол в точках рано или поздно находит окно, где он больше остатка.
   Пола нет: желаемый размер держит aspect-ratio, в тесной плите стол сжимается.
   Поля — по той же причине маленькие. Места — абсолютные дети сукна на проценте
   от его бокса, но ростом в пикселях (аватар + мини-рука ≈ 50), поэтому крайнее
   всегда торчит за кромку; большие поля пытались зарезервировать этот ореол, но
   в тесной плите сами становились вылетом и резали сильнее (20/20 → −11 там,
   где 8/8 даёт 0). Итог замера: рез 0 в шести окнах из семи.
   ОСТАЁТСЯ 320×568: там на карточку остаётся ~0 точек бюджета, сукно вырождается
   в 2 точки, а метки мест сваливаются в кучу и торчат на 51. Это уже не про
   стол, а про рост самой карточки шага (заголовок + usage съедают её целиком) —
   развилка founder-а: снимать морф ниже некоторой высоты или ужимать заголовок. */
.intent-preview-module .table-surface {
  flex: 1 1 auto;
  min-height: 0;
  aspect-ratio: 16 / 10;
  max-height: 230px;
  margin: 8px 0;
}
.intent-preview-module .table-seat i { width: 26px; height: 26px; }
.intent-preview-module .table-seat b, .intent-preview-module .table-seat small { display: none; }
.intent-preview-module .table-chip { padding: 4px 8px; border-radius: 9px; }
.intent-preview-module .table-chip b { font-size: 11px; max-width: 96px; }
.intent-preview-module .table-chip small { font-size: 8px; }
.intent-preview-module .table-felt { inset: 12% 5%; }
.intent-preview-module .table-chip:nth-child(n+2) { display: none; }

.table-cards {
  display: flex;
  gap: 0;
  margin-right: 8px;
}
.table-card {
  width: 22px;
  height: 32px;
  border-radius: 4px;
  border: 1px solid color-mix(in srgb, var(--like, var(--accent)) 60%, transparent);
  background:
    repeating-linear-gradient(135deg, color-mix(in srgb, var(--accent) 35%, #0c130e) 0 3px, color-mix(in srgb, var(--accent) 18%, #0a100c) 3px 6px);
  box-shadow: 0 3px 8px rgba(0, 0, 0, 0.5);
}
.table-card + .table-card { margin-left: -14px; transform: rotate(9deg); }
.table-hand {
  display: flex;
  margin-top: 2px;
}
.table-hand .table-card {
  width: 13px;
  height: 19px;
  border-radius: 3px;
}
.table-hand .table-card + .table-card { margin-left: -7px; transform: rotate(12deg); }
.intent-preview-module .table-cards .table-card { width: 18px; height: 26px; }

/* Шоудаун: лицевые карты. Красные масти краснеют, чёрные держат светлый ink —
   узнаваемость колоды важнее монохрома темы, поэтому масть решает цвет. */
.table-card.is-face {
  display: grid;
  place-content: center;
  gap: 0;
  background: linear-gradient(168deg, #f6f4ef, #dcd8cf);
  border-color: rgba(255, 255, 255, 0.75);
  color: #14181c;
  line-height: 1;
}
.table-card.is-face b { font-size: 11px; font-weight: 800; }
.table-card.is-face i { font-size: 10px; font-style: normal; text-align: center; }
.table-card.is-face[data-suit="♥"], .table-card.is-face[data-suit="♦"] { color: #c0392b; }
.table-cards.is-open { gap: 3px; margin-right: 0; }
.table-cards.is-open .table-card { width: 26px; height: 36px; }
.table-cards.is-open .table-card + .table-card { margin-left: 0; transform: none; }
.table-chip.is-hand { padding: 6px 9px; gap: 5px; }
.intent-preview-module .table-cards.is-open .table-card { width: 20px; height: 28px; }
.intent-preview-module .table-card.is-face b { font-size: 9px; }
.intent-preview-module .table-card.is-face i { font-size: 8px; }
