/* normalize */
*,
*::before,
*::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}
img,
video,
svg {
  display: block;
  max-width: 100%;
}
input,
button,
textarea,
select {
  font: inherit;
}
button {
  background: none;
  border: 0;
  color: inherit;
  cursor: pointer;
}
p,
h1,
h2,
h3,
h4,
h5,
h6 {
  overflow-wrap: break-word;
}
/* ------------------------- Scaling System by Osmo [https://osmo.supply/] -------------------------  */
/* Desktop */
:root {
  --size-unit: 16; /* body font-size in design - no px */
  --size-container-ideal: 1440; /* screen-size in design - no px */
  --size-container-min: 992px;
  --size-container-max: 1920px;
  --size-container: clamp(var(--size-container-min), 100vw, var(--size-container-max));
  --size-font: calc(var(--size-container) / (var(--size-container-ideal) / var(--size-unit)));
  --cut: 10px;
}
body {
  font-size: var(--size-font);
}
.container {
  max-width: var(--size-container);
  margin: 0 auto;
}
.container.medium {
  max-width: calc(var(--size-container) * 0.85);
}
.container.small {
  max-width: calc(var(--size-container) * 0.7);
}
/* Tablet */
@media screen and (max-width: 991px) {
  :root {
    --size-container-ideal: 834; /* screen-size in design - no px */
    --size-container-min: 768px;
    --size-container-max: 991px;
  }
}
/* Mobile Landscape */
@media screen and (max-width: 767px) {
  :root {
    --size-container-ideal: 550; /* screen-size in design - no px */
    --size-container-min: 480px;
    --size-container-max: 767px;
  }
}
/* Mobile Portrait */
@media screen and (max-width: 479px) {
  :root {
    --size-container-ideal: 390; /* screen-size in design - no px */
    --size-container-min: 320px;
    --size-container-max: 479px;
  }
}
/* Прогрессивный blur у нижней границы навбара (только в hero theme,
     где nav transparent). 5 слоёв backdrop-filter с возрастающей силой
     blur'а и mask linear-gradient, который раскрывает каждый слой ближе
     к нижнему краю — суммарно даёт мягкий fade от чёткого верха к сильно
     размытому низу. Saturate убран (переутяжелял эффект). */
.nav_blur {
  pointer-events: none;
  isolation: isolate;
  contain: paint;
  width: 100%;
  height: 12em;
  transform-style: preserve-3d;
  position: absolute;
  top: 0;
  left: 0;
  overflow: hidden;
  /* scaleY(-1) переворачивает прогрессию: оригинальный mask gradient
       копит сильный blur у НИЖНЕЙ грани элемента (для bottom-fixed snippet);
       нам в top-nav нужно сильный blur у ВЕРХА (там логотип/пункты), fade
       к нижней границе nav. Flip = эквивалент инверсии всех 5 gradients. */
  transform: scaleY(-1) translateZ(0);
  z-index: 90;
}
.nav_blur_layer {
  position: absolute;
  inset: 0;
}
.nav_blur_layer.is--1 {
  -webkit-backdrop-filter: blur(0.09375em);
  backdrop-filter: blur(0.09375em);
  -webkit-mask: linear-gradient(#0000 50%, #000 62.5% 75%, #0000 87.5%);
  mask: linear-gradient(#0000 50%, #000 62.5% 75%, #0000 87.5%);
}
.nav_blur_layer.is--2 {
  -webkit-backdrop-filter: blur(0.1875em);
  backdrop-filter: blur(0.1875em);
  -webkit-mask: linear-gradient(#0000 62.5%, #000 75% 87.5%, #0000 100%);
  mask: linear-gradient(#0000 62.5%, #000 75% 87.5%, #0000 100%);
}
.nav_blur_layer.is--3 {
  -webkit-backdrop-filter: blur(0.375em);
  backdrop-filter: blur(0.375em);
  -webkit-mask: linear-gradient(#0000 75%, #000 87.5% 100%);
  mask: linear-gradient(#0000 75%, #000 87.5% 100%);
}
.nav_blur_layer.is--4 {
  -webkit-backdrop-filter: blur(0.75em);
  backdrop-filter: blur(0.75em);
  -webkit-mask: linear-gradient(#0000 82%, #000 92% 100%);
  mask: linear-gradient(#0000 82%, #000 92% 100%);
}
.nav_blur_layer.is--5 {
  -webkit-backdrop-filter: blur(1.5em);
  backdrop-filter: blur(1.5em);
  -webkit-mask: linear-gradient(#0000 88%, #000 100%);
  mask: linear-gradient(#0000 88%, #000 100%);
}
/* Эффект активен только в hero state (nav transparent). На light/dark
     навбар имеет solid фон — blur layers не нужны и убираются из render. */
.nav:not([data-theme-nav="hero"]) .nav_blur {
  display: none;
}
/* Mobile menu open на hero: прозрачный навбар → dark (чтобы открытое меню
     не просвечивало сверху). На .nav-container уже есть transition
     background-color → close плавно возвращает transparent. Блюр прячем —
     бар теперь непрозрачный, blur не нужен. Специфичность (0,4,0) бьёт
     базовое hero-правило transparent (0,3,0). */
.nav[data-theme-nav="hero"][data-menu-open] .nav-container {
  background-color: var(--alias-grey-800);
}
.nav[data-menu-open] .nav_blur {
  display: none;
}
/* ══════════════════════════════════════════════════════════════
   Nav theme switcher — attribute-driven (data-theme-nav)
   Tweak speed/easing below in --nav-theme-transition
   ══════════════════════════════════════════════════════════════ */
:root {
  --nav-theme-transition: 0.15s ease;
}
/* Color cascade — flows from .nav-container down */
.nav .nav-container,
.nav .nav_wrap,
.nav .nav_items-wrap,
.nav .nav_item,
.nav .nav_item-text,
.nav .nav_contact,
.nav .nav_logo-container,
.nav .nav_logo-svg {
  color: inherit;
}
.nav_logo-svg {
  display: block;
  height: 100%;
  width: auto;
}
/* Clear inner pill so .nav-container's bg shows full-width */
.nav .nav_wrap {
  background-color: transparent !important;
  border-color: transparent !important;
}
/* ─── Hero — transparent bg + subtle visible borders + native backdrop ─── */
.nav[data-theme-nav="hero"] .nav-container {
  background-color: transparent;
  color: var(--alias-grey-100);
}
.nav[data-theme-nav="hero"],
.nav[data-theme-nav="hero"] .nav-container,
.nav[data-theme-nav="hero"] .nav_logo-container,
.nav[data-theme-nav="hero"] .nav_contact,
.nav[data-theme-nav="hero"] .nav_item.is-border,
.nav[data-theme-nav="hero"] .nav_menu-toggle {
  border-color: transparent; /* hero — все бордеры скрыты для чистого вида поверх hero-видео */
}
/* ─── Light — alias tokens (mode-independent) ─── */
.nav[data-theme-nav="light"] .nav-container {
  background-color: var(--alias-grey-100);
  color: var(--alias-grey-900);
}
.nav[data-theme-nav="light"],
.nav[data-theme-nav="light"] .nav-container,
.nav[data-theme-nav="light"] .nav_logo-container,
.nav[data-theme-nav="light"] .nav_contact,
.nav[data-theme-nav="light"] .nav_item.is-border,
.nav[data-theme-nav="light"] .nav_menu-toggle {
  border-color: var(--alias-grey-200);
}
.nav[data-theme-nav="light"] .nav_backdrop {
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}
/* ─── Dark — alias tokens (mode-independent) ─── */
.nav[data-theme-nav="dark"] .nav-container {
  background-color: var(--alias-grey-800);
  color: var(--alias-grey-100);
}
.nav[data-theme-nav="dark"],
.nav[data-theme-nav="dark"] .nav-container,
.nav[data-theme-nav="dark"] .nav_logo-container,
.nav[data-theme-nav="dark"] .nav_contact,
.nav[data-theme-nav="dark"] .nav_item.is-border,
.nav[data-theme-nav="dark"] .nav_menu-toggle {
  border-color: var(--alias-grey-700);
}
.nav[data-theme-nav="dark"] .nav_backdrop {
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}
/* ─── Transitions ─── */
.nav,
.nav .nav-container,
.nav .nav_backdrop,
.nav .nav_logo-container,
.nav .nav_contact,
.nav .nav_item.is-border {
  transition:
    background-color var(--nav-theme-transition),
    border-color var(--nav-theme-transition),
    color var(--nav-theme-transition),
    backdrop-filter var(--nav-theme-transition);
}
/* Меню открыто → прячем статичный border-bottom навбара: разделитель бар↔меню
     теперь живёт на самой панели (.nav_menu border-top, styles.css) и едет с ней.
     Иначе статичная линия мигает под движущимся слоем панели. Стоит ПОСЛЕ
     theme-групп (равная спец. 0,2,0 → решает порядок); border-color transition
     выше делает фейд плавным. */
.nav[data-menu-open] {
  border-bottom-color: transparent;
}
/* ─── Hover / active states on .nav_item per theme ─── */
.nav[data-theme-nav="light"] .nav_item:hover {
  background-color: var(--alias-grey-150);
}
.nav[data-theme-nav="light"] .nav_item:active {
  background-color: var(--alias-grey-200);
}
.nav[data-theme-nav="dark"] .nav_item:active {
  background-color: var(--alias-grey-700);
}
/* ─── Active nav item — подсветка фоном + боковые бордеры; без ховера.
     Фон = цвет hover-состояния пункта (light: alias/150, dark: alias/750):
     активный пункт всегда выглядит «подсвеченным», ховер на нём не срабатывает. */
.nav .nav_item.is-active {
  border-left: 1px solid;
  border-right: 1px solid;
}
.nav[data-theme-nav="hero"] .nav_item.is-active,
.nav[data-theme-nav="hero"] .nav_item.is-active:hover {
  border-color: transparent;
  background-color: var(--alias-grey-750);
}
.nav[data-theme-nav="dark"] .nav_item.is-active,
.nav[data-theme-nav="dark"] .nav_item.is-active:hover {
  border-color: var(--alias-grey-700);
  background-color: var(--alias-grey-750);
}
.nav[data-theme-nav="light"] .nav_item.is-active,
.nav[data-theme-nav="light"] .nav_item.is-active:hover {
  border-color: var(--alias-grey-200);
  background-color: var(--alias-grey-150);
}
/* «Кейсы» (is-border) уже имеет свой правый бордер; у «О Компании» левый
     бордер — это правый бордер «Кейсы». В этих двух случаях левый бордер
     активного пункта не добавляем — у остальных он остаётся (и слева, и справа). */
.nav .nav_item.is-active.is-border,
.nav .nav_item.is-border + .nav_item.is-active {
  border-left-style: none;
}
/* ––––– Pattern ––––– */
[data-pattern="plus"] {
  position: relative;
  isolation: isolate;
}
[data-pattern="plus"]::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background-color: var(--pattern-plus-color);
  -webkit-mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path d='M4 7h8v1H4zM7 4h1v8H7z' fill='black'/></svg>");
  mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path d='M4 7h8v1H4zM7 4h1v8H7z' fill='black'/></svg>");
  -webkit-mask-repeat: repeat;
  mask-repeat: repeat;
  -webkit-mask-size: 16px 16px;
  mask-size: 16px 16px;
}
[data-pattern="plus"] > * {
  position: relative;
  z-index: 1;
}
/* Цвет через существующие data-theme-section секций */
[data-theme-section="light"] [data-pattern="plus"]::before {
  --pattern-plus-color: var(--alias-grey-200);
}
[data-theme-section="dark"] [data-pattern="plus"]::before {
  --pattern-plus-color: var(--alias-grey-700);
}
/* Grid-item min-width fix: по дефолту grid item имеет min-width: auto
     (= min-content / natural size). Если внутри cell большая картинка,
     её natural width «толкает» cell — Grid template 1fr 1fr теряет
     равномерность. Override на 0 чтобы 1fr cells оставались equal. */
.content-grid > .grid-col {
  min-width: 0;
  min-height: 0;
}

/* Универсальный фолбек фона секции по data-theme-section.
     В Webflow-экспорте фон навешан на каждый .section_* класс отдельно —
     для новых секций без зарегистрированного класса фон тянем напрямую
     от data-theme-section. Существующие .section_* правила имеют выше
     специфичность и продолжают работать. */
section[data-theme-section="light"],
section[data-theme-section="dark"] {
  background-color: var(--mapped-surface-default);
}
/* Product slider — Osmo Parallax Image Gallery with Thumbnails.
     Full-width, парallax img внутри slide (xPercent +/- 50% при transition),
     thumbnails внизу, drag/wheel/touch навигация через GSAP Observer.
     Логика — initImageSlider() в main.js. */
.section_product-slider {
  width: 100%;
  border-top: 1px solid var(--mapped-border-default);
}
.img-slider {
  grid-column-gap: 1rem;
  grid-row-gap: 1rem;
  border-radius: 0.5em;
  justify-content: center;
  align-items: flex-end;
  width: 100%;
  height: 85vh;
  display: flex;
  position: relative;
}
.img-slider__list {
  grid-template-rows: 100%;
  grid-template-columns: 100%;
  place-items: center;
  width: 100%;
  height: 100%;
  display: grid;
  overflow: hidden;
}
.img-slide {
  opacity: 0;
  pointer-events: none;
  will-change: transform, opacity;
  grid-area: 1 / 1 / -1 / -1;
  place-items: center;
  width: 100%;
  height: 100%;
  display: grid;
  position: relative;
  overflow: hidden;
}
.img-slide.is--current {
  opacity: 1;
  pointer-events: auto;
}
.img-slide__inner {
  object-fit: cover;
  will-change: transform;
  width: 100%;
  height: 100%;
  position: absolute;
}
.img-slider__nav {
  z-index: 2;
  grid-column-gap: 0.5rem;
  grid-row-gap: 0.5rem;
  pointer-events: none;
  flex-flow: wrap;
  justify-content: center;
  align-items: center;
  max-width: 95vw;
  display: flex;
  position: absolute;
  bottom: 2rem;
}
.img-slider__thumb {
  aspect-ratio: 1.5;
  pointer-events: auto;
  cursor: pointer;
  border: 2px solid rgba(255, 255, 255, 0.2);
  border-radius: 0.3125rem;
  width: 7rem;
  transition: border-color 0.2s;
  position: relative;
  overflow: hidden;
}
.img-slider__thumb:hover {
  border-color: rgba(255, 255, 255, 0.4);
}
.img-slider__thumb.is--current {
  border-color: var(--mapped-surface-action);
}
.slider-thumb__img {
  object-fit: cover;
  width: 100%;
  height: 100%;
}
@media screen and (max-width: 991px) {
  .img-slider__list {
    width: 100%;
  }
  .img-slider__thumb {
    flex: none;
  }
}
@media screen and (max-width: 767px) {
  .img-slider__nav {
    flex-flow: wrap;
  }
  .img-slider__thumb {
    border-radius: 0.25rem;
    width: 5rem;
  }
}
@media screen and (max-width: 479px) {
  .img-slider__thumb {
    width: 4.5rem;
  }
  /* Слайдер продукта ниже на мобилке (десктоп 85vh). Override здесь
       (base.css грузится последним — иначе styles.css проиграет .img-slider). */
  .img-slider {
    height: 30em;
  }
}

/* Scroll-reveal для секций: элементы появляются opacity-fade'ом
     (без сдвига) при попадании секции в viewport. Stagger в reading
     order через transition-delay. Управляется initSectionReveal()
     в main.js — навешивает .section-reveal на элементы (initial state),
     потом .is-revealed (final state). */
.section-reveal {
  opacity: 0;
  transition: opacity 0.5s cubic-bezier(0.33, 1, 0.68, 1);
  will-change: opacity;
}
.section-reveal.is-revealed {
  opacity: 1;
}
/* Highlight-marker text reveal — Osmo snippet. На заголовок навешивается
     SplitText по строкам, каждая строка получает absolute-bar (фирменный
     оранжевый), при попадании в viewport bar анимированно «уезжает» вправо
     (scaleX → 0). data-highlight-marker-reveal автоматически проставляется
     JS на все h1.heading_h1 / h2.heading_h2 (см. initHighlightMarkerTextReveal).
     FOUC placeholder через ::before — в Base.astro inline style. */
[data-highlight-marker-reveal] .highlight-marker-line {
  width: auto;
  display: inline-block !important;
  margin: -0.055em 0;
}
.highlight-marker-bar {
  position: absolute;
  inset: -0.055em 0;
  z-index: 1;
  pointer-events: none;
}
/* Click-to-zoom (lightbox для image-графиков). Источник — Osmo-snippet,
     адаптация: только cursor + lightbox overlay (BEM-классы .click-zoom__visual
     и .click-zoom__img НЕ используются, у нас свои image-контейнеры). */
img[data-click-zoom],
[data-click-zoom] img,
[data-click-zoom-article] img {
  cursor: zoom-in;
}
[data-click-zoom-lightbox] > img {
  display: block;
  max-width: 100%;
  max-height: 100%;
  user-select: none;
  -webkit-user-drag: none;
  border-radius: 0.5em;
}
.click-zoom__lightbox {
  z-index: 100;
  cursor: zoom-out;
  background-color: rgba(0, 0, 0, 0.9);
  justify-content: center;
  align-items: center;
  padding: 3em;
  display: none;
  position: fixed;
  inset: 0;
}
:root {
  --nav-sweep-ease: cubic-bezier(0.675, 0.15, 0.1, 1);
  --nav-sweep-duration-in: 0.425s;
  --nav-sweep-duration-out: 0.375s;
}
/* Positioning context + клип-зона */
.nav_item,
.nav_contact {
  position: relative;
  isolation: isolate; /* чтобы z-index:-1 не уехал за карточку */
  overflow: hidden; /* sweep не вылезает за бордеры */
}
/* Отключаем существующий direct bg на hover — заменим на анимированный sweep */
.nav_item:hover:not(.is-active),
.nav_contact:hover {
  background-color: transparent !important;
}
/* Sweep-слой */
.nav_item::before,
.nav_contact::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background-color: var(--nav-sweep-fill);
  clip-path: polygon(0% 50%, 100% 50%, 100% 50%, 0% 50%);
  transition: clip-path var(--nav-sweep-duration-out) var(--nav-sweep-ease);
}
@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  .nav_item:hover:not(.is-active)::before,
  .nav_contact:hover::before {
    clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%);
    transition: clip-path var(--nav-sweep-duration-in) 0.05s var(--nav-sweep-ease);
  }
}
/* Per-theme заливки — каждая ветка задаёт fill цвет, дальше всё работает каскадно */
.nav[data-theme-nav="hero"] {
  --nav-sweep-fill: rgba(255, 255, 255, 0.1);
} /* ← подкорректируй под текущий hero hover bg */
.nav[data-theme-nav="light"] {
  --nav-sweep-fill: var(--alias-grey-150);
}
.nav[data-theme-nav="dark"] {
  --nav-sweep-fill: var(--alias-grey-700);
}
/* .nav_contact может хотеть свой цвет — добавляем отдельную перекидку если нужно */
.nav[data-theme-nav="light"] .nav_contact::before {
  background-color: var(--mapped-surface-action); /* ← подставь текущий */
}
.nav[data-theme-nav="dark"] .nav_contact::before {
  background-color: var(--mapped-surface-action); /* ← подставь текущий */
}
.nav[data-theme-nav="hero"] .nav_contact::before {
  background-color: var(--mapped-surface-action); /* ← подставь текущий */
}
/* ══════════════════════════════════════════════════════════════
   Globe section — attribute-based state + Mapbox overrides.
   Base positioning and visual styling comes from Webflow Designer
   (already present in shared.css). This block covers only what
   Designer can't express: [data-globe-*] attribute selectors and
   .mapboxgl-* internal controls.
   ══════════════════════════════════════════════════════════════ */
/* Globe section sizes to viewport minus fixed nav (desktop only;
   mobile keeps Designer's column-stack behavior) */
@media screen and (min-width: 992px) {
  .globe-wrap {
    height: calc(100vh - var(--cctp-nav-height, 0px));
  }
}
/* Info panel custom property */
[data-globe-init] {
  --globe-info-width: 30em;
}
/* Info panel slide-out transition on collapse */
[data-globe-info] {
  opacity: 1;
  visibility: visible;
  transform: translate(0em, 0px);
  transition: all 0.65s cubic-bezier(0.625, 0.05, 0, 1);
}
[data-globe-init][data-collapsed="true"] [data-globe-info] {
  opacity: 0;
  visibility: hidden;
  transform: translate(4em, 0px);
}
/* Reopen tab — visible only when panel is collapsed (also in Designer preview) */
[data-globe-reopen] {
  opacity: 0;
  visibility: hidden;
  transform: translate(2em, -50%);
  transition: all 0.4s cubic-bezier(0.625, 0.05, 0, 1);
}
[data-globe-init][data-collapsed="true"] [data-globe-reopen],
.wf-design-mode [data-globe-reopen] {
  opacity: 1;
  visibility: visible;
  transform: translate(0em, -50%);
}
/* Nav shifts right when panel collapses */
[data-globe-nav] {
  transition: all 0.65s cubic-bezier(0.625, 0.05, 0, 1);
}
[data-globe-init][data-collapsed="true"] [data-globe-nav] {
  right: 1em;
}
/* Active marker outline */
[data-globe-marker][data-active="true"] {
  outline: 1px solid var(--alias-grey-100);
  outline-offset: 0.5em;
}
/* Hide marker template once script clones it */
[data-globe-init="initialized"] [data-globe-marker-template] {
  display: none;
}
/* Mapbox built-in controls → CCTP dark palette */
.mapboxgl-ctrl-group {
  background: var(--alias-grey-800) !important;
  border: 1px solid var(--alias-grey-700) !important;
}
.mapboxgl-ctrl-group button {
  background: var(--alias-grey-800) !important;
}
.mapboxgl-ctrl button .mapboxgl-ctrl-icon {
  filter: invert(1);
}
.mapboxgl-ctrl-group button + button {
  border-top: 1px solid var(--alias-grey-700);
}
.mapboxgl-ctrl-logo,
.mapboxgl-ctrl-attrib {
  display: none !important;
}

.product-card_img-holder video {
  object-fit: contain;
}
/* Mobile: video заполняет holder (per user). base.css грузится последним —
   override должен быть здесь, иначе desktop правило победит styles.css. */
@media screen and (max-width: 479px) {
  .product-card_img-holder video {
    object-fit: cover;
  }
}

:root {
  --button-color: var(--mapped-text-on-action);
  --button-color-background: var(--mapped-surface-action);
  --button-hover-color: var(--mapped-text-on-action);
  --button-hover-color-background: var(--mapped-surface-action-hover);
  --button-color-focus: #000;
  --button-padding: 2em 3.1em 2em 2.5em;
  --button-padding-big: 3em;
  --button-corner: 0.8em;
  --button-click-scale: 0.955 0.925;
  --button-default-text-scale: 1.2;
  --button-hover-text-scale: 0.85;
  --button-ease-hover: cubic-bezier(0.675, 0.15, 0.1, 1);
  --button-ease-click: cubic-bezier(0.4, 0, 0.2, 1);
  --button-ease-focus: cubic-bezier(0.32, 0.72, 0, 1);
  --button-icon-size: 1.2em; /* высота иконки */
  --button-icon-aspect: 11 / 10; /* пропорции твоей SVG */
  --button-icon-gap: 0.5em;
}
/* Click scale — applies to whole button */
.button {
  transition: scale 0.15s var(--button-ease-click);
  -webkit-tap-highlight-color: transparent;
}
.button:active {
  scale: var(--button-click-scale);
}
/* Focus ring — inset shadow that follows octagonal clip */
.button::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 3;
  grid-area: 1 / 1;
  box-shadow: none;
  transition: box-shadow 0.3s var(--button-ease-focus);
  clip-path: polygon(
    var(--button-corner) 0%,
    calc(100% - var(--button-corner)) 0%,
    100% var(--button-corner),
    100% calc(100% - var(--button-corner)),
    calc(100% - var(--button-corner)) 100%,
    var(--button-corner) 100%,
    0% calc(100% - var(--button-corner)),
    0% var(--button-corner)
  );
}
.button:focus-visible::after {
  box-shadow: inset 0 0 0 0.125em var(--button-color-focus);
}
/* Octagonal corners on the background layer */
.button__bg {
  clip-path: polygon(
    var(--button-corner) 0%,
    calc(100% - var(--button-corner)) 0%,
    100% var(--button-corner),
    100% calc(100% - var(--button-corner)),
    calc(100% - var(--button-corner)) 100%,
    var(--button-corner) 100%,
    0% calc(100% - var(--button-corner)),
    0% var(--button-corner)
  );
}
/* Hover sweep — animates clip-path from horizontal stripe to full */
.button__hover {
  clip-path: polygon(0% 50%, 100% 50%, 100% 50%, 0% 50%);
  transition: clip-path 0.375s var(--button-ease-hover);
}
/* Text scale + fade transitions */
.button__hover .button__text {
  scale: var(--button-hover-text-scale);
  opacity: 0;
  transition:
    scale 0.35s var(--button-ease-hover),
    opacity 0.15s 0.05s ease-out;
}
.button__default .button__text {
  scale: 1;
  opacity: 1;
  transition:
    scale 0.35s var(--button-ease-hover),
    opacity 0.15s 0.1s ease-out;
}
/* Hover state animations */
@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  .button:is(:hover, :focus-visible) .button__hover,
  [data-hover]:is(:hover, :focus-visible) .button .button__hover {
    clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%);
    transition: clip-path 0.425s 0.05s var(--button-ease-hover);
  }
  .button:is(:hover, :focus-visible) .button__hover .button__text,
  [data-hover]:is(:hover, :focus-visible) .button .button__hover .button__text {
    scale: 1;
    opacity: 1;
    transition: scale 0.35s 0.1s var(--button-ease-hover);
    /* opacity 0.15s 0.15s ease-out; */
  }
  .button:is(:hover, :focus-visible) .button__default .button__text,
  [data-hover]:is(:hover, :focus-visible) .button .button__default .button__text {
    scale: var(--button-default-text-scale);
    opacity: 0;
    transition: scale 0.35s 0.05s var(--button-ease-hover);
    /* opacity 0.15s ease-out; */
  }
}
/* ────────────────────────────────────────────────────────────────
   Case card link variant — small faint link in bottom-right corner.
   Reuses .button markup with scoped --button-* overrides.
   Каждая переменная редактируется отдельно — не влияет на основные.
   ──────────────────────────────────────────────────────────────── */
.case-card_link.button {
  --button-color: var(--mapped-text-body-inverse);
  --button-color-background: transparent;
  --button-hover-color: var(--mapped-text-on-action);
  --button-hover-color-background: var(--mapped-surface-action);
  --button-corner: 0.35em;
  --button-padding: 1em 2em;
  --button-click-scale: 0.96;
}
/* Disable inherited ::before icon (link is text-only) */
.case-card_link.button .button__text::before {
  content: none;
  display: none;
}
/* Bg widths 100% (override base calc(100% - 1/2px)) */
.case-card_link.button .button__bg,
.case-card_link.button .button__bg.is--default {
  width: 100%;
  height: 100%;
}
/* Default text faint at rest, readable on card hover */
.case-card_link.button .button__default .button__text {
  opacity: 0.35;
  transition: opacity 0.3s ease;
}
@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  .case-card_item:hover .case-card_link.button .button__default .button__text,
  [data-hover]:hover .case-card_link.button .button__default .button__text {
    opacity: 1;
  }
}
/* Card-wide click → button scales (CSS-only press feedback) */
.case-card_item:active .case-card_link.button {
  scale: var(--button-click-scale);
}
/* ────────────────────────────────────────────────────────────────
   Case card hover anim — pattern color + mask FOUC prevention
   ──────────────────────────────────────────────────────────────── */
.case-card_item[data-pattern="plus"]::before {
  opacity: 0;
  transition: opacity 0.3s ease;
  --pattern-plus-color: var(--alias-grey-150);
}
.case-card_item[data-pattern="plus"]:hover::before {
  opacity: 1;
}
.case-card_item[data-pattern="plus"] > .case-card_description,
.case-card_item[data-pattern="plus"] > .case-card_date,
.case-card_item[data-pattern="plus"] > .case-card_img {
  position: absolute;
}
.case-card_img {
  overflow: hidden;
}
.case-card_img img {
  display: block;
  visibility: hidden;
  will-change: transform;
}
.case-card_date {
  overflow: hidden;
}
.case-card_date .text-block {
  display: inline-block;
  visibility: hidden;
  will-change: transform;
}
.case-card_description .text_size-small {
  visibility: hidden;
}
.case-card_item {
  cursor: pointer;
}

/* ──── Mobile case-card — упрощённая архитектура ─────────────────────
     На мобилке: НЕ overlay с absolute, а простой flow column. Все
     hover-reveal элементы (img/date/description) видны сразу — GSAP reveal
     отключён в initCaseCardHover на ≤479px. Override ЗДЕСЬ (base.css), т.к.
     base.css грузится последним: дубликаты absolute + visibility:hidden
     живут выше в этом же файле и перебили бы styles.css/animation.css. */
@media screen and (max-width: 479px) {
  .case-card_item {
    aspect-ratio: auto;
    flex-direction: column;
    align-items: flex-start;
    justify-content: flex-start;
  }
  .case-card_item[data-pattern="plus"] > .case-card_description,
  .case-card_item[data-pattern="plus"] > .case-card_date,
  .case-card_item[data-pattern="plus"] > .case-card_img,
  .case-card_title,
  .case-card_link.button {
    position: static;
  }
  .case-card_img img,
  .case-card_date .text-block {
    visibility: visible;
  }
  /* Результат-текст (.text_size-small) скрыт на мобилке (per user).
       Скоуп к .case-card_description — этот же класс есть в footer. */
  .case-card_description .text_size-small {
    display: none;
  }
  /* case-card_link выглядит сразу как hovered (на мобилке hover нет):
       раскрываем .button__hover слой (clip-path stripe→full → оранжевый
       bg is--hover + hover-текст), default-текст прячем. Воспроизводит
       полный hover state, а не просто readable faint-текст. */
  .case-card_link.button .button__hover {
    clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%);
  }
  .case-card_link.button .button__hover .button__text {
    scale: 1;
    opacity: 1;
  }
  .case-card_link.button .button__default .button__text {
    opacity: 0;
  }
  /* Изображение компактнее */
  .case-card_img {
    max-width: 5em;
  }
  /* Порядок элементов в flow column (per user): description → img → title
       → link. date скрыта (display:none). Link явно order:4 — иначе при
       unset (order:0) уехал бы в начало. */
  .case-card_description {
    order: 1;
  }
  .case-card_img {
    order: 2;
  }
  .case-card_title {
    order: 3;
  }
  .case-card_link {
    order: 4;
  }
}

/* ──── Barba page transition overlay ─────────────────────────────────
     Структура: .transition (fixed wrap, pointer-events:none) → .transition__panel
     (оранжевая плашка с plus-крестиками #BA431F) → .transition__label
     (название страницы между двумя SVG-брекетами из Eyebrow).

     Изначально панель находится за нижним краем экрана (top:100%); GSAP
     timeline'ы в main.js → leave: панель уходит вверх до yPercent:-100
     (закрывает viewport, label виден); enter: продолжает до -200 (улетает
     над верхним краем, раскрывая новую страницу). Panel reset → autoAlpha:0
     после каждого перехода. */
.transition {
  z-index: 100;
  pointer-events: none;
  position: fixed;
  inset: 0;
  overflow: clip;
}
.transition__panel {
  position: absolute;
  top: 100%;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: var(--mapped-surface-dark);
  /* Plus-крестики тем же цветом что и на остальных dark-секциях
       (см. [data-pattern='plus']::before выше — --alias-grey-700 #2f3032). */
  --pattern-plus-color: var(--alias-grey-700);
  display: flex;
  align-items: center;
  justify-content: center;
}
.transition__label {
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
  color: var(--alias-grey-100);
  font-family: var(--brand-font-family);
  font-size: 3em;
  line-height: 1;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  font-weight: var(--brand-weight-medium, 500);
}
.transition__bracket {
  display: inline-flex;
  width: 0.3em;
  height: 1em;
  color: var(--alias-grey-100);
}
.transition__bracket.is-right {
  transform: scaleX(-1);
}
.transition__bracket svg {
  width: 100%;
  height: 100%;
}

/* Mobile: плашка barba — текст 3em слишком крупный, длинные лейблы
     (ИНЖИНИРИНГ / О КОМПАНИИ) не влазят по ширине. Уменьшаем font-size
     лейбла — скобочки (width:0.3em / height:1em) наследуют font-size и
     масштабируются пропорционально автоматически. */
@media screen and (max-width: 767px) {
  .transition__label {
    font-size: 2em;
  }
}

/* Reduced motion → панель не показывается, переход через autoAlpha. */
@media (prefers-reduced-motion: reduce) {
  .transition__panel {
    display: none;
  }
}

/* Footer всегда в DOM (для Barba shell-consistency), но скрыт на страницах
     с noFooter (=/contacts/). Селектор intrinsic к контейнеру — никаких
     body-attr swap'ов, работает SSR и после Barba transition одинаково. */
.page[data-no-footer="true"] .footer-wrap {
  display: none;
}

/* Pointer-events lock на body во время Barba transition. Барба сама
     queue-ит navigation через preventRunning, но UI-клики (hover, buttons)
     могут ломать DOM state mid-transition. body[data-barba-running] флаг
     ставится в beforeLeave / снимается в afterEnter. */
body[data-barba-running="true"] {
  pointer-events: none;
}

/* ──── Preloader (главная, cold load) ────────────────────────────────
     2×2 grid:
       Row 1: [orange — лого+статус] [pattern fill — % progress]
       Row 2: [card-left shape illu]  [card-right shape-service illu]
     Анимация: bottom illus играют forward/reverse → pattern заполняется →
     pattern push orange влево → bottom cards slide DOWN (stagger) →
     pattern block slide DOWN. % → ГОТОВО на 100. */
.preloader {
  position: fixed;
  inset: 0;
  z-index: 200;
  overflow: clip;
  /* bg = grey-700 (#2f3032) hardcoded — этим же цветом крестики паттерна (см. .preloader__fill SVG).
     mapped-border-default НЕ используем — он зависит от data-theme-section cascade,
     а preloader находится вне theme-секций. */
  background-color: var(--alias-grey-700);
  /* НЕ ставить will-change/transform/filter — иначе background-attachment:fixed
       на .preloader__fill перестанет быть viewport-relative (крестики «поедут»). */
}
.preloader__grid {
  position: absolute;
  inset: 0;
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-template-rows: 1fr 1fr;
  /* 1px зазор между ячейками = окружающий dark фон просвечивает как border-line */
  gap: 1px;
}
.preloader__cell {
  position: relative;
  overflow: hidden;
  background-color: var(--mapped-surface-dark);
}
/* Top-left: orange — лого/статус. БЕЗ срезанных углов. */
.preloader__orange {
  background-color: var(--mapped-surface-action);
  color: var(--alias-grey-900);
  /* padding-left identичный card'ам — задано через CSS var ниже */
  padding: var(--mapped-padding-md) var(--mapped-padding-sm);
  display: flex;
  flex-direction: column;
  /* z=2 чтобы при squeeze в reveal-фазе orange ехал поверх pattern */
  z-index: 2;
}
.preloader__logo {
  font-family: var(--brand-font-family);
  font-weight: var(--brand-weight-bold, 700);
  font-size: 5em;
  line-height: 1;
  letter-spacing: -0.01em;
}
.preloader__status {
  margin-top: var(--mapped-padding-sm);
  display: flex;
  flex-direction: column;
  gap: 0.5em;
  font-family: var(--brand-font-family);
  font-size: 0.875em;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.preloader__copy {
  font-family: var(--brand-font-family);
  font-size: 0.75em;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
/* Top-right: pattern fill + % progress. */
.preloader__pattern {
  background-color: var(--mapped-surface-dark);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1;
}
/* Крестики СТАТИЧНЫ — background-attachment:fixed → привязка к viewport, fill
     открывает неподвижные крестики L→R. Цвет grey-700 (#2f3032) зашит в SVG. */
.preloader__fill {
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
  width: calc(var(--preloader-load, 0) * 100%);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M4 7h8v1H4zM7 4h1v8H7z' fill='%232f3032'/%3E%3C/svg%3E");
  background-repeat: repeat;
  background-size: 16px 16px;
  background-attachment: fixed;
}
.preloader__progress {
  position: relative;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
  color: var(--alias-grey-300);
  font-family: var(--brand-font-family);
  font-size: 2.5em;
  line-height: 1;
}
.preloader__square {
  width: 0.6em;
  height: 0.6em;
  background-color: var(--mapped-surface-action);
  animation: cctpPreloaderBlink 1.2s ease-in-out infinite;
}
@keyframes cctpPreloaderBlink {
  0%,
  100% {
    opacity: 1;
  }
  50% {
    opacity: 0.35;
  }
}
/* Overlay для reveal-фазы — отдельный слой поверх grid'а. Изначально расположен
   точно поверх top-right pattern cell, растёт во время squeeze к full viewport.
   Cells grid'а ВНУТРИ остаются на местах (overlay их перекрывает). */
.preloader__overlay {
  position: absolute;
  z-index: 5;
  display: none; /* JS включает на startReveal */
  align-items: center;
  justify-content: center;
  background-color: var(--mapped-surface-dark);
  overflow: hidden;
}
.preloader__overlay-fill {
  position: absolute;
  inset: 0;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M4 7h8v1H4zM7 4h1v8H7z' fill='%232f3032'/%3E%3C/svg%3E");
  background-repeat: repeat;
  background-size: 16px 16px;
  background-attachment: fixed;
}

/* Row 2 cards: dark cells с annotation сверху + shape-иллюстрацией. */
.preloader__card {
  background-color: var(--mapped-surface-dark);
  padding: var(--mapped-padding-md) var(--mapped-padding-sm);
  display: flex;
  flex-direction: column;
  gap: var(--mapped-padding-md);
  color: var(--alias-grey-300);
  align-items: flex-start; /* всё содержимое прижато к левому краю */
}
.preloader__card .card_head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--mapped-padding-sm);
}
.preloader__card .card_head-left {
  display: flex;
  align-items: center;
  gap: var(--mapped-padding-sm);
}
.preloader__card .card_illustration-wrap {
  flex: 1;
  margin: 0; /* без auto-центрирования — иллюстрация к левому краю */
}
/* Мобилка (≤767): сплит вертикально — pattern + % сверху, остальное скрыто. */
@media screen and (max-width: 767px) {
  .preloader__grid {
    grid-template-columns: 1fr;
    grid-template-rows: 1fr;
  }
  .preloader__orange,
  .preloader__card {
    display: none;
  }
}
/* Reduced motion → прелоадер не показываем (JS делает instant-reveal). */
@media (prefers-reduced-motion: reduce) {
  .preloader {
    display: none;
  }
}

/* feature-list_badge — оранжевая плашка-бейдж. Базовое правило ЗДЕСЬ (а не
   только в TaskList.astro <style is:global>), т.к. Astro грузит is:global-чанк
   компонента ТОЛЬКО на страницах где компонент рендерится. Бейдж «Факт» в
   StatCard на /about/ (там TaskList нет) иначе оставался без плашки. Дубликат
   в TaskList идентичен — base.css грузится последним, конфликта нет. */
.feature-list_badge {
  display: inline-block;
  background-color: var(--mapped-surface-action);
  color: var(--mapped-text-on-action);
  padding: var(--mapped-padding-xxs) var(--mapped-padding-xs);
  border-radius: 0.1em;
  font-size: var(--type-paragraph-sm-size);
  line-height: 1;
  white-space: nowrap;
  flex-shrink: 0;
}
/* is-neutral — нейтральный (серый) вариант бейджа. Для «Факт»-плашки в StatCard
   (about). Базовый оранжевый «Выполнено» (FeatureRow на кейсе) не трогается. */
.feature-list_badge.is-neutral {
  background-color: var(--brand-grey-750);
  color: var(--mapped-text-headings-inverse);
}

/* Override: внутри centered-варианта StatCard (Facts на /about/) бейдж «Факт»
   должен оставаться по центру и на мобилке. TaskList.astro <style is:global>
   ставит .feature-list_badge { align-self: flex-start } @media 479 (нужно для
   column-стека в TaskList items) — сбрасываем тут, чтобы align-items:center от
   родителя .is-centered снова работал. Селектор (0,3,0) бьёт (0,1,0) из TaskList
   детерминированно, независимо от load-order. */
@media screen and (max-width: 479px) {
  .card_info-block-big.is-centered .feature-list_badge {
    align-self: auto;
  }
}

/* ── Number Odometer (rolling-digit reveal) ──────────────────────────────
   Структурные стили из сниппета. Демо-класс .odometer-h1 НЕ переносим —
   типографику даёт .heading_display-large. Активируется opt-in пропом
   odometer на StatCard. JS: initNumberOdometer() в main.js. */
[data-odometer-element] {
  display: inline-flex;
  align-items: center;
  font-variant-numeric: tabular-nums;
}

[data-odometer-part="mask"],
[data-odometer-part="static"] {
  display: inline-block;
  overflow: clip;
  padding: 0.05em;
  margin: -0.05em;
}

[data-odometer-part="roller"] {
  display: block;
  white-space: pre;
  text-align: center;
  will-change: transform;
}

[data-odometer-part="static"] {
  display: inline-block;
}

/* ────────────────────────────────────────────────────────────────────────
 * EN-only typography override
 *
 * Всё на /en/* (lang="en"):
 *   - h1/h2/h3 + display-large/small → uppercase + letter-spacing -0.035em
 *   - h4/h5 → uppercase + letter-spacing -0.02em
 *   - h6 → uppercase + letter-spacing 0
 *
 * RU (lang="ru" / default) не затрагивается — заголовки рендерятся как было.
 * ──────────────────────────────────────────────────────────────────────── */
html[lang="en"] h1,
html[lang="en"] h2,
html[lang="en"] h3,
html[lang="en"] .heading_h1,
html[lang="en"] .heading_h2,
html[lang="en"] .heading_h2-copy,
html[lang="en"] .heading_h3,
html[lang="en"] .heading_display-large,
html[lang="en"] .heading_display-small {
  text-transform: uppercase;
  letter-spacing: -0.035em;
}

html[lang="en"] h4,
html[lang="en"] h5,
html[lang="en"] .heading_h4,
html[lang="en"] .heading_h5 {
  text-transform: uppercase;
  letter-spacing: -0.02em;
}

html[lang="en"] h6,
html[lang="en"] .heading_h6 {
  text-transform: uppercase;
  letter-spacing: 0;
}

/* EN-only: hero heading container шире — английский занимает меньше места по горизонтали */
html[lang="en"] .hero_heading_container {
  max-width: 31.6em;
}
