/* ============================================================
   MOTION LAYER — стили.

   Всё, что здесь есть, читает две переменные, которые ставит js/motion.js:
   --lx и --ly, обе в [-1, 1]. Считает их только JS, рисует только CSS.
   Ни одного пересчёта тени в скрипте: box-shadow, который трогают из rAF,
   заставляет браузер перерисовывать слой каждый кадр.

   Палитра не расширяется: чёрный, белый, серый #8A8A8A и штатная
   волосяная линия. Ни одного нового цвета — это ограничение сайта.
   ============================================================ */

:root { --lx: 0; --ly: 0; }

/* Слои глубины двигает JS через transform. Здесь только обещание, что
   переход не будет дёрганым в момент, когда слой появляется или
   размораживается после скролла. */
[data-depth] { will-change: transform; }

/* «Меньше движения» — модуль не инициализируется, переменные остаются в
   нуле, и все выражения ниже честно схлопываются в статичную тень. */
.no-motion [data-depth] { will-change: auto; transform: none !important; }


/* ---------- СВЕТ ----------
   Тень уходит в сторону, ПРОТИВОПОЛОЖНУЮ наклону: наклонил вправо —
   источник света слева — тень вправо. Знак минус здесь не опечатка. */

.tile-image,
.gallery img,
[data-lit] {
  box-shadow:
    calc(var(--lx) * -10px) calc(var(--ly) * -10px + 8px)
    24px -16px rgba(0, 0, 0, .34);
}

/* Блик — один и тот же градиент на всех обложках, ездит вслед за светом.
   Держится на opacity, поэтому не стоит браузеру ни одного layout-прохода. */
.tile-image::before,
[data-lit]::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background: linear-gradient(
    calc(90deg + var(--lx) * 40deg),
    rgba(255, 255, 255, .12),
    rgba(255, 255, 255, 0) 38%);
  /* Приглушено намеренно. Первая версия (.18 белого при непрозрачности до .6)
     читалась на светлых обложках как белёсая дымка — на плитке YIGMA заметно
     с первого взгляда. Сайт держит строгую палитру, и блик здесь обязан
     ЧУВСТВОВАТЬСЯ при движении, а не ВИДЕТЬСЯ на статичном экране. */
  opacity: calc(.18 + var(--lx) * .12);
}
.no-motion .tile-image::before,
.no-motion [data-lit]::before { display: none; }


/* ---------- ШТОРКА ДО/ПОСЛЕ ----------
   Два кадра одного размера лежат стопкой; верхний обрезается по --cut,
   которую ведёт наклон или палец. Ширина реза 10…90% — края не схлопываются
   до нуля, иначе кажется, что один из кадров пропал. */

.curtain {
  --cut: 50%;
  position: relative;
  overflow: hidden;
  touch-action: pan-y;                  /* вертикальный скролл страницы остаётся за страницей */
}
.curtain img {
  display: block;
  width: 100%;
  height: auto;
}
.curtain img + img {
  position: absolute;
  inset: 0;
  height: 100%;
  object-fit: cover;
  clip-path: inset(0 0 0 var(--cut));
}
/* Линия реза — волосяная, из штатной палитры. */
.curtain::after {
  content: '';
  position: absolute;
  top: 0;
  bottom: 0;
  left: var(--cut);
  width: 1px;
  background: var(--paper);
  mix-blend-mode: difference;
  pointer-events: none;
}
.curtain__legend {
  display: flex;
  justify-content: space-between;
  margin-top: 10px;
  font-size: 13px;
  letter-spacing: -.02em;
  color: var(--muted);
}


/* ---------- КНОПКИ ----------
   Стиль текстовых кнопок сайта: без иконок, без цвета, без скруглений
   больше существующих. */

.tilt-btn,
.wire-btn {
  font: 500 13px/1 var(--font);
  letter-spacing: -.02em;
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--hairline);
  padding: 9px 14px;
  cursor: pointer;
}
.tilt-btn {
  position: fixed;
  right: 16px;
  bottom: 16px;
  z-index: 60;
  transition: opacity .35s ease;
}
.tilt-btn.gone { opacity: 0; pointer-events: none; }

.wire-btn { margin-top: 24px; }
.wire-btn[aria-pressed="true"] { background: var(--ink); color: var(--paper); }

/* Точка-подтверждение вместо кнопки: разрешение получено, движение живое. */
.tilt-dot {
  position: fixed;
  right: 22px;
  bottom: 22px;
  z-index: 60;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--ink);
  animation: tilt-dot 1.2s ease-in-out infinite;
}
@keyframes tilt-dot { 0%, 100% { opacity: .25; } 50% { opacity: 1; } }

/* Индикатор жеста и затухание смены роли убраны вместе с самим жестом
   (08.09.2026): наклон больше ничего не переключает. */

/* ---------- ЛОГОТИП БРЕНДА: ПОВОРОТ, А НЕ СДВИГ ----------
   Плоский сдвиг на 22px, как у обложек, для марки не годится: она не «слой
   позади», она предмет над страницей. Поэтому логотип не съезжает, а
   поворачивается к зрителю — perspective + rotateX/rotateY, как у иконок
   tvOS; тень при этом уходит в противоположную сторону и выдаёт высоту.

   Ни строчки JS: и поворот, и тень собраны из --lx/--ly, которые motion.js
   уже пишет в :root. Отсюда же и поведение по умолчанию — без модуля (или
   при prefers-reduced-motion, когда он не стартует вовсе) переменных нет,
   var(...) отдаёт 0, и трансформ становится единичным.

   `perspective` задана в пикселях, поэтому одно и то же значение искажает
   широкий элемент сильнее узкого — а марки тут от 97px (martin-belda) до
   384px (oxu.az). Отсюда `--sp`: по умолчанию 700px — спокойно для строчных
   логотипов-надписей высотой 64px, которым сильный поворот идёт как сбой;
   квадратная плитка вроде orange 160×160 — это карточка, ей ставится своё
   значение прямо в разметке (страница уже так задаёт `--sbg` у экранов).
   Величины подобраны сравнением пяти вариантов на живой марке, не на глаз.

   ВАЖНО: у этих элементов НЕТ data-depth. Цикл в motion.js пишет
   `el.style.transform` инлайном, а инлайн бьёт таблицу — поворот бы просто
   стёрло плоским сдвигом. Два механизма на один элемент не вешать. */
[data-spatial] {
  transform:
    perspective(var(--sp, 700px))
    rotateY(calc(var(--lx, 0) * 10deg))
    rotateX(calc(var(--ly, 0) * -10deg))
    translateZ(0);
  filter: drop-shadow(calc(var(--lx, 0) * -7px)
                      calc(var(--ly, 0) * -7px + 4px) 30px rgba(0, 0, 0, .13));
  will-change: transform;
}
/* Марка без силуэта тени не получает. drop-shadow рисует контур АЛЬФЫ файла,
   поэтому непрозрачный растр (у oxu.az и twelfy логотип — PNG без прозрачных
   пикселей, фон в файле совпадает с фоном страницы) отбрасывает тень своего
   прямоугольника, а не букв: вокруг знака появляется тёмная плашка. На почти
   чёрной oxu её не видно, на синей twelfy видно сразу. Поворот при этом
   работает — гасим только тень.

   Та же болезнь была у JOEL, но там причина другая и чинилась в файле:
   экспорт из Figma положил в SVG фон всей страницы (`<rect 1440×14031
   fill="white">`), знак стал белой плашкой 196×64 и отбрасывал её тень.
   Плашка лежала там с 27.06.2026 и на белой странице была не видна — её
   проявила именно тень. Правило: перед `data-spatial` проверять альфу файла,
   а не полагаться на то, что «на белом всё в порядке». */
[data-spatial][data-flat] { filter: none; }

/* Исключение, найденное замером, а не оставленное случайно: у BakuTV марка
   красится в белое собственным `filter: brightness(0) invert(1)` на
   селекторе тяжелее, поэтому тень туда не доезжает. Чинить нечем и незачем —
   чёрная тень на почти чёрном фоне всё равно не читается. Поворот работает,
   тени там нет намеренно. */

/* В каркасе предмет становится чертежом: ни объёма, ни тени. */
.wire [data-spatial] { transform: none; }

/* ---------- WIREFRAME ----------
   Сайт снимает отделку: картинки становятся серыми прямоугольниками,
   у блоков проступают рамки и подписи. Текст остаётся — это каркас,
   а не заглушка.

   Переход 250 мс и только по opacity/filter: перекраска сотен узлов
   через transition на color дала бы заметный провал кадров. */

html { transition: none; }
.wire, .wire body { background: var(--paper); }

/* Картинка становится не серым прямоугольником, а обводкой собственного
   содержимого: обесцветить → лапласиан (детектор границ) → инвертировать с
   усилением. На экранах интерфейса это настоящий вайрфрейм — видно колонки,
   кнопки, набор; на документальной фотографии выходит гравюра, и это честно:
   у фотографии структуры нет, придумывать её нечем.

   Ядро и наклон подобраны замером на трёх типах картинок (скрин приложения,
   репортажное фото, продуктовый пэкшот). Размытие перед лапласианом пробовал
   — оно только съедает тонкие линии интерфейса, поэтому stdDeviation нет.
   Проверено в Chromium и WebKit: рендер совпадает, Safari фильтр держит.
   Сам <filter> вставляет js/motion.js при первом включении каркаса. */
.wire img,
.wire video,
.wire iframe {
  filter: url(#wire-edge);
  outline: 1px dashed var(--muted);
  outline-offset: -1px;
}

/* Подпись размера поверх каждой картинки. Работает через обёртку —
   у самого <img> нет ::after, поэтому подпись вешаем на родителя,
   размеченного data-component. */
.wire [data-component] {
  outline: 1px dashed var(--hairline);
  outline-offset: 2px;
  position: relative;
}
.wire [data-component]::after {
  content: attr(data-component);
  position: absolute;
  top: 0;
  left: 0;
  z-index: 40;
  padding: 2px 5px;
  background: var(--ink);
  color: var(--paper);
  font: 500 10px/1.2 var(--font);
  letter-spacing: 0;
  text-transform: lowercase;
  pointer-events: none;
}

/* Цвет уходит целиком. Одних градиентов мало: у страниц есть собственные
   фоны — /ideas/orange/ целиком оранжевая, кейсы oxu, baku-tv и twelfy тёмные
   со светлым текстом. Гасили только background-image — и каркас Orange
   оставался оранжевым, то есть отделку не снимал вообще.

   Поэтому фон обнуляется у всех, а цвет текста принудительно уходит в чернила:
   иначе белый текст тёмных кейсов лёг бы на белое и исчез. Полутон при этом
   теряется — так и задумано, каркас одноцветный. Псевдоэлементы селектор `*`
   не ловит, поэтому подписи компонентов и размеров свою инверсию сохраняют. */
/* Токены темы возвращаются к базовым ДО того, как что-то красить. Тёмные
   кейсы (oxu, baku-tv, twelfy) переопределяют --ink на белый прямо на body —
   и `color: var(--ink)` честно давал белый текст на белом каркасе. Селектор
   html.wire body (0,1,2) намеренно тяжелее, чем body.theme-oxu (0,1,1):
   иначе тема перебивает сброс. */
html.wire body {
  --ink: #000;
  --paper: #FFF;
  --muted: #8A8A8A;
  --hairline: rgba(0, 0, 0, .12);
  --block-bg: #FFF;
  --surface: #FFF;
  --accent: #000;
  --muted-strong: rgba(0, 0, 0, .72);
  --muted-subtle: rgba(0, 0, 0, .35);
}
.wire * {
  background-image: none !important;
  background-color: transparent !important;
  color: var(--ink) !important;
  border-color: var(--hairline) !important;
  box-shadow: none !important;
  text-shadow: none !important;
}
/* Свои же органы управления из-под этой гребёнки возвращаем поимённо. */
.wire .wire-btn[aria-pressed="true"] { background: var(--ink) !important; color: var(--paper) !important; }
.wire .tilt-dot { background: var(--ink) !important; }
.wire .tile-image::before,
.wire [data-lit]::before { display: none; }
/* Натуральный размер файла — в правом нижнем углу его бокса. Ставит
   подпись js/motion.js, и только там, где бокс родителя равен боксу картинки:
   иначе цифра относилась бы не к тому.

   ПОРЯДОК ЗДЕСЬ ЗНАЧИМ. Подпись и блик — один и тот же ::before на одном и
   том же .tile-image; выше блик гасится через display:none, и первая версия
   этого блока стояла ВЫШЕ той строки — подписи проставлялись в DOM, но на
   экране их не было ни одной. Поэтому правило живёт после гашения и само
   возвращает display, а заодно снимает inset:0 блика: без этого текст уехал
   бы в левый верхний угол растянутого на весь бокс прямоугольника. */
.wire [data-size] { position: relative; }
.wire [data-size]::before {
  content: attr(data-size);
  display: block;
  position: absolute;
  inset: auto 0 0 auto;
  z-index: 40;
  width: auto;
  height: auto;
  padding: 2px 5px;
  background: var(--ink);
  color: var(--paper);
  font: 500 10px/1.2 var(--font);
  letter-spacing: 0;
  opacity: 1;
  pointer-events: none;
}

.wire [data-depth] { transform: none !important; }
.wire .tile-image,
.wire .gallery img,
.wire [data-lit] { box-shadow: none; }

.wire, .wire * { transition: filter .25s ease, opacity .25s ease; }

/* Сетка и отступы видимыми линиями — но только на крупных контейнерах,
   иначе страница превращается в кашу из рамок. */
.wire .tiles,
.wire .gallery,
.wire .list { outline: 1px dashed var(--hairline); }


/* ---------- ТАЧ ----------
   На пальце hover залипает, поэтому подсветку блика на тач-экранах не
   включаем вовсе — правило из общей таблицы, повторённое здесь осознанно. */
@media (hover: none) {
  .tile-image::before,
  [data-lit]::before { opacity: .3; }
}
