*{box-sizing:border-box}
html{color-scheme:light only;background:var(--bg);-webkit-text-size-adjust:100%;forced-color-adjust:none;scroll-behavior:smooth}
html,body{margin:0;padding:0}
body{background:var(--bg);color:var(--ink);font-family:var(--body);-webkit-font-smoothing:antialiased}
.wrap{max-width:1320px;margin:0 auto;padding:0 20px}

a:focus-visible{outline:2px solid var(--accent);outline-offset:4px}

/* ---- появление при прокрутке ----------------------------------------
   Прячем только при js-rv на <html> — класс ставит скрипт в <head>, так что
   без JS страница остаётся видимой. Список селекторов дублирует reveal.js.

   Список обёрнут в :where() намеренно: он не добавляет веса, поэтому весь
   селектор весит ровно как html.js-rv и правило показа ниже всегда сильнее.
   Без этого составные селекторы (.chead .brief, .phases > div) оказывались
   тяжелее .rv--in — класс навешивался, а блок оставался невидимым. */
html.js-rv :where(
  .chead .brief, .chead .meta, .labelrow, .stub__t, .lead,
  .phases > div, .insights > div, .jtbd__card, .fact, .shot,
  .tbl, .claim, .note, .about__grid, .project, .contact, .outcome
){opacity:0;transform:translateY(14px)}
html.js-rv .rv{
  transition:opacity .6s ease, transform .6s cubic-bezier(.22,.8,.2,1)}
html.js-rv .rv--in{opacity:1;transform:none}

@media (prefers-reduced-motion: reduce){
  html.js-rv .rv{transition:none}
}

/* ---- кнопка «Наверх» ----
   Стекло то же, что у навигации. На узком экране уезжает выше нижней
   навигации по разделам, на широком встаёт справа от неё — на той же
   линии и той же высоты.

   Про padding. У навигации кегль и отступы ссылки те же (11px/16px), но
   ссылка лежит внутри обёртки с padding:6px, поэтому пилюля на 12px выше.
   Здесь обёртки нет, и эти 6px добавлены прямо к кнопке — иначе она
   оказывалась ниже и мельче соседней пилюли. Менять только парой. */
.totop{position:fixed;right:24px;bottom:18px;z-index:61;
  display:inline-flex;align-items:center;gap:8px;
  padding:calc(11px + 6px) calc(16px + 6px);border-radius:999px;cursor:pointer;
  font-family:var(--mono);font-size:11px;letter-spacing:.14em;text-transform:uppercase;
  color:var(--ink-2);background:rgba(255,255,255,.66);
  -webkit-backdrop-filter:blur(24px) saturate(1.8);
  backdrop-filter:blur(24px) saturate(1.8);
  border:1px solid rgba(11,11,12,.09);
  box-shadow:0 10px 32px rgba(11,11,12,.10), inset 0 1px 0 rgba(255,255,255,.7);
  opacity:0;visibility:hidden;transform:translateY(10px);
  transition:opacity .28s ease,transform .28s cubic-bezier(.22,.8,.2,1),
             visibility .28s,color .2s ease}
.totop.on{opacity:1;visibility:visible;transform:none}
.totop:hover{color:var(--ink)}
.totop b{font-weight:400;color:var(--accent);font-size:13px;line-height:1}
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .totop{background:rgba(255,255,255,.95)}
}
@media (max-width:900px){
  .totop{right:12px;bottom:78px;padding:10px 13px;font-size:10px;letter-spacing:.1em}
}
@media (prefers-reduced-motion: reduce){
  .totop{transition:opacity .01s,visibility .01s;transform:none}
}

/* только для скринридеров */
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
  clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0}

@media (prefers-reduced-motion: reduce){
  html{scroll-behavior:auto}
}

/* ---- общий размер плавающих пилюль ----
   Навбар и пилюля статуса стоят на одной линии и обязаны быть одной высоты.
   Держим это переменными, а не совпадающими числами в двух файлах: высота
   складывается из padding, кегля и межстрочного, и стоит разойтись одному —
   пилюли разъезжаются на пару пикселей, что видно сразу. line-height задан
   явно по той же причине: у ссылки он был `normal`, у статуса — 1, и при
   одинаковом padding пилюли всё равно отличались бы на три пикселя. */
:root{--pill-py:11px;--pill-px:20px;--pill-fs:11px;--pill-lh:1.25}
@media (max-width:520px){
  :root{--pill-py:10px;--pill-px:16px;--pill-fs:10px}
}

/* ---- плавающий навбар ---- */
.navbar{position:fixed;top:14px;left:0;right:0;z-index:50;
  display:flex;justify-content:center;pointer-events:none;
  transition:transform .42s cubic-bezier(.2,.8,.2,1), opacity .3s ease}
.navbar.hidden{transform:translateY(calc(-100% - 22px));opacity:0}

/* Полустекло: подложку делаем прозрачнее, а читаемость возвращаем размытием
   и подъёмом насыщенности. Ниже .62 текст начинает проигрывать тёмному хиро,
   который проезжает под навбаром, поэтому дальше не разбавляем. */
.nav{pointer-events:auto;display:flex;align-items:center;gap:30px;
  padding:var(--pill-py) var(--pill-px);border-radius:999px;
  background:rgba(246,244,241,.62);
  -webkit-backdrop-filter:blur(22px) saturate(1.8);
  backdrop-filter:blur(22px) saturate(1.8);
  border:1px solid rgba(11,11,12,.08);
  box-shadow:0 8px 26px rgba(11,11,12,.08),
             inset 0 1px 0 rgba(255,255,255,.55)}
/* если браузер не умеет backdrop-filter — возвращаем плотный фон */
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .nav{background:rgba(246,244,241,.94)}
}
.nav a{position:relative;
  font-family:var(--mono);font-size:var(--pill-fs);line-height:var(--pill-lh);
  letter-spacing:.16em;text-transform:uppercase;
  text-decoration:none;color:var(--ink-3);transition:color .2s}
.nav a:focus-visible{color:var(--ink)}
@media (hover: hover){
  .nav a:hover{color:var(--ink)}
}
.nav .back{color:var(--ink)}
.nav__l{display:flex;gap:20px}

/* ---- пиксельный ховер ----
   Раньше ссылка просто темнела. Теперь под ней слева направо собирается
   строка из квадратов — тот же словарь, что у разделителя .rule (квадрат 5px,
   шаг 12px), и то же движение, что у частиц в хиро: россыпь становится
   структурой. Ведёт сборку коралловый квадрат: он приклеен к правому краю
   растущей полосы, поэтому едет впереди неё, как курсор.

   Цвет ссылки продолжает меняться — на одну только полосу состояние вешать
   нельзя, она слишком мелкая, чтобы быть единственным признаком.

   Квадрат здесь 3px, а не 5px как у .rule: под текстом внутри пилюли всего
   11px отступа, и пятипиксельная полоса упиралась бы прямо в границу. Это
   внутренний масштаб того же словаря, как мелкий пунктир в таблицах. */
.nav a::after{
  content:"";position:absolute;left:0;bottom:-4px;height:3px;width:0;
  background-image:
    linear-gradient(var(--accent) 0 0),
    repeating-linear-gradient(to right,
      rgba(11,11,12,.45) 0 3px, transparent 3px 8px);
  background-size:3px 3px, auto auto;
  background-position:right center, left center;
  background-repeat:no-repeat, repeat-x;
  transition:width .34s cubic-bezier(.2,.8,.2,1)}
.nav a:focus-visible::after{width:100%}
@media (hover: hover){
  .nav a:hover::after{width:100%}
}
@media (prefers-reduced-motion: reduce){
  .nav a::after{transition:none}
}

.spacer{height:78px}

@media (prefers-reduced-motion: reduce){ .navbar{transition:none} }
@media (max-width:520px){
  .nav{gap:18px}
  .nav__l{gap:14px}
  .nav__l a,.nav a{letter-spacing:.14em}
}

/* ---- словарь: строка из квадратиков вместо линии ---- */
.rule{height:5px;border:0;margin:0;
  background-image:repeating-linear-gradient(to right,
    rgba(11,11,12,.30) 0 5px, transparent 5px 12px);
  -webkit-mask-image:linear-gradient(to right,transparent,#000 8%,#000 82%,transparent);
  mask-image:linear-gradient(to right,transparent,#000 8%,#000 82%,transparent)}

/* ---- акцент на ключевой фразе (.mark) ---------------------------------
   Каретка печатает фразу. Порядок: каретка появляется в начале фразы →
   мигает один раз → едет вправо, проявляя текст из-под маски → исчезает,
   оставив фразу в полном --ink на фоне обычного --ink-2. Всего ~1.56 с.
   Инструкция по применению — docs/ACCENT_MARK.md.

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

   ЦВЕТ. Каретка чёрная, не коралловая. Это сознательно: акцент не тратит
   единственный акцентный цвет проекта и не заводит второго. Выделение
   держится плотностью — фраза чернее окружающего текста.

   МАСКА ЖИВЁТ ТОЛЬКО ПРИ РАБОТАЮЩЕМ JS. Классы .mark__w и .is-armed
   навешивает mark.js; без него в разметке остаётся голый <span class=mark>
   с обычным видимым текстом. Это не перестраховка: маска скрывает фразу
   целиком, и если бы она применялась из CSS, то при любом отказе JS в
   предложении навсегда осталась бы дыра — то есть сломанный текст, а не
   просто пропавший эффект.

   ПОЧЕМУ ПО СЛОВАМ, А НЕ ОДНОЙ МАСКОЙ НА ФРАЗУ. Маска на строчном элементе
   считается по общему прямоугольнику всех его строк, поэтому перенесённая
   фраза раскрывалась бы на обеих строках разом. Слово переносом не рвётся,
   значит каждое гарантированно лежит на одной строке — и каретка сама
   перепрыгивает на новую строку вместе со словом. */
.mark{
  --mark-in:220ms;      /* каретка появляется */
  --mark-blink:590ms;   /* один цикл мигания */
  --mark-write:750ms;   /* печать всей фразы */
  position:relative;
  color:var(--ink)}

/* Слово. inline-block — чтобы маска считалась по самому слову.
   white-space:pre защищает пробел внутри слова, если он там окажется. */
.mark__w{position:relative;display:inline-block;white-space:pre}

/* Скрытое состояние. Навешивается только после разбивки на слова. */
.mark.is-armed .mark__w{
  -webkit-mask-image:linear-gradient(#000 0 0);mask-image:linear-gradient(#000 0 0);
  -webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;
  -webkit-mask-position:left center;mask-position:left center;
  -webkit-mask-size:0 100%;mask-size:0 100%}

/* Каретка до старта — стоит перед первой буквой фразы. */
.mark::before{content:"";position:absolute;left:-1px;top:.02em;
  height:1.06em;width:2px;background:var(--ink);opacity:0}
.mark.is-cursor::before{opacity:1;
  transition:opacity var(--mark-in) cubic-bezier(.2,.8,.2,1)}
.mark.is-blink::before{opacity:1;
  animation:mark-blink var(--mark-blink) steps(1,end) 1}
@keyframes mark-blink{0%,49%{opacity:1}50%,100%{opacity:0}}

/* Каретка внутри слова — ведёт раскрытие этого слова и гаснет на его конце.
   Своя каретка у каждого слова: так переход на новую строку получается сам,
   без вычисления координат. */
.mark__w::after{content:"";position:absolute;left:0;top:.02em;
  height:1.06em;width:2px;background:var(--ink);opacity:0}

/* Печать. --w-dur и --w-del своя у каждого слова, их считает mark.js по
   ширине слова — иначе каретка дёргалась бы, проскакивая короткие слова
   за то же время, что и длинные. */
.mark.is-armed.is-writing .mark__w{
  -webkit-mask-size:100% 100%;mask-size:100% 100%;
  transition:-webkit-mask-size var(--w-dur) linear var(--w-del),
             mask-size var(--w-dur) linear var(--w-del)}
.mark.is-writing .mark__w::after{
  animation:mark-caret var(--w-dur) linear var(--w-del)}
@keyframes mark-caret{
  0%{left:0;opacity:1}
  99%{opacity:1}
  100%{left:100%;opacity:0}}

/* Финал: текст открыт, кареток нет. */
.mark.is-armed.is-done .mark__w{
  -webkit-mask-size:100% 100%;mask-size:100% 100%}

/* Отказоустойчивость. Оба случая ведут к одному — обычный видимый текст.
   При печати фоны и маски ведут себя непредсказуемо, а при reduce человек
   просил не двигать: показываем фразу сразу и целиком. */
@media print{
  .mark.is-armed .mark__w{-webkit-mask-image:none;mask-image:none}
  .mark::before,.mark__w::after{display:none}
}
@media (prefers-reduced-motion: reduce){
  .mark.is-armed .mark__w{-webkit-mask-image:none;mask-image:none}
  .mark::before,.mark__w::after{display:none}
}

.labelrow{display:flex;flex-direction:column;gap:16px;margin-bottom:56px}
.sect__label,.work__label,.about__label{font-family:var(--mono);font-size:11px;letter-spacing:.18em;
  text-transform:uppercase;color:var(--ink-3);margin:0}

/* ---- таблицы ----------------------------------------------------------
   Не коробка с рамками, а сетка на самом фоне страницы: строки разделены
   той же пунктирной строкой из квадратиков, что и .rule, номер — квадрат
   с коралловым контуром. Строка «собирается» по наведению: подложка
   становится белой, квадрат заливается акцентом.
   Класс общий, рассчитан и на кейсы, и на главную. */
.tbl{margin:0;overflow-x:auto}
/* fixed — чтобы заданная ширина левой колонки соблюдалась точно и текст
   во всех таблицах начинался с одной вертикали */
.tbl table{width:100%;border-collapse:collapse;text-align:left;table-layout:fixed}
/* заголовок таблицы — на одной ступени с метками секций (.sect__label) */
.tbl caption{text-align:left;font-family:var(--mono);font-size:11px;letter-spacing:.18em;
  text-transform:uppercase;color:var(--ink-2);padding:0 0 28px}

.tbl th,.tbl td{vertical-align:top;padding:26px 24px 28px;font-weight:400}
.tbl th:first-child,.tbl td:first-child{padding-left:0}
.tbl th:last-child,.tbl td:last-child{padding-right:0}

/* шапка: только мелкая моно-подпись, без заливки */
.tbl thead th{font-family:var(--mono);font-size:10px;letter-spacing:.2em;text-transform:uppercase;
  color:var(--ink-3);padding-top:0;padding-bottom:18px}

/* Разделитель строк — тот же род, но на ступень тише секционного .rule:
   квадраты 5px с шагом 12 → штрихи 2px с шагом 7, высота 5px → 1px,
   плотность .30 → .16. Секцию делит крупный пунктир, строку внутри — мелкий,
   поэтому таблица не разваливается на отдельные блоки. */
.tbl tbody tr{
  background-image:repeating-linear-gradient(to right,
    rgba(11,11,12,.16) 0 2px, transparent 2px 7px);
  background-size:100% 1px;background-position:left bottom;background-repeat:repeat-x}
.tbl tbody tr:last-child{background-image:none}

.tbl td{font-size:15px;line-height:1.62;color:var(--ink-2);text-wrap:pretty}
.tbl td b{font-weight:500;color:var(--ink)}
/* колонка держит комфортную длину строки, а не всю ширину таблицы */
.tbl__txt{margin:0;max-width:62ch;text-wrap:pretty}

/* номер строки — квадрат из общего словаря */
.tbl__n{display:inline-flex;align-items:center;justify-content:center;
  width:34px;height:34px;border-radius:7px;flex:none;
  font-family:var(--mono);font-size:12px;line-height:1;
  color:var(--accent);border:1px solid rgba(228,87,58,.42);background:transparent}

/* подпись строки слева */
.tbl__k{font-family:var(--mono);font-size:10px;letter-spacing:.18em;text-transform:uppercase;
  color:var(--ink-3);white-space:nowrap}

/* итоговая строка — то, ради чего таблицу и читают */
.tbl__sum td{color:var(--ink)}
.tbl__sum .tbl__k{color:var(--accent)}

/* На узком экране сравнение не скроллим вбок, а разворачиваем в карточки:
   название колонки подставляется из data-col. */
/* Селекторы удвоены (.tbl.tbl--stack) намеренно: колоночные ширины и отбивки
   конкретных таблиц живут в case.css, который подключается позже, и при равном
   весе перебивали бы эти правила — ячейка получала бы десктопную ширину вроде
   calc((100% - 132px)/4) и схлопывалась на телефоне до одного слова в строке. */
@media (max-width:760px){
  .tbl.tbl--stack table,.tbl.tbl--stack tbody,.tbl.tbl--stack tr,
  .tbl.tbl--stack th,.tbl.tbl--stack td{display:block;width:auto}
  .tbl.tbl--stack thead{display:none}
  /* у table:display:block подпись перестаёт наследовать ширину таблицы
     и сжимается по содержимому — рвётся на слова при пустом месте справа */
  .tbl.tbl--stack caption{display:block;width:auto}
  .tbl.tbl--stack tbody tr{padding:20px 0 24px}
  .tbl.tbl--stack th[scope=row]{width:auto;padding:0 0 14px}
  .tbl.tbl--stack td{padding:0 0 18px}
  .tbl.tbl--stack td:last-child{padding-bottom:0}
  .tbl.tbl--stack td::before{content:attr(data-col);display:block;
    font-family:var(--mono);font-size:10px;letter-spacing:.16em;text-transform:uppercase;
    color:var(--ink-3);margin-bottom:7px}
}
