/* ═══════════════════════════════════════════════════════════
   Rocket CDN · голографические метки, оформление

   Над объектами демо-полёта (планеты, узлы сети, станции) висят
   подписи-голограммы. Клиент просил дословно: «текст = голограммы
   красивые реагирующие на клики касания наведение на полёте дальше
   красиво растворяются появляются».

   Правила слоя:
   - Геометрию считает игра, здесь только вид. Модуль rc-holo.js
     каждый кадр кладёт в метку четыре числа: --h-x, --h-y (экранные
     пиксели), --h-d (глубина 0..1) и изредка --h-lh (подъём выноски
     при разведении меток). Ничего, что вызывает пересчёт вёрстки,
     в кадре не меняется - только transform и opacity.
   - Метка в покое: перекрестье на объекте, тонкая выноска вверх,
     узкая планка с заголовком. Наведение или касание раскрывает
     карточку вниз: подзаголовок, описание, скобки по углам.
   - Дальние метки мельче и бледнее, и у них слабее вся мягкая
     оптика - ореол, свечение, хроматический двойник. Близкие
     метки светятся, дальние остаются тонкой графикой.
   - Появление разворачивается от точки: сначала выноска, затем
     полка, затем «печатается» текст. Растворение - то же назад,
     плюс распад на полосы.

   Почему @property: обычную custom property браузер не умеет
   плавно гнать по времени и не пересчитывает по ней градиенты.
   Зарегистрированные свойства анимируются и участвуют в масках -
   на них держатся и печать текста, и распад на полосы, и подъём
   выноски при разведении.
   ═══════════════════════════════════════════════════════════ */

@property --h-d    { syntax: "<number>";     inherits: true;  initial-value: 0.5; }
@property --h-lh   { syntax: "<number>";     inherits: true;  initial-value: 46; }
@property --h-lit  { syntax: "<number>";     inherits: true;  initial-value: 0; }
@property --h-grow { syntax: "<number>";     inherits: false; initial-value: 1; }
@property --h-arm  { syntax: "<number>";     inherits: false; initial-value: 1; }
@property --h-wipe { syntax: "<percentage>"; inherits: false; initial-value: 112%; }

/* ── Слой ────────────────────────────────────────────────────
   Лежит поверх холста игры и сам по себе прозрачен для мыши:
   ловят события только сами метки. overflow скрывает метки,
   уехавшие за край кадра, contain отсекает их влияние на
   раскладку страницы под игрой. */
.rc-holo {
  position: absolute;
  inset: 0;
  z-index: 12;
  overflow: hidden;
  pointer-events: none;
  contain: layout paint style;
  font-family: 'Golos Text', -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
  -webkit-font-smoothing: antialiased;

  --h-cy: #42B2DC;
  --h-vi: #8A59F6;
  --h-tx: #CFE9F5;
  --h-ac: var(--h-cy);
  --h-ease: cubic-bezier(.22, 1, .36, 1);
}
.rc-holo i,
.rc-holo b { display: block; font-style: normal; font-weight: inherit; }

/* Тип объекта красит акцент. Синий - сеть и планеты, фиолетовый -
   рукотворное, янтарь - тревога. */
.rch-k-node    { --h-ac: #5EC8EE; }
.rch-k-planet  { --h-ac: #42B2DC; }
.rch-k-station { --h-ac: #9B78FF; }
.rch-k-relay   { --h-ac: #8A59F6; }
.rch-k-warn    { --h-ac: #FFB066; }

/* ── Корень метки ────────────────────────────────────────────
   Единственное, что меняется в кадре, - transform корня и три
   переменные. Никакого left/top: они бы дёргали раскладку
   шестьдесят раз в секунду. */
.rch-tag {
  position: absolute;
  left: 0;
  top: 0;
  width: 0;
  height: 0;
  color: var(--h-tx);
  pointer-events: none;
  transform: translate3d(calc(var(--h-x, 0) * 1px), calc(var(--h-y, 0) * 1px), 0);
  will-change: transform;
  --h-lit: 0;
  transition: --h-lit .26s var(--h-ease), --h-lh .34s var(--h-ease);
}
.rch-tag.off { display: none; }
.rch-tag.on  { --h-lit: 1; }

/* Глубина: масштаб и общая бледность. Оба свойства композитные,
   пересчёт вёрстки не трогают. */
.rch-sc {
  position: absolute;
  left: 0;
  top: 0;
  width: 0;
  height: 0;
  transform-origin: 0 0;
  transform: scale(calc(1 - var(--h-d) * .38));
  opacity: calc(1 - var(--h-d) * .5);
}

/* ── Маркер на объекте ───────────────────────────────────────
   Кнопка, а не div: метку должно быть видно клавиатуре и
   скринридеру, а Enter обязан работать как клик. */
.rch-mark {
  position: absolute;
  left: -20px;
  top: -20px;
  width: 40px;
  height: 40px;
  padding: 0;
  margin: 0;
  border: 0;
  background: none;
  color: inherit;
  cursor: pointer;
  pointer-events: auto;
  -webkit-tap-highlight-color: transparent;
}
/* Палец толще курсора: зона касания шире рисунка, но сам рисунок
   не растёт. */
.rch-mark::after { content: ''; position: absolute; inset: -9px; }
.rch-mark:focus { outline: none; }
.rch-mark:focus-visible .rch-ring { stroke-width: 1.6; }

.rch-glyph {
  display: block;
  width: 100%;
  height: 100%;
  overflow: visible;
  fill: none;
  stroke: var(--h-ac);
  stroke-width: 1;
  stroke-linecap: round;
}
.rch-ring {
  opacity: calc(.72 + var(--h-lit) * .28);
  stroke-width: calc(1px + var(--h-lit) * .3px);
}
.rch-hair {
  opacity: calc(.42 + var(--h-lit) * .5);
  stroke-dasharray: 4.4 7.6;
  transform-origin: 20px 20px;
  animation: rchSpin 22s linear infinite;
}
/* У раскрытой метки прицел довернут: движение подтверждает, что
   именно она сейчас в фокусе. */
.rch-tag.on .rch-hair { animation-duration: 5.5s; }
.rch-tick { opacity: calc(.5 + var(--h-lit) * .45); }
.rch-core { fill: var(--h-ac); stroke: none; }

/* Ореол - вся мягкая оптика метки. Он и есть та «размытость»,
   которой у дальних меток быть не должно. */
.rch-halo {
  position: absolute;
  inset: -10px;
  border-radius: 50%;
  color: var(--h-ac);
  background: radial-gradient(circle at 50% 50%, currentColor 0%, rgba(0, 0, 0, 0) 66%);
  filter: blur(5px);
  opacity: calc((.22 + var(--h-lit) * .42) * (1 - var(--h-d) * .86));
}

/* Кольцо подтверждения по клику */
.rch-ping {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 28px;
  height: 28px;
  margin: -14px 0 0 -14px;
  border: 1px solid var(--h-ac);
  border-radius: 50%;
  opacity: 0;
}
.rch-ping::after {
  content: '';
  position: absolute;
  inset: -1px;
  border: 1px solid var(--h-ac);
  border-radius: 50%;
  opacity: .5;
}

/* ── Выноска ─────────────────────────────────────────────────
   Стойка нарисована высотой в сто пикселей и сжимается по месту:
   масштабировать однопиксельную полоску нельзя - градиент внутри
   растянулся бы в сплошную заливку. */
.rch-riser {
  position: absolute;
  left: 0;
  top: calc(-113px);
  width: 1px;
  height: 100px;
  color: var(--h-ac);
  background: linear-gradient(180deg, currentColor 0%, currentColor 55%, rgba(120, 200, 236, .15) 100%);
  transform-origin: 50% 100%;
  transform: scaleY(calc(var(--h-lh) * var(--h-grow) / 100));
  opacity: calc(.5 + var(--h-lit) * .45);
}

/* Всё, что висит на выноске, поднято одним transform: подъём при
   разведении меток не должен пересчитывать вёрстку. */
/* Лёгкий разворот вокруг выноски: карточка не наклейка на экране,
   а проекция, висящая в пространстве. Угол маленький - текст
   должен остаться читаемым, а не «уехать в перспективу». */
.rch-lift {
  position: absolute;
  left: 0;
  top: 0;
  width: 0;
  height: 0;
  transform-origin: 0 50%;
  transform: translateY(calc(var(--h-lh) * -1px - 13px)) perspective(900px) rotateY(6deg);
}
.rch-arm {
  position: absolute;
  left: 0;
  top: 0;
  width: 26px;
  height: 1px;
  color: var(--h-ac);
  background: linear-gradient(90deg, currentColor 0%, rgba(120, 200, 236, .3) 100%);
  transform-origin: 0 50%;
  transform: scaleX(var(--h-arm));
  opacity: calc(.6 + var(--h-lit) * .4);
}
/* Точка на изломе: глаз цепляется за неё и достраивает связь
   маркера с карточкой. */
.rch-arm::after {
  content: '';
  position: absolute;
  right: -1px;
  top: -1.5px;
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: var(--h-ac);
}

/* ── Карточка ────────────────────────────────────────────────
   Ширина постоянная: HUD не должен прыгать по горизонтали, когда
   карточка раскрывается. Растёт она только вниз. */
.rch-body {
  position: absolute;
  left: 24px;
  top: -13px;
  /* Ширину назначает полёт через --rch-w: она считается от проёма
     окна рубки. Жёсткие 214 пикселей на телефоне не помещались в
     окно шириной 297 вместе с выноской, и подпись обрезалась кромкой
     остекления - ровно то, что заказчик увидел как «АСТЕРОИ» и
     «ЗВЁЗДНАЯ СИСТ». */
  width: var(--rch-w, 214px);
  pointer-events: auto;
  transform-origin: 0 0;
}

/* ── Переброс на левую сторону ───────────────────────────────
   У правого края кадра карточка не влезает и обрезалась бы слоем.
   Тогда она уходит влево от выноски: зеркалится только геометрия,
   текст остаётся как был. */
.rch-tag.flip .rch-lift {
  transform: translateY(calc(var(--h-lh) * -1px - 13px)) perspective(900px) rotateY(-6deg);
}
.rch-tag.flip .rch-arm {
  left: auto;
  right: 0;
  transform-origin: 100% 50%;
  background: linear-gradient(270deg, currentColor 0%, rgba(120, 200, 236, .3) 100%);
}
.rch-tag.flip .rch-arm::after { right: auto; left: -1px; }
.rch-tag.flip .rch-body {
  left: auto;
  right: 24px;
  transform-origin: 100% 0;
}
.rch-tag.flip .rch-edge {
  left: auto;
  right: -1px;
}
.rch-tag.flip .rch-rows {
  padding-left: 11px;
  padding-right: 12px;
}

/* Стекло: заливка, рамка и сканлайны. Полосы сделаны маской, а не
   накладкой, - так они съедают и рамку, и метка выглядит не
   плёнкой поверх, а самой проекцией. */
.rch-glass {
  position: absolute;
  inset: 0;
  background:
    linear-gradient(94deg, rgba(66, 178, 220, .16) 0%, rgba(66, 178, 220, .04) 26%, rgba(0, 0, 0, 0) 52%),
    linear-gradient(200deg, rgba(138, 89, 246, .13) 0%, rgba(4, 11, 22, .0) 62%),
    linear-gradient(180deg, rgba(5, 14, 27, .5) 0%, rgba(3, 9, 18, .38) 100%);
  border: 1px solid rgba(120, 200, 236, calc(.16 + var(--h-lit) * .42));
  -webkit-mask-image: repeating-linear-gradient(180deg, #000 0 2px, rgba(0, 0, 0, .3) 2px 4px);
  mask-image: repeating-linear-gradient(180deg, #000 0 2px, rgba(0, 0, 0, .3) 2px 4px);
  /* Срезанный угол - опознавательный знак приборной графики.
     Прямоугольник читался бы как веб-подсказка, а не как HUD. */
  clip-path: polygon(0 0, calc(100% - 10px) 0, 100% 10px, 100% 100%, 0 100%);
  animation: rchFlick 5.6s linear infinite;
  animation-delay: calc(var(--h-seed, 0) * -6s);
}
.rch-tag.flip .rch-glass {
  clip-path: polygon(10px 0, 100% 0, 100% 100%, 0 100%, 0 10px);
}
/* Левая грань не в рамке, а отдельной планкой: маска со сканлайнами
   изрезала бы её в пунктир, а она держит связь с выноской. */
.rch-edge {
  position: absolute;
  left: -1px;
  top: -1px;
  bottom: -1px;
  width: 2px;
  background: linear-gradient(180deg, var(--h-ac) 0%, rgba(66, 178, 220, .25) 100%);
  opacity: calc(.7 + var(--h-lit) * .3);
}
/* Луч сканирования бежит по стеклу только у раскрытой карточки:
   двадцать четыре вечных луча кадру не нужны. */
.rch-glass::before {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  height: 34%;
  background: linear-gradient(180deg, rgba(207, 233, 245, 0) 0%, rgba(207, 233, 245, .16) 60%, rgba(207, 233, 245, 0) 100%);
  opacity: 0;
}
.rch-tag.on .rch-glass::before { animation: rchSweep 2.6s var(--h-ease) infinite; }

/* Свечение под карточкой. Мягкая оптика - привилегия близких
   меток, у дальних она гаснет вместе с --h-d. */
.rch-glow {
  position: absolute;
  inset: -7px -9px -9px -7px;
  background:
    radial-gradient(120% 150% at 0% 0%, rgba(66, 178, 220, .5) 0%, rgba(66, 178, 220, 0) 62%),
    radial-gradient(90% 120% at 100% 100%, rgba(138, 89, 246, .34) 0%, rgba(138, 89, 246, 0) 68%);
  filter: blur(9px);
  opacity: calc((.3 + var(--h-lit) * .55) * (1 - var(--h-d) * .8));
}
/* Тень отдельным слоем: попади она на стекло, маска нарезала бы
   и её в полоску. */
.rch-body::before {
  content: '';
  position: absolute;
  inset: 0;
  box-shadow: 0 12px 34px rgba(1, 6, 14, .5);
  z-index: -1;
}

/* Скобки по углам. Появляются вразнобой - так рамка «защёлкивается»,
   а не просто включается. */
.rch-cor {
  position: absolute;
  width: 9px;
  height: 9px;
  border: 0 solid var(--h-ac);
  opacity: 0;
  transition: opacity .18s linear, transform .26s var(--h-ease);
}
.rch-cor.c1 { left: -3px;  top: -3px;    border-left-width: 1px; border-top-width: 1px;    transform: translate(5px, 5px); }
.rch-cor.c2 { right: -3px; top: -3px;    border-right-width: 1px; border-top-width: 1px;   transform: translate(-5px, 5px); }
.rch-cor.c3 { left: -3px;  bottom: -3px; border-left-width: 1px; border-bottom-width: 1px; transform: translate(5px, -5px); }
.rch-cor.c4 { right: -3px; bottom: -3px; border-right-width: 1px; border-bottom-width: 1px; transform: translate(-5px, -5px); }
.rch-tag.on .rch-cor { opacity: .95; transform: none; }
.rch-tag.on .rch-cor.c1 { transition-delay: .02s; }
.rch-tag.on .rch-cor.c2 { transition-delay: .07s; }
.rch-tag.on .rch-cor.c3 { transition-delay: .12s; }
.rch-tag.on .rch-cor.c4 { transition-delay: .17s; }

/* Полосы распада: включаются только на растворении */
.rch-shred {
  position: absolute;
  inset: -1px;
  opacity: 0;
  color: var(--h-ac);
  background: repeating-linear-gradient(0deg, currentColor 0 1px, rgba(0, 0, 0, 0) 1px 7px);
}

/* ── Текст ───────────────────────────────────────────────────
   Маска-«печать»: --h-wipe гонит границу слева направо. Она же
   на растворении едет назад, но уже сквозь полосы. */
.rch-rows {
  position: relative;
  padding: 7px 11px 8px 12px;
  -webkit-mask-image: linear-gradient(90deg, #000 var(--h-wipe), rgba(0, 0, 0, 0) calc(var(--h-wipe) + 9%));
  mask-image: linear-gradient(90deg, #000 var(--h-wipe), rgba(0, 0, 0, 0) calc(var(--h-wipe) + 9%));
}
.rch-title {
  display: flex;
  align-items: center;
  gap: 7px;
  min-width: 0;
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: .085em;
  text-transform: uppercase;
  color: #EAF7FF;
  animation: rchFlick 5.6s linear infinite;
  animation-delay: calc(var(--h-seed, 0) * -6s - 1.7s);
}
/* Ширина карточки постоянная, иначе HUD прыгал бы при раскрытии.
   Пустоту справа занимает телеметрия - она же показывает, что
   метка живая, а не подпись на картинке. */
.rch-meter {
  margin-left: auto;
  flex: none;
  width: 30px;
  height: 10px;
  opacity: calc((.4 + var(--h-lit) * .45) * (1 - var(--h-d) * .55));
}
.rch-meter svg {
  display: block;
  width: 100%;
  height: 100%;
  fill: none;
  stroke: var(--h-ac);
  stroke-width: 1.1;
  stroke-linecap: round;
}
.rch-ico {
  width: 13px;
  height: 13px;
  flex: none;
  fill: none;
  stroke: var(--h-ac);
  stroke-width: 1.25;
  stroke-linejoin: round;
  overflow: visible;
}
/* Имя тела переносится, а не отрезается многоточием.

   Здесь стояло white-space: nowrap с многоточием, и длинное имя
   резалось на ЛЮБОМ экране, включая 1920: «АСТЕРОИДНЫЙ ПОЯС»
   уходило в «АСТЕРОИДНЫЙ П…», в чужих рукавах три метки подряд
   читались одинаково - «TRAPP…», «TRAPP…», «TRAPP…». Это и есть
   «кучу обрезаний», про которые писал заказчик.

   Многоточие в подписи объекта бессмысленно: имя это единственное,
   ради чего метку читают. Даём слову вторую строку и разрешаем
   рвать по буквам там, где слово длиннее карточки (каталожные
   имена вроде LHS 1140 b пробелов почти не имеют). Больше двух
   строк не даём: карточка не должна расти в столб. */
.rch-tname {
  position: relative;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  min-width: 0;
  max-width: 100%;
  overflow: hidden;
  white-space: normal;
  overflow-wrap: anywhere;
  line-height: 1.12;
}
/* Хроматический сдвиг: два цветных двойника заголовка. Не blend-mode -
   маска строк отрезала бы его от фона и эффект пропал бы. */
/* Цветные двойники заголовка обязаны переноситься так же, как сам
   заголовок. Раньше у них стояло white-space: nowrap: имя из двух
   слов вставало в две строки, а двойники за ним оставались в одну и
   вылезали за карточку. Замер на телефоне поймал это как ширину 147
   при коробке в 101 - и это была ширина именно двойников, сам текст
   переносился верно. Даём им ширину родителя и те же правила
   переноса, тогда хроматический сдвиг ложится ровно по буквам. */
.rch-tname::before,
.rch-tname::after {
  content: attr(data-t);
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  white-space: normal;
  overflow-wrap: anywhere;
  line-height: inherit;
  pointer-events: none;
  filter: blur(.45px);
  opacity: calc((.22 + var(--h-lit) * .16) * (1 - var(--h-d) * .95));
}
.rch-tname::before { color: var(--h-cy); transform: translateX(-.5px); }
.rch-tname::after  { color: var(--h-vi); transform: translateX(.6px); }

/* Подзаголовок и описание живут свёрнутыми и раскрываются лесенкой */
.rch-sub,
.rch-info {
  max-height: 0;
  margin-top: 0;
  opacity: 0;
  overflow: hidden;
  transform: translateY(-4px);
  transition:
    max-height .28s var(--h-ease),
    margin-top .28s var(--h-ease),
    opacity .2s linear,
    transform .28s var(--h-ease);
}
.rch-sub {
  font-size: 9.5px;
  font-weight: 600;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--h-ac);
}
/* Волосяная линейка отделяет описание от шапки: в раскрытом виде
   карточка перестаёт быть сплошным абзацем. */
.rch-info {
  font-size: 10.5px;
  line-height: 1.45;
  color: rgba(207, 233, 245, .82);
}
/* Линейка нарисована внутренним блоком, а не border-top: рамка
   торчала бы из свёрнутой строки, у которой max-height равен нулю. */
.rch-info::before {
  content: '';
  display: block;
  height: 1px;
  margin: 0 0 6px;
  background: linear-gradient(90deg, rgba(120, 200, 236, .3), rgba(120, 200, 236, .04));
}
.rch-tag.on .rch-sub  { max-height: 2.4em; margin-top: 5px; opacity: .95; transform: none; transition-delay: .04s; }
.rch-tag.on .rch-info { max-height: 8em;   margin-top: 6px; opacity: 1;   transform: none; transition-delay: .11s; }

/* Планка снизу: у зафиксированной метки она горит целиком - видно,
   что выбор принят и держится. */
.rch-bar {
  position: absolute;
  left: 0;
  bottom: -1px;
  height: 1px;
  width: 100%;
  background: linear-gradient(90deg, var(--h-ac) 0%, rgba(120, 200, 236, 0) 100%);
  transform-origin: 0 50%;
  transform: scaleX(calc(.22 + var(--h-lit) * .5));
  opacity: calc(.5 + var(--h-lit) * .5);
  transition: transform .3s var(--h-ease);
}
.rch-tag.pick .rch-bar { transform: scaleX(1); }

/* Зафиксированная метка: рамка ярче, стекло теплее по свету */
.rch-tag.pick .rch-glass { border-color: var(--h-ac); }
.rch-tag.pick .rch-cor { opacity: 1; }

/* Курсор-указатель и лёгкий подъём - только там, где есть мышь.
   На тач-экране этого состояния не существует. */
@media (hover: hover) {
  .rch-body { cursor: pointer; }
  .rch-tag.on .rch-body { transform: translateY(-1px); transition: transform .24s var(--h-ease); }
}

/* Размытие фона стоит дорого, поэтому включается только под
   раскрытой карточкой - их на экране одна-две. */
.rch-tag.on .rch-glass {
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
}

/* ── Появление ───────────────────────────────────────────────
   Порядок жёсткий: точка, стойка, полка, карточка, текст. Он и
   читается как «разворачивается от объекта». */
.rch-tag.in .rch-mark  { animation: rchMarkIn .46s var(--h-ease) both; }
.rch-tag.in .rch-riser { animation: rchRiseIn .34s var(--h-ease) .06s both; }
.rch-tag.in .rch-arm   { animation: rchArmIn .24s var(--h-ease) .3s both; }
.rch-tag.in .rch-body  { animation: rchBodyIn .34s var(--h-ease) .34s both; }
.rch-tag.in .rch-rows  { animation: rchType .44s linear .46s both; }

@keyframes rchMarkIn {
  0%   { opacity: 0; transform: scale(.2) rotate(-40deg); }
  55%  { opacity: 1; transform: scale(1.16) rotate(6deg); }
  100% { opacity: 1; transform: scale(1) rotate(0deg); }
}
@keyframes rchRiseIn { from { --h-grow: 0; } to { --h-grow: 1; } }
@keyframes rchArmIn  { from { --h-arm: 0; }  to { --h-arm: 1; } }
@keyframes rchBodyIn {
  0%   { opacity: 0; transform: scaleY(.02) scaleX(.86); }
  60%  { opacity: 1; }
  100% { opacity: 1; transform: scaleY(1) scaleX(1); }
}
@keyframes rchType { from { --h-wipe: -8%; } to { --h-wipe: 112%; } }

/* ── Растворение ─────────────────────────────────────────────
   Обратный ход плюс распад: маска строк режется полосами, поверх
   пробегает штриховка, карточка сдувается и уходит вправо. */
.rch-tag.out .rch-mark  { animation: rchMarkOut .3s var(--h-ease) .14s both; }
.rch-tag.out .rch-riser { animation: rchRiseOut .28s var(--h-ease) .1s both; }
.rch-tag.out .rch-arm   { animation: rchArmOut .2s var(--h-ease) .06s both; }
.rch-tag.out .rch-body  { animation: rchBodyOut .38s var(--h-ease) both; }
.rch-tag.out .rch-shred { animation: rchBands .38s linear both; }
.rch-tag.out .rch-rows {
  -webkit-mask-image:
    repeating-linear-gradient(0deg, #000 0 3px, rgba(0, 0, 0, .04) 3px 7px),
    linear-gradient(90deg, #000 var(--h-wipe), rgba(0, 0, 0, 0) calc(var(--h-wipe) + 24%));
  mask-image:
    repeating-linear-gradient(0deg, #000 0 3px, rgba(0, 0, 0, .04) 3px 7px),
    linear-gradient(90deg, #000 var(--h-wipe), rgba(0, 0, 0, 0) calc(var(--h-wipe) + 24%));
  -webkit-mask-composite: source-in;
  mask-composite: intersect;
  animation: rchShred .38s linear both;
}
@keyframes rchMarkOut {
  0%   { opacity: 1; transform: scale(1); }
  100% { opacity: 0; transform: scale(.34) rotate(28deg); }
}
@keyframes rchRiseOut { from { --h-grow: 1; } to { --h-grow: 0; } }
@keyframes rchArmOut  { from { --h-arm: 1; }  to { --h-arm: 0; } }
@keyframes rchBodyOut {
  0%   { opacity: 1; transform: translateX(0) scaleY(1); filter: blur(0); }
  45%  { opacity: .82; }
  100% { opacity: 0; transform: translateX(11px) scaleY(.78); filter: blur(1.6px); }
}
@keyframes rchShred { from { --h-wipe: 112%; } to { --h-wipe: -14%; } }
@keyframes rchBands {
  0%   { opacity: 0;   transform: translateX(0); }
  22%  { opacity: .6; }
  100% { opacity: 0;   transform: translateX(18px); }
}
/* Переброшенная карточка и рассыпается в свою сторону - иначе
   полосы уезжали бы внутрь кадра, а не прочь от него. */
.rch-tag.flip.out .rch-body  { animation-name: rchBodyOutL; }
.rch-tag.flip.out .rch-shred { animation-name: rchBandsL; }
@keyframes rchBodyOutL {
  0%   { opacity: 1; transform: translateX(0) scaleY(1); filter: blur(0); }
  45%  { opacity: .82; }
  100% { opacity: 0; transform: translateX(-11px) scaleY(.78); filter: blur(1.6px); }
}
@keyframes rchBandsL {
  0%   { opacity: 0;   transform: translateX(0); }
  22%  { opacity: .6; }
  100% { opacity: 0;   transform: translateX(-18px); }
}

/* ── Клик ────────────────────────────────────────────────────
   Вспышка по карточке и кольцо от маркера: подтверждение видно
   и в центре внимания, и на самом объекте. */
.rch-tag.pop .rch-ping { animation: rchPing .66s var(--h-ease) both; }
.rch-tag.pop .rch-ping::after { animation: rchPing .66s var(--h-ease) .12s both; }
.rch-tag.pop .rch-body { animation: rchFlash .44s var(--h-ease) both; }
.rch-tag.pop .rch-glow { animation: rchBloom .5s var(--h-ease) both; }
@keyframes rchPing {
  0%   { opacity: .95; transform: scale(.32); }
  100% { opacity: 0;   transform: scale(3.2); }
}
@keyframes rchFlash {
  0%   { filter: brightness(2.4) saturate(.55); }
  22%  { filter: brightness(1.35) saturate(.9); }
  100% { filter: brightness(1) saturate(1); }
}
@keyframes rchBloom {
  0%   { transform: scale(1.14); }
  100% { transform: scale(1); }
}

/* ── Общие циклы ─────────────────────────────────────────────
   Мерцание нарочно неровное и у каждой метки со своей фазой -
   ровное дыхание читалось бы как анимация загрузки. */
@keyframes rchFlick {
  0%, 46%   { opacity: 1; }
  47.4%     { opacity: .58; }
  48.2%     { opacity: .94; }
  49%       { opacity: .7; }
  50%, 71%  { opacity: 1; }
  72%       { opacity: .84; }
  73%, 100% { opacity: 1; }
}
@keyframes rchSpin { to { transform: rotate(360deg); } }
@keyframes rchSweep {
  0%   { opacity: 0;   transform: translateY(-40%); }
  18%  { opacity: .9; }
  70%  { opacity: 0;   transform: translateY(230%); }
  100% { opacity: 0;   transform: translateY(230%); }
}

/* ── Спокойный режим ─────────────────────────────────────────
   Человеку, которому движение мешает, метки просто показываются
   и просто исчезают. Все переменные при выключенных анимациях
   остаются на своих initial-value, поэтому вид не ломается. */
@media (prefers-reduced-motion: reduce) {
  .rc-holo *,
  .rc-holo *::before,
  .rc-holo *::after {
    animation: none !important;
    transition: none !important;
  }
  .rch-tag.out { opacity: 0; }
  .rch-tag.out .rch-rows {
    -webkit-mask-image: none;
    mask-image: none;
  }
}

/* ── Дальняя метка - только имя ──────────────────────────────
   Пока объект не «зажжён» приближением, стеклянная плашка не
   рисуется вовсе: пустые заготовки-рамки посреди окна владелец
   справедливо назвал кривыми окнами. У дальней метки остаются
   маркер и имя, стекло с описанием приходит вместе с .on. */
.rch-tag:not(.on) .rch-glass,
.rch-tag:not(.on) .rch-info,
.rch-tag:not(.on) .rch-sub {
  opacity: 0 !important;
  transition: opacity .25s var(--h-ease);
}
.rch-tag.on .rch-glass,
.rch-tag.on .rch-info,
.rch-tag.on .rch-sub {
  transition: opacity .25s var(--h-ease);
}

/* ── Метки тел живут на стекле ─────────────────────────────────
   Слой раскинут на весь кадр и обрезался по нему. С трёхмерной
   рубкой этого мало: за проёмом идёт рама, и подпись планеты,
   уехавшая к краю, ложилась прямо на стойку. Заказчик написал
   коротко: «окна выходят за рамки накладываются на эту рамку».

   Границы проёма приходят числами из rc-flight (--cab-w*), те же,
   по которым живут все остальные голограммы. Метка, вышедшая за
   стекло, обрезается им, а не кромкой экрана. */
.rc-flight .rc-holo {
  /* Слой не двигаем: метка знает свои экранные пиксели, и сдвиг
     коробки увёз бы её от тела. Режем содержимое по контуру.

     Раньше здесь стоял признак rcf-native-cab, то есть обрезка
     работала только в объёмной кабине. В прокрутке рубка плоская,
     признак снимается - и там метка у кромки окна светила ореолом
     на железо рубки. Приёмка ловила это на планшете и телефоне
     (rch-halo, rch-glow за границей стекла), а проверка вручную
     показывала обрезку - потому что вручную игра открывалась сразу
     в объёмной кабине. Контур --cab-clip рубка публикует в обоих
     режимах, значит и резать надо в обоих. */
  clip-path: var(--cab-clip, none);
}
