/* ═══════════════════════════════════════════════════════════════
   ПРОЗА — типографика в потоке, таблицы, иллюстрации, выноски.

   ЗАГРУЖАЕТСЯ ОБОИМИ ТИРАМИ, ОБЯЗАТЕЛЬНО.

   Причина: gate.js расшифровывает фрагмент и вставляет его в .unlocked.
   Фрагменты используют .tablewrap, table, figure, figcaption, .placeholder
   и .pull — а эти селекторы жили только в case.css. Пока страницы по
   случайности грузили и case.css тоже, это работало. Шаблон
   template-experience-landing.html этого не делал, и любая таблица в нём
   рендерилась голой уже сегодня. Когда за паролем оказывается весь сайт,
   такая случайность перестаёт спасать.

   Поэтому проза — часть общего слоя, а не довесок к vault: публичным
   нарративам кейсов нужны те же таблицы.
   ═══════════════════════════════════════════════════════════════ */

section { padding: var(--sp-8) 0; border-bottom: 1px solid var(--rule); }
section:last-of-type { border-bottom: 0; }

h1 {
  font-size: var(--fs-hero); line-height: 1.04;
  font-weight: var(--fw-bold); letter-spacing: -.03em;
  color: var(--ink);
}

h2 {
  font-size: clamp(24px, 3.4vw, 32px);
  font-weight: var(--fw-bold); letter-spacing: var(--tracking-tight);
  color: var(--ink); line-height: 1.16;
  /* Было: сверху 0, а у h3 — 32px. Подзаголовок получал больше воздуха,
     чем глава над ним. Правило: верхний отступ уровня на шаг больше
     детского. */
  margin: var(--sp-7) 0 var(--sp-4);
}

h3 {
  font-size: var(--fs-2xl);
  font-weight: var(--fw-bold);
  color: var(--ink);
  margin: var(--sp-6) 0 var(--sp-2);
  letter-spacing: -.02em;
  line-height: var(--lh-snug);
}

h4 {
  font-size: var(--fs-lg); font-weight: var(--fw-semibold);
  color: var(--ink); margin: var(--sp-5) 0 var(--sp-1);
}

/* Первый элемент секции не отбивается сверху — секция уже отбита сама. */
section > .wrap > :first-child,
.section-head > :first-child { margin-top: 0; }

/* Лид секции. Точка входа каждой главы, ценой ноль изменений в контенте —
   и единственное, что превращает односабзацные секции из обрубка в
   утверждение. Публичному тиру, где картинок нет по NDA, ритм нести нечем
   кроме типографики. */
section > .wrap > p:first-of-type,
.section-head + p {
  font-size: var(--fs-xl);
  line-height: 1.5;
  color: var(--ink);
}

p { margin-bottom: var(--sp-4); max-width: var(--measure); }
p:last-child { margin-bottom: 0; }

strong, b { color: var(--ink); font-weight: var(--fw-semibold); }
em { color: var(--ink); font-style: italic; }

/* Технические имена — селектор, свойство, токен — набираются моноширинным,
   чтобы отличаться от прозы. На странице про дизайн-инжиниринг это не
   украшение: читатель должен видеть, где называется механизм. */
code {
  font-family: var(--font-mono);
  font-size: max(13px, .92em);
  line-height: 1;
  padding: .1em .34em;
  border-radius: 4px;
  background: var(--bg-pill);
  color: var(--ink);
  /* nowrap давал горизонтальный скролл ВСЕЙ страницы на 375px: одно
     длинное имя вроде widget/horizontal/1-slot-overall/1-slot-displayed
     шире вьюпорта. Переносим по границам слов, а длинные пути ломаем
     где придётся — читаемость имени важнее его целостности в одну строку. */
  overflow-wrap: anywhere;
  word-break: break-word;
}

/* ── блоки кода и вывода ────────────────────────────────────────
   Принцип, по которому эти блоки вообще существуют: не скриншотить то,
   что можно сверстать. Объявление вариантов компонента, вывод гейта и
   диффы токенов на визуальном портфолио обычно приезжают картинками —
   70 КБ на 2 КБ смысла, замыленные на ретине, невыделяемые и нечитаемые
   на телефоне. Как текст они весят на порядок меньше, ищутся поиском и
   остаются резкими.

   Внутри pre перенос ЗАПРЕЩЁН, в отличие от инлайнового code: в выводе
   гейта колонки значат больше, чем помещаемость. Поэтому блок скроллится
   сам, а страница — нет. */
pre {
  margin: var(--sp-5) 0;
  padding: var(--sp-4) var(--sp-5);
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: var(--radius-md);
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  font-family: var(--font-mono);
  font-size: max(12px, .8em);
  line-height: 1.55;
  color: var(--ink);
  tab-size: 2;
  max-width: 100%;
}
/* Вложенный code не должен получать вторую подложку и второй радиус:
   таблетка поверх плиты читается как ошибка вёрстки. */
pre code {
  display: block;
  padding: 0;
  background: none;
  border-radius: 0;
  font-size: inherit;
  line-height: inherit;
  white-space: pre;
  overflow-wrap: normal;
  word-break: normal;
}
/* Роли внутри блока. b — то, ради чего блок стоит на странице (счётчик
   храповика, имя правила); i — приглушённая рамка вокруг: комментарий,
   строка приглашения, путь. Никакой подсветки синтаксиса: она красит
   всё одинаково важным, а здесь важны две строки из двадцати. */
pre b { color: var(--accent); font-weight: var(--fw-semibold); }
pre i { color: var(--muted); font-style: normal; }

/* Ссылки в прозе отличались ТОЛЬКО цветом — это провал SC 1.4.1
   независимо от контраста. Подчёркивание обязательно. */
.prose a,
p a, li a, td a, figcaption a {
  color: var(--accent);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: .18em;
}
.prose a:hover, p a:hover, li a:hover { text-decoration-thickness: 2px; }

/* ── списки ─────────────────────────────────────────────────── */
ul.list, section ul:not(.list), .prose ul { list-style: none; margin: var(--sp-5) 0; max-width: var(--measure); }
ul.list li, section ul:not(.list) li, .prose ul li { position: relative; padding-left: var(--sp-5); margin-bottom: var(--sp-3); }
ul.list li::before, section ul:not(.list) li::before, .prose ul li::before {
  content: ""; position: absolute; left: 2px; top: .62em;
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--accent-dim);
}
/* Список, где маркером служит эмодзи: точка перед ним — второй маркер об
   одном и том же, и строка начинает читаться как «• 🧾», а не как «🧾». */
/* Селекторы длиннее, чем хочется: правило с точкой выше идёт через
   section ul:not(.list) li и по весу перебивает короткое ul.list-emoji. */
section ul.list-emoji li::before,
.prose ul.list-emoji li::before,
ul.list-emoji li::before { content: none; }
/* Висячая строка вместо колонки: grid на <li> разложил бы по ячейкам
   каждый инлайн-узел, и текст рассыпался бы по словам. Отрицательный
   отступ первой строки оставляет эмодзи на поле, а перенос — по тексту. */
section ul.list-emoji li,
.prose ul.list-emoji li,
ul.list-emoji li { padding-left: 1.9em; text-indent: -1.9em; }

section ol, .prose ol { margin: var(--sp-5) 0 var(--sp-5) var(--sp-5); max-width: var(--measure); }
section ol li, .prose ol li { margin-bottom: var(--sp-3); padding-left: var(--sp-2); }

/* ── выноска ────────────────────────────────────────────────── */
.pull {
  margin: var(--sp-6) 0;
  padding: var(--sp-5) var(--sp-5);
  background: var(--surface);
  border-left: 3px solid var(--accent);
  border-radius: 0 var(--radius-md) var(--radius-md) 0;
  font-size: var(--fs-lg); line-height: 1.5;
  color: var(--ink); font-weight: var(--fw-medium);
  max-width: 30rem;
}

/* ── таблицы ────────────────────────────────────────────────── */
/* Таблица шире обёртки скроллится, но без tabindex до неё нельзя добраться
   с клавиатуры — а на кейсе Uvoteam за краем спрятаны колонки «vs. control»
   и «probability of outperforming», то есть весь смысл таблицы. */
.tablewrap:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}
.tablewrap {
  overflow-x: auto;
  margin: var(--sp-5) 0;
  -webkit-overflow-scrolling: touch;
}
table { border-collapse: collapse; width: 100%; min-width: 520px; font-size: var(--fs-sm); }
th, td { text-align: left; padding: var(--sp-3) var(--sp-4); border-bottom: 1px solid var(--rule); }
th {
  font-size: var(--fs-xs); font-weight: var(--fw-semibold);
  letter-spacing: .1em; text-transform: uppercase;
  color: var(--muted); border-bottom-color: var(--ink);
}
td strong { color: var(--accent); font-weight: var(--fw-semibold); }
tbody tr:hover { background: var(--surface); }

/* ── иллюстрации ────────────────────────────────────────────── */
figure { margin: var(--sp-6) 0; }
/* Ширина колонки — не команда растянуться. Скриншот 320px в высоту 1500
   при width:100% превращается в трёхметровую простыню, потому что колонка
   втрое шире исходника. Потолок по высоте решает это без класса на каждую
   картинку: широкие кадры его не достают и ведут себя как прежде, узкие
   упираются и центрируются в своём масштабе. */
/* Раскладка выбирается по ПРОПОРЦИИ, класс ставит сборка (aspect_class).
   Одно правило на всех давало либо растянутые портреты, либо мелкие
   слайды — потому что 16:9 и 3:10 нельзя обслужить одинаково. */
figure img {
  display: block;
  width: auto;
  max-width: 100%;
  height: auto;
  margin-inline: auto;
  border-radius: var(--radius-md);
  border: 1px solid var(--rule);
  background: var(--surface);
}
/* Широкое и «оконное»: во всю колонку, ширина не зависит ни от чего. */
figure.fig-wide img,
figure.fig-app img { width: 100%; }

/* Портрет никогда не занимает всю колонку — мёртвая правая половина
   отдаётся подписи. */
figure.fig-portrait,
figure.fig-strip {
  display: grid;
  grid-template-columns: minmax(0, 420px) minmax(0, 1fr);
  gap: var(--sp-5);
  align-items: start;
}
figure.fig-portrait img { width: 100%; max-height: 700px; }
figure.fig-portrait figcaption,
figure.fig-strip figcaption { margin-top: 0; }

/* Полоса вроде 450×1500: масштабировать её до марки нечестно, показываем
   в натуральную величину в рамке со своим скроллом. Рамку ставит сборка
   (hoist), поэтому здесь описывается окно, а не картинка. */
figure.fig-strip {
  grid-template-columns: minmax(0, 340px) minmax(0, 1fr);
  /* Подпись к полосе короткая, а окно высокое: прижатая к верху,
     она оставляет справа пустой прямоугольник в полкадра. По центру
     пустота делится пополам и читается как поле, а не как обрыв. */
  align-items: center;
}
figure.fig-strip .strip-frame {
  max-height: 560px;
  overflow-y: auto;
  overscroll-behavior: contain;
  /* Тонкая полоса прокрутки — единственный признак того, что кадр
     продолжается ниже рамки; без неё окно читается как обрезанная
     картинка. */
  scrollbar-width: thin;
  scrollbar-color: var(--rule) transparent;
  border: 1px solid var(--rule);
  border-radius: var(--radius-md);
  background: var(--surface);
}
figure.fig-strip .strip-frame img {
  display: block;
  width: 100%;
  max-width: none;
  height: auto;
  max-height: none;
  border: 0;
  border-radius: 0;
  margin: 0;
}

@media (max-width: 760px) {
  figure.fig-portrait,
  figure.fig-strip { grid-template-columns: 1fr; }
  figure.fig-portrait img { max-width: 320px; }
  figure.fig-strip .strip-frame { max-width: 320px; max-height: 520px; }
}

/* Встроенная доска: единственный внешний ресурс во всём vault. Грузится
   с miro.com, поэтому живёт в рамке с фиксированной пропорцией — чтобы
   страница не прыгала, пока iframe тянется, и чтобы на телефоне доска
   не отбирала весь экран. Прод-CSP разрешает кадр только с miro.com. */
figure .board-embed {
  position: relative;
  aspect-ratio: 16 / 9;
  border: 1px solid var(--rule);
  border-radius: var(--radius-md);
  background: var(--surface);
  overflow: hidden;
}
figure .board-embed iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
  display: block;
}
@media (max-width: 760px) {
  figure .board-embed { aspect-ratio: 4 / 3; }
}

figcaption {
  margin-top: var(--sp-3);
  padding-left: var(--sp-4);
  border-left: 2px solid var(--rule);
  font-size: var(--fs-sm);
  color: var(--muted);
  line-height: 1.55;
  /* Не мера прозы: подпись принадлежит картинке и живёт по её ширине.
     Иначе под кадром в 870px висит блок в 620px, прижатый влево. */
  max-width: min(100%, 42rem);
}
figcaption strong { color: var(--body); }

/* Место под материал, которого ещё нет. Появляется только внутри vault:
   на публичном тире обещать пустую рамку нечестно. */
.placeholder {
  aspect-ratio: 16 / 9;
  display: grid; place-items: center;
  border-radius: var(--radius-md);
  border: 1px dashed var(--vault-seal-rule);
  background: var(--vault-seal-wash);
  color: var(--muted); font-size: var(--fs-sm);
  text-align: center; padding: var(--sp-5);
}

/* ── редакция ───────────────────────────────────────────────────
   Публичный тир помечает ею место, где число существует, но не
   называется. Это и есть момент передачи: публичное число делает
   работу, редакция доказывает, что приватное число существует. */
.redacted {
  background: var(--redaction-bg);
  color: var(--redaction-ink);
  padding: 0 .34em; border-radius: 3px;
  font-weight: var(--fw-semibold);
  text-decoration: underline;
  text-decoration-style: dotted;
  text-underline-offset: .2em;
  cursor: help;
}

/* Исход секции. Раньше единственный результат был набран тем же кеглем и
   тем же приглушённым цветом, что и три повтора вокруг него. */
p.result-line {
  font-size: var(--fs-lg);
  color: var(--ink);
  font-weight: var(--fw-medium);
  padding-left: var(--sp-4);
  border-left: 2px solid var(--accent);
  max-width: var(--measure);
}

.figure-loss { color: var(--accent-red); }

.vault-note {
  margin-top: var(--sp-6);
  padding: var(--sp-4) var(--sp-5);
  border: 1px solid var(--vault-seal-rule);
  border-radius: var(--radius-md);
  background: var(--vault-seal-wash);
  color: var(--body); font-size: var(--fs-sm);
  max-width: var(--measure);
}

@media (max-width: 768px) {
  section { padding: var(--sp-7) 0; }
  .pull { font-size: var(--fs-lg); padding: var(--sp-4); }
}
