/* ==========================================================================
   Базовые стили. Только логические свойства — RTL для иврита работает сам,
   без отдельной таблицы стилей и без единого переопределения.
   Инлайновых style= и <script> в разметке нет: на этом держится строгий CSP.
   ========================================================================== */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  background: var(--c-ink);
}

body {
  margin: 0;
  min-block-size: 100vh;
  display: flex;
  flex-direction: column;
  font-family: var(--font-sans);
  font-size: var(--fs-base);
  font-weight: var(--fw-light);
  line-height: var(--lh-base);
  color: var(--c-fg);
  background: var(--c-ink);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

h1, h2, h3, h4 {
  margin: 0 0 var(--sp-4);
  font-weight: var(--fw-light);
  line-height: var(--lh-tight);
  letter-spacing: var(--track-display);
  text-wrap: balance;
}
h1 { font-size: var(--fs-3xl); line-height: var(--lh-display); }
h2 { font-size: var(--fs-2xl); }
h3 { font-size: var(--fs-xl); letter-spacing: -0.015em; }
h4 { font-size: var(--fs-lg); letter-spacing: 0; }

p { margin: 0 0 var(--sp-4); max-inline-size: var(--container-text); }

a { color: var(--c-accent); text-underline-offset: 0.2em; text-decoration-thickness: 1px; }
a:hover { color: var(--c-accent-2); }

img, svg, video { max-inline-size: 100%; block-size: auto; display: block; }

ul, ol { margin: 0 0 var(--sp-4); padding-inline-start: var(--sp-5); }
li { margin-block-end: var(--sp-2); }

hr { border: 0; border-block-start: 1px solid var(--c-hair); margin-block: var(--sp-7); }

::selection { background: var(--c-accent); color: var(--c-on-accent); }

/* --- Фокус: виден всегда. Требование доступности, не украшение. ----------- */
:focus-visible {
  outline: 2px solid var(--c-focus);
  outline-offset: 3px;
}

/* --- Ссылка «перейти к содержанию» ---------------------------------------- */
.skip-link {
  position: absolute;
  inset-block-start: var(--sp-2);
  inset-inline-start: var(--sp-2);
  z-index: 200;
  padding: var(--sp-3) var(--sp-5);
  background: var(--c-accent);
  color: var(--c-on-accent);
  font-weight: var(--fw-medium);
  text-decoration: none;
  transform: translateY(-250%);
}
.skip-link:focus { transform: translateY(0); }

/* ==========================================================================
   Раскладка
   ========================================================================== */

.container {
  inline-size: 100%;
  max-inline-size: var(--container);
  margin-inline: auto;
  padding-inline: var(--pad);
}

/* --------------------------------------------------------------------------
   РИТМ СТРАНИЦЫ: тишина и удар
   --------------------------------------------------------------------------
   До этого между всеми секциями стоял ОДИН отступ. Именно равномерность, а не
   отсутствие украшений, читается как «сделано по шаблону»: глаз проходит
   страницу и не получает ни паузы, ни удара, и делает вывод, что содержимое
   везде одинаково важно — то есть не важно нигде.

   Три ступени вместо одной:
     обычная   — рядовой переход, ничего не подчёркнуто;
     --pause   — длинная тишина ПЕРЕД секцией, ради которой страница написана;
                 пустота здесь работает так же, как пауза перед фразой;
     --seam    — шва почти нет: два блока должны читаться одним разворотом,
                 поэтому линия между ними убирается, а зазор сжимается.

   Ступеней ровно три и больше не будет: четвёртая перестаёт быть системой
   и становится подбором на глаз.
   -------------------------------------------------------------------------- */
.section {
  padding-block: clamp(4rem, 10vh, 8.5rem);
  border-block-end: 1px solid var(--c-hair);
}
.section:last-of-type { border-block-end: 0; }

.section--pause { padding-block-start: clamp(7rem, 17vh, 13.5rem); }

/* Шов парный: сжать надо и низ предыдущей секции. Он находится через :has(),
   а не вторым классом в разметке, — чтобы пару физически нельзя было забыть
   проставить. Там, где :has() не поддержан, обе секции просто сохраняют
   обычный ритм: страница остаётся правильной, теряется только нюанс. */
.section--seam { padding-block-start: var(--sp-7); }
.section:has(+ .section--seam) {
  padding-block-end: var(--sp-8);
  border-block-end: 0;
}
.section--raised { background: var(--c-ink-2); }

/* --------------------------------------------------------------------------
   ПЕРЕХОД ПО ЯКОРЮ: куда встаёт страница и как она об этом сообщает
   --------------------------------------------------------------------------
   Кнопка «Отправить модель» в шапке и на главной ведёт не на верх страницы
   контактов, а прямо к действию: `/contact#send`. Обещание кнопки и то, что
   человек видит, должны совпадать — иначе он приземляется на заголовок и
   прокручивает ещё раз, уже не понимая, туда ли попал.

   Всё сделано разметкой и стилями: адрес со «решёткой» плюс `:target`.
   Ни одной строки скрипта — значит, работает и при выключенном JS, и когда
   скрипт ещё не загрузился, то есть ровно в тот момент, когда переход и
   происходит.
   -------------------------------------------------------------------------- */

/* Липкая шапка высотой 76 px накрыла бы верх цели. Отодвигаем цель от края
   на высоту шапки с запасом — тогда над надписью раздела остаётся воздух,
   а не срезанная строка. */
[data-anchor] { scroll-margin-block-start: clamp(96px, 14vh, 160px); }

/* ПРИБЫТИЕ. Блок действия на секунду берётся в акцентную рамку и гаснет.
   Почему рамка, а не движение: элемент в этот момент И ТАК въезжает по своей
   лесенке появления, и второе движение поверх первого читается как рывок.
   Цвет ничего не двигает и складывается с ходом без спора.
   Анимация трогает ТОЛЬКО box-shadow — с переходом появления (он идёт по
   opacity и transform) они не пересекаются.
   Отдельной оговорки про prefers-reduced-motion здесь не нужно: общее правило
   ниже по файлу гасит длительность всех анимаций до 0.001 мс. */
#send:target .lane__act { animation: arrive var(--dur-5) linear both; }

/* РАСКЛАДКА ПО ВРЕМЕНИ ВЫВЕРЕНА ЗАМЕРОМ, А НЕ НА ГЛАЗ.
   Первая отрисовка страницы наступает не мгновенно: в замере блок появлялся
   на экране около 250 мс после запроса. С обычной для сайта кривой --ease-out
   рамка к этому моменту успевала погаснуть на девять десятых — то есть
   подсказку никто не видел. Поэтому здесь: рамка ДЕРЖИТСЯ первые 30 % (420 мс)
   и только потом гаснет за оставшиеся 980 мс, а ход равномерный (linear).
   Равномерность тут уместна: это не движение, а угасание цвета, и закон
   «резкий разгон, долгий выкат» к нему не относится. */
@keyframes arrive {
  0%, 30% { box-shadow: 0 0 0 1px var(--c-accent), 0 0 0 5px rgba(37, 185, 233, 0.14); }
  100%    { box-shadow: 0 0 0 1px rgba(37, 185, 233, 0), 0 0 0 5px rgba(37, 185, 233, 0); }
}

/* Разворот: текстовая колонка и объект рядом */
.spread {
  display: grid;
  gap: clamp(2.5rem, 5vw, 5rem);
  grid-template-columns: 1fr;
  align-items: start;
}
@media (min-width: 60rem) {
  .spread { grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr); }
  .spread--flip > :first-child { order: 2; }
}

/* ==========================================================================
   Типографские детали
   ========================================================================== */

/* Микроподпись. Латиница и кириллица — капитель с широким трекингом,
   иврит — размером и цветом (см. переопределение токенов в tokens.css). */
.eyebrow {
  margin: 0;
  font-size: var(--fs-xs);
  font-weight: var(--fw-medium);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--c-fg-3);
}
:lang(he) .eyebrow { text-transform: none; font-size: var(--fs-sm); }

.lead { font-size: var(--fs-lg); color: var(--c-fg-2); max-inline-size: 50ch; }
.muted { color: var(--c-fg-3); }
.accent { color: var(--c-accent); }

/* Цифры: моноширинные и изолированные от bidi.
   Без изоляции «277 × 156 × 300» в иврите разворачивается задом наперёд. */
.num {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums lining-nums;
  letter-spacing: -0.01em;
}

/* ==========================================================================
   Кнопки
   ========================================================================== */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  min-block-size: 52px;
  padding-inline: var(--sp-6);
  border: 1px solid transparent;
  border-radius: var(--radius);
  font: inherit;
  font-weight: var(--fw-medium);
  font-size: var(--fs-sm);
  letter-spacing: 0.02em;
  line-height: 1.2;
  text-decoration: none;
  cursor: pointer;
  transition: background var(--transition), border-color var(--transition), color var(--transition);
}

.btn--primary {
  background: var(--c-accent);
  color: var(--c-on-accent);
  border-color: var(--c-accent);
}
.btn--primary:hover { background: var(--c-accent-2); border-color: var(--c-accent-2); color: var(--c-on-accent); }

.btn--ghost {
  background: transparent;
  color: var(--c-fg);
  border-color: var(--c-hair-2);
}
.btn--ghost:hover { border-color: var(--c-fg); color: var(--c-fg); }

/* Крупная кнопка. Не «покрупнее для красоты»: она ставится там, где кнопка
   и есть цель страницы, и должна читаться как цель ещё до того, как человек
   прочтёт надпись. Кегль поднят на ступень, высота — до 64 px, поля втрое
   шире обычных; на узком экране кнопка растягивается на всю ширину блока,
   потому что делить строку ей там не с кем. */
.btn--lg {
  min-block-size: 64px;
  padding-inline: var(--sp-9);
  font-size: var(--fs-base);
  letter-spacing: 0.01em;
}
@media (max-width: 30rem) {
  .btn--lg { inline-size: 100%; }
}

/* Стрелка в кнопке зеркалится вместе с направлением письма */
.btn__arrow { transition: transform var(--transition); }
:lang(he) .btn__arrow { transform: scaleX(-1); }
.btn:hover .btn__arrow { transform: translateX(3px); }
:lang(he) .btn:hover .btn__arrow { transform: scaleX(-1) translateX(3px); }

/* ==========================================================================
   Пометка временного текста (видна только при ?draft=1)
   ========================================================================== */
.is-draft {
  background: rgba(255, 214, 0, 0.22);
  box-shadow: 0 0 0 1px rgba(255, 214, 0, 0.45);
}

/* ==========================================================================
   Уважение к настройкам пользователя
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
}

/* ==========================================================================
   ПРОРАБОТКА ДИЗАЙНА — предложение
   ========================================================================== */

/* --- Зерно ------------------------------------------------------------------
   Ровная чернильная заливка на больших площадях выглядит пустой, а не глубокой.
   Тончайшее зерно поверх — то, что отличает «дорогое тёмное» от «дешёвого».
   Шум встроен как data-URI: ни одного запроса наружу, CSP не трогаем. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  opacity: 0.05;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)'/%3E%3C/svg%3E");
}
/* Содержимое — над зерном */
.site-header, main, .site-footer { position: relative; z-index: 2; }

/* --- Появление «печатью» ----------------------------------------------------
   Блок проявляется снизу вверх, как деталь, растущая по слоям.
   Работает только когда JS поставил класс js-reveal: без него всё видно сразу.
   Это принципиально — иначе при выключенном JS страница осталась бы пустой. */
/* ВНИМАНИЕ, ГРАБЛИ. Скрывать элемент через clip-path нельзя:
   IntersectionObserver считает пересечение ПОСЛЕ применения обрезки, поэтому
   обрезанный элемент отдаёт ratio 0 и не пересекается никогда — то есть
   наблюдатель не проявит то, что сам же и спрятал. Проверено: 16 из 16
   элементов оставались невидимыми навсегда.
   Прячем прозрачностью и сдвигом: их IntersectionObserver не учитывает. */
/* Порог непрозрачности задан токеном --reveal-floor и посчитан, а не подобран:
   см. расчёт в tokens.css. Ниже него текст во время появления уходил бы под
   норму контраста — это ловил axe (на строке каталога было 4.21 вместо 6.59).
   Раз порог считается по худшей паре, добавление новых блоков не требует
   пересчёта: ни один кадр не может провалиться. */
.js-reveal [data-reveal] {
  opacity: var(--reveal-floor);
  /* Одна ось. Ход задан токеном --travel и одинаков на всём сайте. */
  transform: translateY(var(--travel));
  /* Прозрачность догоняет раньше, чем заканчивается ход: блок успевает
     стать плотным до того, как встанет на место. Основную работу делает
     именно ход — затухание здесь только придыхание. */
  transition:
    opacity   var(--dur-3) var(--ease-out),
    transform var(--dur-4) var(--ease-out);
  will-change: opacity, transform;
}
.js-reveal [data-reveal].is-in {
  opacity: 1;
  transform: none;
  will-change: auto;
}
/* Появление одним только ходом, без придыхания. */
.js-reveal [data-reveal="move"] { opacity: 1; }

/* Лесенка: элементы внутри группы появляются друг за другом.
   Шаг задан токеном — менять ритм всего сайта одной цифрой. */
.js-reveal [data-reveal-delay="1"] { transition-delay: calc(var(--stagger) * 1); }
.js-reveal [data-reveal-delay="2"] { transition-delay: calc(var(--stagger) * 2); }
.js-reveal [data-reveal-delay="3"] { transition-delay: calc(var(--stagger) * 3); }
.js-reveal [data-reveal-delay="4"] { transition-delay: calc(var(--stagger) * 4); }
.js-reveal [data-reveal-delay="5"] { transition-delay: calc(var(--stagger) * 5); }

@media (prefers-reduced-motion: reduce) {
  .js-reveal [data-reveal] {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }
}

/* ==========================================================================
   СКЛЕЙКА МЕЖДУ СТРАНИЦАМИ (View Transitions)

   Сайт многостраничный: каждая ссылка — настоящая перезагрузка. Обычно это
   и выдаёт «сайт»: белая вспышка, прыжок шапки, потеря места. View Transitions
   превращают перезагрузку в монтажную склейку — браузер снимает кадр старой
   страницы и сводит его с новой.

   Стоит это ноль килобайт скрипта и одну строку разметки.
   Где не поддерживается (Firefox) — обычный мгновенный переход, как раньше.
   Прогрессивное улучшение в чистом виде: ломаться нечему.

   ГРАБЛИ, ПРОВЕРЕНО НА СНИМКАХ. Поведение браузера по умолчанию —
   кросс-фейд: обе страницы одновременно видны в середине перехода. У нас это
   давало нечитаемую кашу из двух заголовков сразу, и сильнее всего при
   переключении языка: иврит и английский зеркальны, поэтому строки
   накладываются друг на друга ровно посередине экрана. Плюс при переходе
   с прокрученной страницы поверх нового первого экрана проступал старый
   середины страницы.

   Поэтому кадры НЕ ПЕРЕКРЫВАЮТСЯ: старый уходит полностью, и только потом
   приходит новый. Это и есть склейка через затемнение — приём из монтажа,
   а не эффект. Заодно снимает проблему целиком, а не маскирует её.
   ========================================================================== */

@view-transition { navigation: auto; }

/* Обязательно: пользователю, попросившему меньше движения, склейка не нужна.
   Глобальное правило с animation-duration: 0.001ms сюда НЕ достаёт —
   селектор * не выбирает псевдоэлементы перехода. Отключаем явно. */
@media (prefers-reduced-motion: reduce) {
  @view-transition { navigation: none; }
}

/* Фон, в который уходит кадр, — грунт документа (--c-ink на html),
   а не чёрный по умолчанию. Затемнение остаётся в палитре сайта. */

::view-transition-old(root),
::view-transition-new(root) {
  /* Браузер по умолчанию сводит кадры в режиме plus-lighter — при собственной
     анимации прозрачности это даёт засветку в середине перехода. */
  mix-blend-mode: normal;
}

/* Уход — чистое затухание, без движения: страница может быть прокручена,
   и любой сдвиг в этот момент читается как рывок. */
::view-transition-old(root) {
  animation: var(--dur-2) var(--ease-in) both vt-out;
}

/* Приход начинается ровно тогда, когда уход закончился: задержка равна
   длительности ухода. Между ними — такт чистого грунта. Это «монтажная
   склейка»; при перекрытии получилось бы растворение, а оно и давало кашу.
   Движение — по вертикали: горизонталь здесь была бы ошибкой, при
   переключении иврит↔английский «вправо» означает противоположные вещи
   на двух страницах одной анимации. */
::view-transition-new(root) {
  animation: var(--dur-3) var(--ease-out) var(--dur-2) both vt-in;
}

@keyframes vt-out { to   { opacity: 0; } }
@keyframes vt-in  { from { opacity: 0; transform: translateY(var(--travel)); } }

/* --- Шапка не участвует в затемнении ---------------------------------------
   Собственное имя выводит шапку из общего кадра: пока содержимое уходит
   в грунт и возвращается, шапка стоит на месте. Именно неподвижная шапка
   и создаёт ощущение приложения, а не набора страниц.
   Имя должно быть уникальным на странице — иначе переход молча отменяется.

   Подменяется шапка мгновенно и ровно в самой тёмной точке перехода:
   steps(1, end) держит старый кадр всю длительность ухода и снимает его
   в конце, новый — наоборот. Плавно сводить их нельзя: при смене языка
   меню зеркалится, и кросс-фейд давал два наложенных набора пунктов.
   При переходе внутри одного языка шапка не меняется вовсе, и подмена
   не видна — что и требуется. */
.site-header { view-transition-name: site-header; }

::view-transition-old(site-header) {
  animation: var(--dur-2) steps(1, end) both vt-swap-out;
}
::view-transition-new(site-header) {
  animation: var(--dur-2) steps(1, end) both vt-swap-in;
}
@keyframes vt-swap-out { from { opacity: 1; } to { opacity: 0; } }
@keyframes vt-swap-in  { from { opacity: 0; } to { opacity: 1; } }

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